mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
fix: stabilize uuid-based outliner ops and undo/redo replay
This commit is contained in:
@@ -664,7 +664,7 @@
|
||||
(let [prev-order (db-order/get-prev-order (db/get-db) nil (:db/id over))]
|
||||
(db-order/gen-key prev-order over-order)))]
|
||||
(db/transact! (state/get-current-repo)
|
||||
[{:db/id (:db/id active)
|
||||
[{:block/uuid (:block/uuid active)
|
||||
:block/order new-order}
|
||||
(outliner-core/block-with-updated-at
|
||||
{:db/id (:db/id block)})]
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
(when (contains? #{:logseq.property/status :logseq.property/priority} (:db/ident property))
|
||||
(state/pub-event! [:init/commands])))
|
||||
|
||||
(defn- ->block-lookup-id
|
||||
[block]
|
||||
[:block/uuid (:block/uuid block)])
|
||||
|
||||
(defn- <upsert-closed-value!
|
||||
"Create new closed value and returns its block UUID."
|
||||
[property item]
|
||||
@@ -64,7 +68,7 @@
|
||||
(if-let [ent (:logseq.property/description property)]
|
||||
(db/transact! (state/get-current-repo)
|
||||
[(outliner-core/block-with-updated-at
|
||||
{:db/id (:db/id ent) :block/title description})]
|
||||
{:block/uuid (:block/uuid ent) :block/title description})]
|
||||
{:outliner-op :save-block})
|
||||
(when-not (string/blank? description)
|
||||
(db-property-handler/set-block-property!
|
||||
@@ -121,8 +125,11 @@
|
||||
(if (= value :no-tag)
|
||||
(toggle-fn)
|
||||
(p/let [result (<create-class-if-not-exists! value)
|
||||
value' (or result value)
|
||||
tx-data [[(if select? :db/add :db/retract) (:db/id property) :logseq.property/classes [:block/uuid value']]]
|
||||
class-uuid (or result value)
|
||||
tx-data [[(if select? :db/add :db/retract)
|
||||
(->block-lookup-id property)
|
||||
:logseq.property/classes
|
||||
[:block/uuid class-uuid]]]
|
||||
_ (db/transact! (state/get-current-repo) tx-data {:outliner-op :update-property})]
|
||||
(when-not multiple-choices? (toggle-fn)))))}]
|
||||
|
||||
@@ -468,10 +475,10 @@
|
||||
(db-order/gen-key prev-order over-order)))]
|
||||
|
||||
(db/transact! (state/get-current-repo)
|
||||
[{:db/id (:db/id active)
|
||||
[{:block/uuid (:block/uuid active)
|
||||
:block/order new-order}
|
||||
(outliner-core/block-with-updated-at
|
||||
{:db/id (:db/id property)})]
|
||||
{:block/uuid (:block/uuid property)})]
|
||||
{:outliner-op :save-block})))})]
|
||||
(shui/dropdown-menu-separator)])
|
||||
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
|
||||
(defn wrap-parse-block
|
||||
[{:block/keys [title level] :as block}]
|
||||
(let [block (or (and (:db/id block) (db/entity (:db/id block))) block)
|
||||
(let [block (or (and (:db/id block) (db/entity (:db/id block)))
|
||||
(and (:block/uuid block) (db/entity [:block/uuid (:block/uuid block)]))
|
||||
block)
|
||||
block (if (nil? title)
|
||||
block
|
||||
(let [ast (mldoc/->edn (string/trim title) :markdown)
|
||||
|
||||
@@ -52,8 +52,9 @@
|
||||
(ldb/built-in? page-entity)
|
||||
(notification/show! "Built-in pages can't be used as tags" :error)
|
||||
:else
|
||||
;; FIXME: should move to worker
|
||||
(let [txs [(db-class/build-new-class (db/get-db)
|
||||
{:db/id (:db/id page-entity)
|
||||
{:block/uuid [:block/uuid (:block/uuid page-entity)]
|
||||
:block/title (:block/title page-entity)
|
||||
:block/created-at (:block/created-at page-entity)})
|
||||
[:db/retract (:db/id page-entity) :block/tags :logseq.class/Page]]]
|
||||
|
||||
@@ -250,8 +250,7 @@
|
||||
|
||||
(defn- save-block-inner!
|
||||
[block value opts]
|
||||
(let [block {:db/id (:db/id block)
|
||||
:block/uuid (:block/uuid block)
|
||||
(let [block {:block/uuid (:block/uuid block)
|
||||
:block/title value}
|
||||
block' (-> (wrap-parse-block block)
|
||||
;; :block/uuid might be changed when backspace/delete
|
||||
@@ -1823,7 +1822,7 @@
|
||||
(content-update-fn (:block/title block))
|
||||
(:block/title block))]
|
||||
(merge (apply dissoc block (conj (if-not keep-uuid? [:block/_refs] [])))
|
||||
{:block/page {:db/id (:db/id page)}
|
||||
{:block/page {:db/id [:block/uuid (:block/uuid page)]}
|
||||
:block/title new-content})))
|
||||
|
||||
(defn- edit-last-block-after-inserted!
|
||||
@@ -1842,11 +1841,12 @@
|
||||
|
||||
(defn- unrecycle-tx-data
|
||||
[root]
|
||||
[[:db/retract (:db/id root) :logseq.property/deleted-at]
|
||||
[:db/retract (:db/id root) :logseq.property/deleted-by-ref]
|
||||
[:db/retract (:db/id root) :logseq.property.recycle/original-parent]
|
||||
[:db/retract (:db/id root) :logseq.property.recycle/original-page]
|
||||
[:db/retract (:db/id root) :logseq.property.recycle/original-order]])
|
||||
(let [root-id [:block/uuid (:block/uuid root)]]
|
||||
[[:db/retract root-id :logseq.property/deleted-at]
|
||||
[:db/retract root-id :logseq.property/deleted-by-ref]
|
||||
[:db/retract root-id :logseq.property.recycle/original-parent]
|
||||
[:db/retract root-id :logseq.property.recycle/original-page]
|
||||
[:db/retract root-id :logseq.property.recycle/original-order]]))
|
||||
|
||||
(defn paste-blocks
|
||||
"Given a vec of blocks, insert them into the target page.
|
||||
|
||||
@@ -21,50 +21,63 @@
|
||||
(conj! *outliner-ops* result)
|
||||
result)
|
||||
|
||||
(defn- ->block-id
|
||||
[block-or-id]
|
||||
(cond
|
||||
(de/entity? block-or-id)
|
||||
(:block/uuid block-or-id)
|
||||
|
||||
(map? block-or-id)
|
||||
(:block/uuid block-or-id)
|
||||
|
||||
:else
|
||||
block-or-id))
|
||||
|
||||
(defn save-block!
|
||||
[block & {:as opts}]
|
||||
(op-transact!
|
||||
(when-let [block' (if (de/entity? block)
|
||||
(assoc (.-kv ^js block) :db/id (:db/id block))
|
||||
(dissoc (.-kv ^js block) :db/id)
|
||||
block)]
|
||||
[:save-block [block' opts]])))
|
||||
|
||||
(defn insert-blocks!
|
||||
[blocks target-block opts]
|
||||
(op-transact!
|
||||
(let [id (:db/id target-block)]
|
||||
(let [id (->block-id target-block)]
|
||||
[:insert-blocks [blocks id opts]])))
|
||||
|
||||
(defn apply-template!
|
||||
[template-id target-block opts]
|
||||
(op-transact!
|
||||
(let [id (:db/id target-block)]
|
||||
[:apply-template [template-id id opts]])))
|
||||
(let [template-id' (->block-id template-id)
|
||||
id (->block-id target-block)]
|
||||
[:apply-template [template-id' id opts]])))
|
||||
|
||||
(defn delete-blocks!
|
||||
[blocks opts]
|
||||
(op-transact!
|
||||
(let [ids (map :db/id blocks)]
|
||||
(let [ids (map ->block-id blocks)]
|
||||
(when (seq ids)
|
||||
[:delete-blocks [ids (current-user-delete-opts opts)]]))))
|
||||
|
||||
(defn move-blocks!
|
||||
[blocks target-block opts]
|
||||
(op-transact!
|
||||
(let [ids (map :db/id blocks)
|
||||
target-id (:db/id target-block)]
|
||||
(let [ids (map ->block-id blocks)
|
||||
target-id (->block-id target-block)]
|
||||
[:move-blocks [ids target-id opts]])))
|
||||
|
||||
(defn move-blocks-up-down!
|
||||
[blocks up?]
|
||||
(op-transact!
|
||||
(let [ids (map :db/id blocks)]
|
||||
(let [ids (map ->block-id blocks)]
|
||||
[:move-blocks-up-down [ids up?]])))
|
||||
|
||||
(defn indent-outdent-blocks!
|
||||
[blocks indent? & {:as opts}]
|
||||
(op-transact!
|
||||
(let [ids (map :db/id blocks)]
|
||||
(let [ids (map ->block-id blocks)]
|
||||
[:indent-outdent-blocks [ids indent? opts]])))
|
||||
|
||||
(defn upsert-property!
|
||||
@@ -75,47 +88,47 @@
|
||||
(defn set-block-property!
|
||||
[block-eid property-id value]
|
||||
(op-transact!
|
||||
[:set-block-property [block-eid property-id value]]))
|
||||
[:set-block-property [(->block-id block-eid) property-id value]]))
|
||||
|
||||
(defn remove-block-property!
|
||||
[block-eid property-id]
|
||||
(op-transact!
|
||||
[:remove-block-property [block-eid property-id]]))
|
||||
[:remove-block-property [(->block-id block-eid) property-id]]))
|
||||
|
||||
(defn delete-property-value!
|
||||
[block-eid property-id property-value]
|
||||
(op-transact!
|
||||
[:delete-property-value [block-eid property-id property-value]]))
|
||||
[:delete-property-value [(->block-id block-eid) property-id property-value]]))
|
||||
|
||||
(defn batch-delete-property-value!
|
||||
[block-eids property-id property-value]
|
||||
(op-transact!
|
||||
[:batch-delete-property-value [block-eids property-id property-value]]))
|
||||
[:batch-delete-property-value [(mapv ->block-id block-eids) property-id property-value]]))
|
||||
|
||||
(defn create-property-text-block!
|
||||
[block-id property-id value opts]
|
||||
(op-transact!
|
||||
[:create-property-text-block [block-id property-id value opts]]))
|
||||
[:create-property-text-block [(->block-id block-id) property-id value opts]]))
|
||||
|
||||
(defn batch-set-property!
|
||||
[block-ids property-id value opts]
|
||||
(op-transact!
|
||||
[:batch-set-property [block-ids property-id value opts]]))
|
||||
[:batch-set-property [(mapv ->block-id block-ids) property-id value opts]]))
|
||||
|
||||
(defn batch-remove-property!
|
||||
[block-ids property-id]
|
||||
(op-transact!
|
||||
[:batch-remove-property [block-ids property-id]]))
|
||||
[:batch-remove-property [(mapv ->block-id block-ids) property-id]]))
|
||||
|
||||
(defn class-add-property!
|
||||
[class-id property-id]
|
||||
(op-transact!
|
||||
[:class-add-property [class-id property-id]]))
|
||||
[:class-add-property [(->block-id class-id) property-id]]))
|
||||
|
||||
(defn class-remove-property!
|
||||
[class-id property-id]
|
||||
(op-transact!
|
||||
[:class-remove-property [class-id property-id]]))
|
||||
[:class-remove-property [(->block-id class-id) property-id]]))
|
||||
|
||||
(defn upsert-closed-value!
|
||||
[property-id closed-value-config]
|
||||
@@ -125,7 +138,7 @@
|
||||
(defn delete-closed-value!
|
||||
[property-id value-block-id]
|
||||
(op-transact!
|
||||
[:delete-closed-value [property-id value-block-id]]))
|
||||
[:delete-closed-value [property-id (->block-id value-block-id)]]))
|
||||
|
||||
(defn add-existing-values-to-closed-values!
|
||||
[property-id values]
|
||||
|
||||
@@ -532,13 +532,31 @@
|
||||
current-sibling
|
||||
(recur (ldb/get-left-sibling sibling))))))
|
||||
|
||||
(defn- rebase-target-ref
|
||||
[target-id]
|
||||
(cond
|
||||
(and (vector? target-id)
|
||||
(= :block/uuid (first target-id))
|
||||
(uuid? (second target-id)))
|
||||
target-id
|
||||
|
||||
(uuid? target-id)
|
||||
[:block/uuid target-id]
|
||||
|
||||
(and (map? target-id) (uuid? (:block/uuid target-id)))
|
||||
[:block/uuid (:block/uuid target-id)]
|
||||
|
||||
:else
|
||||
target-id))
|
||||
|
||||
(defn- rebase-resolve-target-and-sibling
|
||||
[current-db rebase-db-before target-id sibling?]
|
||||
(let [target (d/entity current-db target-id)
|
||||
(let [target-ref (rebase-target-ref target-id)
|
||||
target (d/entity current-db target-ref)
|
||||
target-before (when rebase-db-before
|
||||
(d/entity rebase-db-before target-id))
|
||||
(d/entity rebase-db-before target-ref))
|
||||
parent-before (when rebase-db-before
|
||||
(:block/parent (d/entity rebase-db-before target-id)))]
|
||||
(:block/parent (d/entity rebase-db-before target-ref)))]
|
||||
(cond
|
||||
target
|
||||
[target sibling?]
|
||||
@@ -552,6 +570,39 @@
|
||||
:else
|
||||
nil)))
|
||||
|
||||
(defn- template-parent-ref
|
||||
[parent]
|
||||
(cond
|
||||
(and (vector? parent) (= :block/uuid (first parent)))
|
||||
parent
|
||||
|
||||
(uuid? parent)
|
||||
[:block/uuid parent]
|
||||
|
||||
(and (map? parent) (uuid? (:block/uuid parent)))
|
||||
[:block/uuid (:block/uuid parent)]
|
||||
|
||||
:else
|
||||
parent))
|
||||
|
||||
(defn- sanitize-template-block
|
||||
[current-db rebase-db-before block]
|
||||
(let [m (into {} block)
|
||||
block-id (:db/id m)
|
||||
block-uuid (or (:block/uuid m)
|
||||
(when (number? block-id)
|
||||
(or (some-> rebase-db-before (d/entity block-id) :block/uuid)
|
||||
(some-> (d/entity current-db block-id) :block/uuid)))
|
||||
(when (and (vector? block-id)
|
||||
(= :block/uuid (first block-id))
|
||||
(uuid? (second block-id)))
|
||||
(second block-id)))]
|
||||
(cond-> (-> m
|
||||
(dissoc :db/id :block/order :block/page :block/tx-id)
|
||||
(update :block/parent template-parent-ref))
|
||||
(uuid? block-uuid)
|
||||
(assoc :block/uuid block-uuid))))
|
||||
|
||||
(defn- ^:large-vars/cleanup-todo replay-canonical-outliner-op!
|
||||
[conn [op args] rebase-db-before]
|
||||
(case op
|
||||
@@ -588,12 +639,40 @@
|
||||
(when-not (and template-id' (d/entity @conn template-id') target)
|
||||
(invalid-rebase-op! op {:args args
|
||||
:reason :missing-template-or-target-block}))
|
||||
(outliner-op/apply-ops!
|
||||
conn
|
||||
[[:apply-template [template-id'
|
||||
target-id'
|
||||
(assoc opts :sibling? sibling?)]]]
|
||||
{:gen-undo-ops? false}))
|
||||
(let [template-uuid (:block/uuid (d/entity @conn template-id'))
|
||||
target-uuid (:block/uuid target)]
|
||||
(when-not (and (uuid? template-uuid) (uuid? target-uuid))
|
||||
(invalid-rebase-op! op {:args args
|
||||
:reason :missing-template-or-target-uuid}))
|
||||
(let [replace-empty-target? (:replace-empty-target? opts)
|
||||
template-blocks' (some->> (:template-blocks opts)
|
||||
(map-indexed
|
||||
(fn [idx block]
|
||||
(let [block' (sanitize-template-block @conn rebase-db-before block)
|
||||
block'' (if (and replace-empty-target?
|
||||
(zero? idx)
|
||||
(nil? (:block/uuid block')))
|
||||
;; Keep replace-empty-target replay consistent with
|
||||
;; initial apply-template payload where the first
|
||||
;; block uuid is the target uuid.
|
||||
(assoc block' :block/uuid target-uuid)
|
||||
block')]
|
||||
(when (:block/uuid block'')
|
||||
block''))))
|
||||
(remove nil?)
|
||||
seq
|
||||
vec)
|
||||
opts' (cond-> (-> opts
|
||||
(assoc :sibling? sibling?)
|
||||
(dissoc :template-blocks))
|
||||
template-blocks'
|
||||
(assoc :template-blocks template-blocks'))]
|
||||
(outliner-op/apply-ops!
|
||||
conn
|
||||
[[:apply-template [template-uuid
|
||||
target-uuid
|
||||
opts']]]
|
||||
{:gen-undo-ops? false}))))
|
||||
|
||||
:move-blocks
|
||||
(let [[ids target-id opts] args
|
||||
|
||||
Reference in New Issue
Block a user