Selaa lähdekoodia

enhance: use block dom node for editing instead of container

Tienson Qin 2 vuotta sitten
vanhempi
sitoutus
d9ab81e00d

+ 67 - 39
src/main/frontend/components/block.cljs

@@ -795,7 +795,7 @@
 (defn- edit-parent-block [e config]
   (when-not (state/editing?)
     (.stopPropagation e)
-    (editor-handler/edit-block! config :max (:block/uuid config))))
+    (editor-handler/edit-block! config :max nil)))
 
 (declare block-container)
 
@@ -2157,7 +2157,7 @@
    (dom/closest target ".query-table")))
 
 (defn- block-content-on-mouse-down
-  [e block block-id content edit-input-id]
+  [e block block-id content edit-input-id ref]
   (let [repo (state/get-current-repo)]
     (when-not (> (count content) (state/block-content-max-length repo))
       (let [target (gobj/get e "target")
@@ -2210,7 +2210,8 @@
                               content
                               block
                               cursor-range
-                              false))]
+                              {:ref ref
+                               :move-cursor? false}))]
                    ;; wait a while for the value of the caret range
                     (if (util/ios?)
                       (f)
@@ -2306,7 +2307,7 @@
          (pv/property-value block property (get (:block/properties block) pid) {:icon? true})))])))
 
 (rum/defc block-content < rum/reactive
-  [config {:block/keys [uuid content properties scheduled deadline format pre-block?] :as block} edit-input-id block-id slide? selected?]
+  [config {:block/keys [uuid content properties scheduled deadline format pre-block?] :as block} edit-input-id block-id slide? selected? *ref]
   (let [repo (state/get-current-repo)
         content (or (:block/original-name block)
                     (property-util/remove-built-in-properties format content))
@@ -2337,7 +2338,7 @@
 
                 (not block-ref?)
                 (assoc mouse-down-key (fn [e]
-                                        (block-content-on-mouse-down e block block-id content edit-input-id))))]
+                                        (block-content-on-mouse-down e block block-id content edit-input-id @*ref))))]
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)
               :class (when selected? "select-none")
@@ -2446,7 +2447,7 @@
                                   (= (:block/uuid block) (:block/uuid (:block config))))
                  default-hide? (not (and current-block-page? (not embed-self?) (state/auto-expand-block-refs?)))]
              (assoc state ::hide-block-refs? (atom default-hide?))))}
-  [state config {:block/keys [uuid format] :as block} edit-input-id block-id edit? hide-block-refs-count? selected?]
+  [state config {:block/keys [uuid format] :as block} edit-input-id block-id edit? hide-block-refs-count? selected? *ref]
   (let [*hide-block-refs? (get state ::hide-block-refs?)
         hide-block-refs? (rum/react *hide-block-refs?)
         editor-box (get config :editor-box)
@@ -2494,8 +2495,8 @@
                                                             (:block/content block))]
                                             (editor-handler/clear-selection!)
                                             (editor-handler/unhighlight-blocks!)
-                                            (state/set-editing! edit-input-id content block ""))}})
-             (block-content config block edit-input-id block-id slide? selected?))]
+                                            (state/set-editing! edit-input-id content block "" {:ref @*ref}))}})
+             (block-content config block edit-input-id block-id slide? selected? *ref))]
 
            (when (and (not hide-block-refs-count?)
                       (not named?))
@@ -2526,9 +2527,11 @@
 (rum/defcs single-block-cp-inner < rum/reactive db-mixins/query
   {:init (fn [state]
            (assoc state
-                  ::init-blocks-container-id (atom nil)))}
+                  ::init-blocks-container-id (atom nil)
+                  ::ref (atom nil)))}
   [state block-uuid]
-  (let [uuid (if (string? block-uuid) (uuid block-uuid) block-uuid)
+  (let [*ref (::ref state)
+        uuid (if (string? block-uuid) (uuid block-uuid) block-uuid)
         *init-blocks-container-id (::init-blocks-container-id state)
         block-entity (db/entity [:block/uuid uuid])
         block-id (:db/id block-entity)
@@ -2551,7 +2554,7 @@
       [:div.single-block.ls-block
        {:class (str block-uuid)
         :id (str "ls-block-" blocks-container-id "-" block-uuid)}
-       (block-content-or-editor config block edit-input-id block-el-id edit? true false)])))
+       (block-content-or-editor config block edit-input-id block-el-id edit? true false *ref)])))
 
 (rum/defc single-block-cp
   [block-uuid]
@@ -2824,9 +2827,31 @@
      (let [top (.-top (.getBoundingClientRect ref))]
        (not (<= top (+ js/window.innerHeight 1000)))))))
 
