Browse Source

fix: broken block ref when backspace/delete a block

Tienson Qin 2 years ago
parent
commit
121bec728f

+ 41 - 16
src/main/frontend/handler/editor.cljs

@@ -353,8 +353,16 @@
         block (apply dissoc block db-schema/retract-attributes)]
     (profile
      "Save block: "
-     (let [block' (wrap-parse-block block)
-           opts' (merge opts {:outliner-op :save-block})]
+     (let [original-uuid (:block/uuid (db/entity (:db/id block)))
+           uuid-changed? (not= (:block/uuid block) original-uuid)
+           block' (-> (wrap-parse-block block)
+                      ;; :block/uuid might be changed when backspace/delete
+                      ;; a block that has been refed
+                      (assoc :block/uuid (:block/uuid block)))
+           opts' (merge opts (cond-> {:outliner-op :save-block}
+                               uuid-changed?
+                               (assoc :uuid-changed {:from (:block/uuid block)
+                                                     :to original-uuid})))]
        (outliner-tx/transact!
         opts'
         (outliner-core/save-block! block'))
@@ -760,6 +768,9 @@
         (let [original-content (util/trim-safe (:block/content block))
               value' (-> (property/remove-built-in-properties format original-content)
                          (drawer/remove-logbook))
+              value (->> value
+                         (property/remove-properties format)
+                         (drawer/remove-logbook))
               new-value (str value' value)
               tail-len (count value)
               pos (max
@@ -788,9 +799,9 @@
        (let [page-id (:db/id (:block/page (db/entity [:block/uuid block-id])))
              page-blocks-count (and page-id (db/get-page-blocks-count repo page-id))]
          (when (> page-blocks-count 1)
-           (let [block (db/entity [:block/uuid block-id])
-                 has-children? (seq (:block/_parent block))
-                 block (db/pull (:db/id block))
+           (let [block-e (db/entity [:block/uuid block-id])
+                 has-children? (seq (:block/_parent block-e))
+                 block (db/pull (:db/id block-e))
                  left (tree/-get-left (outliner-core/block block))
                  left-has-children? (and left
                                          (when-let [block-id (:block/uuid (:data left))]
@@ -808,9 +819,15 @@
                                        (assoc :concat-data
                                               {:last-edit-block (:block/uuid block)}))]
                    (outliner-tx/transact! transact-opts
-                     (when concat-prev-block?
-                       (save-block! repo prev-block new-content))
-                     (delete-block-aux! block delete-children?))
+                     (if concat-prev-block?
+                       (let [prev-block' (if (seq (:block/_refs block-e))
+                                           (assoc prev-block
+                                                  :block/uuid (:block/uuid block)
+                                                  :block/additional-properties (:block/properties block))
+                                           prev-block)]
+                         (delete-block-aux! block delete-children?)
+                         (save-block! repo prev-block' new-content))
+                       (delete-block-aux! block delete-children?)))
                    (move-fn)))))))))
    (state/set-editor-op! nil)))
 
@@ -1224,10 +1241,7 @@
   (let [value (string/trim value)]
     ;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
     ;; maybe we shouldn't save the block/file in "will-unmount" event?
-    (save-block-if-changed! block value
-                            (merge
-                             {:init-properties (:block/properties block)}
-                             opts))))
+    (save-block-if-changed! block value opts)))
 
 (defn save-block!
   ([repo block-or-uuid content]
@@ -2611,11 +2625,22 @@
             transact-opts {:outliner-op :delete-block
                            :concat-data {:last-edit-block (:block/uuid edit-block)
                                          :end? true}}
-            new-content (str value "" (:block/content next-block))
-            repo (state/get-current-repo)]
+            next-block-has-refs? (some? (:block/_refs (db/entity (:db/id next-block))))
+            new-content (if next-block-has-refs?
+                          (str value ""
+                               (->> (:block/content next-block)
+                                    (property/remove-properties (:block/format next-block))
+                                    (drawer/remove-logbook)))
+                          (str value "" (:block/content next-block)))
+            repo (state/get-current-repo)
+            edit-block' (if next-block-has-refs?
+                          (assoc edit-block
+                                 :block/uuid (:block/uuid next-block)
+                                 :block/additional-properties (dissoc (:block/properties next-block) :block/uuid))
+                          edit-block)]
         (outliner-tx/transact! transact-opts
-          (save-block! repo edit-block new-content)
-          (delete-block-aux! next-block false))
+          (delete-block-aux! next-block false)
+          (save-block! repo edit-block' new-content))
 
         (state/set-edit-content! input-id new-content)
         (cursor/move-cursor-to input current-pos)))))

+ 8 - 3
src/main/frontend/modules/file/core.cljs

