support page create/rename/delete in rtc

This commit is contained in:
rcmerci
2023-08-30 22:10:55 +08:00
parent 27ccf55d11
commit 510df28e06
8 changed files with 132 additions and 155 deletions

View File

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

View File

@@ -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}))))
(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])
(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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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