persist edit-operation

pull/10016/head
rcmerci 2023-08-02 22:54:05 +08:00
parent e85557bacd
commit aff2620505
11 changed files with 214 additions and 113 deletions

View File

@ -11,10 +11,10 @@
(let [stmt (sqlite-db/prepare db "CREATE TABLE IF NOT EXISTS rtc_ops (
id INTEGER PRIMARY KEY AUTOINCREMENT,
v TEXT NOT NULL)"
(str db-name))
init-stmt (sqlite-db/prepare db "INSERT INTO rtc_ops (v) VALUES (@v)" (str db-name))]
(str db-name))]
(.run ^object stmt)
(.run ^object init-stmt (clj->js (ds-op->sqlite-op {:local-tx 0})))))
(let [init-stmt (sqlite-db/prepare db "INSERT INTO rtc_ops (v) VALUES (@v)" (str db-name))]
(.run ^object init-stmt (clj->js (ds-op->sqlite-op {:local-tx 0}))))))
(defn init!
[graphs-dir repo]

View File

@ -33,7 +33,8 @@
[logseq.db.sqlite.db :as sqlite-db]
[logseq.db.sqlite.util :as sqlite-util]
[logseq.common.graph :as common-graph]
[promesa.core :as p]))
[promesa.core :as p]
[electron.rtc-handler]))
(defmethod handle :mkdir [_window [_ dir]]
(fs/mkdirSync dir))

View File

@ -4,7 +4,8 @@
[frontend.util :as util]
[frontend.config :as config]
[cljs.core.async :as async :refer [<! >! chan go go-loop offer!
poll! timeout]]))
poll! timeout]]
[electron.ipc :as ipc]))
(def ws-addr config/RTC-WS-URL)
@ -31,3 +32,9 @@
(>! data-from-ws-chan data))))
(set! (.-onclose (:ws @*ws)) (fn [_e] (println :ws-stopped))))
(defn init-rtc-op-db
[repo]
(when (config/db-based-graph? repo)
(ipc/ipc :rtc/init repo)))

View File

