Skip to content

Commit 6189341

Browse files
committed
refactor(app): unify project close actions
1 parent 52e049c commit 6189341

3 files changed

Lines changed: 3 additions & 117 deletions

File tree

packages/app/src/pages/layout.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import {
8383
import { workspaceOpenState } from "./layout/sidebar-workspace-helpers"
8484
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
8585
import { SidebarContent } from "./layout/sidebar-shell"
86-
import { askProjectClose, closeProject as runProjectClose } from "./layout/project-close"
86+
import { closeProject as runProjectClose } from "./layout/project-close"
8787

8888
export default function Layout(props: ParentProps) {
8989
const [store, setStore, , ready] = persisted(
@@ -1341,16 +1341,6 @@ export default function Layout(props: ParentProps) {
13411341
open: navigateToProject,
13421342
})
13431343

1344-
const askClose = (project: LocalProject) =>
1345-
askProjectClose({
1346-
project,
1347-
t: language.t,
1348-
show: dialog.show,
1349-
dismiss: dialog.close,
1350-
onClose: close,
1351-
list: globalSDK.client.session.list,
1352-
})
1353-
13541344
function toggleProjectWorkspaces(project: LocalProject) {
13551345
const enabled = layout.sidebar.workspaces(project.worktree)()
13561346
if (enabled) {
@@ -2025,7 +2015,7 @@ export default function Layout(props: ParentProps) {
20252015
<DropdownMenu.Item
20262016
data-action="project-close-menu"
20272017
data-project={base64Encode(p().worktree)}
2028-
onSelect={() => void askClose(p())}
2018+
onSelect={() => close(p().worktree)}
20292019
>
20302020
<DropdownMenu.ItemLabel>{language.t("common.close")}</DropdownMenu.ItemLabel>
20312021
</DropdownMenu.Item>

packages/app/src/pages/layout/project-close.test.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
11
import { describe, expect, test } from "bun:test"
2-
import { closeProject, projectCloseBody } from "./project-close"
3-
4-
const t = (key: string, vars?: Record<string, string | number>) => {
5-
if (key === "dialog.project.close.note") return "Reopen later."
6-
if (key === "dialog.project.close.sessions.one") return "1 active session."
7-
if (key === "dialog.project.close.sessions.many") return `${vars?.count} active sessions.`
8-
return key
9-
}
10-
11-
describe("project close copy", () => {
12-
test("shows note when there are no active sessions", () => {
13-
expect(projectCloseBody(0, t)).toBe("Reopen later.")
14-
})
15-
16-
test("shows singular session warning with note", () => {
17-
expect(projectCloseBody(1, t)).toBe("1 active session. Reopen later.")
18-
})
19-
20-
test("shows plural session warning with note", () => {
21-
expect(projectCloseBody(3, t)).toBe("3 active sessions. Reopen later.")
22-
})
23-
})
2+
import { closeProject } from "./project-close"
243

254
describe("closeProject", () => {
265
test("closes inactive project without navigation", () => {
Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import { base64Encode } from "@opencode-ai/util/encode"
2-
import { Button } from "@opencode-ai/ui/button"
3-
import { Dialog } from "@opencode-ai/ui/dialog"
4-
import { createMemo, Show, type JSX } from "solid-js"
5-
import type { Session } from "@opencode-ai/sdk/v2/client"
62
import type { LocalProject } from "@/context/layout"
7-
import { displayName } from "./helpers"
8-
9-
type T = (key: string, vars?: Record<string, string | number>) => string
103

114
type Nav = {
125
directory: string
@@ -18,29 +11,6 @@ type Nav = {
1811
open: (directory: string) => Promise<void> | void
1912
}
2013

21-
type Ask = {
22-
project: LocalProject
23-
t: T
24-
show: (cb: () => JSX.Element) => void
25-
dismiss: () => void
26-
onClose: (directory: string) => void
27-
list: (input: { directory: string }) => Promise<{ data?: Session[] | null }>
28-
}
29-
30-
type DialogProps = {
31-
count: number
32-
project: LocalProject
33-
t: T
34-
onCancel: () => void
35-
onClose: () => void
36-
}
37-
38-
export function projectCloseBody(count: number, t: T) {
39-
if (count === 0) return t("dialog.project.close.note")
40-
if (count === 1) return `${t("dialog.project.close.sessions.one")} ${t("dialog.project.close.note")}`
41-
return `${t("dialog.project.close.sessions.many", { count })} ${t("dialog.project.close.note")}`
42-
}
43-
4414
export function closeProject(input: Nav) {
4515
const go = input.go ?? input.navigate
4616
const index = input.list.findIndex((x) => x.worktree === input.directory)
@@ -65,56 +35,3 @@ export function closeProject(input: Nav) {
6535
void input.open(next.worktree)
6636
})
6737
}
68-
69-
async function count(project: LocalProject, list: Ask["list"]) {
70-
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
71-
const all = await Promise.all(
72-
dirs.map((directory) =>
73-
list({ directory })
74-
.then((x) => x.data ?? [])
75-
.catch(() => []),
76-
),
77-
)
78-
return all.flat().filter((session) => session.time.archived === undefined).length
79-
}
80-
81-
export async function askProjectClose(input: Ask) {
82-
const total = await count(input.project, input.list)
83-
input.show(() => (
84-
<DialogCloseProject
85-
count={total}
86-
project={input.project}
87-
t={input.t}
88-
onCancel={input.dismiss}
89-
onClose={() => {
90-
input.dismiss()
91-
input.onClose(input.project.worktree)
92-
}}
93-
/>
94-
))
95-
}
96-
97-
export function DialogCloseProject(props: DialogProps) {
98-
const name = createMemo(() => displayName(props.project))
99-
100-
return (
101-
<Dialog title={props.t("dialog.project.close.title")} fit>
102-
<div class="flex flex-col gap-4 pl-6 pr-2.5 pb-3">
103-
<div class="flex flex-col gap-1">
104-
<span class="text-14-regular text-text-strong">
105-
{props.t("dialog.project.close.confirm", { name: name() })}
106-
</span>
107-
<span class="text-12-regular text-text-weak">{projectCloseBody(props.count, props.t)}</span>
108-
</div>
109-
<div class="flex justify-end gap-2">
110-
<Button variant="ghost" size="large" onClick={props.onCancel}>
111-
{props.t("common.cancel")}
112-
</Button>
113-
<Button variant="primary" size="large" onClick={props.onClose}>
114-
{props.t("common.close")}
115-
</Button>
116-
</div>
117-
</div>
118-
</Dialog>
119-
)
120-
}

0 commit comments

Comments
 (0)