254 lines
6.3 KiB
TypeScript
254 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 { 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,
|
|
})),
|
|
{ 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),
|
|
),
|
|
);
|
|
}
|