import { useEffect, useMemo } from 'react'; import type { LayoutNode } from 'officerdev'; import { WorkspaceView } from 'officerdev'; import { useDashboardState } from 'state/useDashboardState'; import { defaultLayout } from './defaultLayout'; // /calendar uses the Workspace/Panel system (like /transmission): the calendar list on the left, the // selected calendar's agenda on the right. Both panels read the officer-caldav sidecar through the // /api/caldav auth proxy, which holds no DAV credentials of its own. // // There is no :param route pair here and so no guard: a collection id is a DAV path with // slashes in it, so the selection lives in ?collection= on this same route. An absent or unknown value // resolves to the first collection inside the panels rather than by redirecting, because until the // collection list has loaded there is no canonical URL to redirect to. const ALLOWED_APP_TYPES = new Set(['calendar-nav', 'calendar-view', null]); function normalizeLayout(node: LayoutNode): LayoutNode { if (node.type === 'panel') { return ALLOWED_APP_TYPES.has(node.appType) ? node : { ...node, appType: 'calendar-view' }; } const children = node.children.map((c) => { const fixed = normalizeLayout(c.node); return fixed === c.node ? c : { ...c, node: fixed }; }); const changed = children.some((c, i) => c !== node.children[i]); return changed ? { ...node, children } : node; } export const CalendarScreen = () => { const rawWorkspace = useDashboardState('screens/calendar', defaultLayout); const workspace = useMemo(() => { const fixed = normalizeLayout(rawWorkspace.value); if (fixed === rawWorkspace.value) return rawWorkspace; return { ...rawWorkspace, value: fixed }; }, [rawWorkspace]); useEffect(() => { if (rawWorkspace.isLoaded && workspace.value !== rawWorkspace.value) { rawWorkspace.setValue(workspace.value); } }, [rawWorkspace.isLoaded, workspace.value, rawWorkspace.value]); return (
); };