refactor: add page outliner ops

The rule is to avoid calling worker modules from UI, all db
transactions should go through apply-outliner-ops.
experiment/tanstack-table
Tienson Qin 2024-06-18 05:19:11 +08:00
parent 3c9c4b3f79
commit 0bd6c05e69
16 changed files with 146 additions and 119 deletions

View File

@ -94,7 +94,23 @@
[:transact
[:catn
[:op :keyword]
[:args [:tuple ::tx-data ::tx-meta]]]]])
[:args [:tuple ::tx-data ::tx-meta]]]]
;; page ops
[:create-page
[:catn
[:op :keyword]
[:args [:tuple ::title ::option]]]]
[:rename-page
[:catn
[:op :keyword]
[:args [:tuple ::uuid ::title]]]]
[:delete-page
[:catn
[:op :keyword]
[:args [:tuple ::uuid]]]]])
(def ^:private ops-schema
[:schema {:registry {::id int?
@ -110,12 +126,20 @@
::option [:maybe map?]
::blocks [:sequential ::block]
::ids [:sequential ::id]
::uuid uuid?
::title string?
::tx-data [:sequential :any]
::tx-meta [:maybe map?]}}
[:sequential op-schema]])
(def ^:private ops-validator (m/validator ops-schema))
(defonce *op-handlers (atom {}))
(defn register-op-handlers!
[handlers]
(reset! *op-handlers handlers))
(defn ^:large-vars/cleanup-todo apply-ops!
[repo conn ops date-formatter opts]
(assert (ops-validator ops) ops)
@ -206,6 +230,9 @@
(apply outliner-property/add-existing-values-to-closed-values! conn args)
:transact
(apply ldb/transact! conn args))))
(apply ldb/transact! conn args)
(when-let [handler (get @*op-handlers op)]
(reset! *result (handler repo conn args))))))
@*result))

View File

