mirror of https://github.com/logseq/logseq
wip: undo redo editor cursor
parent
01382219ca
commit
81c267ed58
|
@ -688,18 +688,22 @@
|
|||
|
||||
(rtc-get-block-update-log
|
||||
[_this block-uuid]
|
||||
(transit/write transit-w (rtc-core/get-block-update-log (uuid block-uuid))))
|
||||
(ldb/write-transit-str (rtc-core/get-block-update-log (uuid block-uuid))))
|
||||
|
||||
(undo
|
||||
[_this repo page-block-uuid-str]
|
||||
(when-let [conn (worker-state/get-datascript-conn repo)]
|
||||
(undo-redo/undo repo (uuid page-block-uuid-str) conn))
|
||||
nil)
|
||||
(ldb/write-transit-str (undo-redo/undo repo (uuid page-block-uuid-str) conn))))
|
||||
|
||||
(redo
|
||||
[_this repo page-block-uuid-str]
|
||||
(when-let [conn (worker-state/get-datascript-conn repo)]
|
||||
(undo-redo/redo repo (uuid page-block-uuid-str) conn)))
|
||||
(ldb/write-transit-str (undo-redo/redo repo (uuid page-block-uuid-str) conn))))
|
||||
|
||||
(record-editor-info
|
||||
[_this repo page-block-uuid-str editor-info-str]
|
||||
(undo-redo/record-editor-info! repo (uuid page-block-uuid-str) (ldb/read-transit-str editor-info-str))
|
||||
nil)
|
||||
|
||||
(keep-alive
|
||||
[_this]
|
||||
|
|
|
@ -169,14 +169,13 @@
|
|||
(when repo
|
||||
(state/set-editor-last-input-time! repo (util/time-ms))))
|
||||
|
||||
;; TODO: remove retry
|
||||
(defn- edit-block-aux
|
||||
[repo block content text-range {:keys [container-id]}]
|
||||
(when block
|
||||
(state/clear-edit!)
|
||||
(let [container-id (or container-id
|
||||
@(:editor/container-id @state/state))]
|
||||
(state/set-editing! "" content block text-range {:container-id container-id}))
|
||||
(state/set-editing! (str "edit-block-" (:block/uuid block)) content block text-range {:container-id container-id}))
|
||||
(mark-last-input-time! repo)))
|
||||
|
||||
(defn sanity-block-content
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[goog.dom :as gdom]
|
||||
[frontend.db :as db]))
|
||||
[frontend.db :as db]
|
||||
[logseq.db :as ldb]))
|
||||
|
||||
(defn did-mount!
|
||||
[state]
|
||||
|
@ -18,7 +19,12 @@
|
|||
(when-let [element (gdom/getElement id)]
|
||||
;; TODO: check whether editor is visible, do less work
|
||||
(js/setTimeout #(util/scroll-editor-cursor element) 50))
|
||||
(state/update-tx-after-cursor-state!))
|
||||
|
||||
(let [^js worker @state/*db-worker
|
||||
page-id (:block/uuid (:block/page (db/entity (:db/id (state/get-edit-block)))))
|
||||
repo (state/get-current-repo)]
|
||||
(when page-id
|
||||
(.record-editor-info worker repo (str page-id) (ldb/write-transit-str (state/get-editor-info))))))
|
||||
state)
|
||||
|
||||
;; (defn will-remount!
|
||||
|
|
|
@ -2,40 +2,39 @@
|
|||
(:require [frontend.db :as db]
|
||||
[frontend.db.transact :as db-transact]
|
||||
[frontend.handler.editor :as editor]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.page :as page-util]
|
||||
[goog.dom :as gdom]
|
||||
[promesa.core :as p]))
|
||||
[promesa.core :as p]
|
||||
[logseq.db :as ldb]))
|
||||
|
||||
(defn restore-cursor!
|
||||
[{:keys [last-edit-block container pos end-pos]} undo?]
|
||||
(when (and container last-edit-block)
|
||||
#_:clj-kondo/ignore
|
||||
(when-let [container (gdom/getElement container)]
|
||||
(when-let [block-uuid (:block/uuid last-edit-block)]
|
||||
(when-let [block (db/pull [:block/uuid block-uuid])]
|
||||
(editor/edit-block! block (or (when undo? pos) end-pos)
|
||||
{:custom-content (:block/content block)}))))))
|
||||
[{:keys [editor-cursors undo?]}]
|
||||
(let [{:keys [block-uuid container-id start-pos end-pos]} (if undo? (first editor-cursors) (last editor-cursors))]
|
||||
(when-let [block (db/pull [:block/uuid block-uuid])]
|
||||
(editor/edit-block! block (or (when undo? start-pos) end-pos)
|
||||
{:container-id container-id
|
||||
:custom-content (:block/content block)}))))
|
||||
|
||||
(defn- get-route-data
|
||||
[route-match]
|
||||
(when (seq route-match)
|
||||
{:to (get-in route-match [:data :name])
|
||||
:path-params (:path-params route-match)
|
||||
:query-params (:query-params route-match)}))
|
||||
(comment
|
||||
(defn- get-route-data
|
||||
[route-match]
|
||||
(when (seq route-match)
|
||||
{:to (get-in route-match [:data :name])
|
||||
:path-params (:path-params route-match)
|
||||
:query-params (:query-params route-match)})))
|
||||
|
||||
(defn restore-app-state!
|
||||
[state]
|
||||
(let [route-match (:route-match state)
|
||||
current-route (:route-match @state/state)
|
||||
prev-route-data (get-route-data route-match)
|
||||
current-route-data (get-route-data current-route)]
|
||||
(when (and (not= prev-route-data current-route-data)
|
||||
prev-route-data)
|
||||
(route-handler/redirect! prev-route-data))
|
||||
(swap! state/state merge state)))
|
||||
(comment
|
||||
(defn restore-app-state!
|
||||
[state]
|
||||
(let [route-match (:route-match state)
|
||||
current-route (:route-match @state/state)
|
||||
prev-route-data (get-route-data route-match)
|
||||
current-route-data (get-route-data current-route)]
|
||||
(when (and (not= prev-route-data current-route-data)
|
||||
prev-route-data)
|
||||
(route-handler/redirect! prev-route-data))
|
||||
(swap! state/state merge state))))
|
||||
|
||||
(let [*last-request (atom nil)]
|
||||
(defn undo!
|
||||
|
@ -55,7 +54,9 @@
|
|||
(state/clear-editor-action!)
|
||||
(state/set-block-op-type! nil)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(reset! *last-request (.undo worker repo current-page-uuid-str))))))))))
|
||||
(reset! *last-request (.undo worker repo current-page-uuid-str))
|
||||
(p/let [result @*last-request]
|
||||
(restore-cursor! (ldb/read-transit-str result)))))))))))
|
||||
|
||||
(let [*last-request (atom nil)]
|
||||
(defn redo!
|
||||
|
@ -71,4 +72,6 @@
|
|||
(util/stop e)
|
||||
(state/clear-editor-action!)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(reset! *last-request (.redo worker repo current-page-uuid-str)))))))))
|
||||
(reset! *last-request (.redo worker repo current-page-uuid-str))
|
||||
(p/let [result @*last-request]
|
||||
(restore-cursor! (ldb/read-transit-str result))))))))))
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
[frontend.state :as state]
|
||||
[datascript.core :as d]
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.history :as history]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.util :as util]))
|
||||
|
||||
|
@ -20,11 +19,6 @@
|
|||
(fn [m] (assoc-in m [tx-id :before] editor-cursor)))
|
||||
(state/set-state! :history/tx-before-editor-cursor nil)))
|
||||
|
||||
(defn restore-cursor-and-app-state!
|
||||
[{:keys [editor-cursor app-state]} undo?]
|
||||
(history/restore-cursor! editor-cursor undo?)
|
||||
(history/restore-app-state! app-state))
|
||||
|
||||
(defn invoke-hooks
|
||||
[{:keys [_request-id tx-meta tx-data deleted-block-uuids affected-keys blocks]}]
|
||||
;; (prn :debug
|
||||
|
@ -72,10 +66,10 @@
|
|||
(state/set-state! :editor/next-edit-block nil)))
|
||||
|
||||
(react/refresh! repo affected-keys)
|
||||
(when-let [state (:ui/restore-cursor-state @state/state)]
|
||||
(when (or undo? redo?)
|
||||
(restore-cursor-and-app-state! state undo?)
|
||||
(state/set-state! :ui/restore-cursor-state nil)))
|
||||
;; (when-let [state (:ui/restore-cursor-state @state/state)]
|
||||
;; (when (or undo? redo?)
|
||||
;; (restore-cursor-and-app-state! state undo?)
|
||||
;; (state/set-state! :ui/restore-cursor-state nil)))
|
||||
|
||||
(state/set-state! :editor/start-pos nil)
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
[opts & body]
|
||||
`(let [test?# frontend.util/node-test?]
|
||||
(when (or test?# (db/request-finished?))
|
||||
(when (nil? @(:history/tx-before-editor-cursor @state/state))
|
||||
(state/set-state! :history/tx-before-editor-cursor (state/get-current-edit-block-and-position)))
|
||||
(let [ops# frontend.modules.outliner.op/*outliner-ops*]
|
||||
(let [ops# frontend.modules.outliner.op/*outliner-ops*
|
||||
editor-info# (state/get-editor-info)]
|
||||
(if ops#
|
||||
(do ~@body) ; nested transact!
|
||||
(binding [frontend.modules.outliner.op/*outliner-ops* (transient [])]
|
||||
|
@ -27,7 +26,9 @@
|
|||
(let [request-id# (state/get-worker-next-request-id)
|
||||
request# #(.apply-outliner-ops ^Object worker# (state/get-current-repo)
|
||||
(pr-str r#)
|
||||
(pr-str (assoc ~opts :request-id request-id#)))
|
||||
(pr-str (assoc ~opts
|
||||
:request-id request-id#
|
||||
:editor-info editor-info#)))
|
||||
response# (state/add-worker-request! request-id# request#)]
|
||||
|
||||
response#))))))))))
|
||||
|
|
|
@ -1966,7 +1966,7 @@ Similar to re-frame subscriptions"
|
|||
(set-selection-blocks! blocks direction)))
|
||||
|
||||
(defn set-editing!
|
||||
[edit-input-id content block cursor-range & {:keys [move-cursor? ref container-id property-block]
|
||||
[edit-input-id content block cursor-range & {:keys [move-cursor? container-id property-block]
|
||||
:or {move-cursor? true}}]
|
||||
(when-not (exists? js/process)
|
||||
(if (> (count content)
|
||||
|
@ -1975,11 +1975,7 @@ Similar to re-frame subscriptions"
|
|||
(when (first elements)
|
||||
(util/scroll-to-element (gobj/get (first elements) "id")))
|
||||
(exit-editing-and-set-selected-blocks! elements))
|
||||
(let [edit-input-id (if ref
|
||||
(or (some-> (gobj/get ref "id") (string/replace "ls-block" "edit-block"))
|
||||
edit-input-id)
|
||||
edit-input-id)]
|
||||
(when (and edit-input-id block
|
||||
(when (and edit-input-id block
|
||||
(or
|
||||
(publishing-enable-editing?)
|
||||
(not @publishing?)))
|
||||
|
@ -1995,12 +1991,12 @@ Similar to re-frame subscriptions"
|
|||
(if property-block
|
||||
(set-editing-block-id! [container-id (:block/uuid property-block) (:block/uuid block)])
|
||||
(set-editing-block-id! [container-id (:block/uuid block)]))
|
||||
(set-state! :editor/container-id container-id)
|
||||
(set-state! :editor/block block)
|
||||
(set-state! :editor/content content :path-in-sub-atom (:block/uuid block))
|
||||
(set-state! :editor/last-key-code nil)
|
||||
(set-state! :editor/set-timestamp-block nil)
|
||||
(set-state! :editor/cursor-range cursor-range)
|
||||
(set-state! :editor/container-id container-id)
|
||||
|
||||
(when-let [input (gdom/getElement edit-input-id)]
|
||||
(let [pos (count cursor-range)]
|
||||
|
@ -2011,7 +2007,7 @@ Similar to re-frame subscriptions"
|
|||
(cursor/move-cursor-to input pos))
|
||||
|
||||
(when (or (util/mobile?) (mobile-util/native-platform?))
|
||||
(set-state! :mobile/show-action-bar? false))))))))))
|
||||
(set-state! :mobile/show-action-bar? false)))))))))
|
||||
|
||||
(defn action-bar-open?
|
||||
[]
|
||||
|
@ -2364,3 +2360,11 @@ Similar to re-frame subscriptions"
|
|||
(defn get-next-container-id
|
||||
[]
|
||||
(swap! (:ui/container-id @state) inc))
|
||||
|
||||
(defn get-editor-info
|
||||
[]
|
||||
(when-let [edit-block (get-edit-block)]
|
||||
{:block-uuid (:block/uuid edit-block)
|
||||
:container-id @(:editor/container-id @state)
|
||||
:start-pos @(:editor/start-pos @state)
|
||||
:end-pos (get-edit-pos)}))
|
||||
|
|
|
@ -42,6 +42,9 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(sr/defkeyword ::update-block
|
||||
"when a block is updated, generate a ::update-block undo-op.")
|
||||
|
||||
(sr/defkeyword ::record-editor-info
|
||||
"record current editor and cursor")
|
||||
|
||||
(sr/defkeyword ::empty-undo-stack
|
||||
"return by undo, when no more undo ops")
|
||||
|
||||
|
@ -89,7 +92,14 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
[:block-origin-collapsed {:optional true} :boolean]
|
||||
[:block-origin-link {:optional true} [:maybe :uuid]]
|
||||
;; TODO: add more attrs
|
||||
]]]]))
|
||||
]]]
|
||||
[::record-editor-info
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:block-uuid :uuid]
|
||||
[:container-id :int]
|
||||
[:start-pos [:maybe :int]]
|
||||
[:end-pos [:maybe :int]]]]]]))
|
||||
|
||||
(def ^:private undo-ops-validator (m/validator [:sequential undo-op-schema]))
|
||||
|
||||
|
@ -126,6 +136,8 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(case (first op)
|
||||
::boundary [op]
|
||||
|
||||
::record-editor-info [op]
|
||||
|
||||
::insert-blocks
|
||||
(keep
|
||||
(fn [block-uuid]
|
||||
|
@ -161,7 +173,8 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(some? block-origin-tags) (assoc :block-origin-tags block-origin-tags)
|
||||
(some? block-origin-collapsed) (assoc :block-origin-collapsed block-origin-collapsed)
|
||||
;; block-origin-link's value maybe nil, so use contains as cond
|
||||
(contains? value-keys :block-origin-link) (assoc :block-origin-link block-origin-link))]]))))
|
||||
(contains? value-keys :block-origin-link) (assoc :block-origin-link block-origin-link))]])
|
||||
nil)))
|
||||
|
||||
(def ^:private apply-conj-vec (partial apply (fnil conj [])))
|
||||
|
||||
|
@ -240,6 +253,10 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
|
||||
|
||||
(defmulti ^:private reverse-apply-op (fn [op _conn _repo] (first op)))
|
||||
(defmethod reverse-apply-op :default
|
||||
[_ _ _]
|
||||
nil)
|
||||
|
||||
(defmethod reverse-apply-op ::remove-block
|
||||
[op conn repo]
|
||||
(let [[_ {:keys [block-uuid block-entity-map]}] op
|
||||
|
@ -360,6 +377,10 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
|
||||
[:push-undo-redo {}])))))
|
||||
|
||||
(defmethod reverse-apply-op ::record-editor-info
|
||||
[_op _conn _repo]
|
||||
[:push-undo-redo {}])
|
||||
|
||||
(defn- sort&merge-ops
|
||||
[ops]
|
||||
(let [groups (group-by first ops)
|
||||
|
@ -394,8 +415,10 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(some-> *undo-redo-info-for-test* (reset! {:op op :tx (second r)}))
|
||||
(apply conj! redo-ops-to-push rev-ops)))))
|
||||
(when-let [rev-ops (not-empty (sort&merge-ops (persistent! redo-ops-to-push)))]
|
||||
(push-redo-ops repo page-block-uuid (cons boundary rev-ops)))
|
||||
nil)
|
||||
(push-redo-ops repo page-block-uuid (vec (cons boundary rev-ops))))
|
||||
(let [editor-infos (filter #(= ::record-editor-info (first %)) ops)]
|
||||
{:undo? true
|
||||
:editor-cursors (map second editor-infos)}))
|
||||
|
||||
(when (empty-undo-stack? repo page-block-uuid)
|
||||
(prn "No further undo information")
|
||||
|
@ -414,8 +437,10 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(some-> *undo-redo-info-for-test* (reset! {:op op :tx (second r)}))
|
||||
(apply conj! undo-ops-to-push rev-ops)))))
|
||||
(when-let [rev-ops (not-empty (sort&merge-ops (persistent! undo-ops-to-push)))]
|
||||
(push-undo-ops repo page-block-uuid (cons boundary rev-ops)))
|
||||
nil)
|
||||
(push-undo-ops repo page-block-uuid (vec (cons boundary rev-ops))))
|
||||
(let [editor-infos (filter #(= ::record-editor-info (first %)) ops)]
|
||||
{:redo? true
|
||||
:editor-cursors (map second editor-infos)}))
|
||||
|
||||
(when (empty-redo-stack? repo page-block-uuid)
|
||||
(prn "No further redo information")
|
||||
|
@ -502,19 +527,32 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
same-entity-datoms-coll))
|
||||
|
||||
(defn- generate-undo-ops
|
||||
[repo db-before db-after same-entity-datoms-coll id->attr->datom gen-boundary-op?]
|
||||
[repo db-before db-after same-entity-datoms-coll id->attr->datom gen-boundary-op? tx-meta]
|
||||
(when-let [page-block-uuid (find-page-block-uuid db-before db-after same-entity-datoms-coll)]
|
||||
(let [ops (mapcat (partial entity-datoms=>ops db-before db-after id->attr->datom) same-entity-datoms-coll)
|
||||
ops (sort&merge-ops ops)]
|
||||
ops (sort&merge-ops ops)
|
||||
editor-info (:editor-info tx-meta)
|
||||
ops' (if editor-info
|
||||
(cons [::record-editor-info editor-info] ops)
|
||||
ops)]
|
||||
(when (seq ops)
|
||||
(push-undo-ops repo page-block-uuid (if gen-boundary-op? (cons boundary ops) ops))))))
|
||||
(push-undo-ops repo page-block-uuid (if gen-boundary-op? (vec (cons boundary ops')) ops'))))))
|
||||
|
||||
(defmethod db-listener/listen-db-changes :gen-undo-ops
|
||||
[_ {:keys [_tx-data tx-meta db-before db-after
|
||||
repo id->attr->datom same-entity-datoms-coll]}]
|
||||
(when (:gen-undo-ops? tx-meta true)
|
||||
(generate-undo-ops repo db-before db-after same-entity-datoms-coll id->attr->datom
|
||||
(:gen-undo-boundary-op? tx-meta true))))
|
||||
(:gen-undo-boundary-op? tx-meta true)
|
||||
tx-meta)))
|
||||
|
||||
(defn record-editor-info!
|
||||
[repo page-block-uuid editor-info]
|
||||
(swap! (:undo/repo->page-block-uuid->undo-ops @worker-state/*state)
|
||||
update-in [repo page-block-uuid]
|
||||
(fn [stack]
|
||||
(when (seq stack)
|
||||
(conj (vec stack) [::record-editor-info editor-info])))))
|
||||
|
||||
;;; listen db changes and push undo-ops (ends)
|
||||
|
||||
|
|
Loading…
Reference in New Issue