-(rum/defc ^:large-vars/cleanup-todo block-container-inner < rum/reactive db-mixins/query
-  [state repo config* block {:keys [edit? edit-input-id navigating-block navigated?]}]
-  (let [ref? (:ref? config*)
+(rum/defcs ^:large-vars/cleanup-todo block-container-inner < rum/reactive db-mixins/query
+  {:init (fn [state]
+           (assoc state ::ref (or (:*ref (second (:rum/args state)))
+                                  (atom nil))))
+
+   :did-mount (fn [state]
+                (when-let [editing-node @(:editor/editing @state/state)]
+                  (let [ref @(::ref state)
+                        editing-prev-node (:editor/editing-prev-node @state/state)
+                        editing-parent-node (:editor/editing-parent-node @state/state)]
+                    (when (and (not= editing-node ref)
+                               (= (gobj/get ref "id") (.-id editing-node))
+                               (or
+                                ;; block indent
+                                (= editing-prev-node (util/rec-get-node (.-parentNode ref) "ls-block"))
+                                ;; block outdent
+                                (= editing-parent-node (.-previousSibling ref))))
+                      (state/set-editing-ref! ref))))
+                state)}
+  [inner-state state repo config* block {:keys [blocks-container-id navigating-block navigated?]}]
+  (let [*ref (::ref inner-state)
+        ref (rum/react *ref)
+        ref? (:ref? config*)
+        edit-input-id (str "edit-block-" blocks-container-id "-" (:block/uuid block))
+        edit? (state/sub-editing? ref)
         custom-query? (boolean (:custom-query? config*))
         ref-or-custom-query? (or ref? custom-query?)
         *navigating-block (get state ::navigating-block)
@@ -2868,6 +2893,7 @@
     [:div.ls-block
      (cond->
       {:id block-id
+       :ref #(when (nil? @*ref) (reset! *ref %))
        :data-refs data-refs
        :data-refs-self data-refs-self
        :data-collapsed (and collapsed? has-child?)
@@ -2933,7 +2959,7 @@
          (let [block (merge block (block/parse-title-and-body uuid (:block/format block) pre-block? content))
                hide-block-refs-count? (and (:embed? config)
                                            (= (:block/uuid block) (:embed-id config)))]
-           (block-content-or-editor config block edit-input-id block-id edit? hide-block-refs-count? selected?))])
+           (block-content-or-editor config block edit-input-id block-id edit? hide-block-refs-count? selected? *ref))])
 
       (when @*show-right-menu?
         (block-right-menu config block edit?))]
@@ -2941,10 +2967,10 @@
      (when (and (config/db-based-graph? repo) (not collapsed?))
        [:div.mt-1 {:style {:padding-left 29}}
         (db-properties-cp config
-                         block
-                         edit-input-id
-                         {:selected? selected?
-                          :in-block-container? true})])
+                          block
+                          edit-input-id
+                          {:selected? selected?
+                           :in-block-container? true})])
 
      (when-not (:hide-children? config)
        (let [children (db/sort-by-left (:block/_parent block) block)
@@ -3005,10 +3031,7 @@
                       (assoc config :original-block original-block)
                       config)
                     (assoc :blocks-container-id @*blocks-container-id))
-        edit-input-id (str "edit-block-" @*blocks-container-id "-" (:block/uuid block))
-        edit? (state/sub-editing? edit-input-id)
-        opts {:edit? edit?
-              :edit-input-id edit-input-id}]
+        opts {:blocks-container-id @*blocks-container-id}]
     (if unloaded?
       [:div.ls-block.flex-1.flex-col.rounded-sm {:style {:width "100%"}}
        [:div.flex.flex-row
@@ -3018,8 +3041,10 @@
         [:div.flex.flex-1
          [:span.opacity-70
           "Loading..."]]]]
-      (block-container-inner state repo config' block
-                             (merge opts {:navigating-block navigating-block :navigated? navigated?})))))
+      (rum/with-key
+        (block-container-inner state repo config' block
+                               (merge opts {:navigating-block navigating-block :navigated? navigated?}))
+        (str "block-inner" (:block/uuid block))))))
 
 
 (defn divide-lists
@@ -3401,7 +3426,7 @@
                                                (not= (select-keys (first (:rum/args old-state)) config-compare-keys)
                                                      (select-keys (first (:rum/args new-state)) config-compare-keys)))]
                       (boolean result)))}
-  [config item {:keys [top? bottom?]}]
+  [config item {:keys [top? bottom? *ref]}]
   (let [original-block item
         linked-block (:block/link item)
         item (or linked-block item)
@@ -3409,7 +3434,9 @@
                (not (:block-children? config))
                (assoc :block.temp/top? top?
                       :block.temp/bottom? bottom?))
-        config (assoc config :block/uuid (:block/uuid item))
+        config (assoc config
+                      :block/uuid (:block/uuid item)
+                      :*ref *ref)
         config' (if linked-block
                   (assoc config :original-block original-block)
                   config)]
