Add ProfileService and integrate profiles management into NostrFeed module
- Introduced ProfileService to handle user profiles, including fetching and displaying profile information. - Updated NostrFeed module to register ProfileService in the DI container and initialize it during installation. - Enhanced NostrFeed.vue to utilize the profiles service for displaying user names alongside posts. - Created useProfiles composable for managing profile-related functionality, including fetching and subscribing to profile updates. These changes improve user engagement by providing richer profile information within the feed, enhancing the overall user experience.
This commit is contained in:
parent
310612a2c5
commit
45391cbaa1
6 changed files with 443 additions and 47 deletions
|
|
@ -1,10 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, watch } from 'vue'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { formatDistanceToNow } from 'date-fns'
|
||||
import { Megaphone, RefreshCw, AlertCircle, Reply, Heart, Share } from 'lucide-vue-next'
|
||||
import { useFeed } from '../composables/useFeed'
|
||||
import { useProfiles } from '../composables/useProfiles'
|
||||
import appConfig from '@/app.config'
|
||||
import type { ContentFilter } from '../services/FeedService'
|
||||
import MarketProduct from './MarketProduct.vue'
|
||||
|
|
@ -35,6 +36,17 @@ const { posts: notes, isLoading, error, refreshFeed } = useFeed({
|
|||
contentFilters: props.contentFilters
|
||||
})
|
||||
|
||||
// Use profiles service for display names
|
||||
const { getDisplayName, fetchProfiles } = useProfiles()
|
||||
|
||||
// Watch for new posts and fetch their profiles
|
||||
watch(notes, async (newNotes) => {
|
||||
if (newNotes.length > 0) {
|
||||
const pubkeys = [...new Set(newNotes.map(note => note.pubkey))]
|
||||
await fetchProfiles(pubkeys)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// Check if we have admin pubkeys configured
|
||||
const hasAdminPubkeys = computed(() => adminPubkeys.length > 0)
|
||||
|
||||
|
|
@ -175,20 +187,25 @@ function onReplyToNote(note: any) {
|
|||
|
||||
<!-- Posts List - Full height scroll -->
|
||||
<div v-else class="h-full overflow-y-auto scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent">
|
||||
<div class="divide-y-2 divide-muted/30">
|
||||
<div v-for="note in notes" :key="note.id">
|
||||
<div>
|
||||
<div v-for="(note, index) in notes" :key="note.id" :class="{ 'bg-muted/20': index % 2 === 1 }">
|
||||
<!-- Market Product Component (kind 30018) -->
|
||||
<template v-if="note.kind === 30018">
|
||||
<div class="p-3">
|
||||
<div class="mb-2 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Badge
|
||||
v-if="isAdminPost(note.pubkey)"
|
||||
variant="default"
|
||||
class="text-xs"
|
||||
>
|
||||
Admin
|
||||
</Badge>
|
||||
<span>{{ formatDistanceToNow(note.created_at * 1000, { addSuffix: true }) }}</span>
|
||||
<div class="p-3 border-b border-border/40">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge
|
||||
v-if="isAdminPost(note.pubkey)"
|
||||
variant="default"
|
||||
class="text-xs"
|
||||
>
|
||||
Admin
|
||||
</Badge>
|
||||
<span class="text-sm font-medium">{{ getDisplayName(note.pubkey) }}</span>
|
||||
</div>
|
||||
<span class="text-xs text-muted-foreground">
|
||||
{{ formatDistanceToNow(note.created_at * 1000, { addSuffix: true }) }}
|
||||
</span>
|
||||
</div>
|
||||
<MarketProduct
|
||||
v-if="getMarketProductData(note)"
|
||||
|
|
@ -219,31 +236,34 @@ function onReplyToNote(note: any) {
|
|||
<!-- Regular Text Posts and Other Event Types -->
|
||||
<div
|
||||
v-else
|
||||
class="p-3 hover:bg-accent/50 transition-colors"
|
||||
class="p-3 hover:bg-accent/50 transition-colors border-b border-border/40"
|
||||
>
|
||||
<!-- Note Header -->
|
||||
<div class="flex items-center flex-wrap gap-1.5 mb-2">
|
||||
<Badge
|
||||
v-if="isAdminPost(note.pubkey)"
|
||||
variant="default"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
Admin
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="note.isReply"
|
||||
variant="secondary"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
Reply
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="isMarketEvent({ kind: note.kind })"
|
||||
variant="outline"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
{{ getMarketEventType({ kind: note.kind }) }}
|
||||
</Badge>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge
|
||||
v-if="isAdminPost(note.pubkey)"
|
||||
variant="default"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
Admin
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="note.isReply"
|
||||
variant="secondary"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
Reply
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="isMarketEvent({ kind: note.kind })"
|
||||
variant="outline"
|
||||
class="text-xs px-1.5 py-0.5"
|
||||
>
|
||||
{{ getMarketEventType({ kind: note.kind }) }}
|
||||
</Badge>
|
||||
<span class="text-sm font-medium">{{ getDisplayName(note.pubkey) }}</span>
|
||||
</div>
|
||||
<span class="text-xs text-muted-foreground">
|
||||
{{ formatDistanceToNow(note.created_at * 1000, { addSuffix: true }) }}
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue