From 2f35e2477dbac5c0ed5e30b6033729b4ad4484ac Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 24 Sep 2026 10:31:08 +0200 Subject: [PATCH] :sparkles: Hide the save failure warning once a save lands --- frontend/src/app/main/data/persistence.cljs | 3 +++ frontend/src/app/main/errors.cljs | 11 ++++++++++- .../frontend_tests/data/persistence_retry_test.cljs | 7 ++++++- frontend/test/frontend_tests/main_errors_test.cljs | 10 ++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/data/persistence.cljs b/frontend/src/app/main/data/persistence.cljs index 257b98374f..34cdaf8d02 100644 --- a/frontend/src/app/main/data/persistence.cljs +++ b/frontend/src/app/main/data/persistence.cljs @@ -501,7 +501,10 @@ (rx/filter #(= commit-id (:id %))) (rx/take 1) (rx/mapcat (fn [_] + ;; A save that lands shows the connection + ;; is back: the failure warning goes away. (rx/of (discard-commit commit-id) + (ntf/hide :tag errors/persistence-failed-tag) (run-persistence-task))))) (rx/of (persist-commit commit-id))) (rx/take-until stoper-s))) diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index bcec978f07..9bad0be310 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -430,6 +430,7 @@ (defn flash "Show error notification banner and emit error report. A nil timeout keeps the notification visible until dismissed or replaced. + A `:tag` lets the notification be hidden later by that tag. The payload format is derived from the cause: environment failures get a compact report. The audit event name is the canonical one requested by @@ -460,7 +461,7 @@ synchronously from inside an error handler creates a re-entrant event-processing cycle that can exhaust the JS call stack (RangeError: Maximum call stack size exceeded)." - [& {:keys [type hint cause timeout report-link?] + [& {:keys [type hint cause timeout tag report-link?] :or {type :handled timeout 5000}}] (js/Promise. (fn [resolve _reject] @@ -480,6 +481,9 @@ :type :toast :level :error :timeout timeout} + (some? tag) + (assoc :tag tag) + link-report (assoc :links [{:label (tr "labels.download" "report.txt") :callback (partial download-report! link-report)}])))) @@ -544,6 +548,10 @@ ;; The retained changes no longer apply to the restored version. (= :vern-conflict code))) +(def persistence-failed-tag + "Tag of the save failure notification, hidden once a save lands." + :persistence-failed) + (defn flash-persistence [cause] (let [data (ex-data cause)] @@ -555,6 +563,7 @@ (flash :cause cause :type :handled :timeout nil + :tag persistence-failed-tag :report-link? true :hint (tr "errors.save-failed"))))) diff --git a/frontend/test/frontend_tests/data/persistence_retry_test.cljs b/frontend/test/frontend_tests/data/persistence_retry_test.cljs index 10ba3cc0b4..b23c2f6b9a 100644 --- a/frontend/test/frontend_tests/data/persistence_retry_test.cljs +++ b/frontend/test/frontend_tests/data/persistence_retry_test.cljs @@ -538,6 +538,9 @@ (t/is (= :error (get-in @store [:persistence :status]))) (t/is (= 1 (count @failures)) "the user is not warned again") + ;; The warning the first failure left on screen. + (ptk/emit! store #(assoc % :notification {:tag errors/persistence-failed-tag + :level :error})) (rx/push! (last @cycles) :tick) (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) (empty? (get-in @store [:persistence :queue]))) @@ -545,7 +548,9 @@ (t/is (= 6 (count @requests))) (t/is (apply = (map (comp :commit-id second) @requests)) "every attempt carries the same commit id") - (t/is (nil? (get-in @store [:persistence :recovering])))))) + (t/is (nil? (get-in @store [:persistence :recovering]))) + (t/is (nil? (get @store :notification)) + "the failure warning goes away once the save lands")))) (fn [_ _] (if (< (swap! calls inc) 6) (rx/throw (ex-info "offline" {:type :offline})) diff --git a/frontend/test/frontend_tests/main_errors_test.cljs b/frontend/test/frontend_tests/main_errors_test.cljs index 44cc76eb1d..2f8b5659ec 100644 --- a/frontend/test/frontend_tests/main_errors_test.cljs +++ b/frontend/test/frontend_tests/main_errors_test.cljs @@ -618,6 +618,16 @@ (t/is (= timeout (get-in state [:notification :timeout]))) (t/is (= :visible (get-in state [:notification :status])))))))) +(t/deftest persistence-notifications-carry-a-tag-to-hide-them + (let [scheduled (atom []) + events (atom [])] + (with-redefs [tm/schedule (mock/stub #(swap! scheduled conj %)) + st/emit! (mock/stub (fn [& emitted] (swap! events into emitted)))] + (errors/flash-persistence nil) + (doseq [callback @scheduled] (callback)) + (let [state (ptk/update (first @events) {})] + (t/is (= errors/persistence-failed-tag (get-in state [:notification :tag]))))))) + (t/deftest ^:async persistence-notifications-include-an-error-report-download (let [scheduled (atom []) idle-callbacks (atom [])