summaryrefslogtreecommitdiffhomepage
path: root/packages/app
diff options
context:
space:
mode:
authorBrendan Allan <[email protected]>2026-04-17 10:27:08 +0800
committerGitHub <[email protected]>2026-04-17 02:27:08 +0000
commitf135c0b5eefae50e95f549002aea4bd1e510c4f7 (patch)
treebee09a1fed9fea9121eed7f955457b2369d0eac9 /packages/app
parentebe6ea580d6f7985b5ae805f373454079f1ca116 (diff)
downloadopencode-f135c0b5eefae50e95f549002aea4bd1e510c4f7.tar.gz
opencode-f135c0b5eefae50e95f549002aea4bd1e510c4f7.zip
app: use tanstack query to load session vcs state (#22277)
Diffstat (limited to 'packages/app')
-rw-r--r--packages/app/src/pages/session.tsx178
1 files changed, 45 insertions, 133 deletions
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index c4d642bf8..4ae973b85 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -1,6 +1,6 @@
-import type { Project, UserMessage, VcsFileDiff } from "@opencode-ai/sdk/v2"
+import type { Project, UserMessage } from "@opencode-ai/sdk/v2"
import { useDialog } from "@opencode-ai/ui/context/dialog"
-import { useMutation } from "@tanstack/solid-query"
+import { createQuery, skipToken, useMutation, useQueryClient } from "@tanstack/solid-query"
import {
batch,
onCleanup,
@@ -324,6 +324,7 @@ export default function Page() {
const local = useLocal()
const file = useFile()
const sync = useSync()
+ const queryClient = useQueryClient()
const dialog = useDialog()
const language = useLanguage()
const sdk = useSDK()
@@ -518,26 +519,6 @@ export default function Page() {
deferRender: false,
})
- const [vcs, setVcs] = createStore<{
- diff: {
- git: VcsFileDiff[]
- branch: VcsFileDiff[]
- }
- ready: {
- git: boolean
- branch: boolean
- }
- }>({
- diff: {
- git: [] as VcsFileDiff[],
- branch: [] as VcsFileDiff[],
- },
- ready: {
- git: false,
- branch: false,
- },
- })
-
const [followup, setFollowup] = persisted(
Persist.workspace(sdk.directory, "followup", ["followup.v1"]),
createStore<{
@@ -571,68 +552,6 @@ export default function Page() {
let todoTimer: number | undefined
let diffFrame: number | undefined
let diffTimer: number | undefined
- const vcsTask = new Map<VcsMode, Promise<void>>()
- const vcsRun = new Map<VcsMode, number>()
-
- const bumpVcs = (mode: VcsMode) => {
- const next = (vcsRun.get(mode) ?? 0) + 1
- vcsRun.set(mode, next)
- return next
- }
-
- const resetVcs = (mode?: VcsMode) => {
- const list = mode ? [mode] : (["git", "branch"] as const)
- list.forEach((item) => {
- bumpVcs(item)
- vcsTask.delete(item)
- setVcs("diff", item, [])
- setVcs("ready", item, false)
- })
- }
-
- const loadVcs = (mode: VcsMode, force = false) => {
- if (sync.project?.vcs !== "git") return Promise.resolve()
- if (!force && vcs.ready[mode]) return Promise.resolve()
-
- if (force) {
- if (vcsTask.has(mode)) bumpVcs(mode)
- vcsTask.delete(mode)
- setVcs("ready", mode, false)
- }
-
- const current = vcsTask.get(mode)
- if (current) return current
-
- const run = bumpVcs(mode)
-
- const task = sdk.client.vcs
- .diff({ mode })
- .then((result) => {
- if (vcsRun.get(mode) !== run) return
- setVcs("diff", mode, list(result.data))
- setVcs("ready", mode, true)
- })
- .catch((error) => {
- if (vcsRun.get(mode) !== run) return
- console.debug("[session-review] failed to load vcs diff", { mode, error })
- setVcs("diff", mode, [])
- setVcs("ready", mode, true)
- })
- .finally(() => {
- if (vcsTask.get(mode) === task) vcsTask.delete(mode)
- })
-
- vcsTask.set(mode, task)
- return task
- }
-
- const refreshVcs = () => {
- resetVcs()
- const mode = untrack(vcsMode)
- if (!mode) return
- if (!untrack(wantsReview)) return
- void loadVcs(mode, true)
- }
createComputed((prev) => {
const open = desktopReviewOpen()
@@ -663,21 +582,52 @@ export default function Page() {
list.push("turn")
return list
})
+ const mobileChanges = createMemo(() => !isDesktop() && store.mobileTab === "changes")
+ const wantsReview = createMemo(() =>
+ isDesktop()
+ ? desktopFileTreeOpen() || (desktopReviewOpen() && activeTab() === "review")
+ : store.mobileTab === "changes",
+ )
const vcsMode = createMemo<VcsMode | undefined>(() => {
if (store.changes === "git" || store.changes === "branch") return store.changes
})
- const reviewDiffs = createMemo(() => {
- if (store.changes === "git") return list(vcs.diff.git)
- if (store.changes === "branch") return list(vcs.diff.branch)
- return turnDiffs()
+ const vcsKey = createMemo(
+ () => ["session-vcs", sdk.directory, sync.data.vcs?.branch ?? "", sync.data.vcs?.default_branch ?? ""] as const,
+ )
+ const vcsQuery = createQuery(() => {
+ const mode = vcsMode()
+ const enabled = wantsReview() && sync.project?.vcs === "git"
+
+ return {
+ queryKey: [...vcsKey(), mode] as const,
+ enabled,
+ staleTime: Number.POSITIVE_INFINITY,
+ gcTime: 60 * 1000,
+ queryFn: mode
+ ? () =>
+ sdk.client.vcs
+ .diff({ mode })
+ .then((result) => list(result.data))
+ .catch((error) => {
+ console.debug("[session-review] failed to load vcs diff", { mode, error })
+ return []
+ })
+ : skipToken,
+ }
})
- const reviewCount = createMemo(() => reviewDiffs().length)
- const hasReview = createMemo(() => reviewCount() > 0)
- const reviewReady = createMemo(() => {
- if (store.changes === "git") return vcs.ready.git
- if (store.changes === "branch") return vcs.ready.branch
+ const refreshVcs = () => void queryClient.invalidateQueries({ queryKey: vcsKey() })
+ const reviewDiffs = () => {
+ if (store.changes === "git" || store.changes === "branch")
+ // avoids suspense
+ return vcsQuery.isFetched ? (vcsQuery.data ?? []) : []
+ return turnDiffs()
+ }
+ const reviewCount = () => reviewDiffs().length
+ const hasReview = () => reviewCount() > 0
+ const reviewReady = () => {
+ if (store.changes === "git" || store.changes === "branch") return !vcsQuery.isPending
return true
- })
+ }
const newSessionWorktree = createMemo(() => {
if (store.newSessionWorktree === "create") return "create"
@@ -897,27 +847,6 @@ export default function Page() {
),
)
- createEffect(
- on(
- () => sdk.directory,
- () => {
- resetVcs()
- },
- { defer: true },
- ),
- )
-
- createEffect(
- on(
- () => [sync.data.vcs?.branch, sync.data.vcs?.default_branch] as const,
- (next, prev) => {
- if (prev === undefined || same(next, prev)) return
- refreshVcs()
- },
- { defer: true },
- ),
- )
-
const stopVcs = sdk.event.listen((evt) => {
if (evt.details.type !== "file.watcher.updated") return
const props =
@@ -1051,13 +980,6 @@ export default function Page() {
}
}
- const mobileChanges = createMemo(() => !isDesktop() && store.mobileTab === "changes")
- const wantsReview = createMemo(() =>
- isDesktop()
- ? desktopFileTreeOpen() || (desktopReviewOpen() && activeTab() === "review")
- : store.mobileTab === "changes",
- )
-
createEffect(() => {
const list = changesOptions()
if (list.includes(store.changes)) return
@@ -1066,22 +988,12 @@ export default function Page() {
setStore("changes", next)
})
- createEffect(() => {
- const mode = vcsMode()
- if (!mode) return
- if (!wantsReview()) return
- void loadVcs(mode)
- })
-
createEffect(
on(
() => sync.data.session_status[params.id ?? ""]?.type,
(next, prev) => {
- const mode = vcsMode()
- if (!mode) return
- if (!wantsReview()) return
if (next !== "idle" || prev === undefined || prev === "idle") return
- void loadVcs(mode, true)
+ refreshVcs()
},
{ defer: true },
),