Files
xboct-flow/src/ui/taskCommands.ts
T
2026-07-15 01:12:40 +05:00

253 lines
6.3 KiB
TypeScript

import * as vscode from "vscode";
import { changeStatus } from "@/commands/changeStatus";
import { createTask } from "@/commands/createTask";
import { deleteTask } from "@/commands/deleteTask";
import { openTask } from "@/commands/openTask";
import { resolveTaskLocations } from "@/commands/resolveTaskLocations";
import { appError, AppErrorVariant } from "@/error";
import { TaskLocation } from "@/model/taskLocation";
import { TaskStatus } from "@/model/task";
import { IConfigProvider, ITaskRepository } from "@/ports";
import { TaskDashboardPanel } from "@/views/taskDashboardPanel";
import { TaskKanbanPanel } from "@/views/taskKanbanPanel";
import { TaskTreeProvider } from "@/views/taskTreeProvider";
import { presentError } from "./presentError";
import { resolveTaskRef } from "./resolveTaskRef";
import { TASK_SCOPE_LABELS, TASK_STATUS_LABELS, TASK_STATUS_ORDER } from "./taskLabels";
export type TaskCommandDeps = {
repo: ITaskRepository;
config: IConfigProvider;
tree: TaskTreeProvider;
/** Refresh dashboard (and any other listeners) after task mutations. */
onTasksMutated?: () => void;
};
function dashboardDeps(deps: TaskCommandDeps) {
return {
repo: deps.repo,
config: deps.config,
onTasksMutated: () => deps.tree.refresh(),
};
}
function notifyMutated(deps: TaskCommandDeps): void {
deps.tree.refresh();
deps.onTasksMutated?.();
}
async function pickCreateLocation(
config: IConfigProvider,
): Promise<TaskLocation | undefined> {
const locations = resolveTaskLocations(config);
if (locations.length === 0) {
presentError(appError(AppErrorVariant.NO_FOLDER));
return undefined;
}
if (locations.length === 1) {
return locations[0];
}
const picked = await vscode.window.showQuickPick(
locations.map((location) => ({
label: TASK_SCOPE_LABELS[location.scope],
description: location.folderPath,
location,
})),
{ placeHolder: "Create task in…" },
);
return picked?.location;
}
export async function runCreateTask(deps: TaskCommandDeps): Promise<void> {
const title = await vscode.window.showInputBox({
prompt: "Task title",
placeHolder: "Enter task title...",
});
if (title === undefined) {
return;
}
const location = await pickCreateLocation(deps.config);
if (location === undefined) {
return;
}
const result = await createTask(deps.repo, {
folderPath: location.folderPath,
title,
});
if (result.isErr()) {
presentError(result.error);
return;
}
notifyMutated(deps);
void vscode.window.showInformationMessage(
`Task created (${TASK_SCOPE_LABELS[location.scope]}): ${result.value.title}`,
);
}
export async function runOpenTask(
deps: TaskCommandDeps,
item?: unknown,
): Promise<void> {
const ref = await resolveTaskRef(deps.repo, deps.config, item);
if (ref === undefined) {
if (resolveTaskLocations(deps.config).length === 0) {
presentError(appError(AppErrorVariant.NO_FOLDER));
}
return;
}
const result = await openTask(deps.repo, {
folderPath: ref.location.folderPath,
id: ref.id,
});
if (result.isErr()) {
presentError(result.error);
return;
}
const uri = vscode.Uri.file(result.value.path);
await vscode.window.showTextDocument(uri);
}
export async function runDeleteTask(
deps: TaskCommandDeps,
item?: unknown,
): Promise<void> {
const ref = await resolveTaskRef(deps.repo, deps.config, item);
if (ref === undefined) {
if (resolveTaskLocations(deps.config).length === 0) {
presentError(appError(AppErrorVariant.NO_FOLDER));
}
return;
}
const confirm = await vscode.window.showWarningMessage(
"Delete this task?",
{ modal: true },
"Delete",
);
if (confirm !== "Delete") {
return;
}
const result = await deleteTask(deps.repo, {
folderPath: ref.location.folderPath,
id: ref.id,
});
if (result.isErr()) {
presentError(result.error);
return;
}
notifyMutated(deps);
void vscode.window.showInformationMessage("Task deleted");
}
export async function runChangeStatus(
deps: TaskCommandDeps,
item?: unknown,
): Promise<void> {
const ref = await resolveTaskRef(deps.repo, deps.config, item);
if (ref === undefined) {
if (resolveTaskLocations(deps.config).length === 0) {
presentError(appError(AppErrorVariant.NO_FOLDER));
}
return;
}
const picked = await vscode.window.showQuickPick(
TASK_STATUS_ORDER.map((value) => ({
label: TASK_STATUS_LABELS[value],
status: value as TaskStatus,
})),
{ placeHolder: "New status" },
);
if (!picked) {
return;
}
const result = await changeStatus(deps.repo, {
folderPath: ref.location.folderPath,
id: ref.id,
status: picked.status,
});
if (result.isErr()) {
presentError(result.error);
return;
}
notifyMutated(deps);
void vscode.window.showInformationMessage(
`Status: ${TASK_STATUS_LABELS[result.value.status]}`,
);
}
export async function runOpenInDashboard(
deps: TaskCommandDeps,
item?: unknown,
): Promise<void> {
const ref = await resolveTaskRef(deps.repo, deps.config, item);
if (ref === undefined) {
if (resolveTaskLocations(deps.config).length === 0) {
presentError(appError(AppErrorVariant.NO_FOLDER));
}
return;
}
TaskDashboardPanel.show(dashboardDeps(deps), { selectedId: ref.id });
}
export function runOpenDashboard(deps: TaskCommandDeps): void {
TaskDashboardPanel.show(dashboardDeps(deps));
}
export function runOpenKanban(deps: TaskCommandDeps): void {
TaskKanbanPanel.show({
repo: deps.repo,
config: deps.config,
onTasksMutated: () => {
deps.tree.refresh();
TaskDashboardPanel.refreshIfOpen();
},
});
}
/** Register all task command adapters on the extension host. */
export function registerTaskCommands(
context: vscode.ExtensionContext,
deps: TaskCommandDeps,
): void {
context.subscriptions.push(
vscode.commands.registerCommand("xboctFlow.createTask", () =>
runCreateTask(deps),
),
vscode.commands.registerCommand(
"xboctFlow.openTask",
(item?: unknown) => runOpenTask(deps, item),
),
vscode.commands.registerCommand(
"xboctFlow.deleteTask",
(item?: unknown) => runDeleteTask(deps, item),
),
vscode.commands.registerCommand(
"xboctFlow.changeStatus",
(item?: unknown) => runChangeStatus(deps, item),
),
vscode.commands.registerCommand("xboctFlow.openDashboard", () =>
runOpenDashboard(deps),
),
vscode.commands.registerCommand(
"xboctFlow.openInDashboard",
(item?: unknown) => runOpenInDashboard(deps, item),
),
vscode.commands.registerCommand("xboctFlow.openKanban", () =>
runOpenKanban(deps),
),
);
}