diff --git a/deps/outliner/src/logseq/outliner/core.cljs b/deps/outliner/src/logseq/outliner/core.cljs index e5090d3d64..534b495cff 100644 --- a/deps/outliner/src/logseq/outliner/core.cljs +++ b/deps/outliner/src/logseq/outliner/core.cljs @@ -612,7 +612,8 @@ (throw (js/Error. (str "[insert-blocks] illegal lookup: " lookup ", block: " block))))) blocks-tx (build-insert-blocks-tx db target-block blocks uuids get-new-id opts)] {:blocks-tx blocks-tx - :id->new-uuid id->new-uuid})) + :id->new-uuid id->new-uuid + :uuid->new-uuid uuids})) (defn- get-target-block [db blocks target-block {:keys [outliner-op bottom? top? indent? sibling? up? replace-empty-target?]}] @@ -763,11 +764,11 @@ :keep-block-order? keep-block-order? :outliner-op outliner-op :insert-template? insert-template?} - {:keys [id->new-uuid blocks-tx]} (insert-blocks-aux db blocks' target-block insert-opts)] - (if (some (fn [b] (or (nil? (:block/parent b)) (nil? (:block/order b)))) blocks-tx) - (throw (ex-info "Invalid outliner data" - {:opts insert-opts - :tx (vec blocks-tx) + {:keys [id->new-uuid uuid->new-uuid blocks-tx]} (insert-blocks-aux db blocks' target-block insert-opts)] + (if (some (fn [b] (or (nil? (:block/parent b)) (nil? (:block/order b)))) blocks-tx) + (throw (ex-info "Invalid outliner data" + {:opts insert-opts + :tx (vec blocks-tx) :blocks (vec blocks) :target-block target-block})) (let [tx (assign-temp-id blocks-tx target-block replace-empty-target?) @@ -778,12 +779,14 @@ (remove old-db-id-blocks) (remove nil?) (map (fn [uuid'] {:block/uuid uuid'}))) - from-property (:logseq.property/created-from-property target-block) - many? (= :db.cardinality/many (:db/cardinality from-property)) - property-values-tx (when (and sibling? from-property many?) + from-property (:logseq.property/created-from-property target-block) + many? (= :db.cardinality/many (:db/cardinality from-property)) + property-values-tx (when (and sibling? from-property many?) (let [top-level-blocks (filter #(= 1 (:block/level %)) blocks')] (mapcat (fn [block] - (when-let [new-id (or (id->new-uuid (:db/id block)) (:block/uuid block))] + (when-let [new-id (or (id->new-uuid (:db/id block)) + (uuid->new-uuid (:block/uuid block)) + (:block/uuid block))] [{:block/uuid new-id :logseq.property/created-from-property (:db/id from-property)} [:db/add diff --git a/src/test/frontend/modules/outliner/core_test.cljs b/src/test/frontend/modules/outliner/core_test.cljs index 20bf9c60ef..cc816a4845 100644 --- a/src/test/frontend/modules/outliner/core_test.cljs +++ b/src/test/frontend/modules/outliner/core_test.cljs @@ -514,6 +514,58 @@ (is (= 1 (count children))) (is (= "child" (:block/title (get-block (first children))))))))) +(deftest test-paste-property-values-into-empty-property-value-block + (testing "replace-empty-target remaps pasted property value uuids before adding many-property refs" + (transact-tree! [[25]]) + (db/transact! test-db [{:block/uuid 25 + :block/title ""}]) + (let [conn (db/get-db test-db false) + _ (db/transact! test-db [{:db/ident :logseq.property/created-from-property + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/one + :db/index true}]) + property-ident :user.property/reproduciblesteps + _ (db/transact! test-db [{:db/ident property-ident + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/many + :logseq.property/type :default}]) + property (d/entity @conn property-ident) + property-ident (:db/ident property) + target-block (get-block 25) + _ (db/transact! test-db [{:db/id (:db/id target-block) + :logseq.property/created-from-property (:db/id property)} + [:db/add (:db/id (:block/parent target-block)) + property-ident + (:db/id target-block)]]) + target-block (get-block 25) + _ (is (some? (:logseq.property/created-from-property target-block))) + copied-blocks [{:block/uuid 101 + :block/title "1" + :block/parent 1} + {:block/uuid 102 + :block/title "2" + :block/parent [:block/uuid 101]} + {:block/uuid 103 + :block/title "3" + :block/parent 1}]] + (outliner-tx/transact! + (transact-opts) + (outliner-core/insert-blocks! conn + copied-blocks + target-block + {:sibling? true + :keep-uuid? true + :outliner-op :paste + :replace-empty-target? true})) + (let [parent (d/entity @conn (:db/id (:block/parent target-block))) + values (get parent property-ident) + titles (set (map :block/title values))] + ;; Old copied uuid should not survive as a dangling property value ref. + (is (nil? (get-block 101))) + ;; Pasted values should be the replaced target and the new sibling top-level block. + (is (= #{"1" "3"} titles)) + (is (contains? (set (map :db/id values)) (:db/id (get-block 25)))))))) + (deftest test-batch-transact (testing "add 4, 5 after 2 and delete 3" (let [tree' [[10 [[2] [3]]]]]