浏览代码

support page create/rename/delete in rtc

rcmerci 2 年之前
父节点
当前提交
510df28e06

+ 2 - 1
src/main/frontend/db/listener.cljs

@@ -73,4 +73,5 @@
     (d/unlisten! conn :persistence)
     (repo-listen-to-tx! repo conn)
     (d/unlisten! conn :gen-ops)
-    (rtc-db-listener/listen-db-to-generate-ops repo conn)))
+    (when (config/db-based-graph? repo)
+      (rtc-db-listener/listen-db-to-generate-ops repo conn))))

+ 103 - 63
src/main/frontend/db/rtc/core.cljs

@@ -17,7 +17,8 @@
             [clojure.set :as set]
             [frontend.state :as state]
             [frontend.db.rtc.op :as op]
-            [frontend.db.rtc.full-upload-download-graph :as full-upload-download-graph]))
+            [frontend.db.rtc.full-upload-download-graph :as full-upload-download-graph]
+            [frontend.handler.page :as page-handler]))
 
 
 
@@ -65,14 +66,18 @@
        [:parents [:sequential :string]]
        [:left [:maybe :string]]
        [:self :string]
-       [:content {:optional true} :string]]]]]])
+       [:content {:optional true} :string]]
+      [:map
+       [:op [:= "update-page"]]
+       [:self :string]
+       [:page-name :string]]
+      [:map
+       [:op [:= "remove-page"]]
+       [:block-uuid :string]]]]]])
 (def data-from-ws-validator (m/validator data-from-ws-schema))
 
 
 
-;; TODO: don't use outliner-core/delete-blocks loop to remove blocks,
-;;       it is suitable for operations from users(e.g. remove consecutive blocks),
-;;       but blocks in remove-ops are scattered, even maybe from different pages
 (defn apply-remote-remove-ops
   [state remove-ops]
   {:pre [(some? @(:*repo state))]}
@@ -85,12 +90,6 @@
          (outliner-core/delete-blocks! [block] {:children? false}))
         (prn :apply-remote-remove-ops (:block-uuid op))))))
 
-(defn <query-blocks-env
-  [block-uuids]
-  ;; TODO
-  {}
-  )
-
 (defn- insert-or-move-block
   [state block-uuid-str remote-parents remote-left-uuid-str content move?]
   {:pre [(some? @(:*repo state))]}
@@ -102,45 +101,41 @@
           b {:block/uuid (uuid block-uuid-str)}]
       (case [(some? local-parent) (some? local-left)]
         [false true]
-        (prn (:tx-data
-              (outliner-tx/transact!
-               {:persist-op? false}
-               (if move?
-                 (do (outliner-core/move-blocks! [b] local-left true)
-                     (when (and content (not= (:block/content b) content))
-                       (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
-                                                         :block/content content))))
-                 (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content :block/format :markdown}]
-                                               local-left {:sibling? true :keep-uuid? true})))))
+        (outliner-tx/transact!
+         {:persist-op? false}
+         (if move?
+           (do (outliner-core/move-blocks! [b] local-left true)
+               (when (and content (not= (:block/content b) content))
+                 (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
+                                                   :block/content content))))
+           (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content :block/format :markdown}]
+                                         local-left {:sibling? true :keep-uuid? true})))
 
         [true true]
         (let [sibling? (not= (:block/uuid local-parent) (:block/uuid local-left))]
-          (prn (:tx-data
-                (outliner-tx/transact!
-                 {:persist-op? false}
-                 (if move?
-                   (do (outliner-core/move-blocks! [b] local-left sibling?)
-                       (when (and content (not= (:block/content b) content))
-                         (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
-                                                           :block/content content))))
-                   (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content
-                                                   :block/format :markdown}]
-                                                 local-left {:sibling? sibling? :keep-uuid? true}))))))
+          (outliner-tx/transact!
+           {:persist-op? false}
+           (if move?
+             (do (outliner-core/move-blocks! [b] local-left sibling?)
+                 (when (and content (not= (:block/content b) content))
+                   (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
+                                                     :block/content content))))
+             (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content
+                                             :block/format :markdown}]
+                                           local-left {:sibling? sibling? :keep-uuid? true}))))
 
         [true false]
