diff --git a/src/public/app/services/froca.ts b/src/public/app/services/froca.ts index d787fbcf0..ca5749d98 100644 --- a/src/public/app/services/froca.ts +++ b/src/public/app/services/froca.ts @@ -372,6 +372,10 @@ class FrocaImpl implements Froca { }); } + getTask(taskId: string) { + return this.tasks[taskId]; + } + async getTasks(parentNoteId: string) { const taskRows = await server.get(`tasks/${parentNoteId}`); return this.processTaskRow(taskRows); diff --git a/src/public/app/services/tasks.ts b/src/public/app/services/tasks.ts index becf7fd25..9f6ef60da 100644 --- a/src/public/app/services/tasks.ts +++ b/src/public/app/services/tasks.ts @@ -1,3 +1,4 @@ +import type FTask from "../entities/ftask.js"; import server from "./server.js"; interface CreateNewTasksOpts { @@ -15,3 +16,15 @@ export async function createNewTask({ parentNoteId, title }: CreateNewTasksOpts) 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 + }); +} diff --git a/src/public/app/widgets/type_widgets/task_list.ts b/src/public/app/widgets/type_widgets/task_list.ts index 9db2ea45c..0241afa19 100644 --- a/src/public/app/widgets/type_widgets/task_list.ts +++ b/src/public/app/widgets/type_widgets/task_list.ts @@ -68,8 +68,8 @@ function buildTasks(tasks: FTask[]) { let html = ''; for (const task of tasks) { - html += `
  • `; - html += ``; + html += `
  • `; + html += ``; html += task.title; html += `
    `; html += `
  • `; @@ -79,8 +79,10 @@ function buildTasks(tasks: FTask[]) { } function buildEditContainer() { - const html = `Edit goes here.`; - return html; + return `\ + + + `; } export default class TaskListWidget extends TypeWidget { @@ -102,9 +104,9 @@ export default class TaskListWidget extends TypeWidget { } }); - this.$taskContainer.on("change", "input", (e) => { - const target = e.target as HTMLInputElement; - const taskId = target.dataset.taskId; + this.$taskContainer.on("change", "input.check", (e) => { + const $target = $(e.target); + const taskId = $target.closest("li")[0].dataset.taskId; if (!taskId) { return; @@ -114,15 +116,47 @@ export default class TaskListWidget extends TypeWidget { }); this.$taskContainer.on("click", "li", (e) => { + const $target = $(e.target); + + // Don't collapse when clicking on an inside element such as the due date dropdown. + if (e.currentTarget !== e.target) { + return; + } + // Clear existing edit containers. const $existingContainers = this.$taskContainer.find(".edit-container"); + $existingContainers.html(""); // Add the new edit container. - const $target = $(e.target); const $editContainer = $target.find(".edit-container"); $editContainer.html(buildEditContainer()); }); + + this.$taskContainer.on("change", "input", async (e) => { + const $target = $(e.target); + const taskId = $target.closest("li")[0].dataset.taskId; + if (!taskId) { + return; + } + + const task = froca.getTask(taskId); + + 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); + }); } async #createNewTask(title: string) { diff --git a/src/routes/api/tasks.ts b/src/routes/api/tasks.ts index 102de086d..72a9ea85a 100644 --- a/src/routes/api/tasks.ts +++ b/src/routes/api/tasks.ts @@ -10,6 +10,10 @@ 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) { diff --git a/src/routes/routes.ts b/src/routes/routes.ts index da37854d3..7d5fea44b 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -283,6 +283,7 @@ function register(app: express.Application) { 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]; diff --git a/src/services/tasks.ts b/src/services/tasks.ts index 92d2b25db..5033ee9b2 100644 --- a/src/services/tasks.ts +++ b/src/services/tasks.ts @@ -1,5 +1,6 @@ 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() @@ -26,3 +27,10 @@ export function toggleTaskDone(taskId: string) { 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(); +}