implement persistent storage with 1sec debounce
Hunter
Thursday, August 1st, 2024 at 7:28 PM
48 <script>
49 document.addEventListener('DOMContentLoaded', function() {
50 const appContainer = document.getElementById('app-container');
- let currentTask = { id: 'root', text: 'todo', state: 0, subtasks: [{ id: Date.now(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null };
51 + let rootTask = loadFromLocalStorage();
52 + let currentTask = rootTask;
53 let taskPath = [currentTask];
54 let lastSubtaskDownArrowReleased = false;
55 + let saveTimer = null;
56 +
57 + function scheduleSave() {
58 + if (saveTimer) {
59 + clearTimeout(saveTimer);
60 + }
61 + saveTimer = setTimeout(saveToLocalStorage, 1000);
62 + }
63 +
64 + function saveToLocalStorage() {
65 + const serializedTasks = serializeTaskTree(taskPath[0]);
66 + localStorage.setItem('taskTree', serializedTasks);
67 + saveTimer = null;
68 + }
69 +
70 + function serializeTaskTree(task, depth = 0) {
71 + const indentation = '\t'.repeat(depth);
72 + let status = task.state === 0 ? '_' : (task.state === 1 ? 'x' : '?');
73 + let serialized = `${indentation}${status} ${task.text}\n`;
74 +
75 + for (let subtask of task.subtasks) {
76 + serialized += serializeTaskTree(subtask, depth + 1);
77 + }
78 +
79 + return serialized;
80 + }
81 +
82 + function loadFromLocalStorage() {
83 + const savedTasks = localStorage.getItem('taskTree');
84 + if (savedTasks) {
85 + return deserializeTaskTree(savedTasks);
86 + } else {
87 + return { id: 'root', text: 'todo', state: 0, subtasks: [{ id: Date.now(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null };
88 + }
89 + }
90 +
91 + function deserializeTaskTree(serialized) {
92 + const lines = serialized.split('\n').filter(line => line.trim() !== '');
93 + const root = { id: 'root', subtasks: [] };
94 + const stack = [{ task: root, depth: -1 }];
95 +
96 + for (let line of lines) {
97 + const depth = (line.match(/^\t*/)[0] || '').length;
98 + const status = line[depth];
99 + const text = line.slice(depth + 2);
100 +
101 + const newTask = {
102 + id: Date.now() + Math.random(),
103 + text: text,
104 + state: status === '_' ? 0 : (status === 'x' ? 1 : 2),
105 + subtasks: [],
106 + selectedSubtaskId: null
107 + };
108 +
109 + while (stack.length > 1 && stack[stack.length - 1].depth >= depth) {
110 + stack.pop();
111 + }
112 +
113 + if (depth === 0) {
114 + Object.assign(root, newTask);
115 + } else {
116 + stack[stack.length - 1].task.subtasks.push(newTask);
117 + }
118 + stack.push({ task: newTask, depth: depth });
119 + }
120 +
121 + return root;
122 + }
123
124 const keyHandler = {
125 backspace: {
243 if (task === currentTask) {
244 updatePageTitle(task);
245 }
246 + scheduleSave();
247 });
248
249 taskInput.addEventListener('focus', () => setActiveTask(taskInput, task));
317 updateParentTaskState(task);
318 renderCurrentView();
319 selectAndFocusTask(task);
320 + scheduleSave();
321 }
322
323 function updateParentTaskState(task) {
406
407 renderCurrentView();
408 selectAndFocusTask(subtask);
409 + scheduleSave();
410 }
411
412 function addNewSubtask(parentTask, currentSubtask = null) {
427
428 renderCurrentView();
429 selectAndFocusTask(newSubtask);
430 + scheduleSave();
431 }
432
433 function selectAndFocusTask(task) {
460 selectAndFocusTask(parentTask);
461 }
462 }
463 + scheduleSave();
464 }
465
466 function deleteCurrentParentTask() {