Merge branch 'master' into enhance/rtc-migrate

This commit is contained in:
Tienson Qin
2025-09-11 17:18:37 +08:00
committed by GitHub
31 changed files with 250 additions and 1047 deletions

View File

@@ -1,6 +1,6 @@
{;; Only lint production namespaces as most dev
;; namespaces are unused
:paths ["src/main" "src/electron" "src/test" "src/rtc_e2e_test"]
:paths ["src/main" "src/electron" "src/test"]
:api-namespaces [;; Ignore b/c too many false positives
frontend.db
;; Used for debugging

3
bb.edn
View File

@@ -164,9 +164,6 @@
dev:lint-and-test
logseq.tasks.dev/lint-and-test
dev:rtc-e2e-test
logseq.tasks.dev/rtc-e2e-test
dev:gen-malli-kondo-config
logseq.tasks.dev/gen-malli-kondo-config

View File

@@ -18,4 +18,6 @@
:extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}
io.github.cognitect-labs/test-runner
{:git/tag "v0.5.1" :git/sha "dfb30dd"}}}
:dev {:extra-paths ["dev" "test"]}}}
:dev {:extra-paths ["dev" "test"]}
:dev-run-rtc-extra-test {:extra-paths ["dev" "test"]
:exec-fn user/run-rtc-extra-test2}}}

View File