@@ -32,8 +32,10 @@
     content))
 
 (defn transform-content
-  [{:block/keys [collapsed? format pre-block? unordered content left page parent properties]} level {:keys [heading-to-list?]}]
-  (let [heading (:heading properties)
+  [{:block/keys [collapsed? format pre-block? unordered content left page parent properties] :as b} level {:keys [heading-to-list?]}]
+  (let [block-ref-not-saved? (and (seq (:block/_refs (db/entity (:db/id b))))
+                                  (not (string/includes? content (str (:block/uuid b)))))
+        heading (:heading properties)
         markdown? (= :markdown format)
         content (or content "")
         pre-block? (or pre-block?
@@ -80,7 +82,10 @@
                                     (string/blank? new-content))
                               ""
                               " ")]
-                    (str prefix sep new-content)))]
+                    (str prefix sep new-content)))
+        content (if block-ref-not-saved?
+                  (property/insert-property format content :id (str (:block/uuid b)))
+                  content)]
     content))
 
 

+ 5 - 0
src/main/frontend/modules/outliner/core.cljs

@@ -863,6 +863,11 @@
   see also `frontend.modules.outliner.transaction/transact!`"
   nil)
 
+(def ^:private ^:dynamic *transaction-opts*
+  "Stores transaction opts that are generated by one or more write-operations,
+  see also `frontend.modules.outliner.transaction/transact!`"
+  nil)
+
 (defn- op-transact!
   [fn-var & args]
   {:pre [(var? fn-var)]}

+ 26 - 2
src/main/frontend/modules/outliner/datascript.cljc

@@ -52,6 +52,28 @@
      [tx-report]
      (get-in tx-report [:tempids :db/current-tx])))
 
+#?(:cljs
+   (defn update-block-refs
+     [txs opts]
+     (if-let [changed (:uuid-changed opts)]
+       (let [{:keys [from to]} changed
+             from-e (db/entity [:block/uuid from])
+             to-e (db/entity [:block/uuid to])
+             from-id (:db/id from-e)
+             to-id (:db/id to-e)
+             refs (:block/_refs from-e)
+             path-refs (:block/_path-refs from-e)
+             refs-txs (mapcat (fn [ref refs]
+                             (let [id (:db/id ref)]
+                               [[:db/retract id :block/refs from-id]
+                                [:db/add id :block/refs to-id]])) refs)
+             path-refs-txs (mapcat (fn [ref refs]
+                                     (let [id (:db/id ref)]
+                                       [[:db/retract id :block/path-refs from-id]
+                                        [:db/add id :block/path-refs to-id]])) path-refs)]
+         (concat txs refs-txs path-refs-txs))
+       txs)))
+
 #?(:cljs
    (defn transact!
      [txs opts before-editor-cursor]
@@ -60,8 +82,10 @@
            txs (map (fn [m] (if (map? m)
                               (dissoc m
                                       :block/children :block/meta :block/top? :block/bottom? :block/anchor
-                                      :block/title :block/body :block/level :block/container :db/other-tx)
-                              m)) txs)]
+                                      :block/title :block/body :block/level :block/container :db/other-tx
+                                      :block/additional-properties)
+                              m)) txs)
+           txs (update-block-refs txs opts)]
        (when (and (seq txs)
                   (not (:skip-transact? opts))
                   (if (react/db-graph? repo)

+ 8 - 3
src/main/frontend/modules/outliner/transaction.cljc

@@ -25,22 +25,27 @@
   [opts & body]
   (assert (or (map? opts) (symbol? opts)) (str "opts is not a map or symbol, type: " (type opts) ))
   `(let [transact-data# frontend.modules.outliner.core/*transaction-data*
+         transaction-opts# frontend.modules.outliner.core/*transaction-opts*
          opts# (if transact-data#
                  (assoc ~opts :nested-transaction? true)
                  ~opts)
          before-editor-cursor# (frontend.state/get-current-edit-block-and-position)]
+     (when transaction-opts# (conj! transaction-opts# opts#))
      (if transact-data#
        (do ~@body)
-       (binding [frontend.modules.outliner.core/*transaction-data* (transient [])]
+       (binding [frontend.modules.outliner.core/*transaction-data* (transient [])
+                 frontend.modules.outliner.core/*transaction-opts* (transient [])]
          ~@body
          (let [r# (persistent! frontend.modules.outliner.core/*transaction-data*)
                tx# (mapcat :tx-data r#)
                ;; FIXME: should we merge all the tx-meta?
                tx-meta# (first (map :tx-meta r#))
                all-tx# (concat tx# (:additional-tx opts#))
-               opts## (merge (dissoc opts# :additional-tx) tx-meta#)]
+               o# (persistent! frontend.modules.outliner.core/*transaction-opts*)
+               full-opts# (apply merge (reverse o#))
+               opts## (merge (dissoc full-opts# :additional-tx :current-block :nested-transaction?) tx-meta#)]
            (when (seq all-tx#) ;; If it's empty, do nothing
-             (when-not (:nested-transaction? opts#) ; transact only for the whole transaction
+             (when-not (:nested-transaction? opts##) ; transact only for the whole transaction
                (let [result# (frontend.modules.outliner.datascript/transact! all-tx# opts## before-editor-cursor#)]
                  {:tx-report result#
                   :tx-data all-tx#