Revert "Merge pull request #1234 from TriliumNext/feature/task_list"

This reverts commit 58a8821c229898c45551da16476d44c010c345ef, reversing
changes made to 50d491b432ce811c4d5e597e952eb18a89ae6c19.
This commit is contained in:
Elian Doran 2025-03-06 23:52:01 +02:00
parent ee7b97ae56
commit 00e576b052
No known key found for this signature in database
26 changed files with 15 additions and 649 deletions

4
.vscode/launch.json vendored
View File

@ -5,8 +5,8 @@
{
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"name": "nodemon server:start",
"program": "${workspaceFolder}/src/main",
"name": "nodemon start-server",
"program": "${workspaceFolder}/src/www",
"request": "launch",
"restart": true,
"runtimeExecutable": "nodemon",

View File

@ -132,14 +132,3 @@ CREATE INDEX IDX_attachments_ownerId_role
CREATE INDEX IDX_notes_blobId on notes (blobId);
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IDX_attachments_blobId on attachments (blobId);
CREATE TABLE IF NOT EXISTS "tasks"
(
"taskId" TEXT NOT NULL PRIMARY KEY,
"parentNoteId" TEXT NOT NULL,
"title" TEXT NOT NULL DEFAULT "",
"dueDate" INTEGER,
"isDone" INTEGER NOT NULL DEFAULT 0,
"isDeleted" INTEGER NOT NULL DEFAULT 0,
"utcDateModified" TEXT NOT NULL
);

View File

@ -12,7 +12,6 @@ import type { AttachmentRow, BlobRow, RevisionRow } from "./entities/rows.js";
import BBlob from "./entities/bblob.js";
import BRecentNote from "./entities/brecent_note.js";
import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import type BTask from "./entities/btask.js";
interface AttachmentOpts {
includeContentLength?: boolean;
@ -33,7 +32,6 @@ export default class Becca {
attributeIndex!: Record<string, BAttribute[]>;
options!: Record<string, BOption>;
etapiTokens!: Record<string, BEtapiToken>;
tasks!: Record<string, BTask>;
allNoteSetCache: NoteSet | null;
@ -50,7 +48,6 @@ export default class Becca {
this.attributeIndex = {};
this.options = {};
this.etapiTokens = {};
this.tasks = {};
this.dirtyNoteSetCache();
@ -216,14 +213,6 @@ export default class Becca {
return this.etapiTokens[etapiTokenId];
}
getTasks(): BTask[] {
return Object.values(this.tasks);
}
getTask(taskId: string): BTask | null {
return this.tasks[taskId];
}
getEntity<T extends AbstractBeccaEntity<T>>(entityName: string, entityId: string): AbstractBeccaEntity<T> | null {
if (!entityName || !entityId) {
return null;

View File

@ -11,10 +11,9 @@ import BOption from "./entities/boption.js";
import BEtapiToken from "./entities/betapi_token.js";
import cls from "../services/cls.js";
import entityConstructor from "../becca/entity_constructor.js";
import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow, TaskRow } from "./entities/rows.js";
import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from "./entities/rows.js";
import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import ws from "../services/ws.js";
import BTask from "./entities/btask.js";
const beccaLoaded = new Promise<void>(async (res, rej) => {
const sqlInit = (await import("../services/sql_init.js")).default;
@ -64,17 +63,6 @@ function load() {
for (const row of sql.getRows<EtapiTokenRow>(`SELECT etapiTokenId, name, tokenHash, utcDateCreated, utcDateModified FROM etapi_tokens WHERE isDeleted = 0`)) {
new BEtapiToken(row);
}
try {
for (const row of sql.getRows<TaskRow>(`SELECT taskId, parentNoteId, title, dueDate, isDone, isDeleted FROM tasks WHERE isDeleted = 0`)) {
new BTask(row);
}
} catch (e: any) {
// Some older migrations trigger becca which would fail since the "tasks" table is not yet defined (didn't reach the right migration).
if (!(e.message.includes("no such table"))) {
throw e;
}
}
});
for (const noteId in becca.notes) {

View File

@ -1,84 +0,0 @@
import date_utils from "../../services/date_utils.js";
import AbstractBeccaEntity from "./abstract_becca_entity.js";
import type BOption from "./boption.js";
import type { TaskRow } from "./rows.js";
export default class BTask extends AbstractBeccaEntity<BOption> {
static get entityName() {
return "tasks";
}
static get primaryKeyName() {
return "taskId";
}
static get hashedProperties() {
return ["taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted"];
}
taskId?: string;
parentNoteId!: string;
title!: string;
dueDate?: string;
isDone!: boolean;
private _isDeleted?: boolean;
constructor(row?: TaskRow) {
super();
if (!row) {
return;
}
this.updateFromRow(row);
this.init();
}
get isDeleted() {
return !!this._isDeleted;
}
updateFromRow(row: TaskRow) {
this.taskId = row.taskId;
this.parentNoteId = row.parentNoteId;
this.title = row.title;
this.dueDate = row.dueDate;
this.isDone = !!row.isDone;
this._isDeleted = !!row.isDeleted;
this.utcDateModified = row.utcDateModified;
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
init() {
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
protected beforeSaving(opts?: {}): void {
super.beforeSaving();
this.utcDateModified = date_utils.utcNowDateTime();
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
getPojo() {
return {
taskId: this.taskId,
parentNoteId: this.parentNoteId,
title: this.title,
dueDate: this.dueDate,
isDone: this.isDone,
isDeleted: this.isDeleted,
utcDateModified: this.utcDateModified
};
}
}

View File

@ -139,13 +139,3 @@ export interface NoteRow {
utcDateModified: string;
content?: string | Buffer;
}
export interface TaskRow {
taskId?: string;
parentNoteId: string;
title: string;
dueDate?: string;
isDone?: boolean;
isDeleted?: boolean;
utcDateModified?: string;
}

View File

@ -9,7 +9,6 @@ import BNote from "./entities/bnote.js";
import BOption from "./entities/boption.js";
import BRecentNote from "./entities/brecent_note.js";
import BRevision from "./entities/brevision.js";
import BTask from "./entities/btask.js";
type EntityClass = new (row?: any) => AbstractBeccaEntity<any>;
@ -22,8 +21,7 @@ const ENTITY_NAME_TO_ENTITY: Record<string, ConstructorData<any> & EntityClass>
notes: BNote,
options: BOption,
recent_notes: BRecentNote,
revisions: BRevision,
tasks: BTask
revisions: BRevision
};
function getEntityFromEntityName(entityName: keyof typeof ENTITY_NAME_TO_ENTITY) {

View File

@ -28,8 +28,7 @@ const NOTE_TYPE_ICONS = {
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap",
geoMap: "bx bx-map-alt",
taskList: "bx bx-list-check"
geoMap: "bx bx-map-alt"
};
/**
@ -37,25 +36,7 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType =
| "file"
| "image"
| "search"
| "noteMap"
| "launcher"
| "doc"
| "contentWidget"
| "text"
| "relationMap"
| "render"
| "canvas"
| "mermaid"
| "book"
| "webView"
| "code"
| "mindMap"
| "geoMap"
| "taskList";
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap";
export interface NotePathRecord {
isArchived: boolean;

View File

@ -1,34 +0,0 @@
import type { Froca } from "../services/froca-interface.js";
export interface FTaskRow {
taskId: string;
parentNoteId: string;
title: string;
dueDate?: string;
isDone?: boolean;
utcDateModified: string;
}
export default class FTask {
private froca: Froca;
taskId!: string;
parentNoteId!: string;
title!: string;
dueDate?: string;
isDone!: boolean;
utcDateModified!: string;
constructor(froca: Froca, row: FTaskRow) {
this.froca = froca;
this.update(row);
}
update(row: FTaskRow) {
this.taskId = row.taskId;
this.parentNoteId = row.parentNoteId;
this.title = row.title;
this.dueDate = row.dueDate;
this.isDone = !!row.isDone;
this.utcDateModified = row.utcDateModified;
}
}

View File

@ -6,8 +6,6 @@ import appContext from "../components/app_context.js";
import FBlob, { type FBlobRow } from "../entities/fblob.js";
import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js";
import type { Froca } from "./froca-interface.js";
import FTask from "../entities/ftask.js";
import type { FTaskRow } from "../entities/ftask.js";
interface SubtreeResponse {
notes: FNoteRow[];
@ -39,7 +37,6 @@ class FrocaImpl implements Froca {
attributes!: Record<string, FAttribute>;
attachments!: Record<string, FAttachment>;
blobPromises!: Record<string, Promise<void | FBlob> | null>;
tasks!: Record<string, FTask>;
constructor() {
this.initializedPromise = this.loadInitialTree();
@ -55,7 +52,6 @@ class FrocaImpl implements Froca {
this.attributes = {};
this.attachments = {};
this.blobPromises = {};
this.tasks = {};
this.addResp(resp);
}
@ -372,24 +368,6 @@ class FrocaImpl implements Froca {
});
}
getTask(taskId: string) {
return this.tasks[taskId];
}
async getTasks(parentNoteId: string) {
const taskRows = await server.get<FTaskRow[]>(`tasks/${parentNoteId}`);
return this.processTaskRow(taskRows);
}
processTaskRow(taskRows: FTaskRow[]): FTask[] {
return taskRows.map((taskRow) => {
const task = new FTask(this, taskRow);
this.tasks[task.taskId] = task;
return task;
});
}
async getBlob(entityType: string, entityId: string) {
// I'm not sure why we're not using blobIds directly, it would save us this composite key ...
// perhaps one benefit is that we're always requesting the latest blob, not relying on perhaps faulty/slow

View File

@ -8,8 +8,6 @@ import FAttribute, { type FAttributeRow } from "../entities/fattribute.js";
import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js";
import type { default as FNote, FNoteRow } from "../entities/fnote.js";
import type { EntityChange } from "../server_types.js";
import type { FTaskRow } from "../entities/ftask.js";
import FTask from "../entities/ftask.js";
async function processEntityChanges(entityChanges: EntityChange[]) {
const loadResults = new LoadResults(entityChanges);
@ -39,8 +37,6 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
processAttachment(loadResults, ec);
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens") {
// NOOP
} else if (ec.entityName === "tasks") {
processTaskChange(loadResults, ec);
} else {
throw new Error(`Unknown entityName '${ec.entityName}'`);
}
@ -310,35 +306,6 @@ function processAttachment(loadResults: LoadResults, ec: EntityChange) {
loadResults.addAttachmentRow(attachmentEntity);
}
function processTaskChange(loadResults: LoadResults, ec: EntityChange) {
if (ec.isErased && ec.entityId in froca.tasks) {
utils.reloadFrontendApp(`${ec.entityName} '${ec.entityId}' is erased, need to do complete reload.`);
return;
}
let task = froca.tasks[ec.entityId];
const taskEntity = ec.entity as FTaskRow;
if (ec.isErased || (ec.entity as any)?.isDeleted) {
if (task) {
delete froca.tasks[ec.entityId];
}
return;
}
if (ec.entity) {
if (task) {
task.update(ec.entity as FTaskRow);
} else {
task = new FTask(froca, ec.entity as FTaskRow);
froca.tasks[task.taskId] = task;
}
}
loadResults.addTaskRow(taskEntity);
}
export default {
processEntityChanges
};

View File

@ -1,4 +1,4 @@
import type { TaskRow, AttachmentRow } from "../../../becca/entities/rows.js";
import type { AttachmentRow } from "../../../becca/entities/rows.js";
import type { AttributeType } from "../entities/fattribute.js";
import type { EntityChange } from "../server_types.js";
@ -71,7 +71,6 @@ export default class LoadResults {
private contentNoteIdToComponentId: ContentNoteIdToComponentIdRow[];
private optionNames: string[];
private attachmentRows: AttachmentRow[];
private taskRows: TaskRow[];
constructor(entityChanges: EntityChange[]) {
const entities: Record<string, Record<string, any>> = {};
@ -100,8 +99,6 @@ export default class LoadResults {
this.optionNames = [];
this.attachmentRows = [];
this.taskRows = [];
}
getEntityRow<T extends EntityRowNames>(entityName: T, entityId: string): EntityRowMappings[T] {
@ -184,14 +181,6 @@ export default class LoadResults {
return this.contentNoteIdToComponentId.find((l) => l.noteId === noteId && l.componentId !== componentId);
}
isTaskListReloaded(parentNoteId: string) {
if (!parentNoteId) {
return false;
}
return !!this.taskRows.find((tr) => tr.parentNoteId === parentNoteId);
}
addOption(name: string) {
this.optionNames.push(name);
}
@ -212,14 +201,6 @@ export default class LoadResults {
return this.attachmentRows;
}
addTaskRow(task: TaskRow) {
this.taskRows.push(task);
}
getTaskRows() {
return this.taskRows;
}
/**
* @returns {boolean} true if there are changes which could affect the attributes (including inherited ones)
* notably changes in note itself should not have any effect on attributes
@ -237,8 +218,7 @@ export default class LoadResults {
this.revisionRows.length === 0 &&
this.contentNoteIdToComponentId.length === 0 &&
this.optionNames.length === 0 &&
this.attachmentRows.length === 0 &&
this.taskRows.length === 0
this.attachmentRows.length === 0
);
}

View File

@ -18,7 +18,6 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
{ title: t("note_types.task-list"), command, type: "taskList", uiIcon: "bx bx-list-check" }
];
const templateNoteIds = await server.get<string[]>("search-templates");

View File

@ -1,30 +0,0 @@
import type FTask from "../entities/ftask.js";
import server from "./server.js";
interface CreateNewTasksOpts {
parentNoteId: string;
title: string;
}
export async function createNewTask({ parentNoteId, title }: CreateNewTasksOpts) {
await server.post(`tasks`, {
parentNoteId,
title: title.trim()
});
}
export async function toggleTaskDone(taskId: string) {
await server.post(`tasks/${taskId}/toggle`);
}
export async function updateTask(task: FTask) {
if (!task.taskId) {
return;
}
await server.patch(`tasks/${task.taskId}/`, {
taskId: task.taskId,
dueDate: task.dueDate,
isDone: task.isDone
});
}

View File

@ -28,8 +28,7 @@ const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
render: null,
search: null,
text: null,
webView: null,
taskList: null
webView: null
};
const byBookType: Record<ViewTypeOptions, string | null> = {

View File

@ -35,7 +35,6 @@ import GeoMapTypeWidget from "./type_widgets/geo_map.js";
import utils from "../services/utils.js";
import type { NoteType } from "../entities/fnote.js";
import type TypeWidget from "./type_widgets/type_widget.js";
import TaskListWidget from "./type_widgets/task_list.js";
const TPL = `
<div class="note-detail">
@ -73,8 +72,7 @@ const typeWidgetClasses = {
attachmentDetail: AttachmentDetailTypeWidget,
attachmentList: AttachmentListTypeWidget,
mindMap: MindMapWidget,
geoMap: GeoMapTypeWidget,
taskList: TaskListWidget
geoMap: GeoMapTypeWidget
};
/**

View File

@ -49,8 +49,7 @@ const NOTE_TYPES: NoteTypeMapping[] = [
{ type: "image", title: t("note_types.image"), selectable: false },
{ type: "launcher", mime: "", title: t("note_types.launcher"), selectable: false },
{ type: "noteMap", mime: "", title: t("note_types.note-map"), selectable: false },
{ type: "search", title: t("note_types.saved-search"), selectable: false },
{ type: "taskList", title: t("note_types.task-list"), selectable: false }
{ type: "search", title: t("note_types.saved-search"), selectable: false }
];
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => !nt.selectable).map((nt) => nt.type);

View File

@ -1,268 +0,0 @@
import type FNote from "../../entities/fnote.js";
import type FTask from "../../entities/ftask.js";
import froca from "../../services/froca.js";
import TypeWidget from "./type_widget.js";
import * as taskService from "../../services/tasks.js";
import type { EventData } from "../../components/app_context.js";
import dayjs from "dayjs";
import calendarTime from "dayjs/plugin/calendar.js";
import { t } from "../../services/i18n.js";
dayjs.extend(calendarTime);
const TPL = `
<div class="note-detail-task-list note-detail-printable">
<header>
<input type="text" placeholder="Add a new task" class="add-new-task" />
</header>
<ol class="task-container">
</ol>
<style>
.note-detail-task-list {
height: 100%;
contain: none;
padding: 10px;
}
.note-detail-task-list > header {
position: sticky;
top: 0;
z-index: 100;
margin: 0;
padding: 0.5em 0;
background-color: var(--main-background-color);
}
.note-detail-task-list .add-new-task {
width: 100%;
padding: 0.25em 0.5em;
}
.note-detail-task-list .task-container {
list-style-type: none;
margin: 0;
padding: 0;
border-radius: var(--bs-border-radius);
overflow: hidden;
}
.note-detail-task-list .task-container li {
background: var(--input-background-color);
border-bottom: 1px solid var(--main-background-color);
padding: 0.5em 1em;
cursor: pointer;
}
.note-detail-task-list .task-container li:hover {
background: var(--input-hover-background);
transition: background 250ms ease-in-out;
}
.note-detail-task-list .task-container li > header {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
}
.note-detail-task-list .task-container li .check {
margin-right: 0.5em;
}
.note-detail-task-list .task-container li .title {
flex-grow: 1;
}
.note-detail-task-list .task-container li .due-date {
font-size: 0.9rem;
}
.note-detail-task-list .task-container li.overdue .due-date {
color: #fd8282;
}
</style>
</div>
`;
function buildTasks(tasks: FTask[]) {
let html = "";
const now = dayjs();
const dateFormat = "DD-MM-YYYY";
for (const task of tasks) {
const classes = ["task"];
if (task.dueDate && dayjs(task.dueDate).isBefore(now, "days")) {
classes.push("overdue");
}
html += `<li class="${classes.join(" ")}" data-task-id="${task.taskId}">`;
html += "<header>";
html += '<span class="title">';
html += `<input type="checkbox" class="check" ${task.isDone ? "checked" : ""} />`;
html += `${task.title}</span>`;
html += '</span>';
if (task.dueDate) {
html += `<span class="due-date">`;
html += `<span class="bx bx-calendar"></span> `;
html += dayjs(task.dueDate).calendar(null, {
sameDay: `[${t("tasks.due.today")}]`,
nextDay: `[${t("tasks.due.tomorrow")}]`,
nextWeek: "dddd",
lastDay: `[${t("tasks.due.yesterday")}]`,
lastWeek: dateFormat,
sameElse: dateFormat
});
html += "</span>";
}
html += "</header>";
html += `<div class="edit-container"></div>`;
html += `</li>`;
}
return html;
}
function buildEditContainer(task: FTask) {
return `\
<label>Due date:</label>
<input type="date" data-tasks-role="due-date" value="${task.dueDate ?? ""}" />
`;
}
export default class TaskListWidget extends TypeWidget {
private $taskContainer!: JQuery<HTMLElement>;
private $addNewTask!: JQuery<HTMLElement>;
static getType() {
return "taskList";
}
doRender() {
this.$widget = $(TPL);
this.$addNewTask = this.$widget.find(".add-new-task");
this.$taskContainer = this.$widget.find(".task-container");
this.$addNewTask.on("keydown", (e) => {
if (e.key === "Enter") {
this.#createNewTask(String(this.$addNewTask.val()));
this.$addNewTask.val("");
}
});
this.$taskContainer.on("change", "input.check", (e) => {
const $target = $(e.target);
const taskId = $target.closest("li")[0].dataset.taskId;
if (!taskId) {
return;
}
taskService.toggleTaskDone(taskId);
});
this.$taskContainer.on("click", "li", (e) => {
if ((e.target as HTMLElement).tagName === "INPUT") {
return;
}
const $target = $(e.target);
// Clear existing edit containers.
const $existingContainers = this.$taskContainer.find(".edit-container");
$existingContainers.html("");
// Add the new edit container.
const $editContainer = $target.closest("li").find(".edit-container");
const task = this.#getCorrespondingTask($target);
if (task) {
$editContainer.html(buildEditContainer(task));
}
});
this.$taskContainer.on("change", "input:not(.check)", async (e) => {
const $target = $(e.target);
const task = this.#getCorrespondingTask($target);
if (!task) {
return;
}
const role = $target.data("tasks-role");
const value = String($target.val());
switch (role) {
case "due-date":
task.dueDate = value;
break;
}
await taskService.updateTask(task);
});
}
#getCorrespondingTask($target: JQuery<HTMLElement>) {
const $parentEl = $target.closest("li");
if (!$parentEl.length) {
return;
}
const taskId = $parentEl[0].dataset.taskId;
if (!taskId) {
return;
}
return froca.getTask(taskId);
}
async #createNewTask(title: string) {
if (!title || !this.noteId) {
return;
}
await taskService.createNewTask({
title,
parentNoteId: this.noteId
});
}
async #getTasks() {
if (!this.noteId) {
return [];
}
return (await froca.getTasks(this.noteId)).toSorted((a, b) => {
// Sort by due date, closest date first.
if (!a.dueDate) {
return 1;
}
if (!b.dueDate) {
return -1;
}
return a.dueDate.localeCompare(b.dueDate, "en");
});
}
async doRefresh(note: FNote) {
this.$widget.show();
if (!this.note || !this.noteId) {
return;
}
const tasks = await this.#getTasks();
const tasksHtml = buildTasks(tasks);
this.$taskContainer.html(tasksHtml);
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (this.noteId && loadResults.isTaskListReloaded(this.noteId)) {
this.refresh();
}
}
}

View File

@ -1422,8 +1422,7 @@
"widget": "Widget",
"confirm-change": "It is not recommended to change note type when note content is not empty. Do you want to continue anyway?",
"geo-map": "Geo Map",
"beta-feature": "Beta",
"task-list": "To-Do List"
"beta-feature": "Beta"
},
"protect_note": {
"toggle-on": "Protect the note",

View File

@ -1,24 +0,0 @@
import type { Request } from "express";
import * as tasksService from "../../services/tasks.js";
export function getTasks(req: Request) {
const { parentNoteId } = req.params;
return tasksService.getTasks(parentNoteId);
}
export function createNewTask(req: Request) {
return tasksService.createNewTask(req.body);
}
export function updateTask(req: Request) {
return tasksService.updateTask(req.params.taskId, req.body);
}
export function toggleTaskDone(req: Request) {
const { taskId } = req.params;
if (!taskId) {
return;
}
return tasksService.toggleTaskDone(taskId);
}

View File

@ -72,7 +72,6 @@ import etapiSpecRoute from "../etapi/spec.js";
import etapiBackupRoute from "../etapi/backup.js";
import apiDocsRoute from "./api_docs.js";
import * as tasksRoute from "./api/tasks.js";
const MAX_ALLOWED_FILE_SIZE_MB = 250;
const GET = "get",
@ -280,11 +279,6 @@ function register(app: express.Application) {
apiRoute(PATCH, "/api/etapi-tokens/:etapiTokenId", etapiTokensApiRoutes.patchToken);
apiRoute(DEL, "/api/etapi-tokens/:etapiTokenId", etapiTokensApiRoutes.deleteToken);
apiRoute(GET, "/api/tasks/:parentNoteId", tasksRoute.getTasks);
apiRoute(PST, "/api/tasks", tasksRoute.createNewTask);
apiRoute(PST, "/api/tasks/:taskId/toggle", tasksRoute.toggleTaskDone);
apiRoute(PATCH, "/api/tasks/:taskId", tasksRoute.updateTask);
// in case of local electron, local calls are allowed unauthenticated, for server they need auth
const clipperMiddleware = isElectron ? [] : [auth.checkEtapiToken];

View File

@ -6,7 +6,7 @@ import packageJson from "../../package.json" with { type: "json" };
import dataDir from "./data_dir.js";
const APP_DB_VERSION = 228;
const SYNC_VERSION = 35;
const SYNC_VERSION = 34;
const CLIPPER_PROTOCOL_VERSION = "1.0";
export default {

View File

@ -888,7 +888,7 @@ class ConsistencyChecks {
return `${tableName}: ${count}`;
}
const tables = ["notes", "revisions", "attachments", "branches", "attributes", "etapi_tokens", "blobs", "tasks"];
const tables = ["notes", "revisions", "attachments", "branches", "attributes", "etapi_tokens", "blobs"];
log.info(`Table counts: ${tables.map((tableName) => getTableRowCount(tableName)).join(", ")}`);
}

View File

@ -15,8 +15,7 @@ const noteTypes = [
{ type: "doc", defaultMime: "" },
{ type: "contentWidget", defaultMime: "" },
{ type: "mindMap", defaultMime: "application/json" },
{ type: "geoMap", defaultMime: "application/json" },
{ type: "taskList", defaultMime: "" }
{ type: "geoMap", defaultMime: "application/json" }
];
function getDefaultMimeForNoteType(typeName: string) {

View File

@ -1,35 +0,0 @@
import becca from "../becca/becca.js";
import BTask from "../becca/entities/btask.js";
import type { TaskRow } from "../becca/entities/rows.js";
export function getTasks(parentNoteId: string) {
return becca.getTasks().filter((task) => task.parentNoteId === parentNoteId && !task.isDone);
}
interface CreateTaskParams {
parentNoteId: string;
title: string;
dueDate?: string;
}
export function createNewTask(params: CreateTaskParams) {
const task = new BTask(params);
task.save();
return {
task
};
}
export function toggleTaskDone(taskId: string) {
const task = becca.tasks[taskId];
task.isDone = !task.isDone;
task.save();
}
export function updateTask(taskId: string, content: TaskRow) {
const task = becca.tasks[taskId];
task.isDone = !!content.isDone;
task.dueDate = content.dueDate;
task.save();
}

View File

@ -188,12 +188,6 @@ function fillInAdditionalProperties(entityChange: EntityChange) {
WHERE attachmentId = ?`,
[entityChange.entityId]
);
} else if (entityChange.entityName === "tasks") {
entityChange.entity = becca.getTask(entityChange.entity);
if (!entityChange.entity) {
entityChange.entity = sql.getRow(`SELECT * FROM tasks WHERE taskId = ?`, [entityChange.entityId]);
}
}
if (entityChange.entity instanceof AbstractBeccaEntity) {