fix: broken block ref when backspace/delete a block

pull/9404/head
Tienson Qin 2023-05-15 14:35:35 +08:00
parent 8a0a41ccb8
commit ea019bcad5
5 changed files with 88 additions and 24 deletions

View File

@ -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)))))

View File

@ -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))

View File

@ -859,6 +859,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)]}

View File

@ -51,6 +51,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]
@ -58,8 +80,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))
(not (contains? (:file/unlinked-dirs @state/state)

View File

@ -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#