@@ -3471,14 +3498,14 @@
   (let [*hidden? (::hidden? state)
         hidden? (rum/react *hidden?)
         *ref (::ref state)]
-    [:div {:ref (fn [r] (when-not @*ref (reset! *ref r)))
-           :key (str "item-"
-                     (:blocks-container-id config)
-                     "-"
-                     (:block/uuid item))}
+    [:<>
      (if (and hidden? (not (:disable-lazy-load? config)))
-       [:div {:style {:height 24}}]
-       (block-item-inner config item opts))]))
+       [:div {:key (str "item-"
+                    (:blocks-container-id config)
+                    "-"
+                    (:block/uuid item))}
+        {:style {:height 24}}]
+       (block-item-inner config item (assoc opts :*ref *ref)))]))
 
 (defn- block-list
   [config blocks]
@@ -3497,11 +3524,12 @@
   {:init (fn [state] (assoc state ::init-blocks-container-id (atom nil)))}
   [state blocks config]
   (let [*init-blocks-container-id (::init-blocks-container-id state)
-        blocks-container-id (if @*init-blocks-container-id
-                              @*init-blocks-container-id
-                              (let [id' (state/next-blocks-container-id)]
-                                (reset! *init-blocks-container-id id')
-                                id'))
+        ;; blocks-container-id (if @*init-blocks-container-id
+        ;;                       @*init-blocks-container-id
+        ;;                       (let [id' (state/next-blocks-container-id)]
+        ;;                         (reset! *init-blocks-container-id id')
+        ;;                         id'))
+        blocks-container-id 0
         config (assoc config :blocks-container-id blocks-container-id)
         doc-mode? (:document/mode? config)]
     (when (seq blocks)

+ 1 - 1
src/main/frontend/components/container.cljs

@@ -444,7 +444,7 @@
                  (state/set-editor-last-pos! pos)))
     :onStop (fn [_event]
               (when-let [block (get @(get @state/state :editor/block) :block/uuid)]
-                (editor-handler/edit-block! block :max (:block/uuid block))
+                (editor-handler/edit-block! block :max nil)
                 (when-let [input (state/get-input)]
                   (when-let [saved-cursor (state/get-editor-last-pos)]
                     (cursor/move-cursor-to input saved-cursor)))))}

+ 1 - 1
src/main/frontend/components/content.cljs

@@ -409,7 +409,6 @@
                          (util/stop e)
                          (editor-handler/reset-cursor-range! (gdom/getElement (str id)))
                          (state/set-edit-content! id content)
-                         (state/set-edit-input-id! id)
                          (when on-click
                            (on-click e))))]
         [:pre.cursor.content.pre-white-space
@@ -450,5 +449,6 @@
   (if hiccup
     [:div
      (hiccup-content id option)]
+    ;; TODO: remove this
     (let [format (gp-util/normalize-format format)]
       (non-hiccup-content id content on-click on-hide config format))))

+ 7 - 7
src/main/frontend/components/editor.cljs

@@ -142,7 +142,7 @@
             input (gdom/getElement id)]
         (when input
           (let [current-pos (cursor/pos input)
-                edit-content (or (state/sub :editor/content :path-in-sub-atom id) "")
+                edit-content (state/sub-edit-content)
                 sidebar? (in-sidebar? input)
                 q (or
                    (editor-handler/get-selected-text)
@@ -277,7 +277,7 @@
           input (gdom/getElement id)
           [id format] (:rum/args state)
           current-pos (cursor/pos input)
-          edit-content (state/sub :editor/content :path-in-sub-atom id)
+          edit-content (state/sub-edit-content)
           edit-block (state/get-edit-block)
           selected-text (editor-handler/get-selected-text)
           q (or
@@ -293,7 +293,7 @@
         input (gdom/getElement id)]
     (when input
       (let [current-pos (cursor/pos input)
-            edit-content (state/sub :editor/content :path-in-sub-atom id)
+            edit-content (state/sub-edit-content)
             q (or
                (when (>= (count edit-content) current-pos)
                  (subs edit-content pos current-pos))
@@ -336,7 +336,7 @@
     (when (and input
                (not (string/blank? property)))
       (let [current-pos (cursor/pos input)
-            edit-content (state/sub :editor/content :path-in-sub-atom id)
+            edit-content (state/sub-edit-content)
             start-idx (string/last-index-of (subs edit-content 0 current-pos)
                                             gp-property/colons)
             q (or
@@ -371,7 +371,7 @@
     (when-let [input (gdom/getElement id)]
       (let [pos          (state/get-editor-last-pos)
             current-pos  (cursor/pos input)
-            edit-content (or (state/sub [:editor/content id]) "")
+            edit-content (or (state/sub-edit-content) "")
             q            (or (editor-handler/get-selected-text)
                              (gp-util/safe-subs edit-content pos current-pos)
                              "")
@@ -385,7 +385,7 @@
                                            (let [prefix (str "```" chosen)
                                                  last-pattern (str "```" q)]
                                              (editor-handler/insert-command! id
-                                               prefix format {:last-pattern last-pattern})
+                                                                             prefix format {:last-pattern last-pattern})
                                              (commands/handle-step [:codemirror/focus])))
                             :on-enter    (fn []
                                            (state/clear-editor-action!)
@@ -718,7 +718,7 @@
   (shortcut/mixin :shortcut.handler/block-editing-only)
   lifecycle/lifecycle
   [state {:keys [format block parent-block]} id config]
-  (let [content (state/sub-edit-content id)
+  (let [content (state/sub-edit-content (:block/uuid block))
         heading-class (get-editor-style-class block content format)
         opts (cond->
                  {:id                id

+ 1 - 1
src/main/frontend/extensions/code.cljs

@@ -453,7 +453,7 @@
                              (util/stop e)
                              (state/clear-selection!)
                              (when-let [block (and (:block/uuid config) (into {} (db/get-block-by-uuid (:block/uuid config))))]
-                               (state/set-editing! id (.getValue editor) block nil false))))
+                               (state/set-editing! id (.getValue editor) block nil {:move-cursor? false}))))
         (.addEventListener element "touchstart"
                            (fn [e]
                              (.stopPropagation e)))

+ 69 - 57
src/main/frontend/handler/block.cljs

@@ -338,18 +338,10 @@
               (when (and near-by (check-node? near-by block-id))
                 near-by))))))))
 
-(defn- get-edit-input-id-with-block-id
-  [block-id direction retry-times current-container-id]
-  (let [container-id (when current-container-id
-                       (string/replace current-container-id "edit-block" "ls-block"))]
-
-    (when-let [target (if (and (< retry-times 2) container-id)
-                        (gdom/getElement container-id)
-                        (or (get-nearby-block-by-id block-id direction)
-                            (util/get-first-block-by-id block-id)))]
-      (string/replace (gobj/get target "id")
-                      "ls-block"
-                      "edit-block"))))
+(defn- get-block-node-by-id
+  [block-id direction]
+  (or (get-nearby-block-by-id block-id direction)
+      (util/get-first-block-by-id block-id)))
 
 (defn- text-range-by-lst-fst-line [content [direction pos]]
   (case direction
@@ -370,52 +362,72 @@
     (db-listener/clear-repo-persistent-job! repo)))
 
 (defn- edit-block-aux
-  [repo block content id text-range {:keys [direction retry-times]
-                                     :or {retry-times 0}
-                                     :as opts}]
-  (when (<= retry-times 10)
-    (let [block-id (:block/uuid block)
-          edit-input-id (if (uuid? id)
-                          (get-edit-input-id-with-block-id id direction retry-times nil)
-                          (let [id (str (subs id 0 (- (count id) 36)) block-id)]
-                            (string/replace id "ls-block" "edit-block")))]
-      (if edit-input-id
+  [repo block content block-node text-range {:keys [direction retry-times]
+                                             :or {retry-times 0}
+                                             :as opts}]
+  (when (and (<= retry-times 10) block)
+    (let [block-node block-node
+          block-id (:block/uuid block)
+          id-class (str "id" block-id)
+          next-edit-node (or
+                          (when (and direction block-node)
+                            (let [blocks (dom/by-class "ls-block")
+                                  idx (.indexOf blocks block-node)]
+                              (when idx
+                                (if (= direction :down)
+                                  (util/nth-safe blocks (inc idx))
+                                  (util/nth-safe blocks (dec idx))))))
+                          (when block-node
+                            (when-let [next (.-nextSibling block-node)]
+                              (when (dom/has-class? next id-class)
+                                next)))
+                          (when block-node
+                            (when-let [prev (.-previousSibling block-node)]
+                              (when (dom/has-class? prev id-class)
+                                prev)))
+                          (when-not block-node
+                            (get-block-node-by-id (:block/uuid block) direction)))]
+      (if next-edit-node
         (do
-          (state/set-editing! edit-input-id content block text-range)
+          (state/set-editing! "" content block text-range {:ref next-edit-node})
           (mark-last-input-time! repo))
-        (js/setTimeout (fn [] (edit-block-aux repo block content id text-range (update opts :retry-times inc)))
-                       5)))))
+        (js/setTimeout (fn [] (edit-block-aux repo block content block-node text-range (update opts :retry-times inc))) 5)))))
 
 (defn edit-block!
-  ([block pos id]
-   (edit-block! block pos id nil))
-  ([block pos id {:keys [custom-content tail-len _direction]
-                  :or {tail-len 0}
-                  :as opts}]
-   (when-not config/publishing?
-     (when-let [block-id (:block/uuid block)]
-       (let [repo (state/get-current-repo)
-             db-graph? (config/db-based-graph? repo)
-             block (or (db/entity [:block/uuid block-id]) block)
-             content (if (and db-graph? (:block/name block))
-                       (:block/original-name block)
-                       (or custom-content (:block/content block) ""))
-             content-length (count content)
-             text-range (cond
-                          (vector? pos)
-                          (text-range-by-lst-fst-line content pos)
-
-                          (and (> tail-len 0) (>= (count content) tail-len))
-                          (subs content 0 (- (count content) tail-len))
-
-                          (or (= :max pos) (<= content-length pos))
-                          content
-
-                          :else
-                          (subs content 0 pos))
-             content (if db-graph?
-                       content
-                       (-> (property-util/remove-built-in-properties (:block/format block) content)
-                           (drawer/remove-logbook)))]
-         (state/clear-selection!)
-         (edit-block-aux repo block content id text-range opts))))))
+  [block pos block-node & {:keys [custom-content tail-len _direction]
+                           :or {tail-len 0}
+                           :as opts}]
+  (when-not config/publishing?
+    (when-let [block-id (:block/uuid block)]
+      (let [repo (state/get-current-repo)
+            block-node (cond
+                         (uuid? block-node)
+                         nil
+                         (string? block-node)
+                         (gdom/getElement(string/replace block-node "edit-block" "ls-block"))
+                         :else
+                         block-node)
+            db-graph? (config/db-based-graph? repo)
+            block (or (db/entity [:block/uuid block-id]) block)
+            content (if (and db-graph? (:block/name block))
+                      (:block/original-name block)
+                      (or custom-content (:block/content block) ""))
+            content-length (count content)
+            text-range (cond
+                         (vector? pos)
+                         (text-range-by-lst-fst-line content pos)
+
+                         (and (> tail-len 0) (>= (count content) tail-len))
+                         (subs content 0 (- (count content) tail-len))
+
+                         (or (= :max pos) (<= content-length pos))
+                         content
+
+                         :else
+                         (subs content 0 pos))
+            content (if db-graph?
+                      content
+                      (-> (property-util/remove-built-in-properties (:block/format block) content)
+                          (drawer/remove-logbook)))]
+        (state/clear-selection!)
+        (edit-block-aux repo block content block-node text-range opts)))))

+ 31 - 53
src/main/frontend/handler/editor.cljs

@@ -286,7 +286,7 @@
          ;; Block has been tagged, so we need to edit the linked page now
          (when (and linked
                     (= (:db/id (state/get-edit-block)) (:db/id original)))
-           (edit-block! linked :max (:block/uuid linked) {})))
+           (edit-block! linked :max nil {})))
 
        ;; file based graph only
        ;; sanitized page name changed
@@ -464,11 +464,13 @@
               (not= :insert (state/get-editor-op)))
      (state/set-editor-op! :insert)
      (when-let [state (get-state)]
-       (let [{:keys [block value id config]} state
+       (let [{:keys [block value config]} state
              value (if (string? block-value) block-value value)
              block-id (:block/uuid block)
              block-self? (block-self-alone-when-insert? config block-id)
-             input (gdom/getElement (state/get-edit-input-id))
+             input-id (state/get-edit-input-id)
+             input (gdom/getElement input-id)
+             block-node (state/get-edit-block-node)
              selection-start (util/get-selection-start input)
              selection-end (util/get-selection-end input)
              [fst-block-text snd-block-text] (compute-fst-snd-block-text value selection-start selection-end)
@@ -497,7 +499,7 @@
                     {:ok-handler
                      (fn insert-new-block!-ok-handler [last-block]
                        (clear-when-saved!)
-                       (edit-block! last-block 0 (if original-block (:block/uuid last-block) id)))}))))
+                       (edit-block! last-block 0 (when-not original-block block-node)))}))))
    (state/set-editor-op! nil)))
 
 (defn api-insert-new-block!
@@ -577,13 +579,8 @@
             (when edit-block?
               (if (and replace-empty-target?
                        (string/blank? (:block/content last-block)))
-                ;; 20ms of waiting for DOM to load the block, to avoid race condition.
-                ;; It's ensuring good response under M1 pro
-                ;; Used to be 10ms before, but is causing occasional failure on M1 pro with a full page of blocks,
-                ;; or failing E2E with a small number of blocks.
-                ;; Should be related to the # of elements in page
-                (js/setTimeout #(edit-block! last-block :max (:block/uuid last-block)) 20)
-                (js/setTimeout #(edit-block! new-block :max (:block/uuid new-block)) 20)))
+                (edit-block! last-block :max nil)
+                (edit-block! new-block :max nil)))
             new-block))))))
 
 (defn insert-first-page-block-if-not-exists!
@@ -720,7 +717,6 @@
           (let [original-block (dom/attr sibling-block "originalblockid")
                 edit-block (some-> (:db/id (state/get-edit-block)) db/entity)
                 edit-block-has-refs? (some? (:block/_refs edit-block))
-                id (if original-block (:block/uuid block) id)
                 original-content (util/trim-safe
                                   (if (:block/name block)
                                     (:block/original-name block)
@@ -736,12 +732,13 @@
                      (if original-content
                        (gobj/get (utf8/encode original-content) "length")
                        0)
-                     0)]
-            (edit-block! (if edit-block-has-refs?
-                           (db/pull (:db/id edit-block))
-                           (db/pull (:db/id block)))
+                     0)
+                edit-target (if edit-block-has-refs?
+                              (db/pull (:db/id edit-block))
+                              (db/pull (:db/id block)))]
+            (edit-block! edit-target
                          pos
-                         id
+                         (state/get-edit-block-node)
                          {:custom-content new-value
                           :tail-len tail-len})
             {:prev-block block
@@ -790,16 +787,7 @@
                   (and prev-block (:block/name prev-block)
                        (not= (:db/id prev-block) (:db/id (:block/parent block)))
                        (model/hidden-page? (:block/page block))) ; embed page
-                  (let [target (or
-                                (some-> (model/get-block-last-direct-child (db/get-db) (:db/id prev-block))
-                                        db/entity)
-                                prev-block)]
-                    (outliner-core/move-blocks! [block] target (not= (:db/id target) (:db/id prev-block)))
-                    (when (:block/collapsed? prev-block)
-                      (expand-block! (:block/uuid prev-block)))
-                    ;; FIXME: save-block! will reset :block/parent && :block/left that have been modified by move-blocks! above,
-                    (util/schedule #(save-block! repo block value {:editor/op :delete}))
-                    (js/setTimeout #(edit-block! (db/pull (:db/id block)) :max (:block/uuid block) {}) 100))
+                  nil
 
                   concat-prev-block?
                   (if (seq (:block/_refs (db/entity (:db/id block))))
@@ -1152,7 +1140,7 @@
                           :block/uuid)]
       (let [pos (state/get-edit-pos)]
         (route-handler/redirect-to-page! id)
-        (util/schedule #(edit-block! {:block/uuid id} pos id))))
+        (util/schedule #(edit-block! {:block/uuid id} pos nil))))
     (js/window.history.forward)))
 
 (defn zoom-out!
@@ -1167,14 +1155,14 @@
                        (:block/uuid block-parent))]
             (do
               (route-handler/redirect-to-page! id)
-              (util/schedule #(edit-block! {:block/uuid block-id} :max block-id)))
+              (util/schedule #(edit-block! {:block/uuid block-id} :max nil)))
             (let [page-id (some-> (db/entity [:block/uuid block-id])
                                   :block/page
                                   :db/id)]
 
               (when-let [page-name (:block/name (db/entity page-id))]
                 (route-handler/redirect-to-page! page-name)
-                (util/schedule #(edit-block! {:block/uuid block-id} :max block-id))))))))
+                (util/schedule #(edit-block! {:block/uuid block-id} :max nil))))))))
     (js/window.history.back)))
 
 (defn cut-block!
@@ -1741,7 +1729,7 @@
               (util/schedule (fn []
                                (when-not (gdom/getElement input-id)
                                  ;; could be crossing containers
-                                 (edit-block! block pos (:block/uuid block))))))))
+                                 (edit-block! block pos nil)))))))
         (let [ids (state/get-selection-block-ids)]
           (when (seq ids)
             (let [lookup-refs (map (fn [id] [:block/uuid id]) ids)
@@ -1995,7 +1983,7 @@
      (when-let [last-block (last (:blocks result))]
        (clear-when-saved!)
        (let [last-block' (db/pull [:block/uuid (:block/uuid last-block)])]
-         (edit-block! last-block' :max (:block/uuid last-block')))))))
+         (edit-block! last-block' :max nil))))))
 
 (defn- nested-blocks
   [blocks]
@@ -2066,7 +2054,6 @@
          (state/set-block-op-type! nil)
          (edit-last-block-after-inserted! result))))))
 
