diff --git a/src/App.tsx b/src/App.tsx index 96c8cd4..a3d56a9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { NWCProvider } from '@/contexts/NWCContext'; import { LanguageProvider } from '@/contexts/LanguageContext'; import { AppConfig } from '@/contexts/AppContext'; import { PWAInstallPrompt } from '@/components/PWAInstallPrompt'; +import { BetaDisclaimerDialog } from '@/components/BetaDisclaimerDialog'; import { AmberCallbackHandler } from '@/components/auth/AmberCallbackHandler'; import AppRouter from './AppRouter'; @@ -63,6 +64,7 @@ export function App() { + diff --git a/src/components/BetaDisclaimerDialog.tsx b/src/components/BetaDisclaimerDialog.tsx new file mode 100644 index 0000000..42d8ec0 --- /dev/null +++ b/src/components/BetaDisclaimerDialog.tsx @@ -0,0 +1,152 @@ +import { useEffect, useState } from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { useLocalStorage } from '@/hooks/useLocalStorage'; +import { AlertTriangle, Code2, FlaskConical, ShieldOff, Wallet, ArrowRight } from 'lucide-react'; + +/** + * Bump this version to re-show the disclaimer to everyone + * (e.g. after terms change or a major release). + */ +const DISCLAIMER_VERSION = 1; +const STORAGE_KEY = `skatespots_disclaimer_accepted_v${DISCLAIMER_VERSION}`; + +const REPO_URL = + 'https://github.com/search?q=SkateSpots'; + +export function BetaDisclaimerDialog() { + const [accepted, setAccepted] = useLocalStorage(STORAGE_KEY, false); + const [open, setOpen] = useState(false); + + // Open on first launch (after mount so SSR/hydration is clean) + useEffect(() => { + if (!accepted) { + const t = setTimeout(() => setOpen(true), 400); + return () => clearTimeout(t); + } + }, [accepted]); + + const handleAccept = () => { + setAccepted(true); + setOpen(false); + }; + + return ( + { + if (!next) return; + setOpen(next); + }} + > + e.preventDefault()} + onEscapeKeyDown={(e) => e.preventDefault()} + > + {/* Accent gradient strip */} +
+ +
+ + {/* Icon badge */} +
+ +
+ + + Heads up — this is beta + + + + A quick word before you drop in. + +
+ + {/* Bullets */} +
+ } + title="Open-source & experimental" + body="Built in the open by the community. Poke around, fork it, break it." + /> + } + title="In beta — not audited" + body="The code has not undergone a formal security audit yet. Bugs happen." + /> + } + title="Handles real Bitcoin & Lightning" + body="Use money on it at your own risk. Only load what you can afford to lose." + /> +
+ + {/* Fine print */} +

+ + + No warranty of any kind. You are solely responsible for your own + funds and keys.{' '} + + View the code + + . + +

+
+ + + + + +
+ ); +} + +function DisclaimerRow({ + icon, + title, + body, +}: { + icon: React.ReactNode; + title: string; + body: string; +}) { + return ( +
+
+ {icon} +
+
+

{title}

+

{body}

+
+
+ ); +} diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 8eb1366..5c5573f 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -863,7 +863,12 @@ export function Layout({ children, noPadding }: LayoutProps) {