From a3c969aa54cac65a14b70666452151f5f865ead3 Mon Sep 17 00:00:00 2001 From: TuringLambdaAI Date: Tue, 20 Oct 2026 19:54:31 +0800 Subject: [PATCH 1/2] fix(linux): keep quick-add drafts intact until save succeeds --- linux/src/main.cpp | 71 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/linux/src/main.cpp b/linux/src/main.cpp index ea905121..47dc3d53 100644 --- a/linux/src/main.cpp +++ b/linux/src/main.cpp @@ -2966,31 +2966,66 @@ std::optional quick_add_target_list() { return std::nullopt; } +// Async parsing/saving must never consume an unsent draft. Keep one add +// in flight so repeated Enter/plus cannot create duplicate tasks. +bool g_quick_add_in_flight = false; + void on_quick_add_activate(GtkEntry* entry, gpointer) { - auto const text = std::string(gtk_editable_get_text(GTK_EDITABLE(entry))); - if (text.empty() || g_state.api == nullptr) return; + auto const submitted = + std::string(gtk_editable_get_text(GTK_EDITABLE(entry))); + if (submitted.find_first_not_of(" \\t\\r\\n") == std::string::npos || + g_state.api == nullptr || g_quick_add_in_flight) { + return; + } auto const target = quick_add_target_list(); if (!target.has_value()) { flash_status(tr("taskCreateListFirst")); return; } - gtk_editable_set_text(GTK_EDITABLE(entry), ""); - gtk_label_set_text(g_state.quick_add_preview, ""); - // One quick-add grammar on every platform: the backend splits text + due - // (parse_quick_add); this host only forwards the result. + + // Capture the concrete destination before the parser request. If the + // user selects another list while this runs, its target cannot change. + // Do not clear the GtkEntry until the backend has actually persisted it. + g_quick_add_in_flight = true; g_state.api->parse_quick_add_async( - text, [target](rivet_app::Result result) { - on_result( - std::move(result), - [target](rivet_app::QuickAddParse const& parse) { - rivet_app::Task seed{}; - seed.text = parse.text; - seed.list_id = *target; - seed.due_date = parse.due_date; - seed.due_time = parse.due_time; - add_task_full(seed); - flash_status(tr("statusTaskAdded")); - }); + submitted, [submitted, target]( + rivet_app::Result result) { + dispatch_ui([submitted, target, result = std::move(result)]() mutable { + try { + auto const parsed = result.get(); + if (g_state.api == nullptr) { + g_quick_add_in_flight = false; + return; + } + g_state.api->add_task_async( + parsed.text, *target, parsed.due_date, parsed.due_time, + std::nullopt, + [submitted](rivet_app::Result added) { + dispatch_ui([submitted, added = std::move(added)]() mutable { + g_quick_add_in_flight = false; + try { + added.get(); + // Do not wipe task B when the completion for task A + // arrives after the user has started typing again. + if (std::string(gtk_editable_get_text( + GTK_EDITABLE(g_state.quick_add))) == submitted) { + gtk_editable_set_text( + GTK_EDITABLE(g_state.quick_add), ""); + gtk_label_set_text(g_state.quick_add_preview, ""); + gtk_widget_set_visible( + GTK_WIDGET(g_state.quick_add_preview), FALSE); + } + flash_status(tr("statusTaskAdded")); + } catch (std::exception const& e) { + set_status_error(e.what()); + } + }); + }); + } catch (std::exception const& e) { + g_quick_add_in_flight = false; + set_status_error(e.what()); + } + }); }); } From 3c0456bcbe37efbbac334fde192dfb82b190236a Mon Sep 17 00:00:00 2001 From: TuringLambdaAI Date: Tue, 20 Oct 2026 19:55:03 +0800 Subject: [PATCH 2/2] fix(linux): recognize whitespace-only quick-add input --- linux/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/src/main.cpp b/linux/src/main.cpp index 47dc3d53..2d404ec4 100644 --- a/linux/src/main.cpp +++ b/linux/src/main.cpp @@ -2973,7 +2973,7 @@ bool g_quick_add_in_flight = false; void on_quick_add_activate(GtkEntry* entry, gpointer) { auto const submitted = std::string(gtk_editable_get_text(GTK_EDITABLE(entry))); - if (submitted.find_first_not_of(" \\t\\r\\n") == std::string::npos || + if (submitted.find_first_not_of(" \t\r\n") == std::string::npos || g_state.api == nullptr || g_quick_add_in_flight) { return; }