"use client"; import { Suspense, useEffect, useState } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import { useQueryClient } from "@tanstack/react-query"; import { useAuthStore } from "@multica/core/auth"; import { workspaceKeys } from "@multica/core/workspace/queries"; import { paths } from "@multica/core/paths"; import { api } from "@multica/core/api"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, } from "@multica/ui/components/ui/card"; import { Button } from "@multica/ui/components/ui/button"; import { Loader2 } from "lucide-react"; function CallbackContent() { const router = useRouter(); const searchParams = useSearchParams(); const qc = useQueryClient(); const loginWithGoogle = useAuthStore((s) => s.loginWithGoogle); const [error, setError] = useState(""); const [desktopToken, setDesktopToken] = useState(null); useEffect(() => { const code = searchParams.get("code"); if (!code) { setError("Missing authorization code"); return; } const errorParam = searchParams.get("error"); if (errorParam) { setError(errorParam === "access_denied" ? "Access denied" : errorParam); return; } const state = searchParams.get("state") || ""; const stateParts = state.split(","); const isDesktop = stateParts.includes("platform:desktop"); const nextPart = stateParts.find((p) => p.startsWith("next:")); const nextUrl = nextPart ? nextPart.slice(5) : null; // strip "next:" prefix const redirectUri = `${window.location.origin}/auth/callback`; if (isDesktop) { // Desktop flow: exchange code for token, then redirect via deep link api .googleLogin(code, redirectUri) .then(({ token }) => { setDesktopToken(token); window.location.href = `multica://auth/callback?token=${encodeURIComponent(token)}`; }) .catch((err) => { setError(err instanceof Error ? err.message : "Login failed"); }); } else { // Normal web flow loginWithGoogle(code, redirectUri) .then(async () => { const wsList = await api.listWorkspaces(); qc.setQueryData(workspaceKeys.list(), wsList); // URL is now the source of truth for the current workspace — the // [workspaceSlug]/layout syncs stores + cookie once we navigate. // Honor ?next= first (e.g. came from /invite/{id}), otherwise land // in the first workspace's issues, or /workspaces/new for zero-workspace users. const [first] = wsList; const defaultDest = first ? paths.workspace(first.slug).issues() : paths.newWorkspace(); router.push(nextUrl || defaultDest); }) .catch((err) => { setError(err instanceof Error ? err.message : "Login failed"); }); } }, [searchParams, loginWithGoogle, router, qc]); if (desktopToken) { return (
Opening Multica You should see a prompt to open the Multica desktop app. If nothing happens, click the button below.
); } if (error) { return (
Login Failed {error} Back to login
); } return (
Signing in... Please wait while we complete your login
); } export default function CallbackPage() { return ( ); }