-        (prn (:tx-data
-              (outliner-tx/transact!
-               {:persist-op? false}
-               (if move?
-                 (do (outliner-core/move-blocks! [b] local-parent false)
-                     (when (and content (not= (:block/content b) content))
-                       (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
-                                                         :block/content content))))
-                 (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content
-                                                 :block/format :markdown}]
-                                               local-parent {:sibling? false :keep-uuid? true}))))
-
-             [false false])
+        (outliner-tx/transact!
+         {:persist-op? false}
+         (if move?
+           (do (outliner-core/move-blocks! [b] local-parent false)
+               (when (and content (not= (:block/content b) content))
+                 (outliner-core/save-block! (assoc (db/pull repo '[*] [:block/uuid (uuid block-uuid-str)])
+                                                   :block/content content))))
+           (outliner-core/insert-blocks! [{:block/uuid (uuid block-uuid-str) :block/content content
+                                           :block/format :markdown}]
+                                         local-parent {:sibling? false :keep-uuid? true})))
+
         (throw (ex-info "Don't know where to insert" {:block-uuid block-uuid-str :remote-parents remote-parents
                                                       :remote-left remote-left-uuid-str}))))))
 
@@ -212,17 +207,33 @@
           (insert-or-move-block state self parents left content true)
           nil
           (when content
-            (prn (:tx-data
-                  (outliner-tx/transact!
-                   {:persist-op? false}
-                   (outliner-core/save-block! (merge (db/pull repo '[*] [:block/uuid (uuid self)])
-                                                     {:block/uuid (uuid self)
-                                                      :block/content content
-                                                      :block/format :markdown})))))))
+            (outliner-tx/transact!
+             {:persist-op? false}
+             (outliner-core/save-block! (merge (db/pull repo '[*] [:block/uuid (uuid self)])
+                                               {:block/uuid (uuid self)
+                                                :block/content content
+                                                :block/format :markdown})))))
 
         (prn :apply-remote-update-ops r self)))))
 
