From 5654d7b4cb34d50732dbe2c9df8eb800643fc4d3 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 23 Mar 2026 18:09:22 +0800 Subject: [PATCH] fix: normalize entity id when replay --- src/main/frontend/worker/sync/apply_txs.cljs | 22 +++++--- src/test/frontend/worker/db_sync_test.cljs | 57 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/main/frontend/worker/sync/apply_txs.cljs b/src/main/frontend/worker/sync/apply_txs.cljs index 8487ab3769..df5f29c596 100644 --- a/src/main/frontend/worker/sync/apply_txs.cljs +++ b/src/main/frontend/worker/sync/apply_txs.cljs @@ -602,6 +602,10 @@ v) v))) +(defn- replay-entity-id-coll + [db ids] + (mapv #(or (replay-entity-id-value db %) %) ids)) + (defn- ^:large-vars/cleanup-todo replay-canonical-outliner-op! [conn [op args]] (case op @@ -702,19 +706,22 @@ :batch-set-property (let [[block-ids property-id v opts] args + block-ids' (replay-entity-id-coll @conn block-ids) property (d/entity @conn property-id) _ (when-not (and property - (seq block-ids) - (every? #(some? (d/entity @conn %)) block-ids)) + (seq block-ids') + (every? #(some? (d/entity @conn %)) block-ids')) (invalid-rebase-op! op {:args args :reason :missing-block-or-property})) v' (replay-property-value @conn property-id v)] (when (and (stable-entity-ref-like? v) (nil? v')) (invalid-rebase-op! op {:args args})) - (outliner-property/batch-set-property! conn block-ids property-id v' opts)) + (outliner-property/batch-set-property! conn block-ids' property-id v' opts)) :batch-remove-property - (apply outliner-property/batch-remove-property! conn args) + (let [[block-ids property-id] args + block-ids' (replay-entity-id-coll @conn block-ids)] + (outliner-property/batch-remove-property! conn block-ids' property-id)) :delete-property-value (let [[block-eid property-id property-value] args @@ -730,16 +737,17 @@ :batch-delete-property-value (let [[block-eids property-id property-value] args + block-eids' (replay-entity-id-coll @conn block-eids) property (d/entity @conn property-id) _ (when-not (and property - (seq block-eids) - (every? #(some? (d/entity @conn %)) block-eids)) + (seq block-eids') + (every? #(some? (d/entity @conn %)) block-eids')) (invalid-rebase-op! op {:args args :reason :missing-block-or-property})) property-value' (replay-property-value @conn property-id property-value)] (when (and (stable-entity-ref-like? property-value) (nil? property-value')) (invalid-rebase-op! op {:args args})) - (outliner-property/batch-delete-property-value! conn block-eids property-id property-value')) + (outliner-property/batch-delete-property-value! conn block-eids' property-id property-value')) :create-property-text-block (apply outliner-property/create-property-text-block! conn args) diff --git a/src/test/frontend/worker/db_sync_test.cljs b/src/test/frontend/worker/db_sync_test.cljs index 73aba91265..e0bbddd1c9 100644 --- a/src/test/frontend/worker/db_sync_test.cljs +++ b/src/test/frontend/worker/db_sync_test.cljs @@ -1427,6 +1427,63 @@ (is (= #{"page y"} (set (map :block/name (:user.property/x7 block')))))))))) +(deftest replay-batch-set-property-converts-raw-uuid-ids-to-eids-test + (testing "replay should resolve raw uuid block ids for batch-set-property" + (let [graph {:properties {:heading {:db/ident :logseq.property/heading + :logseq.property/type :number + :db/cardinality :db.cardinality/one}} + :pages-and-blocks + [{:page {:block/title "page 1"} + :blocks [{:block/title "local object"}]}]} + conn (db-test/create-conn-with-blocks graph) + block (db-test/find-block-by-content @conn "local object") + block-ref [:block/uuid (:block/uuid block)]] + (is (some? (#'sync-apply/replay-canonical-outliner-op! + conn + [:batch-set-property [[(:block/uuid block)] + :logseq.property/heading + 2 + nil]]))) + (is (= 2 + (:logseq.property/heading (d/entity @conn block-ref))))))) + +(deftest apply-history-action-redo-replays-batch-set-property-with-raw-uuid-ids-test + (testing "redo should replay batch-set-property when semantic op stores raw uuid block ids" + (let [graph {:properties {:heading {:db/ident :logseq.property/heading + :logseq.property/type :number + :db/cardinality :db.cardinality/one}} + :pages-and-blocks + [{:page {:block/title "page 1"} + :blocks [{:block/title "local object"}]}]} + conn (db-test/create-conn-with-blocks graph) + client-ops-conn (d/create-conn client-op/schema-in-db)] + (with-datascript-conns conn client-ops-conn + (fn [] + (let [block (db-test/find-block-by-content @conn "local object") + block-uuid (:block/uuid block) + block-ref [:block/uuid block-uuid] + action-tx-id (random-uuid)] + (ldb/transact! client-ops-conn + [{:db-sync/tx-id action-tx-id + :db-sync/pending? true + :db-sync/forward-outliner-ops + [[:batch-set-property [[block-uuid] + :logseq.property/heading + 2 + nil]]] + :db-sync/inverse-outliner-ops + [[:batch-remove-property [[block-ref] + :logseq.property/heading]]] + :db-sync/normalized-tx-data [] + :db-sync/reversed-tx-data []}]) + (is (= true + (:applied? (#'sync-apply/apply-history-action! test-repo action-tx-id false {})))) + (is (= 2 + (:logseq.property/heading (d/entity @conn block-ref)))) + (is (= true + (:applied? (#'sync-apply/apply-history-action! test-repo action-tx-id true {})))) + (is (nil? (:logseq.property/heading (d/entity @conn block-ref)))))))))) + (deftest replay-set-block-property-converts-lookup-ref-to-eid-test (testing "replay should resolve stable lookup refs back to entity ids for set-block-property" (let [graph {:properties {:x7 {:logseq.property/type :page