From fcb90c6e27254eff2ae2871311ccf78e8eb899b4 Mon Sep 17 00:00:00 2001 From: Gordon Date: Mon, 29 Mar 2021 03:47:32 +0100 Subject: [PATCH] Moving tasks --- ext-src/KanbnBoardPanel.ts | 72 ++++++++++++++++++++------ ext-src/KanbnStatusBarItem.ts | 35 +++++++------ ext-src/extension.ts | 18 +++---- src/Board.tsx | 96 +++++++++++++++++++++++++---------- 4 files changed, 153 insertions(+), 68 deletions(-) diff --git a/ext-src/KanbnBoardPanel.ts b/ext-src/KanbnBoardPanel.ts index 8c669fa..aa260b8 100644 --- a/ext-src/KanbnBoardPanel.ts +++ b/ext-src/KanbnBoardPanel.ts @@ -8,43 +8,52 @@ export default class KanbnBoardPanel { private readonly _panel: vscode.WebviewPanel; private readonly _extensionPath: string; + private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main'); private _disposables: vscode.Disposable[] = []; - public static createOrShow(extensionPath: string) { + public static createOrShow( + extensionPath: string, + kanbn: typeof import('@basementuniverse/kanbn/src/main') + ) { const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; - // If we already have a panel, show it. - // Otherwise, create a new panel. + // If we already have a panel, show it, otherwise create a new panel if (KanbnBoardPanel.currentPanel) { KanbnBoardPanel.currentPanel._panel.reveal(column); } else { KanbnBoardPanel.currentPanel = new KanbnBoardPanel( extensionPath, - column || vscode.ViewColumn.One + column || vscode.ViewColumn.One, + kanbn ); } } - public static async updateBoard(kanbn: typeof import('@basementuniverse/kanbn/src/main')) { + public static async update() { if (KanbnBoardPanel.currentPanel) { let index: any; try { - index = await kanbn.getIndex(); + index = await KanbnBoardPanel.currentPanel._kanbn.getIndex(); } catch (error) { vscode.window.showErrorMessage(error instanceof Error ? error.message : error); return; } KanbnBoardPanel.currentPanel._panel.webview.postMessage({ index, - tasks: (await kanbn.loadAllTrackedTasks(index)).map( - task => kanbn.hydrateTask(index, task) + tasks: (await KanbnBoardPanel.currentPanel._kanbn.loadAllTrackedTasks(index)).map( + task => KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task) ) }); } } - private constructor(extensionPath: string, column: vscode.ViewColumn) { + private constructor( + extensionPath: string, + column: vscode.ViewColumn, + kanbn: typeof import('@basementuniverse/kanbn/src/main') + ) { this._extensionPath = extensionPath; + this._kanbn = kanbn; // Create and show a new webview panel this._panel = vscode.window.createWebviewPanel(KanbnBoardPanel.viewType, "Kanbn Board", column, { @@ -68,11 +77,42 @@ export default class KanbnBoardPanel { this._panel.onDidDispose(() => this.dispose(), null, this._disposables); // Handle messages from the webview - this._panel.webview.onDidReceiveMessage(message => { + this._panel.webview.onDidReceiveMessage(async message => { switch (message.command) { + + // Display error message case 'error': vscode.window.showErrorMessage(message.text); return; + + // Move a task + case 'kanbn.move': + try { + await kanbn.moveTask(message.task, message.column, message.position); + } catch (e) { + vscode.window.showErrorMessage(e.message); + } + return; + + // Create a task + case 'kanbn.create': + // + return; + + // Update a task + case 'kanbn.update': + // + return; + + // Rename a task + case 'kanbn.rename': + // + return; + + // Delete a task + case 'kanbn.delete': + // + return; } }, null, this._disposables); } @@ -82,7 +122,6 @@ export default class KanbnBoardPanel { // Clean up our resources this._panel.dispose(); - while (this._disposables.length) { const x = this._disposables.pop(); if (x) { @@ -95,11 +134,12 @@ export default class KanbnBoardPanel { const manifest = require(path.join(this._extensionPath, 'build', 'asset-manifest.json')); const mainScript = manifest['main.js']; const mainStyle = manifest['main.css']; - - const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'build', mainScript)); - const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' }); - const stylePathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'build', mainStyle)); - const styleUri = stylePathOnDisk.with({ scheme: 'vscode-resource' }); + const scriptUri = vscode.Uri + .file(path.join(this._extensionPath, 'build', mainScript)) + .with({ scheme: 'vscode-resource' }); + const styleUri = vscode.Uri + .file(path.join(this._extensionPath, 'build', mainStyle)) + .with({ scheme: 'vscode-resource' }); // Use a nonce to whitelist which scripts can be run const nonce = getNonce(); diff --git a/ext-src/KanbnStatusBarItem.ts b/ext-src/KanbnStatusBarItem.ts index e1ff46b..49c2332 100644 --- a/ext-src/KanbnStatusBarItem.ts +++ b/ext-src/KanbnStatusBarItem.ts @@ -1,19 +1,24 @@ import * as vscode from 'vscode'; export default class KanbnStatusBarItem { - statusBarItem: vscode.StatusBarItem; + private readonly _statusBarItem: vscode.StatusBarItem; + private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main'); - constructor(context: vscode.ExtensionContext) { - this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); - context.subscriptions.push(this.statusBarItem); + constructor( + context: vscode.ExtensionContext, + kanbn: typeof import('@basementuniverse/kanbn/src/main') + ) { + this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); + context.subscriptions.push(this._statusBarItem); + this._kanbn = kanbn; } - async update(kanbn: typeof import('@basementuniverse/kanbn/src/main')): Promise { - if (this.statusBarItem === undefined) { + async update(): Promise { + if (this._statusBarItem === undefined) { return; } - if (await kanbn.initialised()) { - const status = await kanbn.status(true); + if (await this._kanbn.initialised()) { + const status = await this._kanbn.status(true); const text = [ `$(project) ${status.tasks}` ]; @@ -28,14 +33,14 @@ export default class KanbnStatusBarItem { text.push(`$(check) ${status.completedTasks}`); tooltip.push(`${status.completedTasks} completed task${status.completedTasks === 1 ? '' : 's'}`); } - this.statusBarItem.text = text.join(' '); - this.statusBarItem.tooltip = tooltip.join('\n'); - this.statusBarItem.command = 'kanbn.board'; + this._statusBarItem.text = text.join(' '); + this._statusBarItem.tooltip = tooltip.join('\n'); + this._statusBarItem.command = 'kanbn.board'; } else { - this.statusBarItem.text = '$(project) Not initialised'; - this.statusBarItem.tooltip = 'Click to initialise'; - this.statusBarItem.command = 'kanbn.init'; + this._statusBarItem.text = '$(project) Not initialised'; + this._statusBarItem.tooltip = 'Click to initialise'; + this._statusBarItem.command = 'kanbn.init'; } - this.statusBarItem.show(); + this._statusBarItem.show(); } } diff --git a/ext-src/extension.ts b/ext-src/extension.ts index c04f53f..1c3dbbe 100644 --- a/ext-src/extension.ts +++ b/ext-src/extension.ts @@ -41,8 +41,8 @@ export async function activate(context: vscode.ExtensionContext) { name: newProjectName }); vscode.window.showInformationMessage(`Initialised kanbn project '${newProjectName}'.`); - kanbnStatusBarItem.update(kanbn); - KanbnBoardPanel.updateBoard(kanbn); + kanbnStatusBarItem.update(); + KanbnBoardPanel.update(); } })); @@ -62,8 +62,8 @@ export async function activate(context: vscode.ExtensionContext) { // If kanbn is initialised, view the kanbn board if (await kanbn.initialised()) { - KanbnBoardPanel.createOrShow(context.extensionPath); - KanbnBoardPanel.updateBoard(kanbn); + KanbnBoardPanel.createOrShow(context.extensionPath, kanbn); + KanbnBoardPanel.update(); } else { vscode.window.showErrorMessage('You need to initialise kanbn before viewing the kanbn board.'); } @@ -77,16 +77,16 @@ export async function activate(context: vscode.ExtensionContext) { const kanbn = await import('@basementuniverse/kanbn/src/main'); // Create status bar item - kanbnStatusBarItem = new KanbnStatusBarItem(context); - kanbnStatusBarItem.update(kanbn); - KanbnBoardPanel.updateBoard(kanbn); + kanbnStatusBarItem = new KanbnStatusBarItem(context, kanbn); + kanbnStatusBarItem.update(); + KanbnBoardPanel.update(); // Initialise file watcher const uri = vscode.workspace.workspaceFolders[0].uri.fsPath; const fileWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '.kanbn/*')); fileWatcher.onDidChange(() => { - kanbnStatusBarItem.update(kanbn); - KanbnBoardPanel.updateBoard(kanbn); + kanbnStatusBarItem.update(); + KanbnBoardPanel.update(); }); } } diff --git a/src/Board.tsx b/src/Board.tsx index 319ac7a..950860f 100644 --- a/src/Board.tsx +++ b/src/Board.tsx @@ -3,54 +3,94 @@ import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; export type task = { id: string, - name: string -} + name: string, + description: string, + column: string, + workload?: number, + remainingWorkload?: number, + progress?: number, + metadata: { + created: Date, + updated?: Date, + completed?: Date, + assigned?: string + }, + relations: Array<{ + type: string, + task: string + }>, + subTasks: Array<{ + text: string, + completed: boolean + }>, + comments: Array<{ + author: string, + date: Date, + text: string + }> +}; + +declare var acquireVsCodeApi: any; +const vscode = acquireVsCodeApi(); const onDragEnd = (result, columns, setColumns) => { - if (!result.destination) return; + + // No destination means the item was dragged to an invalid location + if (!result.destination) { + return; + } + + // Get the source and destination columns const { source, destination } = result; + // The item that was moved + let removed: task; + + // The task was dragged from one column to another if (source.droppableId !== destination.droppableId) { - const sourceColumn = columns[source.droppableId]; - const destColumn = columns[destination.droppableId]; - const sourceItems = [...sourceColumn.items]; - const destItems = [...destColumn.items]; - const [removed] = sourceItems.splice(source.index, 1); + const sourceItems = columns[source.droppableId]; + const destItems = columns[destination.droppableId]; + [removed] = sourceItems.splice(source.index, 1); destItems.splice(destination.index, 0, removed); setColumns({ ...columns, - [source.droppableId]: { - ...sourceColumn, - items: sourceItems - }, - [destination.droppableId]: { - ...destColumn, - items: destItems - } + [source.droppableId]: sourceItems, + [destination.droppableId]: destItems }); + + // The task was dragged into the same column } else { - const column = columns[source.droppableId]; - const copiedItems = [...column.items]; - const [removed] = copiedItems.splice(source.index, 1); + + // If the task was dragged to the same position that it currently occupies, don't move it (this will + // prevent unnecessarily setting the task's updated date) + if (source.index === destination.index) { + return; + } + const copiedItems = columns[source.droppableId]; + [removed] = copiedItems.splice(source.index, 1); copiedItems.splice(destination.index, 0, removed); setColumns({ ...columns, - [source.droppableId]: { - ...column, - items: copiedItems - } + [source.droppableId]: copiedItems }); } + + // Post a message back to the extension so we can move the task in the index + vscode.postMessage({ + command: 'kanbn.move', + task: removed.id, + column: destination.droppableId, + position: destination.index + }); }; const Board = ({ columns }) => { - // const [columns, setColumns] = useState(props.columns); + const [, setColumns] = useState(columns); return (
- {/* onDragEnd(result, columns, setColumns)} - > */} - + > {Object.entries(columns).map(([columnId, column]) => { return (
{
); -} +}; export default Board;