@ -1,22 +1,16 @@
(ns frontend.db.rtc.op
(:require [electron.ipc :as ipc]
[malli.core :as m]
[cljs.core.async :as async]
[cljs.core.async.interop :refer [p->c]]
[frontend.components.select :as select]))
[cljs.core.async.interop :refer [p->c]]))
(def op-schema
[:or
[:catn
[:op [:= "move"]]
[:value [:map
[:block-uuid :string]
[:target-uuid :string]
[:sibling? :boolean]]]]
[:value [:map [:block-uuids [:sequential :string]]]]]
[:catn
[:op [:= "remove"]]
[:value [:map
[:block-uuids [:sequential :string]]]]]
[:value [:map [:block-uuids [:sequential :string]]]]]
[:catn
[:op [:= "update"]]
[:value [:map
@ -27,25 +21,23 @@
(defn <move-block-op!
[repo block-uuid target-uuid sibling?]
(let [op ["move" {:block-uuid (str block-uuid)
:target-uuid (str target-uuid)
:sibling? sibling?}]]
(assert (op-validator op) "illegal op")
(p->c (ipc/ipc :rtc/add-ops repo (pr-str op)))))
[repo block-uuids]
(let [op ["move" {:block-uuids (mapv str block-uuids)}]]
(assert (op-validator op) op)
(p->c (ipc/ipc :rtc/add-ops repo (pr-str [op])))))
(defn <remove-blocks-op!
[repo block-uuids]
(let [op ["remove" {:block-uuids (mapv str block-uuids)}]]
(assert (op-validator op) "illegal op")
(p->c (ipc/ipc :rtc/add-ops repo (pr-str op)))))
(p->c (ipc/ipc :rtc/add-ops repo (pr-str [op])))))
(defn <update-block-op!
[repo block-uuid attrs-map]
(let [op ["update" (merge {:block-uuid (str block-uuid)}
(select-keys attrs-map [:content]))]]
(assert (op-validator op) "illegal op")
(p->c (ipc/ipc :rtc/add-ops repo (pr-str op)))))
(p->c (ipc/ipc :rtc/add-ops repo (pr-str [op])))))
(defn <get-ops&local-tx
[repo]

View File

@ -45,6 +45,7 @@
[promesa.core :as p]
[frontend.mobile.core :as mobile]
[frontend.db.listener :as db-listener]
[frontend.db.rtc.core :as rtc-core]
[cljs-bean.core :as bean]))
(defn- set-global-error-notification!
@ -87,6 +88,7 @@
(-> (db-restore/restore-graph! repo)
(p/then
(fn []
(rtc-core/init-rtc-op-db repo)
(db-listener/listen-and-persist! repo)
;; try to load custom css only for current repo
(ui-handler/add-style-if-exists!)

View File

@ -60,9 +60,12 @@
(defn indent-outdent-block!
[block direction]
(let [opts {:outliner-op :move-blocks}]
(let [repo (state/get-current-repo)
opts (cond-> {:outliner-op :move-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact! opts
(outliner-core/indent-outdent-blocks! [block] (= direction :right)))))
(outliner-core/indent-outdent-blocks! [block] (= direction :right)))))
(defn select-block!
[block-uuid]

View File

@ -7,7 +7,8 @@
[frontend.modules.outliner.transaction :as outliner-tx]
[logseq.graph-parser.util.block-ref :as block-ref]
[frontend.state :as state]
[frontend.db :as db]))
[frontend.db :as db]
[frontend.config :as config]))
(defn move-blocks
[^js event blocks target-block move-to]
@ -17,7 +18,8 @@
nested? (= move-to :nested)
alt-key? (and event (.-altKey event))
current-format (:block/format first-block)
target-format (:block/format target-block)]
target-format (:block/format target-block)
repo (state/get-current-repo)]
(cond
;; alt pressed, make a block-ref
(and alt-key? (= (count blocks) 1))
@ -36,22 +38,23 @@
:status :warning
:clear? true}])
(every? map? (conj blocks' target-block))
(let [target-node (outliner-core/block target-block)]
(outliner-tx/transact!
{:outliner-op :move-blocks}
(editor-handler/save-current-block!)
(if top?
(let [first-child?
(= (tree/-get-parent-id target-node)
(tree/-get-left-id target-node))]
(if first-child?
(let [parent (tree/-get-parent target-node)]
(outliner-core/move-blocks! blocks' (:data parent) false))
(let [before-node (tree/-get-left target-node)]
(outliner-core/move-blocks! blocks' (:data before-node) true))))
(outliner-core/move-blocks! blocks' target-block (not nested?)))))
(cond-> {:outliner-op :move-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))
(editor-handler/save-current-block!)
(if top?
(let [first-child?
(= (tree/-get-parent-id target-node)
(tree/-get-left-id target-node))]
(if first-child?
(let [parent (tree/-get-parent target-node)]
(outliner-core/move-blocks! blocks' (:data parent) false))
(let [before-node (tree/-get-left target-node)]
(outliner-core/move-blocks! blocks' (:data before-node) true))))
(outliner-core/move-blocks! blocks' target-block (not nested?)))))
:else
nil)))

View File

@ -248,7 +248,8 @@
[block value opts]
(let [block {:db/id (:db/id block)
:block/uuid (:block/uuid block)
:block/content value}]
:block/content value}
repo (state/get-current-repo)]
(profile
"Save block: "
(let [original-uuid (:block/uuid (db/entity (:db/id block)))
@ -260,7 +261,10 @@
opts' (merge opts (cond-> {:outliner-op :save-block}
uuid-changed?
(assoc :uuid-changed {:from (:block/uuid block)
:to original-uuid})))]
:to original-uuid})
(config/db-based-graph? repo)
(assoc :persist-op? true
:repo repo)))]
(outliner-tx/transact!
opts'
@ -339,9 +343,13 @@
true
:else
(not has-children?))]
(not has-children?))
repo (state/get-current-repo)
opts (cond-> {:outliner-op :insert-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact!
{:outliner-op :insert-blocks}
opts
(save-current-block! {:current-block current-block})
(outliner-core/insert-blocks! [new-block] current-block {:sibling? sibling?
:keep-uuid? keep-uuid?
@ -638,12 +646,17 @@
(when-let [blocks (seq (get-selected-blocks))]
(let [ids (->> (distinct (map #(when-let [id (dom/attr % "blockid")]
(uuid id)) blocks))
(remove nil?))]
(outliner-tx/transact! {:outliner-op :cycle-todos}
(doseq [id ids]
(let [block (db/pull [:block/uuid id])]
(when (not-empty (:block/content block))
(set-marker block))))))))
(remove nil?))
repo (state/get-current-repo)
opts (cond-> {:outliner-op :cycle-todos}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact!
opts
(doseq [id ids]
(let [block (db/pull [:block/uuid id])]
(when (not-empty (:block/content block))
(set-marker block))))))))
(defn cycle-todo!
[]
@ -672,10 +685,13 @@
(defn delete-block-aux!
[{:block/keys [uuid repo] :as _block} children?]
(let [repo (or repo (state/get-current-repo))
block (db/pull repo '[*] [:block/uuid uuid])]
block (db/pull repo '[*] [:block/uuid uuid])
opts (cond-> {:outliner-op :delete-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(when block
(outliner-tx/transact!
{:outliner-op :delete-blocks}
opts
(outliner-core/delete-blocks! [block] {:children? children?})))))
(defn- move-to-prev-block
@ -748,7 +764,9 @@
{:outliner-op :delete-blocks}
concat-prev-block?
(assoc :concat-data
{:last-edit-block (:block/uuid block)}))]
{:last-edit-block (:block/uuid block)})
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact! transact-opts
(if concat-prev-block?
(let [prev-block' (if (seq (:block/_refs block-e))
@ -768,10 +786,13 @@
(let [uuid->dom-block (zipmap block-uuids dom-blocks)
block (first blocks)
block-parent (get uuid->dom-block (:block/uuid block))
sibling-block (when block-parent (util/get-prev-block-non-collapsed-non-embed block-parent))]
sibling-block (when block-parent (util/get-prev-block-non-collapsed-non-embed block-parent))
opts (cond-> {:outliner-op :delete-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(prn {:blocks blocks})
(outliner-tx/transact!
{:outliner-op :delete-blocks}
opts
(outliner-core/delete-blocks! blocks {}))
(when sibling-block
(move-to-prev-block repo sibling-block
@ -1215,10 +1236,14 @@
(defn save-blocks!
[blocks]
(outliner-tx/transact!
{:outliner-op :save-block}
(doseq [[block value] blocks]
(save-block-if-changed! block value))))
(let [repo (state/get-current-repo)
opts (cond-> {:outliner-op :save-block}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact!
opts
(doseq [[block value] blocks]
(save-block-if-changed! block value)))))
(defn save-current-block!
"skip-properties? if set true, when editing block is likely be properties, skip saving"
@ -1652,9 +1677,13 @@
(util/stop event)
(save-current-block!)
(let [edit-block-id (:block/uuid (state/get-edit-block))
repo (state/get-current-repo)
transact-opts (cond-> {:outliner-op :move-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))
move-nodes (fn [blocks]
(outliner-tx/transact!
{:outliner-op :move-blocks}
transact-opts
(outliner-core/move-blocks-up-down! blocks up?))
(when-let [block-node (util/get-first-block-by-id (:block/uuid (first blocks)))]
(.scrollIntoView block-node #js {:behavior "smooth" :block "nearest"})))]
@ -1683,11 +1712,15 @@
(defn on-tab
"`direction` = :left | :right."
[direction]
(let [blocks (get-selected-ordered-blocks)]
(let [blocks (get-selected-ordered-blocks)
repo (state/get-current-repo)
opts (cond-> {:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(when (seq blocks)
(outliner-tx/transact!
{:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
opts
(outliner-core/indent-outdent-blocks! blocks (= direction :right))))))
(defn- get-link [format link label]
@ -1972,12 +2005,16 @@
(when has-unsaved-edits
(outliner-tx/transact!
{:outliner-op :save-block}
(cond-> {:outliner-op :save-block}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))
(outliner-core/save-block! editing-block)))
(outliner-tx/transact!
{:outliner-op :insert-blocks
:additional-tx revert-cut-txs}
(cond-> {:outliner-op :insert-blocks
:additional-tx revert-cut-txs}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))
(when target-block'
(let [format (or (:block/format target-block') (state/get-preferred-format))
blocks' (map (fn [block]
@ -2084,16 +2121,19 @@
false
:else
true)]
true)
transact-opts (cond-> {:outliner-op :insert-blocks
:created-from-journal-template? journal?}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact!
{:outliner-op :insert-blocks
:created-from-journal-template? journal?}
(save-current-block!)
(let [result (outliner-core/insert-blocks! blocks'
target
(assoc opts
:sibling? sibling?'))]
(edit-last-block-after-inserted! result))))))))
transact-opts
(save-current-block!)
(let [result (outliner-core/insert-blocks! blocks'
target
(assoc opts
:sibling? sibling?'))]
(edit-last-block-after-inserted! result))))))))
(defn template-on-chosen-handler
[element-id]
@ -2147,11 +2187,15 @@
(defn outdent-on-enter
[node]
(when-not (parent-is-page? node)
(let [parent-node (tree/-get-parent node)]
(let [parent-node (tree/-get-parent node)
repo (state/get-current-repo)
opts (cond-> {:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(save-current-block!)
(outliner-tx/transact!
{:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
opts
(outliner-core/move-blocks! [(:data node)] (:data parent-node) true)))))
(defn- last-top-level-child?
@ -2409,10 +2453,15 @@
(outdent-on-enter current-node)
:else
(profile
"Insert block"
(outliner-tx/transact! {:outliner-op :insert-blocks}
(insert-new-block! state)))))))))
(let [repo (state/get-current-repo)
opts (cond-> {:outliner-op :insert-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(profile
"Insert block"
(outliner-tx/transact!
opts
(insert-new-block! state))))))))))
(defn- inside-of-single-block
"When we are in a single block wrapper, we should always insert a new line instead of new block"
@ -2578,9 +2627,6 @@
:else
(let [edit-block (state/get-edit-block)
transact-opts {:outliner-op :delete-blocks
:concat-data {:last-edit-block (:block/uuid edit-block)
:end? true}}
next-block-has-refs? (some? (:block/_refs (db/entity (:db/id next-block))))
new-content (if next-block-has-refs?
(str value ""
@ -2593,10 +2639,16 @@
(assoc edit-block
:block/uuid (:block/uuid next-block)
:block.temp/additional-properties (dissoc (:block/properties next-block) :block/uuid))
edit-block)]
(outliner-tx/transact! transact-opts
(delete-block-aux! next-block false)
(save-block! repo edit-block' new-content {:editor/op :delete}))
edit-block)
transact-opts (cond-> {:outliner-op :delete-blocks
:concat-data {:last-edit-block (:block/uuid edit-block)
:end? true}}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(outliner-tx/transact!
transact-opts
(delete-block-aux! next-block false)
(save-block! repo edit-block' new-content {:editor/op :delete}))
(let [block (if next-block-has-refs? next-block edit-block)]
(edit-block! block current-pos (:block/uuid block)))))))
@ -2717,12 +2769,16 @@
(save-current-block!)
(state/set-editor-op! :indent-outdent)
(let [pos (some-> (state/get-input) cursor/pos)
{:keys [block]} (get-state)]
{:keys [block]} (get-state)
repo (state/get-current-repo)
opts (cond-> {:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(when block
(state/set-editor-last-pos! pos)
(outliner-tx/transact!
{:outliner-op :move-blocks
:real-outliner-op :indent-outdent}
opts
(outliner-core/indent-outdent-blocks! [block] indent?)))
(state/set-editor-op! :nil)))
@ -3378,18 +3434,21 @@
repo (state/get-current-repo)
value (boolean value)]
(when repo
(save-current-block!) ;; Save the input contents before collapsing
(outliner-tx/transact! ;; Save the new collapsed state as an undo transaction (if it changed)
{:outliner-op :collapse-expand-blocks}
(let [opts (cond-> {:outliner-op :collapse-expand-blocks}
(config/db-based-graph? repo)
(assoc :persist-op? true :repo repo))]
(save-current-block!) ;; Save the input contents before collapsing
(outliner-tx/transact! ;; Save the new collapsed state as an undo transaction (if it changed)
opts
(doseq [block-id block-ids]
(when-let [block (db/entity [:block/uuid block-id])]
(let [current-value (boolean (:block/collapsed? block))]
(when-not (= current-value value)
(let [block {:block/uuid block-id
:block/collapsed? value}]
(outliner-core/save-block! block)))))))
(doseq [block-id block-ids]
(when-let [block (db/entity [:block/uuid block-id])]
(let [current-value (boolean (:block/collapsed? block))]
(when-not (= current-value value)
(let [block {:block/uuid block-id
:block/collapsed? value}]
(outliner-core/save-block! block)))))))
(doseq [block-id block-ids]
(state/set-collapsed-block! block-id value)))))
(state/set-collapsed-block! block-id value))))))
(defn collapse-block! [block-id]
(when (collapsable? block-id)
@ -3724,10 +3783,10 @@
(if (config/db-based-graph? repo)
(property-handler/batch-set-block-property! repo block-ids :heading heading)
(outliner-tx/transact!
{:outliner-op :save-block}
(doseq [block-id block-ids]
(when-let [block (set-heading-aux! repo block-id heading)]
(outliner-core/save-block! block)))))))
{:outliner-op :save-block}
(doseq [block-id block-ids]
(when-let [block (set-heading-aux! repo block-id heading)]
(outliner-core/save-block! block)))))))
(defn set-heading!
[block-id heading]
@ -3737,7 +3796,6 @@
[block-id]
(set-heading! block-id nil))
(defn batch-remove-heading!
[block-ids]
(batch-set-heading! block-ids nil))

View File

@ -36,7 +36,8 @@
[logseq.common.config :as common-config]
[frontend.db.react :as react]
[frontend.db.listener :as db-listener]
[frontend.modules.outliner.core :as outliner-core]))
[frontend.modules.outliner.core :as outliner-core]
[frontend.db.rtc.core :as rtc-core]))
;; Project settings should be checked in two situations:
;; 1. User changes the config.edn directly in logseq.com (fn: alter-file)
@ -409,6 +410,7 @@
(p/do!
(state/set-db-restoring! true)
(db-restore/restore-graph! repo)
(rtc-core/init-rtc-op-db repo)
(repo-config-handler/restore-repo-config! repo)
(when (config/global-config-enabled?)
(global-config-handler/restore-global-config!))

View File

@ -17,7 +17,9 @@
[cljs.spec.alpha :as s]
[frontend.format.block :as block]
[frontend.handler.file-based.property.util :as property-util]
[frontend.handler.property.util :as pu]))
[frontend.handler.property.util :as pu]
[frontend.db.rtc.op :as rtc-op]
[clojure.core.async :as async]))
(s/def ::block-map (s/keys :opt [:db/id :block/uuid :block/page :block/left :block/parent]))
@ -974,24 +976,52 @@
(defn save-block!
[block]
(let [repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(rtc-op/<update-block-op! repo (:block/uuid block) (select-keys block [:block/content]))))
(op-transact! #'save-block block))
(defn insert-blocks!
[blocks target-block opts]
(op-transact! #'insert-blocks blocks target-block opts))
(let [r (op-transact! #'insert-blocks blocks target-block opts)
repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(async/go
(rtc-op/<move-block-op! repo (keep :block/uuid (:blocks r)))
;; (rtc-op/<update-block-op! repo (:block/uuid))
))
r))
(defn delete-blocks!
[blocks opts]
(let [repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(rtc-op/<remove-blocks-op! repo (keep :block/uuid blocks))))
(op-transact! #'delete-blocks blocks opts))
(defn move-blocks!
[blocks target-block sibling?]
(let [repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(rtc-op/<move-block-op! repo (keep :block/uuid blocks))))
(op-transact! #'move-blocks blocks target-block {:sibling? sibling?}))
(defn move-blocks-up-down!
[blocks up?]
(let [repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(rtc-op/<move-block-op! repo (keep :block/uuid blocks))))
(op-transact! #'move-blocks-up-down blocks up?))
(defn indent-outdent-blocks!
[blocks indent?]
(let [repo (:repo (*transaction-opts* 0))
persist-op? (:persist-op? (*transaction-opts* 0))]
(when (and persist-op? repo)
(rtc-op/<move-block-op! repo (keep :block/uuid blocks))))
(op-transact! #'indent-outdent-blocks blocks indent?))

View File

@ -15,7 +15,9 @@
{:graph \"Which graph will be transacted to\"
:outliner-op \"For example, :save-block, :insert-blocks, etc. \"
:additional-tx \"Additional tx data that can be bundled together
with the body in this macro.\"}
with the body in this macro.\"
:persist-op? \"Boolean, store ops into db (sqlite)\"
:repo \"Needed when :persist-op? is true\"}
`Example`:
(transact! {:graph \"test\"}
(insert-blocks! ...)
@ -23,9 +25,10 @@
(move-blocks! ...)
(delete-blocks! ...))"
[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*
_# (assert (or (map? ~opts) (symbol? ~opts)) (str "opts is not a map or symbol, type: " (type ~opts)))
opts# (if transact-data#
(assoc ~opts :nested-transaction? true)
~opts)
@ -37,7 +40,7 @@
~@body)
(binding [frontend.modules.outliner.core/*transaction-data* (transient [])
frontend.modules.outliner.core/*transaction-opts* (transient [])]
(conj! frontend.modules.outliner.core/*transaction-opts* transaction-opts# opts#)
(conj! frontend.modules.outliner.core/*transaction-opts* opts#)
~@body
(let [r# (persistent! frontend.modules.outliner.core/*transaction-data*)
tx# (mapcat :tx-data r#)