diff options
Diffstat (limited to 'packages/app/src/context/global-sync.tsx')
| -rw-r--r-- | packages/app/src/context/global-sync.tsx | 159 |
1 files changed, 84 insertions, 75 deletions
diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 86496bad5..2c80f31b1 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -14,17 +14,25 @@ import { createStore, produce, reconcile } from "solid-js/store" import { useLanguage } from "@/context/language" import type { InitError } from "../pages/error" import { useGlobalSDK } from "./global-sdk" -import { bootstrapDirectory, bootstrapGlobal, clearProviderRev } from "./global-sync/bootstrap" +import { + bootstrapDirectory, + bootstrapGlobal, + clearProviderRev, + loadGlobalConfigQuery, + loadPathQuery, + loadProjectsQuery, + loadProvidersQuery, +} from "./global-sync/bootstrap" import { createChildStoreManager } from "./global-sync/child-store" import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer" -import { createRefreshQueue } from "./global-sync/queue" import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch" import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load" import { trimSessions } from "./global-sync/session-trim" import type { ProjectMeta } from "./global-sync/types" import { SESSION_RECENT_LIMIT } from "./global-sync/types" import { formatServerError } from "@/utils/server-errors" -import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query" +import { queryOptions, skipToken, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query" +import { createRefreshQueue } from "./global-sync/queue" type GlobalStore = { ready: boolean @@ -43,6 +51,18 @@ type GlobalStore = { export const loadSessionsQuery = (directory: string) => queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken }) +export const loadMcpQuery = (directory: string, sdk?: OpencodeClient) => + queryOptions({ + queryKey: [directory, "mcp"], + queryFn: sdk ? () => sdk.mcp.status().then((r) => r.data ?? {}) : skipToken, + }) + +export const loadLspQuery = (directory: string, sdk?: OpencodeClient) => + queryOptions({ + queryKey: [directory, "lsp"], + queryFn: sdk ? () => sdk.lsp.status().then((r) => r.data ?? []) : skipToken, + }) + function createGlobalSync() { const globalSDK = useGlobalSDK() const language = useLanguage() @@ -54,15 +74,34 @@ function createGlobalSync() { const sessionLoads = new Map<string, Promise<void>>() const sessionMeta = new Map<string, { limit: number }>() + const [configQuery, providerQuery, pathQuery] = useQueries(() => ({ + queries: [loadGlobalConfigQuery(), loadProvidersQuery(null), loadPathQuery(null), loadProjectsQuery()], + })) + const [globalStore, setGlobalStore] = createStore<GlobalStore>({ - ready: false, - path: { state: "", config: "", worktree: "", directory: "", home: "" }, + get ready() { + return bootstrap.isPending + }, project: [], session_todo: {}, - provider: { all: [], connected: [], default: {} }, provider_auth: {}, - config: {}, - reload: undefined, + get path() { + const EMPTY = { state: "", config: "", worktree: "", directory: "", home: "" } + if (pathQuery.isLoading) return EMPTY + return pathQuery.data ?? EMPTY + }, + get provider() { + const EMPTY = { all: [], connected: [], default: {} } + if (providerQuery.isLoading) return EMPTY + return providerQuery.data ?? EMPTY + }, + get config() { + if (configQuery.isLoading) return {} + return configQuery.data ?? {} + }, + get reload() { + return updateConfigMutation.isPending ? "pending" : undefined + }, }) const queryClient = useQueryClient() @@ -88,6 +127,22 @@ function createGlobalSync() { return (setGlobalStore as (...args: unknown[]) => unknown)(...input) }) as typeof setGlobalStore + const bootstrap = useQuery(() => ({ + queryKey: ["bootstrap"], + queryFn: async () => { + await bootstrapGlobal({ + globalSDK: globalSDK.client, + requestFailedTitle: language.t("common.requestFailed"), + translate: language.t, + formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }), + setGlobalStore: setBootStore, + queryClient, + }) + bootedAt = Date.now() + return bootedAt + }, + })) + const set = ((...input: unknown[]) => { if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) { setProjects(input[1] as Project[] | ((draft: Project[]) => Project[])) @@ -114,10 +169,21 @@ function createGlobalSync() { const queue = createRefreshQueue({ paused, - bootstrap, + bootstrap: () => queryClient.fetchQuery({ queryKey: ["bootstrap"] }), bootstrapInstance, }) + const sdkFor = (directory: string) => { + const cached = sdkCache.get(directory) + if (cached) return cached + const sdk = globalSDK.createClient({ + directory, + throwOnError: true, + }) + sdkCache.set(directory, sdk) + return sdk + } + const children = createChildStoreManager({ owner, isBooting: (directory) => booting.has(directory), @@ -133,19 +199,9 @@ function createGlobalSync() { clearSessionPrefetchDirectory(directory) }, translate: language.t, + getSdk: sdkFor, }) - const sdkFor = (directory: string) => { - const cached = sdkCache.get(directory) - if (cached) return cached - const sdk = globalSDK.createClient({ - directory, - throwOnError: true, - }) - sdkCache.set(directory, sdk) - return sdk - } - async function loadSessions(directory: string) { const pending = sessionLoads.get(directory) if (pending) return pending @@ -264,26 +320,13 @@ function createGlobalSync() { const event = e.details const recent = bootingRoot || Date.now() - bootedAt < 1500 - if (event.type === "session.error") { - const error = event.properties.error - if (error?.name !== "MessageAbortedError") { - console.error("[global-sync] session error", { - scope: directory === "global" ? "global" : "workspace", - directory: directory === "global" ? undefined : directory, - project: directory === "global" ? undefined : getFilename(directory), - sessionID: event.properties.sessionID, - error, - }) - } - } - if (directory === "global") { applyGlobalEvent({ event, project: globalStore.project, refresh: () => { if (recent) return - queue.refresh() + bootstrap.refetch() }, setGlobalProject: setProjects, }) @@ -309,12 +352,7 @@ function createGlobalSync() { setSessionTodo, vcsCache: children.vcsCache.get(directory), loadLsp: () => { - void sdkFor(directory) - .lsp.status() - .then((x) => { - setStore("lsp", x.data ?? []) - setStore("lsp_ready", true) - }) + void queryClient.fetchQuery(loadLspQuery(directory, sdkFor(directory))) }, }) }) @@ -329,23 +367,6 @@ function createGlobalSync() { } }) - async function bootstrap() { - bootingRoot = true - try { - await bootstrapGlobal({ - globalSDK: globalSDK.client, - requestFailedTitle: language.t("common.requestFailed"), - translate: language.t, - formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }), - setGlobalStore: setBootStore, - queryClient, - }) - bootedAt = Date.now() - } finally { - bootingRoot = false - } - } - onMount(() => { if (typeof requestAnimationFrame === "function") { eventFrame = requestAnimationFrame(() => { @@ -361,7 +382,6 @@ function createGlobalSync() { void globalSDK.event.start() }, 0) } - void bootstrap() }) const projectApi = { @@ -374,21 +394,10 @@ function createGlobalSync() { }, } - const updateConfig = async (config: Config) => { - setGlobalStore("reload", "pending") - return globalSDK.client.global.config - .update({ config }) - .then(bootstrap) - .then(() => { - queue.refresh() - setGlobalStore("reload", undefined) - queue.refresh() - }) - .catch((error) => { - setGlobalStore("reload", undefined) - throw error - }) - } + const updateConfigMutation = useMutation(() => ({ + mutationFn: (config: Config) => globalSDK.client.global.config.update({ config }), + onSuccess: () => bootstrap.refetch(), + })) return { data: globalStore, @@ -401,8 +410,8 @@ function createGlobalSync() { }, child: children.child, peek: children.peek, - bootstrap, - updateConfig, + // bootstrap, + updateConfig: updateConfigMutation.mutateAsync, project: projectApi, todo: { set: setSessionTodo, |
