Browse Source

feat: virtual list

Tienson Qin 3 years ago
parent
commit
bfae3f07d4

+ 1 - 0
package.json

@@ -110,6 +110,7 @@
         "react-textarea-autosize": "8.3.3",
         "react-tippy": "1.4.0",
         "react-transition-group": "4.3.0",
+        "react-virtuoso": "^2.8.5",
         "reakit": "0.11.1",
         "remove-accents": "0.4.2",
         "send-intent": "3.0.11",

+ 28 - 38
src/main/frontend/components/block.cljs

@@ -2238,7 +2238,8 @@
                    (when pre-block? " pre-block")
                    (when (and card? (not review-cards?)) " shadow-xl"))
        :blockid (str uuid)
-       :haschild (str has-child?)}
+       :haschild (str has-child?)
+       :style {:padding-left (* (- level 1) 24)}}
 
        level
        (assoc :level level)
@@ -2274,7 +2275,7 @@
 
       (block-content-or-editor config block edit-input-id block-id heading-level edit?)]
 
-     (block-children config children collapsed?)
+     ;; (block-children config children collapsed?)
 
      (dnd-separator-wrapper block block-id slide? false false)]))
 
@@ -2846,16 +2847,20 @@
   [config col]
   (map #(markup-element-cp config %) col))
 
+(defn- block-item
+  [config blocks idx item]
+  (let [item (->
+              (dissoc item :block/meta)
+              (assoc :block/top? (zero? idx)
+                     :block/bottom? (= (count blocks) (inc idx))))
+        config (assoc config :block/uuid (:block/uuid item))]
+    (rum/with-key (block-container config item)
+      (str (:block/uuid item)))))
+
 (defn- block-list
   [config blocks]
   (for [[idx item] (medley/indexed blocks)]
-    (let [item (->
-                (dissoc item :block/meta)
-                (assoc :block/top? (zero? idx)
-                       :block/bottom? (= (count blocks) (inc idx))))
-          config (assoc config :block/uuid (:block/uuid item))]
-      (rum/with-key (block-container config item)
-        (str (:block/uuid item))))))
+    (block-item config blocks idx item)))
 
 (defn- custom-query-or-ref?
   [config]
@@ -2863,8 +2868,6 @@
         custom-query? (:custom-query? config)]
     (or custom-query? ref?)))
 
