From ba30bef94aec805d3ab8908f51d2bf029221d60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20R=C3=A9mond?= Date: Fri, 13 Feb 2026 08:49:28 +0100 Subject: [PATCH] refactor(store): remove `lastInteractedAt` updates on room join --- apps/fluux/src-tauri/tauri.conf.json | 2 +- .../fluux-sdk/src/stores/roomStore.test.ts | 22 ++++++++++++------- packages/fluux-sdk/src/stores/roomStore.ts | 17 +++++--------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/apps/fluux/src-tauri/tauri.conf.json b/apps/fluux/src-tauri/tauri.conf.json index d08540d..d7df32f 100644 --- a/apps/fluux/src-tauri/tauri.conf.json +++ b/apps/fluux/src-tauri/tauri.conf.json @@ -57,7 +57,7 @@ ], "macOS": { "minimumSystemVersion": "10.13", - "bundleVersion": "e0f24f9", + "bundleVersion": "673aeec", "entitlements": "Entitlements.plist", "signingIdentity": null }, diff --git a/packages/fluux-sdk/src/stores/roomStore.test.ts b/packages/fluux-sdk/src/stores/roomStore.test.ts index c91b754..2f9c076 100644 --- a/packages/fluux-sdk/src/stores/roomStore.test.ts +++ b/packages/fluux-sdk/src/stores/roomStore.test.ts @@ -404,7 +404,10 @@ describe('roomStore', () => { expect(room?.notifyAllPersistent).toBe(true) }) - it('should set lastInteractedAt to last message timestamp when joining', () => { + it('should NOT set lastInteractedAt when joining room with messages', () => { + // MUC history arrives before the join confirmation, so room.messages may + // already have content. But these are history messages, not user interaction. + // Only setActiveRoom (user clicking) should set lastInteractedAt. const messageTime = new Date(Date.now() - 30 * 60 * 1000) // 30 minutes ago roomStore.getState().addRoom(createRoom('test@conference.example.com', { @@ -426,13 +429,12 @@ describe('roomStore', () => { const room = roomStore.getState().rooms.get('test@conference.example.com') const meta = roomStore.getState().roomMeta.get('test@conference.example.com') - expect(room?.lastInteractedAt?.getTime()).toBe(messageTime.getTime()) - expect(meta?.lastInteractedAt?.getTime()).toBe(messageTime.getTime()) + // lastInteractedAt should remain undefined - sorting falls back to lastMessage.timestamp + expect(room?.lastInteractedAt).toBeUndefined() + expect(meta?.lastInteractedAt).toBeUndefined() }) it('should NOT set lastInteractedAt when joining room with no messages', () => { - // When joining with no messages (e.g., autojoin before MAM loads), - // lastInteractedAt should remain undefined so sorting falls back to lastMessage.timestamp roomStore.getState().addRoom(createRoom('test@conference.example.com', { joined: false, messages: [], @@ -442,11 +444,10 @@ describe('roomStore', () => { const room = roomStore.getState().rooms.get('test@conference.example.com') - // Should be undefined, not "now" - this allows sorting by lastMessage after MAM loads expect(room?.lastInteractedAt).toBeUndefined() }) - it('should preserve lastInteractedAt when leaving room', () => { + it('should preserve existing lastInteractedAt when joining or leaving', () => { const interactionTime = new Date(Date.now() - 60 * 60 * 1000) // 1 hour ago roomStore.getState().addRoom(createRoom('test@conference.example.com', { @@ -454,9 +455,14 @@ describe('roomStore', () => { lastInteractedAt: interactionTime, })) + // Leave roomStore.getState().setRoomJoined('test@conference.example.com', false) + let room = roomStore.getState().rooms.get('test@conference.example.com') + expect(room?.lastInteractedAt?.getTime()).toBe(interactionTime.getTime()) - const room = roomStore.getState().rooms.get('test@conference.example.com') + // Rejoin + roomStore.getState().setRoomJoined('test@conference.example.com', true) + room = roomStore.getState().rooms.get('test@conference.example.com') expect(room?.lastInteractedAt?.getTime()).toBe(interactionTime.getTime()) }) }) diff --git a/packages/fluux-sdk/src/stores/roomStore.ts b/packages/fluux-sdk/src/stores/roomStore.ts index 47fa449..328fe6b 100644 --- a/packages/fluux-sdk/src/stores/roomStore.ts +++ b/packages/fluux-sdk/src/stores/roomStore.ts @@ -405,14 +405,11 @@ export const roomStore = createStore()( const existing = newRooms.get(roomJid) if (!existing) return state - // When joining, set lastInteractedAt to last message timestamp (if available) - // DON'T fall back to new Date() - that would make all autojoined rooms sort to top - // Instead, leave it undefined so sorting falls back to lastMessage.timestamp from MAM - const lastMessage = existing.messages?.[existing.messages.length - 1] - const newLastInteractedAt = joined - ? (lastMessage?.timestamp ?? existing.lastInteractedAt) // Keep existing or undefined - : existing.lastInteractedAt // Preserve on leave - + // DON'T set lastInteractedAt on join - only setActiveRoom (user clicking) should set it. + // MUC history messages arrive before the join confirmation, so existing.messages may + // contain history whose timestamps don't reflect actual user interaction. + // Leaving lastInteractedAt undefined lets allRooms() fall back to lastMessage.timestamp + // (populated by MAM preview), which correctly reflects each room's latest activity. const updatedRoom = { ...existing, joined, @@ -422,7 +419,6 @@ export const roomStore = createStore()( unreadCount: joined ? existing.unreadCount : 0, mentionsCount: joined ? existing.mentionsCount : 0, notifyAll: joined ? existing.notifyAll : undefined, - lastInteractedAt: newLastInteractedAt, } newRooms.set(roomJid, updatedRoom) @@ -433,7 +429,7 @@ export const roomStore = createStore()( newEntities.set(roomJid, { ...existingEntity, joined, isJoining: false }) } - // Update metadata (unreadCount, mentionsCount, notifyAll, lastInteractedAt) + // Update metadata (unreadCount, mentionsCount, notifyAll) const newMeta = new Map(state.roomMeta) const existingMeta = newMeta.get(roomJid) if (existingMeta) { @@ -442,7 +438,6 @@ export const roomStore = createStore()( unreadCount: joined ? existingMeta.unreadCount : 0, mentionsCount: joined ? existingMeta.mentionsCount : 0, notifyAll: joined ? existingMeta.notifyAll : undefined, - lastInteractedAt: newLastInteractedAt, }) }