diff --git a/src/components/nostr/ChatComponent.vue b/src/components/nostr/ChatComponent.vue index 3697882..7757778 100644 --- a/src/components/nostr/ChatComponent.vue +++ b/src/components/nostr/ChatComponent.vue @@ -412,7 +412,10 @@ const { markMessagesAsRead, getUnreadCount, totalUnreadCount, - getLatestMessageTimestamp + getLatestMessageTimestamp, + clearAllUnreadCounts, + debugUnreadData, + getUnreadData } = nostrChat // Computed @@ -602,6 +605,61 @@ const getPeerInitials = (peer: Peer) => { return peer.pubkey.slice(0, 2).toUpperCase() } +// Debug function to reset unread counts (can be called from browser console) +const debugResetUnreadCounts = () => { + console.log('🔧 Debug: Resetting all unread counts...') + clearAllUnreadCounts() + console.log('🔧 Debug: Unread counts reset. You may need to refresh the page to see the changes.') +} + +// Debug function to show unread count details for a specific peer +const debugPeerUnreadCounts = (peerPubkey?: string) => { + if (peerPubkey) { + console.log(`🔧 Debug: Unread count details for peer ${peerPubkey}:`) + debugUnreadData(peerPubkey) + console.log(`Current unread count: ${getUnreadCount(peerPubkey)}`) + + // Show timestamp details for debugging + const peerMessages = messages.value.get(peerPubkey) || [] + const unreadData = getUnreadData(peerPubkey) + console.log(`Last read timestamp: ${unreadData.lastReadTimestamp} (${new Date(unreadData.lastReadTimestamp * 1000).toLocaleString()})`) + console.log(`Total messages: ${peerMessages.length}`) + + // Show messages that would count as unread + const unreadMessages = peerMessages.filter(msg => !msg.sent && msg.created_at > unreadData.lastReadTimestamp) + console.log(`Messages that count as unread: ${unreadMessages.length}`) + unreadMessages.forEach(msg => { + console.log(` - ${msg.content.substring(0, 50)}... (${new Date(msg.created_at * 1000).toLocaleString()})`) + }) + } else { + console.log('🔧 Debug: All unread counts:') + console.log('Total unread count:', totalUnreadCount.value) + // Simplified peer iteration to avoid TypeScript issues + const peerList = peers.value + if (peerList && peerList.length > 0) { + peerList.forEach((peer: any) => { + const count = getUnreadCount(peer.pubkey) + if (count > 0) { + const name = peer.username || peer.pubkey.slice(0, 8) + console.log(`${name}: ${count}`) + } + }) + } + } +} + +// Make debug functions available globally for browser console access +if (typeof window !== 'undefined') { + // Use type assertion to avoid TypeScript errors + const globalWindow = window as any + globalWindow.debugResetUnreadCounts = debugResetUnreadCounts + globalWindow.debugPeerUnreadCounts = debugPeerUnreadCounts + + console.log('🔧 Debug functions available:') + console.log(' - debugResetUnreadCounts() - reset all unread counts') + console.log(' - debugPeerUnreadCounts(peerPubkey?) - show unread count details') +} + // Lifecycle onMounted(async () => { checkMobile() diff --git a/src/composables/useNostrChat.ts b/src/composables/useNostrChat.ts index 6b9c76e..7df01d9 100644 --- a/src/composables/useNostrChat.ts +++ b/src/composables/useNostrChat.ts @@ -189,43 +189,88 @@ export function useNostrChat() { // Update unread count for a peer const updateUnreadCount = (peerPubkey: string, count: number): void => { - const current = unreadCounts.value.get(peerPubkey) || 0 - unreadCounts.value.set(peerPubkey, current + count) + if (count > 0) { + unreadCounts.value.set(peerPubkey, count) + } else { + unreadCounts.value.delete(peerPubkey) + } + // Force reactivity + unreadCounts.value = new Map(unreadCounts.value) // Save to localStorage const unreadData = getUnreadData(peerPubkey) - unreadData.unreadCount = current + count + unreadData.unreadCount = count saveUnreadData(peerPubkey, unreadData) } // Mark messages as read for a peer const markMessagesAsRead = (peerPubkey: string): void => { - const current = unreadCounts.value.get(peerPubkey) || 0 - if (current > 0) { - unreadCounts.value.set(peerPubkey, 0) - - // Save to localStorage - const unreadData = getUnreadData(peerPubkey) - unreadData.unreadCount = 0 - unreadData.lastReadTimestamp = Date.now() - saveUnreadData(peerPubkey, unreadData) + const currentTimestamp = Math.floor(Date.now() / 1000) + + // Update last read timestamp, reset unread count, and clear processed message IDs + const updatedData: UnreadMessageData = { + lastReadTimestamp: currentTimestamp, + unreadCount: 0, + processedMessageIds: new Set() // Clear processed messages when marking as read } + + saveUnreadData(peerPubkey, updatedData) + updateUnreadCount(peerPubkey, 0) + + // Also clear any processed message IDs from the global set that might be from this peer + // This helps prevent duplicate message issues + console.log(`Marked messages as read for peer: ${peerPubkey}`) } // Load unread counts from localStorage - // const loadUnreadCounts = (): void => { - // try { - // // Load unread counts for all peers we have messages for - // for (const [peerPubkey] of messages.value) { - // const unreadData = getUnreadData(peerPubkey) - // if (unreadData.unreadCount > 0) { - // unreadCounts.value.set(peerPubkey, unreadData.unreadCount) - // } - // } - // } catch (error) { - // console.warn('Failed to load unread counts:', error) - // } - // } + const loadUnreadCounts = (): void => { + try { + const keys = Object.keys(localStorage).filter(key => + key.startsWith(`${UNREAD_MESSAGES_KEY}-`) + ) + + console.log('Loading unread counts from localStorage. Found keys:', keys) + + for (const key of keys) { + const peerPubkey = key.replace(`${UNREAD_MESSAGES_KEY}-`, '') + const unreadData = getUnreadData(peerPubkey) + + // Recalculate unread count based on actual messages and lastReadTimestamp + const peerMessages = messages.value.get(peerPubkey) || [] + let actualUnreadCount = 0 + + for (const message of peerMessages) { + // Only count messages not sent by us and created after last read timestamp + if (!message.sent && message.created_at > unreadData.lastReadTimestamp) { + actualUnreadCount++ + } + } + + // Update the stored count to match reality + if (actualUnreadCount !== unreadData.unreadCount) { + console.log(`Correcting unread count for peer ${peerPubkey}: stored=${unreadData.unreadCount}, actual=${actualUnreadCount}`) + unreadData.unreadCount = actualUnreadCount + saveUnreadData(peerPubkey, unreadData) + } + + console.log(`Peer ${peerPubkey}:`, { + lastReadTimestamp: unreadData.lastReadTimestamp, + unreadCount: unreadData.unreadCount, + processedMessageIdsCount: unreadData.processedMessageIds.size, + messageCount: peerMessages.length + }) + + if (actualUnreadCount > 0) { + unreadCounts.value.set(peerPubkey, actualUnreadCount) + } + } + } catch (error) { + console.warn('Failed to load unread counts:', error) + } + } + + // Initialize unread counts on startup + loadUnreadCounts() // Clear unread count for a peer // const clearUnreadCount = (peerPubkey: string): void => { @@ -247,6 +292,22 @@ export function useNostrChat() { unreadData.unreadCount = 0 saveUnreadData(peerPubkey, unreadData) } + + // Also clear from localStorage for all stored keys + try { + const keys = Object.keys(localStorage).filter(key => + key.startsWith(`${UNREAD_MESSAGES_KEY}-`) + ) + + for (const key of keys) { + const peerPubkey = key.replace(`${UNREAD_MESSAGES_KEY}-`, '') + const unreadData = getUnreadData(peerPubkey) + unreadData.unreadCount = 0 + saveUnreadData(peerPubkey, unreadData) + } + } catch (error) { + console.warn('Failed to clear unread counts from localStorage:', error) + } } // Clear processed message IDs for a peer @@ -509,6 +570,12 @@ export function useNostrChat() { return } + // Check if we've already processed this message to prevent duplicates + if (processedMessageIds.value.has(event.id)) { + console.log('Message already processed, skipping:', event.id) + return + } + try { // For NIP-04 direct messages, always use peerPubkey as the second argument // This is the public key of the other party in the conversation @@ -550,17 +617,36 @@ export function useNostrChat() { } messages.value.get(peerPubkey)!.push(message) - // Mark as unread if not sent by us + // Mark as unread if not sent by us AND created after last read timestamp if (!isSentByMe) { - // For now, just update the unread count - // TODO: Implement proper unread message tracking - const currentCount = unreadCounts.value.get(peerPubkey) || 0 - updateUnreadCount(peerPubkey, currentCount + 1) + const unreadData = getUnreadData(peerPubkey) + + // Only count as unread if message was created after last read timestamp + if (event.created_at > unreadData.lastReadTimestamp) { + // Increment the unread count for this peer + const currentCount = unreadCounts.value.get(peerPubkey) || 0 + const newCount = currentCount + 1 + unreadCounts.value.set(peerPubkey, newCount) + + // Force reactivity + unreadCounts.value = new Map(unreadCounts.value) + + // Save to localStorage + unreadData.unreadCount = newCount + saveUnreadData(peerPubkey, unreadData) + + console.log(`Message marked as unread for peer ${peerPubkey}. Count: ${newCount}`) + } else { + console.log(`Message not marked as unread (created before last read): ${event.created_at} <= ${unreadData.lastReadTimestamp}`) + } } // Update latest message timestamp updateLatestMessageTimestamp(peerPubkey, event.created_at) + // Mark this message as processed to prevent duplicates + processedMessageIds.value.add(event.id) + // Trigger callback if set if (onMessageAdded.value) { onMessageAdded.value(peerPubkey) @@ -813,6 +899,7 @@ export function useNostrChat() { clearAllUnreadCounts, clearProcessedMessageIds, debugUnreadData, + getUnreadData, // Timestamp methods (for sorting) getLatestMessageTimestamp,