propagate status up and down the tree
Hunter
Monday, August 5th, 2024 at 4:47 PM
412 }
413
414 function toggleTaskState(task) {
- if (task.state === 1) {
- if (task.subtasks.length === 0 || !task.subtasks.every(t => t.state === 1)) {
- task.state = 0;
- updateSubtasksState(task, 0);
415 + if (task.state === 1) {
416 + // Only allow unchecking if not all direct children are checked
417 + if (task.subtasks.length === 0 || !task.subtasks.every(t => t.state === 1)) {
418 + task.state = 0;
419 + updateSubtasksState(task, 0);
420 +
421 + const parent = findParentTask(task);
422 + if (parent) {
423 + updateTaskAndAncestors(parent);
424 }
- } else {
- task.state = 1;
- updateSubtasksState(task, 1);
425 +
426 + renderCurrentView();
427 + selectAndFocusTask(task);
428 + scheduleSave();
429 + }
430 + // If all direct children are checked, do nothing
431 + } else {
432 + task.state = 1;
433 + updateSubtasksState(task, 1);
434 +
435 + const parent = findParentTask(task);
436 + if (parent) {
437 + updateTaskAndAncestors(parent);
438 }
- updateParentTaskState(task);
439 +
440 renderCurrentView();
441 selectAndFocusTask(task);
442 scheduleSave();
443 }
444 + }
445
- function updateParentTaskState(task) {
- const parent = findParentTask(task);
- if (parent) {
- const allChecked = parent.subtasks.every(t => t.state === 1);
- const anyUnchecked = parent.subtasks.some(t => t.state === 0);
446 + //recalculates the state of a task based on its subtasks
447 + function recalculateTaskState(task) {
448 + if (task.subtasks.length === 0) {
449 + return task.state; // If no subtasks, keep current state
450 + }
451 + const allChecked = task.subtasks.every(t => t.state === 1);
452 + const anyUnchecked = task.subtasks.some(t => t.state === 0);
453 + if (allChecked) {
454 + return 1; // Checked
455 + } else if (anyUnchecked) {
456 + return 0; // Unchecked
457 + }
458 + }
459
- if (allChecked) {
- parent.state = 1;
- } else if (anyUnchecked) {
- parent.state = 0;
460 + function updateTaskAndAncestors(task) {
461 + const newState = recalculateTaskState(task);
462 + if (task.state !== newState) {
463 + const oldState = task.state;
464 + task.state = newState;
465 +
466 + // If the task changed from checked to unchecked, update its descendants
467 + if (oldState === 1 && newState === 0) {
468 + updateSubtasksState(task, 0);
469 + }
470 +
471 + const parent = findParentTask(task);
472 + if (parent) {
473 + updateTaskAndAncestors(parent);
474 }
475 + }
476 + }
477 +
478 + function deleteSubtask(subtask) {
479 + const parentTask = taskPath[taskPath.length - 1];
480 + const index = parentTask.subtasks.findIndex(t => t.id === subtask.id);
481
- updateParentTaskState(parent);
482 + if (parentTask.id === 'root' && parentTask.subtasks.length === 1) {
483 + return;
484 }
485 +
486 + // Delete the subtask
487 + parentTask.subtasks = parentTask.subtasks.filter(t => t.id !== subtask.id);
488 +
489 + // Recalculate parent state and propagate changes
490 + updateTaskAndAncestors(parentTask);
491 +
492 + if (parentTask.subtasks.length === 0 && taskPath.length > 1) {
493 + navigateToParentTask();
494 + } else {
495 + renderCurrentView();
496 + if (parentTask.subtasks.length > 0) {
497 + const targetIndex = Math.max(0, index - 1);
498 + selectAndFocusTask(parentTask.subtasks[targetIndex]);
499 + } else {
500 + selectAndFocusTask(parentTask);
501 + }
502 + }
503 + scheduleSave();
504 }
505
506 function updateSubtasksState(task, state) {
592 parentTask.subtasks.push(newSubtask);
593 }
594
- parentTask.state = 0;
- parentTask.subtasks.forEach(subtask => {
- if (subtask.state === 2) {
- subtask.state = 0;
- }
- });
595 + updateTaskAndAncestors(parentTask);
596
597 renderCurrentView();
598 selectAndFocusTask(newSubtask);
612 taskInput.setSelectionRange(0, 0);
613 }
614
- function deleteSubtask(subtask) {
- const parentTask = taskPath[taskPath.length - 1];
- const index = parentTask.subtasks.findIndex(t => t.id === subtask.id);
-
- if (parentTask.id === 'root' && parentTask.subtasks.length === 1) {
- return;
- }
-
- parentTask.subtasks = parentTask.subtasks.filter(t => t.id !== subtask.id);
- updateParentTaskState(parentTask);
-
- if (parentTask.subtasks.length === 0 && taskPath.length > 1) {
- navigateToParentTask();
- } else {
- renderCurrentView();
- if (parentTask.subtasks.length > 0) {
- const targetIndex = Math.max(0, index - 1);
- selectAndFocusTask(parentTask.subtasks[targetIndex]);
- } else {
- selectAndFocusTask(parentTask);
- }
- }
- scheduleSave();
- }
-
615 function deleteCurrentParentTask() {
616 if (taskPath.length <= 1) return; // Don't delete root task
617
626 const index = grandparentTask.subtasks.findIndex(t => t.id === currentParentTask.id);
627
628 grandparentTask.subtasks = grandparentTask.subtasks.filter(t => t.id !== currentParentTask.id);
- updateParentTaskState(grandparentTask);
629 + updateTaskAndAncestors(grandparentTask);
630
631 taskPath.pop(); // Remove the deleted task from the path
632 currentTask = grandparentTask;
693 const currentTaskId = currentTask.id;
694 taskPath.pop();
695 currentTask = taskPath[taskPath.length - 1];
- updateParentTaskState(currentTask);
696 updateBreadcrumbs(currentTask);
697 renderCurrentView();
698 const selectedSubtask = currentTask.subtasks.find(t => t.id === currentTaskId);
789 const task = currentTask.id === taskId ? currentTask : currentTask.subtasks.find(t => t.id === taskId);
790 if (task) {
791 task.text = activeTaskInput.value;
- updateParentTaskState(task);
792 scheduleSave();
793 }
794 }