Skip to content

Commit cb39e85

Browse files
committed
feat: move archive action into task menu
1 parent 95b139a commit cb39e85

2 files changed

Lines changed: 21 additions & 33 deletions

File tree

src/App.test.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,12 @@ describe("Taskmate application", () => {
181181
const title = await screen.findByLabelText("Task title");
182182
fireEvent.change(title, { target: { value: "Latest title" } });
183183
await user.click(trigger);
184-
expect(screen.getAllByRole("menuitem")).toHaveLength(2);
184+
expect(screen.getAllByRole("menuitem")).toHaveLength(3);
185+
expect(screen.getByRole("menuitem", { name: "Archive task" })).toBeInTheDocument();
185186
expect(screen.getByRole("menuitem", { name: "Copy title" })).toBeInTheDocument();
186187
expect(screen.getByRole("menuitem", { name: "Copy file path" })).toBeInTheDocument();
188+
expect(screen.queryByRole("button", { name: "Archive task" })).not.toBeInTheDocument();
189+
expect(screen.queryByRole("button", { name: "Permanently delete" })).not.toBeInTheDocument();
187190
await user.click(screen.getByRole("menuitem", { name: "Copy title" }));
188191

189192
expect(copyText).toHaveBeenLastCalledWith("Latest title");
@@ -202,6 +205,18 @@ describe("Taskmate application", () => {
202205
expect(await screen.findByRole("alert")).toHaveTextContent("clipboard unavailable");
203206
});
204207

208+
it("archives the current task from the task actions menu", async () => {
209+
const user = userEvent.setup();
210+
render(<App />);
211+
await user.click(screen.getByRole("button", { name: "Open workspace" }));
212+
await user.click(await screen.findByRole("button", { name: "New task" }));
213+
await user.click(screen.getByRole("button", { name: "Task actions" }));
214+
await user.click(screen.getByRole("menuitem", { name: "Archive task" }));
215+
216+
expect(await screen.findByRole("heading", { name: "Select a task" })).toBeInTheDocument();
217+
expect(localStorage.getItem("taskmate-browser-demo")).toContain('"archived":true');
218+
});
219+
205220
it("localizes the task copy menu in Simplified Chinese", async () => {
206221
localStorage.setItem("taskmate.locale.v1", "zh-CN");
207222
const user = userEvent.setup();
@@ -210,6 +225,7 @@ describe("Taskmate application", () => {
210225
await user.click(await screen.findByRole("button", { name: "新建任务" }));
211226
await user.click(screen.getByRole("button", { name: "任务操作" }));
212227

228+
expect(screen.getByRole("menuitem", { name: "归档任务" })).toBeInTheDocument();
213229
expect(screen.getByRole("menuitem", { name: "复制标题" })).toBeInTheDocument();
214230
expect(screen.getByRole("menuitem", { name: "复制文件路径" })).toBeInTheDocument();
215231
});

src/App.tsx

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
Search,
2727
Settings2,
2828
Sun,
29-
Trash2,
3029
X,
3130
} from "lucide-react";
3231
import { api } from "./lib/api";
@@ -351,7 +350,6 @@ function WorkspaceSession() {
351350
const searchRequest = useRef(0);
352351
const externalTask = useWorkspaceState((state) => state.externalTask);
353352
const setExternalTask = useWorkspaceState((state) => state.setExternalTask);
354-
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
355353
const [filterDialogOpen, setFilterDialogOpen] = useState(false);
356354
const [detailNarrow, setDetailNarrow] = useState(false);
357355
const [propertiesDrawerOpen, setPropertiesDrawerOpen] = useState(false);
@@ -606,26 +604,6 @@ function WorkspaceSession() {
606604
setTask(null);
607605
setOpenTabs((tabs) => tabs.filter((tab) => tab.id !== archivedId));
608606
};
609-
const remove = async () => {
610-
if (!task) return;
611-
if (!task.archived) {
612-
setError(t("tasks.archiveBeforeDelete"));
613-
return;
614-
}
615-
setDeleteConfirmOpen(true);
616-
};
617-
const confirmDelete = async () => {
618-
if (!task) return;
619-
try {
620-
await api.deleteTask(task.id);
621-
setOpenTabs((tabs) => tabs.filter((tab) => tab.id !== task.id));
622-
setTask(null);
623-
setDeleteConfirmOpen(false);
624-
await refresh();
625-
setSearchEpoch((epoch) => epoch + 1);
626-
} catch (cause) { setError(errorMessage(cause)); }
627-
};
628-
629607
const closeTab = async (id: string) => {
630608
if (task?.id === id && saveState === "dirty") await save(task);
631609
const index = openTabs.findIndex((tab) => tab.id === id);
@@ -926,6 +904,10 @@ function WorkspaceSession() {
926904
</Tooltip>
927905
<DropdownMenu.Portal>
928906
<DropdownMenu.Content className="z-[200] w-[180px] rounded-lg border border-line bg-surface p-1 shadow-[0_12px_30px_rgba(0,0,0,.14)]" align="end" sideOffset={5} collisionPadding={8}>
907+
<DropdownMenu.Item className="flex min-h-[30px] cursor-default items-center gap-1.5 rounded-[5px] px-2 py-1.5 text-xs outline-none data-[highlighted]:bg-accent-soft data-[highlighted]:text-accent" onSelect={() => void archive()}>
908+
{task.archived ? <ArchiveRestore size={15} /> : <Archive size={15} />}
909+
{task.archived ? t("tasks.restore") : t("tasks.archiveAction")}
910+
</DropdownMenu.Item>
929911
<DropdownMenu.Item className="flex min-h-[30px] cursor-default items-center gap-1.5 rounded-[5px] px-2 py-1.5 text-xs outline-none data-[highlighted]:bg-accent-soft data-[highlighted]:text-accent" onSelect={() => void copyText(task.title)}>
930912
<Copy size={15} />
931913
{t("editor.copyTitle")}
@@ -943,8 +925,6 @@ function WorkspaceSession() {
943925
<Button ref={propertiesButton} variant="outline" size="icon" className="size-8 shrink-0 [&_svg]:size-[17px]" aria-label={t("editor.openProperties")} onClick={() => setPropertiesDrawerOpen(true)}><Settings2 /></Button>
944926
</Tooltip>
945927
) : null}
946-
<Tooltip label={task.archived ? t("tasks.restore") : t("tasks.archiveAction")}><Button variant="ghost" size="icon" className="size-8 shrink-0 [&_svg]:size-[17px]" aria-pressed={task.archived} aria-label={task.archived ? t("tasks.restore") : t("tasks.archiveAction")} onClick={() => void archive()}>{task.archived ? <ArchiveRestore /> : <Archive />}</Button></Tooltip>
947-
<Tooltip label={t("tasks.deleteAction")}><Button variant="ghost" size="icon" className="size-8 shrink-0 text-danger [&_svg]:size-[17px]" aria-label={t("tasks.deleteAction")} onClick={() => void remove()}><Trash2 /></Button></Tooltip>
948928
</header>
949929
<div
950930
className="grid min-h-0 flex-1 overflow-hidden"
@@ -999,14 +979,6 @@ function WorkspaceSession() {
999979
>
1000980
{externalTask && task ? <div className="grid grid-cols-2 gap-2.5 [&_pre]:max-h-80 [&_pre]:overflow-auto [&_pre]:whitespace-pre-wrap [&_pre]:rounded-lg [&_pre]:border [&_pre]:border-line [&_pre]:bg-surface-soft [&_pre]:p-3 [&_pre]:text-xs"><div><strong>{t("external.editor")}</strong><pre>{task.body}</pre></div><div><strong>{t("external.disk")}</strong><pre>{externalTask.body}</pre></div></div> : null}
1001981
</Dialog>
1002-
<Dialog
1003-
open={deleteConfirmOpen}
1004-
onOpenChange={setDeleteConfirmOpen}
1005-
title={t("tasks.deleteAction")}
1006-
description={t("tasks.deleteConfirm", { title: task?.title ?? "" })}
1007-
closeLabel={t("common.close")}
1008-
footer={<><Button variant="outline" onClick={() => setDeleteConfirmOpen(false)}>{t("common.cancel")}</Button><Button variant="destructive" onClick={() => void confirmDelete()}>{t("common.delete")}</Button></>}
1009-
/>
1010982
</main>
1011983
</div>
1012984
</div>

0 commit comments

Comments
 (0)