Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/fluux/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
],
"macOS": {
"minimumSystemVersion": "10.13",
"bundleVersion": "e0f24f9",
"bundleVersion": "673aeec",
"entitlements": "Entitlements.plist",
"signingIdentity": null
},
Expand Down
22 changes: 14 additions & 8 deletions packages/fluux-sdk/src/stores/roomStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand All @@ -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: [],
Expand All @@ -442,21 +444,25 @@ 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', {
joined: true,
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())
})
})
Expand Down
17 changes: 6 additions & 11 deletions packages/fluux-sdk/src/stores/roomStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,11 @@ export const roomStore = createStore<RoomState>()(
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,
Expand All @@ -422,7 +419,6 @@ export const roomStore = createStore<RoomState>()(
unreadCount: joined ? existing.unreadCount : 0,
mentionsCount: joined ? existing.mentionsCount : 0,
notifyAll: joined ? existing.notifyAll : undefined,
lastInteractedAt: newLastInteractedAt,
}
newRooms.set(roomJid, updatedRoom)

Expand All @@ -433,7 +429,7 @@ export const roomStore = createStore<RoomState>()(
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) {
Expand All @@ -442,7 +438,6 @@ export const roomStore = createStore<RoomState>()(
unreadCount: joined ? existingMeta.unreadCount : 0,
mentionsCount: joined ? existingMeta.mentionsCount : 0,
notifyAll: joined ? existingMeta.notifyAll : undefined,
lastInteractedAt: newLastInteractedAt,
})
}

Expand Down
Loading