From ea019bcad5f5e1345317738485ced88f243f40da Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 15 May 2023 14:35:35 +0800 Subject: [PATCH] fix: broken block ref when backspace/delete a block --- src/main/frontend/handler/editor.cljs | 57 +++++++++++++------ src/main/frontend/modules/file/core.cljs | 11 +++- src/main/frontend/modules/outliner/core.cljs | 5 ++ .../frontend/modules/outliner/datascript.cljc | 28 ++++++++- .../modules/outliner/transaction.cljc | 11 +++- 5 files changed, 88 insertions(+), 24 deletions(-) diff --git a/src/main/frontend/handler/editor.cljs b/src/main/frontend/handler/editor.cljs index 81670020d..f3afb0635 100644 --- a/src/main/frontend/handler/editor.cljs +++ b/src/main/frontend/handler/editor.cljs @@ -353,8 +353,16 @@ block (apply dissoc block db-schema/retract-attributes)] (profile "Save block: " - (let [block' (wrap-parse-block block) - opts' (merge opts {:outliner-op :save-block})] + (let [original-uuid (:block/uuid (db/entity (:db/id block))) + uuid-changed? (not= (:block/uuid block) original-uuid) + block' (-> (wrap-parse-block block) + ;; :block/uuid might be changed when backspace/delete + ;; a block that has been refed + (assoc :block/uuid (:block/uuid block))) + opts' (merge opts (cond-> {:outliner-op :save-block} + uuid-changed? + (assoc :uuid-changed {:from (:block/uuid block) + :to original-uuid})))] (outliner-tx/transact! opts' (outliner-core/save-block! block')) @@ -760,6 +768,9 @@ (let [original-content (util/trim-safe (:block/content block)) value' (-> (property/remove-built-in-properties format original-content) (drawer/remove-logbook)) + value (->> value + (property/remove-properties format) + (drawer/remove-logbook)) new-value (str value' value) tail-len (count value) pos (max @@ -788,9 +799,9 @@ (let [page-id (:db/id (:block/page (db/entity [:block/uuid block-id]))) page-blocks-count (and page-id (db/get-page-blocks-count repo page-id))] (when (> page-blocks-count 1) - (let [block (db/entity [:block/uuid block-id]) - has-children? (seq (:block/_parent block)) - block (db/pull (:db/id block)) + (let [block-e (db/entity [:block/uuid block-id]) + has-children? (seq (:block/_parent block-e)) + block (db/pull (:db/id block-e)) left (tree/-get-left (outliner-core/block block)) left-has-children? (and left (when-let [block-id (:block/uuid (:data left))] @@ -808,9 +819,15 @@ (assoc :concat-data {:last-edit-block (:block/uuid block)}))] (outliner-tx/transact! transact-opts - (when concat-prev-block? - (save-block! repo prev-block new-content)) - (delete-block-aux! block delete-children?)) + (if concat-prev-block? + (let [prev-block' (if (seq (:block/_refs block-e)) + (assoc prev-block + :block/uuid (:block/uuid block) + :block/additional-properties (:block/properties block)) + prev-block)] + (delete-block-aux! block delete-children?) + (save-block! repo prev-block' new-content)) + (delete-block-aux! block delete-children?))) (move-fn))))))))) (state/set-editor-op! nil))) @@ -1224,10 +1241,7 @@ (let [value (string/trim value)] ;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever ;; maybe we shouldn't save the block/file in "will-unmount" event? - (save-block-if-changed! block value - (merge - {:init-properties (:block/properties block)} - opts)))) + (save-block-if-changed! block value opts))) (defn save-block! ([repo block-or-uuid content] @@ -2611,11 +2625,22 @@ transact-opts {:outliner-op :delete-block :concat-data {:last-edit-block (:block/uuid edit-block) :end? true}} - new-content (str value "" (:block/content next-block)) - repo (state/get-current-repo)] + next-block-has-refs? (some? (:block/_refs (db/entity (:db/id next-block)))) + new-content (if next-block-has-refs? + (str value "" + (->> (:block/content next-block) + (property/remove-properties (:block/format next-block)) + (drawer/remove-logbook))) + (str value "" (:block/content next-block))) + repo (state/get-current-repo) + edit-block' (if next-block-has-refs? + (assoc edit-block + :block/uuid (:block/uuid next-block) + :block/additional-properties (dissoc (:block/properties next-block) :block/uuid)) + edit-block)] (outliner-tx/transact! transact-opts - (save-block! repo edit-block new-content) - (delete-block-aux! next-block false)) + (delete-block-aux! next-block false) + (save-block! repo edit-block' new-content)) (state/set-edit-content! input-id new-content) (cursor/move-cursor-to input current-pos))))) diff --git a/src/main/frontend/modules/file/core.cljs b/src/main/frontend/modules/file/core.cljs index 4162c5e55..f5ee06e58 100644 --- a/src/main/frontend/modules/file/core.cljs +++ b/src/main/frontend/modules/file/core.cljs @@ -32,8 +32,10 @@ content)) (defn transform-content - [{:block/keys [collapsed? format pre-block? unordered content left page parent properties]} level {:keys [heading-to-list?]}] - (let [heading (:heading properties) + [{:block/keys [collapsed? format pre-block? unordered content left page parent properties] :as b} level {:keys [heading-to-list?]}] + (let [block-ref-not-saved? (and (seq (:block/_refs (db/entity (:db/id b)))) + (not (string/includes? content (str (:block/uuid b))))) + heading (:heading properties) markdown? (= :markdown format) content (or content "") pre-block? (or pre-block? @@ -80,7 +82,10 @@ (string/blank? new-content)) "" " ")] - (str prefix sep new-content)))] + (str prefix sep new-content))) + content (if block-ref-not-saved? + (property/insert-property format content :id (str (:block/uuid b))) + content)] content)) diff --git a/src/main/frontend/modules/outliner/core.cljs b/src/main/frontend/modules/outliner/core.cljs index 2fbb9d5fd..ab66d8c06 100644 --- a/src/main/frontend/modules/outliner/core.cljs +++ b/src/main/frontend/modules/outliner/core.cljs @@ -859,6 +859,11 @@ see also `frontend.modules.outliner.transaction/transact!`" nil) +(def ^:private ^:dynamic *transaction-opts* + "Stores transaction opts that are generated by one or more write-operations, + see also `frontend.modules.outliner.transaction/transact!`" + nil) + (defn- op-transact! [fn-var & args] {:pre [(var? fn-var)]} diff --git a/src/main/frontend/modules/outliner/datascript.cljc b/src/main/frontend/modules/outliner/datascript.cljc index 5a5928775..212c5864a 100644 --- a/src/main/frontend/modules/outliner/datascript.cljc +++ b/src/main/frontend/modules/outliner/datascript.cljc @@ -51,6 +51,28 @@ [tx-report] (get-in tx-report [:tempids :db/current-tx]))) +#?(:cljs + (defn update-block-refs + [txs opts] + (if-let [changed (:uuid-changed opts)] + (let [{:keys [from to]} changed + from-e (db/entity [:block/uuid from]) + to-e (db/entity [:block/uuid to]) + from-id (:db/id from-e) + to-id (:db/id to-e) + refs (:block/_refs from-e) + path-refs (:block/_path-refs from-e) + refs-txs (mapcat (fn [ref refs] + (let [id (:db/id ref)] + [[:db/retract id :block/refs from-id] + [:db/add id :block/refs to-id]])) refs) + path-refs-txs (mapcat (fn [ref refs] + (let [id (:db/id ref)] + [[:db/retract id :block/path-refs from-id] + [:db/add id :block/path-refs to-id]])) path-refs)] + (concat txs refs-txs path-refs-txs)) + txs))) + #?(:cljs (defn transact! [txs opts before-editor-cursor] @@ -58,8 +80,10 @@ txs (map (fn [m] (if (map? m) (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor - :block/title :block/body :block/level :block/container :db/other-tx) - m)) txs)] + :block/title :block/body :block/level :block/container :db/other-tx + :block/additional-properties) + m)) txs) + txs (update-block-refs txs opts)] (when (and (seq txs) (not (:skip-transact? opts)) (not (contains? (:file/unlinked-dirs @state/state) diff --git a/src/main/frontend/modules/outliner/transaction.cljc b/src/main/frontend/modules/outliner/transaction.cljc index 5f0ad51f7..e3b39715e 100644 --- a/src/main/frontend/modules/outliner/transaction.cljc +++ b/src/main/frontend/modules/outliner/transaction.cljc @@ -25,22 +25,27 @@ [opts & body] (assert (or (map? opts) (symbol? opts)) (str "opts is not a map or symbol, type: " (type opts) )) `(let [transact-data# frontend.modules.outliner.core/*transaction-data* + transaction-opts# frontend.modules.outliner.core/*transaction-opts* opts# (if transact-data# (assoc ~opts :nested-transaction? true) ~opts) before-editor-cursor# (frontend.state/get-current-edit-block-and-position)] + (when transaction-opts# (conj! transaction-opts# opts#)) (if transact-data# (do ~@body) - (binding [frontend.modules.outliner.core/*transaction-data* (transient [])] + (binding [frontend.modules.outliner.core/*transaction-data* (transient []) + frontend.modules.outliner.core/*transaction-opts* (transient [])] ~@body (let [r# (persistent! frontend.modules.outliner.core/*transaction-data*) tx# (mapcat :tx-data r#) ;; FIXME: should we merge all the tx-meta? tx-meta# (first (map :tx-meta r#)) all-tx# (concat tx# (:additional-tx opts#)) - opts## (merge (dissoc opts# :additional-tx) tx-meta#)] + o# (persistent! frontend.modules.outliner.core/*transaction-opts*) + full-opts# (apply merge (reverse o#)) + opts## (merge (dissoc full-opts# :additional-tx :current-block :nested-transaction?) tx-meta#)] (when (seq all-tx#) ;; If it's empty, do nothing - (when-not (:nested-transaction? opts#) ; transact only for the whole transaction + (when-not (:nested-transaction? opts##) ; transact only for the whole transaction (let [result# (frontend.modules.outliner.datascript/transact! all-tx# opts## before-editor-cursor#)] {:tx-report result# :tx-data all-tx#