Files
buzz/desktop/src/main.tsx
T

119 lines
3.9 KiB
TypeScript

import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "@/app/App";
import { NostrBindConsentDialog } from "@/features/profile/ui/NostrBindConsentDialog";
import "@fontsource-variable/inter/wght.css";
import "@/shared/styles/globals.css";
import { UpdaterProvider } from "@/features/settings/hooks/UpdaterProvider";
import { migrateLegacyCommunityStorageBeforeRender } from "@/features/communities/legacyCommunityStorage";
import { CommunitiesProvider } from "@/features/communities/useCommunities";
import { CommunityOnboardingProvider } from "@/features/onboarding/communityOnboarding";
import { ThemeProvider } from "@/shared/theme/ThemeProvider";
import { EmojiBurstProvider } from "@/shared/ui/EmojiBurstProvider";
import { PoofBurstProvider } from "@/shared/ui/PoofBurstProvider";
import { Toaster } from "@/shared/ui/sonner";
import { TooltipProvider } from "@/shared/ui/tooltip";
type E2eWindow = Window & {
__BUZZ_E2E__?: unknown;
};
const E2E_DEFAULT_PUBKEY = "deadbeef".repeat(8);
const E2E_COMMUNITY_ID = "e2e-default-community";
const ONBOARDING_COMPLETION_STORAGE_KEY_PREFIX = "buzz-onboarding-complete.v1:";
const DEV_STATE_RESET_PARAM = "resetDevState";
function resetDevWebviewStateFromUrl() {
if (!import.meta.env.DEV) {
return;
}
const url = new URL(window.location.href);
if (url.searchParams.get(DEV_STATE_RESET_PARAM) !== "1") {
return;
}
// WebKit groups every Buzz binary under one disk directory, but storage is
// isolated by origin. Clearing here resets only this dev server's origin;
// deleting the shared WebKit directory would also destroy installed-app state.
window.localStorage.clear();
window.sessionStorage.clear();
url.searchParams.delete(DEV_STATE_RESET_PARAM);
window.history.replaceState(window.history.state, "", url);
}
function configureDevE2eBridgeFromUrl() {
if (!import.meta.env.DEV) {
return;
}
const url = new URL(window.location.href);
if (url.searchParams.get("e2e") !== "mock") {
return;
}
const e2eWindow = window as E2eWindow;
e2eWindow.__BUZZ_E2E__ ??= { mode: "mock" };
const community = {
addedAt: new Date().toISOString(),
id: E2E_COMMUNITY_ID,
name: "E2E Test",
relayUrl: "ws://localhost:3000",
};
window.localStorage.setItem("buzz-communities", JSON.stringify([community]));
window.localStorage.setItem("buzz-active-community-id", E2E_COMMUNITY_ID);
window.localStorage.setItem(
`${ONBOARDING_COMPLETION_STORAGE_KEY_PREFIX}${E2E_DEFAULT_PUBKEY}`,
"true",
);
}
function renderApp() {
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<CommunitiesProvider>
<CommunityOnboardingProvider>
<ThemeProvider defaultTheme="buzz">
<TooltipProvider delayDuration={300}>
<EmojiBurstProvider>
<PoofBurstProvider>
<UpdaterProvider>
<App />
<NostrBindConsentDialog />
</UpdaterProvider>
<Toaster />
</PoofBurstProvider>
</EmojiBurstProvider>
</TooltipProvider>
</ThemeProvider>
</CommunityOnboardingProvider>
</CommunitiesProvider>
</React.StrictMode>,
);
}
async function installE2eBridgeIfConfigured() {
// The mock bridge is compiled only into dev and explicit E2E builds. A
// pre-bootstrap global alone must never activate mock IPC in production.
if (
!(import.meta.env.DEV || import.meta.env.MODE === "e2e") ||
!(window as E2eWindow).__BUZZ_E2E__
) {
return;
}
const { maybeInstallE2eTauriMocks } = await import("@/testing/e2eBridge");
maybeInstallE2eTauriMocks();
}
async function bootstrap() {
resetDevWebviewStateFromUrl();
configureDevE2eBridgeFromUrl();
await installE2eBridgeIfConfigured();
await migrateLegacyCommunityStorageBeforeRender();
renderApp();
}
void bootstrap();