import { ref, computed, onMounted, onUnmounted } from 'vue' import { injectService, SERVICE_TOKENS } from '@/core/di-container' import { eventBus } from '@/core/event-bus' import type { Event as NostrEvent, Filter } from 'nostr-tools' export interface FeedConfig { feedType: 'announcements' | 'general' | 'mentions' maxPosts?: number refreshInterval?: number adminPubkeys?: string[] } export function useFeed(config: FeedConfig) { const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) const posts = ref([]) const isLoading = ref(false) const error = ref(null) let refreshTimer: number | null = null let unsubscribe: (() => void) | null = null const filteredPosts = computed(() => { let filtered = posts.value // Filter by feed type if (config.feedType === 'announcements' && config.adminPubkeys) { filtered = filtered.filter(post => config.adminPubkeys!.includes(post.pubkey)) } // Sort by created timestamp (newest first) filtered = filtered.sort((a, b) => b.created_at - a.created_at) // Limit posts if (config.maxPosts) { filtered = filtered.slice(0, config.maxPosts) } return filtered }) const loadFeed = async () => { if (!relayHub) { error.value = 'RelayHub not available' return } isLoading.value = true error.value = null try { // Create filter based on feed type const filter: Filter = { kinds: [1], // Text notes limit: config.maxPosts || 50 } if (config.feedType === 'announcements' && config.adminPubkeys) { filter.authors = config.adminPubkeys } // Subscribe to events await relayHub.subscribe('feed-subscription', [filter], { onEvent: (event: NostrEvent) => { // Add new event if not already present if (!posts.value.some(p => p.id === event.id)) { posts.value = [event, ...posts.value] // Emit event for other modules eventBus.emit('nostr-feed:new-post', { event, feedType: config.feedType }, 'nostr-feed') } }, onEose: () => { console.log('Feed subscription end of stored events') isLoading.value = false }, onClose: () => { console.log('Feed subscription closed') } }) unsubscribe = () => { relayHub.unsubscribe('feed-subscription') } } catch (err) { console.error('Failed to load feed:', err) error.value = err instanceof Error ? err.message : 'Failed to load feed' isLoading.value = false } } const refreshFeed = () => { posts.value = [] loadFeed() } const startAutoRefresh = () => { if (config.refreshInterval && config.refreshInterval > 0) { refreshTimer = setInterval(refreshFeed, config.refreshInterval) as unknown as number } } const stopAutoRefresh = () => { if (refreshTimer) { clearInterval(refreshTimer) refreshTimer = null } } // Lifecycle onMounted(() => { loadFeed() startAutoRefresh() }) onUnmounted(() => { stopAutoRefresh() if (unsubscribe) { unsubscribe() } }) return { posts: filteredPosts, isLoading, error, refreshFeed, loadFeed } }