Просмотр исходного кода

fix(sync): preserve apply-template block uuids on redo

Tienson Qin 15 часов назад
Родитель
Сommit
3b1b320977

+ 20 - 1
deps/outliner/src/logseq/outliner/op/construct.cljc

@@ -390,6 +390,24 @@
        distinct
        vec))
 
+(defn- template-children-blocks-for-history
+  [db template-ref]
+  (when-let [template (d/entity db template-ref)]
+    (let [template-id (:db/id template)
+          template-blocks (some->> (ldb/get-block-and-children db (:block/uuid template)
+                                                               {:include-property-block? true})
+                                   rest
+                                   seq
+                                   vec)]
+      (when (seq template-blocks)
+        (vec
+         (cons (assoc (into {} (first template-blocks))
+                      :db/id (:db/id (first template-blocks))
+                      :logseq.property/used-template template-id)
+               (map (fn [block]
+                      (assoc (into {} block) :db/id (:db/id block)))
+                    (rest template-blocks))))))))
+
 (defn- canonicalize-insert-blocks-op
   [db tx-data args]
   (let [[blocks target-id opts] args
@@ -460,7 +478,8 @@
     (let [[template-id target-id opts] args
           template-ref (stable-entity-ref db template-id)
           target-ref (stable-entity-ref db target-id)
-          template-blocks (:template-blocks opts)
+          template-blocks (or (some-> (:template-blocks opts) seq vec)
+                              (template-children-blocks-for-history db template-ref))
           opts-base (dissoc opts :template-id :outliner-op)
           opts' (if (seq template-blocks)
                   (let [[blocks* _target-ref insert-opts]

+ 26 - 0
deps/outliner/test/logseq/outliner/op_construct_test.cljs

@@ -284,6 +284,32 @@
       (is (= #{[:block/uuid tag-uuid]}
              (get-in inverse-outliner-ops [0 1 2 :template-blocks 0 :block/tags]))))))
 
+(deftest derive-history-outliner-ops-apply-template-captures-template-blocks-when-missing-in-op-test
+  (testing "apply-template forward op should capture concrete template-blocks even if original op omitted them"
+    (let [conn (db-test/create-conn-with-blocks
+                {:pages-and-blocks
+                 [{:page {:block/title "page"}
+                   :blocks [{:block/title "template"
+                             :build/children [{:block/title "template child 1"}
+                                              {:block/title "template child 2"}]}
+                            {:block/title "target"}]}]})
+          template (db-test/find-block-by-content @conn "template")
+          target (db-test/find-block-by-content @conn "target")
+          inserted-child-1-uuid (random-uuid)
+          inserted-child-2-uuid (random-uuid)
+          tx-data [{:e 900001 :a :block/uuid :v inserted-child-1-uuid :added true}
+                   {:e 900002 :a :block/uuid :v inserted-child-2-uuid :added true}]
+          tx-meta {:outliner-op :apply-template
+                   :outliner-ops [[:apply-template [(:db/id template)
+                                                    (:db/id target)
+                                                    {:sibling? true}]]]}
+          {:keys [forward-outliner-ops]}
+          (op-construct/derive-history-outliner-ops @conn @conn tx-data tx-meta)]
+      (is (= :apply-template (ffirst forward-outliner-ops)))
+      (is (= true (get-in forward-outliner-ops [0 1 2 :keep-uuid?])))
+      (is (= [inserted-child-1-uuid inserted-child-2-uuid]
+             (mapv :block/uuid (get-in forward-outliner-ops [0 1 2 :template-blocks])))))))
+
 (deftest derive-history-outliner-ops-builds-delete-page-inverse-for-class-property-and-today-page-test
   (testing "delete-page inverse restores hard-retracted class/property/today pages with stable db/ident"
     (let [today (date-time-util/ms->journal-day (js/Date.))

+ 36 - 0
src/test/frontend/worker/db_sync_test.cljs

@@ -4881,3 +4881,39 @@
                 (is (= "followup" (:block/title followup)))))
             (finally
               (reset! undo-redo/*apply-history-action! prev-apply-action))))))))
+
+(deftest undo-redo-apply-template-without-template-blocks-keeps-followup-insert-target-test
+  (testing "redo after apply-template (without template-blocks in original op) should preserve inserted target uuid"
+    (let [{:keys [template-root-uuid empty-target-uuid seed-conn client-ops-conn]}
+          (setup-rebase-apply-template-repro-state)
+          conn (d/conn-from-db @seed-conn)
+          followup-uuid (random-uuid)
+          prev-apply-action @undo-redo/*apply-history-action!]
+      (with-datascript-conns conn client-ops-conn
+        (fn []
+          (reset! undo-redo/*apply-history-action! sync-apply/apply-history-action!)
+          (try
+            ;; Match editor path: apply-template op only carries target/template ids + opts.
+            (d/transact! conn [[:db/add [:block/uuid empty-target-uuid] :block/title "target"]])
+            (apply-ops! conn
+                        [[:apply-template [(:db/id (d/entity @conn [:block/uuid template-root-uuid]))
+                                           (:db/id (d/entity @conn [:block/uuid empty-target-uuid]))
+                                           {:sibling? true}]]]
+                        local-tx-meta)
+            (let [inserted-three (select-offline-inserted-three conn template-root-uuid)]
+              (is (some? inserted-three))
+              (apply-ops! conn
+                          [[:insert-blocks [[{:block/uuid followup-uuid
+                                              :block/title "followup"}]
+                                            (:db/id inserted-three)
+                                            {:sibling? true
+                                             :keep-uuid? true}]]]
+                          local-tx-meta)
+              (undo-all! test-repo)
+              (is (nil? (d/entity @conn [:block/uuid followup-uuid])))
+              (redo-all! test-repo)
+              (let [followup (d/entity @conn [:block/uuid followup-uuid])]
+                (is (some? followup))
+                (is (= "followup" (:block/title followup)))))
+            (finally
+              (reset! undo-redo/*apply-history-action! prev-apply-action))))))))