-
+(defn apply-remote-update-page-ops
+  [state update-page-ops]
+  {:pre [(some? @(:*repo state))]}
+  (let [repo @(:*repo state)]
+    (doseq [{:keys [self page-name]} update-page-ops]
+      (if-let [old-page-name (:block/name (db/entity repo [:block/uuid (uuid self)]))]
+        (when (not= old-page-name page-name)
+          (page-handler/rename! old-page-name page-name false false))
+        (page-handler/create! page-name {:redirect? false :create-first-block? false
+                                         :uuid (uuid self) :persist-op? false})))))
+
+(defn apply-remote-remove-page-ops
+  [state remove-page-ops]
+  {:pre [(some? @(:*repo state))]}
+  (let [repo @(:*repo state)]
+    (doseq [op remove-page-ops]
+      (when-let [page-name (:block/name (db/entity repo [:block/uuid (uuid (:block-uuid op))]))]
+        (page-handler/delete! page-name nil {:redirect-to-home? false :persist-op? false})))))
 
 
 (defn <apply-remote-data
@@ -232,19 +243,26 @@
   (go
     (let [affected-blocks-map (update-keys (:affected-blocks data-from-ws) name)
           remote-t (:t data-from-ws)
-          {remove-ops-map "remove" move-ops-map "move" update-ops-map "update-attrs"}
+          {remove-ops-map "remove" move-ops-map "move" update-ops-map "update-attrs"
+           update-page-ops-map "update-page" remove-page-ops-map "remove-page"}
           (update-vals
            (group-by (fn [[_ env]] (get env :op)) affected-blocks-map)
            (partial into {}))
           remove-ops (vals remove-ops-map)
           sorted-move-ops (move-ops-map->sorted-move-ops move-ops-map)
-          update-ops (vals update-ops-map)]
+          update-ops (vals update-ops-map)
+          update-page-ops (vals update-page-ops-map)
+          remove-page-ops (vals remove-page-ops-map)]
+      (prn :start-apply-remote-update-page-ops)
+      (apply-remote-update-page-ops state update-page-ops)
       (prn :start-apply-remote-remove-ops)
       (apply-remote-remove-ops state remove-ops)
       (prn :start-apply-remote-move-ops)
       (apply-remote-move-ops state sorted-move-ops)
       (prn :start-apply-remote-update-ops)
       (apply-remote-update-ops state update-ops)
+      (prn :start-apply-remote-remove-page-ops)
+      (apply-remote-remove-page-ops state remove-page-ops)
       (<! (p->c (op/<update-local-tx! @(:*repo state) remote-t))))))
 
 (defn- <push-data-from-ws-handler
@@ -256,28 +274,38 @@
   [state ops]
   {:pre [(some? @(:*repo state))]}
   (let [repo @(:*repo state)
-        [remove-block-uuids-set update-block-uuids-set move-block-uuids-set]
+        [remove-block-uuids-set update-block-uuids-set move-block-uuids-set update-page-uuids-set remove-page-uuids-set]
         (loop [[op & other-ops] ops
                remove-block-uuids #{}
                update-block-uuids #{}
-               move-block-uuids #{}]
+               move-block-uuids #{}
+               update-page-uuids #{}
+               remove-page-uuids #{}]
           (if-not op
-            [remove-block-uuids update-block-uuids move-block-uuids]
+            [remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids]
             (case (first op)
               "move"
               (let [block-uuids (set (:block-uuids (second op)))
                     move-block-uuids (set/union move-block-uuids block-uuids)
                     remove-block-uuids (set/difference remove-block-uuids block-uuids)]
-                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids))
+                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids))
               "remove"
               (let [block-uuids (set (:block-uuids (second op)))
                     move-block-uuids (set/difference move-block-uuids block-uuids)
                     remove-block-uuids (set/union remove-block-uuids block-uuids)]
-                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids))
+                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids))
               "update"
               (let [block-uuid (:block-uuid (second op))
                     update-block-uuids (conj update-block-uuids block-uuid)]
-                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids))
+                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids))
+              "update-page"
+              (let [block-uuid (:block-uuid (second op))
+                    update-page-uuids (conj update-page-uuids block-uuid)]
+                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids))
+              "remove-page"
+              (let [block-uuid (:block-uuid (second op))
+                    remove-page-uuids (conj remove-page-uuids block-uuid)]
+                (recur other-ops remove-block-uuids update-block-uuids move-block-uuids update-page-uuids remove-page-uuids))
               (throw (ex-info "unknown op type" op)))))
         {move-ops "move" remove-ops "remove" _update-ops "update"} (group-by first ops)
         move-block-uuids (->> move-ops
@@ -290,6 +318,8 @@
                                                (let [block-uuids (set (:block-uuids (second op)))]
                                                  (seq (set/intersection remove-block-uuids-set block-uuids))))))
         update-block-uuids (seq update-block-uuids-set)
