diff --git a/client/src/App.tsx b/client/src/App.tsx index 590b247..b587503 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useCallback } from 'react' import './App.css' import { DialogProvider } from './components/ModalDialog.tsx' import AuthOnboarding from './components/AuthOnboarding.tsx' @@ -59,7 +59,7 @@ function App() { const [shareKey, setShareKey] = useState('') // Public demo mode (no account required) - const [isDemoMode, setIsDemoMode] = useState(false) + const [isDemoMode, setIsDemoMode] = useState(() => window.location.pathname === '/demo') const syncQueueCount = useLiveQuery( () => activeLogbookId ? db.syncQueue.where({ logbookId: activeLogbookId }).count() : db.syncQueue.count(), @@ -138,26 +138,37 @@ function App() { } }, [isAuthenticated]) - useEffect(() => { + const syncRouteFromLocation = useCallback(() => { const params = new URLSearchParams(window.location.search) const hashParams = new URLSearchParams(window.location.hash.substring(1)) + const path = window.location.pathname - if (window.location.pathname === '/demo') { + if (path === '/demo') { setIsDemoMode(true) + setIsViewerMode(false) + setIsAcceptingInvite(false) return } - if (window.location.pathname === '/share' && params.has('token') && hashParams.has('key')) { + setIsDemoMode(false) + + if (path === '/share' && params.has('token') && hashParams.has('key')) { setShareToken(params.get('token') || '') setShareKey(hashParams.get('key') || '') setIsViewerMode(true) + setIsAcceptingInvite(false) return } + setIsViewerMode(false) + if (params.has('token')) { setIsAcceptingInvite(true) + return } + setIsAcceptingInvite(false) + const savedUser = localStorage.getItem('active_username') const key = getActiveMasterKey() if (savedUser && key) { @@ -171,6 +182,19 @@ function App() { } }, []) + useEffect(() => { + syncRouteFromLocation() + window.addEventListener('popstate', syncRouteFromLocation) + return () => window.removeEventListener('popstate', syncRouteFromLocation) + }, [syncRouteFromLocation]) + + const openDemo = useCallback(() => { + window.history.pushState({}, document.title, '/demo') + setIsDemoMode(true) + setIsViewerMode(false) + setIsAcceptingInvite(false) + }, []) + useEffect(() => { registerNavigation({ setActiveTab, @@ -245,7 +269,7 @@ function App() { const handleExitDemo = () => { window.history.replaceState({}, document.title, '/') - setIsDemoMode(false) + syncRouteFromLocation() } if (isDemoMode) { @@ -288,7 +312,7 @@ function App() { if (!isAuthenticated) { return (
- +
) } diff --git a/client/src/components/AuthOnboarding.tsx b/client/src/components/AuthOnboarding.tsx index f5dcb7f..d614c05 100644 --- a/client/src/components/AuthOnboarding.tsx +++ b/client/src/components/AuthOnboarding.tsx @@ -16,9 +16,10 @@ import RegistrationDisclaimer from './RegistrationDisclaimer.tsx' interface AuthOnboardingProps { onAuthenticated: () => void + onOpenDemo?: () => void } -export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps) { +export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnboardingProps) { const { t, i18n } = useTranslation() const [username, setUsername] = useState('') const [loading, setLoading] = useState(false) @@ -526,7 +527,7 @@ export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps)