From c138a8bcf6719fdf4484595375dd207b29066444 Mon Sep 17 00:00:00 2001 From: Tristan Michael Date: Mon, 30 Mar 2026 16:14:40 -0700 Subject: [PATCH] fix(gui): wire sync credentials, fix memory leak, race condition, add delete confirmations - Add load_credentials Tauri command and use it in triggerSync() instead of passing empty username/password strings - Replace raw __TAURI_INTERNALS__.invoke() with proper invoke import in SettingsScreen - Wrap window event listeners in $effect() with cleanup to prevent memory leak on component remount - Return created Task from createTask() and use it directly in NewTaskInput instead of guessing from array index - Add confirm() dialogs before deleting tasks, lists, and workspaces --- apps/tauri/src-tauri/src/lib.rs | 6 ++++++ .../src/lib/components/NewTaskInput.svelte | 5 ++--- .../src/lib/components/TaskDetailView.svelte | 1 + .../src/lib/screens/SettingsScreen.svelte | 8 ++++--- apps/tauri/src/lib/screens/TasksScreen.svelte | 21 ++++++++++++------- apps/tauri/src/lib/stores/app.svelte.ts | 12 +++++++---- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/apps/tauri/src-tauri/src/lib.rs b/apps/tauri/src-tauri/src/lib.rs index b4ef687..9c2ed51 100644 --- a/apps/tauri/src-tauri/src/lib.rs +++ b/apps/tauri/src-tauri/src/lib.rs @@ -310,6 +310,11 @@ fn store_credentials( webdav::store_credentials(&domain, &username, &password).map_err(|e| e.to_string()) } +#[tauri::command] +fn load_credentials(domain: String) -> Result<(String, String), String> { + webdav::load_credentials(&domain).map_err(|e| e.to_string()) +} + #[tauri::command] async fn test_webdav_connection( url: String, @@ -373,6 +378,7 @@ pub fn run() { reorder_task, set_webdav_config, store_credentials, + load_credentials, test_webdav_connection, sync_workspace, ]) diff --git a/apps/tauri/src/lib/components/NewTaskInput.svelte b/apps/tauri/src/lib/components/NewTaskInput.svelte index 9efc02b..189566a 100644 --- a/apps/tauri/src/lib/components/NewTaskInput.svelte +++ b/apps/tauri/src/lib/components/NewTaskInput.svelte @@ -15,9 +15,8 @@ async function handleSubmit() { if (!title.trim()) return; - await app.createTask(title.trim(), description.trim() || undefined); - if (dueDate && app.tasks.length > 0) { - const created = app.tasks[app.tasks.length - 1]; + const created = await app.createTask(title.trim(), description.trim() || undefined); + if (dueDate && created) { await app.updateTask({ ...created, due_date: dueDate, updated_at: new Date().toISOString() }); } title = ""; diff --git a/apps/tauri/src/lib/components/TaskDetailView.svelte b/apps/tauri/src/lib/components/TaskDetailView.svelte index 20085e7..259dccc 100644 --- a/apps/tauri/src/lib/components/TaskDetailView.svelte +++ b/apps/tauri/src/lib/components/TaskDetailView.svelte @@ -50,6 +50,7 @@ async function handleDelete() { showMenu = false; + if (!confirm(`Delete task "${task.title}"?`)) return; await app.deleteTask(task.id); onback(); } diff --git a/apps/tauri/src/lib/screens/SettingsScreen.svelte b/apps/tauri/src/lib/screens/SettingsScreen.svelte index 311ddc2..492ddef 100644 --- a/apps/tauri/src/lib/screens/SettingsScreen.svelte +++ b/apps/tauri/src/lib/screens/SettingsScreen.svelte @@ -1,4 +1,5 @@ diff --git a/apps/tauri/src/lib/screens/TasksScreen.svelte b/apps/tauri/src/lib/screens/TasksScreen.svelte index 8bf756e..9f8396e 100644 --- a/apps/tauri/src/lib/screens/TasksScreen.svelte +++ b/apps/tauri/src/lib/screens/TasksScreen.svelte @@ -39,9 +39,6 @@ if (wsMenuName && !target.closest("[data-ws-menu]")) wsMenuName = null; } - if (typeof window !== "undefined") { - window.addEventListener("mousedown", handleWindowClick); - } let newListName = $state(""); let showCompleted = $state(false); let completedVisible = $state(false); @@ -52,13 +49,19 @@ let resizing = $state(false); let resizeTimer: ReturnType; - if (typeof window !== "undefined") { - window.addEventListener("resize", () => { + $effect(() => { + window.addEventListener("mousedown", handleWindowClick); + const handleResize = () => { resizing = true; clearTimeout(resizeTimer); resizeTimer = setTimeout(() => (resizing = false), 150); - }); - } + }; + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("mousedown", handleWindowClick); + window.removeEventListener("resize", handleResize); + }; + }); async function handleNewList() { if (!newListName.trim()) return; @@ -69,6 +72,8 @@ async function handleDeleteList(id: string) { listMenuId = null; + const list = app.lists.find(l => l.id === id); + if (!confirm(`Delete list "${list?.title ?? id}" and all its tasks?`)) return; await app.deleteList(id); } @@ -270,7 +275,7 @@ {#if wsMenuName === name}