From 18c8769f05653a9b2ab212d28c178cf169a4d954 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 18 Jun 2026 10:43:30 +0200 Subject: [PATCH] :recycle: Extract wait-for-persistence into shared helper (#10272) Add wait-persisted and force-persist-and-wait to app.main.data.persistence, removing 5 inline copies and 2 private helper functions across the codebase. Replaced in: - assets.cljs -> dwp/force-persist-and-wait 400 - clipboard.cljs -> dps/force-persist-and-wait 400 - versions.cljs -> dwp/wait-persisted (3 call sites, dropped 2 priv fns) - shape.cljs -> dwp/wait-persisted 5000 Co-authored-by: deepseek-v4-flash --- .../src/app/main/data/exports/assets.cljs | 9 +------ frontend/src/app/main/data/persistence.cljs | 21 +++++++++++++++ .../app/main/data/workspace/clipboard.cljs | 8 ++---- .../src/app/main/data/workspace/versions.cljs | 26 +++++-------------- frontend/src/app/plugins/shape.cljs | 6 +---- 5 files changed, 31 insertions(+), 39 deletions(-) diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index 6a2105b737..24d72e5e92 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -15,7 +15,6 @@ [app.main.data.modal :as modal] [app.main.data.persistence :as dwp] [app.main.features :as features] - [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] [app.util.dom :as dom] @@ -204,13 +203,7 @@ :wait true :is-wasm (wasm-export-enabled? state)}] (rx/concat - (rx/of ::dwp/force-persist) - - ;; Wait the persist to be succesfull - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/first) - (rx/timeout 400 (rx/empty))) + (dwp/force-persist-and-wait 400) (->> (rp/cmd! :export params) (rx/map (fn [{:keys [filename mtype uri]}] diff --git a/frontend/src/app/main/data/persistence.cljs b/frontend/src/app/main/data/persistence.cljs index ba86873122..03fbdb319a 100644 --- a/frontend/src/app/main/data/persistence.cljs +++ b/frontend/src/app/main/data/persistence.cljs @@ -12,6 +12,7 @@ [app.common.uuid :as uuid] [app.main.data.changes :as dch] [app.main.data.helpers :as dsh] + [app.main.refs :as refs] [app.main.repo :as rp] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -26,6 +27,26 @@ (def force-persist? #(= % ::force-persist)) +(defn wait-persisted + "Returns an observable that emits the first terminal persistence status + (nil | :saved) and completes. With a timeout-ms, if persistence doesn't + settle in time the observable completes silently without emitting." + ([] (wait-persisted nil)) + ([timeout-ms] + (let [base (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) + (rx/filter #(or (nil? %) (= :saved %))) + (rx/take 1))] + (if timeout-ms + (->> base (rx/timeout timeout-ms (rx/empty))) + base)))) + +(defn force-persist-and-wait + "Convenience that emits the force-persist event and then waits for + persistence to settle. Returns the combined observable." + ([] (force-persist-and-wait nil)) + ([timeout-ms] + (rx/concat (rx/of ::force-persist) (wait-persisted timeout-ms)))) + (defn- update-status [status] (ptk/reify ::update-status diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index 265ff3139b..c87a5707f9 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -37,7 +37,7 @@ [app.main.data.exports.wasm :as wasm.exports] [app.main.data.helpers :as dsh] [app.main.data.notifications :as ntf] - [app.main.data.persistence :as-alias dps] + [app.main.data.persistence :as dps] [app.main.data.workspace.media :as dwm] [app.main.data.workspace.selection :as dws] [app.main.data.workspace.shapes :as dwsh] @@ -1166,11 +1166,7 @@ (rx/concat ;; Ensure current state persisted before exporting. - (rx/of ::dps/force-persist) - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/first) - (rx/timeout 400 (rx/empty))) + (dps/force-persist-and-wait 400) ;; Exporting itself can take its time, better to notify that we are busy. (rx/of (ntf/info (tr "workspace.clipboard.copying"))) diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs index 4b0abde02c..7dc4bfbb1f 100644 --- a/frontend/src/app/main/data/workspace/versions.cljs +++ b/frontend/src/app/main/data/workspace/versions.cljs @@ -19,7 +19,6 @@ [app.main.data.workspace.pages :as dwpg] [app.main.data.workspace.thumbnails :as th] [app.main.features :as features] - [app.main.refs :as refs] [app.main.repo :as rp] [app.util.i18n :refer [tr]] [beicon.v2.core :as rx] @@ -73,9 +72,7 @@ (rx/of ::dwp/force-persist (ev/event {::ev/name "create-version"})) - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/take 1) + (->> (dwp/wait-persisted) (rx/mapcat #(rp/cmd! :create-file-snapshot {:file-id file-id :label label})) (rx/mapcat (fn [{:keys [id]}] @@ -120,13 +117,6 @@ (effect [_ _ _] (th/clear-queue!)))) -(defn- wait-for-persistence - [file-id snapshot-id] - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/take 1) - (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id snapshot-id})))) - (defn delete-version [id] (assert (uuid? id) "expected valid uuid for `id`") @@ -223,7 +213,8 @@ (rx/of ::dwp/force-persist (dw/remove-layout-flag :document-history)) - (->> (wait-for-persistence file-id id) + (->> (dwp/wait-persisted) + (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/map #(initialize-version)))))))) (defn enter-restore @@ -349,12 +340,6 @@ ;; PLUGINS SPECIFIC EVENTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- wait-persisted-status - [] - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/take 1))) - (defn create-version-from-plugins [file-id label resolve reject] @@ -371,7 +356,7 @@ (rx/of ::dwp/force-persist)) (->> (if (= file-id current-file-id) - (wait-persisted-status) + (dwp/wait-persisted) (rx/of :nothing)) (rx/mapcat (fn [_] @@ -401,7 +386,8 @@ ::ev/origin "plugins"}) ::dwp/force-persist) - (->> (wait-for-persistence file-id id) + (->> (dwp/wait-persisted) + (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/map #(initialize-version))) (->> (rx/of 1) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 7d0f478b66..ad44488723 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -48,7 +48,6 @@ [app.main.data.workspace.texts :as dwt] [app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.variants :as dwv] - [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] [app.plugins.fills :as fills] @@ -1304,10 +1303,7 @@ ;; renders locally and does not need this.) (st/emit! ::dwp/force-persist) (->> (rx/concat - (->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) - (rx/filter #(or (nil? %) (= :saved %))) - (rx/first) - (rx/timeout 5000 (rx/empty)) + (->> (dwp/wait-persisted 5000) (rx/ignore)) (rp/cmd! :export payload)) (rx/mapcat (fn [{:keys [uri]}]