diff --git a/client/app.tsx b/client/app.tsx index 94cc85b..054c637 100644 --- a/client/app.tsx +++ b/client/app.tsx @@ -6,14 +6,13 @@ import { OAuthCallbackRoute, } from './client-routes.tsx' import { Router } from './client-router.tsx' +import { + fetchSessionInfo, + type SessionInfo, + type SessionStatus, +} from './session.ts' import { colors, spacing, typography } from './styles/tokens.ts' -type SessionInfo = { - email: string -} - -type SessionStatus = 'idle' | 'loading' | 'ready' - type NavLink = { href: string label: string @@ -27,22 +26,7 @@ export function App(handle: Handle) { if (sessionStatus !== 'idle') return sessionStatus = 'loading' - try { - const response = await fetch('/session', { - headers: { Accept: 'application/json' }, - credentials: 'include', - }) - const payload = await response.json().catch(() => null) - const email = - response.ok && - payload?.ok && - typeof payload?.session?.email === 'string' - ? payload.session.email.trim() - : '' - session = email ? { email } : null - } catch { - session = null - } + session = await fetchSessionInfo() sessionStatus = 'ready' handle.update() diff --git a/client/client-routes.tsx b/client/client-routes.tsx index a8bf8c6..31b25a8 100644 --- a/client/client-routes.tsx +++ b/client/client-routes.tsx @@ -1,6 +1,11 @@ import { type Handle } from 'remix/component' import { navigate } from './client-router.tsx' import { Counter } from './counter.tsx' +import { + fetchSessionInfo, + type SessionInfo, + type SessionStatus, +} from './session.ts' import { colors, radius, @@ -354,6 +359,8 @@ function OAuthAuthorizeForm(handle: Handle) { let message: OAuthAuthorizeMessage | null = null let submitting = false let lastSearch = '' + let session: SessionInfo | null = null + let sessionStatus: SessionStatus = 'idle' function setMessage(next: OAuthAuthorizeMessage | null) { message = next @@ -420,6 +427,16 @@ function OAuthAuthorizeForm(handle: Handle) { } } + async function loadSession() { + if (sessionStatus !== 'idle') return + sessionStatus = 'loading' + + session = await fetchSessionInfo() + + sessionStatus = 'ready' + handle.update() + } + async function submitDecision( decision: 'approve' | 'deny', form?: HTMLFormElement, @@ -486,7 +503,11 @@ function OAuthAuthorizeForm(handle: Handle) { async function handleSubmit(event: SubmitEvent) { event.preventDefault() if (!(event.currentTarget instanceof HTMLFormElement)) return - await submitDecision('approve', event.currentTarget) + const hasSession = Boolean(session?.email) + await submitDecision( + 'approve', + hasSession ? undefined : event.currentTarget, + ) } return () => { @@ -496,12 +517,26 @@ function OAuthAuthorizeForm(handle: Handle) { lastSearch = currentSearch void loadInfo() } + if (sessionStatus === 'idle') { + void loadSession() + } const clientLabel = info?.client?.name ?? 'Unknown client' const scopes = info?.scopes ?? [] const scopeLabel = scopes.length > 0 ? scopes.join(', ') : 'No scopes requested.' - const actionsDisabled = status !== 'ready' || submitting + const sessionEmail = session?.email ?? '' + const isSessionReady = sessionStatus === 'ready' + const isSessionLoading = + sessionStatus === 'loading' || sessionStatus === 'idle' + const isLoggedIn = isSessionReady && Boolean(sessionEmail) + const actionsDisabled = status !== 'ready' || submitting || isSessionLoading + const formReady = status === 'ready' && !isSessionLoading + const authorizeLabel = submitting + ? 'Submitting...' + : isLoggedIn + ? 'Approve connection' + : 'Authorize' return (

{scopeLabel}

+ {isSessionLoading ? ( +

Checking your session…

+ ) : null} + {isLoggedIn ? ( +
+

+ Signed in as {sessionEmail} +

+

+ Approve to continue with this account. +

+
+ ) : null} {status === 'loading' ? (

Loading authorization details… @@ -573,62 +636,66 @@ function OAuthAuthorizeForm(handle: Handle) { border: `1px solid ${colors.border}`, backgroundColor: colors.surface, boxShadow: shadows.sm, - opacity: status === 'ready' ? 1 : 0.7, + opacity: formReady ? 1 : 0.7, }} on={{ submit: handleSubmit }} > - - + {!isLoggedIn && isSessionReady ? ( + <> + + + + ) : null}