perf(web): parallelize auth init and non-blocking dashboard layout (#220)
- Fire getMe() and listWorkspaces() in parallel instead of serially, saving one network round-trip (~200ms on cloud) - Render dashboard sidebar shell immediately once user is authenticated, show loading indicator in content area while workspace hydrates Closes MUL-41
This commit is contained in:
parent
56b66908a1
commit
461dad0dd5
2 changed files with 35 additions and 16 deletions
|
|
@ -38,12 +38,20 @@ export default function DashboardLayout({
|
|||
);
|
||||
}
|
||||
|
||||
if (!user || !workspace) return null;
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<SidebarProvider className="h-svh">
|
||||
<AppSidebar />
|
||||
<SidebarInset className="overflow-hidden">{children}</SidebarInset>
|
||||
<SidebarInset className="overflow-hidden">
|
||||
{workspace ? (
|
||||
children
|
||||
) : (
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<MulticaIcon className="size-6 animate-pulse" />
|
||||
</div>
|
||||
)}
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,26 +10,37 @@ const logger = createLogger("auth");
|
|||
|
||||
/**
|
||||
* Initializes auth + workspace state from localStorage on mount.
|
||||
* Must wrap the app to ensure stores are hydrated before children render.
|
||||
* Fires getMe() and listWorkspaces() in parallel when a cached token exists.
|
||||
*/
|
||||
export function AuthInitializer({ children }: { children: ReactNode }) {
|
||||
const initialize = useAuthStore((s) => s.initialize);
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const isLoading = useAuthStore((s) => s.isLoading);
|
||||
const hydrateWorkspace = useWorkspaceStore((s) => s.hydrateWorkspace);
|
||||
|
||||
useEffect(() => {
|
||||
initialize();
|
||||
}, [initialize]);
|
||||
const token = localStorage.getItem("multica_token");
|
||||
if (!token) {
|
||||
useAuthStore.setState({ isLoading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading || !user) return;
|
||||
api.setToken(token);
|
||||
const wsId = localStorage.getItem("multica_workspace_id");
|
||||
|
||||
api.listWorkspaces().then((wsList) => {
|
||||
hydrateWorkspace(wsList, wsId);
|
||||
}).catch((err) => logger.error("workspace hydration failed", err));
|
||||
}, [user, isLoading, hydrateWorkspace]);
|
||||
// Fire getMe and listWorkspaces in parallel
|
||||
const mePromise = api.getMe();
|
||||
const wsPromise = api.listWorkspaces();
|
||||
|
||||
Promise.all([mePromise, wsPromise])
|
||||
.then(([user, wsList]) => {
|
||||
useAuthStore.setState({ user, isLoading: false });
|
||||
useWorkspaceStore.getState().hydrateWorkspace(wsList, wsId);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("auth init failed", err);
|
||||
api.setToken(null);
|
||||
api.setWorkspaceId(null);
|
||||
localStorage.removeItem("multica_token");
|
||||
localStorage.removeItem("multica_workspace_id");
|
||||
useAuthStore.setState({ user: null, isLoading: false });
|
||||
});
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue