diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index 08769068909c..c84b333fe2c5 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/packages/app/src/components/dialog-select-server.tsx @@ -19,6 +19,7 @@ import { normalizeServerUrl, ServerConnection, useServer } from "@/context/serve import { type ServerHealth, useCheckServerHealth } from "@/utils/server-health" import { useSettings } from "@/context/settings" import { useTabs } from "@/context/tabs" +import { completeServerForm } from "./server-form-lifecycle" const DEFAULT_USERNAME = "opencode" @@ -189,7 +190,9 @@ export function DialogSelectServer() { ) } -export function useServerManagementController(options: { onSelect?: () => void; navigateOnAdd?: boolean } = {}) { +export function useServerManagementController( + options: { onSelect?: () => void; onFormExit?: () => void; navigateOnAdd?: boolean } = {}, +) { const navigate = useNavigate() const server = useServer() const tabs = useTabs() @@ -247,7 +250,7 @@ export function useServerManagementController(options: { onSelect?: () => void; mutationFn: async (value: string) => { const normalized = normalizeServerUrl(value) if (!normalized) { - resetAdd() + completeServerForm(resetAdd, options.onFormExit) return } @@ -264,13 +267,16 @@ export function useServerManagementController(options: { onSelect?: () => void; return } - resetAdd() if (options.navigateOnAdd === false) { - server.add(conn) - options.onSelect?.() + completeServerForm(resetAdd, options.onFormExit, () => { + server.add(conn) + options.onSelect?.() + }) return } + resetAdd() await select(conn, true) + options.onFormExit?.() }, })) @@ -279,7 +285,7 @@ export function useServerManagementController(options: { onSelect?: () => void; if (input.original.type !== "http") return const normalized = normalizeServerUrl(input.value) if (!normalized) { - resetEdit() + completeServerForm(resetEdit, options.onFormExit) return } @@ -293,7 +299,7 @@ export function useServerManagementController(options: { onSelect?: () => void; username === input.original.http.username && password === input.original.http.password ) { - resetEdit() + completeServerForm(resetEdit, options.onFormExit) return } @@ -313,7 +319,7 @@ export function useServerManagementController(options: { onSelect?: () => void; replaceServer(input.original, conn) } - resetEdit() + completeServerForm(resetEdit, options.onFormExit) }, })) @@ -506,7 +512,7 @@ export function useServerManagementController(options: { onSelect?: () => void; createEffect(() => { if (!store.editServer.id) return if (editing()) return - resetEdit() + completeServerForm(resetEdit, options.onFormExit) }) async function handleRemove(key: ServerConnection.Key) { diff --git a/packages/app/src/components/server-form-lifecycle.test.ts b/packages/app/src/components/server-form-lifecycle.test.ts new file mode 100644 index 000000000000..8b0854369143 --- /dev/null +++ b/packages/app/src/components/server-form-lifecycle.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, test } from "bun:test" +import { completeServerForm } from "./server-form-lifecycle" + +describe("completeServerForm", () => { + test("preserves reset-before-exit ordering", () => { + const calls: string[] = [] + + completeServerForm( + () => calls.push("reset"), + () => calls.push("exit"), + () => calls.push("action"), + ) + + expect(calls).toEqual(["reset", "exit", "action"]) + }) + + test("preserves legacy reset behavior without an exit callback", () => { + let resets = 0 + + completeServerForm(() => resets++) + + expect(resets).toBe(1) + }) +}) diff --git a/packages/app/src/components/server-form-lifecycle.ts b/packages/app/src/components/server-form-lifecycle.ts new file mode 100644 index 000000000000..596d9722d55d --- /dev/null +++ b/packages/app/src/components/server-form-lifecycle.ts @@ -0,0 +1,5 @@ +export function completeServerForm(reset: () => void, onExit?: () => void, action?: () => void) { + reset() + onExit?.() + action?.() +} diff --git a/packages/app/src/components/settings-v2/dialog-server-v2.tsx b/packages/app/src/components/settings-v2/dialog-server-v2.tsx index 7113cdcf0a0a..c8291598ba83 100644 --- a/packages/app/src/components/settings-v2/dialog-server-v2.tsx +++ b/packages/app/src/components/settings-v2/dialog-server-v2.tsx @@ -3,7 +3,7 @@ import { Dialog, DialogBody, DialogFooter, DialogHeader, DialogTitle } from "@op import { DividerV2 } from "@opencode-ai/ui/v2/divider-v2" import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2" import { useDialog } from "@opencode-ai/ui/context/dialog" -import { type Component, Show, createEffect, createSignal, onCleanup, onMount } from "solid-js" +import { type Component, Show, onCleanup, onMount } from "solid-js" import { useLanguage } from "@/context/language" import { type ServerConnection } from "@/context/server" import { useServerManagementController } from "../dialog-select-server" @@ -16,27 +16,23 @@ export const DialogServerV2: Component<{ const dialog = useDialog() const language = useLanguage() const controller = useServerManagementController({ - onSelect: () => dialog.close(), + onFormExit: () => dialog.close(), navigateOnAdd: false, }) - const [opened, setOpened] = createSignal(false) onMount(() => { if (props.mode === "add") controller.startAdd() - if (props.mode === "edit" && props.server) controller.startEdit(props.server) - setOpened(true) + if (props.mode === "edit" && props.server) { + controller.startEdit(props.server) + return + } + if (props.mode === "edit") dialog.close() }) onCleanup(() => { controller.resetForm() }) - createEffect(() => { - if (!opened()) return - if (controller.isFormMode()) return - dialog.close() - }) - const keyDown = (event: KeyboardEvent) => { if (event.key !== "Enter" || event.isComposing) return event.preventDefault() diff --git a/packages/app/src/components/status-popover-body.tsx b/packages/app/src/components/status-popover-body.tsx index 68a3f6b22676..7f9ee813692c 100644 --- a/packages/app/src/components/status-popover-body.tsx +++ b/packages/app/src/components/status-popover-body.tsx @@ -248,7 +248,7 @@ function ServerStatusList(props: { state: ServerStatusState }) { ) } -export function StatusPopoverBody(props: { shown: Accessor }) { +export function StatusPopoverBody(_props: { shown: Accessor }) { const sync = useSync() const global = useGlobal() const server = useServer() @@ -266,10 +266,6 @@ export function StatusPopoverBody(props: { shown: Accessor }) { }) } - createEffect(() => { - if (!props.shown()) return - }) - let dialogRun = 0 let dialogDead = false onCleanup(() => { diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index efbab7809ee9..59976f07aa50 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -102,8 +102,6 @@ const HOME_SEARCH_RESULT_TITLE = const HOME_SEARCH_RESULT_META = "min-w-0 flex-[1_1_auto] overflow-hidden text-ellipsis whitespace-nowrap text-[13px] leading-4 tracking-[-0.04px] text-v2-text-text-muted [font-weight:440]" -let pendingHomeNavigation: { server: ServerConnection.Key; href: string } | undefined - function buildHomeSessionRecords(input: { sync: Pick projectDirectories: () => string[] @@ -416,13 +414,6 @@ export function NewHome() { if (conn) setSelection({ server: ServerConnection.key(conn) }) }) - createEffect(() => { - const pending = pendingHomeNavigation - if (!pending || pending.server !== server.key) return - pendingHomeNavigation = undefined - navigate(pending.href) - }) - function focusServer(conn: ServerConnection.Any) { setSelection({ server: ServerConnection.key(conn) }) }