This commit is contained in:
Gordon 2021-04-07 21:25:57 +01:00
parent 63bf065331
commit a26d1ba7c0
6 changed files with 594 additions and 500 deletions

View File

@ -2,16 +2,69 @@ import * as path from 'path';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
function transformTaskData(taskData: any) {
const result = {
id: taskData.id,
name: taskData.name,
description: taskData.description,
metadata: {
created: taskData.metadata.created
? new Date(taskData.metadata.created)
: new Date(),
updated: new Date(),
assigned: taskData.metadata.assigned,
progress: taskData.progress,
tags: taskData.metadata.tags
} as any,
relations: taskData.relations,
subTasks: taskData.subTasks,
comments: taskData.comments.map((comment: any) => ({
author: comment.author,
date: new Date(Date.parse(comment.date)),
text: comment.text
}))
} as any;
// Add assigned
if (taskData.metadata.assigned) {
result.metadata['assigned'] = taskData.metadata.assigned;
}
// Add progress
if (taskData.progress > 0) {
result.metadata['progress'] = taskData.progress;
}
// Add tags
if (taskData.metadata.tags.length) {
result.metadata['tags'] = taskData.metadata.tags;
}
// Add due, started and completed dates if present
if (taskData.metadata.due) {
result.metadata['due'] = new Date(Date.parse(taskData.metadata.due));
}
if (taskData.metadata.started) {
result.metadata['started'] = new Date(Date.parse(taskData.metadata.started));
}
if (taskData.metadata.completed) {
result.metadata['completed'] = new Date(Date.parse(taskData.metadata.completed));
}
return result;
}
export default class KanbnTaskPanel { export default class KanbnTaskPanel {
private static readonly viewType = 'react'; private static readonly viewType = 'react';
private static panels: KanbnTaskPanel[] = []; private static panels: Record<string, KanbnTaskPanel> = {};
private readonly _panel: vscode.WebviewPanel; private readonly _panel: vscode.WebviewPanel;
private readonly _extensionPath: string; private readonly _extensionPath: string;
private readonly _workspacePath: string; private readonly _workspacePath: string;
private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main'); private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main');
private readonly _taskId: string|null; private _taskId: string|null;
private readonly _columnName: string; private _columnName: string|null;
private readonly _panelUuid: string;
private _disposables: vscode.Disposable[] = []; private _disposables: vscode.Disposable[] = [];
public static async show( public static async show(
@ -22,55 +75,20 @@ export default class KanbnTaskPanel {
columnName: string|null columnName: string|null
) { ) {
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;
let index: any;
try {
index = await kanbn.getIndex();
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
let tasks: any[];
try {
tasks = (await kanbn.loadAllTrackedTasks(index)).map(
task => ({
uuid: uuidv4(),
...kanbn.hydrateTask(index, task)
})
);
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
let task = null;
if (taskId) {
task = tasks.find(t => t.id === taskId) ?? null;
}
// If no columnName is specified, use the first column
if (!columnName) {
columnName = Object.keys(index.columns)[0];
}
// Create a new panel // Create a new panel
const panelUuid = uuidv4();
const taskPanel = new KanbnTaskPanel( const taskPanel = new KanbnTaskPanel(
extensionPath, extensionPath,
workspacePath, workspacePath,
column || vscode.ViewColumn.One, column || vscode.ViewColumn.One,
kanbn, kanbn,
taskId, taskId,
columnName columnName,
panelUuid
); );
KanbnTaskPanel.panels.push(taskPanel); KanbnTaskPanel.panels[panelUuid] = taskPanel;
taskPanel.update();
// Send task data to the webview
taskPanel._panel.webview.postMessage({
type: 'task',
index,
task,
columnName: taskPanel._columnName,
tasks,
dateFormat: kanbn.getDateFormat(index)
});
} }
private constructor( private constructor(
@ -79,13 +97,15 @@ export default class KanbnTaskPanel {
column: vscode.ViewColumn, column: vscode.ViewColumn,
kanbn: typeof import('@basementuniverse/kanbn/src/main'), kanbn: typeof import('@basementuniverse/kanbn/src/main'),
taskId: string|null, taskId: string|null,
columnName: string columnName: string|null,
panelUuid: string
) { ) {
this._extensionPath = extensionPath; this._extensionPath = extensionPath;
this._workspacePath = workspacePath; this._workspacePath = workspacePath;
this._kanbn = kanbn; this._kanbn = kanbn;
this._taskId = taskId; this._taskId = taskId;
this._columnName = columnName; this._columnName = columnName;
this._panelUuid = panelUuid;
// Create and show a new webview panel // Create and show a new webview panel
this._panel = vscode.window.createWebviewPanel(KanbnTaskPanel.viewType, 'New task', column, { this._panel = vscode.window.createWebviewPanel(KanbnTaskPanel.viewType, 'New task', column, {
@ -137,26 +157,31 @@ export default class KanbnTaskPanel {
// Create a task // Create a task
case 'kanbn.create': case 'kanbn.create':
// TODO convert dates await this._kanbn.createTask(transformTaskData(message.taskData), message.taskData.column);
// await this._kanbn.createTask(message.taskData, message.taskData.column); KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id;
vscode.window.showInformationMessage(`Created task ${message.taskData.name}.`); KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column;
KanbnTaskPanel.panels[message.panelUuid].update();
// vscode.window.showInformationMessage(`Created task '${message.taskData.name}'.`);
return; return;
// Update a task // Update a task
case 'kanbn.update': case 'kanbn.update':
// TODO convert dates await this._kanbn.updateTask(message.taskId, transformTaskData(message.taskData), message.taskData.column);
// await this._kanbn.updateTask(message.taskData.id, message.taskData, message.taskData.column); KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id;
vscode.window.showInformationMessage(`Updated task ${message.taskData.name}.`); KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column;
KanbnTaskPanel.panels[message.panelUuid].update();
// vscode.window.showInformationMessage(`Updated task '${message.taskData.name}'.`);
return; return;
// Delete a task // Delete a task and close the webview panel
case 'kanbn.delete': case 'kanbn.delete':
vscode.window.showInformationMessage(`Delete task ${message.taskData.name}?`, 'Yes', 'No').then( vscode.window.showInformationMessage(`Delete task '${message.taskData.name}'?`, 'Yes', 'No').then(
async value => { async value => {
if (value === 'Yes') { if (value === 'Yes') {
// await this._kanbn.deleteTask(message.taskId, true); await this._kanbn.deleteTask(message.taskId, true);
vscode.window.showInformationMessage(`Deleted task ${message.taskData.name}.`); KanbnTaskPanel.panels[message.panelUuid].dispose();
// TODO close panel, will need to generate uuid for each panel delete KanbnTaskPanel.panels[message.panelUuid];
// vscode.window.showInformationMessage(`Deleted task '${message.taskData.name}'.`);
} }
} }
); );
@ -175,6 +200,48 @@ export default class KanbnTaskPanel {
} }
} }
private async update() {
let index: any;
try {
index = await this._kanbn.getIndex();
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
let tasks: any[];
try {
tasks = (await this._kanbn.loadAllTrackedTasks(index)).map(
task => ({
uuid: uuidv4(),
...this._kanbn.hydrateTask(index, task)
})
);
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
let task = null;
if (this._taskId) {
task = tasks.find(t => t.id === this._taskId) ?? null;
}
// If no columnName is specified, use the first column
if (!this._columnName) {
this._columnName = Object.keys(index.columns)[0];
}
// Send task data to the webview
this._panel.webview.postMessage({
type: 'task',
index,
task,
tasks,
columnName: this._columnName,
dateFormat: this._kanbn.getDateFormat(index),
panelUuid: this._panelUuid
});
}
private _getHtmlForWebview() { private _getHtmlForWebview() {
const manifest = require(path.join(this._extensionPath, 'build', 'asset-manifest.json')); const manifest = require(path.join(this._extensionPath, 'build', 'asset-manifest.json'));
const mainScript = manifest['main.js']; const mainScript = manifest['main.js'];

View File

@ -42,9 +42,9 @@ export async function activate(context: vscode.ExtensionContext) {
name: newProjectName name: newProjectName
}); });
vscode.window.showInformationMessage(`Initialised kanbn project '${newProjectName}'.`); vscode.window.showInformationMessage(`Initialised kanbn project '${newProjectName}'.`);
kanbnStatusBarItem.update();
KanbnBoardPanel.update(); KanbnBoardPanel.update();
} }
kanbnStatusBarItem.update();
})); }));
// Register a command to open the kanbn board. This command will be invoked when the status bar item is clicked // Register a command to open the kanbn board. This command will be invoked when the status bar item is clicked
@ -72,6 +72,7 @@ export async function activate(context: vscode.ExtensionContext) {
} else { } else {
vscode.window.showErrorMessage('You need to initialise kanbn before viewing the kanbn board.'); vscode.window.showErrorMessage('You need to initialise kanbn before viewing the kanbn board.');
} }
kanbnStatusBarItem.update();
})); }));
// Register a command to add a new kanbn task. // Register a command to add a new kanbn task.