+        update-page-uuids (seq update-page-uuids-set)
+        remove-page-uuids (seq remove-page-uuids-set)
         move-ops* (keep
                    (fn [block-uuid]
                      (when-let [block (db/entity repo [:block/uuid (uuid block-uuid)])]
@@ -314,8 +344,18 @@
                                          parent-uuid (some-> b :block/parent :block/uuid str)]
                                      ["update" {:block-uuid block-uuid
                                                 :target-uuid left-uuid :sibling? (not= left-uuid parent-uuid)
-                                                :content (:block/content b)}])))))]
-    [remove-ops* move-ops* update-ops*]))
+                                                :content (:block/content b)}])))))
+        update-page-ops* (->> update-page-uuids
+                              (keep (fn [block-uuid]
+                                      (when-let [page-name (:block/name (db/entity repo [:block/uuid (uuid block-uuid)]))]
+                                        ["update-page" {:block-uuid block-uuid
+                                                        :page-name page-name}]))))
+        remove-page-ops* (->> remove-page-uuids
+                              (keep (fn [block-uuid]
+                                      (let [b (db/entity repo [:block/uuid (uuid block-uuid)])]
+                                        (when-not b
+                                          ["remove-page" {:block-uuid block-uuid}])))))]
+    [update-page-ops* remove-ops* move-ops* update-ops* remove-page-ops*]))
 
 
 (defn- <client-op-update-handler

+ 3 - 7
src/main/frontend/db/rtc/db_listener.cljs

@@ -2,10 +2,7 @@
   "listen datascript changes, infer operations from the db tx-report"
   (:require [datascript.core :as d]
             [frontend.db :as db]
-            [frontend.db.rtc.op :as op]
-            [frontend.util :as util]))
-
-
+            [frontend.db.rtc.op :as op]))
 
 (defn- gen-block-ops
   [repo same-entity-datoms]
@@ -30,7 +27,7 @@
             (when-let [block-uuid (:block/uuid (db/entity repo e))]
               (mapv (fn [op]
                       (case op
-                        :move ["move" {:block-uuid (str block-uuid)}]
+                        :move ["move" {:block-uuids [(str block-uuid)]}]
                         :update ["update" {:block-uuid (str block-uuid)}])) ops))))))))
 
 (defn- gen-page-ops
@@ -81,8 +78,7 @@
                             :else
                             (recur others))))]
               (recur (conj ops-coll ops) same-entity-datoms-coll*))))]
-    (prn :ops ops)
-    ))
+    (op/<add-ops! repo ops)))
 
 
 (defn listen-db-to-generate-ops

+ 0 - 18
src/main/frontend/db/rtc/op.cljs

@@ -23,24 +23,6 @@
 
 (def op-validator (m/validator op-schema))
 
-(defn <move-blocks-op!
-  [repo block-uuids]
-  (let [op ["move" {:block-uuids (mapv str block-uuids)}]]
-    (assert (op-validator op) op)
-    (op-store/<add-op! repo op)))
-
-(defn <remove-blocks-op!
-  [repo block-uuids]
-  (let [op ["remove" {:block-uuids (mapv str block-uuids)}]]
-    (assert (op-validator op) "illegal op")
-    (op-store/<add-op! repo op)))
-
-(defn <update-block-op!
-  [repo block-uuid]
-  (let [op ["update" {:block-uuid (str block-uuid)}]]
-    (assert (op-validator op) op)
-    (op-store/<add-op! repo op)))
-
 (defn <add-ops!
   [repo ops]
   (assert (every? op-validator ops) ops)

+ 0 - 22
src/main/frontend/db/rtc/ops_idb_store.cljs

@@ -25,15 +25,6 @@
   {:pre [(some? graph-uuid)]}
   (idb-keyval/set "graph-uuid" graph-uuid (ensure-store repo)))
 
-(defn- <add-op*!
-  [repo op]
-  (let [store (ensure-store repo)]
-    (p/loop [key* (tc/to-long (t/now))]
-      (p/let [old-v (idb-keyval/get key* store)]
-        (if old-v
-          (p/recur (inc key*))
-          (idb-keyval/set key* (clj->js op) store))))))
-
 (defn- <add-ops*!
   [repo ops]
   (let [store (ensure-store repo)
@@ -55,19 +46,6 @@
         (recur))
     (recur)))
 
-(def ^:private add-op-ch (async/chan 100))
-(async/go-loop []
-  (if-let [[repo op] (async/<! add-op-ch)]
-    (do (prn :add-op op)
-        (async/<! (p->c (<add-op*! repo op)))
-        (recur))
-    (recur)))
-
-(defn <add-op!
-  [repo op]
-  (async/go (async/>! add-op-ch [repo op])))
-
-
 (defn <add-ops!
   [repo ops]
   (async/go (async/>! add-ops-ch [repo ops])))

+ 1 - 1
src/main/frontend/handler/editor.cljs

@@ -274,7 +274,7 @@
              (when (and (:block/pre-block? block')
                         (not (string/blank? title))
                         (not= (util/page-name-sanity-lc title) old-page-name))
-               (state/pub-event! [:page/title-property-changed old-page-name title])))
+               (state/pub-event! [:page/title-property-changed old-page-name title true])))
            (js/console.error (str "Title is not a string: " title))))))))
 
 ;; id: block dom id, "ls-block-counter-uuid"

