diff --git a/deps/db/script/replay_sync_sqlite.cljs b/deps/db/script/replay_sync_sqlite.cljs index 3c01d26431..d9cd87d858 100644 --- a/deps/db/script/replay_sync_sqlite.cljs +++ b/deps/db/script/replay_sync_sqlite.cljs @@ -28,8 +28,6 @@ (def sqlite (if (find-ns 'nbb.core) (aget sqlite3 "default") sqlite3)) -(def canonical-transact-op [[:transact nil]]) - (def cli-spec {:help {:alias :h :desc "Show help"} @@ -297,10 +295,7 @@ (defn usable-history-ops [ops] - (let [ops' (some-> ops seq vec)] - (when (and (seq ops') - (not= canonical-transact-op ops')) - ops'))) + ops) (defn entity-id->block-uuid [db id] @@ -725,21 +720,14 @@ :outliner-op (:outliner-op local-tx)})))) (defn rebase-history-ops - [mode local-tx] + [_mode local-tx] (let [forward-outliner-ops (seq (:forward-outliner-ops local-tx)) inverse-outliner-ops (seq (:inverse-outliner-ops local-tx)) - fallback-forward-ops (when (and (nil? forward-outliner-ops) - (seq (:tx local-tx)) - (if (= mode :legacy) - (= :rebase (:outliner-op local-tx)) - true)) - canonical-transact-op) - forward-ops (or forward-outliner-ops fallback-forward-ops) - inverse-ops (or inverse-outliner-ops - (when forward-ops canonical-transact-op))] + forward-ops forward-outliner-ops + inverse-ops inverse-outliner-ops] {:forward-ops forward-ops :inverse-ops inverse-ops - :fallback? (boolean fallback-forward-ops)})) + :fallback? false})) (defn transact-remote-txs! [conn remote-rows] @@ -820,13 +808,8 @@ (let [{:keys [forward-ops inverse-ops fallback?]} (rebase-history-ops mode local-tx)] (if (seq forward-ops) (try - (if (= canonical-transact-op (vec forward-ops)) - (when-let [tx-data (seq (:tx local-tx))] - (ldb/transact! conn tx-data {:outliner-op :transact - :reapply-local? true - :tx-id (:tx-id local-tx)})) - (doseq [op forward-ops] - (replay-canonical-outliner-op! conn op rebase-db-before))) + (doseq [op forward-ops] + (replay-canonical-outliner-op! conn op rebase-db-before)) {:tx-id (:tx-id local-tx) :status :rebased :fallback? fallback? diff --git a/deps/outliner/src/logseq/outliner/op.cljs b/deps/outliner/src/logseq/outliner/op.cljs index ad4f27ea1a..06c06e8aee 100644 --- a/deps/outliner/src/logseq/outliner/op.cljs +++ b/deps/outliner/src/logseq/outliner/op.cljs @@ -10,7 +10,8 @@ [logseq.outliner.property :as outliner-property] [logseq.outliner.recycle :as outliner-recycle] [logseq.outliner.transaction :as outliner-tx] - [malli.core :as m])) + [malli.core :as m] + [logseq.outliner.op.construct :as op-construct])) (def ^:private ^:large-vars/data-var op-schema [:multi {:dispatch first} @@ -398,12 +399,13 @@ (defn apply-ops! [conn ops opts] (assert (ops-validator ops) ops) - (let [single-op-outliner-op (when (= 1 (count ops)) + (let [semantic-ops (filter (fn [op] (get op-construct/semantic-outliner-ops (first op))) ops) + single-op-outliner-op (when (= 1 (count ops)) (first (first ops))) opts' (cond-> (assoc opts :transact-opts {:conn conn} :local-tx? true - :outliner-ops ops + :outliner-ops semantic-ops :db-sync/tx-id (or (:db-sync/tx-id opts) (random-uuid))) (and single-op-outliner-op (nil? (:outliner-op opts))) diff --git a/deps/outliner/src/logseq/outliner/op/construct.cljc b/deps/outliner/src/logseq/outliner/op/construct.cljc index 8be0cf72e8..7a392ce314 100644 --- a/deps/outliner/src/logseq/outliner/op/construct.cljc +++ b/deps/outliner/src/logseq/outliner/op/construct.cljc @@ -41,7 +41,6 @@ (def ^:api rebase-refs-key :block.temp/sync-rebase-refs) (def ^:api rebase-created-refs-key :block.temp/sync-created-refs) -(def ^:api canonical-transact-op [[:transact nil]]) (defn- stable-entity-ref [db x] @@ -936,14 +935,10 @@ (canonicalize-explicit-outliner-ops db tx-data explicit-forward-ops) (seq outliner-ops) - (if (every? (fn [[op]] - (contains? semantic-outliner-ops op)) - outliner-ops) - (canonicalize-explicit-outliner-ops db tx-data outliner-ops) - canonical-transact-op) + (canonicalize-explicit-outliner-ops db tx-data outliner-ops) - (contains? #{:transact :batch-import-edn} (:outliner-op tx-meta)) - canonical-transact-op))) + :else + nil))) (defn- unresolved-numeric-entity-id? [x] @@ -1052,11 +1047,6 @@ canonical-forward-outliner-ops (some-> canonical-forward-outliner-ops seq vec) _ (assert-no-stale-numeric-ids! db-after canonical-forward-outliner-ops :forward-outliner-ops) forward-outliner-ops canonical-forward-outliner-ops - forward-outliner-ops (when (seq forward-outliner-ops) - (if (and (> (count forward-outliner-ops) 1) - (some (fn [[op]] (= :transact op)) forward-outliner-ops)) - canonical-transact-op - forward-outliner-ops)) built-inverse-outliner-ops (some-> (build-strict-inverse-outliner-ops db-before db-after tx-data forward-outliner-ops) seq vec) @@ -1081,11 +1071,6 @@ (nil? explicit-inverse-outliner-ops) nil - ;; Treat explicit transact placeholder as "no semantic inverse". - ;; Keep nil so semantic replay must fail-fast when required. - (= canonical-transact-op explicit-inverse-outliner-ops) - nil - :else explicit-inverse-outliner-ops) inverse-outliner-ops (some-> inverse-outliner-ops seq vec) diff --git a/deps/outliner/src/logseq/outliner/page.cljs b/deps/outliner/src/logseq/outliner/page.cljs index 730869e293..56ab7fa337 100644 --- a/deps/outliner/src/logseq/outliner/page.cljs +++ b/deps/outliner/src/logseq/outliner/page.cljs @@ -121,7 +121,7 @@ [:delete-page [page-uuid {:deleted-by-uuid deleted-by-uuid :now-ms now-ms}]]) rename? - (assoc :real-outliner-op :rename-page))] + (assoc :source-outliner-op :rename-page))] ;; TODO: maybe we should add $$$favorites to built-in pages? (cond (or (ldb/built-in? page) (ldb/hidden? page)) diff --git a/deps/outliner/test/logseq/outliner/op_construct_test.cljs b/deps/outliner/test/logseq/outliner/op_construct_test.cljs index b2284edceb..5052b5093b 100644 --- a/deps/outliner/test/logseq/outliner/op_construct_test.cljs +++ b/deps/outliner/test/logseq/outliner/op_construct_test.cljs @@ -38,22 +38,6 @@ (is (= [[:delete-page [page-uuid {}]]] inverse-outliner-ops))))) -(deftest derive-history-outliner-ops-collapses-mixed-stream-to-transact-placeholder-test - (testing "mixed semantic/non-semantic ops collapse to transact placeholder" - (let [conn (db-test/create-conn-with-blocks - {:pages-and-blocks - [{:page {:block/title "page"} - :blocks [{:block/title "child"}]}]}) - child (db-test/find-block-by-content @conn "child") - tx-meta {:outliner-op :save-block - :outliner-ops [[:save-block [{:block/uuid (:block/uuid child) - :block/title "changed"} {}]] - [:transact nil]]} - {:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops @conn @conn [] tx-meta)] - (is (= [[:transact nil]] forward-outliner-ops)) - (is (nil? inverse-outliner-ops))))) - (deftest derive-history-outliner-ops-handles-replace-empty-target-insert-inverse-test (testing "replace-empty-target insert keeps source uuid and inverse deletes target placeholder" (let [conn (db-test/create-conn-with-blocks @@ -211,23 +195,6 @@ (is (= [:block/uuid target-uuid] (get-in forward-outliner-ops [0 1 1])))))) -(deftest derive-history-outliner-ops-delete-closed-value-resolves-value-id-from-tx-data-test - (testing "delete-closed-value is treated as raw transact placeholder" - (let [conn (db-test/create-conn-with-blocks - {:properties {:status {:logseq.property/type :default}} - :pages-and-blocks []}) - property-page (d/entity @conn :user.property/status) - property-id (:db/id property-page) - stale-value-id 8888888 - value-uuid (random-uuid) - tx-data [{:e stale-value-id :a :block/uuid :v value-uuid :added false}] - tx-meta {:outliner-op :delete-closed-value - :outliner-ops [[:delete-closed-value [property-id stale-value-id]]]} - {:keys [forward-outliner-ops]} - (op-construct/derive-history-outliner-ops @conn @conn tx-data tx-meta)] - (is (= op-construct/canonical-transact-op - forward-outliner-ops))))) - (deftest derive-history-outliner-ops-builds-delete-page-inverse-for-class-property-and-today-page-test (testing "delete-page inverse restores hard-retracted class/property/today pages with stable db/ident" (let [today (date-time-util/ms->journal-day (js/Date.)) @@ -287,11 +254,7 @@ page (db-test/find-page-by-title @conn "page") parent (db-test/find-block-by-content @conn "parent") child-a (db-test/find-block-by-content @conn "child-a") - child-b (db-test/find-block-by-content @conn "child-b") - prop-block-1 (db-test/find-block-by-content @conn "prop-block-1") - prop-block-2 (db-test/find-block-by-content @conn "prop-block-2") - class-id (:db/id (d/entity @conn :user.class/c1)) - property-id (:db/id (d/entity @conn :user.property/p1))] + child-b (db-test/find-block-by-content @conn "child-b")] (testing ":save-block" (let [{:keys [inverse-outliner-ops]} (op-construct/derive-history-outliner-ops @@ -355,63 +318,6 @@ (is (= [[:restore-recycled [(:block/uuid page)]]] inverse-outliner-ops)))) - (testing ":set-block-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :set-block-property - :outliner-ops [[:set-block-property [(:db/id prop-block-1) - :user.property/p1 - "new-value"]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":remove-block-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :remove-block-property - :outliner-ops [[:remove-block-property [(:db/id prop-block-1) - :user.property/p1]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":batch-set-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :batch-set-property - :outliner-ops [[:batch-set-property [[(:db/id prop-block-1) - (:db/id prop-block-2)] - :user.property/p1 - "new-value" - {}]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":batch-remove-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :batch-remove-property - :outliner-ops [[:batch-remove-property [[(:db/id prop-block-1) - (:db/id prop-block-2)] - :user.property/p1]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":class-add-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :class-add-property - :outliner-ops [[:class-add-property [class-id property-id]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":class-remove-property" - (let [{:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn @conn [] {:outliner-op :class-remove-property - :outliner-ops [[:class-remove-property [class-id property-id]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - (testing ":upsert-property" (let [property-ident :user.property/test-inverse expected-page-uuid (common-uuid/gen-uuid :db-ident-block-uuid property-ident) @@ -453,82 +359,6 @@ :logical-outdenting? nil}]]] inverse-outliner-ops))))) -(deftest derive-history-outliner-ops-property-history-blocks-undo-cleanup-test - (testing ":set-block-property now falls back to transact placeholder history" - (let [conn (db-test/create-conn-with-blocks - {:properties {:pnum {:logseq.property/type :number - :db/cardinality :db.cardinality/one}} - :pages-and-blocks - [{:page {:block/title "page"} - :blocks [{:block/title "task" - :build/properties {:pnum 1}}]}]}) - block (db-test/find-block-by-content @conn "task") - block-id (:db/id block) - property-id (:db/id (d/entity @conn :user.property/pnum)) - history-uuid (random-uuid) - {:keys [db-after tx-data]} - (d/with @conn - [[:db/add block-id :user.property/pnum 2] - {:db/id -1 - :block/uuid history-uuid - :logseq.property.history/block block-id - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2}] - {}) - {:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn - db-after - tx-data - {:outliner-op :set-block-property - :outliner-ops [[:set-block-property [block-id :user.property/pnum 2]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops)))) - - (testing ":batch-set-property now falls back to transact placeholder history" - (let [conn (db-test/create-conn-with-blocks - {:properties {:pnum {:logseq.property/type :number - :db/cardinality :db.cardinality/one}} - :pages-and-blocks - [{:page {:block/title "page"} - :blocks [{:block/title "task-1" - :build/properties {:pnum 1}} - {:block/title "task-2"}]}]}) - block-1 (db-test/find-block-by-content @conn "task-1") - block-2 (db-test/find-block-by-content @conn "task-2") - block-1-id (:db/id block-1) - block-2-id (:db/id block-2) - property-id (:db/id (d/entity @conn :user.property/pnum)) - history-uuid-1 (random-uuid) - history-uuid-2 (random-uuid) - {:keys [db-after tx-data]} - (d/with @conn - [[:db/add block-1-id :user.property/pnum 2] - [:db/add block-2-id :user.property/pnum 2] - {:db/id -1 - :block/uuid history-uuid-1 - :logseq.property.history/block block-1-id - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2} - {:db/id -2 - :block/uuid history-uuid-2 - :logseq.property.history/block block-2-id - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2}] - {}) - {:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn - db-after - tx-data - {:outliner-op :batch-set-property - :outliner-ops [[:batch-set-property [[block-1-id block-2-id] - :user.property/pnum - 2 - {}]]]})] - (is (= op-construct/canonical-transact-op forward-outliner-ops)) - (is (nil? inverse-outliner-ops))))) - (deftest derive-history-outliner-ops-direct-outdent-with-extra-moved-blocks-keeps-semantic-ops-test (testing "direct outdent keeps semantic indent-outdent op and inverse" (let [conn (db-test/create-conn-with-blocks @@ -557,13 +387,3 @@ {:parent-original nil :logical-outdenting? nil}]]] inverse-outliner-ops))))) - -(deftest build-history-action-metadata-non-semantic-outliner-op-does-not-throw-test - (testing "non-semantic outliner-op with transact placeholder should not fail strict semantic validation" - (let [conn (db-test/create-conn-with-blocks {:pages-and-blocks []}) - tx-meta {:outliner-op :restore-recycled - :outliner-ops [[:transact nil]]} - result (op-construct/derive-history-outliner-ops @conn @conn [] tx-meta)] - (is (= [[:transact nil]] - (:forward-outliner-ops result))) - (is (nil? (:inverse-outliner-ops result)))))) diff --git a/src/main/frontend/components/views.cljs b/src/main/frontend/components/views.cljs index 076789b04d..edc459552c 100644 --- a/src/main/frontend/components/views.cljs +++ b/src/main/frontend/components/views.cljs @@ -1877,7 +1877,8 @@ (cond-> {:page (:block/uuid page) :properties properties - :edit-block? false} + :edit-block? false + :outliner-op :create-view} auto-triggered? (assoc :custom-uuid view-block-id)))] (db/entity [:block/uuid (:block/uuid result)])))) diff --git a/src/main/frontend/handler/block.cljs b/src/main/frontend/handler/block.cljs index 579ff2b0c4..2aa3f78cb6 100644 --- a/src/main/frontend/handler/block.cljs +++ b/src/main/frontend/handler/block.cljs @@ -250,7 +250,7 @@ (p/do! (ui-outliner-tx/transact! {:outliner-op :move-blocks - :real-outliner-op :indent-outdent} + :source-outliner-op :indent-outdent} (when save-current-block (save-current-block)) (outliner-op/indent-outdent-blocks! (get-top-level-blocks blocks') indent? diff --git a/src/main/frontend/handler/editor.cljs b/src/main/frontend/handler/editor.cljs index fd0096629e..f2479cc521 100644 --- a/src/main/frontend/handler/editor.cljs +++ b/src/main/frontend/handler/editor.cljs @@ -594,12 +594,14 @@ (into new-block properties) new-block)] (ui-outliner-tx/transact! - {:outliner-op :insert-blocks} + (cond-> + {:outliner-op :insert-blocks} + (not= outliner-op :insert-blocks) + (assoc :source-outliner-op outliner-op)) (outliner-insert-block! config target-block new-block' {:sibling? sibling? :keep-uuid? true :ordered-list? ordered-list? - :outliner-op outliner-op :replace-empty-target? replace-empty-target?}))) (when edit-block? (if (and replace-empty-target? diff --git a/src/main/frontend/worker/sync/apply_txs.cljs b/src/main/frontend/worker/sync/apply_txs.cljs index ac307043e0..0eeabeb027 100644 --- a/src/main/frontend/worker/sync/apply_txs.cljs +++ b/src/main/frontend/worker/sync/apply_txs.cljs @@ -174,51 +174,14 @@ (when-let [queue (:asset-queue client)] (swap! queue (fn [prev] (p/then prev (fn [_] (task))))))) -(def ^:private canonical-transact-op op-construct/canonical-transact-op) - -(defn- contains-transact-op? - [ops] - (op-construct/contains-transact-op? ops)) - -(defn- explicit-transact-forward-op? - [tx-meta] - (let [explicit-forward-ops (or (some-> (:db-sync/forward-outliner-ops tx-meta) - seq - vec) - (some-> (:outliner-ops tx-meta) - seq - vec))] - (and (seq explicit-forward-ops) - (contains-transact-op? explicit-forward-ops)))) - (defn- derive-history-outliner-ops [db-before db-after tx-data tx-meta] - ;; Rebased txs can carry explicit forward ops like [[:transact nil]]. - ;; Keep them as raw-tx placeholders instead of forcing semantic canonicalization. - (if (explicit-transact-forward-op? tx-meta) - {:forward-outliner-ops canonical-transact-op - :inverse-outliner-ops (or (some-> (:db-sync/inverse-outliner-ops tx-meta) seq vec) - (some-> (:inverse-outliner-ops tx-meta) seq vec))} - (op-construct/derive-history-outliner-ops db-before db-after tx-data tx-meta))) + (op-construct/derive-history-outliner-ops db-before db-after tx-data tx-meta)) (defn- rebase-history-ops [local-tx] - (let [forward-outliner-ops (seq (:forward-outliner-ops local-tx)) - inverse-outliner-ops (seq (:inverse-outliner-ops local-tx)) - forward-ops (or forward-outliner-ops - (when (seq (:tx local-tx)) - canonical-transact-op)) - inverse-ops (or inverse-outliner-ops - (when (seq (:reversed-tx local-tx)) - canonical-transact-op) - (when forward-ops canonical-transact-op))] - {:forward-ops forward-ops - :inverse-ops inverse-ops})) - -(defn- semantic-op-entry? - [op-entry] - (and (sequential? op-entry) - (contains? op-construct/semantic-outliner-ops (first op-entry)))) + {:forward-ops (seq (:forward-outliner-ops local-tx)) + :inverse-ops (seq (:inverse-outliner-ops local-tx))}) (defn- normalize-tx-data-for-rebase [tx-data] @@ -734,11 +697,10 @@ :db-sync/tx-id (:tx-id local-tx) :db-sync/forward-outliner-ops forward-ops :db-sync/inverse-outliner-ops inverse-ops} - semantic-forward-ops? (and (seq forward-ops) - (every? semantic-op-entry? forward-ops)) - forward-ops' (if semantic-forward-ops? forward-ops - (let [tx-data (-> (:tx local-tx) normalize-tx-data-for-rebase)] - [[:transact [tx-data]]]))] + forward-ops' (if (seq forward-ops) + forward-ops + (let [tx-data (-> (:tx local-tx) normalize-tx-data-for-rebase)] + [[:transact [tx-data nil]]]))] (try (let [rebase-tx-report (ldb/batch-transact-with-temp-conn! diff --git a/src/main/frontend/worker/undo_redo.cljs b/src/main/frontend/worker/undo_redo.cljs index 9d283e66ff..fd103812a6 100644 --- a/src/main/frontend/worker/undo_redo.cljs +++ b/src/main/frontend/worker/undo_redo.cljs @@ -314,7 +314,8 @@ (true? local-tx?) outliner-op (not (false? (:gen-undo-ops? tx-meta))) - (not (:create-today-journal? tx-meta))) + (not (:create-today-journal? tx-meta)) + (not (contains? #{:create-view} (:source-outliner-op tx-meta)))) (let [all-ids (distinct (map :e tx-data)) retracted-ids (set (filter diff --git a/src/test/frontend/worker/db_sync_test.cljs b/src/test/frontend/worker/db_sync_test.cljs index 06aad6ff21..3b027e73e7 100644 --- a/src/test/frontend/worker/db_sync_test.cljs +++ b/src/test/frontend/worker/db_sync_test.cljs @@ -1,46 +1,46 @@ (ns frontend.worker.db-sync-test - (:require [cljs.test :refer [deftest is testing async]] - [clojure.set :as set] - [clojure.string :as string] - [datascript.core :as d] - [frontend.common.crypt :as crypt] - [frontend.worker-common.util :as worker-util] - [frontend.worker.handler.page :as worker-page] - [frontend.worker.pipeline :as worker-pipeline] - [frontend.worker.shared-service :as shared-service] - [frontend.worker.state :as worker-state] - [frontend.worker.sync :as db-sync] - [frontend.worker.sync.apply-txs :as sync-apply] - [frontend.worker.sync.assets :as sync-assets] - [frontend.worker.sync.client-op :as client-op] - [frontend.worker.sync.crypt :as sync-crypt] - [frontend.worker.sync.handle-message :as sync-handle-message] - [frontend.worker.sync.large-title :as sync-large-title] - [frontend.worker.sync.log-and-state :as sync-log-state] - [frontend.worker.sync.presence :as sync-presence] - [frontend.worker.sync.transport :as sync-transport] - [frontend.worker.sync.temp-sqlite :as sync-temp-sqlite] - [frontend.worker.sync.upload :as sync-upload] - [frontend.worker.undo-redo :as undo-redo] - [logseq.common.config :as common-config] - [logseq.common.util :as common-util] - [logseq.common.util.page-ref :as page-ref] - [logseq.db :as ldb] - [logseq.db-sync.checksum :as sync-checksum] - [logseq.db-sync.storage :as sync-storage] - [logseq.db-sync.worker.handler.sync :as sync-handler] - [logseq.db-sync.worker.ws :as ws] - [logseq.db.common.normalize :as db-normalize] - [logseq.db.common.sqlite :as common-sqlite] - [logseq.db.frontend.validate :as db-validate] - [logseq.db.sqlite.util :as sqlite-util] - [logseq.db.test.helper :as db-test] - [logseq.outliner.core :as outliner-core] - [logseq.outliner.op :as outliner-op] - [logseq.outliner.op.construct :as op-construct] - [logseq.outliner.page :as outliner-page] - [logseq.outliner.property :as outliner-property] - [promesa.core :as p])) + (:require + [cljs.test :refer [async deftest is testing]] + [clojure.set :as set] + [clojure.string :as string] + [datascript.core :as d] + [frontend.common.crypt :as crypt] + [frontend.worker-common.util :as worker-util] + [frontend.worker.handler.page :as worker-page] + [frontend.worker.pipeline :as worker-pipeline] + [frontend.worker.shared-service :as shared-service] + [frontend.worker.state :as worker-state] + [frontend.worker.sync :as db-sync] + [frontend.worker.sync.apply-txs :as sync-apply] + [frontend.worker.sync.assets :as sync-assets] + [frontend.worker.sync.client-op :as client-op] + [frontend.worker.sync.crypt :as sync-crypt] + [frontend.worker.sync.handle-message :as sync-handle-message] + [frontend.worker.sync.large-title :as sync-large-title] + [frontend.worker.sync.log-and-state :as sync-log-state] + [frontend.worker.sync.presence :as sync-presence] + [frontend.worker.sync.temp-sqlite :as sync-temp-sqlite] + [frontend.worker.sync.transport :as sync-transport] + [frontend.worker.sync.upload :as sync-upload] + [frontend.worker.undo-redo :as undo-redo] + [logseq.common.config :as common-config] + [logseq.common.util :as common-util] + [logseq.common.util.page-ref :as page-ref] + [logseq.db :as ldb] + [logseq.db-sync.checksum :as sync-checksum] + [logseq.db-sync.storage :as sync-storage] + [logseq.db-sync.worker.handler.sync :as sync-handler] + [logseq.db-sync.worker.ws :as ws] + [logseq.db.common.normalize :as db-normalize] + [logseq.db.common.sqlite :as common-sqlite] + [logseq.db.frontend.validate :as db-validate] + [logseq.db.sqlite.util :as sqlite-util] + [logseq.db.test.helper :as db-test] + [logseq.outliner.core :as outliner-core] + [logseq.outliner.op :as outliner-op] + [logseq.outliner.page :as outliner-page] + [logseq.outliner.property :as outliner-property] + [promesa.core :as p])) (def ^:private test-repo "test-db-sync-repo") (def ^:private local-tx-meta @@ -1206,8 +1206,6 @@ txs (mapcat :tx pending)] (is (seq pending)) (is (= :toggle-reaction (:outliner-op (first pending)))) - (is (= [[:transact nil]] - (:forward-outliner-ops (first pending)))) (is (some (fn [tx] (and (vector? tx) (= :db/add (first tx)) @@ -1366,22 +1364,6 @@ (finally (reset! ldb/*transact-invalid-callback prev-invalid-callback)))))))) -(deftest enqueue-local-tx-canonicalizes-batch-import-to-transact-test - (testing "batch-import-edn local tx persists as canonical transact op" - (let [{:keys [conn client-ops-conn]} (setup-parent-child) - tx-report (d/with @conn - [{:block/uuid (random-uuid) - :block/title "imported" - :block/tags :logseq.class/Page - :block/created-at 1760000000000 - :block/updated-at 1760000000000}] - (assoc local-tx-meta :outliner-op :batch-import-edn))] - (with-datascript-conns conn client-ops-conn - (fn [] - (db-sync/enqueue-local-tx! test-repo tx-report) - (let [{:keys [forward-outliner-ops]} (first (#'sync-apply/pending-txs test-repo))] - (is (= [[:transact nil]] forward-outliner-ops)))))))) - (deftest enqueue-local-tx-preserves-existing-tx-id-test (testing "local tx persistence reuses tx-id already attached to tx-meta" (let [{:keys [conn client-ops-conn child1]} (setup-parent-child) @@ -1629,56 +1611,6 @@ (is (= [[:block/uuid block-uuid]] (get-in forward-outliner-ops [1 1 0]))))))))) -(deftest apply-history-action-redo-fails-fast-on-transact-placeholder-test - (testing "redo ignores transact placeholder and replays semantic ops" - (let [{:keys [conn client-ops-conn child1]} (setup-parent-child) - tx-id (random-uuid) - child-uuid (:block/uuid child1) - before-title (:block/title (d/entity @conn (:db/id child1))) - semantic-title "semantic replay value" - raw-title "raw replay value" - forward-ops [[:save-block [{:block/uuid child-uuid - :block/title semantic-title} {}]] - [:transact nil]] - tx-data [[:db/add [:block/uuid child-uuid] :block/title raw-title]] - reversed-tx-data [[:db/add [:block/uuid child-uuid] :block/title before-title]]] - (with-datascript-conns conn client-ops-conn - (fn [] - (seed-client-op-txs! - test-repo - [{:db-sync/tx-id tx-id - :db-sync/pending? true - :db-sync/created-at (.now js/Date) - :db-sync/outliner-op :save-block - :db-sync/forward-outliner-ops forward-ops - :db-sync/normalized-tx-data tx-data - :db-sync/reversed-tx-data reversed-tx-data}]) - (is (= true - (:applied? (#'sync-apply/apply-history-action! test-repo tx-id false {})))) - (is (= semantic-title - (:block/title (d/entity @conn [:block/uuid child-uuid]))))))))) - -(deftest enqueue-local-tx-allows-explicit-transact-placeholder-forward-op-test - (testing "enqueue-local-tx should preserve explicit transact placeholder forward ops" - (let [{:keys [conn client-ops-conn child1]} (setup-parent-child) - child-id (:db/id child1) - tx-id (random-uuid) - tx-report (d/with @conn - [[:db/add child-id :block/title "placeholder replay"]] - (assoc local-tx-meta - :db-sync/tx-id tx-id - :db-sync/forward-outliner-ops [[:transact nil]] - :db-sync/inverse-outliner-ops nil - :outliner-op :toggle-reaction))] - (with-datascript-conns conn client-ops-conn - (fn [] - (db-sync/enqueue-local-tx! test-repo tx-report) - (let [pending (first (#'sync-apply/pending-txs test-repo))] - (is (= tx-id (:tx-id pending))) - (is (= [[:transact nil]] - (:forward-outliner-ops pending))) - (is (= [] (:inverse-outliner-ops pending))))))))) - (deftest apply-history-action-undo-delete-blocks-noops-when-target-missing-test (testing "undo delete-blocks should no-op when the target block is already missing" (let [{:keys [conn client-ops-conn child1]} (setup-parent-child) @@ -3941,39 +3873,6 @@ (set (map :db/ident (:block/tags block-restored))))) (is (= base-history-count restored-history-count))))))))) -(deftest derive-history-set-block-property-inverse-includes-property-history-cleanup-test - (testing "derive-history-outliner-ops falls back to transact placeholder for set-block-property" - (let [conn (db-test/create-conn-with-blocks - {:properties {:pnum {:logseq.property/type :number - :db/cardinality :db.cardinality/one}} - :pages-and-blocks - [{:page {:block/title "page1"} - :blocks [{:block/title "task" - :build/properties {:pnum 1}}]}]}) - block-before (db-test/find-block-by-content @conn "task") - block-id (:db/id block-before) - property-id (:db/id (d/entity @conn :user.property/pnum)) - history-uuid (random-uuid) - {:keys [db-after tx-data]} - (d/with @conn - [[:db/add block-id :user.property/pnum 2] - {:db/id -1 - :block/uuid history-uuid - :logseq.property.history/block block-id - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2}] - {}) - {:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn - db-after - tx-data - {:outliner-op :set-block-property - :outliner-ops [[:set-block-property [block-id :user.property/pnum 2]]]})] - (is (= op-construct/canonical-transact-op - forward-outliner-ops)) - (is (nil? inverse-outliner-ops))))) - (deftest pending-reversed-txs-for-batch-status-changes-restore-base-db-test (testing "fresh persisted reversed tx rows from repeated batch status changes should restore the base db" (let [conn (db-test/create-conn-with-blocks @@ -4017,51 +3916,6 @@ (set (map :db/ident (:block/tags block-restored))))) (is (= base-history-count restored-history-count))))))))) -(deftest derive-history-batch-set-property-inverse-includes-property-history-cleanup-test - (testing "derive-history-outliner-ops falls back to transact placeholder for batch-set-property" - (let [conn (db-test/create-conn-with-blocks - {:properties {:pnum {:logseq.property/type :number - :db/cardinality :db.cardinality/one}} - :pages-and-blocks - [{:page {:block/title "page1"} - :blocks [{:block/title "task-1" - :build/properties {:pnum 1}} - {:block/title "task-2"}]}]}) - block-1 (db-test/find-block-by-content @conn "task-1") - block-2 (db-test/find-block-by-content @conn "task-2") - property-id (:db/id (d/entity @conn :user.property/pnum)) - history-uuid-1 (random-uuid) - history-uuid-2 (random-uuid) - {:keys [db-after tx-data]} - (d/with @conn - [[:db/add (:db/id block-1) :user.property/pnum 2] - [:db/add (:db/id block-2) :user.property/pnum 2] - {:db/id -1 - :block/uuid history-uuid-1 - :logseq.property.history/block (:db/id block-1) - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2} - {:db/id -2 - :block/uuid history-uuid-2 - :logseq.property.history/block (:db/id block-2) - :logseq.property.history/property property-id - :logseq.property.history/scalar-value 2}] - {}) - {:keys [forward-outliner-ops inverse-outliner-ops]} - (op-construct/derive-history-outliner-ops - @conn - db-after - tx-data - {:outliner-op :batch-set-property - :outliner-ops [[:batch-set-property [[(:db/id block-1) - (:db/id block-2)] - :user.property/pnum - 2 - {}]]]})] - (is (= op-construct/canonical-transact-op - forward-outliner-ops)) - (is (nil? inverse-outliner-ops))))) - (deftest normalize-rebased-pending-tx-keeps-reconstructive-reverse-for-retract-entity-test (testing "rebased pending tx should keep non-empty reverse datoms even when forward tx collapses to retractEntity" (let [conn (db-test/create-conn-with-blocks diff --git a/src/test/frontend/worker/undo_redo_test.cljs b/src/test/frontend/worker/undo_redo_test.cljs index cb97d0806f..3c33a180aa 100644 --- a/src/test/frontend/worker/undo_redo_test.cljs +++ b/src/test/frontend/worker/undo_redo_test.cljs @@ -339,15 +339,12 @@ [[:db/add [:block/uuid child-uuid] :block/title "restored child"]] (local-tx-meta {:client-id "test-client" - :outliner-op :restore-recycled - :outliner-ops [[:transact nil]]})) + :outliner-op :restore-recycled})) (let [undo-op (last (get @worker-undo-redo/*undo-ops test-repo)) data (some #(when (= ::worker-undo-redo/db-transact (first %)) (second %)) undo-op)] (is (some? data)) - (is (= [[:transact nil]] - (:db-sync/forward-outliner-ops data))) (is (nil? (:db-sync/inverse-outliner-ops data))))))) (deftest undo-history-canonicalizes-insert-block-uuids-test