diff --git a/src/modules/chat/services/chat-service.ts b/src/modules/chat/services/chat-service.ts index 0aa52de..7f0b829 100644 --- a/src/modules/chat/services/chat-service.ts +++ b/src/modules/chat/services/chat-service.ts @@ -22,6 +22,8 @@ export class ChatService extends BaseService { private subscriptionUnsubscriber?: () => void private marketMessageHandler?: (event: any) => Promise private visibilityUnsubscribe?: () => void + private isFullyInitialized = false + private authCheckInterval?: ReturnType constructor(config: ChatConfig) { super() @@ -38,20 +40,58 @@ export class ChatService extends BaseService { * Service-specific initialization (called by BaseService) */ protected async onInitialize(): Promise { - // Check if we have user pubkey - if (!this.authService?.user?.value?.pubkey) { - this.debug('User not authenticated yet, deferring full initialization') + this.debug('Chat service onInitialize called') + + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey + + this.debug('Auth detection:', { + hasAuthService: !!hasAuthService, + hasGlobalAuth: !!hasGlobalAuth, + authServicePubkey: hasAuthService ? hasAuthService.substring(0, 10) + '...' : null, + globalAuthPubkey: hasGlobalAuth ? hasGlobalAuth.substring(0, 10) + '...' : null + }) + + if (!hasAuthService && !hasGlobalAuth) { + this.debug('User not authenticated yet, deferring full initialization with periodic check') // Listen for auth events to complete initialization when user logs in const unsubscribe = eventBus.on('auth:login', async () => { this.debug('Auth login detected, completing chat initialization...') unsubscribe() + if (this.authCheckInterval) { + clearInterval(this.authCheckInterval) + this.authCheckInterval = undefined + } + // Re-inject dependencies and complete initialization await this.waitForDependencies() await this.completeInitialization() }) + // Also check periodically in case we missed the auth event + this.authCheckInterval = setInterval(async () => { + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey + + if (hasAuthService || hasGlobalAuth) { + this.debug('Auth detected via periodic check, completing initialization') + + if (this.authCheckInterval) { + clearInterval(this.authCheckInterval) + this.authCheckInterval = undefined + } + + unsubscribe() + await this.waitForDependencies() + await this.completeInitialization() + } + }, 2000) // Check every 2 seconds + return } @@ -62,6 +102,13 @@ export class ChatService extends BaseService { * Complete the initialization once all dependencies are available */ private async completeInitialization(): Promise { + if (this.isFullyInitialized) { + this.debug('Chat service already fully initialized, skipping') + return + } + + this.debug('Completing chat service initialization...') + // Load peers from storage first this.loadPeersFromStorage() @@ -76,13 +123,14 @@ export class ChatService extends BaseService { // Register with visibility service this.registerWithVisibilityService() + this.isFullyInitialized = true this.debug('Chat service fully initialized and ready!') } // Initialize message handling (subscription + history loading) async initializeMessageHandling(): Promise { // Set up real-time subscription - this.setupMessageSubscription() + await this.setupMessageSubscription() // Load message history for known peers await this.loadMessageHistory() @@ -198,14 +246,29 @@ export class ChatService extends BaseService { // Refresh peers from API async refreshPeers(): Promise { + // Check if we should trigger full initialization + const { auth } = await import('@/composables/useAuth') + const hasAuth = this.authService?.user?.value?.pubkey || auth.currentUser.value?.pubkey + + if (!this.isFullyInitialized && hasAuth) { + console.log('💬 Refresh peers triggered full initialization') + await this.completeInitialization() + } + return this.loadPeersFromAPI() } // Check if services are available for messaging - private checkServicesAvailable(): { relayHub: any; authService: any } | null { - // Dependencies are already injected by BaseService + private async checkServicesAvailable(): Promise<{ relayHub: any; authService: any; userPubkey: string; userPrivkey: string } | null> { + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.prvkey + const hasGlobalAuth = auth.currentUser.value?.prvkey - if (!this.relayHub || !this.authService?.user?.value?.prvkey) { + const userPubkey = hasAuthService ? this.authService.user.value.pubkey : auth.currentUser.value?.pubkey + const userPrivkey = hasAuthService ? this.authService.user.value.prvkey : auth.currentUser.value?.prvkey + + if (!this.relayHub || (!hasAuthService && !hasGlobalAuth)) { return null } @@ -213,22 +276,24 @@ export class ChatService extends BaseService { return null } - return { relayHub: this.relayHub, authService: this.authService } + return { + relayHub: this.relayHub, + authService: this.authService || auth, + userPubkey: userPubkey!, + userPrivkey: userPrivkey! + } } // Send a message async sendMessage(peerPubkey: string, content: string): Promise { try { - const services = this.checkServicesAvailable() + const services = await this.checkServicesAvailable() if (!services) { throw new Error('Chat services not ready. Please wait for connection to establish.') } - const { relayHub, authService } = services - - const userPrivkey = authService.user.value.prvkey - const userPubkey = authService.user.value.pubkey + const { relayHub, userPrivkey, userPubkey } = services // Encrypt the message using NIP-04 const encryptedContent = await nip04.encrypt(userPrivkey, peerPubkey, content) @@ -378,15 +443,23 @@ export class ChatService extends BaseService { // Load message history for known peers private async loadMessageHistory(): Promise { try { - // Dependencies are already injected by BaseService + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey - if (!this.relayHub || !this.authService?.user?.value?.pubkey) { + const userPubkey = hasAuthService ? this.authService.user.value.pubkey : auth.currentUser.value?.pubkey + const userPrivkey = hasAuthService ? this.authService.user.value.prvkey : auth.currentUser.value?.prvkey + + if (!this.relayHub || (!hasAuthService && !hasGlobalAuth)) { console.warn('Cannot load message history: missing services') return } - const userPubkey = this.authService.user.value.pubkey - const userPrivkey = this.authService.user.value.prvkey + if (!userPubkey || !userPrivkey) { + console.warn('Cannot load message history: missing user keys') + return + } const peerPubkeys = Array.from(this.peers.value.keys()) if (peerPubkeys.length === 0) { @@ -459,12 +532,26 @@ export class ChatService extends BaseService { } // Setup subscription for incoming messages - private setupMessageSubscription(): void { + private async setupMessageSubscription(): Promise { try { - // Dependencies are already injected by BaseService + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey + const userPubkey = hasAuthService ? this.authService.user.value.pubkey : auth.currentUser.value?.pubkey - if (!this.relayHub || !this.authService?.user?.value?.pubkey) { + this.debug('Setup message subscription auth check:', { + hasAuthService: !!hasAuthService, + hasGlobalAuth: !!hasGlobalAuth, + hasRelayHub: !!this.relayHub, + relayHubConnected: this.relayHub?.isConnected, + userPubkey: userPubkey ? userPubkey.substring(0, 10) + '...' : null + }) + + if (!this.relayHub || (!hasAuthService && !hasGlobalAuth)) { console.warn('💬 Cannot setup message subscription: missing services') + // Retry after 2 seconds + setTimeout(() => this.setupMessageSubscription(), 2000) return } @@ -475,10 +562,16 @@ export class ChatService extends BaseService { console.log('💬 RelayHub connected, setting up message subscription...') this.setupMessageSubscription() }) + // Also retry after timeout in case event is missed + setTimeout(() => this.setupMessageSubscription(), 5000) return } - const userPubkey = this.authService.user.value.pubkey + if (!userPubkey) { + console.warn('💬 No user pubkey available for subscription') + setTimeout(() => this.setupMessageSubscription(), 2000) + return + } // Subscribe to encrypted direct messages (kind 4) addressed to this user this.subscriptionUnsubscriber = this.relayHub.subscribe({ @@ -498,14 +591,16 @@ export class ChatService extends BaseService { await this.processIncomingMessage(event) }, onEose: () => { - console.log('Chat message subscription EOSE received') + console.log('💬 Chat message subscription EOSE received') } }) - console.log('Chat message subscription set up successfully') + console.log('💬 Chat message subscription set up successfully for pubkey:', userPubkey.substring(0, 10) + '...') } catch (error) { - console.error('Failed to setup message subscription:', error) + console.error('💬 Failed to setup message subscription:', error) + // Retry after delay + setTimeout(() => this.setupMessageSubscription(), 3000) } } @@ -575,8 +670,13 @@ export class ChatService extends BaseService { */ private async processIncomingMessage(event: any): Promise { try { - const userPubkey = this.authService?.user?.value?.pubkey - const userPrivkey = this.authService?.user?.value?.prvkey + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey + + const userPubkey = hasAuthService ? this.authService.user.value.pubkey : auth.currentUser.value?.pubkey + const userPrivkey = hasAuthService ? this.authService.user.value.prvkey : auth.currentUser.value?.prvkey if (!userPubkey || !userPrivkey) { console.warn('Cannot process message: user not authenticated') @@ -637,7 +737,13 @@ export class ChatService extends BaseService { * Load recent messages for a specific peer */ private async loadRecentMessagesForPeer(peerPubkey: string): Promise { - const userPubkey = this.authService?.user?.value?.pubkey + // Check both injected auth service AND global auth composable + const { auth } = await import('@/composables/useAuth') + const hasAuthService = this.authService?.user?.value?.pubkey + const hasGlobalAuth = auth.currentUser.value?.pubkey + + const userPubkey = hasAuthService ? this.authService.user.value.pubkey : auth.currentUser.value?.pubkey + if (!userPubkey || !this.relayHub) return try { @@ -675,6 +781,12 @@ export class ChatService extends BaseService { * Cleanup when service is disposed (overrides BaseService) */ protected async onDispose(): Promise { + // Clear auth check interval + if (this.authCheckInterval) { + clearInterval(this.authCheckInterval) + this.authCheckInterval = undefined + } + // Unregister from visibility service if (this.visibilityUnsubscribe) { this.visibilityUnsubscribe() @@ -689,6 +801,7 @@ export class ChatService extends BaseService { this.messages.value.clear() this.peers.value.clear() + this.isFullyInitialized = false this.debug('Chat service disposed') }