From e17c4df27eabf40ff890300a146b1d0b134a6797 Mon Sep 17 00:00:00 2001 From: Wes Date: Sun, 16 Aug 2026 12:19:12 -0600 Subject: [PATCH] fix(desktop): cancel superseded channel reselect Return huddle navigation promises so deferred navigation can clear its skeleton when route commits fail, and cover reselecting the current channel while a new channel is pending. Co-authored-by: Carl Signed-off-by: Wes --- .../useDeferredSidebarNavigation.ts | 9 +++++-- desktop/src/app/useHuddlePresentation.ts | 4 +-- desktop/tests/e2e/messaging.spec.ts | 26 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/desktop/src/app/navigation/useDeferredSidebarNavigation.ts b/desktop/src/app/navigation/useDeferredSidebarNavigation.ts index d9e67e186..e1f5d8faa 100644 --- a/desktop/src/app/navigation/useDeferredSidebarNavigation.ts +++ b/desktop/src/app/navigation/useDeferredSidebarNavigation.ts @@ -78,7 +78,12 @@ export function useDeferredSidebarNavigation({ const selectDeferred = React.useCallback( (channelId: string) => { if (channelId === selectedChannelId) { - selectChannel(channelId); + if (pendingChannelId !== null) { + dispatchNavigationIntent(); + cancel(); + } + const navigationResult = selectChannel(channelId); + if (navigationResult) void navigationResult.catch(() => undefined); return; } dispatchNavigationIntent(); @@ -122,7 +127,7 @@ export function useDeferredSidebarNavigation({ }); }); }, - [cancel, selectChannel, selectedChannelId], + [cancel, pendingChannelId, selectChannel, selectedChannelId], ); return { pendingChannelId, selectDeferred }; diff --git a/desktop/src/app/useHuddlePresentation.ts b/desktop/src/app/useHuddlePresentation.ts index 6d9d0dc54..d94226ca4 100644 --- a/desktop/src/app/useHuddlePresentation.ts +++ b/desktop/src/app/useHuddlePresentation.ts @@ -298,7 +298,7 @@ export function useHuddlePresentation() { void queryClient.invalidateQueries({ queryKey: channelWindowKey(ephemeralChannelId), }); - void goChannel(ephemeralChannelId); + return goChannel(ephemeralChannelId); }, [goChannel, queryClient, revealHuddleChannel], ); @@ -306,7 +306,7 @@ export function useHuddlePresentation() { (ephemeralChannelId: string) => { activeHuddleChannelIdRef.current = ephemeralChannelId; trackHuddleBackingChannel(ephemeralChannelId); - viewHuddleChannel(ephemeralChannelId); + return viewHuddleChannel(ephemeralChannelId); }, [trackHuddleBackingChannel, viewHuddleChannel], ); diff --git a/desktop/tests/e2e/messaging.spec.ts b/desktop/tests/e2e/messaging.spec.ts index 7fe82648c..581f7b3c7 100644 --- a/desktop/tests/e2e/messaging.spec.ts +++ b/desktop/tests/e2e/messaging.spec.ts @@ -2887,6 +2887,32 @@ test("sidebar selection paints before cached channel work starts", async ({ await expect(page.getByTestId("chat-title")).toHaveText("random"); }); +test("reselecting the current channel cancels pending sidebar navigation", async ({ + page, +}) => { + await page.goto("/"); + await page.getByTestId("channel-general").click(); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + + const generalUrl = page.url(); + const historyLength = await page.evaluate(() => window.history.length); + await page.evaluate(() => { + document + .querySelector('[data-testid="channel-random"]') + ?.click(); + document + .querySelector('[data-testid="channel-general"]') + ?.click(); + }); + + await expect(page.getByTestId("pending-channel-skeleton")).toHaveCount(0); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + await page.waitForTimeout(100); + await expect(page).toHaveURL(generalUrl); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + expect(await page.evaluate(() => window.history.length)).toBe(historyLength); +}); + test("superseded sidebar feedback cannot override newer navigation", async ({ page, }) => {