mirror of
https://github.com/logseq/logseq.git
synced 2026-05-24 04:34:14 +00:00
enhance: add selection state to undo/redo history
This commit is contained in:
@@ -11,12 +11,21 @@
|
||||
|
||||
(defn- restore-cursor!
|
||||
[{:keys [editor-cursors block-content undo?]}]
|
||||
(let [{:keys [block-uuid container-id start-pos end-pos]} (if undo? (first editor-cursors) (or (last editor-cursors) (first editor-cursors)))
|
||||
(let [cursor (if undo?
|
||||
(first editor-cursors)
|
||||
(or (last editor-cursors) (first editor-cursors)))
|
||||
{:keys [selected-block-uuids selection-direction block-uuid container-id start-pos end-pos]} cursor
|
||||
selected-blocks (when (seq selected-block-uuids)
|
||||
(->> selected-block-uuids
|
||||
(mapcat util/get-blocks-by-id)
|
||||
vec))
|
||||
pos (if undo? (or start-pos end-pos) (or end-pos start-pos))]
|
||||
(when-let [block (db/pull [:block/uuid block-uuid])]
|
||||
(editor/edit-block! block pos
|
||||
{:container-id container-id
|
||||
:custom-content block-content}))))
|
||||
(if (seq selected-blocks)
|
||||
(state/exit-editing-and-set-selected-blocks! selected-blocks selection-direction)
|
||||
(when-let [block (db/pull [:block/uuid block-uuid])]
|
||||
(editor/edit-block! block pos
|
||||
{:container-id container-id
|
||||
:custom-content block-content})))))
|
||||
|
||||
(defn- restore-app-state!
|
||||
[state]
|
||||
|
||||
@@ -2044,11 +2044,18 @@ Similar to re-frame subscriptions"
|
||||
|
||||
(defn get-editor-info
|
||||
[]
|
||||
(when-let [edit-block (get-edit-block)]
|
||||
{:block-uuid (:block/uuid edit-block)
|
||||
:container-id (or @(:editor/container-id @state) :unknown-container)
|
||||
:start-pos @(:editor/start-pos @state)
|
||||
:end-pos (get-edit-pos)}))
|
||||
(let [selected-block-uuids (some-> (get-selection-block-ids) seq vec)
|
||||
selection-info (when selected-block-uuids
|
||||
{:selected-block-uuids selected-block-uuids
|
||||
:selection-direction (get-selection-direction)})]
|
||||
(if-let [edit-block (get-edit-block)]
|
||||
(cond-> {:block-uuid (:block/uuid edit-block)
|
||||
:container-id (or @(:editor/container-id @state) :unknown-container)
|
||||
:start-pos @(:editor/start-pos @state)
|
||||
:end-pos (get-edit-pos)}
|
||||
selection-info
|
||||
(merge selection-info))
|
||||
selection-info)))
|
||||
|
||||
(defn conj-block-ref!
|
||||
[ref-entity]
|
||||
|
||||
@@ -23,6 +23,20 @@
|
||||
(sr/defkeyword :gen-undo-ops?
|
||||
"tx-meta option, generate undo ops from tx-data when true (default true)"))
|
||||
|
||||
(def ^:private selection-editor-info-schema
|
||||
[:map
|
||||
[:selected-block-uuids [:sequential :uuid]]
|
||||
[:selection-direction {:optional true} [:maybe [:enum :up :down]]]])
|
||||
|
||||
(def ^:private editor-cursor-info-schema
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:container-id [:or :int [:enum :unknown-container]]]
|
||||
[:start-pos [:maybe :int]]
|
||||
[:end-pos [:maybe :int]]
|
||||
[:selected-block-uuids {:optional true} [:sequential :uuid]]
|
||||
[:selection-direction {:optional true} [:maybe [:enum :up :down]]]])
|
||||
|
||||
(def ^:private undo-op-item-schema
|
||||
(mu/closed-schema
|
||||
[:multi {:dispatch first}
|
||||
@@ -42,11 +56,9 @@
|
||||
|
||||
[::record-editor-info
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:container-id [:or :int [:enum :unknown-container]]]
|
||||
[:start-pos [:maybe :int]]
|
||||
[:end-pos [:maybe :int]]]]]
|
||||
[:or
|
||||
editor-cursor-info-schema
|
||||
selection-editor-info-schema]]]
|
||||
|
||||
[::ui-state
|
||||
[:cat :keyword :string]]]))
|
||||
@@ -316,10 +328,11 @@
|
||||
(push-opposite-op! repo undo? op')
|
||||
(let [editor-cursors (->> (filter #(= ::record-editor-info (first %)) op)
|
||||
(map second))
|
||||
block-content (:block/title (d/entity @conn [:block/uuid (:block-uuid
|
||||
(if undo?
|
||||
(first editor-cursors)
|
||||
(last editor-cursors)))]))]
|
||||
cursor (if undo?
|
||||
(first editor-cursors)
|
||||
(or (last editor-cursors) (first editor-cursors)))
|
||||
block-content (when-let [block-uuid (:block-uuid cursor)]
|
||||
(:block/title (d/entity @conn [:block/uuid block-uuid])))]
|
||||
{:undo? undo?
|
||||
:editor-cursors editor-cursors
|
||||
:block-content block-content}))
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
(ns frontend.handler.history-test
|
||||
(:require [clojure.test :refer [deftest is]]
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.editor :as editor]
|
||||
[frontend.handler.history :as history]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[logseq.db :as ldb]))
|
||||
|
||||
(deftest restore-cursor-and-state-prefers-ui-state-test
|
||||
@@ -51,3 +54,54 @@
|
||||
(is (= 1 (count @cursor-calls)))
|
||||
(is (nil? (:ui-state-str (first @cursor-calls))))
|
||||
(is (= false (:undo? (first @cursor-calls)))))))
|
||||
|
||||
(deftest restore-cursor-prefers-block-selection-test
|
||||
(let [selection-calls (atom [])
|
||||
edit-calls (atom [])]
|
||||
(with-redefs [util/get-blocks-by-id (fn [block-id]
|
||||
(case block-id
|
||||
#uuid "00000000-0000-0000-0000-000000000001" [:node-1]
|
||||
#uuid "00000000-0000-0000-0000-000000000002" [:node-2]
|
||||
nil))
|
||||
state/exit-editing-and-set-selected-blocks! (fn [blocks direction]
|
||||
(swap! selection-calls conj [blocks direction]))
|
||||
editor/edit-block! (fn [& args]
|
||||
(swap! edit-calls conj args))
|
||||
db/pull (constantly nil)]
|
||||
(#'history/restore-cursor!
|
||||
{:undo? true
|
||||
:editor-cursors [{:selected-block-uuids [#uuid "00000000-0000-0000-0000-000000000001"
|
||||
#uuid "00000000-0000-0000-0000-000000000002"]
|
||||
:selection-direction :down}]})
|
||||
(is (= [[[:node-1 :node-2] :down]]
|
||||
@selection-calls))
|
||||
(is (empty? @edit-calls)))))
|
||||
|
||||
(deftest restore-cursor-selection-falls-back-to-editor-cursor-test
|
||||
(let [selection-calls (atom [])
|
||||
edit-calls (atom [])
|
||||
block-uuid #uuid "00000000-0000-0000-0000-000000000003"]
|
||||
(with-redefs [util/get-blocks-by-id (constantly nil)
|
||||
state/exit-editing-and-set-selected-blocks! (fn [blocks direction]
|
||||
(swap! selection-calls conj [blocks direction]))
|
||||
editor/edit-block! (fn [& args]
|
||||
(swap! edit-calls conj args))
|
||||
db/pull (fn [[_lookup-k id]]
|
||||
(when (= block-uuid id)
|
||||
{:db/id 42
|
||||
:block/uuid block-uuid}))]
|
||||
(#'history/restore-cursor!
|
||||
{:undo? false
|
||||
:editor-cursors [{:selected-block-uuids [#uuid "00000000-0000-0000-0000-000000000001"]
|
||||
:selection-direction :up
|
||||
:block-uuid block-uuid
|
||||
:container-id 99
|
||||
:start-pos 1
|
||||
:end-pos 3}]})
|
||||
(is (empty? @selection-calls))
|
||||
(is (= [[{:db/id 42
|
||||
:block/uuid block-uuid}
|
||||
3
|
||||
{:container-id 99
|
||||
:custom-content nil}]]
|
||||
@edit-calls)))))
|
||||
|
||||
@@ -24,3 +24,18 @@
|
||||
{:shortcuts {:ui/toggle-brackets "t b"}}
|
||||
{:shortcuts {:editor/up ["ctrl+p" "up"]}}))
|
||||
"Map values get merged across configs"))
|
||||
|
||||
(deftest get-editor-info-includes-selection-when-not-editing-test
|
||||
(let [selected-ids [(random-uuid) (random-uuid)]]
|
||||
(with-redefs [state/get-edit-block (constantly nil)
|
||||
state/get-selection-block-ids (constantly selected-ids)
|
||||
state/get-selection-direction (constantly :down)]
|
||||
(is (= {:selected-block-uuids selected-ids
|
||||
:selection-direction :down}
|
||||
(state/get-editor-info))))))
|
||||
|
||||
(deftest get-editor-info-returns-nil-when-not-editing-and-no-selection-test
|
||||
(with-redefs [state/get-edit-block (constantly nil)
|
||||
state/get-selection-block-ids (constantly nil)
|
||||
state/get-selection-direction (constantly nil)]
|
||||
(is (nil? (state/get-editor-info)))))
|
||||
|
||||
@@ -86,6 +86,27 @@
|
||||
:outliner-ops [[:save-block [{:block/uuid block-uuid
|
||||
:block/title title} {}]]]}))))
|
||||
|
||||
(deftest undo-redo-selection-editor-info-roundtrip-test
|
||||
(testing "undo/redo result keeps block selection editor info when no cursor is recorded"
|
||||
(worker-undo-redo/clear-history! test-repo)
|
||||
(let [conn (worker-state/get-datascript-conn test-repo)
|
||||
{:keys [child-uuid]} (seed-page-parent-child!)
|
||||
selection-info {:selected-block-uuids [child-uuid]
|
||||
:selection-direction :down}]
|
||||
(d/transact! conn
|
||||
[[:db/add [:block/uuid child-uuid] :block/title "selection-history"]]
|
||||
(local-tx-meta
|
||||
{:outliner-op :save-block
|
||||
:undo-redo/editor-info selection-info
|
||||
:outliner-ops [[:save-block [{:block/uuid child-uuid
|
||||
:block/title "selection-history"} {}]]]}))
|
||||
(let [undo-result (worker-undo-redo/undo test-repo)]
|
||||
(is (= [selection-info] (:editor-cursors undo-result)))
|
||||
(is (nil? (:block-content undo-result))))
|
||||
(let [redo-result (worker-undo-redo/redo test-repo)]
|
||||
(is (= [selection-info] (:editor-cursors redo-result)))
|
||||
(is (nil? (:block-content redo-result)))))))
|
||||
|
||||
(defn- undo-all!
|
||||
[]
|
||||
(loop [results []]
|
||||
|
||||
Reference in New Issue
Block a user