+ 24 - 19
src/main/frontend/handler/page.cljs

@@ -148,17 +148,19 @@
    * :class?              - when true, adds a :block/type 'class'
    * :whiteboard?         - when true, adds a :block/type 'whiteboard'
    * :tags                - tag uuids that are added to :block/tags
+   * :persist-op?         - when true, add an update-page op
    TODO: Add other options"
   ([title]
    (create! title {}))
-  ([title {:keys [redirect? create-first-block? format properties split-namespace? journal? uuid rename?]
+  ([title {:keys [redirect? create-first-block? format properties split-namespace? journal? uuid rename? persist-op?]
            :or   {redirect?           true
                   create-first-block? true
                   rename?             false
                   format              nil
                   properties          nil
                   split-namespace?    true
-                  uuid                nil}
+                  uuid                nil
+                  persist-op?         true}
            :as options}]
    (let [title      (-> (string/trim title)
                         (text/page-ref-un-brackets!)
@@ -203,7 +205,7 @@
                        txs
                        last-txs)]
          (when (seq txs)
-           (db/transact! txs)))
+           (db/transact! repo txs {:persist-op? persist-op?})))
 
        (when create-first-block?
          (when (or
@@ -474,9 +476,10 @@
           tx-data)))))
 
 (defn delete!
-  [page-name ok-handler & {:keys [delete-file? redirect-to-home?]
+  [page-name ok-handler & {:keys [delete-file? redirect-to-home? persist-op?]
                            :or {delete-file? true
-                                redirect-to-home? true}}]
+                                redirect-to-home? true
+                                persist-op? true}}]
   (when redirect-to-home? (route-handler/redirect-to-home!))
   (when page-name
     (when-let [repo (state/get-current-repo)]
@@ -507,7 +510,7 @@
                              nil)
             tx-data (concat truncate-blocks-tx-data delete-page-tx)]
 
-        (db/transact! repo tx-data {:outliner-op :delete-page})
+        (db/transact! repo tx-data {:outliner-op :delete-page :persist-op? persist-op?})
 
         (unfavorite-page! page-name)
 
@@ -747,7 +750,7 @@
                               :path-params {:name to-page-name}})))
 
 (defn db-based-merge-pages!
-  [from-page-name to-page-name]
+  [from-page-name to-page-name persist-op?]
   (when (and (db/page-exists? from-page-name)
              (db/page-exists? to-page-name)
              (not= from-page-name to-page-name))
@@ -778,13 +781,13 @@
                                     (assoc :block/parent {:db/id to-id})))) blocks)
           replace-ref-tx-data (db-replace-ref repo from-page to-page)
           tx-data (concat blocks-tx-data replace-ref-tx-data)]
-      (db/transact! repo tx-data)
+      (db/transact! repo tx-data {:persist-op? persist-op?})
       (rename-update-namespace! from-page
                                 (util/get-page-original-name from-page)
                                 (util/get-page-original-name to-page)))
 
 
