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()) // Computed properties const connectedRelayCount = computed(() => relayHub.connectedRelayCount) const totalRelayCount = computed(() => relayHub.totalRelayCount) const totalSubscriptionCount = computed(() => relayHub.totalSubscriptionCount) 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) } // Setup event listeners for the relay hub const setupEventListeners = (): void => { // Connection events relayHub.on('connected', (count: number) => { console.log(`Connected to ${count} relays`) isConnected.value = true connectionStatus.value = 'connected' error.value = null }) relayHub.on('disconnected', () => { console.log('Disconnected from all relays') isConnected.value = false connectionStatus.value = 'disconnected' }) relayHub.on('connectionError', (err: Error) => { console.error('Connection error:', err) error.value = err connectionStatus.value = 'error' isConnected.value = false }) relayHub.on('allRelaysDisconnected', () => { console.warn('All relays disconnected') isConnected.value = false connectionStatus.value = 'disconnected' }) 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' }) relayHub.on('maxReconnectAttemptsReached', () => { console.error('Max reconnection attempts reached') connectionStatus.value = 'error' isConnected.value = false error.value = new Error('Max reconnection attempts reached') }) relayHub.on('networkOffline', () => { console.log('Network went offline') connectionStatus.value = 'disconnected' isConnected.value = false }) // Update relay statuses periodically const updateRelayStatuses = () => { relayStatuses.value = relayHub.relayStatuses } // 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, connectionHealth, // Methods initialize, connect, disconnect, subscribe, publishEvent, queryEvents, reconnect, getRelayStatus, isRelayConnected, cleanup } }