diff options
| author | Brendan Allan <[email protected]> | 2026-03-21 23:33:04 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-21 23:33:04 +0800 |
| commit | 6a16db4b929422b6f5ef7072ac889cec41ae1eb2 (patch) | |
| tree | 2663160c18632706372f83432d44f1a741a613bf /packages/app/src/pages/session/composer | |
| parent | 9ad6588f3e0066125033810a5e0e4dc08f0c6961 (diff) | |
| download | opencode-6a16db4b929422b6f5ef7072ac889cec41ae1eb2.tar.gz opencode-6a16db4b929422b6f5ef7072ac889cec41ae1eb2.zip | |
app: manage mutation loading states with tanstack query (#18501)
Diffstat (limited to 'packages/app/src/pages/session/composer')
| -rw-r--r-- | packages/app/src/pages/session/composer/session-question-dock.tsx | 84 |
1 files changed, 44 insertions, 40 deletions
diff --git a/packages/app/src/pages/session/composer/session-question-dock.tsx b/packages/app/src/pages/session/composer/session-question-dock.tsx index b66c27579..7ba07b15d 100644 --- a/packages/app/src/pages/session/composer/session-question-dock.tsx +++ b/packages/app/src/pages/session/composer/session-question-dock.tsx @@ -1,5 +1,6 @@ import { For, Show, createMemo, onCleanup, onMount, type Component } from "solid-js" import { createStore } from "solid-js/store" +import { useMutation } from "@tanstack/solid-query" import { Button } from "@opencode-ai/ui/button" import { DockPrompt } from "@opencode-ai/ui/dock-prompt" import { Icon } from "@opencode-ai/ui/icon" @@ -24,7 +25,6 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit custom: cached?.custom ?? ([] as string[]), customOn: cached?.customOn ?? ([] as boolean[]), editing: false, - sending: false, }) let root: HTMLDivElement | undefined @@ -126,36 +126,40 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit showToast({ title: language.t("common.requestFailed"), description: message }) } - const reply = async (answers: QuestionAnswer[]) => { - if (store.sending) return - - props.onSubmit() - setStore("sending", true) - try { - await sdk.client.question.reply({ requestID: props.request.id, answers }) + const replyMutation = useMutation(() => ({ + mutationFn: (answers: QuestionAnswer[]) => sdk.client.question.reply({ requestID: props.request.id, answers }), + onMutate: () => { + props.onSubmit() + }, + onSuccess: () => { replied = true cache.delete(props.request.id) - } catch (err) { - fail(err) - } finally { - setStore("sending", false) - } + }, + onError: fail, + })) + + const rejectMutation = useMutation(() => ({ + mutationFn: () => sdk.client.question.reject({ requestID: props.request.id }), + onMutate: () => { + props.onSubmit() + }, + onSuccess: () => { + replied = true + cache.delete(props.request.id) + }, + onError: fail, + })) + + const sending = createMemo(() => replyMutation.isPending || rejectMutation.isPending) + + const reply = async (answers: QuestionAnswer[]) => { + if (sending()) return + await replyMutation.mutateAsync(answers) } const reject = async () => { - if (store.sending) return - - props.onSubmit() - setStore("sending", true) - try { - await sdk.client.question.reject({ requestID: props.request.id }) - replied = true - cache.delete(props.request.id) - } catch (err) { - fail(err) - } finally { - setStore("sending", false) - } + if (sending()) return + await rejectMutation.mutateAsync() } const submit = () => void reply(questions().map((_, i) => store.answers[i] ?? [])) @@ -175,7 +179,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } const customToggle = () => { - if (store.sending) return + if (sending()) return if (!multi()) { setStore("customOn", store.tab, true) @@ -198,14 +202,14 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } const customOpen = () => { - if (store.sending) return + if (sending()) return if (!on()) setStore("customOn", store.tab, true) setStore("editing", true) customUpdate(input(), true) } const selectOption = (optIndex: number) => { - if (store.sending) return + if (sending()) return if (optIndex === options().length) { customOpen() @@ -227,7 +231,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } const next = () => { - if (store.sending) return + if (sending()) return if (store.editing) commitCustom() if (store.tab >= total() - 1) { @@ -240,14 +244,14 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } const back = () => { - if (store.sending) return + if (sending()) return if (store.tab <= 0) return setStore("tab", store.tab - 1) setStore("editing", false) } const jump = (tab: number) => { - if (store.sending) return + if (sending()) return setStore("tab", tab) setStore("editing", false) } @@ -270,7 +274,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit (store.answers[i()]?.length ?? 0) > 0 || (store.customOn[i()] === true && (store.custom[i()] ?? "").trim().length > 0) } - disabled={store.sending} + disabled={sending()} onClick={() => jump(i())} aria-label={`${language.t("ui.tool.questions")} ${i() + 1}`} /> @@ -281,16 +285,16 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } footer={ <> - <Button variant="ghost" size="large" disabled={store.sending} onClick={reject}> + <Button variant="ghost" size="large" disabled={sending()} onClick={reject}> {language.t("ui.common.dismiss")} </Button> <div data-slot="question-footer-actions"> <Show when={store.tab > 0}> - <Button variant="secondary" size="large" disabled={store.sending} onClick={back}> + <Button variant="secondary" size="large" disabled={sending()} onClick={back}> {language.t("ui.common.back")} </Button> </Show> - <Button variant={last() ? "primary" : "secondary"} size="large" disabled={store.sending} onClick={next}> + <Button variant={last() ? "primary" : "secondary"} size="large" disabled={sending()} onClick={next}> {last() ? language.t("ui.common.submit") : language.t("ui.common.next")} </Button> </div> @@ -311,7 +315,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit data-picked={picked()} role={multi() ? "checkbox" : "radio"} aria-checked={picked()} - disabled={store.sending} + disabled={sending()} onClick={() => selectOption(i())} > <span data-slot="question-option-check" aria-hidden="true"> @@ -345,7 +349,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit data-picked={on()} role={multi() ? "checkbox" : "radio"} aria-checked={on()} - disabled={store.sending} + disabled={sending()} onClick={customOpen} > <span @@ -377,7 +381,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit role={multi() ? "checkbox" : "radio"} aria-checked={on()} onMouseDown={(e) => { - if (store.sending) { + if (sending()) { e.preventDefault() return } @@ -419,7 +423,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit placeholder={language.t("ui.question.custom.placeholder")} value={input()} rows={1} - disabled={store.sending} + disabled={sending()} onKeyDown={(e) => { if (e.key === "Escape") { e.preventDefault() |
