propagate status up and down the tree

Hunter
Monday, August 5th, 2024 at 4:47 PM

index.html+80-52

index.html
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  						}