implement task version history
This commit is contained in:
Vendored
+61
-12
@@ -686,10 +686,22 @@
|
|||||||
const fileIds = this.storageModel.list(taskContainerPath);
|
const fileIds = this.storageModel.list(taskContainerPath);
|
||||||
return fileIds;
|
return fileIds;
|
||||||
};
|
};
|
||||||
getTaskFileContent = (taskId) => {
|
listTaskVersionIds = (taskId) => {
|
||||||
|
const versionIds = this.fileModel.listFileContentIds(taskId);
|
||||||
|
return versionIds;
|
||||||
|
};
|
||||||
|
getLatestTaskFileContent = (taskId) => {
|
||||||
const taskFileContentOrNull = this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
const taskFileContentOrNull = this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
||||||
return taskFileContentOrNull;
|
return taskFileContentOrNull;
|
||||||
};
|
};
|
||||||
|
getSpecificTaskFileContent = (taskId, versionId) => {
|
||||||
|
const taskFileContentOrNull = this.fileModel.getFileContent(
|
||||||
|
taskId,
|
||||||
|
versionId,
|
||||||
|
TaskFileContentReference
|
||||||
|
);
|
||||||
|
return taskFileContentOrNull;
|
||||||
|
};
|
||||||
deleteTask = (boardId, taskId) => {
|
deleteTask = (boardId, taskId) => {
|
||||||
const taskFilePath = this.getTaskFilePath(taskId);
|
const taskFilePath = this.getTaskFilePath(taskId);
|
||||||
const taskReferencePath = this.getTaskReferencePath(
|
const taskReferencePath = this.getTaskReferencePath(
|
||||||
@@ -1468,6 +1480,8 @@
|
|||||||
priority = new State("");
|
priority = new State("");
|
||||||
date = new State("");
|
date = new State("");
|
||||||
time = new State("");
|
time = new State("");
|
||||||
|
selectedVersionId = new State("");
|
||||||
|
versionIds = new ListState();
|
||||||
// view
|
// view
|
||||||
open = () => {
|
open = () => {
|
||||||
this.boardViewModel.selectTask(this);
|
this.boardViewModel.selectTask(this);
|
||||||
@@ -1506,7 +1520,25 @@
|
|||||||
this.boardViewModel.removeTaskFromList(this.task.fileId);
|
this.boardViewModel.removeTaskFromList(this.task.fileId);
|
||||||
};
|
};
|
||||||
// load
|
// load
|
||||||
|
loadVersionIds = () => {
|
||||||
|
const versionIds = this.boardModel.listTaskVersionIds(
|
||||||
|
this.task.fileId
|
||||||
|
);
|
||||||
|
const sortedVersionIds = versionIds.sort(localeCompare).reverse();
|
||||||
|
this.versionIds.clear();
|
||||||
|
this.versionIds.add(...sortedVersionIds);
|
||||||
|
};
|
||||||
|
switchVersion = (versionId) => {
|
||||||
|
const taskFileContent = this.boardModel.getSpecificTaskFileContent(this.task.fileId, versionId);
|
||||||
|
if (taskFileContent == null) return;
|
||||||
|
this.task = taskFileContent;
|
||||||
|
this.loadTaskData();
|
||||||
|
};
|
||||||
loadAllData = () => {
|
loadAllData = () => {
|
||||||
|
this.loadTaskData();
|
||||||
|
this.loadVersionIds();
|
||||||
|
};
|
||||||
|
loadTaskData = () => {
|
||||||
this.name.value = this.task.name;
|
this.name.value = this.task.name;
|
||||||
this.description.value = this.task.description ?? "";
|
this.description.value = this.task.description ?? "";
|
||||||
this.category.value = this.task.category ?? "";
|
this.category.value = this.task.category ?? "";
|
||||||
@@ -1514,6 +1546,7 @@
|
|||||||
this.priority.value = this.task.priority ?? "";
|
this.priority.value = this.task.priority ?? "";
|
||||||
this.date.value = this.task.date ?? "";
|
this.date.value = this.task.date ?? "";
|
||||||
this.time.value = this.task.time ?? "";
|
this.time.value = this.task.time ?? "";
|
||||||
|
this.selectedVersionId.value = this.task.fileContentId;
|
||||||
};
|
};
|
||||||
// init
|
// init
|
||||||
constructor(boardModel, boardViewModel, taskFileContent) {
|
constructor(boardModel, boardViewModel, taskFileContent) {
|
||||||
@@ -1521,6 +1554,9 @@
|
|||||||
this.boardViewModel = boardViewModel;
|
this.boardViewModel = boardViewModel;
|
||||||
this.task = taskFileContent;
|
this.task = taskFileContent;
|
||||||
this.loadAllData();
|
this.loadAllData();
|
||||||
|
this.selectedVersionId.subscribeSilent((selectedVersionId) => {
|
||||||
|
this.switchVersion(selectedVersionId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1653,7 +1689,7 @@
|
|||||||
this.boardInfo.fileId
|
this.boardInfo.fileId
|
||||||
);
|
);
|
||||||
for (const taskId of taskIds) {
|
for (const taskId of taskIds) {
|
||||||
const taskFileContent = this.boardModel.getTaskFileContent(taskId);
|
const taskFileContent = this.boardModel.getLatestTaskFileContent(taskId);
|
||||||
if (taskFileContent == null) continue;
|
if (taskFileContent == null) continue;
|
||||||
const taskViewModel = new TaskViewModel(
|
const taskViewModel = new TaskViewModel(
|
||||||
this.boardModel,
|
this.boardModel,
|
||||||
@@ -1992,7 +2028,8 @@
|
|||||||
closeButton: "Close",
|
closeButton: "Close",
|
||||||
confirmButton: "Confirm",
|
confirmButton: "Confirm",
|
||||||
saveButton: "Save",
|
saveButton: "Save",
|
||||||
setButton: "Set"
|
setButton: "Set",
|
||||||
|
fileVersionLabel: "Version"
|
||||||
},
|
},
|
||||||
regional: {
|
regional: {
|
||||||
weekdays: {
|
weekdays: {
|
||||||
@@ -2394,9 +2431,29 @@
|
|||||||
return RibbonButton(label, icon, isSelected, select);
|
return RibbonButton(label, icon, isSelected, select);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/View/Components/option.tsx
|
||||||
|
function Option(text, value, selectedOnCreate) {
|
||||||
|
return /* @__PURE__ */ createElement("option", { value, "toggle:selected": selectedOnCreate }, text);
|
||||||
|
}
|
||||||
|
var StringToOption = (string) => {
|
||||||
|
return Option(string, string, false);
|
||||||
|
};
|
||||||
|
var VersionIdToOption = (versionId) => {
|
||||||
|
const [date, rest] = versionId.split("T");
|
||||||
|
const [time] = rest.split(".");
|
||||||
|
const readableName = `${date} ${time}`;
|
||||||
|
return Option(readableName, versionId, false);
|
||||||
|
};
|
||||||
|
|
||||||
// src/View/Modals/taskSettingsModal.tsx
|
// src/View/Modals/taskSettingsModal.tsx
|
||||||
function TaskSettingsModal(taskViewModel) {
|
function TaskSettingsModal(taskViewModel) {
|
||||||
return /* @__PURE__ */ createElement("div", { class: "modal", open: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.taskSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskNameLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.name }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDescriptionLabel), /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement("div", { class: "modal", open: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.taskSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "history"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.general.fileVersionLabel), /* @__PURE__ */ createElement(
|
||||||
|
"select",
|
||||||
|
{
|
||||||
|
"bind:value": taskViewModel.selectedVersionId,
|
||||||
|
"children:append": [taskViewModel.versionIds, VersionIdToOption]
|
||||||
|
}
|
||||||
|
), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskNameLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.name }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDescriptionLabel), /* @__PURE__ */ createElement(
|
||||||
"textarea",
|
"textarea",
|
||||||
{
|
{
|
||||||
rows: "10",
|
rows: "10",
|
||||||
@@ -3104,14 +3161,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/View/Components/option.tsx
|
|
||||||
function Option(text, value, selectedOnCreate) {
|
|
||||||
return /* @__PURE__ */ createElement("option", { value, "toggle:selected": selectedOnCreate }, text);
|
|
||||||
}
|
|
||||||
var StringToOption = (string) => {
|
|
||||||
return Option(string, string, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// src/View/Components/chatEntry.tsx
|
// src/View/Components/chatEntry.tsx
|
||||||
function ChatEntry(chatViewModel) {
|
function ChatEntry(chatViewModel) {
|
||||||
const view = /* @__PURE__ */ createElement(
|
const view = /* @__PURE__ */ createElement(
|
||||||
|
|||||||
Vendored
+5
-1
@@ -677,6 +677,10 @@ label.tile:has(> span.icon) :is(input, select, textarea) {
|
|||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.padding-right {
|
.padding-right {
|
||||||
padding-right: var(--control-padding) !important;
|
padding-right: var(--control-padding) !important;
|
||||||
}
|
}
|
||||||
@@ -1230,4 +1234,4 @@ input[type="checkbox"],
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
src: url("Icons/material-icons.woff") format("woff");
|
src: url("Icons/material-icons.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import {
|
|||||||
checkMatchesObjectStructure,
|
checkMatchesObjectStructure,
|
||||||
} from "../Utility/typeSafety";
|
} from "../Utility/typeSafety";
|
||||||
import FileModel, { FileContent, FileModelSubPath } from "./fileModel";
|
import FileModel, { FileContent, FileModelSubPath } from "./fileModel";
|
||||||
|
import { v4, version } from "uuid";
|
||||||
|
|
||||||
import ChatModel from "../Chat/chatModel";
|
import ChatModel from "../Chat/chatModel";
|
||||||
import { Color } from "../../colors";
|
import { Color } from "../../colors";
|
||||||
import { HandlerManager } from "../Utility/utility";
|
import { HandlerManager } from "../Utility/utility";
|
||||||
import StorageModel from "../Global/storageModel";
|
import StorageModel from "../Global/storageModel";
|
||||||
import { v4 } from "uuid";
|
|
||||||
|
|
||||||
export default class BoardModel {
|
export default class BoardModel {
|
||||||
storageModel: StorageModel;
|
storageModel: StorageModel;
|
||||||
@@ -169,12 +169,30 @@ export default class BoardModel {
|
|||||||
return fileIds;
|
return fileIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
getTaskFileContent = (taskId: string): TaskFileContent | null => {
|
listTaskVersionIds = (taskId: string): string[] => {
|
||||||
|
const versionIds: string[] = this.fileModel.listFileContentIds(taskId);
|
||||||
|
return versionIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
getLatestTaskFileContent = (taskId: string): TaskFileContent | null => {
|
||||||
const taskFileContentOrNull: TaskFileContent | null =
|
const taskFileContentOrNull: TaskFileContent | null =
|
||||||
this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
this.fileModel.getLatestFileContent(taskId, TaskFileContentReference);
|
||||||
return taskFileContentOrNull;
|
return taskFileContentOrNull;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getSpecificTaskFileContent = (
|
||||||
|
taskId: string,
|
||||||
|
versionId: string
|
||||||
|
): TaskFileContent | null => {
|
||||||
|
const taskFileContentOrNull: TaskFileContent | null =
|
||||||
|
this.fileModel.getFileContent(
|
||||||
|
taskId,
|
||||||
|
versionId,
|
||||||
|
TaskFileContentReference
|
||||||
|
);
|
||||||
|
return taskFileContentOrNull;
|
||||||
|
};
|
||||||
|
|
||||||
deleteTask = (boardId: string, taskId: string): void => {
|
deleteTask = (boardId: string, taskId: string): void => {
|
||||||
const taskFilePath: string[] = this.getTaskFilePath(taskId);
|
const taskFilePath: string[] = this.getTaskFilePath(taskId);
|
||||||
const taskReferencePath: string[] = this.getTaskReferencePath(
|
const taskReferencePath: string[] = this.getTaskReferencePath(
|
||||||
|
|||||||
@@ -13,3 +13,12 @@ export const StringToOption: React.StateItemConverter<string> = (
|
|||||||
) => {
|
) => {
|
||||||
return Option(string, string, false);
|
return Option(string, string, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const VersionIdToOption: React.StateItemConverter<string> = (
|
||||||
|
versionId: string
|
||||||
|
) => {
|
||||||
|
const [date, rest] = versionId.split("T");
|
||||||
|
const [time] = rest.split(".");
|
||||||
|
const readableName = `${date} ${time}`;
|
||||||
|
return Option(readableName, versionId, false);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
import { StringToOption, VersionIdToOption } from "../Components/option";
|
||||||
|
|
||||||
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
import { DangerousActionButton } from "../Components/dangerousActionButton";
|
||||||
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
|
||||||
import { translations } from "../translations";
|
import { translations } from "../translations";
|
||||||
@@ -11,6 +13,20 @@ export function TaskSettingsModal(taskViewModel: TaskViewModel) {
|
|||||||
<main>
|
<main>
|
||||||
<h2>{translations.chatPage.task.taskSettingsHeadline}</h2>
|
<h2>{translations.chatPage.task.taskSettingsHeadline}</h2>
|
||||||
|
|
||||||
|
<label class="tile flex-no">
|
||||||
|
<span class="icon">history</span>
|
||||||
|
<div>
|
||||||
|
<span>{translations.general.fileVersionLabel}</span>
|
||||||
|
<select
|
||||||
|
bind:value={taskViewModel.selectedVersionId}
|
||||||
|
children:append={[taskViewModel.versionIds, VersionIdToOption]}
|
||||||
|
></select>
|
||||||
|
<span class="icon">arrow_drop_down</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<hr></hr>
|
||||||
|
|
||||||
<label class="tile flex-no">
|
<label class="tile flex-no">
|
||||||
<span class="icon">label</span>
|
<span class="icon">label</span>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
const englishTranslations = {
|
const englishTranslations = {
|
||||||
general: {
|
general: {
|
||||||
deleteItemButtonAudioLabel: "delete item",
|
deleteItemButtonAudioLabel: "delete item",
|
||||||
|
|
||||||
abortButton: "Abort",
|
abortButton: "Abort",
|
||||||
closeButton: "Close",
|
closeButton: "Close",
|
||||||
|
|
||||||
confirmButton: "Confirm",
|
confirmButton: "Confirm",
|
||||||
saveButton: "Save",
|
saveButton: "Save",
|
||||||
setButton: "Set",
|
setButton: "Set",
|
||||||
|
|
||||||
|
fileVersionLabel: "Version",
|
||||||
},
|
},
|
||||||
|
|
||||||
regional: {
|
regional: {
|
||||||
@@ -134,7 +136,7 @@ const englishTranslations = {
|
|||||||
createBoardButtonAudioLabel: "create board",
|
createBoardButtonAudioLabel: "create board",
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
noBoardSelected: "No board selected",
|
noBoardSelected: "No board selected",
|
||||||
boardNotFound: "Board not found",
|
boardNotFound: "Board not found",
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ export default class BoardViewModel {
|
|||||||
);
|
);
|
||||||
for (const taskId of taskIds) {
|
for (const taskId of taskIds) {
|
||||||
const taskFileContent: TaskFileContent | null =
|
const taskFileContent: TaskFileContent | null =
|
||||||
this.boardModel.getTaskFileContent(taskId);
|
this.boardModel.getLatestTaskFileContent(taskId);
|
||||||
if (taskFileContent == null) continue;
|
if (taskFileContent == null) continue;
|
||||||
|
|
||||||
const taskViewModel: TaskViewModel = new TaskViewModel(
|
const taskViewModel: TaskViewModel = new TaskViewModel(
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "bloatless-react";
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
import BoardModel, { TaskFileContent } from "../../Model/Files/boardModel";
|
import BoardModel, { TaskFileContent } from "../../Model/Files/boardModel";
|
||||||
|
import { localeCompare, padZero } from "../../Model/Utility/utility";
|
||||||
|
|
||||||
import BoardViewModel from "./boardViewModel";
|
import BoardViewModel from "./boardViewModel";
|
||||||
import { padZero } from "../../Model/Utility/utility";
|
|
||||||
|
|
||||||
export default class TaskViewModel {
|
export default class TaskViewModel {
|
||||||
boardModel: BoardModel;
|
boardModel: BoardModel;
|
||||||
@@ -49,6 +49,9 @@ export default class TaskViewModel {
|
|||||||
date: React.State<string> = new React.State("");
|
date: React.State<string> = new React.State("");
|
||||||
time: React.State<string> = new React.State("");
|
time: React.State<string> = new React.State("");
|
||||||
|
|
||||||
|
selectedVersionId: React.State<string> = new React.State("");
|
||||||
|
versionIds: React.ListState<string> = new React.ListState();
|
||||||
|
|
||||||
// view
|
// view
|
||||||
open = (): void => {
|
open = (): void => {
|
||||||
this.boardViewModel.selectTask(this);
|
this.boardViewModel.selectTask(this);
|
||||||
@@ -96,7 +99,30 @@ export default class TaskViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// load
|
// load
|
||||||
|
loadVersionIds = (): void => {
|
||||||
|
const versionIds: string[] = this.boardModel.listTaskVersionIds(
|
||||||
|
this.task.fileId
|
||||||
|
);
|
||||||
|
const sortedVersionIds = versionIds.sort(localeCompare).reverse();
|
||||||
|
this.versionIds.clear();
|
||||||
|
this.versionIds.add(...sortedVersionIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
switchVersion = (versionId: string): void => {
|
||||||
|
const taskFileContent: TaskFileContent | null =
|
||||||
|
this.boardModel.getSpecificTaskFileContent(this.task.fileId, versionId);
|
||||||
|
if (taskFileContent == null) return;
|
||||||
|
|
||||||
|
this.task = taskFileContent;
|
||||||
|
this.loadTaskData();
|
||||||
|
};
|
||||||
|
|
||||||
loadAllData = (): void => {
|
loadAllData = (): void => {
|
||||||
|
this.loadTaskData();
|
||||||
|
this.loadVersionIds();
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTaskData = (): void => {
|
||||||
this.name.value = this.task.name;
|
this.name.value = this.task.name;
|
||||||
this.description.value = this.task.description ?? "";
|
this.description.value = this.task.description ?? "";
|
||||||
|
|
||||||
@@ -106,6 +132,8 @@ export default class TaskViewModel {
|
|||||||
|
|
||||||
this.date.value = this.task.date ?? "";
|
this.date.value = this.task.date ?? "";
|
||||||
this.time.value = this.task.time ?? "";
|
this.time.value = this.task.time ?? "";
|
||||||
|
|
||||||
|
this.selectedVersionId.value = this.task.fileContentId;
|
||||||
};
|
};
|
||||||
|
|
||||||
// init
|
// init
|
||||||
@@ -119,6 +147,12 @@ export default class TaskViewModel {
|
|||||||
|
|
||||||
this.task = taskFileContent;
|
this.task = taskFileContent;
|
||||||
|
|
||||||
|
// load
|
||||||
this.loadAllData();
|
this.loadAllData();
|
||||||
|
|
||||||
|
// subscriptions
|
||||||
|
this.selectedVersionId.subscribeSilent((selectedVersionId) => {
|
||||||
|
this.switchVersion(selectedVersionId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user