From 8604cc0d40a2ca642254f02319c2661c0ae2bbb6 Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Tue, 29 Sep 2026 14:15:38 -0700 Subject: [PATCH] chat: fall back to this tab when the popout is blocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing the button's href made a blocked window.open (popup blocker, null return) leave the viewer with neither the panel — the handler still hid it — nor a popout. When window.open returns null, send this tab to the popout route instead of hiding chat, and only collapse the panel when a window actually opened. 07-chat-popout covers the blocked path by stubbing window.open to return null: the tab must land on /chat-popout/ with no popup. --- .../src/components/chat/chat-box.tsx | 22 ++++++++++------ js/e2e-web/flows/07-chat-popout.spec.ts | 26 +++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/js/components/src/components/chat/chat-box.tsx b/js/components/src/components/chat/chat-box.tsx index de3a27440..71ff8cfd4 100644 --- a/js/components/src/components/chat/chat-box.tsx +++ b/js/components/src/components/chat/chat-box.tsx @@ -694,14 +694,20 @@ export function ChatBox({ }} onPress={() => { const did = linfo?.author?.did ?? profile?.did; - if (did) { - const u = new URL(window.location.href); - u.pathname = `/chat-popout/${did}`; - window.open( - u.toString(), - "_blank", - "popup=true,width=480,height=600", - ); + if (!did) return; + const u = new URL(window.location.href); + u.pathname = `/chat-popout/${did}`; + const popup = window.open( + u.toString(), + "_blank", + "popup=true,width=480,height=600", + ); + if (!popup) { + // A blocked popup returns null. Don't hide the panel and + // leave the viewer with no chat at all — send this tab to + // the popout instead. + window.location.assign(u.toString()); + return; } setIsChatVisible?.(false); }} diff --git a/js/e2e-web/flows/07-chat-popout.spec.ts b/js/e2e-web/flows/07-chat-popout.spec.ts index 42822cd90..704b1a780 100644 --- a/js/e2e-web/flows/07-chat-popout.spec.ts +++ b/js/e2e-web/flows/07-chat-popout.spec.ts @@ -47,3 +47,29 @@ test("07-chat-popout: opens one window, leaves the stream page alone", async ({ await popup.close(); expect(page.context().pages()).toHaveLength(1); }); + +test("07-chat-popout: blocked popup falls back to this tab", async ({ + page, +}) => { + await loginThroughPds(page); + + await page.goto(`${HTTPS_URL}/`); + await page.getByTestId("home-stream-card").first().click(); + await expect( + page.getByText("Now streaming - e2e test stream").first(), + ).toBeVisible({ timeout: 30_000 }); + const popoutButton = page.getByTestId("chat-popout-button"); + await expect(popoutButton).toBeVisible({ timeout: 30_000 }); + + let popups = 0; + page.on("popup", () => popups++); + // A popup blocker makes window.open return null with no window; the button + // must not just hide the chat panel and leave the viewer with nothing. + await page.evaluate(() => { + window.open = () => null; + }); + + await popoutButton.click(); + await page.waitForURL(/\/chat-popout\/did:plc:/, { timeout: 10_000 }); + expect(popups).toBe(0); +}); -- knotmirror 0.0.1