-
 (defn- block-tree->blocks
   "keep-uuid? - maintain the existing :uuid in tree vec"
   [repo tree-vec format keep-uuid? page-name]
@@ -2246,7 +2233,7 @@
             :real-outliner-op :indent-outdent}
            (outliner-core/move-blocks! [block] target true))
           (when original-block
-            (util/schedule #(edit-block! block pos (:block/uuid block)))))))))
+            (util/schedule #(edit-block! block pos nil))))))))
 
 (defn- last-top-level-child?
   [{:keys [id]} current-node]
@@ -2565,6 +2552,7 @@
 (defn- move-cross-boundary-up-down
   [direction move-opts]
   (let [input (state/get-input)
+        input-id (when input (.-id input))
         line-pos (util/get-first-or-last-line-pos input)
         repo (state/get-current-repo)
         f (case direction
@@ -2579,13 +2567,12 @@
                       (string/trim value))
             (save-block! repo uuid value)))
 
-        (let [new-id (string/replace (gobj/get sibling-block "id") "ls-block" "edit-block")
-              new-uuid (cljs.core/uuid sibling-block-id)
+        (let [new-uuid (cljs.core/uuid sibling-block-id)
               block (db/pull repo '[*] [:block/uuid new-uuid])]
           (edit-block! block
                        (or (:pos move-opts)
-                        [direction line-pos])
-                       new-id
+                           [direction line-pos])
+                       (state/get-edit-block-node)
                        {:direction direction})))
       (case direction
         :up (cursor/move-cursor-to input 0)
@@ -2618,7 +2605,6 @@
   (let [up? (= :left direction)
         pos (if up? :max 0)
         {:block/keys [format uuid] :as block} (state/get-edit-block)
-        id (state/get-edit-input-id)
         repo (state/get-current-repo)
         editing-block (gdom/getElement (state/get-editing-block-dom-id))
         f (if up? util/get-prev-block-non-collapsed util/get-next-block-non-collapsed)
@@ -2635,7 +2621,7 @@
                       (string/trim value))
             (save-block! repo uuid value)))
         (let [block (db/pull repo '[*] [:block/uuid (cljs.core/uuid sibling-block-id)])]
-          (edit-block! block pos id))))))
+          (edit-block! block pos (state/get-edit-block-node)))))))
 
 (defn keydown-arrow-handler
   [direction]
@@ -2708,7 +2694,7 @@
          (delete-block-aux! next-block false)
          (save-block! repo edit-block' new-content {:editor/op :delete}))
         (let [block (if next-block-has-refs? next-block edit-block)]
-          (edit-block! block current-pos (:block/uuid block)))))))
+          (edit-block! block current-pos nil))))))
 
 (defn keydown-delete-handler
   [_e]
@@ -2827,11 +2813,6 @@
   (save-current-block!)
   (state/set-editor-op! :indent-outdent)
   (let [editor (state/get-input)
-        crossing-container? (when editor
-                              (or (and (not indent?) (outliner-core/get-current-editing-original-block))
-                                  (and indent?
-                                       (when-let [sibling (db-model/get-prev-sibling (db/get-db) (:db/id (state/get-edit-block)))]
-                                         (some? (:block/link sibling))))))
         pos (some-> editor cursor/pos)
         {:keys [block]} (get-state)]
     (when block
@@ -2839,9 +2820,7 @@
       (outliner-tx/transact!
        {:outliner-op :move-blocks
         :real-outliner-op :indent-outdent}
-       (outliner-core/indent-outdent-blocks! [block] indent?))
-      (when crossing-container?
-        (util/schedule #(edit-block! block (state/get-edit-pos) (:block/uuid block)))))
+       (outliner-core/indent-outdent-blocks! [block] indent?)))
     (state/set-editor-op! :nil)))
 
 (defn keydown-tab-handler
@@ -3312,12 +3291,11 @@
       (let [block    {:block/uuid block-id}
             block-id (-> selected-blocks
                          f
-                         (gobj/get "id")
-                         (string/replace "ls-block" "edit-block"))
+                         (gobj/get "id"))
             left?    (= direction :left)]
         (edit-block! block
-                    (if left? 0 :max)
-                    block-id)))))
+                     (if left? 0 :max)
+                     (when block-id (gdom/getElement block-id)))))))
 
 (defn shortcut-left-right [direction]
   (fn [e]

+ 1 - 1
src/main/frontend/handler/editor/lifecycle.cljs

@@ -9,7 +9,7 @@
 (defn did-mount!
   [state]
   (let [[{:keys [block-parent-id]} id] (:rum/args state)
-        content (get @(get @state/state :editor/content) id)]
+        content (state/get-edit-content)]
     (when block-parent-id
       (state/set-editing-block-dom-id! block-parent-id))
     ;; FIXME: remove ugly :editor/property-triggered-by-click?

+ 1 - 1
src/main/frontend/handler/page.cljs

@@ -195,7 +195,7 @@
 (defn on-chosen-handler
   [input id _q pos format]
   (let [current-pos (cursor/pos input)
-        edit-content (state/sub :editor/content :path-in-sub-atom id)
+        edit-content (state/get-edit-content)
         action (state/get-editor-action)
         hashtag? (= action :page-search-hashtag)
         q (or

+ 80 - 65
src/main/frontend/state.cljs

@@ -587,11 +587,11 @@ Similar to re-frame subscriptions"
       :else    (util/react (rum/cursor state ks)))))
 
 (defn sub-editing?
-  [editor-id]
-  (when editor-id
+  [block-node]
+  (when block-node
     (rum/react
-     (rum/derived-atom [(:editor/editing @state)] [:ui/editing editor-id]
-                       (fn [id] (= editor-id id))))))
+     (rum/derived-atom [(:editor/editing @state)] [:ui/editing block-node]
+       (fn [editing-node] (= editing-node block-node))))))
 
 (defn sub-config
   "Sub equivalent to get-config which should handle all sub user-config access"
@@ -663,10 +663,6 @@ Similar to re-frame subscriptions"
   []
   (get-in (sub-config) [:default-home :page] ""))
 
-(defn sub-edit-content
-  [id]
-  (sub :editor/content :path-in-sub-atom id))
-
 (defn- get-selected-block-ids
   [blocks]
   (->> blocks
@@ -924,6 +920,10 @@ Similar to re-frame subscriptions"
   []
   (:editor/set-timestamp-block @state))
 
+(defn get-edit-block
+  []
+  @(get @state :editor/block))
+
 (defn set-edit-content!
   ([input-id value] (set-edit-content! input-id value true))
   ([input-id value set-input-value?]
@@ -931,18 +931,24 @@ Similar to re-frame subscriptions"
      (when set-input-value?
        (when-let [input (gdom/getElement input-id)]
          (util/set-change-value input value)))
-     (update-state! :editor/content (fn [m]
-                                      (assoc m input-id value))))))
+     (set-state! :editor/content value :path-in-sub-atom
+                 (or (:block/uuid (get-edit-block)) input-id)))))
 
 (defn get-edit-input-id
   []
-  @(:editor/editing @state))
+  (when-let [node @(:editor/editing @state)]
+    (some-> (dom/sel1 node "textarea")
+            (gobj/get "id"))))
 
 (defn get-input
   []
   (when-let [id (get-edit-input-id)]
     (gdom/getElement id)))
 
