From 9d3c54a78fd987c83f8a6c8e02e812e638ad6fd6 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Sun, 23 Aug 2026 22:07:16 -0700 Subject: [PATCH] fix(desktop): preserve huddle community bootstrap Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho --- desktop/src/app/companionWindow.test.mjs | 30 ++++++++++++++++++++++++ desktop/src/app/companionWindow.ts | 27 ++++++++++++++++----- desktop/src/main.tsx | 25 ++++++++++++++------ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/desktop/src/app/companionWindow.test.mjs b/desktop/src/app/companionWindow.test.mjs index 415ad74863..f94d4a98b4 100644 --- a/desktop/src/app/companionWindow.test.mjs +++ b/desktop/src/app/companionWindow.test.mjs @@ -3,6 +3,7 @@ import { describe, it } from "node:test"; import { acceptsNativeDeepLinks, + companionCommunityBootstrap, companionCommunityIdForHash, companionWindowKindForLabel, } from "./companionWindow.ts"; @@ -30,6 +31,35 @@ describe("acceptsNativeDeepLinks", () => { }); }); +describe("companionCommunityBootstrap", () => { + it("lets huddle companions boot through normal community selection", () => { + assert.deepEqual(companionCommunityBootstrap("huddle", ""), { + initialActiveCommunityId: undefined, + missingRequiredCommunity: false, + }); + }); + + it("rejects agent activity companions without community context", () => { + assert.deepEqual(companionCommunityBootstrap("agent-activity", ""), { + initialActiveCommunityId: undefined, + missingRequiredCommunity: true, + }); + }); + + it("pins agent activity companions to their encoded community", () => { + assert.deepEqual( + companionCommunityBootstrap( + "agent-activity", + "#/channels/channel?community=community-one", + ), + { + initialActiveCommunityId: "community-one", + missingRequiredCommunity: false, + }, + ); + }); +}); + describe("companionCommunityIdForHash", () => { it("reads and decodes immutable community identity from the route", () => { assert.equal( diff --git a/desktop/src/app/companionWindow.ts b/desktop/src/app/companionWindow.ts index 02ab356e0c..250f50d873 100644 --- a/desktop/src/app/companionWindow.ts +++ b/desktop/src/app/companionWindow.ts @@ -31,12 +31,27 @@ export function companionCommunityIdForHash(hash: string): string | null { return new URLSearchParams(hash.slice(query + 1)).get("community"); } -/** Immutable community selected by a native companion's bootstrap URL. */ -export function companionCommunityId( - hash: string = typeof window === "undefined" ? "" : window.location.hash, -): string | null | undefined { - if (currentCompanionWindowKind() === null) return undefined; - return companionCommunityIdForHash(hash); +export type CompanionCommunityBootstrap = { + initialActiveCommunityId: string | undefined; + missingRequiredCommunity: boolean; +}; + +/** Agent activity windows pin their origin community; huddles use normal selection. */ +export function companionCommunityBootstrap( + companionKind: CompanionWindowKind | null, + hash: string, +): CompanionCommunityBootstrap { + if (companionKind !== "agent-activity") { + return { + initialActiveCommunityId: undefined, + missingRequiredCommunity: false, + }; + } + const communityId = companionCommunityIdForHash(hash); + return { + initialActiveCommunityId: communityId ?? undefined, + missingRequiredCommunity: communityId === null, + }; } /** Whether this realm owns the native pending deep-link queue. */ diff --git a/desktop/src/main.tsx b/desktop/src/main.tsx index c173d0d01b..a1cefaa2ff 100644 --- a/desktop/src/main.tsx +++ b/desktop/src/main.tsx @@ -13,7 +13,11 @@ import { UpdaterProvider } from "@/features/settings/hooks/UpdaterProvider"; import { migrateLegacyCommunityStorageBeforeRender } from "@/features/communities/legacyCommunityStorage"; import { loadCommunities } from "@/features/communities/communityStorage"; import { CommunitiesProvider } from "@/features/communities/useCommunities"; -import { companionCommunityId, isCompanionWindow } from "@/app/companionWindow"; +import { + companionCommunityBootstrap, + currentCompanionWindowKind, + isCompanionWindow, +} from "@/app/companionWindow"; import { CommunityOnboardingProvider } from "@/features/onboarding/communityOnboarding"; import { ThemeProvider } from "@/shared/theme/ThemeProvider"; import { EmojiBurstProvider } from "@/shared/ui/EmojiBurstProvider"; @@ -99,17 +103,22 @@ function renderCompanionBootstrapError(message: string) { } function renderApp() { - const companion = isCompanionWindow(); - const companionCommunity = companionCommunityId(); - if (companion && !companionCommunity) { + const companionKind = currentCompanionWindowKind(); + const companionCommunity = companionCommunityBootstrap( + companionKind, + window.location.hash, + ); + if (companionCommunity.missingRequiredCommunity) { renderCompanionBootstrapError( "This window is missing its community context. Close it and try again.", ); return; } if ( - companionCommunity && - !loadCommunities().some(({ id }) => id === companionCommunity) + companionCommunity.initialActiveCommunityId && + !loadCommunities().some( + ({ id }) => id === companionCommunity.initialActiveCommunityId, + ) ) { renderCompanionBootstrapError( "This community is no longer available. Close this window and open activity again from Buzz.", @@ -122,7 +131,9 @@ function renderApp() { {/* block/buzz#5078 — catch any uncaught render error so a WebKit SecurityError from localStorage can't blank the whole window. */} - +