diff --git a/apps/web/src/components/layout/app/shell/app-shell.tsx b/apps/web/src/components/layout/app/shell/app-shell.tsx index 26c05cd..a4d97fd 100644 --- a/apps/web/src/components/layout/app/shell/app-shell.tsx +++ b/apps/web/src/components/layout/app/shell/app-shell.tsx @@ -1,6 +1,7 @@ import { Outlet, useRouterState } from "@tanstack/react-router" import { AuthProvider } from "@tripwire/auth/components" import { TopNav } from "#/components/layout/app/shell/top-nav" +import { BetaBanner } from "#/components/layout/app/shell/beta-banner" import { WorkspaceRedirect } from "#/components/layout/app/shell/workspace-redirect" import { InstallGitHubPrompt } from "#/components/layout/app/shell/install-github-prompt" import { AskSidePanel } from "#/components/layout/app/chat/ask-side-panel" @@ -55,8 +56,11 @@ function AppShellInner() { isChatRoute ? undefined : { boxShadow: "#00000008 0px 1px 4px" } } > -
- {needsInstall ? : } +
+ +
+ {needsInstall ? : } +
diff --git a/apps/web/src/components/layout/app/shell/beta-banner.tsx b/apps/web/src/components/layout/app/shell/beta-banner.tsx new file mode 100644 index 0000000..8fc9f7a --- /dev/null +++ b/apps/web/src/components/layout/app/shell/beta-banner.tsx @@ -0,0 +1,78 @@ +import { useState, useSyncExternalStore } from "react" +import { Button } from "@tripwire/ui/button" +import { CloseIcon } from "@tripwire/ui/icons/close-icon" +import { cn } from "@tripwire/ui/utils" + +const STORAGE_KEY = "tripwire:beta-banner:dismissed" +const EVENT_NAME = "tripwire:beta-banner-dismissed" + +function subscribe(callback: () => void): () => void { + window.addEventListener(EVENT_NAME, callback) + return () => window.removeEventListener(EVENT_NAME, callback) +} + +function getSnapshot(): boolean { + return window.localStorage.getItem(STORAGE_KEY) === "true" +} + +function getServerSnapshot(): boolean { + return true +} + +function dismiss(): void { + window.localStorage.setItem(STORAGE_KEY, "true") + window.dispatchEvent(new Event(EVENT_NAME)) +} + +export function BetaBanner() { + const dismissed = useSyncExternalStore( + subscribe, + getSnapshot, + getServerSnapshot + ) + const [closing, setClosing] = useState(false) + + if (dismissed) return null + + return ( +
{ + if (e.target === e.currentTarget) dismiss() + }} + > +
+
+

+ Tripwire is in closed beta. Please raise any issues via DM on X{" "} + + @tripwiredotsh + +

+ +
+
+
+ ) +}