import { ref, computed, onMounted, onUnmounted } from 'vue' import { relayHub, type SubscriptionConfig, type RelayStatus } from '../lib/nostr/relayHub' import { config } from '../lib/config' export function useRelayHub() { // Reactive state const isConnected = ref(false) const connectionStatus = ref<'connecting' | 'connected' | 'disconnected' | 'error'>('disconnected') const relayStatuses = ref([]) const error = ref(null) const activeSubscriptions = ref>(new Set()) // Reactive relay counts - these will be updated when relayHub state changes const connectedRelayCount = ref(0) const totalRelayCount = ref(0) const totalSubscriptionCount = ref(0) // Reactive subscription details const subscriptionDetails = ref>([]) // Computed properties const connectionHealth = computed(() => { if (totalRelayCount.value === 0) return 0 return (connectedRelayCount.value / totalRelayCount.value) * 100 }) // Initialize relay hub const initialize = async (): Promise => { try { connectionStatus.value = 'connecting' error.value = null // Get relay URLs from config const relayUrls = config.nostr.relays if (!relayUrls || relayUrls.length === 0) { throw new Error('No relay URLs configured') } // Initialize the relay hub await relayHub.initialize(relayUrls) // Set up event listeners setupEventListeners() connectionStatus.value = 'connected' isConnected.value = true console.log('RelayHub initialized successfully') } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to initialize RelayHub') error.value = errorObj connectionStatus.value = 'error' isConnected.value = false console.error('Failed to initialize RelayHub:', errorObj) throw errorObj } } // Connect to relays const connect = async (): Promise => { try { if (!relayHub.isInitialized) { await initialize() return } connectionStatus.value = 'connecting' error.value = null await relayHub.connect() connectionStatus.value = 'connected' isConnected.value = true } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to connect') error.value = errorObj connectionStatus.value = 'error' isConnected.value = false throw errorObj } } // Disconnect from relays const disconnect = (): void => { relayHub.disconnect() connectionStatus.value = 'disconnected' isConnected.value = false error.value = null } // Subscribe to events const subscribe = (config: SubscriptionConfig): (() => void) => { try { const unsubscribe = relayHub.subscribe(config) activeSubscriptions.value.add(config.id) // Return enhanced unsubscribe function return () => { unsubscribe() activeSubscriptions.value.delete(config.id) } } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to subscribe') error.value = errorObj throw errorObj } } // Publish an event const publishEvent = async (event: any): Promise<{ success: number; total: number }> => { try { return await relayHub.publishEvent(event) } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to publish event') error.value = errorObj throw errorObj } } // Query events (one-time fetch) const queryEvents = async (filters: any[], relays?: string[]): Promise => { try { return await relayHub.queryEvents(filters, relays) } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to query events') error.value = errorObj throw errorObj } } // Force reconnection const reconnect = async (): Promise => { try { connectionStatus.value = 'connecting' error.value = null await relayHub.reconnect() connectionStatus.value = 'connected' isConnected.value = true } catch (err) { const errorObj = err instanceof Error ? err : new Error('Failed to reconnect') error.value = errorObj connectionStatus.value = 'error' isConnected.value = false throw errorObj } } // Get relay status const getRelayStatus = (url: string): RelayStatus | undefined => { return relayStatuses.value.find(status => status.url === url) } // Check if a specific relay is connected const isRelayConnected = (url: string): boolean => { return relayHub.isRelayConnected(url) } // Set up event listeners for relay hub events const setupEventListeners = (): void => { relayHub.on('connected', (count: number) => { console.log('Connected to relays:', count) isConnected.value = true connectionStatus.value = 'connected' error.value = null connectedRelayCount.value = count totalRelayCount.value = relayHub.totalRelayCount totalSubscriptionCount.value = relayHub.totalSubscriptionCount }) relayHub.on('disconnected', () => { console.log('Disconnected from all relays') isConnected.value = false connectionStatus.value = 'disconnected' error.value = null connectedRelayCount.value = 0 totalSubscriptionCount.value = 0 }) relayHub.on('connectionError', (err: Error) => { console.error('Connection error:', err) error.value = err connectionStatus.value = 'error' isConnected.value = false connectedRelayCount.value = 0 }) relayHub.on('allRelaysDisconnected', () => { console.warn('All relays disconnected') isConnected.value = false connectionStatus.value = 'disconnected' connectedRelayCount.value = 0 }) relayHub.on('partialDisconnection', ({ connected, total }: { connected: number; total: number }) => { console.warn(`Partial disconnection: ${connected}/${total} relays connected`) isConnected.value = connected > 0 connectionStatus.value = connected > 0 ? 'connected' : 'disconnected' connectedRelayCount.value = connected totalRelayCount.value = total }) relayHub.on('maxReconnectAttemptsReached', () => { console.error('Max reconnection attempts reached') connectionStatus.value = 'error' isConnected.value = false error.value = new Error('Max reconnection attempts reached') connectedRelayCount.value = 0 }) relayHub.on('networkOffline', () => { console.log('Network went offline') connectionStatus.value = 'disconnected' isConnected.value = false connectedRelayCount.value = 0 }) // Subscription events relayHub.on('subscriptionCreated', ({ count }: { id: string; count: number }) => { console.log('Subscription created, total count:', count) totalSubscriptionCount.value = count }) relayHub.on('subscriptionRemoved', ({ count }: { id: string; count: number }) => { console.log('Subscription removed, total count:', count) totalSubscriptionCount.value = count }) // Update relay statuses periodically const updateRelayStatuses = () => { relayStatuses.value = relayHub.relayStatuses // Also update the reactive counts to keep them in sync connectedRelayCount.value = relayHub.connectedRelayCount totalRelayCount.value = relayHub.totalRelayCount totalSubscriptionCount.value = relayHub.totalSubscriptionCount subscriptionDetails.value = relayHub.subscriptionDetails } // Update immediately and then every 10 seconds updateRelayStatuses() const statusInterval = setInterval(updateRelayStatuses, 10000) // Cleanup interval on unmount onUnmounted(() => { clearInterval(statusInterval) }) } // Cleanup function const cleanup = (): void => { // Close all active subscriptions activeSubscriptions.value.forEach(subId => { relayHub.unsubscribe(subId) }) activeSubscriptions.value.clear() } // Auto-initialize on mount if config is available onMounted(async () => { try { if (config.nostr.relays && config.nostr.relays.length > 0) { await initialize() } } catch (err) { console.warn('Auto-initialization failed:', err) } }) // Cleanup on unmount onUnmounted(() => { cleanup() }) return { // State isConnected, connectionStatus, relayStatuses, error, activeSubscriptions, // Computed connectedRelayCount, totalRelayCount, totalSubscriptionCount, subscriptionDetails, connectionHealth, // Methods initialize, connect, disconnect, subscribe, publishEvent, queryEvents, reconnect, getRelayStatus, isRelayConnected, cleanup } }