-    (delete! from-page-name nil :redirect-to-home? false)
+    (delete! from-page-name nil :redirect-to-home? false :persist-op? persist-op?)
 
     (route-handler/redirect! {:to          :page
                               :push        false
@@ -793,8 +796,8 @@
 ;; FIXME:
 (defn db-based-rename!
   ([old-name new-name]
-   (db-based-rename! old-name new-name true))
-  ([old-name new-name redirect?]
+   (db-based-rename! old-name new-name true true))
+  ([old-name new-name redirect? persist-op?]
    (let [old-name      (string/trim old-name)
          new-name      (string/trim new-name)
          old-page-name (util/page-name-sanity-lc old-name)
@@ -808,18 +811,20 @@
        (cond
          (= old-page-name new-page-name) ; case changed
          (db/transact! [{:db/id (:db/id page-e)
-                         :block/original-name new-name}])
+                         :block/original-name new-name}]
+                       {:persist-op? persist-op?})
 
          (and (not= old-page-name new-page-name)
               (db/entity [:block/name new-page-name])) ; merge page
-         (db-based-merge-pages! old-page-name new-page-name)
+         (db-based-merge-pages! old-page-name new-page-name persist-op?)
 
          :else                          ; rename
          (create! new-page-name
                   {:rename? true
                    :uuid (:block/uuid page-e)
                    :redirect? redirect?
-                   :create-first-block? false}))
+                   :create-first-block? false
+                   :persist-op? persist-op?}))
 
        (when (string/blank? new-name)
          (notification/show! "Please use a valid name, empty name is not allowed!" :error)))
@@ -856,11 +861,11 @@
 
 (defn rename!
   ([old-name new-name] (rename! old-name new-name true))
-  ([old-name new-name redirect?]
-   (let [f (if (config/db-based-graph? (state/get-current-repo))
-             db-based-rename!
-             file-based-rename!)]
-     (f old-name new-name redirect?))))
+  ([old-name new-name redirect?] (rename! old-name new-name redirect? true))
+  ([old-name new-name redirect? persist-op?]
+   (if (config/db-based-graph? (state/get-current-repo))
+     (db-based-rename! old-name new-name redirect? persist-op?)
+     (file-based-rename! old-name new-name redirect?))))
 
 (defn- split-col-by-element
   [col element]

+ 1 - 26
src/main/frontend/modules/outliner/core.cljs

@@ -1096,49 +1096,24 @@
 
 (defn save-block!
   [block]
-  (let [repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<update-block-op! repo (:block/uuid block))))
   (op-transact! #'save-block block))
 
 (defn insert-blocks!
   [blocks target-block opts]
-  (let [r (op-transact! #'insert-blocks blocks target-block (assoc opts :outliner-op :insert-blocks))
-        repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<move-blocks-op! repo (keep :block/uuid (:blocks r))))
-    r))
+  (op-transact! #'insert-blocks blocks target-block (assoc opts :outliner-op :insert-blocks)))
 
 (defn delete-blocks!
   [blocks opts]
-  (let [repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<remove-blocks-op! repo (keep :block/uuid blocks))))
   (op-transact! #'delete-blocks blocks (assoc opts :outliner-op :delete-blocks)))
 
 (defn move-blocks!
   [blocks target-block sibling?]
-  (let [repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<move-blocks-op! repo (keep :block/uuid blocks))))
   (op-transact! #'move-blocks blocks target-block {:sibling? sibling?
                                                    :outliner-op :move-blocks}))
 (defn move-blocks-up-down!
   [blocks up?]
-  (let [repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<move-blocks-op! repo (keep :block/uuid blocks))))
   (op-transact! #'move-blocks-up-down blocks up?))
 
 (defn indent-outdent-blocks!
   [blocks indent?]
-  (let [repo (:repo *transaction-args*)
-        persist-op? (:persist-op? *transaction-args*)]
-    (when (and persist-op? repo)
-      (rtc-op/<move-blocks-op! repo (keep :block/uuid blocks))))
   (op-transact! #'indent-outdent-blocks blocks indent?))