+(defn get-edit-block-node
+  []
+  @(:editor/editing @state))
+
 (defn editing?
   []
   (let [input (get-input)]
@@ -950,7 +956,15 @@ Similar to re-frame subscriptions"
 
 (defn get-edit-content
   []
-  (get @(:editor/content @state) (get-edit-input-id)))
+  (when-let [id (:block/uuid (get-edit-block))]
+    (get @(:editor/content @state) id)))
+
+(defn sub-edit-content
+  ([]
+   (sub-edit-content (:block/uuid (get-edit-block))))
+  ([block-id]
+   (when block-id
+     (sub :editor/content {:path-in-sub-atom block-id}))))
 
 (defn get-cursor-range
   []
@@ -1023,10 +1037,6 @@ Similar to re-frame subscriptions"
   []
   (set-state! :editor/action nil))
 
-(defn set-edit-input-id!
-  [input-id]
-  (set-state! :editor/editing input-id))
-
 (defn get-edit-pos
   []
   (when-let [input (get-input)]
@@ -1217,10 +1227,6 @@ Similar to re-frame subscriptions"
     (doseq [item items]
       (set-state! [:ui/sidebar-collapsed-blocks item] collapsed?))))
 
-(defn get-edit-block
-  []
-  @(get @state :editor/block))
-
 (defn get-current-edit-block-and-position
   []
   (let [edit-input-id (get-edit-input-id)
@@ -1237,6 +1243,8 @@ Similar to re-frame subscriptions"
 (defn clear-edit!
   []
   (set-state! :editor/editing nil)
+  (set-state! :editor/editing-prev-node nil)
+  (set-state! :editor/editing-parent-node nil)
   (swap! state merge {:cursor-range    nil
                       :editor/last-saved-cursor nil})
   (set-state! :editor/content {})
@@ -1940,51 +1948,56 @@ Similar to re-frame subscriptions"
    (clear-edit!)
    (set-selection-blocks! blocks direction)))
 
+(defn set-editing-ref!
+  [ref]
+  (set-state! :editor/editing ref)
+  (when ref
+    (when-let [prev (.-previousSibling ref)]
+      (set-state! :editor/editing-prev-node prev))
+    (when-let [parent (util/rec-get-node (.-parentNode ref) "ls-block")]
+      (set-state! :editor/editing-parent-node parent))))
+
 (defn set-editing!
-  ([edit-input-id content block cursor-range]
-   (set-editing! edit-input-id content block cursor-range true))
-  ([edit-input-id content block cursor-range move-cursor?]
-   (if (> (count content)
-          (block-content-max-length (get-current-repo)))
-     (let [elements (array-seq (js/document.getElementsByClassName (str "id" (:block/uuid block))))]
-       (when (first elements)
-         (util/scroll-to-element (gobj/get (first elements) "id")))
-       (exit-editing-and-set-selected-blocks! elements))
-     (when (and edit-input-id block
-                (or
-                 (publishing-enable-editing?)
-                 (not @publishing?)))
-       (let [block-element (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block"))
-             container (util/get-block-container block-element)
-             block (if container
-                     (assoc block
-                            :block.temp/container (gobj/get container "id"))
-                     block)
-             content (string/trim (or content ""))]
-         (set-state! :editor/editing edit-input-id)
-         (swap! state
-                (fn [state]
-                  (-> state
-                      (assoc
-                       :editor/set-timestamp-block nil
-                       :cursor-range cursor-range))))
-         (set-state! :editor/block block)
-         (set-state! :editor/content content :path-in-sub-atom edit-input-id)
-         (set-state! :editor/last-key-code nil)
-
-         (when-let [input (gdom/getElement edit-input-id)]
-           (let [pos (count cursor-range)]
-             (when content
-               (util/set-change-value input content))
-
-             (when move-cursor?
-               (cursor/move-cursor-to input pos))
-
-             (when (or (util/mobile?) (mobile-util/native-platform?))
-               (set-state! :mobile/show-action-bar? false)))))))))
-
-(defn remove-watch-state [key]
-  (remove-watch state key))
+  [edit-input-id content block cursor-range & {:keys [move-cursor? ref]
+                                               :or {move-cursor? true}}]
+  (if (> (count content)
+         (block-content-max-length (get-current-repo)))
+    (let [elements (array-seq (js/document.getElementsByClassName (str "id" (:block/uuid block))))]
+      (when (first elements)
+        (util/scroll-to-element (gobj/get (first elements) "id")))
+      (exit-editing-and-set-selected-blocks! elements))
+    (when (and edit-input-id block
+               (or
+                (publishing-enable-editing?)
+                (not @publishing?)))
+      (let [block-element (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block"))
+            container (util/get-block-container block-element)
+            block (if container
+                    (assoc block
+                           :block.temp/container (gobj/get container "id"))
+                    block)
+            content (string/trim (or content ""))]
+        (set-editing-ref! ref)
+        (swap! state
+               (fn [state]
+                 (-> state
+                     (assoc
+                      :editor/set-timestamp-block nil
+                      :cursor-range cursor-range))))
+        (set-state! :editor/block block)
+        (set-state! :editor/content content :path-in-sub-atom (:block/uuid block))
+        (set-state! :editor/last-key-code nil)
+
+        (when-let [input (gdom/getElement edit-input-id)]
+          (let [pos (count cursor-range)]
+            (when content
+              (util/set-change-value input content))
+
+            (when move-cursor?
+              (cursor/move-cursor-to input pos))
+
+            (when (or (util/mobile?) (mobile-util/native-platform?))
+              (set-state! :mobile/show-action-bar? false))))))))
 
 (defn get-git-auto-commit-enabled?
   []
@@ -2281,7 +2294,9 @@ Similar to re-frame subscriptions"
 
 (defn next-blocks-container-id
   []
-  (swap! (:ui/blocks-container-id @state) inc))
+  0
+  ;; (swap! (:ui/blocks-container-id @state) inc)
+  )
 
 (defn set-page-properties-changed!
   [page-name]

+ 1 - 2
src/main/frontend/ui.cljs

@@ -960,8 +960,7 @@
   [state {:keys [fixed-position? open? in-editor? html] :as opts} child]
   (let [*mounted? (::mounted? state)
         manual (not= open? nil)
-        edit-id (state/sub :editor/editing)
-        editing-node (when edit-id (gdom/getElement edit-id))
+        editing-node (state/sub :editor/editing)
         editing? (some? editing-node)
         scrolling? (state/sub :ui/scrolling?)
         open? (if manual open? @*mounted?)