From b0101915c7a270a952c4b897a12a0cdd4d70de54 Mon Sep 17 00:00:00 2001 From: padreug Date: Fri, 8 Aug 2025 23:19:35 +0200 Subject: [PATCH] feat: Implement sorting of peers by latest message timestamp and unread status in Nostr chat - Introduce a computed property to sort peers based on the latest message timestamp and unread message count, enhancing the user experience by prioritizing relevant conversations. - Add methods to track and retrieve the latest message timestamp for each peer, ensuring accurate sorting. - Update the ChatComponent to utilize the new sorting logic, improving the display of peers in the chat interface. refactor: Reorganize fuzzy search and mobile detection logic in ChatComponent - Move fuzzy search implementation and mobile detection methods to improve code clarity and maintainability. - Ensure consistent functionality for searching peers by username or pubkey with typo tolerance. - Maintain mobile navigation logic for better user experience on smaller devices. --- src/components/nostr/ChatComponent.vue | 79 +++++++++++++++++++------- src/composables/useNostrChat.ts | 35 +++++++++++- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/components/nostr/ChatComponent.vue b/src/components/nostr/ChatComponent.vue index 63ca539..07fa4a8 100644 --- a/src/components/nostr/ChatComponent.vue +++ b/src/components/nostr/ChatComponent.vue @@ -401,6 +401,62 @@ const scrollTarget = ref(null) // Mobile detection const isMobile = ref(false) +// Nostr chat composable +const { + isConnected, + messages, + connect, + disconnect, + subscribeToPeer, + subscribeToPeerForNotifications, + sendMessage: sendNostrMessage, + onMessageAdded, + markMessagesAsRead, + getUnreadCount, + getTotalUnreadCount, + getLatestMessageTimestamp +} = useNostrChat() + +// Computed +const currentMessages = computed(() => { + if (!selectedPeer.value) return [] + return messages.value.get(selectedPeer.value.pubkey) || [] +}) + +// Sort peers by latest message timestamp (newest first) and unread status +const sortedPeers = computed(() => { + const sorted = [...peers.value].sort((a, b) => { + const aTimestamp = getLatestMessageTimestamp(a.pubkey) + const bTimestamp = getLatestMessageTimestamp(b.pubkey) + const aUnreadCount = getUnreadCount(a.pubkey) + const bUnreadCount = getUnreadCount(b.pubkey) + + // First, sort by unread count (peers with unread messages appear first) + if (aUnreadCount > 0 && bUnreadCount === 0) return -1 + if (aUnreadCount === 0 && bUnreadCount > 0) return 1 + + // Then, sort by latest message timestamp (newest first) + if (aTimestamp !== bTimestamp) { + return bTimestamp - aTimestamp + } + + // Finally, sort alphabetically by username for peers with same timestamp + return (a.username || '').localeCompare(b.username || '') + }) + + // Debug logging (only in development) + if (process.env.NODE_ENV === 'development' && sorted.length > 0) { + console.log('Sorted peers:', sorted.map(p => ({ + username: p.username, + pubkey: p.pubkey.slice(0, 8) + '...', + latestTimestamp: getLatestMessageTimestamp(p.pubkey), + unreadCount: getUnreadCount(p.pubkey) + }))) + } + + return sorted +}) + // Fuzzy search for peers // This integrates the useFuzzySearch composable to provide intelligent search functionality // for finding peers by username or pubkey with typo tolerance and scoring @@ -410,7 +466,7 @@ const { isSearching, resultCount, clearSearch -} = useFuzzySearch(peers, { +} = useFuzzySearch(sortedPeers, { fuseOptions: { keys: [ { name: 'username', weight: 0.7 }, // Username has higher weight for better UX @@ -442,27 +498,6 @@ const goBackToPeers = () => { selectedPeer.value = null } -// Nostr chat composable -const { - isConnected, - messages, - connect, - disconnect, - subscribeToPeer, - subscribeToPeerForNotifications, - sendMessage: sendNostrMessage, - onMessageAdded, - markMessagesAsRead, - getUnreadCount, - getTotalUnreadCount -} = useNostrChat() - -// Computed -const currentMessages = computed(() => { - if (!selectedPeer.value) return [] - return messages.value.get(selectedPeer.value.pubkey) || [] -}) - // Methods const loadPeers = async () => { try { diff --git a/src/composables/useNostrChat.ts b/src/composables/useNostrChat.ts index fc71849..24f66de 100644 --- a/src/composables/useNostrChat.ts +++ b/src/composables/useNostrChat.ts @@ -74,6 +74,9 @@ export function useNostrChat() { // Reactive unread counts const unreadCounts = ref>(new Map()) + + // Track latest message timestamp for each peer (for sorting) + const latestMessageTimestamps = ref>(new Map()) // Computed const isLoggedIn = computed(() => !!currentUser.value) @@ -97,6 +100,26 @@ export function useNostrChat() { return total } + // Get latest message timestamp for a peer + const getLatestMessageTimestamp = (peerPubkey: string): number => { + return latestMessageTimestamps.value.get(peerPubkey) || 0 + } + + // Get all latest message timestamps + const getAllLatestMessageTimestamps = (): Map => { + return new Map(latestMessageTimestamps.value) + } + + // Update latest message timestamp for a peer + const updateLatestMessageTimestamp = (peerPubkey: string, timestamp: number): void => { + const currentLatest = latestMessageTimestamps.value.get(peerPubkey) || 0 + if (timestamp > currentLatest) { + latestMessageTimestamps.value.set(peerPubkey, timestamp) + // Force reactivity + latestMessageTimestamps.value = new Map(latestMessageTimestamps.value) + } + } + // Update unread count for a peer const updateUnreadCount = (peerPubkey: string, count: number): void => { if (count > 0) { @@ -584,6 +607,9 @@ export function useNostrChat() { // Force reactivity by triggering a change messages.value = new Map(messages.value) + // Update latest message timestamp for this peer (for sorting) + updateLatestMessageTimestamp(peerPubkey, message.created_at) + // Track unread messages (only for received messages, not sent ones) if (!isSentByMe) { const unreadData = getUnreadData(peerPubkey) @@ -748,6 +774,9 @@ export function useNostrChat() { // Force reactivity by triggering a change messages.value = new Map(messages.value) + // Update latest message timestamp for this peer (for sorting) + updateLatestMessageTimestamp(peerPubkey, message.created_at) + // Trigger callback if set if (onMessageAdded.value) { onMessageAdded.value(peerPubkey) @@ -792,6 +821,10 @@ export function useNostrChat() { getTotalUnreadCount, clearAllUnreadCounts, clearProcessedMessageIds, - debugUnreadData + debugUnreadData, + + // Timestamp methods (for sorting) + getLatestMessageTimestamp, + getAllLatestMessageTimestamps } } \ No newline at end of file