mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
fix(workspace): seed React Query cache at all list-acquisition points
- staleTime: 0 on fetchQuery after leave/delete so fresh data is fetched - setQueryData before switchWorkspace in createWorkspace so sidebar is consistent on first render - seed workspaceKeys.list() cache in login, Google callback, and settings save so the first useQuery(workspaceListOptions()) hit is free - remove dead onError from WorkspaceStoreOptions (used only by the deleted refreshWorkspaces action) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
|
||||
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 { useWorkspaceStore } from "@multica/core/workspace";
|
||||
import { workspaceKeys } from "@multica/core/workspace/queries";
|
||||
import { api } from "@multica/core/api";
|
||||
import {
|
||||
Card,
|
||||
@@ -17,6 +19,7 @@ import { Loader2 } from "lucide-react";
|
||||
function CallbackContent() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const qc = useQueryClient();
|
||||
const loginWithGoogle = useAuthStore((s) => s.loginWithGoogle);
|
||||
const hydrateWorkspace = useWorkspaceStore((s) => s.hydrateWorkspace);
|
||||
const [error, setError] = useState("");
|
||||
@@ -39,6 +42,7 @@ function CallbackContent() {
|
||||
loginWithGoogle(code, redirectUri)
|
||||
.then(async () => {
|
||||
const wsList = await api.listWorkspaces();
|
||||
qc.setQueryData(workspaceKeys.list(), wsList);
|
||||
const lastWsId = localStorage.getItem("multica_workspace_id");
|
||||
await hydrateWorkspace(wsList, lastWsId);
|
||||
router.push("/issues");
|
||||
@@ -46,7 +50,7 @@ function CallbackContent() {
|
||||
.catch((err) => {
|
||||
setError(err instanceof Error ? err.message : "Login failed");
|
||||
});
|
||||
}, [searchParams, loginWithGoogle, hydrateWorkspace, router]);
|
||||
}, [searchParams, loginWithGoogle, hydrateWorkspace, router, qc]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { Workspace } from "../types";
|
||||
import { api } from "../api";
|
||||
import { workspaceKeys, workspaceListOptions } from "./queries";
|
||||
import { useWorkspaceStore } from "./index";
|
||||
@@ -9,7 +10,8 @@ export function useCreateWorkspace() {
|
||||
mutationFn: (data: { name: string; slug: string; description?: string }) =>
|
||||
api.createWorkspace(data),
|
||||
onSuccess: (newWs) => {
|
||||
// Switch to the newly created workspace immediately
|
||||
// Add to cache before switching so sidebar list is consistent on first render
|
||||
qc.setQueryData(workspaceKeys.list(), (old: Workspace[] = []) => [...old, newWs]);
|
||||
useWorkspaceStore.getState().switchWorkspace(newWs);
|
||||
},
|
||||
onSettled: () => {
|
||||
@@ -25,8 +27,8 @@ export function useLeaveWorkspace() {
|
||||
onSuccess: async (_, workspaceId) => {
|
||||
const currentWsId = useWorkspaceStore.getState().workspace?.id;
|
||||
if (currentWsId === workspaceId) {
|
||||
// Left our current workspace — refetch and pick another
|
||||
const wsList = await qc.fetchQuery(workspaceListOptions());
|
||||
// staleTime: 0 forces a real network fetch — cache still has the left workspace
|
||||
const wsList = await qc.fetchQuery({ ...workspaceListOptions(), staleTime: 0 });
|
||||
useWorkspaceStore.getState().hydrateWorkspace(wsList);
|
||||
}
|
||||
},
|
||||
@@ -43,8 +45,8 @@ export function useDeleteWorkspace() {
|
||||
onSuccess: async (_, workspaceId) => {
|
||||
const currentWsId = useWorkspaceStore.getState().workspace?.id;
|
||||
if (currentWsId === workspaceId) {
|
||||
// Deleted our current workspace — refetch and pick another
|
||||
const wsList = await qc.fetchQuery(workspaceListOptions());
|
||||
// staleTime: 0 forces a real network fetch — cache still has the deleted workspace
|
||||
const wsList = await qc.fetchQuery({ ...workspaceListOptions(), staleTime: 0 });
|
||||
useWorkspaceStore.getState().hydrateWorkspace(wsList);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@ const logger = createLogger("workspace-store");
|
||||
|
||||
interface WorkspaceStoreOptions {
|
||||
storage?: StorageAdapter;
|
||||
onError?: (message: string) => void;
|
||||
}
|
||||
|
||||
interface WorkspaceState {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, type ReactNode } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
} from "@multica/ui/components/ui/input-otp";
|
||||
import { useAuthStore } from "@multica/core/auth";
|
||||
import { useWorkspaceStore } from "@multica/core/workspace";
|
||||
import { workspaceKeys } from "@multica/core/workspace/queries";
|
||||
import { api } from "@multica/core/api";
|
||||
import type { User } from "@multica/core/types";
|
||||
|
||||
@@ -87,6 +89,7 @@ export function LoginPage({
|
||||
lastWorkspaceId,
|
||||
onTokenObtained,
|
||||
}: LoginPageProps) {
|
||||
const qc = useQueryClient();
|
||||
const [step, setStep] = useState<"email" | "code" | "cli_confirm">("email");
|
||||
const [email, setEmail] = useState("");
|
||||
const [code, setCode] = useState("");
|
||||
@@ -167,6 +170,7 @@ export function LoginPage({
|
||||
// Normal path
|
||||
await useAuthStore.getState().verifyCode(email, value);
|
||||
const wsList = await api.listWorkspaces();
|
||||
qc.setQueryData(workspaceKeys.list(), wsList);
|
||||
useWorkspaceStore.getState().hydrateWorkspace(wsList, lastWorkspaceId);
|
||||
onTokenObtained?.();
|
||||
onSuccess();
|
||||
@@ -178,7 +182,7 @@ export function LoginPage({
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[email, onSuccess, cliCallback, lastWorkspaceId, onTokenObtained],
|
||||
[email, onSuccess, cliCallback, lastWorkspaceId, onTokenObtained, qc],
|
||||
);
|
||||
|
||||
const handleResend = async () => {
|
||||
|
||||
@@ -18,19 +18,21 @@ import {
|
||||
AlertDialogAction,
|
||||
} from "@multica/ui/components/ui/alert-dialog";
|
||||
import { toast } from "sonner";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuthStore } from "@multica/core/auth";
|
||||
import { useWorkspaceStore } from "@multica/core/workspace";
|
||||
import { useLeaveWorkspace, useDeleteWorkspace } from "@multica/core/workspace/mutations";
|
||||
import { useWorkspaceId } from "@multica/core/hooks";
|
||||
import { memberListOptions } from "@multica/core/workspace/queries";
|
||||
import { memberListOptions, workspaceKeys } from "@multica/core/workspace/queries";
|
||||
import { api } from "@multica/core/api";
|
||||
import type { Workspace } from "@multica/core/types";
|
||||
|
||||
export function WorkspaceTab() {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const workspace = useWorkspaceStore((s) => s.workspace);
|
||||
const wsId = useWorkspaceId();
|
||||
const { data: members = [] } = useQuery(memberListOptions(wsId));
|
||||
const qc = useQueryClient();
|
||||
const updateWorkspace = useWorkspaceStore((s) => s.updateWorkspace);
|
||||
const leaveWorkspace = useLeaveWorkspace();
|
||||
const deleteWorkspace = useDeleteWorkspace();
|
||||
@@ -67,6 +69,9 @@ export function WorkspaceTab() {
|
||||
context,
|
||||
});
|
||||
updateWorkspace(updated);
|
||||
qc.setQueryData(workspaceKeys.list(), (old: Workspace[] | undefined) =>
|
||||
old?.map((ws) => (ws.id === updated.id ? updated : ws)),
|
||||
);
|
||||
toast.success("Workspace settings saved");
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : "Failed to save workspace settings");
|
||||
|
||||
Reference in New Issue
Block a user