diff --git a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-unread-toast.spec.ts b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-unread-toast.spec.ts index c288e3a206..d469bae40e 100644 --- a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-unread-toast.spec.ts +++ b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-unread-toast.spec.ts @@ -181,15 +181,6 @@ test.describe("Room list unread activity toast", () => { // Wait until the collapsed Chats header has been pushed offscreen (all favourites synced). await expect(chatsHeader).not.toBeInViewport(); - // Tagging rooms into the Favourites section raises a transient "Chat moved" toast, which - // shares the single toast slot with — and takes precedence over — the unread-activity toast - // (see RoomListView). Dismiss it via its close button so the unread toast can surface; by now - // all favourites have synced, so it will not re-appear. - const chatMovedToast = page.getByText("Chat moved"); - await expect(chatMovedToast).toBeVisible(); - await page.getByRole("button", { name: "Close" }).click(); - await expect(chatMovedToast).not.toBeVisible(); - // The collapsed Chats header is offscreen, but its hidden notification raises the toast. await expect(getToast(page)).toBeVisible(); diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-collapsed-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-collapsed-linux.png index e227652042..5896c11bee 100644 Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-collapsed-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-collapsed-linux.png differ diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-linux.png index 798d3e2038..d4b1cbb7c6 100644 Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-sections.spec.ts/room-list-sections-linux.png differ diff --git a/apps/web/src/stores/room-list-v3/RoomListStoreV3.ts b/apps/web/src/stores/room-list-v3/RoomListStoreV3.ts index 00c14be075..7ebf0d6ad7 100644 --- a/apps/web/src/stores/room-list-v3/RoomListStoreV3.ts +++ b/apps/web/src/stores/room-list-v3/RoomListStoreV3.ts @@ -272,8 +272,16 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient { } case "MatrixActions.Room.tags": { + // Re-sort on any tag change, but don't emit ROOM_TAGGED_EVENT here: the js-sdk + // re-emits RoomEvent.Tags for every m.tag on every sync, which would show a spurious + // "chat moved" toast on load. It is emitted from tagRoom.success below instead. const room = payload.room; this.addRoomAndEmit(room); + break; + } + + case "RoomListActions.tagRoom.success": { + // Tag change initiated by the local user, so surface the "chat moved" toast. this.emit(ROOM_TAGGED_EVENT); break; } diff --git a/apps/web/test/unit-tests/stores/room-list-v3/RoomListStoreV3-test.ts b/apps/web/test/unit-tests/stores/room-list-v3/RoomListStoreV3-test.ts index 9878a4bb40..2ca3200812 100644 --- a/apps/web/test/unit-tests/stores/room-list-v3/RoomListStoreV3-test.ts +++ b/apps/web/test/unit-tests/stores/room-list-v3/RoomListStoreV3-test.ts @@ -13,6 +13,7 @@ import type { MatrixClient } from "matrix-js-sdk/src/matrix"; import type { RoomNotificationState } from "../../../../src/stores/notifications/RoomNotificationState"; import { LISTS_UPDATE_EVENT, + ROOM_TAGGED_EVENT, SECTION_CREATED_EVENT, RoomListStoreV3Class, type Section, @@ -346,6 +347,19 @@ describe("RoomListStoreV3", () => { expect(fn).toHaveBeenCalled(); }); + it("emits ROOM_TAGGED_EVENT on a local user tag action", async () => { + const { store, dispatcher } = await getRoomListStore(); + const fn = jest.fn(); + store.on(ROOM_TAGGED_EVENT, fn); + dispatcher.dispatch( + { + action: "RoomListActions.tagRoom.success", + }, + true, + ); + expect(fn).toHaveBeenCalled(); + }); + it("Room is re-inserted on decryption", async () => { const { store, rooms, client, dispatcher } = await getRoomListStore(); jest.spyOn(client, "getRoom").mockImplementation(() => rooms[10]);