View File

@ -20,6 +20,7 @@ function App() {
const [tasks, setTasks] = useState({}); const [tasks, setTasks] = useState({});
const [columnName, setColumnName] = useState(''); const [columnName, setColumnName] = useState('');
const [columnNames, setColumnNames] = useState([] as string[]); const [columnNames, setColumnNames] = useState([] as string[]);
const [panelUuid, setPanelUuid] = useState('');
window.addEventListener('message', event => { window.addEventListener('message', event => {
const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task])); const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task]));
@ -42,6 +43,7 @@ function App() {
setTasks(tasks); setTasks(tasks);
setColumnName(event.data.columnName); setColumnName(event.data.columnName);
setColumnNames(Object.keys(event.data.index.columns)); setColumnNames(Object.keys(event.data.index.columns));
setPanelUuid(event.data.panelUuid);
break; break;
} }
setType(event.data.type); setType(event.data.type);
@ -70,6 +72,7 @@ function App() {
columnName={columnName} columnName={columnName}
columnNames={columnNames} columnNames={columnNames}
dateFormat={dateFormat} dateFormat={dateFormat}
panelUuid={panelUuid}
vscode={vscode} vscode={vscode}
/> />
} }

File diff suppressed because it is too large Load Diff

View File

@ -64,6 +64,7 @@ const TaskItem = ({ task, position, dateFormat, vscode }: {
<div className="kanbn-task-row"> <div className="kanbn-task-row">
{ {
'tags' in task.metadata && 'tags' in task.metadata &&
task.metadata.tags!.length > 0 &&
<div className="kanbn-task-data kanbn-task-tags"> <div className="kanbn-task-data kanbn-task-tags">
{task.metadata.tags!.map(tag => { {task.metadata.tags!.map(tag => {
return ( return (
@ -81,6 +82,7 @@ const TaskItem = ({ task, position, dateFormat, vscode }: {
<div className="kanbn-task-row"> <div className="kanbn-task-row">
{ {
'assigned' in task.metadata && 'assigned' in task.metadata &&
!!task.metadata.assigned &&
<div className="kanbn-task-data kanbn-task-assigned"> <div className="kanbn-task-data kanbn-task-assigned">
<i className="codicon codicon-account"></i>{task.metadata.assigned} <i className="codicon codicon-account"></i>{task.metadata.assigned}
</div> </div>

View File

@ -120,6 +120,7 @@ body {
.kanbn-column-task-list { .kanbn-column-task-list {
margin: 0 8px; margin: 0 8px;
border-left: 4px var(--vscode-activityBar-inactiveForeground) solid; border-left: 4px var(--vscode-activityBar-inactiveForeground) solid;
min-height: 46px;
} }
.kanbn-column-task-list.drag-over { .kanbn-column-task-list.drag-over {
@ -270,6 +271,11 @@ body {
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid; border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
} }
.kanbn-task-editor-dirty {
margin-left: 8px;
color: #f42;
}
.kanbn-task-editor-dates { .kanbn-task-editor-dates {
font-size: var(--vscode-font-size); font-size: var(--vscode-font-size);
font-style: italic; font-style: italic;