-;; TODO: virtual tree for better UX and memory usage reduce
-
 (defn- load-more-blocks!
   [config flat-blocks]
   (when-let [db-id (:db/id config)]
@@ -2874,33 +2877,20 @@
 (rum/defcs lazy-blocks < rum/reactive
   {:init (fn [state]
            (assoc state ::id (str (random-uuid))))}
-  [state config flat-blocks blocks->vec-tree]
-  (let [db-id (:db/id config)
-        blocks (blocks->vec-tree flat-blocks)]
-    (if (:custom-query? config)
-      (block-list config blocks)
-      (let [last-block-id (:db/id (last flat-blocks))
-            bottom-reached (fn []
-                             (when (and db-id
-                                        ;; To prevent scrolling after inserting new blocks
-                                        (> (- (util/time-ms) (:start-time config)) 200))
-                               (load-more-blocks! config flat-blocks)))
-            has-more? (when db-id
-                        (and (not= last-block-id (model/get-block-last-child db-id))
-                             (not= last-block-id db-id)))
-            dom-id (str "lazy-blocks-" (::id state))]
-        [:div {:id dom-id}
-         (ui/infinite-list
-          "main-content-container"
-          (block-list config blocks)
-          {:on-load bottom-reached
-           :bottom-reached (fn []
-                             (when-let [node (gdom/getElement dom-id)]
-                               (ui/bottom-reached? node 1000)))
-           :has-more has-more?
-           :more (if (or (:preview? config) (:sidebar? config))
-                   "More"
-                   (ui/loading "Loading"))})]))))
+  [state config blocks blocks->vec-tree]
+  (let [db-id (:db/id config)]
+    (if (or (:custom-query? config) (not db-id))
+      (let [blocks (blocks->vec-tree blocks)]
+        (block-list config blocks))
+      (let [blocks (tree/blocks-with-level blocks)
+            bottom-reached (fn [] (load-more-blocks! config blocks))]
+        (ui/virtual-list {:style {:height "calc(100vh - 180px)"}
+                          :data (bean/->js blocks)
+                          :end-reached bottom-reached
+                          :overscan 200
+                          :item-content (fn [idx block]
+                                          (let [block (bean/->clj block)]
+                                            (block-item config blocks idx block)))})))))
 
 (rum/defcs blocks-container <
   {:init (fn [state]

+ 7 - 6
src/main/frontend/modules/outliner/file.cljs

@@ -53,12 +53,13 @@
 
 (defn sync-to-file
   [{page-db-id :db/id}]
-  (if (nil? page-db-id)
-    (notification/show!
-     "Write file failed, can't find the current page!"
-     :error)
-    (when-let [repo (state/get-current-repo)]
-      (async/put! write-chan [repo page-db-id]))))
+  ;; (if (nil? page-db-id)
+  ;;   (notification/show!
+  ;;    "Write file failed, can't find the current page!"
+  ;;    :error)
+  ;;   (when-let [repo (state/get-current-repo)]
+  ;;     (async/put! write-chan [repo page-db-id])))
+  )
 
 (util/batch write-chan
             batch-write-interval

+ 17 - 0
src/main/frontend/modules/outliner/tree.cljs

@@ -80,3 +80,20 @@
   [blocks-exclude-root root]
   (let [parent-groups (atom (group-by :block/parent blocks-exclude-root))]
     (flatten (concat (sort-blocks-aux [root] parent-groups) (vals @parent-groups)))))
+
+(defn blocks-with-level
+  "`blocks` should be sorted already."
+  [blocks]
+  (let [level->block (atom {})]
+    (loop [blocks blocks
+           result []]
+      (if-let [block (first blocks)]
+        (let [parent-id (:db/id (:block/parent block))
+              parent-level (get @level->block parent-id)
+              level' (inc (or parent-level 0))
+              block (assoc block :block/level level')]
+          (when-not parent-level
+            (swap! level->block assoc parent-id parent-level))
+          (swap! level->block assoc (:db/id block) level')
+          (recur (rest blocks) (conj result block)))
+        result))))

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

@@ -24,6 +24,7 @@
             ["react-textarea-autosize" :as TextareaAutosize]
             ["react-tippy" :as react-tippy]
             ["react-transition-group" :refer [CSSTransition TransitionGroup]]
+            ["react-virtuoso" :refer [Virtuoso]]
             ["@logseq/react-tweet-embed" :as react-tweet-embed]
             [rum.core :as rum]
             [frontend.db-mixins :as db-mixins]
@@ -33,6 +34,7 @@
 (defonce transition-group (r/adapt-class TransitionGroup))
 (defonce css-transition (r/adapt-class CSSTransition))
 (defonce textarea (r/adapt-class (gobj/get TextareaAutosize "default")))
+(defonce virtual-list (r/adapt-class Virtuoso))
 (def resize-provider (r/adapt-class (gobj/get Resize "ResizeProvider")))
 (def resize-consumer (r/adapt-class (gobj/get Resize "ResizeConsumer")))
 (def Tippy (r/adapt-class (gobj/get react-tippy "Tooltip")))

+ 20 - 0
yarn.lock

@@ -1271,6 +1271,18 @@
   dependencies:
     "@types/node" "*"
 
+"@virtuoso.dev/react-urx@^0.2.12":
+  version "0.2.13"
+  resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz#e2cfc42d259d2a002695e7517d34cb97b64ee9c4"
+  integrity sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==
+  dependencies:
+    "@virtuoso.dev/urx" "^0.2.13"
+
+"@virtuoso.dev/urx@^0.2.12", "@virtuoso.dev/urx@^0.2.13":
+  version "0.2.13"
+  resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.13.tgz#a65e7e8d923cb03397ac876bfdd45c7f71c8edf1"
+  integrity sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw==
+
 acorn-node@^1.6.1:
   version "1.8.2"
   resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
@@ -6826,6 +6838,14 @@ [email protected]:
     loose-envify "^1.4.0"
     prop-types "^15.6.2"
 
+react-virtuoso@^2.8.5:
+  version "2.8.5"
+  resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.8.5.tgz#92f22d22255b444380dd0a29004d9bf678df834a"
+  integrity sha512-ayFESqgt++or9NLZ5XZR9Pta5W9jiT9pf9cYa/FYX5BoDuWMFYhou7xCal624JY6CzOOnwUlCGck95dtxsVDiA==
+  dependencies:
+    "@virtuoso.dev/react-urx" "^0.2.12"
+    "@virtuoso.dev/urx" "^0.2.12"
+
 [email protected]:
   version "17.0.2"
   resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"