浏览代码

enhance: new lazy blocks implementation

1. GC blocks below viewport only
2. No long uses heavy react-intersection-observer
Tienson Qin 2 年之前
父节点
当前提交
f8f2f987c4

+ 163 - 154
src/main/frontend/components/block.cljs

@@ -2795,133 +2795,155 @@
       [block* result]
       [nil result])))
 
-(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*)
-        custom-query? (boolean (:custom-query? config*))
-        ref-or-custom-query? (or ref? custom-query?)
-        *navigating-block (get state ::navigating-block)
-        {:block/keys [uuid pre-block? refs content properties]} block
-        config (build-config config* block {:navigated? navigated? :navigating-block navigating-block})
-        level (:level config)
-        blocks-container-id (:blocks-container-id config)
-        heading? (pu/lookup properties :heading)
-        *control-show? (get state ::control-show?)
-        db-collapsed? (util/collapsed? block)
-        collapsed? (cond
-                     (or ref-or-custom-query? (root-block? config block))
-                     (state/sub-collapsed uuid)
-
-                     :else
-                     db-collapsed?)
-        breadcrumb-show? (:breadcrumb-show? config)
-        *show-left-menu? (::show-block-left-menu? state)
-        *show-right-menu? (::show-block-right-menu? state)
-        slide? (boolean (:slide? config))
-        doc-mode? (:document/mode? config)
-        embed? (:embed? config)
-        reference? (:reference? config)
-        whiteboard-block? (pu/shape-block? block)
-        block-id (str "ls-block-" blocks-container-id "-" uuid)
-        has-child? (first (:block/_parent (db/entity (:db/id block))))
-        top? (zero? (:idx config))
-        original-block (:original-block config)
-        attrs (on-drag-and-mouse-attrs block original-block uuid top? block-id *move-to)
-        children-refs (get-children-refs block)
-        data-refs (build-refs-data-value children-refs)
-        data-refs-self (build-refs-data-value refs)
-        card? (string/includes? data-refs-self "\"card\"")
-        review-cards? (:review-cards? config)
-        own-number-list? (:own-order-number-list? config)
-        order-list? (boolean own-number-list?)
-        selected? (when-not slide?
-                    (state/sub-block-selected? blocks-container-id uuid))]
-    [:div.ls-block
-     (cond->
-      {:id block-id
-       :data-refs data-refs
-       :data-refs-self data-refs-self
-       :data-collapsed (and collapsed? has-child?)
-       :class (str (str "id" uuid)      ; ID starts with a number can't be selected
-                   (when pre-block? " pre-block")
-                   (when (and card? (not review-cards?)) " shadow-md")
-                   (when selected? " selected")
-                   (when order-list? " is-order-list")
-                   (when (string/blank? content) " is-blank"))
-       :blockid (str uuid)
-       :haschild (str (boolean has-child?))}
-
-       original-block
-       (assoc :originalblockid (str (:block/uuid original-block)))
-
-       level
-       (assoc :level level)
-
-       (not slide?)
-       (merge attrs)
-
-       (or reference? embed?)
-       (assoc :data-transclude true)
-
-       embed?
-       (assoc :data-embed true)
+(defn- hide-block?
+  [ref]
+  (boolean
+   (when ref
+     (let [top (.-top (.getBoundingClientRect ref))]
+       (not (<= top (+ js/window.innerHeight 500)))))))
+
+(rum/defcs ^:large-vars/cleanup-todo block-container-inner < rum/reactive db-mixins/query
+  (rum/local nil ::ref)
+  (rum/local nil ::hidden?)
+  {:did-mount (fn [state]
+                (let [hide? (hide-block? @(::ref state))]
+                  (reset! (::hidden? state) hide?)
+                  state))}
+  [inner-state state repo config* block {:keys [edit? edit-input-id navigating-block navigated?]}]
+  (let [*hidden? (::hidden? inner-state)
+        *ref (::ref inner-state)
+        _scroll-top (state/sub :ui/main-container-scroll-top)]
+    (when-not (nil? @*hidden?)
+      (reset! *hidden? (hide-block? @*ref)))
+
+    [:div {:ref (fn [r] (when-not @*ref (reset! *ref r)))}
+     (if (and (not edit?) @*hidden?)
+       [:div {:style {:height 24}}]
+       (let [ref? (:ref? config*)
+             custom-query? (boolean (:custom-query? config*))
+             ref-or-custom-query? (or ref? custom-query?)
+             *navigating-block (get state ::navigating-block)
+             {:block/keys [uuid pre-block? refs content properties]} block
+             config (build-config config* block {:navigated? navigated? :navigating-block navigating-block})
+             level (:level config)
+             blocks-container-id (:blocks-container-id config)
+             heading? (pu/lookup properties :heading)
+             *control-show? (get state ::control-show?)
+             db-collapsed? (util/collapsed? block)
+             collapsed? (cond
+                          (or ref-or-custom-query? (root-block? config block))
+                          (state/sub-collapsed uuid)
 
-       custom-query?
-       (assoc :data-query true))
-
-     (when (and ref? breadcrumb-show?)
-       (breadcrumb config repo uuid {:show-page? false
-                                     :indent? true
-                                     :navigating-block *navigating-block}))
+                          :else
+                          db-collapsed?)
+             breadcrumb-show? (:breadcrumb-show? config)
+             *show-left-menu? (::show-block-left-menu? state)
+             *show-right-menu? (::show-block-right-menu? state)
+             slide? (boolean (:slide? config))
+             doc-mode? (:document/mode? config)
+             embed? (:embed? config)
+             reference? (:reference? config)
+             whiteboard-block? (pu/shape-block? block)
+             block-id (str "ls-block-" blocks-container-id "-" uuid)
+             has-child? (first (:block/_parent (db/entity (:db/id block))))
+             top? (zero? (:idx config))
+             original-block (:original-block config)
+             attrs (on-drag-and-mouse-attrs block original-block uuid top? block-id *move-to)
+             children-refs (get-children-refs block)
+             data-refs (build-refs-data-value children-refs)
+             data-refs-self (build-refs-data-value refs)
+             card? (string/includes? data-refs-self "\"card\"")
+             review-cards? (:review-cards? config)
+             own-number-list? (:own-order-number-list? config)
+             order-list? (boolean own-number-list?)
+             selected? (when-not slide?
+                         (state/sub-block-selected? blocks-container-id uuid))]
+         [:div.ls-block
+          (cond->
+           {:id block-id
+            :data-refs data-refs
+            :data-refs-self data-refs-self
+            :data-collapsed (and collapsed? has-child?)
+            :class (str (str "id" uuid)      ; ID starts with a number can't be selected
+                        (when pre-block? " pre-block")
+                        (when (and card? (not review-cards?)) " shadow-md")
+                        (when selected? " selected")
+                        (when order-list? " is-order-list")
+                        (when (string/blank? content) " is-blank"))
+            :blockid (str uuid)
+            :haschild (str (boolean has-child?))}
+
+            original-block
+            (assoc :originalblockid (str (:block/uuid original-block)))
+
+            level
+            (assoc :level level)
+
+            (not slide?)
+            (merge attrs)
+
+            (or reference? embed?)
+            (assoc :data-transclude true)
+
+            embed?
+            (assoc :data-embed true)
+
+            custom-query?
+            (assoc :data-query true))
+
+          (when (and ref? breadcrumb-show?)
+            (breadcrumb config repo uuid {:show-page? false
+                                          :indent? true
+                                          :navigating-block *navigating-block}))
 
      ;; only render this for the first block in each container
-     (when top?
-       (dnd-separator-wrapper block block-id slide? true false))
-
-     [:div.block-main-container.flex.flex-row.pr-2
-      {:class (if (and heading? (seq (:block/title block))) "items-baseline" "")
-       :on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
-       :on-touch-move (fn [event]
-                        (block-handler/on-touch-move event block uuid edit? *show-left-menu? *show-right-menu?))
-       :on-touch-end (fn [event]
-                       (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
-       :on-touch-cancel (fn [_e]
-                          (block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
-       :on-mouse-over (fn [e]
-                        (block-mouse-over e *control-show? block-id doc-mode?))
-       :on-mouse-leave (fn [e]
-                         (block-mouse-leave e *control-show? block-id doc-mode?))}
-      (when (not slide?)
-        (block-control config block uuid block-id collapsed? *control-show? edit?))
-
-      (when @*show-left-menu?
-        (block-left-menu config block))
-
-      (if whiteboard-block?
-        (block-reference {} (str uuid) nil)
+          (when top?
+            (dnd-separator-wrapper block block-id slide? true false))
+
+          [:div.block-main-container.flex.flex-row.pr-2
+           {:class (if (and heading? (seq (:block/title block))) "items-baseline" "")
+            :on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
+            :on-touch-move (fn [event]
+                             (block-handler/on-touch-move event block uuid edit? *show-left-menu? *show-right-menu?))
+            :on-touch-end (fn [event]
+                            (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
+            :on-touch-cancel (fn [_e]
+                               (block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
+            :on-mouse-over (fn [e]
+                             (block-mouse-over e *control-show? block-id doc-mode?))
+            :on-mouse-leave (fn [e]
+                              (block-mouse-leave e *control-show? block-id doc-mode?))}
+           (when (not slide?)
+             (block-control config block uuid block-id collapsed? *control-show? edit?))
+
+           (when @*show-left-menu?
+             (block-left-menu config block))
+
+           (if whiteboard-block?
+             (block-reference {} (str uuid) nil)
         ;; Not embed self
-        [:div.flex.flex-col.w-full
-         (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?))
-         (when (and (config/db-based-graph? repo) (not collapsed?))
-           (db-properties-cp config
-                             block
-                             edit-input-id
-                             {:selected? selected?
-                              :in-block-container? true}))])
-
-      (when @*show-right-menu?
-        (block-right-menu config block edit?))]
-
-     (when-not (:hide-children? config)
-       (let [children (db/sort-by-left (:block/_parent block) block)
-             config' (-> (update config :level inc)
-                         (dissoc :original-block))]
-         (block-children config' block children collapsed?)))
-
-     (dnd-separator-wrapper block block-id slide? false false)]))
+             [:div.flex.flex-col.w-full
+              (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?))
+              (when (and (config/db-based-graph? repo) (not collapsed?))
+                (db-properties-cp config
+                                  block
+                                  edit-input-id
+                                  {:selected? selected?
+                                   :in-block-container? true}))])
+
+           (when @*show-right-menu?
+             (block-right-menu config block edit?))]
+
+          (when-not (:hide-children? config)
+            (let [children (db/sort-by-left (:block/_parent block) block)
+                  config' (-> (update config :level inc)
+                              (dissoc :original-block))]
+              (block-children config' block children collapsed?)))
+
+          (dnd-separator-wrapper block block-id slide? false false)]))]))
 
 (defn- block-changed?
   [old-block new-block]
@@ -3359,7 +3381,7 @@
   [config col]
   (map #(markup-element-cp config %) col))
 
-(rum/defc block-item <
+(rum/defcs block-item <
   {:should-update (fn [old-state new-state]
                     (let [config-compare-keys [:show-cloze? :hide-children? :own-order-list-type :own-order-list-index :original-block]
                           b1                  (second (:rum/args old-state))
@@ -3370,7 +3392,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? edit-block]}]
+  [state config item {:keys [top? bottom?]}]
   (let [original-block item
         linked-block (:block/link item)
         item (or linked-block item)
@@ -3381,38 +3403,25 @@
         config (assoc config :block/uuid (:block/uuid item))
         config' (if linked-block
                   (assoc config :original-block original-block)
-                  config)
-        ref? (:ref? config)
-        custom-query? (boolean (:custom-query? config))
-        lazy? (or ref? custom-query? (:lazy? config))
-        initial-state (= (:block/uuid item) (:block/uuid edit-block))
-        cp-f (fn []
-               (rum/with-key (block-container config' item)
-                 (str (:blocks-container-id config')
-                      "-"
-                      (:block/uuid item)
-                      (when linked-block
-                        (str "-" (:block/uuid original-block))))))]
-    (if lazy?
-      (ui/lazy-visible cp-f
-                       {:debug-id (str "block-container-ref " (:db/id item))
-                        :initial-state initial-state
-                        :fade-in? false})
-      (cp-f))))
+                  config)]
+    (rum/with-key (block-container config' item)
+      (str (:blocks-container-id config')
+           "-"
+           (:block/uuid item)
+           (when linked-block
+             (str "-" (:block/uuid original-block)))))))
 
 (defn- block-list
   [config blocks]
-  (let [edit-block (state/get-edit-block)]
-    (for [[idx item] (medley/indexed blocks)]
-      (let [top? (zero? idx)
-            bottom? (= (count blocks) (inc idx))]
-        (rum/with-key
-          (block-item (assoc config :idx idx) item {:top? top?
-                                                    :bottom? bottom?
-                                                    :edit-block edit-block})
-          (str "blocks-" (:blocks-container-id config)
-               "-"
-               (:block/uuid item)))))))
+  (for [[idx item] (medley/indexed blocks)]
+    (let [top? (zero? idx)
+          bottom? (= (count blocks) (inc idx))]
+      (rum/with-key
+        (block-item (assoc config :idx idx) item {:top? top?
+                                                  :bottom? bottom?})
+        (str "blocks-" (:blocks-container-id config)
+             "-"
+             (:block/uuid item))))))
 
 (rum/defcs blocks-container <
   {:init (fn [state] (assoc state ::init-blocks-container-id (atom nil)))}

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

@@ -486,7 +486,7 @@
 
 (rum/defc main <
   {:did-mount (fn [state]
-                (when-let [element (gdom/getElement "app-container")]
+                (when-let [element (gdom/getElement "main-content-container")]
                   (dnd/subscribe!
                    element
                    :upload-files
@@ -499,7 +499,7 @@
                     (set! (.. element -scrollTop) 0)))
                 state)
    :will-unmount (fn [state]
-                   (when-let [el (gdom/getElement "app-container")]
+                   (when-let [el (gdom/getElement "main-content-container")]
                      (dnd/unsubscribe! el :upload-files))
                    state)}
   [{:keys [route-match margin-less-pages? route-name indexeddb-support? db-restoring? main-content show-action-bar? show-recording-bar?]}]

+ 17 - 17
src/main/frontend/components/page.cljs

@@ -85,13 +85,14 @@
 
 (rum/defc page-blocks-inner <
   {:did-mount open-root-block!}
-  [page-name _block hiccup sidebar? whiteboard? _block-uuid]
-  [:div.page-blocks-inner {:style {:margin-left (if (or sidebar? whiteboard?) 0 -20)}}
-   (rum/with-key
-     (content/content page-name
-                      {:hiccup   hiccup
-                       :sidebar? sidebar?})
-     (str page-name "-hiccup"))])
+  [page-name _block blocks config sidebar? whiteboard? _block-uuid]
+  (let [hiccup (component-block/->hiccup blocks config {})]
+    [:div.page-blocks-inner {:style {:margin-left (if (or sidebar? whiteboard?) 0 -20)}}
+     (rum/with-key
+       (content/content page-name
+                        {:hiccup   hiccup
+                         :sidebar? sidebar?})
+       (str page-name "-hiccup"))]))
 
 (declare page)
 
@@ -124,7 +125,8 @@
      [:a.add-button-link.block
       (ui/icon "circle-plus")]]]])
 
-(rum/defc page-blocks-cp < rum/reactive db-mixins/query
+(rum/defcs page-blocks-cp < rum/reactive db-mixins/query
+  (rum/local nil ::ref)
   {:will-mount (fn [state]
                  (let [page-e (second (:rum/args state))
                        page-name (:block/name page-e)]
@@ -133,7 +135,7 @@
                                   (date/journal-title->int (date/today))))
                      (state/pub-event! [:journal/insert-template page-name])))
                  state)}
-  [repo page-e {:keys [sidebar? whiteboard?] :as config}]
+  [state repo page-e {:keys [sidebar? whiteboard?] :as config}]
   (when page-e
     (let [page-name (or (:block/name page-e)
                         (str (:block/uuid page-e)))
@@ -151,7 +153,8 @@
         (dummy-block page-name)
 
         :else
-        (let [document-mode? (state/sub :document/mode?)
+        (let [*ref (::ref state)
+              document-mode? (state/sub :document/mode?)
               hiccup-config (merge
                              {:id (if block? (str block-id) page-name)
                               :db/id (:db/id block)
@@ -159,13 +162,10 @@
                               :editor-box editor/box
                               :document/mode? document-mode?}
                              config)
-              hiccup-config (common-handler/config-with-document-mode hiccup-config)
-              blocks (if block? [block] (db/sort-by-left (:block/_parent block) block))
-              non-collapsed-blocks-count (count (remove :block/collapsed? (:block/_page (db/entity (:db/id page-e)))))
-              lazy? (> non-collapsed-blocks-count 50)
-              hiccup (component-block/->hiccup blocks (assoc hiccup-config :lazy? lazy?) {})]
-          [:div
-           (page-blocks-inner page-name block hiccup sidebar? whiteboard? block-id)
+              config (common-handler/config-with-document-mode hiccup-config)
+              blocks (if block? [block] (db/sort-by-left (:block/_parent block) block))]
+          [:div {:ref #(reset! *ref %)}
+           (page-blocks-inner page-name block blocks config sidebar? whiteboard? block-id)
            (when-not config/publishing?
              (let [args (if block-id
                           {:block-uuid block-id}

+ 16 - 11
src/main/frontend/handler/common.cljs

@@ -7,7 +7,9 @@
             [frontend.util :as util]
             [frontend.handler.property :as property-handler]
             [goog.object :as gobj]
-            ["ignore" :as Ignore]))
+            [goog.dom :as gdom]
+            ["ignore" :as Ignore]
+            [goog.functions :refer [debounce]]))
 
 (defn copy-to-clipboard-without-id-property!
   [repo format raw-text html blocks]
@@ -71,13 +73,16 @@
 
 (defn listen-to-scroll!
   [element]
-  (let [*scroll-timer (atom nil)]
-    (.addEventListener element "scroll"
-                       (fn []
-                         (when @*scroll-timer
-                           (js/clearTimeout @*scroll-timer))
-                         (state/set-state! :ui/scrolling? true)
-                         (state/save-scroll-position! (util/scroll-top))
-                         (reset! *scroll-timer (js/setTimeout
-                                                (fn [] (state/set-state! :ui/scrolling? false)) 500)))
-                       false)))
+  (let [*scroll-timer (atom nil)
+        on-scroll (fn []
+                    (when @*scroll-timer
+                      (js/clearTimeout @*scroll-timer))
+                    (state/set-state! :ui/scrolling? true)
+                    (state/save-scroll-position! (util/scroll-top))
+                    (state/save-main-container-position!
+                     (-> (gdom/getElement "main-content-container")
+                         (gobj/get "scrollTop")))
+                    (reset! *scroll-timer (js/setTimeout
+                                           (fn [] (state/set-state! :ui/scrolling? false)) 500)))
+        debounced-on-scroll (debounce on-scroll 100)]
+    (.addEventListener element "scroll" debounced-on-scroll false)))

+ 1 - 1
src/main/frontend/modules/outliner/datascript.cljs

@@ -170,7 +170,7 @@
               rs (db/transact! repo txs (assoc opts :outliner/transact? true))
               tx-id (get-tx-id rs)]
           ;; TODO: disable this when db is stable
-          (when config/dev? (validate-db! rs))
+          ;; (when config/dev? (validate-db! rs))
           (state/update-state! :history/tx->editor-cursor
                                (fn [m] (assoc m tx-id before-editor-cursor)))
 

+ 4 - 0
src/main/frontend/state.cljs

@@ -1351,6 +1351,10 @@ Similar to re-frame subscriptions"
   ([value path]
    (set-state! :ui/paths-scroll-positions value :path-in-sub-atom path)))
 
+(defn save-main-container-position!
+  [value]
+  (set-state! :ui/main-container-scroll-top value))
+
 (defn get-saved-scroll-position
   ([]
    (get-saved-scroll-position js/window.location.hash))