✨ Hide the save failure warning once a save lands

This commit is contained in:
alonso.torres 2026-09-24 10:31:08 +02:00
parent 275989e5b5
commit 2f35e2477d
4 changed files with 29 additions and 2 deletions

View File

@ -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)))

View File

@ -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")))))

View File

@ -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}))

View File

@ -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 [])