Below md width, the app now forces view-only (same canEdit mechanism as a shared view-only diagram) rather than trying to support touch editing: palette and inspector panels hide, editing controls disable, and the top bar collapses to just Diagrams + Account. A bottom tab bar swaps between a full-screen canvas and a full-screen Bill of Materials, since there's no room for both side by side. Canvas gets a few mobile-specific trims too: MiniMap and the interactivity lock button hide whenever canEdit is false (the lock button doesn't actually grant editing either way — nodesDraggable/ nodesConnectable already override it — so leaving it visible would just be misleading), and h-screen is replaced with h-dvh throughout so the layout doesn't get clipped by a mobile browser's collapsing address bar. Also fixes quick-add's anti-stack offset (24px — far smaller than a device node) to actually tile devices apart instead of leaving them nearly overlapping, and logs a deferred "Auto-arrange" layout feature in organized-ideas.md, surfaced by mobile visitors having no way to fix an overlapping diagram themselves. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import type { Session } from '@supabase/supabase-js'
|
|
import { useEffect, useState } from 'react'
|
|
import CompleteProfileScreen from './components/auth/CompleteProfileScreen'
|
|
import LoginScreen from './components/auth/LoginScreen'
|
|
import AppShell from './components/layout/AppShell'
|
|
import { supabase } from './data/supabaseClient'
|
|
|
|
export default function App() {
|
|
// undefined = still checking for an existing session; null = signed out.
|
|
const [session, setSession] = useState<Session | null | undefined>(undefined)
|
|
// undefined = haven't checked yet; null = has one; true = needs to set one.
|
|
const [needsUsername, setNeedsUsername] = useState<boolean | null | undefined>(undefined)
|
|
|
|
useEffect(() => {
|
|
supabase.auth.getSession().then(({ data }) => setSession(data.session))
|
|
const {
|
|
data: { subscription },
|
|
} = supabase.auth.onAuthStateChange((_event, session) => setSession(session))
|
|
return () => subscription.unsubscribe()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
// Nothing to check while signed out — the render logic below never
|
|
// reads needsUsername in that case anyway.
|
|
if (!session) return
|
|
supabase
|
|
.from('profiles')
|
|
.select('username')
|
|
.eq('id', session.user.id)
|
|
.single()
|
|
.then(({ data }) => setNeedsUsername(!data?.username))
|
|
}, [session])
|
|
|
|
if (session === undefined) {
|
|
return <div className="flex h-dvh items-center justify-center text-sm text-slate-400">Loading…</div>
|
|
}
|
|
|
|
if (!session) {
|
|
return <LoginScreen />
|
|
}
|
|
|
|
if (needsUsername === undefined) {
|
|
return <div className="flex h-dvh items-center justify-center text-sm text-slate-400">Loading…</div>
|
|
}
|
|
|
|
if (needsUsername) {
|
|
return <CompleteProfileScreen onDone={() => setNeedsUsername(false)} />
|
|
}
|
|
|
|
return <AppShell />
|
|
}
|