@ -502,33 +502,11 @@
(search/build-page-indice repo @conn)
nil))
;; page ops
(page-search
[this repo q options]
(when-let [conn (worker-state/get-datascript-conn repo)]
(search/page-search repo @conn q (bean/->clj options))))
(page-rename
[this repo page-uuid-str new-name]
(assert (common-util/uuid-string? page-uuid-str))
(when-let [conn (worker-state/get-datascript-conn repo)]
(let [config (worker-state/get-config repo)
f (if (sqlite-util/db-based-graph? repo)
db-worker-page-rename/rename!
file-worker-page-rename/rename!)
result (f repo conn config (uuid page-uuid-str) new-name)]
(bean/->js {:result result}))))
(page-delete
[this repo page-uuid-str]
(assert (common-util/uuid-string? page-uuid-str))
(when-let [conn (worker-state/get-datascript-conn repo)]
(let [error-handler (fn [{:keys [msg]}]
(worker-util/post-message :notification
[[:div [:p msg]] :error]))
result (worker-page/delete! repo conn (uuid page-uuid-str) {:error-handler error-handler})]
(bean/->js {:result result}))))
(apply-outliner-ops
[this repo ops-str opts-str]
(when-let [conn (worker-state/get-datascript-conn repo)]
@ -710,10 +688,40 @@
(p/let [dbs (.listDB this)]
(p/all (map #(.unsafeUnlinkDB this (:name %)) dbs)))))
(defn- rename-page!
[repo conn page-uuid new-name]
(let [config (worker-state/get-config repo)
f (if (sqlite-util/db-based-graph? repo)
db-worker-page-rename/rename!
file-worker-page-rename/rename!)]
(f repo conn config page-uuid new-name)))
(defn- delete-page!
[repo conn page-uuid]
(let [error-handler (fn [{:keys [msg]}]
(worker-util/post-message :notification
[[:div [:p msg]] :error]))]
(worker-page/delete! repo conn page-uuid {:error-handler error-handler})))
(defn- create-page!
[repo conn title options]
(let [config (worker-state/get-config repo)]
(worker-page/create! repo conn config title options)))
(defn- outliner-register-op-handlers!
[]
(outliner-op/register-op-handlers!
{:create-page (fn [repo conn [title options]]
(create-page! repo conn title options))
:rename-page (fn [repo conn [page-uuid new-name]]
(rename-page! repo conn page-uuid new-name))
:delete-page (fn [repo conn [page-uuid]]
(delete-page! repo conn page-uuid))}))
(defn init
"web worker entry"
[]
(let [^js obj (DBWorker.)]
(outliner-register-op-handlers!)
(worker-state/set-worker-object! obj)
(file/<ratelimit-file-writes!)
(js/setInterval #(.postMessage js/self "keepAliveResponse") (* 1000 25))

View File

@ -7,13 +7,11 @@
[frontend.handler.config :as config-handler]
[frontend.handler.route :as route-handler]
[frontend.state :as state]
[frontend.worker.handler.page :as worker-page]
[logseq.common.util :as common-util]
[logseq.common.config :as common-config]
[frontend.handler.ui :as ui-handler]
[frontend.config :as config]
[frontend.fs :as fs]
[goog.object :as gobj]
[promesa.core :as p]
[frontend.handler.block :as block-handler]
[logseq.db :as ldb]
@ -22,32 +20,6 @@
[frontend.modules.outliner.ui :as ui-outliner-tx]
[frontend.modules.outliner.op :as outliner-op]))
(defn create!
"Create page. Has the following options:
* :redirect? - when true, redirect to the created page, otherwise return sanitized page name.
* :create-first-block? - when true, create an empty block if the page is empty.
* :uuid - when set, use this uuid instead of generating a new one.
* :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
"
([title]
(create! title {}))
([title {:keys [redirect?]
:or {redirect? true}
:as options}]
(let [repo (state/get-current-repo)
conn (db/get-db repo false)
config (state/get-config repo)
[_ page-name page-uuid] (worker-page/create! repo conn config title options)]
(when redirect?
(route-handler/redirect-to-page! page-uuid))
(when-let [first-block (ldb/get-first-child (db/get-db) (:db/id (db/get-page page-uuid)))]
(block-handler/edit-block! first-block :max {:container-id :unknown-container}))
page-name)))
(defn <create!
([title]
(<create! title {}))
@ -56,9 +28,10 @@
:as options}]
(p/let [repo (state/get-current-repo)
conn (db/get-db repo false)
config (state/get-config repo)
[p _page-name page-uuid] (worker-page/create! repo conn config title options)
_result p]
result (ui-outliner-tx/transact!
{:outliner-op :create-page}
(outliner-op/create-page! title options))
[_page-name page-uuid] (ldb/read-transit-str result)]
(when redirect?
(route-handler/redirect-to-page! page-uuid))
(let [page (db/get-page (or page-uuid title))]
@ -147,10 +120,11 @@
(assert (or (uuid? page-uuid-or-name) (string? page-uuid-or-name)))
(when-let [page-uuid (or (and (uuid? page-uuid-or-name) page-uuid-or-name)
(:block/uuid (db/get-page page-uuid-or-name)))]
(when-let [^Object worker @state/*db-worker]
(-> (p/let [repo (state/get-current-repo)
res (.page-delete worker repo (str page-uuid))
res' (gobj/get res "result")]
(when @state/*db-worker
(-> (p/let [res (ui-outliner-tx/transact!
{:outliner-op :delete-page}
(outliner-op/delete-page! page-uuid))
res' (ldb/read-transit-str res)]
(if res'
(when ok-handler (ok-handler))
(when error-handler (error-handler))))

View File

@ -36,7 +36,6 @@
[electron.ipc :as ipc]
[frontend.context.i18n :refer [t]]
[frontend.persist-db.browser :as db-browser]
[cljs-bean.core :as bean]
[datascript.core :as d]
[frontend.db.conn :as conn]
[logseq.db :as ldb]
@ -45,7 +44,6 @@
[frontend.modules.outliner.op :as outliner-op]
[frontend.handler.property.util :as pu]))
(def create! page-common-handler/create!)
(def <create! page-common-handler/<create!)
(def <delete! page-common-handler/<delete!)
@ -135,17 +133,18 @@
(defn rename!
[page-uuid-or-old-name new-name & {:as _opts}]
(when-let [^js worker @db-browser/*worker]
(p/let [repo (state/get-current-repo)
page-uuid (cond
(when @db-browser/*worker
(p/let [page-uuid (cond
(uuid? page-uuid-or-old-name)
page-uuid-or-old-name
(common-util/uuid-string? page-uuid-or-old-name)
page-uuid-or-old-name
:else
(:block/uuid (db/get-page page-uuid-or-old-name)))
result (.page-rename worker repo (str page-uuid) new-name)
result' (:result (bean/->clj result))]
result (ui-outliner-tx/transact!
{:outliner-op :rename-page}
(outliner-op/rename-page! page-uuid new-name))
result' (ldb/read-transit-str result)]
(case (if (string? result') (keyword result') result')
:built-in-page
(notification/show! "Built-in page's name cannot be modified" :warning)
@ -166,7 +165,7 @@
current-blocks (ldb/sort-by-order (ldb/get-page-blocks @conn (:db/id favorites-page) {}))]
(p/do!
(ui-outliner-tx/transact!
{}
{:outliner-op :reorder-favorites}
(doseq [[page-block-db-id block] (zipmap favorite-page-block-db-id-coll current-blocks)]
(when (not= page-block-db-id (:db/id (:block/link block)))
(outliner-op/save-block! (assoc block :block/link page-block-db-id)))))

View File

@ -122,3 +122,18 @@
[tx-data tx-meta]
(op-transact!
[:transact [tx-data tx-meta]]))
(defn create-page!
[title options]
(op-transact!
[:create-page [title options]]))
(defn rename-page!
[page-uuid new-name]
(op-transact!
[:rename-page [page-uuid new-name]]))
(defn delete-page!
[page-uuid]
(op-transact!
[:delete-page [page-uuid]]))

View File

@ -92,6 +92,7 @@ generate undo ops.")
(let [tx-meta (merge (batch-tx/get-batch-opts) tx-meta)
pipeline-replace? (:pipeline-replace? tx-meta)
in-batch-tx-mode? (:batch-tx/batch-tx-mode? tx-meta)]
(batch-tx/set-batch-opts tx-meta)
(when-not pipeline-replace?
(if (and in-batch-tx-mode?
(not (:batch-tx/exit? tx-meta)))

View File

@ -34,7 +34,7 @@
* :persist-op? - when true, add an update-page op
* :properties - properties to add to the page
TODO: Add other options"
[repo conn config title & options]
[repo conn config title & {:as options}]
(if (ldb/db-based-graph? @conn)
(db-worker-page/create! conn config title options)
(file-worker-page/create! repo conn config title options)))

View File

@ -84,11 +84,11 @@
(build-first-block-tx (:block/uuid (first page-txs)) format))
txs (concat
page-txs
first-block-tx)
[page-uuid result] (when (seq txs)
[page-uuid (ldb/transact! conn txs (cond-> {:persist-op? persist-op?
:outliner-op :create-page}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name)))])]
[result page-name page-uuid]))))
first-block-tx)]
(when (seq txs)
(ldb/transact! conn txs (cond-> {:persist-op? persist-op?
:outliner-op :create-page}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name))))
[page-name page-uuid]))))

View File

@ -113,11 +113,11 @@
txs (concat
txs
page-txs
first-block-tx)
[page-uuid result] (when (seq txs)
[page-uuid (ldb/transact! conn txs (cond-> {:persist-op? persist-op?
:outliner-op :create-page}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name)))])]
[result page-name page-uuid]))))
first-block-tx)]
(when (seq txs)
(ldb/transact! conn txs (cond-> {:persist-op? persist-op?
:outliner-op :create-page}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name))))
[page-name page-uuid]))))

View File

@ -5,7 +5,6 @@
[frontend.test.helper :as test-helper]
[datascript.core :as d]
[logseq.outliner.property :as outliner-property]
[frontend.handler.page :as page-handler]
[logseq.db :as ldb]))
(def repo test-helper/test-db-name-db-version)
@ -24,8 +23,8 @@
;; (deftest get-db-property-values-test-with-pages
;; (let [opts {:redirect? false :create-first-block? false}
;; _ (page-handler/create! "page1" opts)
;; _ (page-handler/create! "page2" opts)
;; _ (test-helper/create-page! "page1" opts)
;; _ (test-helper/create-page! "page2" opts)
;; p1id (:block/uuid (db/get-page "page1"))
;; p2id (:block/uuid (db/get-page "page2"))]
;; (outliner-property/upsert-property! repo "property-1" {:type :page} {})
@ -35,20 +34,20 @@
(deftest get-all-classes-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (page-handler/create! "class2" opts)]
_ (test-helper/create-page! "class1" opts)
_ (test-helper/create-page! "class2" opts)]
(is (= ["Card" "Root class" "Task" "class1" "class2"] (sort (map first (model/get-all-classes repo)))))))
(deftest get-class-objects-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (test-helper/create-page! "class1" opts)
class (db/get-case-page "class1")
_ (test-helper/save-block! repo fbid "Block 1" {:tags ["class1"]})]
(is (= (model/get-class-objects repo (:db/id class))
[(:db/id (db/entity [:block/uuid fbid]))]))
(testing "classes parent"
(page-handler/create! "class2" opts)
(test-helper/create-page! "class2" opts)
;; set class2's parent to class1
(let [class2 (db/get-case-page "class2")]
(db/transact! [{:db/id (:db/id class2)
@ -60,8 +59,8 @@
(deftest get-classes-with-property-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (page-handler/create! "class2" opts)
_ (test-helper/create-page! "class1" opts)
_ (test-helper/create-page! "class2" opts)
class1 (db/get-case-page "class1")
class2 (db/get-case-page "class2")
conn (db/get-db false)]
@ -75,7 +74,7 @@
(deftest get-tag-blocks-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (test-helper/create-page! "class1" opts)
_ (test-helper/save-block! repo fbid "Block 1" {:tags ["class1"]})
_ (test-helper/save-block! repo sbid "Block 2" {:tags ["class1"]})]
(is
@ -85,16 +84,16 @@
(deftest hidden-page-test
(let [opts {:redirect? false :create-first-block? false}
_ (page-handler/create! "page 1" opts)]
_ (test-helper/create-page! "page 1" opts)]
(is (false? (model/hidden-page? (db/get-page "page 1"))))
(is (true? (model/hidden-page? "$$$test")))
(is (true? (model/hidden-page? (str "$$$" (random-uuid)))))))
(deftest get-class-children-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (page-handler/create! "class2" opts)
_ (page-handler/create! "class3" opts)
_ (test-helper/create-page! "class1" opts)
_ (test-helper/create-page! "class2" opts)
_ (test-helper/create-page! "class3" opts)
class1 (db/get-case-page "class1")
class2 (db/get-case-page "class2")
class3 (db/get-case-page "class3")

View File

@ -5,7 +5,6 @@
[frontend.test.helper :as test-helper]
[datascript.core :as d]
[frontend.state :as state]
[frontend.handler.page :as page-handler]
[logseq.db.frontend.property :as db-property]))
(def repo test-helper/test-db-name-db-version)
@ -173,9 +172,9 @@
;; get-block-classes-properties
(deftest property-class-test
(let [opts {:redirect? false :create-first-block? false :class? true}
_ (page-handler/create! "class1" opts)
_ (page-handler/create! "class2" opts)
_ (page-handler/create! "class3" opts)
_ (test-helper/create-page! "class1" opts)
_ (test-helper/create-page! "class2" opts)
_ (test-helper/create-page! "class3" opts)
c1 (db/get-case-page "class1")
c2 (db/get-case-page "class2")
c1id (:db/id c1)

View File

@ -3,7 +3,6 @@
[clojure.test :refer [deftest is testing use-fixtures]]
[frontend.test.helper :as test-helper]
[datascript.core :as d]
[frontend.handler.page :as page-handler]
[frontend.db :as db]))
(def init-data (test-helper/initial-test-page-and-blocks))
@ -20,7 +19,7 @@
(let [pages (map (fn [i] (str "Page " i)) (range 15))]
;; create pages
(doseq [page pages]
(page-handler/create! page {:redirect? false :create-first-block? false :class? true})
(test-helper/create-page! page {:redirect? false :create-first-block? false :class? true})
(db-recent-handler/add-page-to-recent! (:db/id (db/get-page page)) false))
(is (= (map :block/original-name (db-recent-handler/get-recent-pages)) (reverse pages)))
(testing "Click existing recent item shouldn't update its position"

View File

@ -16,7 +16,8 @@
[logseq.db.frontend.order :as db-order]
[logseq.db.sqlite.build :as sqlite-build]
[frontend.handler.file-based.status :as status]
[logseq.outliner.db-pipeline :as db-pipeline]))
[logseq.outliner.db-pipeline :as db-pipeline]
[frontend.worker.handler.page :as worker-page]))
(def node? (exists? js/process))
@ -241,3 +242,11 @@ This can be called in synchronous contexts as no async fns should be invoked"
(editor-handler/save-block! repo block-uuid content)
(doseq [tag tags]
(page-handler/add-tag repo block-uuid (db/get-page tag))))
(defn create-page!
[title & {:as opts}]
(let [repo (state/get-current-repo)
conn (db/get-db repo false)
config (state/get-config repo)
[page-name _page-uuid] (worker-page/create! repo conn config title opts)]
page-name))

View File

@ -2,7 +2,6 @@
(:require [clojure.test :refer [deftest is testing use-fixtures are]]
[frontend.test.helper :as test-helper]
[datascript.core :as d]
[frontend.handler.page :as page-handler]
[frontend.db :as db]
[clojure.string :as string]
[frontend.util :as util]
@ -40,7 +39,7 @@
(is (= "New name" (:block/original-name (db/entity (:db/id page)))))))
(testing "Merge existing page"
(page-handler/create! "Existing page" {:redirect? false :create-first-block? true})
(test-helper/create-page! "Existing page" {:redirect? false :create-first-block? true})
(let [page (db/get-page "new name")]
(page-rename (:block/uuid page) "Existing page"))
(let [e1 (db/get-page "new name")
@ -51,7 +50,7 @@
(is (= (count (:block/_page e2)) (+ 1 (dec (count init-data))))))))
(deftest merge-with-empty-page
(page-handler/create! "Existing page" {:redirect? false :create-first-block? false})
(test-helper/create-page! "Existing page" {:redirect? false :create-first-block? false})
(let [page (db/get-page "test")]
(page-rename (:block/uuid page) "Existing page"))
(let [e1 (db/get-page "test")
@ -64,7 +63,7 @@
(deftest merge-existing-pages-should-update-ref-ids
(testing "Merge existing page"
(editor-handler/save-block! repo fbid "Block 1 [[Test]]")
(page-handler/create! "Existing page" {:redirect? false :create-first-block? true})
(test-helper/create-page! "Existing page" {:redirect? false :create-first-block? true})
(let [page (db/get-page "test")]
(page-rename (:block/uuid page) "Existing page"))
(let [e1 (db/get-page "test")
@ -187,4 +186,4 @@
"#bar bla [[bar]] bla #bar"
["#logseq/foo bla [[logseq/foo]] bla [[file:./pages/logseq.foo.org][logseq/foo]] bla #logseq/foo" "logseq/foo" "logseq/bar"]
"#logseq/bar bla [[logseq/bar]] bla [[file:./pages/logseq.bar.org][logseq/bar]] bla #logseq/bar"))
"#logseq/bar bla [[logseq/bar]] bla [[file:./pages/logseq.bar.org][logseq/bar]] bla #logseq/bar"))

View File

@ -2,7 +2,6 @@
(:require [clojure.test :as t :refer [deftest is testing use-fixtures]]
[datascript.core :as d]
[frontend.db.conn :as conn]
[frontend.handler.page :as page-handler]
[frontend.state :as state]
[frontend.test.helper :as test-helper]
[frontend.worker.rtc.client :as r.client]
@ -36,7 +35,7 @@
(is (rtc-const/to-ws-ops-validator r) r)
r))]
(testing "create a new page"
(page-handler/create! page1-name {:redirect? false :create-first-block? false :uuid page1-uuid})
(test-helper/create-page! page1-name {:redirect? false :create-first-block? false :uuid page1-uuid})
(let [ops (gen-ops-fn)
ops* (map
(fn [[op-type op-value]]

View File

@ -2,7 +2,6 @@
(:require [clojure.test :as t :refer [deftest is testing use-fixtures]]
[datascript.core :as d]
[frontend.db.conn :as conn]
[frontend.handler.page :as page-handler]
[frontend.state :as state]
[frontend.test.helper :as test-helper]
[frontend.worker.rtc.const :as rtc-const]
@ -71,7 +70,7 @@
opts {:persist-op? false
:transact-opts {:repo repo
:conn conn}}]
(page-handler/create! "gen-remote-ops-test" {:redirect? false :create-first-block? false :uuid uuid1})
(test-helper/create-page! "gen-remote-ops-test" {:redirect? false :create-first-block? false :uuid uuid1})
(outliner-tx/transact!
opts
(outliner-core/insert-blocks!
@ -114,7 +113,7 @@
[page-uuid
uuid1-client uuid2-client
uuid1-remote uuid2-remote] (repeatedly random-uuid)]
(page-handler/create! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(test-helper/create-page! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(outliner-tx/transact!
opts
(outliner-core/insert-blocks!
@ -186,7 +185,7 @@
;; uuid1-remote
;; uuid1-not-exist
;; tag1-uuid] (repeatedly random-uuid)]
;; (page-handler/create! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
;; (test-helper/create-page! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
;; (outliner-tx/transact!
;; opts
;; (outliner-core/insert-blocks!
@ -292,7 +291,7 @@
[page-uuid
uuid1-client uuid2-client
uuid1-not-exist] (repeatedly random-uuid)]
(page-handler/create! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(test-helper/create-page! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(outliner-tx/transact!
opts
(outliner-core/insert-blocks!
@ -357,7 +356,7 @@ result:
page-name "apply-remote-remove-ops-test2"
[page-uuid
uuid1 uuid2 uuid3] (repeatedly random-uuid)]
(page-handler/create! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(test-helper/create-page! page-name {:redirect? false :create-first-block? false :uuid page-uuid})
(outliner-tx/transact!
opts
(outliner-core/insert-blocks!
@ -448,7 +447,7 @@ result:
[page1-uuid page2-uuid
uuid1-client uuid2-client
uuid1-remote uuid2-remote] (repeatedly random-uuid)]
(page-handler/create! page-name {:redirect? false :create-first-block? false :uuid page1-uuid})
(test-helper/create-page! page-name {:redirect? false :create-first-block? false :uuid page1-uuid})
(outliner-tx/transact!
opts
(outliner-core/insert-blocks!