Unverified 提交 8a38dc75 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Refactor React Query keys to use centralized factory pattern (#2268)

Introduce a type-safe, centralized query key system following TanStack Query best practices: - Add src/lib/queryKeys.ts with hierarchical factory functions - Use `as const` assertions for full type inference - Group keys by feature (apps, chats, versions, etc.) - Support prefix-based invalidation via parent keys Update all 30+ hooks and components to use the new queryKeys: - Replace inline string arrays with factory calls - Replace scattered exports (CHATS_QUERY_KEY, TOKEN_COUNT_QUERY_KEY, etc.) - Consistent pattern: queryKeys.<feature>.<operation>(params) Benefits: - Single source of truth for all query keys - Full autocomplete/IntelliSense support - Type-safe invalidation (catches typos at compile time) - Easier refactoring and key discovery <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Centralized React Query keys behind a typed queryKeys factory and updated 30+ hooks/components to use it. This improves type-safety, enables autocomplete, and makes invalidation and refactors simpler. - **Refactors** - Added src/lib/queryKeys.ts with hierarchical factory functions (as const) grouped by feature. - Replaced inline arrays and scattered constants (e.g., CHATS_QUERY_KEY, TOKEN_COUNT_QUERY_KEY, APP_THEME_QUERY_KEY, SUPABASE_QUERY_KEYS). - Standardized invalidate/remove calls to use parent keys (e.g., queryKeys.chats.all, queryKeys.versions.list({ appId })). - Structured MCP keys (mcp.toolsByServer.all and mcp.toolsByServer.list({ serverIds })) and updated Supabase branches to include organizationSlug. - No behavior changes; safer invalidation and consistent keys across the app. - **Migration** - Use queryKeys.<feature>.<operation>(params object) for all queryKey definitions. - Invalidate broadly via parent keys when needed (e.g., queryKeys.chats.all). - Update Supabase branches calls to pass organizationSlug: queryKeys.supabase.branches({ projectId, organizationSlug }). - Do not add or export per-hook key constants; rely on queryKeys. <sup>Written for commit 2b80e408f077b8ea3141369ca21f62e514852cfd. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces a centralized, typed React Query key factory and applies it across the app for consistency and safer invalidation. > > - Adds `src/lib/queryKeys.ts` with hierarchical, `as const` key factories (e.g., `queryKeys.apps.detail`, `queryKeys.versions.list`) > - Refactors 30+ hooks/components to use factory keys in `useQuery`/`useMutation` and `invalidateQueries`/`removeQueries` > - Replaces scattered constants (e.g., `CHATS_QUERY_KEY`, `TOKEN_COUNT_QUERY_KEY`, `APP_THEME_QUERY_KEY`, Supabase/MCP keys) with `queryKeys` > - Updates IPC-driven UI pieces (`AppUpgrades`, `CapacitorControls`, `ModelPicker`, `ChatInput`, preview panels, Neon, MCP, Supabase, Vercel, etc.) to the new keys > - Documentation: `AGENTS.md` adds an Architecture section with usage guidelines and changes format script to `npm run fmt` > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2b80e408f077b8ea3141369ca21f62e514852cfd. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: 's avatarClaude <noreply@anthropic.com>
上级 69c0259b
......@@ -25,7 +25,7 @@ Otherwise, run the following commands directly:
**Formatting**
```sh
npm run prettier
npm run fmt
```
**Linting**
......@@ -61,11 +61,39 @@ Note: if you do this, then you will need to re-add the changes and commit again.
3. `src/ipc/ipc_host.ts` registers handlers that live in files under `src/ipc/handlers/` (e.g., `app_handlers.ts`, `chat_stream_handlers.ts`, `settings_handlers.ts`).
4. IPC handlers should `throw new Error("...")` on failure instead of returning `{ success: false }` style payloads.
## Architecture
### React Query key factory
All React Query keys must be defined in `src/lib/queryKeys.ts` using the centralized factory pattern. This provides:
- Type-safe query keys with full autocomplete
- Hierarchical structure for easy invalidation (invalidate parent to invalidate children)
- Consistent naming across the codebase
- Single source of truth for all query keys
**Usage:**
```ts
import { queryKeys } from "@/lib/queryKeys";
// In useQuery:
useQuery({
queryKey: queryKeys.apps.detail({ appId }),
queryFn: () => IpcClient.getInstance().getApp(appId),
});
// Invalidating queries:
queryClient.invalidateQueries({ queryKey: queryKeys.apps.all });
```
**Adding new keys:** Add entries to the appropriate domain in `queryKeys.ts`. Follow the existing pattern with `all` for the base key and factory functions using object parameters for parameterized keys.
## React + IPC integration pattern
When creating hooks/components that call IPC handlers:
- Wrap reads in `useQuery`, providing a stable `queryKey`, async `queryFn` that calls the relevant `IpcClient` method, and conditionally use `enabled`/`initialData`/`meta` as needed.
- Wrap reads in `useQuery`, using keys from `queryKeys` factory (see above), async `queryFn` that calls the relevant `IpcClient` method, and conditionally use `enabled`/`initialData`/`meta` as needed.
- Wrap writes in `useMutation`; validate inputs locally, call the IPC client, and invalidate related queries on success. Use shared utilities (e.g., toast helpers) in `onError`.
- Synchronize TanStack Query data with any global state (like Jotai atoms) via `useEffect` only if required.
......
......@@ -5,6 +5,7 @@ import { Terminal } from "lucide-react";
import { IpcClient } from "@/ipc/ipc_client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AppUpgrade } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export function AppUpgrades({ appId }: { appId: number | null }) {
const queryClient = useQueryClient();
......@@ -14,7 +15,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
isLoading,
error: queryError,
} = useQuery({
queryKey: ["app-upgrades", appId],
queryKey: queryKeys.appUpgrades.byApp({ appId }),
queryFn: () => {
if (!appId) {
return Promise.resolve([]);
......@@ -40,13 +41,19 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
});
},
onSuccess: (_, upgradeId) => {
queryClient.invalidateQueries({ queryKey: ["app-upgrades", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.appUpgrades.byApp({ appId }),
});
if (upgradeId === "capacitor") {
// Capacitor upgrade is done, so we need to invalidate the Capacitor
// query to show the new status.
queryClient.invalidateQueries({ queryKey: ["is-capacitor", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.appUpgrades.isCapacitor({ appId }),
});
}
queryClient.invalidateQueries({ queryKey: ["versions", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.versions.list({ appId }),
});
},
});
......
......@@ -24,6 +24,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { queryKeys } from "@/lib/queryKeys";
interface CapacitorControlsProps {
appId: number;
......@@ -42,7 +43,7 @@ export function CapacitorControls({ appId }: CapacitorControlsProps) {
// Check if Capacitor is installed
const { data: isCapacitor, isLoading } = useQuery({
queryKey: ["is-capacitor", appId],
queryKey: queryKeys.appUpgrades.isCapacitor({ appId }),
queryFn: () => IpcClient.getInstance().isCapacitor({ appId }),
enabled: appId !== undefined && appId !== null,
});
......
......@@ -28,7 +28,7 @@ import { PriceBadge } from "@/components/PriceBadge";
import { TURBO_MODELS } from "@/ipc/shared/language_model_constants";
import { cn } from "@/lib/utils";
import { useQueryClient } from "@tanstack/react-query";
import { TOKEN_COUNT_QUERY_KEY } from "@/hooks/useCountTokens";
import { queryKeys } from "@/lib/queryKeys";
export function ModelPicker() {
const { settings, updateSettings } = useSettings();
......@@ -37,7 +37,7 @@ export function ModelPicker() {
updateSettings({ selectedModel: model });
// Invalidate token count when model changes since different models have different context windows
// (technically they have different tokenizers, but we don't keep track of that).
queryClient.invalidateQueries({ queryKey: TOKEN_COUNT_QUERY_KEY });
queryClient.invalidateQueries({ queryKey: queryKeys.tokenCount.all });
};
const [open, setOpen] = useState(false);
......
......@@ -36,11 +36,12 @@ import { ContextFilesPicker } from "@/components/ContextFilesPicker";
import { FileAttachmentDropdown } from "./FileAttachmentDropdown";
import { CustomThemeDialog } from "@/components/CustomThemeDialog";
import { useThemes } from "@/hooks/useThemes";
import { useAppTheme, APP_THEME_QUERY_KEY } from "@/hooks/useAppTheme";
import { useAppTheme } from "@/hooks/useAppTheme";
import { useCustomThemes } from "@/hooks/useCustomThemes";
import { useSettings } from "@/hooks/useSettings";
import { IpcClient } from "@/ipc/ipc_client";
import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
interface AuxiliaryActionsMenuProps {
onFileSelect: (
......@@ -108,7 +109,9 @@ export function AuxiliaryActionsMenu({
themeId,
});
// Invalidate app theme query to refresh
queryClient.invalidateQueries({ queryKey: APP_THEME_QUERY_KEY(appId) });
queryClient.invalidateQueries({
queryKey: queryKeys.appTheme.byApp({ appId }),
});
} else {
// Update default theme in settings (for new apps)
// Store as string for settings (empty string for no theme)
......@@ -126,7 +129,7 @@ export function AuxiliaryActionsMenu({
if (!open) {
// Refresh custom themes when dialog closes
queryClient.invalidateQueries({
queryKey: ["custom-themes"],
queryKey: queryKeys.customThemes.all,
});
}
};
......
......@@ -80,7 +80,7 @@ import { useChatModeToggle } from "@/hooks/useChatModeToggle";
import { VisualEditingChangesDialog } from "@/components/preview_panel/VisualEditingChangesDialog";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
import { useQueryClient } from "@tanstack/react-query";
import { TOKEN_COUNT_QUERY_KEY } from "@/hooks/useCountTokens";
import { queryKeys } from "@/lib/queryKeys";
const showTokenBarAtom = atom(false);
......@@ -102,7 +102,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
const queryClient = useQueryClient();
const toggleShowTokenBar = useCallback(() => {
setShowTokenBar((prev) => !prev);
queryClient.invalidateQueries({ queryKey: TOKEN_COUNT_QUERY_KEY });
queryClient.invalidateQueries({ queryKey: queryKeys.tokenCount.all });
}, [setShowTokenBar, queryClient]);
const [selectedComponents, setSelectedComponents] = useAtom(
selectedComponentsPreviewAtom,
......
......@@ -24,6 +24,7 @@ import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client";
import { useNavigate } from "@tanstack/react-router";
import { NeonConfigure } from "./NeonConfigure";
import { queryKeys } from "@/lib/queryKeys";
const EnvironmentVariablesTitle = () => (
<div className="flex items-center gap-2">
......@@ -62,7 +63,7 @@ export const ConfigurePanel = () => {
isLoading,
error,
} = useQuery({
queryKey: ["app-env-vars", selectedAppId],
queryKey: queryKeys.appEnvVars.byApp({ appId: selectedAppId }),
queryFn: async () => {
if (!selectedAppId) return [];
const ipcClient = IpcClient.getInstance();
......@@ -83,7 +84,7 @@ export const ConfigurePanel = () => {
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["app-env-vars", selectedAppId],
queryKey: queryKeys.appEnvVars.byApp({ appId: selectedAppId }),
});
showSuccess("Environment variables saved");
},
......
......@@ -16,6 +16,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { useSettings } from "@/hooks/useSettings";
import { useCheckProblems } from "@/hooks/useCheckProblems";
import { getLanguage } from "@/utils/get_language";
import { queryKeys } from "@/lib/queryKeys";
interface FileEditorProps {
appId: number | null;
......@@ -195,7 +196,9 @@ export const FileEditor = ({
filePath,
currentValueRef.current,
);
await queryClient.invalidateQueries({ queryKey: ["versions", appId] });
await queryClient.invalidateQueries({
queryKey: queryKeys.versions.list({ appId }),
});
if (settings?.enableAutoFixProblems) {
checkProblems();
}
......
......@@ -10,6 +10,7 @@ import { useLoadApp } from "@/hooks/useLoadApp";
import { IpcClient } from "@/ipc/ipc_client";
import type { GetNeonProjectResponse, NeonBranch } from "@/ipc/ipc_types";
import { NeonDisconnectButton } from "@/components/NeonDisconnectButton";
import { queryKeys } from "@/lib/queryKeys";
const getBranchTypeColor = (type: NeonBranch["type"]) => {
switch (type) {
......@@ -40,7 +41,7 @@ export const NeonConfigure = () => {
isLoading,
error,
} = useQuery<GetNeonProjectResponse, Error>({
queryKey: ["neon-project", selectedAppId],
queryKey: queryKeys.neon.project({ appId: selectedAppId }),
queryFn: async () => {
if (!selectedAppId) throw new Error("No app selected");
const ipcClient = IpcClient.getInstance();
......
......@@ -3,6 +3,7 @@ import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useSecurityReview } from "@/hooks/useSecurityReview";
import { IpcClient } from "@/ipc/ipc_client";
import { queryKeys } from "@/lib/queryKeys";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
......@@ -748,7 +749,7 @@ export const SecurityPanel = () => {
rulesContent,
);
await queryClient.invalidateQueries({
queryKey: ["versions", selectedAppId],
queryKey: queryKeys.versions.list({ appId: selectedAppId }),
});
if (warning) {
showWarning(warning);
......
......@@ -18,6 +18,7 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
interface ModelsSectionProps {
providerId: string;
......@@ -35,10 +36,10 @@ export function ModelsSection({ providerId }: ModelsSectionProps) {
const invalidateModels = () => {
queryClient.invalidateQueries({
queryKey: ["language-models", providerId],
queryKey: queryKeys.languageModels.forProvider({ providerId }),
});
queryClient.invalidateQueries({
queryKey: ["language-models-by-providers"],
queryKey: queryKeys.languageModels.byProviders,
});
};
......
......@@ -7,6 +7,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import type { AgentToolName } from "../pro/main/ipc/handlers/local_agent/tool_definitions";
import type { AgentTool } from "@/ipc/ipc_types";
import { AgentToolConsent } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys";
// Re-export types for convenience
export type { AgentToolName, AgentTool };
......@@ -15,7 +16,7 @@ export function useAgentTools() {
const queryClient = useQueryClient();
const toolsQuery = useQuery({
queryKey: ["agent-tools"],
queryKey: queryKeys.agentTools.all,
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getAgentTools();
......@@ -31,7 +32,7 @@ export function useAgentTools() {
return ipcClient.setAgentToolConsent(params);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["agent-tools"] });
queryClient.invalidateQueries({ queryKey: queryKeys.agentTools.all });
},
});
......
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
export const APP_THEME_QUERY_KEY = (appId: number | undefined) => [
"app-theme",
appId,
];
import { queryKeys } from "@/lib/queryKeys";
export function useAppTheme(appId: number | undefined) {
const queryClient = useQueryClient();
const query = useQuery({
queryKey: APP_THEME_QUERY_KEY(appId),
queryKey: queryKeys.appTheme.byApp({ appId }),
queryFn: async (): Promise<string | null> => {
return IpcClient.getInstance().getAppTheme({ appId: appId! });
},
......@@ -18,7 +14,9 @@ export function useAppTheme(appId: number | undefined) {
});
const invalidate = () => {
queryClient.invalidateQueries({ queryKey: APP_THEME_QUERY_KEY(appId) });
queryClient.invalidateQueries({
queryKey: queryKeys.appTheme.byApp({ appId }),
});
};
return {
......
import { IpcClient } from "@/ipc/ipc_client";
import type { ChatSummary } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys";
import { useQuery, useQueryClient } from "@tanstack/react-query";
export const CHATS_QUERY_KEY = "chats";
export function useChats(appId: number | null) {
const queryClient = useQueryClient();
const { data, isLoading } = useQuery<ChatSummary[]>({
queryKey: [CHATS_QUERY_KEY, appId],
queryKey: queryKeys.chats.list({ appId }),
queryFn: async () => {
return IpcClient.getInstance().getChats(appId ?? undefined);
},
......@@ -17,7 +16,7 @@ export function useChats(appId: number | null) {
const invalidateChats = () => {
// Invalidate all chat queries (any appId) since mutations affect both
// app-specific lists and the global list (appId=null)
queryClient.invalidateQueries({ queryKey: [CHATS_QUERY_KEY] });
queryClient.invalidateQueries({ queryKey: queryKeys.chats.all });
};
return {
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { queryKeys } from "@/lib/queryKeys";
export const useCheckName = (appName: string) => {
return useQuery({
queryKey: ["checkAppName", appName],
queryKey: queryKeys.appName.check({ name: appName }),
queryFn: async () => {
const result = await IpcClient.getInstance().checkAppName({ appName });
return result;
......
......@@ -2,6 +2,7 @@ import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { ProblemReport } from "@/ipc/ipc_types";
import { useSettings } from "./useSettings";
import { queryKeys } from "@/lib/queryKeys";
export function useCheckProblems(appId: number | null) {
const { settings } = useSettings();
......@@ -11,7 +12,7 @@ export function useCheckProblems(appId: number | null) {
error,
refetch: checkProblems,
} = useQuery<ProblemReport, Error>({
queryKey: ["problems", appId],
queryKey: queryKeys.problems.byApp({ appId }),
queryFn: async (): Promise<ProblemReport> => {
if (!appId) {
throw new Error("App ID is required");
......
......@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { useSetAtom } from "jotai";
import { activeCheckoutCounterAtom } from "@/store/appAtoms";
import { queryKeys } from "@/lib/queryKeys";
interface CheckoutVersionVariables {
appId: number;
......@@ -30,10 +31,10 @@ export function useCheckoutVersion() {
onSuccess: (_, variables) => {
// Invalidate queries that depend on the current version/branch
queryClient.invalidateQueries({
queryKey: ["currentBranch", variables.appId],
queryKey: queryKeys.branches.current({ appId: variables.appId }),
});
queryClient.invalidateQueries({
queryKey: ["versions", variables.appId],
queryKey: queryKeys.versions.list({ appId: variables.appId }),
});
},
meta: { showErrorToast: true },
......
import { IpcClient } from "@/ipc/ipc_client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { showError, showSuccess } from "@/lib/toast";
import { queryKeys } from "@/lib/queryKeys";
export function useCommitChanges() {
const queryClient = useQueryClient();
......@@ -19,9 +20,13 @@ export function useCommitChanges() {
onSuccess: (_, { appId }) => {
showSuccess("Changes committed successfully");
// Invalidate uncommitted files query
queryClient.invalidateQueries({ queryKey: ["uncommittedFiles", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.uncommittedFiles.byApp({ appId }),
});
// Also invalidate versions query to update version count
queryClient.invalidateQueries({ queryKey: ["versions", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.versions.list({ appId }),
});
},
onError: (error: Error) => {
showError(`Failed to commit: ${error.message}`);
......
......@@ -3,6 +3,7 @@ import { useAtomValue } from "jotai";
import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { IpcClient } from "@/ipc/ipc_client";
import { GlobPath, ContextPathResults } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys";
export function useContextPaths() {
const queryClient = useQueryClient();
......@@ -13,7 +14,7 @@ export function useContextPaths() {
isLoading,
error,
} = useQuery<ContextPathResults, Error>({
queryKey: ["context-paths", appId],
queryKey: queryKeys.contextPaths.byApp({ appId }),
queryFn: async () => {
if (!appId)
return {
......@@ -53,7 +54,9 @@ export function useContextPaths() {
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["context-paths", appId] });
queryClient.invalidateQueries({
queryKey: queryKeys.contextPaths.byApp({ appId }),
});
},
});
......
......@@ -6,8 +6,7 @@ import {
import { IpcClient } from "@/ipc/ipc_client";
import type { TokenCountResult } from "@/ipc/ipc_types";
import { useCallback, useEffect, useState } from "react";
export const TOKEN_COUNT_QUERY_KEY = ["tokenCount"] as const;
import { queryKeys } from "@/lib/queryKeys";
export function useCountTokens(chatId: number | null, input: string = "") {
const queryClient = useQueryClient();
......@@ -35,7 +34,7 @@ export function useCountTokens(chatId: number | null, input: string = "") {
error,
refetch,
} = useQuery<TokenCountResult | null>({
queryKey: [...TOKEN_COUNT_QUERY_KEY, chatId, debouncedInput],
queryKey: queryKeys.tokenCount.forChat({ chatId, input: debouncedInput }),
queryFn: async () => {
if (chatId === null) return null;
return IpcClient.getInstance().countTokens({
......@@ -49,7 +48,7 @@ export function useCountTokens(chatId: number | null, input: string = "") {
// For imperative invalidation (e.g., after streaming completes)
const invalidateTokenCount = useCallback(() => {
queryClient.invalidateQueries({ queryKey: TOKEN_COUNT_QUERY_KEY });
queryClient.invalidateQueries({ queryKey: queryKeys.tokenCount.all });
}, [queryClient]);
return {
......
......@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { showError } from "@/lib/toast";
import type { CreateAppParams, CreateAppResult } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export function useCreateApp() {
const queryClient = useQueryClient();
......@@ -17,7 +18,7 @@ export function useCreateApp() {
},
onSuccess: () => {
// Invalidate apps list to trigger refetch
queryClient.invalidateQueries({ queryKey: ["apps"] });
queryClient.invalidateQueries({ queryKey: queryKeys.apps.all });
},
onError: (error) => {
showError(error);
......
import { IpcClient } from "@/ipc/ipc_client";
import { useQuery } from "@tanstack/react-query";
import type { BranchResult } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export function useCurrentBranch(appId: number | null) {
const {
......@@ -8,7 +9,7 @@ export function useCurrentBranch(appId: number | null) {
isLoading,
refetch: refetchBranchInfo,
} = useQuery<BranchResult, Error>({
queryKey: ["currentBranch", appId],
queryKey: queryKeys.branches.current({ appId }),
queryFn: async (): Promise<BranchResult> => {
if (appId === null) {
// This case should ideally be handled by the `enabled` option
......
......@@ -5,6 +5,7 @@ import type {
LanguageModelProvider,
} from "@/ipc/ipc_types";
import { showError } from "@/lib/toast";
import { queryKeys } from "@/lib/queryKeys";
export function useCustomLanguageModelProvider() {
const queryClient = useQueryClient();
......@@ -33,7 +34,9 @@ export function useCustomLanguageModelProvider() {
},
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ["languageModelProviders"] });
queryClient.invalidateQueries({
queryKey: queryKeys.languageModels.providers,
});
},
onError: (error) => {
showError(error);
......@@ -63,7 +66,9 @@ export function useCustomLanguageModelProvider() {
},
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ["languageModelProviders"] });
queryClient.invalidateQueries({
queryKey: queryKeys.languageModels.providers,
});
},
onError: (error) => {
showError(error);
......@@ -80,7 +85,9 @@ export function useCustomLanguageModelProvider() {
},
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ["languageModelProviders"] });
queryClient.invalidateQueries({
queryKey: queryKeys.languageModels.providers,
});
},
onError: (error) => {
showError(error);
......
......@@ -7,16 +7,14 @@ import type {
GenerateThemePromptParams,
GenerateThemePromptResult,
} from "@/ipc/ipc_types";
// Query key for custom themes
export const CUSTOM_THEMES_QUERY_KEY = ["custom-themes"];
import { queryKeys } from "@/lib/queryKeys";
/**
* Hook to fetch all custom themes.
*/
export function useCustomThemes() {
const query = useQuery({
queryKey: CUSTOM_THEMES_QUERY_KEY,
queryKey: queryKeys.customThemes.all,
queryFn: async (): Promise<CustomTheme[]> => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getCustomThemes();
......@@ -47,7 +45,7 @@ export function useCreateCustomTheme() {
onSuccess: () => {
// Invalidate all custom theme queries using prefix matching
queryClient.invalidateQueries({
queryKey: ["custom-themes"],
queryKey: queryKeys.customThemes.all,
});
},
});
......@@ -66,7 +64,7 @@ export function useUpdateCustomTheme() {
onSuccess: () => {
// Invalidate all custom theme queries using prefix matching
queryClient.invalidateQueries({
queryKey: ["custom-themes"],
queryKey: queryKeys.customThemes.all,
});
},
});
......@@ -83,7 +81,7 @@ export function useDeleteCustomTheme() {
onSuccess: () => {
// Invalidate all custom theme queries using prefix matching
queryClient.invalidateQueries({
queryKey: ["custom-themes"],
queryKey: queryKeys.customThemes.all,
});
},
});
......
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { queryKeys } from "@/lib/queryKeys";
interface DeleteCustomModelParams {
providerId: string;
......@@ -29,11 +30,13 @@ export function useDeleteCustomModel({
onSuccess: (data, params: DeleteCustomModelParams) => {
// Invalidate queries related to language models for the specific provider
queryClient.invalidateQueries({
queryKey: ["language-models", params.providerId],
queryKey: queryKeys.languageModels.forProvider({
providerId: params.providerId,
}),
});
// Invalidate general model list if needed
queryClient.invalidateQueries({
queryKey: ["language-models-by-providers"],
queryKey: queryKeys.languageModels.byProviders,
});
onSuccess?.();
},
......
......@@ -7,13 +7,14 @@ import {
VertexProviderSetting,
AzureProviderSetting,
} from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys";
export function useLanguageModelProviders() {
const ipcClient = IpcClient.getInstance();
const { settings, envVars } = useSettings();
const queryResult = useQuery<LanguageModelProvider[], Error>({
queryKey: ["languageModelProviders"],
queryKey: queryKeys.languageModels.providers,
queryFn: async () => {
return ipcClient.getLanguageModelProviders();
},
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { LanguageModel } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
/**
* Fetches all available language models grouped by their provider IDs.
......@@ -11,7 +12,7 @@ export function useLanguageModelsByProviders() {
const ipcClient = IpcClient.getInstance();
return useQuery<Record<string, LanguageModel[]>, Error>({
queryKey: ["language-models-by-providers"],
queryKey: queryKeys.languageModels.byProviders,
queryFn: async () => {
return ipcClient.getLanguageModelsByProviders();
},
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { LanguageModel } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
/**
* Fetches the list of available language models for a specific provider.
......@@ -15,7 +16,9 @@ export function useLanguageModelsForProvider(providerId: string | undefined) {
LanguageModel[],
Error // Specify Error type for better error handling
>({
queryKey: ["language-models", providerId],
queryKey: queryKeys.languageModels.forProvider({
providerId: providerId ?? "",
}),
queryFn: async () => {
if (!providerId) {
// Avoid calling IPC if providerId is not set
......
......@@ -4,6 +4,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import { useAtom } from "jotai";
import { currentAppAtom } from "@/atoms/appAtoms";
import { App } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export function useLoadApp(appId: number | null) {
const [, setApp] = useAtom(currentAppAtom);
......@@ -14,7 +15,7 @@ export function useLoadApp(appId: number | null) {
error,
refetch: refreshApp,
} = useQuery<App | null, Error>({
queryKey: ["app", appId],
queryKey: queryKeys.apps.detail({ appId }),
queryFn: async () => {
if (appId === null) {
return null;
......@@ -44,5 +45,7 @@ export const invalidateAppQuery = (
queryClient: QueryClient,
{ appId }: { appId: number | null },
) => {
return queryClient.invalidateQueries({ queryKey: ["app", appId] });
return queryClient.invalidateQueries({
queryKey: queryKeys.apps.detail({ appId }),
});
};
......@@ -8,6 +8,7 @@ import type {
McpToolConsent,
CreateMcpServer,
} from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export type Transport = "stdio" | "http";
......@@ -15,7 +16,7 @@ export function useMcp() {
const queryClient = useQueryClient();
const serversQuery = useQuery<McpServer[], Error>({
queryKey: ["mcp", "servers"],
queryKey: queryKeys.mcp.servers,
queryFn: async () => {
const ipc = IpcClient.getInstance();
const list = await ipc.listMcpServers();
......@@ -30,7 +31,7 @@ export function useMcp() {
);
const toolsByServerQuery = useQuery<Record<number, McpTool[]>, Error>({
queryKey: ["mcp", "tools-by-server", serverIds],
queryKey: queryKeys.mcp.toolsByServer.list({ serverIds }),
enabled: serverIds.length > 0,
queryFn: async () => {
const ipc = IpcClient.getInstance();
......@@ -43,7 +44,7 @@ export function useMcp() {
});
const consentsQuery = useQuery<McpToolConsent[], Error>({
queryKey: ["mcp", "consents"],
queryKey: queryKeys.mcp.consents,
queryFn: async () => {
const ipc = IpcClient.getInstance();
const list = await ipc.getMcpToolConsents();
......@@ -66,9 +67,9 @@ export function useMcp() {
return ipc.createMcpServer(params);
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
await queryClient.invalidateQueries({
queryKey: ["mcp", "tools-by-server"],
queryKey: queryKeys.mcp.toolsByServer.all,
});
},
meta: { showErrorToast: true },
......@@ -80,9 +81,9 @@ export function useMcp() {
return ipc.updateMcpServer(params);
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
await queryClient.invalidateQueries({
queryKey: ["mcp", "tools-by-server"],
queryKey: queryKeys.mcp.toolsByServer.all,
});
},
meta: { showErrorToast: true },
......@@ -94,9 +95,9 @@ export function useMcp() {
return ipc.deleteMcpServer(id);
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] });
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers });
await queryClient.invalidateQueries({
queryKey: ["mcp", "tools-by-server"],
queryKey: queryKeys.mcp.toolsByServer.all,
});
},
meta: { showErrorToast: true },
......@@ -112,7 +113,7 @@ export function useMcp() {
return ipc.setMcpToolConsent(params);
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["mcp", "consents"] });
await queryClient.invalidateQueries({ queryKey: queryKeys.mcp.consents });
},
meta: { showErrorToast: true },
});
......@@ -137,9 +138,11 @@ export function useMcp() {
const refetchAll = async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["mcp", "servers"] }),
queryClient.invalidateQueries({ queryKey: ["mcp", "tools-by-server"] }),
queryClient.invalidateQueries({ queryKey: ["mcp", "consents"] }),
queryClient.invalidateQueries({ queryKey: queryKeys.mcp.servers }),
queryClient.invalidateQueries({
queryKey: queryKeys.mcp.toolsByServer.all,
}),
queryClient.invalidateQueries({ queryKey: queryKeys.mcp.consents }),
]);
};
......
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { queryKeys } from "@/lib/queryKeys";
export interface PromptItem {
id: number;
......@@ -13,7 +14,7 @@ export interface PromptItem {
export function usePrompts() {
const queryClient = useQueryClient();
const listQuery = useQuery({
queryKey: ["prompts"],
queryKey: queryKeys.prompts.all,
queryFn: async (): Promise<PromptItem[]> => {
const ipc = IpcClient.getInstance();
return ipc.listPrompts();
......@@ -31,7 +32,7 @@ export function usePrompts() {
return ipc.createPrompt(params);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["prompts"] });
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
},
meta: {
showErrorToast: true,
......@@ -49,7 +50,7 @@ export function usePrompts() {
return ipc.updatePrompt(params);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["prompts"] });
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
},
meta: {
showErrorToast: true,
......@@ -62,7 +63,7 @@ export function usePrompts() {
return ipc.deletePrompt(id);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["prompts"] });
queryClient.invalidateQueries({ queryKey: queryKeys.prompts.all });
},
meta: {
showErrorToast: true,
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { ProposalResult } from "@/lib/schemas";
import { queryKeys } from "@/lib/queryKeys";
export function useProposal(chatId?: number | undefined) {
const {
......@@ -9,7 +10,7 @@ export function useProposal(chatId?: number | undefined) {
error,
refetch: refreshProposal,
} = useQuery<ProposalResult | null, Error>({
queryKey: ["proposal", chatId],
queryKey: queryKeys.proposals.detail({ chatId }),
queryFn: async (): Promise<ProposalResult | null> => {
if (chatId === undefined) {
return null;
......
......@@ -3,6 +3,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import { showError } from "@/lib/toast";
import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useAtomValue } from "jotai";
import { queryKeys } from "@/lib/queryKeys";
interface RenameBranchParams {
appId: number;
......@@ -30,10 +31,10 @@ export function useRenameBranch() {
onSuccess: (_, variables) => {
// Invalidate queries that depend on branch information
queryClient.invalidateQueries({
queryKey: ["currentBranch", variables.appId],
queryKey: queryKeys.branches.current({ appId: variables.appId }),
});
queryClient.invalidateQueries({
queryKey: ["versions", variables.appId],
queryKey: queryKeys.versions.list({ appId: variables.appId }),
});
// Potentially show a success message or trigger other actions
},
......
import { IpcClient } from "@/ipc/ipc_client";
import type { AppFileSearchResult } from "@/ipc/ipc_types";
import { useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
export function useSearchAppFiles(appId: number | null, query: string) {
const trimmedQuery = query.trim();
const enabled = Boolean(appId != null && trimmedQuery.length > 0);
const { data, isFetching, isLoading, error } = useQuery({
queryKey: ["search-app-files", appId, trimmedQuery],
queryKey: queryKeys.files.search({ appId, query: trimmedQuery }),
enabled,
queryFn: async (): Promise<AppFileSearchResult[]> => {
return IpcClient.getInstance().searchAppFiles(appId!, trimmedQuery);
......
import { IpcClient } from "@/ipc/ipc_client";
import { AppSearchResult } from "@/lib/schemas";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
export function useSearchApps(query: string) {
const enabled = Boolean(query && query.trim().length > 0);
const { data, isFetching, isLoading } = useQuery({
queryKey: ["search-apps", query],
queryKey: queryKeys.apps.search({ query }),
enabled,
queryFn: async (): Promise<AppSearchResult[]> => {
return IpcClient.getInstance().searchApps(query);
......
import { IpcClient } from "@/ipc/ipc_client";
import type { ChatSearchResult } from "@/lib/schemas";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
export function useSearchChats(appId: number | null, query: string) {
const enabled = Boolean(appId && query && query.trim().length > 0);
const { data, isFetching, isLoading } = useQuery({
queryKey: ["search-chats", appId, query],
queryKey: queryKeys.chats.search({ appId, query }),
enabled,
queryFn: async (): Promise<ChatSearchResult[]> => {
// Non-null assertion safe due to enabled guard
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { queryKeys } from "@/lib/queryKeys";
export function useSecurityReview(appId: number | null) {
return useQuery({
queryKey: ["security-review", appId],
queryKey: queryKeys.securityReview.byApp({ appId }),
queryFn: async () => {
if (!appId) {
throw new Error("App ID is required");
......
......@@ -28,6 +28,7 @@ import { usePostHog } from "posthog-js/react";
import { useCheckProblems } from "./useCheckProblems";
import { useSettings } from "./useSettings";
import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "@/lib/queryKeys";
export function getRandomNumberId() {
return Math.floor(Math.random() * 1_000_000_000_000_000);
......@@ -162,7 +163,9 @@ export function useStreamChat({
});
}
// Use queryClient directly with the chatId parameter to avoid stale closure issues
queryClient.invalidateQueries({ queryKey: ["proposal", chatId] });
queryClient.invalidateQueries({
queryKey: queryKeys.proposals.detail({ chatId }),
});
refetchUserBudget();
......
......@@ -12,12 +12,7 @@ import {
} from "@/ipc/ipc_types";
import { useSettings } from "./useSettings";
import { isSupabaseConnected } from "@/lib/schemas";
const SUPABASE_QUERY_KEYS = {
organizations: ["supabase", "organizations"] as const,
projects: ["supabase", "projects"] as const,
branches: (projectId: string) => ["supabase", "branches", projectId] as const,
};
import { queryKeys } from "@/lib/queryKeys";
export interface UseSupabaseOptions {
branchesProjectId?: string | null;
......@@ -37,7 +32,7 @@ export function useSupabase(options: UseSupabaseOptions = {}) {
// Query: Load all connected Supabase organizations
// Only runs when Supabase is connected to avoid unnecessary API calls
const organizationsQuery = useQuery<SupabaseOrganizationInfo[], Error>({
queryKey: SUPABASE_QUERY_KEYS.organizations,
queryKey: queryKeys.supabase.organizations,
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
return ipcClient.listSupabaseOrganizations();
......@@ -49,7 +44,7 @@ export function useSupabase(options: UseSupabaseOptions = {}) {
// Query: Load Supabase projects from all connected organizations
// Only runs when there are connected organizations to avoid unauthorized errors
const projectsQuery = useQuery<SupabaseProject[], Error>({
queryKey: SUPABASE_QUERY_KEYS.projects,
queryKey: queryKeys.supabase.projects,
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
return ipcClient.listAllSupabaseProjects();
......@@ -70,9 +65,9 @@ export function useSupabase(options: UseSupabaseOptions = {}) {
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: SUPABASE_QUERY_KEYS.organizations,
queryKey: queryKeys.supabase.organizations,
});
queryClient.invalidateQueries({ queryKey: SUPABASE_QUERY_KEYS.projects });
queryClient.invalidateQueries({ queryKey: queryKeys.supabase.projects });
},
meta: { showErrorToast: true },
});
......@@ -101,10 +96,10 @@ export function useSupabase(options: UseSupabaseOptions = {}) {
// Query: Load branches for a Supabase project
const branchesQuery = useQuery<SupabaseBranch[], Error>({
queryKey: [
...SUPABASE_QUERY_KEYS.branches(branchesProjectId ?? ""),
branchesOrganizationSlug ?? null,
],
queryKey: queryKeys.supabase.branches({
projectId: branchesProjectId ?? "",
organizationSlug: branchesOrganizationSlug ?? null,
}),
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
const list = await ipcClient.listSupabaseBranches({
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { localTemplatesData, type Template } from "@/shared/templates";
import { queryKeys } from "@/lib/queryKeys";
export function useTemplates() {
const query = useQuery({
queryKey: ["templates"],
queryKey: queryKeys.templates.all,
queryFn: async (): Promise<Template[]> => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getTemplates();
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { themesData, type Theme } from "@/shared/themes";
import { queryKeys } from "@/lib/queryKeys";
export function useThemes() {
const query = useQuery({
queryKey: ["themes"],
queryKey: queryKeys.themes.all,
queryFn: async (): Promise<Theme[]> => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getThemes();
......
import { IpcClient } from "@/ipc/ipc_client";
import { useQuery } from "@tanstack/react-query";
import type { UncommittedFile } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export type { UncommittedFile };
......@@ -10,7 +11,7 @@ export function useUncommittedFiles(appId: number | null) {
isLoading,
refetch: refetchUncommittedFiles,
} = useQuery<UncommittedFile[], Error>({
queryKey: ["uncommittedFiles", appId],
queryKey: queryKeys.uncommittedFiles.byApp({ appId }),
queryFn: async (): Promise<UncommittedFile[]> => {
if (appId === null) {
throw new Error("appId is null, cannot fetch uncommitted files.");
......
import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { UserBudgetInfo } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
const FIVE_MINUTES_IN_MS = 5 * 60 * 1000;
export function useUserBudgetInfo() {
const queryKey = ["userBudgetInfo"];
const { data, isLoading, error, isFetching, refetch } = useQuery<
UserBudgetInfo | null,
Error,
UserBudgetInfo | null
>({
queryKey: queryKey,
queryKey: queryKeys.userBudget.info,
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getUserBudget();
......
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { VercelDeployment } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
export function useVercelDeployments(appId: number) {
const queryClient = useQueryClient();
......@@ -11,7 +12,7 @@ export function useVercelDeployments(appId: number) {
error,
refetch,
} = useQuery<VercelDeployment[], Error>({
queryKey: ["vercel-deployments", appId],
queryKey: queryKeys.vercel.deployments({ appId }),
queryFn: async () => {
const ipcClient = IpcClient.getInstance();
return ipcClient.getVercelDeployments({ appId });
......@@ -26,7 +27,9 @@ export function useVercelDeployments(appId: number) {
},
onSuccess: () => {
// Clear deployments cache when project is disconnected
queryClient.removeQueries({ queryKey: ["vercel-deployments", appId] });
queryClient.removeQueries({
queryKey: queryKeys.vercel.deployments({ appId }),
});
},
});
......
......@@ -6,6 +6,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import { chatMessagesByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import type { RevertVersionResponse, Version } from "@/ipc/ipc_types";
import { queryKeys } from "@/lib/queryKeys";
import { toast } from "sonner";
export function useVersions(appId: number | null) {
......@@ -20,7 +21,7 @@ export function useVersions(appId: number | null) {
error,
refetch: refreshVersions,
} = useQuery<Version[], Error>({
queryKey: ["versions", appId],
queryKey: queryKeys.versions.list({ appId }),
queryFn: async (): Promise<Version[]> => {
if (appId === null) {
return [];
......@@ -71,9 +72,11 @@ export function useVersions(appId: number | null) {
} else if ("warningMessage" in result) {
toast.warning(result.warningMessage);
}
await queryClient.invalidateQueries({ queryKey: ["versions", appId] });
await queryClient.invalidateQueries({
queryKey: ["currentBranch", appId],
queryKey: queryKeys.versions.list({ appId }),
});
await queryClient.invalidateQueries({
queryKey: queryKeys.branches.current({ appId }),
});
if (selectedChatId) {
const chat = await IpcClient.getInstance().getChat(selectedChatId);
......@@ -84,7 +87,7 @@ export function useVersions(appId: number | null) {
});
}
await queryClient.invalidateQueries({
queryKey: ["problems", appId],
queryKey: queryKeys.problems.byApp({ appId }),
});
},
meta: { showErrorToast: true },
......
差异被折叠。
......@@ -18,6 +18,7 @@ import {
pendingAgentConsentsAtom,
agentTodosByChatIdAtom,
} from "./atoms/chatAtoms";
import { queryKeys } from "./lib/queryKeys";
// @ts-ignore
console.log("Running in mode:", import.meta.env.MODE);
......@@ -202,7 +203,10 @@ function App() {
useEffect(() => {
const ipc = IpcClient.getInstance();
const unsubscribe = ipc.onAgentProblemsUpdate((payload) => {
queryClient.setQueryData(["problems", payload.appId], payload.problems);
queryClient.setQueryData(
queryKeys.problems.byApp({ appId: payload.appId }),
payload.problems,
);
});
return () => unsubscribe();
}, []);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论