ソースを参照

refactor: build tx-data in db-worker instead of the UI thread

So the tx-data will be built on top of the full db instead of the
partial db from the UI thread, it also reduces the risk to use
outdated data since RTC can transact data from the worker.
Tienson Qin 2 年 前
コミット
4e1ca386d4

+ 6 - 31
src/main/frontend/db_worker.cljs

@@ -25,8 +25,7 @@
             [frontend.worker.util :as worker-util]
             [frontend.worker.util :as worker-util]
             [frontend.worker.handler.page.rename :as worker-page-rename]
             [frontend.worker.handler.page.rename :as worker-page-rename]
             [frontend.worker.handler.page :as worker-page]
             [frontend.worker.handler.page :as worker-page]
-            [logseq.outliner.core :as outliner-core]
-            [logseq.outliner.transaction :as outliner-tx]))
+            [frontend.worker.outliner-op :as worker-outliner-op]))
 
 
 (defonce *sqlite worker-state/*sqlite)
 (defonce *sqlite worker-state/*sqlite)
 (defonce *sqlite-conns worker-state/*sqlite-conns)
 (defonce *sqlite-conns worker-state/*sqlite-conns)
@@ -470,36 +469,12 @@
      (let [result (worker-page/delete! repo conn page-name nil {})]
      (let [result (worker-page/delete! repo conn page-name nil {})]
        (bean/->js {:result result}))))
        (bean/->js {:result result}))))
 
 
-  ;; block ops
-
-  (delete-blocks
-   [this repo block-ids opts-str]
-   (when-let [conn (worker-state/get-datascript-conn repo)]
-     (let [block-ids (remove nil? block-ids)]
-       (when (seq block-ids)
-         (let [opts (if opts-str
-                      (edn/read-string opts-str)
-                      {})
-               blocks (map #(d/entity @conn %) block-ids)]
-           (outliner-tx/transact!
-             {:local-tx? true           ; keep this transaction in undo/redo history
-             :outliner-op :delete-blocks
-             :transact-opts {:repo repo
-                             :conn conn}}
-            (outliner-core/delete-blocks! repo conn
-                                          (worker-state/get-date-formatter repo)
-                                          blocks
-                                          opts)))))
-     nil))
-
-  (move-blocks-up-down
-   [this repo block-ids up?]
+  (apply-outliner-ops
+   [this repo ops-str opts-str]
    (when-let [conn (worker-state/get-datascript-conn repo)]
    (when-let [conn (worker-state/get-datascript-conn repo)]
-     (let [block-ids (remove nil? block-ids)]
-       (when (seq block-ids)
-         (let [blocks (map #(d/entity @conn %) block-ids)
-               _ (outliner-core/move-blocks-up-down! repo conn blocks up?)]
-           )))))
+     (let [ops (edn/read-string ops-str)
+           opts (edn/read-string opts-str)]
+       (worker-outliner-op/apply-ops! repo conn ops opts))))
 
 
   (file-writes-finished?
   (file-writes-finished?
    [this repo]
    [this repo]

+ 5 - 6
src/main/frontend/handler/block.cljs

@@ -8,6 +8,7 @@
    [frontend.mobile.haptics :as haptics]
    [frontend.mobile.haptics :as haptics]
    [logseq.outliner.core :as outliner-core]
    [logseq.outliner.core :as outliner-core]
    [frontend.modules.outliner.ui :as ui-outliner-tx]
    [frontend.modules.outliner.ui :as ui-outliner-tx]
+   [frontend.modules.outliner.op :as outliner-op]
    [frontend.state :as state]
    [frontend.state :as state]
    [frontend.util :as util]
    [frontend.util :as util]
    [frontend.util.drawer :as drawer]
    [frontend.util.drawer :as drawer]
@@ -334,12 +335,10 @@
        {:outliner-op :move-blocks
        {:outliner-op :move-blocks
         :real-outliner-op :indent-outdent}
         :real-outliner-op :indent-outdent}
        (when save-current-block (save-current-block))
        (when save-current-block (save-current-block))
-       (outliner-core/indent-outdent-blocks! (state/get-current-repo)
-                                             (db/get-db false)
-                                             (get-top-level-blocks blocks)
-                                             indent?
-                                             {:parent-original (get-first-block-original)
-                                              :logical-outdenting? (state/logical-outdenting?)})))))
+       (outliner-op/indent-outdent-blocks! (get-top-level-blocks blocks)
+                                           indent?
+                                           {:parent-original (get-first-block-original)
+                                            :logical-outdenting? (state/logical-outdenting?)})))))
 
 
 (def *swipe (atom nil))
 (def *swipe (atom nil))
 
 

+ 11 - 13
src/main/frontend/handler/common/page.cljs

@@ -21,7 +21,7 @@
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [datascript.core :as d]
             [datascript.core :as d]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [logseq.outliner.core :as outliner-core]))
+            [frontend.modules.outliner.op :as outliner-op]))
 
 
 (defn build-hidden-page-tx-data
 (defn build-hidden-page-tx-data
   [page-name]
   [page-name]
@@ -126,28 +126,26 @@
 (defn <favorite-page!-v2
 (defn <favorite-page!-v2
   [page-block-uuid]
   [page-block-uuid]
   {:pre [(uuid? page-block-uuid)]}
   {:pre [(uuid? page-block-uuid)]}
-  (let [repo (state/get-current-repo)
-        favorites-page (d/entity (conn/get-db) [:block/name favorites-page-name])
+  (let [favorites-page (d/entity (conn/get-db) [:block/name favorites-page-name])
         favorites-page-tx-data (build-hidden-page-tx-data "favorites")]
         favorites-page-tx-data (build-hidden-page-tx-data "favorites")]
     (when (d/entity (conn/get-db) [:block/uuid page-block-uuid])
     (when (d/entity (conn/get-db) [:block/uuid page-block-uuid])
       (p/do!
       (p/do!
        (when-not favorites-page (ldb/transact! nil [favorites-page-tx-data]))
        (when-not favorites-page (ldb/transact! nil [favorites-page-tx-data]))
        (ui-outliner-tx/transact!
        (ui-outliner-tx/transact!
         {:outliner-op :insert-blocks}
         {:outliner-op :insert-blocks}
-        (outliner-core/insert-blocks! repo (conn/get-db false) [{:block/link [:block/uuid page-block-uuid]
-                                                                 :block/content ""
-                                                                 :block/format :markdown}]
-                                      (d/entity (conn/get-db) [:block/name favorites-page-name])
-                                      {}))))))
+        (outliner-op/insert-blocks! [{:block/link [:block/uuid page-block-uuid]
+                                      :block/content ""
+                                      :block/format :markdown}]
+                                    (d/entity (conn/get-db) [:block/name favorites-page-name])
+                                    {}))))))
 
 
 (defn <unfavorite-page!-v2
 (defn <unfavorite-page!-v2
   [page-block-uuid]
   [page-block-uuid]
   {:pre [(uuid? page-block-uuid)]}
   {:pre [(uuid? page-block-uuid)]}
-  (let [repo (state/get-current-repo)]
-    (when-let [block (find-block-in-favorites-page page-block-uuid)]
-      (ui-outliner-tx/transact!
-       {:outliner-op :delete-blocks}
-       (outliner-core/delete-blocks! repo (conn/get-db false) (state/get-date-formatter) [block] {})))))
+  (when-let [block (find-block-in-favorites-page page-block-uuid)]
+    (ui-outliner-tx/transact!
+     {:outliner-op :delete-blocks}
+     (outliner-op/delete-blocks! [block] {}))))
 
 
 
 
 ;; favorites fns end ================
 ;; favorites fns end ================

+ 3 - 3
src/main/frontend/handler/db_based/editor.cljs

@@ -14,8 +14,8 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
             [frontend.handler.repo-config :as repo-config-handler]
             [frontend.handler.repo-config :as repo-config-handler]
-            [logseq.outliner.core :as outliner-core]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
+            [frontend.modules.outliner.op :as outliner-op]
             [frontend.schema.handler.repo-config :as repo-config-schema]
             [frontend.schema.handler.repo-config :as repo-config-schema]
             [promesa.core :as p]
             [promesa.core :as p]
             [logseq.db.frontend.content :as db-content]))
             [logseq.db.frontend.content :as db-content]))
@@ -152,6 +152,6 @@
   [repo block-ids heading]
   [repo block-ids heading]
   (ui-outliner-tx/transact!
   (ui-outliner-tx/transact!
    {:outliner-op :save-block}
    {:outliner-op :save-block}
-   (doseq [block-tx (keep #(set-heading-aux! % heading) block-ids)]
-     (outliner-core/save-block! repo (db/get-db false) (state/get-date-formatter) block-tx))
+   (doseq [block (keep #(set-heading-aux! % heading) block-ids)]
+     (outliner-op/save-block! block))
    (property-handler/batch-set-block-property! repo block-ids :heading heading)))
    (property-handler/batch-set-block-property! repo block-ids :heading heading)))

+ 6 - 6
src/main/frontend/handler/dnd.cljs

@@ -2,9 +2,10 @@
   "Provides fns for drag and drop"
   "Provides fns for drag and drop"
   (:require [frontend.handler.editor :as editor-handler]
   (:require [frontend.handler.editor :as editor-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
-            [logseq.outliner.core :as outliner-core]
             [logseq.outliner.tree :as otree]
             [logseq.outliner.tree :as otree]
+            [logseq.outliner.core :as outliner-core]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
+            [frontend.modules.outliner.op :as outliner-op]
             [logseq.common.util.block-ref :as block-ref]
             [logseq.common.util.block-ref :as block-ref]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.db :as db]
@@ -12,8 +13,7 @@
 
 
 (defn move-blocks
 (defn move-blocks
   [^js event blocks target-block original-block move-to]
   [^js event blocks target-block original-block move-to]
-  (let [repo (state/get-current-repo)
-        blocks' (map #(db/pull (:db/id %)) blocks)
+  (let [blocks' (map #(db/pull (:db/id %)) blocks)
         first-block (first blocks')
         first-block (first blocks')
         top? (= move-to :top)
         top? (= move-to :top)
         nested? (= move-to :nested)
         nested? (= move-to :nested)
@@ -53,10 +53,10 @@
                     (otree/-get-left-id target-node conn))]
                     (otree/-get-left-id target-node conn))]
              (if first-child?
              (if first-child?
                (when-let [parent (otree/-get-parent target-node conn)]
                (when-let [parent (otree/-get-parent target-node conn)]
-                 (outliner-core/move-blocks! repo conn blocks' (:data parent) false))
+                 (outliner-op/move-blocks! blocks' (:data parent) false))
                (when-let [before-node (otree/-get-left target-node conn)]
                (when-let [before-node (otree/-get-left target-node conn)]
-                 (outliner-core/move-blocks! repo conn blocks' (:data before-node) true))))
-           (outliner-core/move-blocks! repo conn blocks' target-block (not nested?)))))
+                 (outliner-op/move-blocks! blocks' (:data before-node) true))))
+           (outliner-op/move-blocks! blocks' target-block (not nested?)))))
 
 
       :else
       :else
       nil)))
       nil)))

+ 49 - 64
src/main/frontend/handler/editor.cljs

@@ -33,6 +33,7 @@
             [frontend.handler.file-based.editor :as file-editor-handler]
             [frontend.handler.file-based.editor :as file-editor-handler]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [logseq.outliner.core :as outliner-core]
             [logseq.outliner.core :as outliner-core]
+            [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.tree :as tree]
             [frontend.modules.outliner.tree :as tree]
             [logseq.outliner.tree :as otree]
             [logseq.outliner.tree :as otree]
@@ -66,7 +67,8 @@
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]
             [rum.core :as rum]
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.db-based.property :as db-property-handler]
-            [frontend.fs.capacitor-fs :as capacitor-fs]))
+            [frontend.fs.capacitor-fs :as capacitor-fs]
+            [clojure.edn :as edn]))
 
 
 ;; FIXME: should support multiple images concurrently uploading
 ;; FIXME: should support multiple images concurrently uploading
 
 
@@ -78,9 +80,7 @@
 
 
 (defn- outliner-save-block!
 (defn- outliner-save-block!
   [block]
   [block]
-  (let [repo (state/get-current-repo)
-        conn (db/get-db false)]
-    (outliner-core/save-block! repo conn (state/get-date-formatter) block)))
+  (outliner-op/save-block! block))
 
 
 (defn get-block-own-order-list-type
 (defn get-block-own-order-list-type
   [block]
   [block]
@@ -345,15 +345,13 @@
 
 
                    :else
                    :else
                    (not has-children?))]
                    (not has-children?))]
-    (p/do!
-     (ui-outliner-tx/transact!
-      {:outliner-op :insert-blocks}
-       (save-current-block! {:current-block current-block})
-       (outliner-core/insert-blocks! (state/get-current-repo) (db/get-db false)
-                                    [new-block] current-block {:sibling? sibling?
-                                                               :keep-uuid? keep-uuid?
-                                                               :ordered-list? ordered-list?
-                                                               :replace-empty-target? replace-empty-target?})))))
+    (ui-outliner-tx/transact!
+     {:outliner-op :insert-blocks}
+     (save-current-block! {:current-block current-block})
+     (outliner-op/insert-blocks! [new-block] current-block {:sibling? sibling?
+                                                            :keep-uuid? keep-uuid?
+                                                            :ordered-list? ordered-list?
+                                                            :replace-empty-target? replace-empty-target?}))))
 
 
 
 
 (defn- block-self-alone-when-insert?
 (defn- block-self-alone-when-insert?
@@ -554,10 +552,10 @@
                                                            :ordered-list? ordered-list?
                                                            :ordered-list? ordered-list?
                                                            :replace-empty-target? replace-empty-target?})
                                                            :replace-empty-target? replace-empty-target?})
              (when edit-block?
              (when edit-block?
-              (if (and replace-empty-target?
-                       (string/blank? (:block/content last-block)))
-                (edit-block! last-block :max nil)
-                (edit-block! new-block :max nil)))
+               (if (and replace-empty-target?
+                        (string/blank? (:block/content last-block)))
+                 (edit-block! last-block :max nil)
+                 (edit-block! new-block :max nil)))
              new-block)))))))
              new-block)))))))
 
 
 (defn insert-first-page-block-if-not-exists!
 (defn insert-first-page-block-if-not-exists!
@@ -687,15 +685,12 @@
     (when block
     (when block
       (let [blocks (block-handler/get-top-level-blocks [block])]
       (let [blocks (block-handler/get-top-level-blocks [block])]
         (state/set-state! :ui/deleting-block uuid)
         (state/set-state! :ui/deleting-block uuid)
-        (p/do!
-         (ui-outliner-tx/transact!
-          {:outliner-op :delete-blocks}
-          (outliner-core/delete-blocks! repo (db/get-db false)
-                                        (state/get-date-formatter)
-                                        blocks
-                                        (merge
-                                         delete-opts
-                                         {:children? children?}))))))))
+        (ui-outliner-tx/transact!
+         {:outliner-op :delete-blocks}
+         (outliner-op/delete-blocks! blocks
+                                     (merge
+                                      delete-opts
+                                      {:children? children?})))))))
 
 
 (defn- move-to-prev-block
 (defn- move-to-prev-block
   [repo sibling-block format _id value]
   [repo sibling-block format _id value]
@@ -846,8 +841,7 @@
       (p/do!
       (p/do!
        (ui-outliner-tx/transact!
        (ui-outliner-tx/transact!
         {:outliner-op :delete-blocks}
         {:outliner-op :delete-blocks}
-        (when-let [^Object worker @state/*db-worker]
-          (.delete-blocks worker repo (clj->js (map :db/id blocks')) nil)))
+         (outliner-op/delete-blocks! blocks' nil))
        (when sibling-block
        (when sibling-block
          (move-to-prev-block repo sibling-block
          (move-to-prev-block repo sibling-block
                              (:block/format block)
                              (:block/format block)
@@ -1740,14 +1734,12 @@
   [up?]
   [up?]
   (fn [event]
   (fn [event]
     (util/stop event)
     (util/stop event)
-    (let [repo (state/get-current-repo)
-          edit-block-id (:block/uuid (state/get-edit-block))
+    (let [edit-block-id (:block/uuid (state/get-edit-block))
           move-nodes (fn [blocks]
           move-nodes (fn [blocks]
                        (let [blocks' (block-handler/get-top-level-blocks blocks)
                        (let [blocks' (block-handler/get-top-level-blocks blocks)
                              result (ui-outliner-tx/transact!
                              result (ui-outliner-tx/transact!
                                      {:outliner-op :move-blocks}
                                      {:outliner-op :move-blocks}
-                                      (when-let [^Object worker @state/*db-worker]
-                                        (.move-blocks-up-down worker repo (clj->js (map :db/id blocks')) up?)))]
+                                      (outliner-op/move-blocks-up-down! blocks' up?))]
                          (when-let [block-node (util/get-first-block-by-id (:block/uuid (first blocks)))]
                          (when-let [block-node (util/get-first-block-by-id (:block/uuid (first blocks)))]
                            (.scrollIntoView block-node #js {:behavior "smooth" :block "nearest"}))
                            (.scrollIntoView block-node #js {:behavior "smooth" :block "nearest"}))
                          result))]
                          result))]
@@ -2046,7 +2038,7 @@
         page (if (:block/name block) block
         page (if (:block/name block) block
                  (when target-block (:block/page (db/entity (:db/id target-block)))))
                  (when target-block (:block/page (db/entity (:db/id target-block)))))
         empty-target? (if (true? skip-empty-target?) false
         empty-target? (if (true? skip-empty-target?) false
-                        (string/blank? (:block/content target-block)))
+                          (string/blank? (:block/content target-block)))
         paste-nested-blocks? (nested-blocks blocks)
         paste-nested-blocks? (nested-blocks blocks)
         target-block-has-children? (db/has-children? (:block/uuid target-block))
         target-block-has-children? (db/has-children? (:block/uuid target-block))
         replace-empty-target? (and empty-target?
         replace-empty-target? (and empty-target?
@@ -2074,23 +2066,21 @@
        {:outliner-op :save-block}
        {:outliner-op :save-block}
        (outliner-save-block! editing-block)))
        (outliner-save-block! editing-block)))
 
 
-    (p/let [*insert-result (atom nil)
-            _ (ui-outliner-tx/transact!
-               {:outliner-op :insert-blocks
-                :additional-tx revert-cut-txs}
-               (when target-block'
-                 (let [format (or (:block/format target-block') (state/get-preferred-format))
-                       repo (state/get-current-repo)
-                       blocks' (map (fn [block]
-                                      (paste-block-cleanup repo block page exclude-properties format content-update-fn keep-uuid?))
-                                    blocks)
-                       result (outliner-core/insert-blocks! repo (db/get-db false) blocks' target-block' {:sibling? sibling?
-                                                                                                          :outliner-op :paste
-                                                                                                          :replace-empty-target? replace-empty-target?
-                                                                                                          :keep-uuid? keep-uuid?})]
-                   (reset! *insert-result result))))]
+    (p/let [result (ui-outliner-tx/transact!
+                    {:outliner-op :insert-blocks
+                     :additional-tx revert-cut-txs}
+                    (when target-block'
+                      (let [format (or (:block/format target-block') (state/get-preferred-format))
+                            repo (state/get-current-repo)
+                            blocks' (map (fn [block]
+                                           (paste-block-cleanup repo block page exclude-properties format content-update-fn keep-uuid?))
+                                         blocks)]
+                        (outliner-op/insert-blocks! blocks' target-block' {:sibling? sibling?
+                                                                           :outliner-op :paste
+                                                                           :replace-empty-target? replace-empty-target?
+                                                                           :keep-uuid? keep-uuid?}))))]
       (state/set-block-op-type! nil)
       (state/set-block-op-type! nil)
-      (when-let [result @*insert-result] (edit-last-block-after-inserted! result)))))
+      (when result (edit-last-block-after-inserted! (edn/read-string result))))))
 
 
 (defn- block-tree->blocks
 (defn- block-tree->blocks
   "keep-uuid? - maintain the existing :uuid in tree vec"
   "keep-uuid? - maintain the existing :uuid in tree vec"
@@ -2190,18 +2180,14 @@
                                :else
                                :else
                                true)]
                                true)]
                (try
                (try
-                 (let [*result (atom nil)]
-                   (p/do!
-                    (ui-outliner-tx/transact!
-                     {:outliner-op :insert-blocks
-                      :created-from-journal-template? journal?}
-                     (when-not (string/blank? (state/get-edit-content))
-                       (save-current-block!))
-                     (let [result (outliner-core/insert-blocks! repo (db/get-db false) blocks'
-                                                                target
-                                                                (assoc opts :sibling? sibling?'))]
-                       (reset! *result result)))
-                    (some-> @*result edit-last-block-after-inserted!)))
+                 (p/let [result (ui-outliner-tx/transact!
+                                 {:outliner-op :insert-blocks
+                                  :created-from-journal-template? journal?}
+                                 (when-not (string/blank? (state/get-edit-content))
+                                   (save-current-block!))
+                                 (outliner-op/insert-blocks! blocks' target
+                                                             (assoc opts :sibling? sibling?')))]
+                   (when result (edit-last-block-after-inserted! (edn/read-string result))))
 
 
                  (catch :default ^js/Error e
                  (catch :default ^js/Error e
                    (notification/show!
                    (notification/show!
@@ -2266,9 +2252,8 @@
        :real-outliner-op :indent-outdent}
        :real-outliner-op :indent-outdent}
       (save-current-block!)
       (save-current-block!)
       (when target
       (when target
-        (outliner-core/move-blocks! (state/get-current-repo) (db/get-db false)
-                                    (block-handler/get-top-level-blocks [block])
-                                    target true)))
+        (outliner-op/move-blocks! (block-handler/get-top-level-blocks [block])
+                                  target true)))
      (when original-block
      (when original-block
        (util/schedule #(edit-block! block pos nil))))))
        (util/schedule #(edit-block! block pos nil))))))
 
 

+ 2 - 4
src/main/frontend/handler/file_based/editor.cljs

@@ -6,8 +6,8 @@
             [frontend.format.block :as block]
             [frontend.format.block :as block]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.format.mldoc :as mldoc]
             [frontend.format.mldoc :as mldoc]
-            [logseq.outliner.core :as outliner-core]
             [frontend.state :as state]
             [frontend.state :as state]
+            [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.util.clock :as clock]
             [frontend.util.clock :as clock]
@@ -195,9 +195,7 @@
    {:outliner-op :save-block}
    {:outliner-op :save-block}
    (doseq [block-id block-ids]
    (doseq [block-id block-ids]
      (when-let [block (set-heading-aux! block-id heading)]
      (when-let [block (set-heading-aux! block-id heading)]
-       (outliner-core/save-block! (state/get-current-repo) (db/get-db false)
-                                  (state/get-date-formatter)
-                                  block)))))
+       (outliner-op/save-block! block)))))
 
 
 (defn set-blocks-id!
 (defn set-blocks-id!
   "Persist block uuid to file if the uuid is valid, and it's not persisted in file.
   "Persist block uuid to file if the uuid is valid, and it's not persisted in file.

+ 2 - 2
src/main/frontend/handler/file_based/page_property.cljs

@@ -2,7 +2,7 @@
   "Page property fns for file graphs"
   "Page property fns for file graphs"
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.outliner.core :as outliner-core]
+            [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]))
             [frontend.util :as util]))
@@ -85,4 +85,4 @@
             (ui-outliner-tx/transact!
             (ui-outliner-tx/transact!
              {:outliner-op :insert-blocks
              {:outliner-op :insert-blocks
               :additional-tx page-properties-tx}
               :additional-tx page-properties-tx}
-             (outliner-core/insert-blocks! repo (db/get-db false) block page {:sibling? false}))))))))
+             (outliner-op/insert-blocks! block page {:sibling? false}))))))))

+ 2 - 4
src/main/frontend/handler/file_based/property.cljs

@@ -3,7 +3,7 @@
   (:require [frontend.db :as db]
   (:require [frontend.db :as db]
             [frontend.handler.block :as block-handler]
             [frontend.handler.block :as block-handler]
             [frontend.handler.file-based.property.util :as property-util]
             [frontend.handler.file-based.property.util :as property-util]
-            [logseq.outliner.core :as outliner-core]
+            [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
             [frontend.state :as state]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
@@ -55,9 +55,7 @@
                          :block/properties-order property-ks
                          :block/properties-order property-ks
                          :block/properties-text-values properties-text-values
                          :block/properties-text-values properties-text-values
                          :block/content content}]
                          :block/content content}]
-              (outliner-core/save-block! (state/get-current-repo) (db/get-db false)
-                                         (state/get-date-formatter)
-                                         block))))))
+              (outliner-op/save-block! block))))))
      (let [block-id (ffirst col)
      (let [block-id (ffirst col)
            block-id (if (string? block-id) (uuid block-id) block-id)
            block-id (if (string? block-id) (uuid block-id) block-id)
            input-pos (or (state/get-edit-pos) :max)]
            input-pos (or (state/get-edit-pos) :max)]

+ 8 - 10
src/main/frontend/handler/page.cljs

@@ -41,7 +41,7 @@
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [logseq.outliner.core :as outliner-core]))
+            [frontend.modules.outliner.op :as outliner-op]))
 
 
 (def create! page-common-handler/create!)
 (def create! page-common-handler/create!)
 (def <create! page-common-handler/<create!)
 (def <create! page-common-handler/<create!)
@@ -139,8 +139,7 @@
 
 
 (defn <reorder-favorites!
 (defn <reorder-favorites!
   [favorites]
   [favorites]
-  (let [repo (state/get-current-repo)
-        conn (conn/get-db false)]
+  (let [conn (conn/get-db false)]
     (when-let [favorites-page-entity (d/entity @conn [:block/name page-common-handler/favorites-page-name])]
     (when-let [favorites-page-entity (d/entity @conn [:block/name page-common-handler/favorites-page-name])]
       (let [favorite-page-block-db-id-coll
       (let [favorite-page-block-db-id-coll
             (keep (fn [page-name]
             (keep (fn [page-name]
@@ -150,13 +149,12 @@
             current-blocks (ldb/sort-by-left (ldb/get-page-blocks @conn page-common-handler/favorites-page-name {})
             current-blocks (ldb/sort-by-left (ldb/get-page-blocks @conn page-common-handler/favorites-page-name {})
                                              favorites-page-entity)]
                                              favorites-page-entity)]
         (p/do!
         (p/do!
-          (ui-outliner-tx/transact!
-              {}
-              (doseq [[page-block-db-id block] (zipmap favorite-page-block-db-id-coll current-blocks)]
-                (when (not= page-block-db-id (:db/id (:block/link block)))
-                  (outliner-core/save-block! repo conn (state/get-date-formatter)
-                                             (assoc block :block/link page-block-db-id)))))
-          (state/update-favorites-updated!))))))
+         (ui-outliner-tx/transact!
+          {}
+          (doseq [[page-block-db-id block] (zipmap favorite-page-block-db-id-coll current-blocks)]
+            (when (not= page-block-db-id (:db/id (:block/link block)))
+              (outliner-op/save-block! (assoc block :block/link page-block-db-id)))))
+         (state/update-favorites-updated!))))))
 
 
 (defn has-more-journals?
 (defn has-more-journals?
   []
   []

+ 69 - 0
src/main/frontend/modules/outliner/op.cljs

@@ -0,0 +1,69 @@
+(ns frontend.modules.outliner.op
+  "Build outliner ops")
+
+(def ^:private ^:dynamic *outliner-ops*
+  "Stores outliner ops that are generated by the following calls"
+  nil)
+
+(defn- op-transact!
+  [fn-var & args]
+  {:pre [(var? fn-var)]}
+  (when (nil? *outliner-ops*)
+    (throw (js/Error. (str (:name (meta fn-var)) " is not used in (transact! ...)"))))
+  (let [result (apply @fn-var args)]
+    (conj! *outliner-ops* result)
+    result))
+
+(defn save-block
+  [block]
+  [:save-block [block]])
+
+(defn insert-blocks
+  [blocks target-block opts]
+  (let [id (:db/id target-block)]
+    [:insert-blocks [blocks id opts]]))
+
+(defn delete-blocks
+  [blocks opts]
+  (let [ids (map :db/id blocks)]
+    [:delete-blocks [ids opts]]))
+
+(defn move-blocks
+  [blocks target-block sibling?]
+  (let [ids (map :db/id blocks)
+        target-id (:db/id target-block)]
+    [:move-blocks [ids target-id sibling?]]))
+
+(defn move-blocks-up-down
+  [blocks up?]
+  (let [ids (map :db/id blocks)]
+    [:move-blocks-up-down [ids up?]]))
+
+(defn indent-outdent-blocks
+  [blocks indent? & {:as opts}]
+  (let [ids (map :db/id blocks)]
+    [:indent-outdent-blocks [ids indent? opts]]))
+
+(defn save-block!
+  [block]
+  (op-transact! #'save-block block))
+
+(defn insert-blocks!
+  [blocks target-block opts]
+  (op-transact! #'insert-blocks blocks target-block opts))
+
+(defn delete-blocks!
+  [blocks opts]
+  (op-transact! #'delete-blocks blocks opts))
+
+(defn move-blocks!
+  [blocks target-block sibling?]
+  (op-transact! #'move-blocks blocks target-block sibling?))
+
+(defn move-blocks-up-down!
+  [blocks up?]
+  (op-transact! #'move-blocks-up-down blocks up?))
+
+(defn indent-outdent-blocks!
+  [blocks indent? & {:as opts}]
+  (op-transact! #'indent-outdent-blocks blocks indent? opts))

+ 11 - 20
src/main/frontend/modules/outliner/ui.cljc

@@ -1,28 +1,19 @@
 (ns frontend.modules.outliner.ui
 (ns frontend.modules.outliner.ui
-  #?(:cljs (:require-macros [logseq.outliner.transaction]))
   #?(:cljs (:require-macros [frontend.modules.outliner.ui]))
   #?(:cljs (:require-macros [frontend.modules.outliner.ui]))
   #?(:cljs (:require [frontend.state :as state]
   #?(:cljs (:require [frontend.state :as state]
-                     [frontend.config :as config]
                      [frontend.db :as db])))
                      [frontend.db :as db])))
 
 
-#?(:cljs
-   (do
-     (defn unlinked-graph?
-       []
-       (let [repo (state/get-current-repo)]
-         (contains? (:file/unlinked-dirs @state/state)
-                    (config/get-repo-dir repo))))
-
-     (def set-state-fn state/set-state!)))
-
 (defmacro transact!
 (defmacro transact!
   [opts & body]
   [opts & body]
   `(when (db/request-finished?)
   `(when (db/request-finished?)
-     (let [transact-opts# {:repo (state/get-current-repo)
-                           :conn (db/get-db false)
-                           :unlinked-graph? frontend.modules.outliner.ui/unlinked-graph?
-                           :set-state-fn frontend.modules.outliner.ui/set-state-fn}]
-       (when (nil? @(:history/tx-before-editor-cursor @state/state))
-         (state/set-state! :history/tx-before-editor-cursor (state/get-current-edit-block-and-position)))
-       (logseq.outliner.transaction/transact! (assoc ~opts :transact-opts transact-opts#)
-                                              ~@body))))
+     (when (nil? @(:history/tx-before-editor-cursor @state/state))
+       (state/set-state! :history/tx-before-editor-cursor (state/get-current-edit-block-and-position)))
+     (let [ops# frontend.modules.outliner.op/*outliner-ops*]
+       (if ops#
+         (do ~@body)                    ; nested transact!
+         (binding [frontend.modules.outliner.op/*outliner-ops* (transient [])]
+           ~@body
+           (let [r# (persistent! frontend.modules.outliner.op/*outliner-ops*)
+                 worker# @state/*db-worker]
+             (when (and worker# (seq r#))
+               (.apply-outliner-ops ^Object worker# (state/get-current-repo) (pr-str r#) (pr-str ~opts)))))))))

+ 53 - 0
src/main/frontend/worker/outliner_op.cljs

@@ -0,0 +1,53 @@
+(ns frontend.worker.outliner-op
+  "Transact outliner ops from UI"
+  (:require [logseq.outliner.transaction :as outliner-tx]
+            [logseq.outliner.core :as outliner-core]
+            [frontend.worker.state :as worker-state]
+            [datascript.core :as d]
+            [promesa.core :as p]))
+
+(defn apply-ops!
+  [repo conn ops opts]
+  (let [opts' (assoc opts
+                     :transact-opts {:repo repo :conn conn}
+                     :local-tx? true)
+        date-formatter (worker-state/get-date-formatter repo)
+        *insert-result (atom nil)]
+    (p/do!
+     (outliner-tx/transact!
+      opts'
+      (doseq [[op args] ops]
+        (case op
+          :save-block
+          (apply outliner-core/save-block! repo conn date-formatter args)
+
+          :insert-blocks
+          (let [[blocks target-block-id opts] args]
+            (when-let [target-block (d/entity @conn target-block-id)]
+              (let [result (outliner-core/insert-blocks! repo conn blocks target-block opts)]
+                (reset! *insert-result result))))
+
+          :delete-blocks
+          (let [[block-ids opts] args
+                blocks (keep #(d/entity @conn %) block-ids)]
+            (outliner-core/delete-blocks! repo conn date-formatter blocks opts))
+
+          :move-blocks
+          (let [[block-ids target-block-id sibling?] args
+                blocks (keep #(d/entity @conn %) block-ids)
+                target-block (d/entity @conn target-block-id)]
+            (when (and target-block (seq blocks))
+              (outliner-core/move-blocks! repo conn blocks target-block sibling?)))
+
+          :move-blocks-up-down
+          (let [[block-ids up?] args
+                blocks (keep #(d/entity @conn %) block-ids)]
+            (when (seq blocks)
+              (outliner-core/move-blocks-up-down! repo conn blocks up?)))
+
+          :indent-outdent-blocks
+          (let [[block-ids indent? opts] args
+                blocks (keep #(d/entity @conn %) block-ids)]
+            (when (seq blocks)
+              (outliner-core/indent-outdent-blocks! repo conn blocks indent? opts))))))
+     (pr-str @*insert-result))))