@@ -74,6 +74,10 @@
(->> (future (run-tests 'logseq.e2e.rtc-extra-test))
(swap! *futures assoc :rtc-extra-test)))
(defn run-rtc-extra-test2
[& _args]
(run-tests 'logseq.e2e.rtc-extra-test))
(defn run-editor-basic-test
[]
(->> (future (run-tests 'logseq.e2e.editor-basic-test))

View File

@@ -1,6 +1,7 @@
(ns logseq.e2e.outliner-basic-test
(:require
[clojure.test :refer [deftest testing is use-fixtures]]
[logseq.e2e.assert :as assert]
[logseq.e2e.block :as b]
[logseq.e2e.fixtures :as fixtures]
[logseq.e2e.keyboard :as k]
@@ -124,3 +125,52 @@
(deftest delete-test-with-children-test
(delete-test-with-children))
(deftest delete-concat-test-2-blocks
(testing "Delete concat with empty block"
(b/new-blocks ["" "b2"])
(b/indent)
(k/arrow-up)
(k/delete)
(util/wait-editor-visible)
(is (= "b2" (util/get-edit-content)))
(util/exit-edit)
(is (= ["b2"] (util/get-page-blocks-contents)))))
(deftest delete-concat-test-3-blocks
(testing "Delete concat with empty block"
(b/new-blocks ["" "b2" "b3"])
(b/indent)
(k/arrow-up)
(k/arrow-up)
(k/delete)
(util/wait-editor-visible)
(is (= "b2" (util/get-edit-content)))
(util/exit-edit)
(is (= ["b2" "b3"] (util/get-page-blocks-contents)))))
(deftest delete-concat-test-with-children
(testing "Delete concat with children blocks"
(b/new-blocks ["" "b2" "b3"])
(b/indent)
(k/arrow-up)
(b/indent)
(k/arrow-up)
(k/delete)
(util/wait-editor-visible)
(is (= "" (util/get-edit-content)))
(is (= 3 (util/page-blocks-count)))))
(deftest delete-concat-test-with-tag
(testing "Delete concat with tag"
(b/new-blocks ["" "b2"])
(b/indent)
(util/set-tag "tag1")
(k/arrow-up)
(k/delete)
(util/wait-editor-visible)
(is (= "b2" (util/get-edit-content)))
(util/exit-edit)
(assert/assert-is-visible
".ls-block a.tag:has-text('tag1')")
(is (= ["b2"] (util/get-page-blocks-contents)))))

View File

@@ -68,10 +68,6 @@
cider/cider-nrepl {:mvn/version "0.55.1"}}
:main-opts ["-m" "shadow.cljs.devtools.cli"]}
:rtc-e2e-test {:extra-paths ["src/rtc_e2e_test"]
:extra-deps {cider/cider-nrepl {:mvn/version "0.50.2"}}
:main-opts ["-m" "shadow.cljs.devtools.cli"]}
:bench {:extra-paths ["src/bench/"]
:extra-deps {olical/cljs-test-runner {:mvn/version "3.8.0"}
fipp/fipp {:mvn/version "0.6.26"}}

View File

@@ -1,19 +0,0 @@
module.exports = function (config) {
config.set({
browsers: ['Chrome'],
// The directory where the output file lives
basePath: 'static/rtc-e2e-test',
// The file itself
files: ['main.js'],
frameworks: ['cljs-test'],
plugins: ['karma-cljs-test', 'karma-chrome-launcher'],
colors: true,
logLevel: config.LOG_INFO,
client: {
args: ["shadow.test.karma.init"],
singleRun: true,
testvar: config.testvar,
seed: config.seed
}
})
};

View File

@@ -26,17 +26,6 @@
(dev-lint/dev)
(test "-e" "long" "-e" "fix-me"))
(defn rtc-e2e-test
"Run karma rtc-e2e-test"
[& [skip-compile?]]
(let [seed (hash (rand))
r0 (when-not skip-compile? (shell (str "clj -M:rtc-e2e-test compile rtc-e2e-test")))
c1 (async/go (shell (str "npx karma start --testvar=client1 --single-run --seed=" seed)))
c2 (async/go (shell (str "npx karma start --testvar=client2 --single-run --seed=" seed)))]
(when (and r0 (not= 0 (:exit r0)))
(throw (ex-info "compile failed" {:r r0})))
(prn :exit-code :client1 (:exit (async/<!! c1)) :client2 (:exit (async/<!! c2)))))
(defn gen-malli-kondo-config
"Generate clj-kondo type-mismatch config from malli schema
.clj-kondo/metosin/malli-types/config.edn"

View File

@@ -6,8 +6,7 @@
;; "." for /static
:dev-http {3001 ["static" "."]
3002 "static/mobile"
8021 "static/rtc-e2e-test"}
3002 "static/mobile"}
:js-options {:js-package-dirs ["node_modules" "packages/tldraw/apps"]}
@@ -174,14 +173,6 @@
:compiler-options {:static-fns false}
:main frontend.test.frontend-node-test-runner/main}
:rtc-e2e-test {:target :karma
:closure-defines {frontend.worker.rtc.const/RTC-E2E-TEST* true}
:output-to "static/rtc-e2e-test/main.js"
:devtools {:enabled true}
:compiler-options {:source-map true
:warnings {:fn-deprecated false
:redef false}}}
:gen-malli-kondo-config {:target :node-script
:closure-defines {frontend.util/NODETEST true}
:devtools {:enabled false}

View File

@@ -131,11 +131,12 @@
(let [cancel (task (or succ (constantly nil)) (or fail fail-case-default-handler))]
#(cancel)))
(defn run-task-throw
"Return the canceler"
[key' task & {:keys [succ]}]
(let [cancel (task (or succ #(log/info :key key' :succ %)) #(throw (ex-info "task stopped" {:key key' :e %})))]
#(cancel)))
(comment
(defn run-task-throw
"Return the canceler"
[key' task & {:keys [succ]}]
(let [cancel (task (or succ #(log/info :key key' :succ %)) #(throw (ex-info "task stopped" {:key key' :e %})))]
#(cancel))))
(defonce ^:private *background-task-cancelers ; key -> canceler
(volatile! {}))

View File

@@ -1162,7 +1162,10 @@
{:data-ref (str uuid-or-title)}
(when (and brackets? (not blank-title?))
[:span.text-gray-500.bracket page-ref/left-brackets])
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
(when (and (config/db-based-graph?)
(or (ldb/class-instance? (db/entity :logseq.class/Task) block)
(:logseq.property/status block)
(:logseq.property/priority block)))
[:div.inline-block
{:style {:margin-right 1
:margin-top -2

View File

@@ -123,7 +123,7 @@
:style {:display "ruby"}}
(cond
(not (db/page? page))
(block/inline-text :markdown (:block/title page))
(block/inline-text :markdown (string/replace (apply str (take 64 (:block/title page))) "\n" " "))
untitled? (t :untitled)
:else (let [title' (pdf-utils/fix-local-asset-pagename title)
parent (:block/parent page)]

View File

@@ -801,10 +801,11 @@
(declare expand-block!)
(defn delete-block-inner!
[repo {:keys [block-id value format config block-container]}]
[repo {:keys [block-id value format config block-container current-block next-block delete-concat?]}]
(when block-id
(when-let [block-e (db/entity [:block/uuid block-id])]
(let [prev-block (db-model/get-prev (db/get-db) (:db/id block-e))]
(let [prev-block (db-model/get-prev (db/get-db) (:db/id block-e))
input-empty? (string/blank? (state/get-edit-content))]
(cond
(and (nil? prev-block)
(nil? (:block/parent block-e)))
@@ -836,6 +837,27 @@
(db-model/hidden-page? (:block/page block))) ; embed page
nil
(and concat-prev-block? input-empty? delete-concat?)
(let [children (:block/_parent (db/entity (:db/id current-block)))]
(p/do!
(ui-outliner-tx/transact!
transact-opts
(when (= (:db/id current-block) (:db/id (:block/parent next-block)))
(property-handler/set-block-properties!
repo
(:block/uuid next-block)
{:block/parent (:db/id (:block/parent current-block))
:block/order (:block/order current-block)}))
(when (seq children)
(outliner-op/move-blocks!
(remove (fn [c] (= (:db/id c) (:db/id next-block))) children)
next-block
{:sibling? false}))
(delete-block-aux! current-block))
(edit-block! (db/entity (:db/id next-block)) 0)))
concat-prev-block?
(let [children (:block/_parent (db/entity (:db/id block)))]
(p/do!
@@ -2745,7 +2767,10 @@
:value (:block/title next-block)
:block-container (util/get-next-block-non-collapsed
(util/rec-get-node (state/get-input) "ls-block")
{:exclude-property? true}))]
{:exclude-property? true})
:current-block current-block
:next-block next-block
:delete-concat? true)]
(delete-block-inner! repo editor-state)))))
(defn keydown-delete-handler

View File

@@ -26,6 +26,7 @@
([content status clear? uid timeout]
(show! content status clear? uid timeout nil))
([content status clear? uid timeout close-cb]
(assert (keyword? status) "status should be a keyword")
(let [contents (state/get-notification-contents)
uid (or uid (keyword (util/unique-id)))]
(state/set-state! :notification/contents (assoc contents

View File

@@ -295,7 +295,7 @@
(icon "info-circle" {:class "text-indigo-500" :size "20"}))
status)]
[:div.ui__notifications-content
{:class (str "notification-" (name (or status :info)))
{:class (str "notification-" (name (or (when (keyword? status) status) :info)))
:style
(when (or (= state "exiting")
(= state "exited"))

View File

@@ -179,8 +179,8 @@
[op e a v])))
datoms))
(defn- moved-block-or-target-deleted?
[conn e->datoms e moved-blocks redo?]
(defn- block-moved-and-target-deleted?
[conn e->datoms e moved-blocks tx-data]
(let [datoms (get e->datoms e)]
(and (moved-blocks e)
(let [b (d/entity @conn e)
@@ -188,16 +188,17 @@
move-datoms (filter (fn [d] (contains? #{:block/parent} (:a d))) datoms)]
(when cur-parent
(let [before-parent (some (fn [d] (when (and (= :block/parent (:a d)) (not (:added d))) (:v d))) move-datoms)
after-parent (some (fn [d] (when (and (= :block/parent (:a d)) (:added d)) (:v d))) move-datoms)]
(and before-parent after-parent ; parent changed
(if redo?
(or (not= cur-parent before-parent)
(nil? (d/entity @conn after-parent)))
(or (not= cur-parent after-parent)
(nil? (d/entity @conn before-parent)))))))))))
not-exists-in-current-db (nil? (d/entity @conn before-parent))
;; reverse tx-data will add parent before back
removed-before-parent (some (fn [d] (and (= :block/uuid (:a d))
(= before-parent (:e d))
(not (:added d)))) tx-data)]
(and before-parent
not-exists-in-current-db
(not removed-before-parent))))))))
(defn get-reversed-datoms
[conn undo? {:keys [tx-data added-ids retracted-ids] :as op} _tx-meta]
[conn undo? {:keys [tx-data added-ids retracted-ids] :as op} tx-meta]
(try
(let [redo? (not undo?)
e->datoms (->> (if redo? tx-data (reverse tx-data))
@@ -218,16 +219,18 @@
:undo? undo?})))
;; new children blocks have been added
(or (and (contains? retracted-ids e) redo?
(other-children-exist? entity retracted-ids)) ; redo delete-blocks
(and (contains? added-ids e) undo? ; undo insert-blocks
(other-children-exist? entity added-ids)))
(and
(not (:local-tx? tx-meta))
(or (and (contains? retracted-ids e) redo?
(other-children-exist? entity retracted-ids)) ; redo delete-blocks
(and (contains? added-ids e) undo? ; undo insert-blocks
(other-children-exist? entity added-ids))))
(throw (ex-info "Children still exists"
(merge op {:error :block-children-exists
:undo? undo?})))
;; block has been moved or target got deleted by another client
(moved-block-or-target-deleted? conn e->datoms e moved-blocks redo?)
(block-moved-and-target-deleted? conn e->datoms e moved-blocks tx-data)
(throw (ex-info "This block has been moved or its target has been deleted"
(merge op {:error :block-moved-or-target-deleted
:undo? undo?})))
@@ -238,7 +241,6 @@
(and (contains? added-ids e) undo?))) ; undo insert-blocks
[[:db/retractEntity e]]
;; reverse datoms
:else
(reverse-datoms conn datoms schema added-ids retracted-ids undo? redo?))))
e->datoms)

View File

@@ -537,7 +537,7 @@
(catch :default e
(prn :debug :error)
(js/console.error e)
(prn :debug :tx-data @conn tx-data)))))
(prn :debug :tx-meta tx-meta :tx-data tx-data)))))
(def-thread-api :thread-api/get-initial-data
[repo]

View File

@@ -4,9 +4,6 @@
[logseq.db.frontend.kv-entity :as kv-entity]
[logseq.db.frontend.property :as db-property]))
(goog-define RTC-E2E-TEST* false)
(def RTC-E2E-TEST RTC-E2E-TEST*)
(defkeywords
:rtc/ignore-attr-when-init-upload
{:doc "keyword option for RTC. ignore this *attr* when initial uploading graph. Default false"}

View File

@@ -8,7 +8,6 @@
[frontend.common.thread-api :as thread-api]
[frontend.worker-common.util :as worker-util]
[frontend.worker.crypt :as crypt]
[frontend.worker.db-listener :as db-listener]
[frontend.worker.db-metadata :as worker-db-metadata]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.const :as rtc-const]
@@ -20,8 +19,6 @@
[lambdaisland.glogi :as log]
[logseq.db :as ldb]
[logseq.db.frontend.malli-schema :as db-malli-schema]
[logseq.db.frontend.schema :as db-schema]
[logseq.db.sqlite.create-graph :as sqlite-create-graph]
[logseq.db.sqlite.util :as sqlite-util]
[logseq.outliner.pipeline :as outliner-pipeline]
[malli.core :as ma]
@@ -161,8 +158,7 @@
(client-op/remove-local-tx repo)
(client-op/add-all-exists-asset-as-ops repo)
(crypt/store-graph-keys-jwk repo aes-key-jwk)
(when-not rtc-const/RTC-E2E-TEST
(c.m/<? (worker-db-metadata/<store repo (pr-str {:kv/value graph-uuid}))))
(c.m/<? (worker-db-metadata/<store repo (pr-str {:kv/value graph-uuid})))
(rtc-log-and-state/rtc-log :rtc.log/upload {:sub-type :upload-completed
:message "upload-graph completed"})
{:graph-uuid graph-uuid})
@@ -269,29 +265,6 @@
[schema-blocks (conj normal-blocks block)]))
[[] []] blocks))
(defn- create-graph-for-rtc-test
"TODO: remove this fn
it's complex to setup db-worker related stuff, when I only want to test rtc related logic"
[repo init-tx-data other-tx-data]
(let [conn (d/create-conn db-schema/schema)
db-initial-data (sqlite-create-graph/build-db-initial-data "")]
(swap! worker-state/*datascript-conns assoc repo conn)
(d/transact! conn db-initial-data {:initial-db? true
:frontend.worker.pipeline/skip-store-conn rtc-const/RTC-E2E-TEST})
(db-listener/listen-db-changes! repo conn)
(d/transact! conn init-tx-data {:rtc-download-graph? true
:gen-undo-ops? false
;; only transact db schema, skip validation to avoid warning
:frontend.worker.pipeline/skip-validate-db? true
:frontend.worker.pipeline/skip-store-conn rtc-const/RTC-E2E-TEST
:persist-op? false})
(d/transact! conn other-tx-data {:rtc-download-graph? true
:gen-undo-ops? false
:frontend.worker.pipeline/skip-store-conn rtc-const/RTC-E2E-TEST
:persist-op? false})
(transact-remote-schema-version! repo)
(<transact-block-refs! repo nil)))
(defn- blocks-resolve-temp-id
[schema-blocks blocks]
(let [uuids (map :block/uuid blocks)
@@ -388,37 +361,35 @@
(client-op/update-local-tx repo remote-t)
(rtc-log-and-state/update-local-t graph-uuid remote-t)
(rtc-log-and-state/update-remote-t graph-uuid remote-t)
(if rtc-const/RTC-E2E-TEST
(create-graph-for-rtc-test repo init-tx-data tx-data)
(c.m/<?
(p/do!
((@thread-api/*thread-apis :thread-api/create-or-open-db) repo {:close-other-db? false})
((@thread-api/*thread-apis :thread-api/export-db) repo)
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-1
:message (str "transacting init data(" (count init-tx-data) ")")
:graph-uuid graph-uuid})
((@thread-api/*thread-apis :thread-api/transact)
repo init-tx-data
{:rtc-download-graph? true
:gen-undo-ops? false
(c.m/<?
(p/do!
((@thread-api/*thread-apis :thread-api/create-or-open-db) repo {:close-other-db? false})
((@thread-api/*thread-apis :thread-api/export-db) repo)
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-1
:message (str "transacting init data(" (count init-tx-data) ")")
:graph-uuid graph-uuid})
((@thread-api/*thread-apis :thread-api/transact)
repo init-tx-data
{:rtc-download-graph? true
:gen-undo-ops? false
;; only transact db schema, skip validation to avoid warning
:frontend.worker.pipeline/skip-validate-db? true
:persist-op? false}
(worker-state/get-context))
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-2
:message (str "transacting other data(" (count tx-data) ")")
:graph-uuid graph-uuid})
(p/doseq [tx-data* (partition-all 500 tx-data)]
((@thread-api/*thread-apis :thread-api/transact)
repo tx-data* {:rtc-download-graph? true
:gen-undo-ops? false
:persist-op? false} (worker-state/get-context))
(p/delay 10))
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-3
:message "transacting remote schema version"
:graph-uuid graph-uuid})
(transact-remote-schema-version! repo)
(<transact-block-refs! repo graph-uuid))))
:frontend.worker.pipeline/skip-validate-db? true
:persist-op? false}
(worker-state/get-context))
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-2
:message (str "transacting other data(" (count tx-data) ")")
:graph-uuid graph-uuid})
(p/doseq [tx-data* (partition-all 500 tx-data)]
((@thread-api/*thread-apis :thread-api/transact)
repo tx-data* {:rtc-download-graph? true
:gen-undo-ops? false
:persist-op? false} (worker-state/get-context))
(p/delay 10))
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :transact-graph-data-to-db-3
:message "transacting remote schema version"
:graph-uuid graph-uuid})
(transact-remote-schema-version! repo)
(<transact-block-refs! repo graph-uuid)))
(shared-service/broadcast-to-clients! :add-repo {:repo repo}))))
;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -490,8 +461,7 @@
:message "transacted all blocks"
:graph-uuid graph-uuid})
(client-op/update-graph-uuid repo graph-uuid)
(when-not rtc-const/RTC-E2E-TEST
(c.m/<? (worker-db-metadata/<store repo (pr-str {:kv/value graph-uuid}))))
(c.m/<? (worker-db-metadata/<store repo (pr-str {:kv/value graph-uuid})))
(worker-state/set-rtc-downloading-graph! false)
(rtc-log-and-state/rtc-log :rtc.log/download {:sub-type :download-completed
:message "download completed"
@@ -600,7 +570,4 @@
datoms)))]
(prn ::count (count refs-tx))
;; (prn ::take-20 (take 20 (sort-by second > (into [] (frequencies (map last refs-tx))))))
)
)
))

View File

@@ -9,10 +9,6 @@
(def ^:private *rtc-log (atom nil))
(def rtc-log-flow
"used by rtc-e2e-test"
(m/watch *rtc-log))
(def ^:private rtc-log-type-schema
(vec
(concat

View File

@@ -618,8 +618,7 @@ so need to pull earlier remote-data from websocket."})
(js/console.groupCollapsed "rtc/apply-remote-ops-log")
(batch-tx/with-batch-tx-mode conn {:rtc-tx? true
:persist-op? false
:gen-undo-ops? false
:frontend.worker.pipeline/skip-store-conn rtc-const/RTC-E2E-TEST}
:gen-undo-ops? false}
(worker-util/profile :ensure-refed-blocks-exist (ensure-refed-blocks-exist repo conn refed-blocks))
(worker-util/profile :apply-remote-update-page-ops (apply-remote-update-page-ops repo conn update-page-ops))
(worker-util/profile :apply-remote-move-ops (apply-remote-move-ops repo conn sorted-move-ops))

View File

@@ -49,10 +49,11 @@
blocking-scroll? (atom false)
sidebar-initial-open? (atom false)
max-x (atom 0)
max-y (atom 0)
min-y (atom 0)
swipe-trigger-distance 50 ;; distance to actually open/close
horiz-intent-threshold 10 ;; start blocking scroll when horizontal intent is clear
max-vertical-drift 50
max-vertical-drift 30
on-touch-start (fn [^js e]
(when-not (sidebar-not-allowed-to-open?)
(let [t (aget e "touches" 0)]
@@ -61,14 +62,18 @@
(reset! touch-start-y (.-pageY t))
(reset! has-triggered? false)
(reset! blocking-scroll? false)
(reset! max-x 0))))
(reset! max-x 0)
(reset! max-y (.-pageY t))
(reset! min-y (.-pageY t)))))
on-touch-move (fn [^js e]
(when-not (sidebar-not-allowed-to-open?)
(let [t (aget e "touches" 0)
_ (reset! max-x (max (.-pageX t) @max-x))
_ (reset! max-y (max (.-pageY t) @max-y))
_ (reset! min-y (min (.-pageY t) @min-y))
dx (- (.-pageX t) @touch-start-x)
dy (js/Math.abs (- (.-pageY t) @touch-start-y))
dy (js/Math.abs (- @max-y @min-y))
abs-dx (js/Math.abs dx)
horizontal-intent (and (> abs-dx horiz-intent-threshold)
(> abs-dx dy))
@@ -79,7 +84,7 @@
(> (- @max-x (.-pageX t)) swipe-trigger-distance)
(< dy max-vertical-drift))]
;; Block vertical scroll as soon as horizontal intent is clear
;; Block vertical scroll as soon as horizontal intent is clear
(when (or @blocking-scroll? (and horizontal-intent
(not @sidebar-initial-open?)
(mobile-state/left-sidebar-open?)))

View File

@@ -73,6 +73,7 @@
(rum/defc mobile-bar < rum/reactive
[]
(when (and (util/mobile?)
(not (state/sub :editor/code-block-context))
(or (state/sub :editor/editing?)
(= "app-keep-keyboard-open-input" (some-> js/document.activeElement (.-id)))))
(let [commands' (commands)]

View File

@@ -9,7 +9,7 @@
[]
[:div.w-full.app-silk-popup-content-inner.px-2
[:div.left-sidebar-inner
[:div.sidebar-contents-container.mt-8
[:div.sidebar-contents-container.mt-11
{:class "!gap-4"}
(app-left-sidebar/sidebar-favorites)
(app-left-sidebar/sidebar-recent-pages)]]])

View File

@@ -35,6 +35,70 @@
:class "-ml-2"}
(shui/tabler-icon (if back? "arrow-left" "chevron-down") {:size 24}))))
(defn- skip-touch-check?
[]
(or (seq @mobile-state/*popup-data)
(:mobile/show-action-bar? @state/state)
(state/editing?)))
(defn- setup-sidebar-touch-swipe!
[ref]
(let [touch-start-x (atom 0)
touch-start-y (atom 0)
has-triggered? (atom false)
blocking-scroll? (atom false)
max-y (atom 0)
min-y (atom 0)
swipe-trigger-distance 50 ;; when to actually open sidebar
horiz-intent-threshold 10 ;; when to start blocking scroll
max-vertical-drift 50
on-touch-start (fn [^js e]
(when-not (skip-touch-check?)
(let [t (aget e "touches" 0)]
(reset! touch-start-x (.-pageX t))
(reset! touch-start-y (.-pageY t))
(reset! has-triggered? false)
(reset! blocking-scroll? false)
(reset! max-y (.-pageY t))
(reset! min-y (.-pageY t)))))
on-touch-move (fn [^js e]
(when-not (skip-touch-check?)
(let [t (aget e "touches" 0)
dx (- (.-pageX t) @touch-start-x)
dy (js/Math.abs (- @max-y @min-y))
_ (reset! max-y (max (.-pageY t) @max-y))
_ (reset! min-y (min (.-pageY t) @min-y))
horizontal-intent (and (> dx horiz-intent-threshold)
(> dx dy))
is-horizontal-swipe (and (> dx swipe-trigger-distance)
(< dy max-vertical-drift))]
;; as soon as we detect horizontal intent, block vertical scrolling
(when (or @blocking-scroll? horizontal-intent)
(reset! blocking-scroll? true)
(.preventDefault e)) ;; <-- stops page from scrolling
(when (and (not @has-triggered?)
is-horizontal-swipe)
(reset! has-triggered? true)
(mobile-state/pop-navigation-history!)))))
on-touch-end (fn [_]
(reset! blocking-scroll? false))]
;; IMPORTANT: passive:false so preventDefault actually works
(.addEventListener ref "touchstart" on-touch-start #js {:passive false})
(.addEventListener ref "touchmove" on-touch-move #js {:passive false})
(.addEventListener ref "touchend" on-touch-end #js {:passive false})
(.addEventListener ref "touchcancel" on-touch-end #js {:passive false})
;; cleanup
#(do
(.removeEventListener ref "touchstart" on-touch-start)
(.removeEventListener ref "touchmove" on-touch-move)
(.removeEventListener ref "touchend" on-touch-end)
(.removeEventListener ref "touchcancel" on-touch-end))))
(rum/defc block-cp
[block]
[:div.app-silk-scroll-content-inner
@@ -43,59 +107,6 @@
(mobile-ui/classic-app-container-wrap
(page/page-cp (db/entity [:block/uuid (:block/uuid block)])))]])
(defn- setup-sidebar-touch-swipe!
[]
(let [touch-start-x (atom 0)
touch-start-y (atom 0)
has-triggered? (atom false)
blocking-scroll? (atom false)
swipe-trigger-distance 50 ;; when to actually open sidebar
horiz-intent-threshold 10 ;; when to start blocking scroll
max-vertical-drift 50
on-touch-start (fn [^js e]
(let [t (aget e "touches" 0)]
(reset! touch-start-x (.-pageX t))
(reset! touch-start-y (.-pageY t))
(reset! has-triggered? false)
(reset! blocking-scroll? false)))
on-touch-move (fn [^js e]
(let [t (aget e "touches" 0)
dx (- (.-pageX t) @touch-start-x)
dy (js/Math.abs (- (.-pageY t) @touch-start-y))
horizontal-intent (and (> dx horiz-intent-threshold)
(> dx dy))
is-horizontal-swipe (and (> dx swipe-trigger-distance)
(< dy max-vertical-drift))]
;; as soon as we detect horizontal intent, block vertical scrolling
(when (or @blocking-scroll? horizontal-intent)
(reset! blocking-scroll? true)
(.preventDefault e)) ;; <-- stops page from scrolling
(when (and (not @has-triggered?)
is-horizontal-swipe)
(reset! has-triggered? true)
(mobile-state/pop-navigation-history!))))
on-touch-end (fn [_]
(reset! blocking-scroll? false))]
;; IMPORTANT: passive:false so preventDefault actually works
(.addEventListener js/document "touchstart" on-touch-start #js {:passive false})
(.addEventListener js/document "touchmove" on-touch-move #js {:passive false})
(.addEventListener js/document "touchend" on-touch-end #js {:passive false})
(.addEventListener js/document "touchcancel" on-touch-end #js {:passive false})
;; cleanup
#(do
(.removeEventListener js/document "touchstart" on-touch-start)
(.removeEventListener js/document "touchmove" on-touch-move)
(.removeEventListener js/document "touchend" on-touch-end)
(.removeEventListener js/document "touchcancel" on-touch-end))))
(rum/defc block-sheet-topbar
[block {:keys [favorited? set-favorited!]}]
@@ -150,18 +161,34 @@
:type :action-sheet}))}
(shui/tabler-icon "dots-vertical" {:size 20}))]]))
(rum/defc sheet-content
[block favorited? set-favorited!]
(let [*ref (hooks/use-ref nil)]
(hooks/use-effect!
(fn []
(when-let [ref (rum/deref *ref)]
(setup-sidebar-touch-swipe! ref)))
[(rum/deref *ref)])
(silkhq/depth-sheet-content
{:class "app-silk-depth-sheet-content"
:ref *ref}
(block-sheet-topbar block {:favorited? favorited?
:set-favorited! set-favorited!})
(silkhq/scroll
{:as-child true}
(silkhq/scroll-view
{:class "app-silk-scroll-view"
:scrollGestureTrap {:yEnd true}}
(silkhq/scroll-content
{:class "app-silk-scroll-content"}
(block-cp block)))))))
(rum/defc block-sheet
[block]
(let [block (when-let [id (:block/uuid block)]
(db/entity [:block/uuid id]))
open? (boolean block)
[favorited? set-favorited!] (hooks/use-state false)]
(hooks/use-effect!
(fn []
(setup-sidebar-touch-swipe!))
[])
(hooks/use-effect!
(fn []
(set-favorited! (page-handler/favorited? (str (:block/uuid block)))))
@@ -188,19 +215,7 @@
:onClickOutside (bean/->js {:dismiss false
:stopOverlayPropagation false})}
(silkhq/depth-sheet-backdrop)
(silkhq/depth-sheet-content
{:class "app-silk-depth-sheet-content"}
(block-sheet-topbar block {:favorited? favorited?
:set-favorited! set-favorited!})
(silkhq/scroll
{:as-child true}
(silkhq/scroll-view
{:class "app-silk-scroll-view"
:scrollGestureTrap {:yEnd true}}
(silkhq/scroll-content
{:class "app-silk-scroll-content"}
(block-cp block))))))))))
(sheet-content block favorited? set-favorited!))))))
(rum/defc blocks-modal < rum/reactive
[]

View File

@@ -1,28 +0,0 @@
(ns basic-edits-test
(:require [client-steps]
[cljs.test :as t :refer [deftest]]
[const]
[fixture]
[helper]
[missionary.core :as m]))
(t/use-fixtures :once
fixture/install-some-consts
fixture/install-example-db-fixture
fixture/clear-test-remote-graphs-fixture
fixture/upload-example-graph-fixture
fixture/build-conn-by-download-example-graph-fixture)
(deftest basic-edits-test
(t/async
done
(js/Promise.
(if const/is-client1?
(m/sp
(doseq [task client-steps/client1-steps]
(m/? task))
(done))
(m/sp
(doseq [task client-steps/client2-steps]
(m/? task))
(done))))))

View File

@@ -1,264 +0,0 @@
(ns client-steps
(:require [cljs.test :as t :refer [is]]
[const]
[datascript.core :as d]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.core :as rtc-core]
[helper]
[frontend.common.missionary :as c.m]
[logseq.db :as ldb]
[missionary.core :as m]))
(def ^:private step0
{:client1
(m/sp
(let [conn (helper/get-downloaded-test-conn)
tx-data (const/tx-data-map :create-page)]
(helper/transact! conn tx-data)
(is (=
#{[:update-page const/page1-uuid]
[:update const/page1-uuid
[[:block/title "[\"~#'\",\"basic-edits-test\"]" true]
[:block/created-at "[\"~#'\",1724836490809]" true]
[:block/updated-at "[\"~#'\",1724836490809]" true]
[:block/type "[\"~#'\",\"page\"]" true]]]
[:move const/block1-uuid]
[:update const/block1-uuid
[[:block/updated-at "[\"~#'\",1724836490810]" true]
[:block/created-at "[\"~#'\",1724836490810]" true]
[:block/title "[\"~#'\",\"block1\"]" true]]]}
(set (map helper/simplify-client-op (client-op/get-all-block-ops const/downloaded-test-repo)))))))
:client2 nil})
(def ^:private step1
"client1: start rtc, wait page1, client1->remote
client2: start rtc, wait page1, remote->client2"
{:client1
(m/sp
(let [r (m/? (rtc-core/new-task--rtc-start false))]
(is (nil? r))
(m/? (helper/new-task--wait-all-client-ops-sent))))
:client2
(m/sp
(let [r (m/? (rtc-core/new-task--rtc-start false))]
(is (nil? r)))
(m/?
(c.m/backoff
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
page1 (d/pull @conn '[*] [:block/uuid const/page1-uuid])
block1 (d/pull @conn '[*] [:block/uuid const/block1-uuid])]
(when-not (:block/uuid page1)
(throw (ex-info "wait page1 synced" {:missionary/retry true})))
(is
(= {:block/title "basic-edits-test"
:block/name "basic-edits-test"
:block/type "page"}
(select-keys page1 [:block/title :block/name :block/type])))
(is
(= {:block/title "block1"
:block/order "a0"
:block/parent {:db/id (:db/id page1)}}
(select-keys block1 [:block/title :block/order :block/parent]))))))))})
(def ^:private step2
"client1: insert 500 blocks, wait for changes to sync to remote
client2: wait for blocks to sync from remote"
{:client1
(m/sp
(let [conn (helper/get-downloaded-test-conn)]
(helper/transact! conn (const/tx-data-map :insert-500-blocks))
(m/? (helper/new-task--wait-all-client-ops-sent))))
:client2
(c.m/backoff
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
page (d/pull @conn '[*] [:block/uuid const/page2-uuid])]
(when-not (:block/uuid page)
(throw (ex-info "wait page to be synced" {:missionary/retry true})))
(let [blocks (ldb/sort-by-order (ldb/get-page-blocks @conn (:db/id page)))]
(is (= 500 (count blocks)))
(is (= (map #(str "x" %) (range 500))
(map :block/title blocks)))))))})
(def ^:private step3
"client1:
1. add #task properties to block1 (`const/block1-uuid`)
2. wait to be synced
3. toggle block1 status to TODO
4. wait to be synced
5. toggle block1 status to DOING
6. wait to be synced
client2:
1. wait the block&its properties to be synced"
{:client1
(m/sp
(let [conn (helper/get-downloaded-test-conn)
tx-data1 (const/tx-data-map :step3-add-task-properties-to-block1)
tx-data2 (const/tx-data-map :step3-toggle-status-TODO)
tx-data3 (const/tx-data-map :step3-toggle-status-DOING)]
(helper/transact! conn tx-data1)
(m/? (helper/new-task--wait-all-client-ops-sent))
(helper/transact! conn tx-data2)
(m/? (helper/new-task--wait-all-client-ops-sent))
(helper/transact! conn tx-data3)
(m/? (helper/new-task--wait-all-client-ops-sent))))
:client2
(c.m/backoff
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
block1 (d/pull @conn
[{:block/tags [:db/ident]}
{:logseq.property/status [:db/ident]}
{:logseq.property/deadline [:block/journal-day]}]
[:block/uuid const/block1-uuid])]
(when-not (= :logseq.property/status.doing (:db/ident (:logseq.property/status block1)))
(throw (ex-info "wait block1's task properties to be synced" {:missionary/retry true})))
(is (= {:block/tags [{:db/ident :logseq.class/Task}],
:logseq.property/status {:db/ident :logseq.property/status.doing}
:logseq.property/deadline {:block/journal-day 20240907}}
block1)))))})
(def ^:private step4
"client1:
client2:
"
{:client1
(m/sp nil)
:client2
(m/sp nil)})
(def ^:private step5
"client1:
- insert some blocks in page2
- wait to be synced
- wait a signal from client2
- send a signal to client2
- stop rtc
- move some blocks
- start rtc
- wait to be synced
- wait client2's message, which contains the result of client2's block tree,
and compare them with blocks in client1
client2:
- wait inserted blocks synced
- send a signal to client1
- wait a signal from client1
- stop rtc
- move some blocks
- start rtc
- wait to be synced
- send a message to client1 contains client2's block tree to client1"
{:client1
(m/sp
(let [conn (helper/get-downloaded-test-conn)
tx-data1 (const/tx-data-map :move-blocks-concurrently-1)
tx-data2 (const/tx-data-map :move-blocks-concurrently-client1)]
(helper/transact! conn tx-data1)
(m/? (helper/new-task--wait-all-client-ops-sent))
(m/? (helper/new-task--client1-sync-barrier-2->1 "move-blocks-concurrently-signal"))
(m/? helper/new-task--stop-rtc)
(helper/transact! conn tx-data2)
(is (nil? (m/? (rtc-core/new-task--rtc-start false))))
(m/? (helper/new-task--wait-all-client-ops-sent))
(m/? (helper/new-task--client1-sync-barrier-2->1 "step5"))
(let [message (m/? (helper/new-task--wait-message-from-other-client
(fn [message] (= "move-blocks-concurrently-page-blocks" (:id message)))
:retry-message "move-blocks-concurrently-page-blocks"))
client2-page-blocks (:page-blocks message)
client1-page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
:pull-keys '[:block/uuid :block/title :block/order
{:block/parent [:block/uuid]}])]
(is (= (set client1-page-blocks) (set client2-page-blocks))))))
:client2
(m/sp
(let [conn (helper/get-downloaded-test-conn)]
(m/?
(c.m/backoff
(take 4 c.m/delays)
(m/sp
(let [page3 (d/pull @conn '[*] [:block/uuid const/page3-uuid])
page3-blocks (some->> (:db/id page3)
(ldb/get-page-blocks @conn))]
(when-not (:block/uuid page3)
(throw (ex-info "wait page3 synced" {:missionary/retry true})))
(is (= 6 (count page3-blocks)))))))
(m/? (helper/new-task--client2-sync-barrier-2->1 "move-blocks-concurrently-signal"))
(m/? helper/new-task--stop-rtc)
(helper/transact! conn (const/tx-data-map :move-blocks-concurrently-client2))
(is (nil? (m/? (rtc-core/new-task--rtc-start false))))
(m/? (helper/new-task--wait-all-client-ops-sent))
(m/? (helper/new-task--client2-sync-barrier-2->1 "step5"))
(m/? (helper/new-task--send-message-to-other-client
{:id "move-blocks-concurrently-page-blocks"
:page-blocks (ldb/get-page-blocks @conn (:db/id (d/entity @conn [:block/uuid const/page3-uuid]))
:pull-keys '[:block/uuid :block/title :block/order
{:block/parent [:block/uuid]}])}))))})
(def ^:private step6
"Delete blocks test-1
client1:
- insert some blocks
- wait to be synced
- stop rtc
- delete blocks
- start rtc
- wait to be synced
client2:
- wait blocks from client1
- wait delete-blocks changes synced from client1
- check block-tree"
{:client1
(m/sp
(let [conn (helper/get-downloaded-test-conn)
tx-data1 (const/tx-data-map :step6-delete-blocks-client1-1)
tx-data2 (const/tx-data-map :step6-delete-blocks-client1-2)]
(helper/transact! conn tx-data1)
(m/? (helper/new-task--wait-all-client-ops-sent))
(m/? (helper/new-task--client1-sync-barrier-1->2 "step6"))
(m/? helper/new-task--stop-rtc)
(helper/transact! conn tx-data2)
(let [r (m/? (rtc-core/new-task--rtc-start false))]
(is (nil? r))
(m/? (helper/new-task--wait-all-client-ops-sent)))))
:client2
(m/sp
(let [conn (helper/get-downloaded-test-conn)]
(m/? (helper/new-task--client2-sync-barrier-1->2 "step6"))
(m/?
(c.m/backoff
{}
(m/sp
(let [page (d/pull @conn '[*] [:block/uuid const/step6-page-uuid])
page-blocks (when-let [page-id (:db/id page)]
(ldb/get-page-blocks @conn page-id
:pull-keys '[:block/uuid {:block/parent [:block/uuid]}]))]
(when-not (= 1 (count page-blocks))
(throw (ex-info "wait delete-blocks changes synced"
{:missionary/retry true
:page-blocks page-blocks})))
(is (= {:block/uuid const/step6-block3-uuid
:block/parent {:block/uuid const/step6-page-uuid}}
(select-keys (first page-blocks) [:block/uuid :block/parent])))))))))})
(defn- wrap-print-step-info
[steps client]
(map-indexed
(fn [idx step]
(m/sp
(helper/log "start step" idx)
(some-> (get step client) m/?)
(helper/log "end step" idx)))
steps))
(def ^:private all-steps [step0 step1 step2 step3 step4 step5 step6])
(def client1-steps
(wrap-print-step-info all-steps :client1))
(def client2-steps
(wrap-print-step-info all-steps :client2))

View File

@@ -1,229 +0,0 @@
(ns const
"Consts for rtc e2e tests"
(:require [logseq.db.common.order :as db-order]))
(assert (exists? js/__karma__))
(def seed js/__karma__.config.seed)
(def testvar js/__karma__.config.testvar)
(prn :karma-config :seed seed :testvar testvar)
(def is-client1? (= "client1" testvar))
(def test-token "TEST-TOKEN")
(def test-graph-name (str "TEST-REPO-" seed))
(def test-repo (str "logseq_db_TEST-REPO-" seed))
(def downloaded-test-graph-name "TEST-REPO-downloaded")
(def downloaded-test-repo "logseq_db_TEST-REPO-downloaded")
;;; tests data
(def message-page-uuid #uuid "a3da426a-4202-4a79-8e97-13f4862b0270")
(def page1-uuid #uuid "c051d36f-98b3-4afb-b52a-d5a06bd8591d")
(def page2-uuid #uuid "91d3e320-d2a6-47ae-96a7-8a366ab96cbb")
(def page3-uuid #uuid "9a846640-2b63-4298-9ad6-8ca6c1285016")
(def block1-uuid #uuid "aa6d5e60-5d3a-4468-812f-bd60dc9639fb")
;;; ----- move-blocks-concurrently case -----
(def block2-uuid #uuid "a78e19fc-7e9a-4f61-8988-0e9a649bc875")
(def block3-uuid #uuid "226166d8-1380-4d7a-9fe1-f98e2d583259")
(def block4-uuid #uuid "fb8f05d2-9d91-492e-81e2-8a0b65f09d8c")
(def block5-uuid #uuid "f3c48e62-1726-4492-b42a-a36f4de7b32f")
(def block6-uuid #uuid "23f51a53-db85-465a-9f18-6ca94e59f56c")
(def block7-uuid #uuid "83f99937-fe0a-4d33-81ce-7fe5837baad3")
;;; ----- delete-blocks case ---------
(def step6-page-uuid #uuid "e22dafa5-b3b4-405d-b93d-470caa420e10")
(def step6-block1-uuid #uuid "776acd4a-d011-4985-bfc2-14ee7bbd6a28")
(def step6-block2-uuid #uuid "ba3998c2-8059-4f9e-9e76-2760d2f14512")
(def step6-block3-uuid #uuid "f9ce5393-370a-43dd-a721-aaa5ef83d3ff")
(def step6-block4-uuid #uuid "db00bb0d-2bef-49e7-96ed-b4882cdf5686")
(def step6-block5-uuid #uuid "d34e8a9c-5e87-4511-b982-2bf2ebc82607")
(def ^:large-vars/data-var tx-data-map
{:create-page
[{:db/id "page"
:block/name "basic-edits-test"
:block/title "basic-edits-test"
:block/uuid page1-uuid
:block/created-at 1724836490809
:block/updated-at 1724836490809
:block/type "page"}
{:block/uuid block1-uuid
:block/updated-at 1724836490810
:block/created-at 1724836490810
:block/title "block1"
:block/parent "page"
:block/order "a0"
:block/page "page"}]
:insert-500-blocks
(cons {:db/id "page"
:block/uuid page2-uuid
:block/name "insert-500-blocks"
:block/title "insert-500-blocks"
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/type "page"}
(map (fn [i order]
{:block/uuid (random-uuid)
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title (str "x" i)
:block/parent "page"
:block/order order
:block/page "page"})
(range 500) (db-order/gen-n-keys 500 "a0" "a1")))
:step3-add-task-properties-to-block1
[{:db/id "id-0907"
:block/uuid #uuid "00000001-2024-0907-0000-000000000000"
:block/updated-at 1725455235108
:block/created-at 1725455235108
:block/journal-day 20240907
:block/title "Sep 7th, 2024"
:block/name "sep 7th, 2024"
:block/type "journal"}
{:block/uuid block1-uuid
:block/updated-at 1725454876718
:block/tags :logseq.class/Task
:logseq.property/status :logseq.property/status.done
:logseq.property/deadline "id-0907"}]
:step3-toggle-status-TODO
[{:block/uuid block1-uuid
:logseq.property/status :logseq.property/status.todo}]
:step3-toggle-status-DOING
[{:block/uuid block1-uuid
:logseq.property/status :logseq.property/status.doing}]
:move-blocks-concurrently-1
[{:db/id "page"
:block/uuid page3-uuid
:block/name "move-blocks-concurrently"
:block/title "move-blocks-concurrently"
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/type "page"}
{:block/uuid block2-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x1"
:block/parent "page"
:block/order "a0"
:block/page "page"}
{:block/uuid block3-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x2"
:block/parent "page"
:block/order "a1"
:block/page "page"}
{:block/uuid block4-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x3"
:block/parent "page"
:block/order "a2"
:block/page "page"}
{:block/uuid block5-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x4"
:block/parent "page"
:block/order "a3"
:block/page "page"}
{:block/uuid block6-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x5"
:block/parent "page"
:block/order "a4"
:block/page "page"}
{:block/uuid block7-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x6"
:block/parent "page"
:block/order "a5"
:block/page "page"}]
:move-blocks-concurrently-client1
[{:block/uuid block6-uuid
:block/parent [:block/uuid block3-uuid]
:block/order "a0"}
{:block/uuid block5-uuid
:block/parent [:block/uuid block6-uuid]
:block/order "a0"}
{:block/uuid block4-uuid
:block/parent [:block/uuid block2-uuid]
:block/order "a0"}
{:block/uuid block7-uuid
:block/parent [:block/uuid page3-uuid]
:block/order (db-order/gen-key "a0" "a1")}]
:move-blocks-concurrently-client2
[{:block/uuid block2-uuid
:block/order "a2V"}
{:block/uuid block5-uuid
:block/parent [:block/uuid block3-uuid]
:block/order "a0"}
{:block/uuid block6-uuid
:block/parent [:block/uuid block5-uuid]
:block/order "a0"}
{:block/uuid block4-uuid
:block/parent [:block/uuid block7-uuid]
:block/order "a0"}]
:step6-delete-blocks-client1-1
;; - 1
;; - 2
;; - 3
;; - 4
;; - 5
[{:db/id "page"
:block/uuid step6-page-uuid
:block/name "step6-delete-blocks"
:block/title "step6-delete-blocks"
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/type "page"}
{:db/id "b1"
:block/uuid step6-block1-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x1"
:block/parent "page"
:block/order "a0"
:block/page "page"}
{:db/id "b2"
:block/uuid step6-block2-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x2"
:block/parent "b1"
:block/order "a0"
:block/page "page"}
{:db/id "b3"
:block/uuid step6-block3-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x3"
:block/parent "page"
:block/order "a1"
:block/page "page"}
{:db/id "b4"
:block/uuid step6-block4-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x4"
:block/parent "b3"
:block/order "a0"
:block/page "page"}
{:db/id "b5"
:block/uuid step6-block5-uuid
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/title "x5"
:block/parent "b4"
:block/order "a0"
:block/page "page"}]
:step6-delete-blocks-client1-2
;; only block3 left
[[:db/retractEntity [:block/uuid step6-block1-uuid]]
[:db/retractEntity [:block/uuid step6-block2-uuid]]
[:db/retractEntity [:block/uuid step6-block4-uuid]]
[:db/retractEntity [:block/uuid step6-block5-uuid]]]})

File diff suppressed because one or more lines are too long

View File

@@ -1,82 +0,0 @@
(ns fixture
(:require [cljs.test :as t]
[const]
[datascript.core :as d]
[example]
[frontend.common.missionary :as c.m]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.db-listener]
[frontend.worker.state :as worker-state]
[helper]
[missionary.core :as m]))
(def graph-schema-version "0")
(defn- transact-graph-schema-version
[conn]
(d/transact! conn [{:db/ident :logseq.kv/schema-version
:kv/value graph-schema-version}]))
(def install-some-consts
{:before
(fn []
(reset! worker-state/*rtc-ws-url "wss://ws-dev.logseq.com/rtc-sync?token=%s"))})
(def install-example-db-fixture
{:before
(fn []
(prn :test-repo const/test-repo)
(swap! worker-state/*client-ops-conns assoc const/test-repo (d/create-conn client-op/schema-in-db))
(let [conn (d/conn-from-db example/example-db)]
(transact-graph-schema-version conn)
(swap! worker-state/*datascript-conns assoc const/test-repo conn)))
:after
(fn []
(swap! worker-state/*datascript-conns dissoc const/test-repo)
(swap! worker-state/*client-ops-conns dissoc const/test-repo))})
(def clear-test-remote-graphs-fixture
{:before
#(when const/is-client1?
(t/async
done
(c.m/run-task-throw
:clear-test-remote-graphs
(m/sp
(m/? helper/new-task--clear-all-test-remote-graphs)
(done)))))})
(def upload-example-graph-fixture
{:before
#(when const/is-client1?
(t/async
done
(c.m/run-task-throw
:upload-example-graph-fixture
(m/sp
(swap! worker-state/*datascript-conns dissoc const/downloaded-test-repo)
(swap! worker-state/*client-ops-conns assoc
const/downloaded-test-repo (d/create-conn client-op/schema-in-db))
(let [{:keys [graph-uuid]} (m/? helper/new-task--upload-example-graph)]
(assert (some? graph-uuid))
(m/? (helper/new-task--wait-creating-graph graph-uuid))
(println :uploaded-graph graph-uuid))
(done)))))})
(def build-conn-by-download-example-graph-fixture
{:before
#(t/async
done
(c.m/run-task-throw
:build-conn-by-download-example-graph-fixture
(m/sp
(swap! worker-state/*datascript-conns dissoc const/downloaded-test-repo)
(swap! worker-state/*client-ops-conns assoc
const/downloaded-test-repo (d/create-conn client-op/schema-in-db))
(let [graph-uuid (m/? helper/new-task--get-remote-example-graph-uuid)]
(assert (some? graph-uuid))
(m/? (helper/new-task--download-graph graph-uuid const/downloaded-test-graph-name)))
(done))))
:after
#(do (swap! worker-state/*datascript-conns dissoc const/downloaded-test-repo)
(swap! worker-state/*client-ops-conns dissoc const/downloaded-test-repo))})

View File

@@ -1,208 +0,0 @@
(ns helper
(:require [cljs.test :as t :refer [is]]
[const]
[datascript.core :as d]
[datascript.transit :as dt]
[fixture]
[frontend.common.missionary :as c.m]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.core :as rtc.core]
[frontend.worker.rtc.log-and-state :as rtc-log-and-state]
[frontend.worker.state :as worker-state]
[logseq.db :as ldb]
[logseq.db.common.order :as db-order]
[logseq.outliner.batch-tx :as batch-tx]
[meander.epsilon :as me]
[missionary.core :as m]))
(defn log
[& objs]
(apply println (if const/is-client1? "[client1]" "[client2]") objs))
(def new-task--upload-example-graph
(rtc.core/new-task--upload-graph const/test-token const/test-repo const/test-graph-name))
(defn new-task--wait-creating-graph
[graph-uuid]
(c.m/backoff
{}
(m/sp
(let [graphs (m/? (rtc.core/new-task--get-graphs const/test-token))
graph (some (fn [graph] (when (= graph-uuid (:graph-uuid graph)) graph)) graphs)]
(when-not graph
(throw (ex-info "graph not exist" {:graph-uuid graph-uuid})))
(log "waiting for graph " graph-uuid " finish creating")
(when (= "creating" (:graph-status graph))
(throw (ex-info "wait creating-graph" {:missionary/retry true})))))))
(def new-task--clear-all-test-remote-graphs
(m/sp
(let [graphs (m/? (rtc.core/new-task--get-graphs const/test-token))
test-graphs (filter (fn [graph]
(not= "deleting" (:graph-status graph)))
graphs)]
(doseq [graph test-graphs]
(m/? (rtc.core/new-task--delete-graph const/test-token (:graph-uuid graph) fixture/graph-schema-version))
(log :deleted-graph (:graph-name graph) (:graph-uuid graph))))))
(def new-task--get-remote-example-graph-uuid
(c.m/backoff
{}
(m/sp
(let [graphs (m/? (rtc.core/new-task--get-graphs const/test-token))
graph
(some (fn [graph]
(when (= const/test-graph-name (:graph-name graph))
graph))
graphs)]
(when (= "deleting" (:graph-status graph))
(throw (ex-info "example graph status is \"deleting\", check server's background-upload-graph log"
{:graph-name (:graph-name graph)
:graph-uuid (:graph-uuid graph)})))
(when-not graph
(throw (ex-info "wait remote-example-graph" {:missionary/retry true
:graphs graphs})))
(when (= "creating" (:graph-status graph))
(throw (ex-info "wait remote-example-graph (creating)" {:missionary/retry true
:graphs graphs})))
(:graph-uuid graph)))))
(defn new-task--download-graph
[graph-uuid graph-name]
(m/sp
(let [download-info-uuid (m/? (rtc.core/new-task--request-download-graph
const/test-token graph-uuid fixture/graph-schema-version))
result (m/? (rtc.core/new-task--wait-download-info-ready
const/test-token download-info-uuid graph-uuid fixture/graph-schema-version 60000))
{:keys [_download-info-uuid
download-info-s3-url
_download-info-tx-instant
_download-info-t
_download-info-created-at]} result]
(when (= result :timeout)
(throw (ex-info "wait download-info-ready timeout" {})))
(m/? (rtc.core/new-task--download-graph-from-s3
graph-uuid graph-name download-info-s3-url)))))
(defn get-downloaded-test-conn
[]
{:post [(some? %)]}
(worker-state/get-datascript-conn const/downloaded-test-repo))
(defn simplify-client-op
[client-op]
#_:clj-kondo/ignore
(me/find
client-op
[?op-type _ {:block-uuid ?block-uuid :av-coll [[!a !v _ !add] ...]}]
[?op-type ?block-uuid (map vector !a !v !add)]
[?op-type _ {:block-uuid ?block-uuid}]
[?op-type ?block-uuid]))
(defn new-task--wait-all-client-ops-sent
[& {:keys [timeout] :or {timeout 10000}}]
(m/sp
(let [r (m/? (m/timeout
(m/reduce (fn [_ v]
(when (and (= :rtc.log/push-local-update (:type v))
(empty? (client-op/get-all-block-ops const/downloaded-test-repo)))
(is (nil? (:ex-data v)))
(reduced v)))
rtc-log-and-state/rtc-log-flow)
timeout :timeout))]
(is (not= :timeout r)))))
(defn new-task--send-message-to-other-client
[message]
(m/sp
(let [conn (get-downloaded-test-conn)
message-page-id (:db/id (ldb/get-page @conn const/message-page-uuid))
sorted-blocks (when message-page-id
(ldb/sort-by-order (ldb/get-page-blocks @conn message-page-id)))
min-order (db-order/gen-key nil (:block/order (first sorted-blocks)))
tx-data [{:db/id "page"
:block/uuid const/message-page-uuid
:block/name "message-page"
:block/title "message-page"
:block/created-at 1725024677501
:block/updated-at 1725024677501
:block/type "page"}
{:block/uuid (random-uuid)
:block/parent "page"
:block/order min-order
:block/title (dt/write-transit-str message)
:block/page "page"
:block/updated-at 1724836490810
:block/created-at 1724836490810}]]
(batch-tx/with-batch-tx-mode conn {:e2e-test const/downloaded-test-repo :frontend.worker.pipeline/skip-store-conn true}
(d/transact! conn tx-data))
(m/? (new-task--wait-all-client-ops-sent))
(log :sent-message message))))
(defn new-task--wait-message-from-other-client
"Return a task that return message from other client"
[block-title-pred-fn & {:keys [retry-message retry-count] :or {retry-count 4}}]
(c.m/backoff
{:delay-seq (take retry-count c.m/delays)}
(m/sp
(let [conn (get-downloaded-test-conn)
message-page-id (:db/id (ldb/get-page @conn const/message-page-uuid))
first-block (when message-page-id
(first (ldb/sort-by-order (ldb/get-page-blocks @conn message-page-id))))
first-block-title (some->> (:block/title first-block) dt/read-transit-str)]
(when-not (and (some? first-block-title)
(block-title-pred-fn first-block-title))
(throw (ex-info (str "wait message from other client " retry-message) {:missionary/retry true})))
first-block-title))))
(defn new-task--client1-sync-barrier-1->2
[message]
(m/sp
(m/? (new-task--send-message-to-other-client (str message "-client1")))
(m/? (new-task--wait-message-from-other-client #(= (str message "-client2") %)))
(log "sync-barrier-1->2" message)))
(defn new-task--client2-sync-barrier-1->2
[message]
(m/sp
(m/? (new-task--wait-message-from-other-client #(= (str message "-client1") %)))
(m/? (new-task--send-message-to-other-client (str message "-client2")))
(log "sync-barrier-1->2" message)))
(defn new-task--client1-sync-barrier-2->1
[message]
(m/sp
(m/? (new-task--wait-message-from-other-client #(= (str message "-client2") %)))
(m/? (new-task--send-message-to-other-client (str message "-client1")))
(log "sync-barrier-2->1" message)))
(defn new-task--client2-sync-barrier-2->1
[message]
(m/sp
(m/? (new-task--send-message-to-other-client (str message "-client2")))
(m/? (new-task--wait-message-from-other-client #(= (str message "-client1") %)))
(log "sync-barrier-2->1" message)))
(defn transact!
[conn tx-data]
{:pre [(seq tx-data)]}
(batch-tx/with-batch-tx-mode conn {:e2e-test const/downloaded-test-repo :frontend.worker.pipeline/skip-store-conn true}
(d/transact! conn tx-data)))
(def new-task--stop-rtc
(m/sp
(rtc.core/rtc-stop)
(let [r (m/?
(m/timeout
(m/reduce
(fn [_ v]
(when (= :rtc.log/cancelled (:type v))
(log :debug-stop-rtc v)
(reduced v)))
rtc-log-and-state/rtc-log-flow)
3000
:timeout))]
(is (not= :timeout r))
;; sleep 0.1s to ensure *rtc-lock released
(m/? (m/sleep 100)))))