1
0
Эх сурвалжийг харах

Merge pull request #11774 from logseq/perf/app-start

perf: app start time && large table
Tienson Qin 6 сар өмнө
parent
commit
fb724ca130
100 өөрчлөгдсөн 3608 нэмэгдсэн , 2888 устгасан
  1. 9 4
      .clj-kondo/config.edn
  2. 2 1
      .cljfmt.edn
  3. 1 1
      deps/common/package.json
  4. 1 2
      deps/common/src/logseq/common/defkeywords.cljc
  5. 25 4
      deps/common/src/logseq/common/util.cljs
  6. 3 3
      deps/common/yarn.lock
  7. 4 0
      deps/db/.carve/ignore
  8. 1 0
      deps/db/.clj-kondo/config.edn
  9. 1 1
      deps/db/bb.edn
  10. 1 1
      deps/db/package.json
  11. 0 1
      deps/db/script/create_graph.cljs
  12. 86 39
      deps/db/src/logseq/db.cljs
  13. 15 1
      deps/db/src/logseq/db/common/property_util.cljs
  14. 161 114
      deps/db/src/logseq/db/common/sqlite.cljs
  15. 577 0
      deps/db/src/logseq/db/common/view.cljs
  16. 1 1
      deps/db/src/logseq/db/file_based/rules.cljc
  17. 14 0
      deps/db/src/logseq/db/frontend/class.cljs
  18. 1 1
      deps/db/src/logseq/db/frontend/content.cljs
  19. 6 6
      deps/db/src/logseq/db/frontend/entity_plus.cljc
  20. 5 0
      deps/db/src/logseq/db/frontend/entity_util.cljs
  21. 1 1
      deps/db/src/logseq/db/frontend/schema.cljs
  22. 1 1
      deps/db/src/logseq/db/sqlite/export.cljs
  23. 20 34
      deps/db/test/logseq/db/common/sqlite_test.cljs
  24. 3 3
      deps/db/yarn.lock
  25. 1 1
      deps/graph-parser/package.json
  26. 6 6
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  27. 2 2
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  28. 3 3
      deps/graph-parser/yarn.lock
  29. 1 1
      deps/outliner/package.json
  30. 6 4
      deps/outliner/src/logseq/outliner/core.cljs
  31. 2 2
      deps/outliner/src/logseq/outliner/tree.cljs
  32. 3 3
      deps/outliner/yarn.lock
  33. 1 1
      deps/publishing/package.json
  34. 3 3
      deps/publishing/yarn.lock
  35. 18 1
      deps/shui/src/logseq/shui/hooks.cljs
  36. 13 14
      deps/shui/src/logseq/shui/table/core.cljc
  37. 0 41
      deps/shui/src/logseq/shui/table/impl.cljc
  38. 1 0
      packages/ui/src/colors.css
  39. 1 1
      scripts/package.json
  40. 1 1
      scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs
  41. 3 3
      scripts/yarn.lock
  42. 48 48
      src/electron/electron/core.cljs
  43. 6 6
      src/main/electron/listener.cljs
  44. 1 1
      src/main/frontend/common.css
  45. 19 0
      src/main/frontend/common/cache.cljs
  46. 3 3
      src/main/frontend/common/file/core.cljs
  47. 240 0
      src/main/frontend/common/graph_view.cljs
  48. 19 5
      src/main/frontend/common/missionary.cljs
  49. 5 1
      src/main/frontend/common/thread_api.cljc
  50. 7 34
      src/main/frontend/components/all_pages.cljs
  51. 389 311
      src/main/frontend/components/block.cljs
  52. 1 1
      src/main/frontend/components/block.css
  53. 6 7
      src/main/frontend/components/class.cljs
  54. 15 18
      src/main/frontend/components/cmdk/core.cljs
  55. 8 4
      src/main/frontend/components/container.cljs
  56. 1 1
      src/main/frontend/components/content.cljs
  57. 22 22
      src/main/frontend/components/editor.cljs
  58. 42 35
      src/main/frontend/components/export.cljs
  59. 4 4
      src/main/frontend/components/file_based/hierarchy.cljs
  60. 4 4
      src/main/frontend/components/file_based/query.cljs
  61. 3 3
      src/main/frontend/components/header.cljs
  62. 0 1
      src/main/frontend/components/imports.cljs
  63. 34 39
      src/main/frontend/components/journal.cljs
  64. 3 3
      src/main/frontend/components/journal.css
  65. 31 20
      src/main/frontend/components/lazy_editor.cljs
  66. 36 118
      src/main/frontend/components/objects.cljs
  67. 186 179
      src/main/frontend/components/page.cljs
  68. 2 2
      src/main/frontend/components/page_menu.cljs
  69. 31 4
      src/main/frontend/components/profiler.cljs
  70. 4 20
      src/main/frontend/components/property.cljs
  71. 36 46
      src/main/frontend/components/property/config.cljs
  72. 201 199
      src/main/frontend/components/property/value.cljs
  73. 59 65
      src/main/frontend/components/query.cljs
  74. 44 53
      src/main/frontend/components/query/builder.cljs
  75. 21 17
      src/main/frontend/components/query/result.cljs
  76. 17 39
      src/main/frontend/components/query/view.cljs
  77. 86 246
      src/main/frontend/components/reference.cljs
  78. 10 9
      src/main/frontend/components/reference_filters.cljs
  79. 174 150
      src/main/frontend/components/right_sidebar.cljs
  80. 15 16
      src/main/frontend/components/rtc/indicator.cljs
  81. 69 50
      src/main/frontend/components/select.cljs
  82. 5 6
      src/main/frontend/components/selection.cljs
  83. 12 1
      src/main/frontend/components/table.css
  84. 468 448
      src/main/frontend/components/views.cljs
  85. 8 0
      src/main/frontend/components/views.css
  86. 21 19
      src/main/frontend/components/whiteboard.cljs
  87. 3 4
      src/main/frontend/db.cljs
  88. 60 95
      src/main/frontend/db/async.cljs
  89. 4 3
      src/main/frontend/db/async/util.cljs
  90. 39 147
      src/main/frontend/db/model.cljs
  91. 5 4
      src/main/frontend/db/query_react.cljs
  92. 21 11
      src/main/frontend/db/react.cljs
  93. 1 6
      src/main/frontend/db/restore.cljs
  94. 11 13
      src/main/frontend/db/rtc/debug_ui.cljs
  95. 14 11
      src/main/frontend/extensions/code.cljs
  96. 1 5
      src/main/frontend/extensions/code.css
  97. 4 3
      src/main/frontend/extensions/fsrs.cljs
  98. 1 1
      src/main/frontend/extensions/handbooks/core.cljs
  99. 21 18
      src/main/frontend/extensions/pdf/assets.cljs
  100. 3 3
      src/main/frontend/extensions/pdf/core.cljs

+ 9 - 4
.clj-kondo/config.edn

@@ -1,10 +1,17 @@
-{:ns-groups [{:pattern "frontend.components.*" :name all-components}]
+{:ns-groups [{:pattern "frontend.components.*" :name all-components}
+             {:pattern "frontend.*" :name all-frontend}]
 
 
  :config-in-ns
  :config-in-ns
  ;; :used-underscored-binding is turned off for components because of false positive
  ;; :used-underscored-binding is turned off for components because of false positive
  ;; for rum/defcs and _state.
  ;; for rum/defcs and _state.
  {all-components
  {all-components
   {:linters {:used-underscored-binding {:level :off}}}
   {:linters {:used-underscored-binding {:level :off}}}
+
+  all-frontend
+  {:linters {:discouraged-namespace
+             {logseq.db.sqlite.cli {:message "frontend should not depend on CLI namespace with sqlite3 dependency"}
+              logseq.outliner.cli {:message "frontend should not depend on CLI namespace with sqlite3 dependency"}}}}
+
   ;; false positive with match/match and _
   ;; false positive with match/match and _
   frontend.handler.paste {:linters {:used-underscored-binding {:level :off}}}
   frontend.handler.paste {:linters {:used-underscored-binding {:level :off}}}
   frontend.db {:linters {:aliased-namespace-symbol
   frontend.db {:linters {:aliased-namespace-symbol
@@ -32,9 +39,6 @@
                                 ;; TODO:lint: Fix when fixing all type hints
                                 ;; TODO:lint: Fix when fixing all type hints
                                 object]}
                                 object]}
 
 
-  :discouraged-namespace
-  {logseq.db.sqlite.cli {:message "frontend should not depend on CLI namespace with sqlite3 dependency"}
-   logseq.outliner.cli {:message "frontend should not depend on CLI namespace with sqlite3 dependency"}}
   :discouraged-var
   :discouraged-var
   {rum.core/use-effect! {:message "Use frontend.hooks/use-effect! instead" :level :info}
   {rum.core/use-effect! {:message "Use frontend.hooks/use-effect! instead" :level :info}
    rum.core/use-memo {:message "Use frontend.hooks/use-memo instead" :level :info}
    rum.core/use-memo {:message "Use frontend.hooks/use-memo instead" :level :info}
@@ -172,6 +176,7 @@
              logseq.db.common.order db-order
              logseq.db.common.order db-order
              logseq.db.common.property-util db-property-util
              logseq.db.common.property-util db-property-util
              logseq.db.common.sqlite sqlite-common-db
              logseq.db.common.sqlite sqlite-common-db
+             logseq.db.common.view db-view
              logseq.db.file-based.rules file-rules
              logseq.db.file-based.rules file-rules
              logseq.db.file-based.schema file-schema
              logseq.db.file-based.schema file-schema
              logseq.db.file-based.entity-util file-entity-util
              logseq.db.file-based.entity-util file-entity-util

+ 2 - 1
.cljfmt.edn

@@ -1,3 +1,4 @@
  {:extra-indents {missionary.core/sp [[:block 0]]
  {:extra-indents {missionary.core/sp [[:block 0]]
-                  missionary.core/ap [[:block 0]]}
+                  missionary.core/ap [[:block 0]]
+                  frontend.common.missionary/run-task [[:inner 0]]}
   :sort-ns-references? true}
   :sort-ns-references? true}

+ 1 - 1
deps/common/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19"
   },
   },
   "scripts": {
   "scripts": {
     "test": "yarn nbb-logseq -cp test -m nextjournal.test-runner"
     "test": "yarn nbb-logseq -cp test -m nextjournal.test-runner"

+ 1 - 2
deps/common/src/logseq/common/defkeywords.cljc

@@ -37,5 +37,4 @@
 (comment
 (comment
   "update anything here to trigger this ns to be recompiled,
   "update anything here to trigger this ns to be recompiled,
 so macro get-all-defined-kw->config's result will be updated."
 so macro get-all-defined-kw->config's result will be updated."
-  1
-  )
+  1)

+ 25 - 4
deps/common/src/logseq/common/util.cljs

@@ -1,14 +1,14 @@
 (ns logseq.common.util
 (ns logseq.common.util
   "Util fns shared between the app. Util fns only rely on
   "Util fns shared between the app. Util fns only rely on
   clojure standard libraries."
   clojure standard libraries."
-  (:require [cljs.reader :as reader]
+  (:require [cljs-time.coerce :as tc]
+            [cljs-time.core :as t]
+            [cljs.reader :as reader]
             [clojure.edn :as edn]
             [clojure.edn :as edn]
             [clojure.string :as string]
             [clojure.string :as string]
             [clojure.walk :as walk]
             [clojure.walk :as walk]
-            [logseq.common.log :as log]
             [goog.string :as gstring]
             [goog.string :as gstring]
-            [cljs-time.coerce :as tc]
-            [cljs-time.core :as t]))
+            [logseq.common.log :as log]))
 
 
 (defn safe-decode-uri-component
 (defn safe-decode-uri-component
   [uri]
   [uri]
@@ -364,3 +364,24 @@ return: [{:id 3} {:id 2 :depend-on 3} {:id 1 :depend-on 2}]"
                 (nil? (:block/created-at block))
                 (nil? (:block/created-at block))
                 (assoc :block/created-at updated-at))]
                 (assoc :block/created-at updated-at))]
     block))
     block))
+
+(defn get-timestamp
+  [value]
+  (let [now (t/now)
+        f t/minus]
+    (if (string? value)
+      (case value
+        "1 day ago"
+        (tc/to-long (f now (t/days 1)))
+        "3 days ago"
+        (tc/to-long (f now (t/days 3)))
+        "1 week ago"
+        (tc/to-long (f now (t/weeks 1)))
+        "1 month ago"
+        (tc/to-long (f now (t/months 1)))
+        "3 months ago"
+        (tc/to-long (f now (t/months 3)))
+        "1 year ago"
+        (tc/to-long (f now (t/years 1)))
+        nil)
+      (tc/to-long (tc/to-date value)))))

+ 3 - 3
deps/common/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 4 - 0
deps/db/.carve/ignore

@@ -24,3 +24,7 @@ logseq.db.sqlite.build/create-blocks
 logseq.db.sqlite.export/build-export
 logseq.db.sqlite.export/build-export
 ;; API
 ;; API
 logseq.db.sqlite.export/build-import
 logseq.db.sqlite.export/build-import
+;; API
+logseq.db.common.view/get-property-values
+;; API
+logseq.db.common.view/get-view-data

+ 1 - 0
deps/db/.clj-kondo/config.edn

@@ -13,6 +13,7 @@
              logseq.db.common.order db-order
              logseq.db.common.order db-order
              logseq.db.common.property-util db-property-util
              logseq.db.common.property-util db-property-util
              logseq.db.common.sqlite sqlite-common-db
              logseq.db.common.sqlite sqlite-common-db
+             logseq.db.common.view db-view
              logseq.db.frontend.content db-content
              logseq.db.frontend.content db-content
              logseq.db.frontend.class db-class
              logseq.db.frontend.class db-class
              logseq.db.frontend.db-ident db-ident
              logseq.db.frontend.db-ident db-ident

+ 1 - 1
deps/db/bb.edn

@@ -43,4 +43,4 @@
  :tasks/config
  :tasks/config
  {:large-vars
  {:large-vars
   {:max-lines-count 50
   {:max-lines-count 50
-   :metadata-exceptions #{:large-vars/doc-var}}}}
+   :metadata-exceptions #{:large-vars/doc-var :large-vars/cleanup-todo}}}}

+ 1 - 1
deps/db/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19"
   },
   },
   "dependencies": {
   "dependencies": {
     "better-sqlite3": "9.3.0"
     "better-sqlite3": "9.3.0"

+ 0 - 1
deps/db/script/create_graph.cljs

@@ -9,7 +9,6 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [datascript.core :as d]
             [datascript.core :as d]
             [logseq.db.sqlite.export :as sqlite-export]
             [logseq.db.sqlite.export :as sqlite-export]
-            #_:clj-kondo/ignore
             [logseq.outliner.cli :as outliner-cli]
             [logseq.outliner.cli :as outliner-cli]
             [nbb.classpath :as cp]
             [nbb.classpath :as cp]
             [nbb.core :as nbb]
             [nbb.core :as nbb]

+ 86 - 39
deps/db/src/logseq/db.cljs

@@ -232,12 +232,14 @@
 
 
 (defn get-page
 (defn get-page
   "Get a page given its unsanitized name"
   "Get a page given its unsanitized name"
-  [db page-name-or-uuid]
+  [db page-id-name-or-uuid]
   (when db
   (when db
-    (if-let [id (if (uuid? page-name-or-uuid) page-name-or-uuid
-                    (parse-uuid page-name-or-uuid))]
-      (d/entity db [:block/uuid id])
-      (d/entity db (get-first-page-by-name db (name page-name-or-uuid))))))
+    (if (number? page-id-name-or-uuid)
+      (d/entity db page-id-name-or-uuid)
+      (if-let [id (if (uuid? page-id-name-or-uuid) page-id-name-or-uuid
+                      (parse-uuid page-id-name-or-uuid))]
+        (d/entity db [:block/uuid id])
+        (d/entity db (get-first-page-by-name db (name page-id-name-or-uuid)))))))
 
 
 (defn get-case-page
 (defn get-case-page
   "Case sensitive version of get-page. For use with DB graphs"
   "Case sensitive version of get-page. For use with DB graphs"
@@ -301,7 +303,8 @@
 
 
 (defn has-children?
 (defn has-children?
   [db block-id]
   [db block-id]
-  (some? (:block/_parent (d/entity db [:block/uuid block-id]))))
+  (let [eid (if (uuid? block-id) [:block/uuid block-id] block-id)]
+    (some? (:block/_parent (d/entity db eid)))))
 
 
 (defn- collapsed-and-has-children?
 (defn- collapsed-and-has-children?
   [db block]
   [db block]
@@ -435,15 +438,37 @@
     (:alias rules/rules))
     (:alias rules/rules))
    distinct))
    distinct))
 
 
+(defn page-alias-set
+  [db page-id]
+  (->>
+   (get-block-alias db page-id)
+   (set)
+   (set/union #{page-id})))
+
 (defn get-block-refs
 (defn get-block-refs
   [db id]
   [db id]
-  (let [alias (->> (get-block-alias db id)
+  (let [entity (d/entity db id)
+        db-based? (db-based-graph? db)
+        alias (->> (get-block-alias db id)
                    (cons id)
                    (cons id)
                    distinct)
                    distinct)
-        refs (->> (mapcat (fn [id] (:block/_path-refs (d/entity db id))) alias)
-                  distinct)]
-    (when (seq refs)
-      (d/pull-many db '[*] (map :db/id refs)))))
+        ref-ids (->> (mapcat (fn [id]
+                               (cond->> (:block/_refs (d/entity db id))
+                                 db-based?
+                                 (remove (fn [ref]
+                                           ;; remove refs that have the block as either tag or property
+                                           (or (and
+                                                (class? entity)
+                                                (d/datom db :eavt (:db/id ref) :block/tags (:db/id entity)))
+                                               (and
+                                                (property? entity)
+                                                (d/datom db :eavt (:db/id ref) (:db/ident entity))))))
+                                 true
+                                 (map :db/id)))
+                             alias)
+                     distinct)]
+    (when (seq ref-ids)
+      (d/pull-many db '[*] ref-ids))))
 
 
 (defn get-block-refs-count
 (defn get-block-refs-count
   [db id]
   [db id]
@@ -451,21 +476,9 @@
           :block/_refs
           :block/_refs
           count))
           count))
 
 
-(defn get-page-unlinked-refs
-  "Get unlinked refs from search result"
-  [db page-id search-result-eids]
-  (let [alias (->> (get-block-alias db page-id)
-                   (cons page-id)
-                   set)
-        eids (remove
-              (fn [eid]
-                (when-let [e (d/entity db eid)]
-                  (or (some alias (map :db/id (:block/refs e)))
-                      (:block/link e)
-                      (nil? (:block/title e)))))
-              search-result-eids)]
-    (when (seq eids)
-      (d/pull-many db '[*] eids))))
+(defn hidden-or-internal-tag?
+  [e]
+  (or (entity-util/hidden? e) (db-class/internal-tags (:db/ident e))))
 
 
 (defn get-all-pages
 (defn get-all-pages
   [db]
   [db]
@@ -473,13 +486,12 @@
    (d/datoms db :avet :block/name)
    (d/datoms db :avet :block/name)
    (keep (fn [d]
    (keep (fn [d]
            (let [e (d/entity db (:e d))]
            (let [e (d/entity db (:e d))]
-             (when-not (or (hidden? e) (internal-tags (:db/ident e)))
+             (when-not (or (hidden-or-internal-tag? e)
+                           ;; Why this happened?
+                           (nil? (:block/title e)))
                e))))))
                e))))))
 
 
-(defn built-in?
-  "Built-in page or block"
-  [entity]
-  (:logseq.property/built-in? entity))
+(def built-in? entity-util/built-in?)
 
 
 (defn built-in-class-property?
 (defn built-in-class-property?
   "Whether property a built-in property for the specific class"
   "Whether property a built-in property for the specific class"
@@ -602,12 +614,47 @@
     :logseq.class/Quote-block :quote
     :logseq.class/Quote-block :quote
     nil))
     nil))
 
 
-(defn get-recent-updated-pages
+(def get-recent-updated-pages sqlite-common-db/get-recent-updated-pages)
+
+(def get-latest-journals sqlite-common-db/get-latest-journals)
+
+(defn get-all-namespace-relation
+  [db]
+  (d/q '[:find ?page ?parent
+         :where
+         [?page :block/namespace ?parent]]
+       db))
+
+(defn get-pages-relation
+  [db with-journal?]
+  (if (entity-plus/db-based-graph? db)
+    (let [q (if with-journal?
+              '[:find ?p ?ref-page
+                :where
+                [?block :block/page ?p]
+                [?block :block/refs ?ref-page]]
+              '[:find ?p ?ref-page
+                :where
+                [?block :block/page ?p]
+                [?p :block/tags]
+                (not [?p :block/tags :logseq.class/Journal])
+                [?block :block/refs ?ref-page]])]
+      (d/q q db))
+    (let [q (if with-journal?
+              '[:find ?p ?ref-page
+                :where
+                [?block :block/page ?p]
+                [?block :block/refs ?ref-page]]
+              '[:find ?p ?ref-page
+                :where
+                [?block :block/page ?p]
+                (not [?p :block/type "journal"])
+                [?block :block/refs ?ref-page]])]
+      (d/q q db))))
+
+(defn get-all-tagged-pages
   [db]
   [db]
-  (->> (d/datoms db :avet :block/updated-at)
-       (reverse)
-       (keep (fn [datom]
-               (let [e (d/entity db (:e datom))]
-                 (when (and (page? e) (not (hidden? e)))
-                   e))))
-       (take 30)))
+  (d/q '[:find ?page ?tag
+         :where
+         [?page :block/tags ?tag]]
+       db))

+ 15 - 1
deps/db/src/logseq/db/common/property_util.cljs

@@ -1,17 +1,31 @@
 (ns logseq.db.common.property-util
 (ns logseq.db.common.property-util
   "Property related util fns. Fns used in both DB and file graphs should go here"
   "Property related util fns. Fns used in both DB and file graphs should go here"
   (:require [datascript.core :as d]
   (:require [datascript.core :as d]
+            [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.sqlite.util :as sqlite-util]))
             [logseq.db.sqlite.util :as sqlite-util]))
 
 
+(defn- get-file-pid-by-ident
+  [db-ident]
+  (get-in db-property/built-in-properties [db-ident :name] (name db-ident)))
+
+;; TODO: refactor later to remove this fn
 (defn get-pid
 (defn get-pid
   "Get a built-in property's id (keyword name for file graph and db-ident for db
   "Get a built-in property's id (keyword name for file graph and db-ident for db
   graph) given its db-ident. No need to use this fn in a db graph only context"
   graph) given its db-ident. No need to use this fn in a db graph only context"
   [repo db-ident]
   [repo db-ident]
   (if (sqlite-util/db-based-graph? repo)
   (if (sqlite-util/db-based-graph? repo)
     db-ident
     db-ident
-    (get-in db-property/built-in-properties [db-ident :name] (name db-ident))))
+    (get-file-pid-by-ident db-ident)))
+
+(defn get-pid-2
+  "Get a built-in property's id (keyword name for file graph and db-ident for db
+  graph) given its db-ident. No need to use this fn in a db graph only context"
+  [db db-ident]
+  (if (entity-plus/db-based-graph? db)
+    db-ident
+    (get-file-pid-by-ident db-ident)))
 
 
 (defn built-in-has-ref-value?
 (defn built-in-has-ref-value?
   "Given a built-in's db-ident, determine if its property value is a ref"
   "Given a built-in's db-ident, determine if its property value is a ref"

+ 161 - 114
deps/db/src/logseq/db/common/sqlite.cljs

@@ -5,13 +5,14 @@
             [clojure.set :as set]
             [clojure.set :as set]
             [clojure.string :as string]
             [clojure.string :as string]
             [datascript.core :as d]
             [datascript.core :as d]
+            [datascript.impl.entity :as de]
             [logseq.common.config :as common-config]
             [logseq.common.config :as common-config]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.common.util.date-time :as date-time-util]
-            [logseq.db.frontend.entity-plus :as entity-plus]
-            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.common.entity-util :as common-entity-util]
             [logseq.db.common.entity-util :as common-entity-util]
             [logseq.db.common.order :as db-order]
             [logseq.db.common.order :as db-order]
+            [logseq.db.frontend.entity-plus :as entity-plus]
+            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.sqlite.util :as sqlite-util]))
             [logseq.db.sqlite.util :as sqlite-util]))
 
 
 (defn- get-pages-by-name
 (defn- get-pages-by-name
@@ -53,12 +54,6 @@
   [db block]
   [db block]
   (update block :block/refs (fn [refs] (map (fn [ref] (d/pull db '[*] (:db/id ref))) refs))))
   (update block :block/refs (fn [refs] (map (fn [ref] (d/pull db '[*] (:db/id ref))) refs))))
 
 
-(defn- with-block-link
-  [db block]
-  (if (:block/link block)
-    (update block :block/link (fn [link] (d/pull db '[*] (:db/id link))))
-    block))
-
 (defn with-parent
 (defn with-parent
   [db block]
   [db block]
   (cond
   (cond
@@ -73,6 +68,13 @@
     :else
     :else
     block))
     block))
 
 
+(comment
+  (defn- with-block-link
+    [db block]
+    (if (:block/link block)
+      (update block :block/link (fn [link] (d/pull db '[*] (:db/id link))))
+      block)))
+
 (defn- mark-block-fully-loaded
 (defn- mark-block-fully-loaded
   [b]
   [b]
   (assoc b :block.temp/fully-loaded? true))
   (assoc b :block.temp/fully-loaded? true))
@@ -80,37 +82,42 @@
 (comment
 (comment
   (defn- property-without-db-attrs
   (defn- property-without-db-attrs
     [property]
     [property]
-    (dissoc property :db/index :db/valueType :db/cardinality)))
+    (dissoc property :db/index :db/valueType :db/cardinality))
 
 
-(defn- property-with-values
-  [db block]
-  (when (entity-plus/db-based-graph? db)
-    (let [block (d/entity db (:db/id block))]
-      (->> (:block/properties block)
-           vals
-           (mapcat
-            (fn [property-values]
-              (let [values (->>
-                            (if (and (coll? property-values)
-                                     (map? (first property-values)))
-                              property-values
-                              #{property-values})
-                            (remove entity-util/page?))
-                    value-ids (when (every? map? values)
-                                (->> (map :db/id values)
-                                     (filter (fn [id] (or (int? id) (keyword? id))))))
-                    value-blocks (->>
-                                  (when (seq value-ids)
-                                    (map
-                                     (fn [id] (d/pull db '[*] id))
-                                     value-ids))
-                             ;; FIXME: why d/pull returns {:db/id db-ident} instead of {:db/id number-eid}?
-                                  (keep (fn [block]
-                                          (let [from-property-id (get-in block [:logseq.property/created-from-property :db/id])]
-                                            (if (keyword? from-property-id)
-                                              (assoc-in block [:logseq.property/created-from-property :db/id] (:db/id (d/entity db from-property-id)))
-                                              block)))))]
-                value-blocks)))))))
+  (defn- property-with-values
+    [db block properties]
+    (when (entity-plus/db-based-graph? db)
+      (let [block (d/entity db (:db/id block))
+            property-vals (if properties
+                            (map block properties)
+                            (vals (:block/properties block)))]
+        (->> property-vals
+             (mapcat
+              (fn [property-values]
+                (let [values (->>
+                              (if (and (coll? property-values)
+                                       (map? (first property-values)))
+                                property-values
+                                #{property-values}))
+                      value-ids (when (every? map? values)
+                                  (->> (map :db/id values)
+                                       (filter (fn [id] (or (int? id) (keyword? id))))))
+                      value-blocks (->>
+                                    (when (seq value-ids)
+                                      (map
+                                       (fn [id] (d/pull db '[:db/id :block/uuid
+                                                             :block/name :block/title
+                                                             :logseq.property/value
+                                                             :block/tags :block/page
+                                                             :logseq.property/created-from-property] id))
+                                       value-ids))
+                                  ;; FIXME: why d/pull returns {:db/id db-ident} instead of {:db/id number-eid}?
+                                    (keep (fn [block]
+                                            (let [from-property-id (get-in block [:logseq.property/created-from-property :db/id])]
+                                              (if (keyword? from-property-id)
+                                                (assoc-in block [:logseq.property/created-from-property :db/id] (:db/id (d/entity db from-property-id)))
+                                                block)))))]
+                  value-blocks))))))))
 
 
 (defn get-block-children-ids
 (defn get-block-children-ids
   "Returns children UUIDs"
   "Returns children UUIDs"
@@ -139,80 +146,93 @@
       (let [ids' (map (fn [id] [:block/uuid id]) ids)]
       (let [ids' (map (fn [id] [:block/uuid id]) ids)]
         (d/pull-many db '[*] ids')))))
         (d/pull-many db '[*] ids')))))
 
 
-(defn get-block-and-children
-  [db id {:keys [children? nested-children?]}]
+(defn- with-raw-title
+  [m entity]
+  (if-let [raw-title (:block/raw-title entity)]
+    (assoc m :block/title raw-title)
+    m))
+
+(defn- entity->map
+  [entity]
+  (-> (into {} entity)
+      (with-raw-title entity)
+      (assoc :db/id (:db/id entity))))
+
+(defn ^:large-vars/cleanup-todo get-block-and-children
+  [db id {:keys [children? children-only? nested-children? including-property-vals? properties children-props]
+          :or {including-property-vals? true}}]
   (let [block (d/entity db (if (uuid? id)
   (let [block (d/entity db (if (uuid? id)
                              [:block/uuid id]
                              [:block/uuid id]
                              id))
                              id))
-        page? (common-entity-util/page? block)
-        get-children (fn [block children]
-                       (let [long-page? (and (> (count children) 500) (not (common-entity-util/whiteboard? block)))]
-                         (if long-page?
-                           (->> (map (fn [e]
-                                       (select-keys e [:db/id :block/uuid :block/page :block/order :block/parent :block/collapsed? :block/link]))
-                                     children)
-                                (map #(with-block-link db %)))
-                           (->> (d/pull-many db '[*] (map :db/id children))
-                                (map #(with-block-refs db %))
-                                (map #(with-block-link db %))
-                                (mapcat (fn [block]
-                                          (let [e (d/entity db (:db/id block))]
-                                            (conj
-                                             (if (seq (:block/properties e))
-                                               (vec (property-with-values db e))
-                                               [])
-                                             block))))))))]
+        block-refs-count? (some #{:block.temp/refs-count} properties)
+        whiteboard? (common-entity-util/whiteboard? block)]
     (when block
     (when block
-      (let [block' (->> (d/pull db '[*] (:db/id block))
-                        (with-parent db)
-                        (with-block-refs db)
-                        (with-block-link db))
-            block' (if (or children? nested-children?)
-                     (mark-block-fully-loaded block')
-                     block')]
-        (cond->
-         {:block block'
-          :properties (property-with-values db block)}
-          children?
-          (assoc :children (get-children block
-                                         (if (and nested-children? (not page?))
-                                           (get-block-children db (:block/uuid block))
-                                           (if page?
+      (let [children (when (or children? children-only?)
+                       (let [page? (common-entity-util/page? block)
+                             children (->>
+                                       (cond
+                                         (and nested-children? (not page?))
+                                         (get-block-children db (:block/uuid block))
+                                         nested-children?
+                                         (:block/_page block)
+                                         :else
+                                         (let [short-page? (when page?
+                                                             (<= (count (:block/_page block)) 100))]
+                                           (if short-page?
                                              (:block/_page block)
                                              (:block/_page block)
-                                             (:block/_parent block))))))))))
+                                             (:block/_parent block))))
+                                       (remove (fn [e] (or (:logseq.property/created-from-property e)
+                                                           (:block/closed-value-property e)))))
+                             children-props (if whiteboard?
+                                              '[*]
+                                              (or children-props
+                                                  [:db/id :block/uuid :block/parent :block/order :block/collapsed? :block/title
+                                                   ;; pre-loading feature-related properties to avoid UI refreshing
+                                                   :logseq.task/status :logseq.property.node/display-type]))]
+                         (map
+                          (fn [block]
+                            (if (= children-props '[*])
+                              (entity->map block)
+                              (-> (select-keys block children-props)
+                                  (with-raw-title block))))
+                          children)))]
+        (if children-only?
+          {:children children}
+          (let [block' (if (seq properties)
+                         (-> (select-keys block properties)
+                             (with-raw-title block)
+                             (assoc :db/id (:db/id block)))
+                         (entity->map block))
+                block' (cond->
+                        (mark-block-fully-loaded block')
+                         including-property-vals?
+                         (update-vals (fn [v]
+                                        (cond
+                                          (de/entity? v)
+                                          (entity->map v)
+                                          (and (coll? v) (every? de/entity? v))
+                                          (map entity->map v)
 
 
-(defn get-latest-journals
-  [db n]
-  (let [today (date-time-util/date->int (js/Date.))]
-    (->>
-     (d/q '[:find [(pull ?page [:db/id :block/journal-day]) ...]
-            :in $ ?today
-            :where
-            [?page :block/name ?page-name]
-            [?page :block/journal-day ?journal-day]
-            [(<= ?journal-day ?today)]]
-          db
-          today)
-     (sort-by :block/journal-day)
-     (reverse)
-     (take n)
-     (mapcat (fn [p]
-               (d/datoms db :eavt (:db/id p)))))))
+                                          :else
+                                          v)))
+                         block-refs-count?
+                         (assoc :block.temp/refs-count (count (:block/_refs block))))]
+            (cond->
+             {:block block'}
+              children?
+              (assoc :children children))))))))
 
 
-(defn get-all-pages
-  "Get all pages including property page's default value"
+(defn get-latest-journals
   [db]
   [db]
-  (let [datoms (d/datoms db :avet :block/name)]
-    (mapcat (fn [d]
-              (let [datoms (d/datoms db :eavt (:e d))]
-                (mapcat
-                 (fn [d]
-                   (if (keyword-identical? (:a d) :logseq.property/default-value)
-                     (concat
-                      (d/datoms db :eavt (:v d))
-                      datoms)
-                     datoms))
-                 datoms))) datoms)))
+  (let [today (date-time-util/date->int (js/Date.))]
+    (->> (d/datoms db :avet :block/journal-day)
+         vec
+         rseq
+         (keep (fn [d]
+                 (and (<= (:v d) today)
+                      (let [e (d/entity db (:e d))]
+                        (when (and (common-entity-util/journal? e) (:db/id e))
+                          e))))))))
 
 
 (defn get-page->refs-count
 (defn get-page->refs-count
   [db]
   [db]
@@ -225,9 +245,19 @@
 
 
 (defn get-structured-datoms
 (defn get-structured-datoms
   [db]
   [db]
-  (->> (d/datoms db :avet :block/closed-value-property)
-       (mapcat (fn [d]
-                 (d/datoms db :eavt (:e d))))))
+  (let [class-property-id (:db/id (d/entity db :logseq.class/Property))]
+    (->> (concat
+          (d/datoms db :avet :block/tags :logseq.class/Tag)
+          (d/datoms db :avet :block/tags :logseq.class/Property)
+          (d/datoms db :avet :block/closed-value-property))
+         (mapcat (fn [d]
+                   (let [block-datoms (d/datoms db :eavt (:e d))
+                         property-desc-datoms (when (= (:v d) class-property-id)
+                                                (when-let [desc (:logseq.property/description (d/entity db (:e d)))]
+                                                  (d/datoms db :eavt (:db/id desc))))]
+                     (if property-desc-datoms
+                       (concat block-datoms property-desc-datoms)
+                       block-datoms)))))))
 
 
 (defn get-favorites
 (defn get-favorites
   "Favorites page and its blocks"
   "Favorites page and its blocks"
@@ -248,8 +278,21 @@
   (let [page-id (get-first-page-by-name db common-config/views-page-name)
   (let [page-id (get-first-page-by-name db common-config/views-page-name)
         children (when page-id (:block/_parent (d/entity db page-id)))]
         children (when page-id (:block/_parent (d/entity db page-id)))]
     (when (seq children)
     (when (seq children)
-      (mapcat (fn [b] (d/datoms db :eavt (:db/id b)))
-              children))))
+      (into
+       (mapcat (fn [b] (d/datoms db :eavt (:db/id b)))
+               children)
+       (d/datoms db :eavt page-id)))))
+
+(defn get-recent-updated-pages
+  [db]
+  (->> (d/datoms db :avet :block/updated-at)
+       vec
+       rseq
+       (keep (fn [datom]
+               (let [e (d/entity db (:e datom))]
+                 (when (and (common-entity-util/page? e) (not (entity-util/hidden? e)))
+                   e))))
+       (take 30)))
 
 
 (defn get-initial-data
 (defn get-initial-data
   "Returns current database schema and initial data.
   "Returns current database schema and initial data.
@@ -270,19 +313,23 @@
                         :logseq.property/empty-placeholder])
                         :logseq.property/empty-placeholder])
         favorites (when db-graph? (get-favorites db))
         favorites (when db-graph? (get-favorites db))
         views (when db-graph? (get-views-data db))
         views (when db-graph? (get-views-data db))
-        latest-journals (get-latest-journals db 1)
         all-files (get-all-files db)
         all-files (get-all-files db)
-        all-pages (get-all-pages db)
         structured-datoms (when db-graph?
         structured-datoms (when db-graph?
                             (get-structured-datoms db))
                             (get-structured-datoms db))
+        recent-updated-pages (let [pages (get-recent-updated-pages db)]
+                               (mapcat (fn [p] (d/datoms db :eavt (:db/id p))) pages))
+        pages-datoms (let [contents-id (get-first-page-by-title db "Contents")
+                           views-id (get-first-page-by-title db common-config/views-page-name)]
+                       (mapcat #(d/datoms db :eavt %)
+                               (remove nil? [contents-id views-id])))
         data (distinct
         data (distinct
               (concat idents
               (concat idents
-                      all-pages
                       structured-datoms
                       structured-datoms
                       favorites
                       favorites
+                      recent-updated-pages
                       views
                       views
-                      latest-journals
-                      all-files))]
+                      all-files
+                      pages-datoms))]
     {:schema schema
     {:schema schema
      :initial-data data}))
      :initial-data data}))
 
 

+ 577 - 0
deps/db/src/logseq/db/common/view.cljs

@@ -0,0 +1,577 @@
+(ns logseq.db.common.view
+  "Main namespace for view fns."
+  (:require [cljs.reader :as reader]
+            [clojure.set :as set]
+            [clojure.string :as string]
+            [datascript.core :as d]
+            [datascript.impl.entity :as de]
+            [logseq.common.log :as log]
+            [logseq.common.util :as common-util]
+            [logseq.db :as ldb]
+            [logseq.db.frontend.class :as db-class]
+            [logseq.db.frontend.entity-plus :as entity-plus]
+            [logseq.db.frontend.entity-util :as entity-util]
+            [logseq.db.frontend.property :as db-property]
+            [logseq.db.frontend.property.type :as db-property-type]
+            [logseq.db.frontend.rules :as rules]))
+
+(def valid-type-for-sort? (some-fn number? string? boolean?))
+
+(defn get-property-value-for-search
+  [block property]
+  (let [v (get block (:db/ident property))]
+    (if (valid-type-for-sort? v)        ;fast path
+      v
+      (let [typ (:logseq.property/type property)
+            many? (keyword-identical? :db.cardinality/many (get property :db/cardinality))
+            number-type? (or (keyword-identical? :number typ)
+                             (keyword-identical? :datetime typ))]
+        (if many?
+          (let [col (->> (if (db-property-type/all-ref-property-types typ) (map db-property/property-value-content v) v)
+                         (remove nil?))]
+            (if number-type?
+              (reduce + (filter number? col))
+              (string/join ", " col)))
+          (let [v' (if (db-property-type/all-ref-property-types typ) (db-property/property-value-content v) v)]
+            (cond
+              (and number-type? (number? v')) v'
+              :else v')))))))
+
+(defn- get-value-for-sort
+  [property]
+  (let [db-ident (or (:db/ident property) (:id property))
+        closed-values (seq (:property/closed-values property))
+        closed-value->sort-number (when closed-values
+                                    (->> (zipmap (map :db/id closed-values)
+                                                 (if (every? :block/order closed-values)
+                                                   (map :block/order closed-values)
+                                                   (range 0 (count closed-values))))
+                                         (into {})))
+        get-property-value-fn (fn [entity]
+                                (if (de/entity? property)
+                                  (get-property-value-for-search entity property)
+                                  (get entity db-ident)))]
+    (fn [entity]
+      (cond
+        closed-values
+        (closed-value->sort-number (:db/id (get entity db-ident)))
+        :else
+        (let [v (get-property-value-fn entity)]
+          (when (valid-type-for-sort? v)
+            v))))))
+
+(defn- by-sorting
+  [sorting]
+  (let [get-value+cmp
+        (map
+         (fn [{:keys [get-value asc?]}]
+           [get-value
+            (if asc? compare #(compare %2 %1))])
+         sorting)]
+    (fn [a b]
+      (reduce
+       (fn [order [get-value cmp]]
+         (if (zero? order)
+           (cmp (get-value a) (get-value b))
+           (reduced order)))
+       0 get-value+cmp))))
+
+(defn- by-one-sorting
+  [{:keys [asc? get-value]}]
+  (let [cmp (if asc? compare #(compare %2 %1))]
+    (fn [a b]
+      (cmp (get-value a) (get-value b)))))
+
+(defn- sort-ref-entities-by-single-property
+  "get all entities sorted by `major-sorting`"
+  [entities {:keys [_id asc?]} get-value-fn]
+  (let [sorting {:asc? asc?
+                 :get-value get-value-fn}
+        sort-cmp (by-one-sorting sorting)]
+    (sort sort-cmp entities)))
+
+(defn- sort-by-single-property
+  [db {:keys [id asc?] :as sorting} entities partition?]
+  (let [property (or (d/entity db id) {:db/ident id})
+        get-value-fn (memoize (get-value-for-sort property))
+        sorted-entities (cond
+                          (= id :block.temp/refs-count)
+                          (cond-> (sort-by :block.temp/refs-count entities)
+                            (not asc?)
+                            reverse)
+
+                          (not (ldb/db-based-graph? db)) ; file graph properties don't support index
+                          (sort (by-sorting
+                                 [{:get-value get-value-fn
+                                   :asc? asc?}]) entities)
+
+                          :else
+                          (let [ref-type? (= :db.type/ref (:db/valueType property))]
+                            (if ref-type?
+                              (sort-ref-entities-by-single-property entities sorting get-value-fn)
+                              (let [datoms (cond->
+                                            (->> (d/datoms db :avet id)
+                                                 (common-util/distinct-by :e)
+                                                 vec)
+                                             (not asc?)
+                                             rseq)
+                                    row-ids (set (map :db/id entities))
+                                    id->row (zipmap (map :db/id entities) entities)]
+                                (keep
+                                 (fn [d]
+                                   (when (row-ids (:e d))
+                                     (id->row (:e d))))
+                                 datoms)))))]
+    (if partition?
+      (partition-by get-value-fn sorted-entities)
+      sorted-entities)))
+
+(defn- sort-entities-by-minor-sorting
+  "minor-sorting - [{:keys [id asc?]} ...]"
+  [db partitioned-entities-by-major-sorting minor-sorting]
+  (let [sorting
+        (map (fn [{:keys [id asc?]}]
+               (let [property (d/entity db id)]
+                 {:asc? asc?
+                  :get-value (memoize (get-value-for-sort property))}))
+             minor-sorting)
+        sort-cmp (by-sorting sorting)]
+    (mapcat (fn [entities] (sort sort-cmp entities)) partitioned-entities-by-major-sorting)))
+
+(defn sort-entities
+  [db sorting entities]
+  (let [major-sorting (or (first sorting)
+                          {:id :block/updated-at :asc? false})
+        minor-sorting (seq (rest sorting))
+        major-sorted-entities
+        (sort-by-single-property db major-sorting entities (not-empty minor-sorting))]
+    (if minor-sorting
+      (sort-entities-by-minor-sorting db major-sorted-entities minor-sorting)
+      major-sorted-entities)))
+
+(defn get-property-value-content
+  [db value]
+  (when value
+    (cond
+      (uuid? value)
+      (db-property/property-value-content (d/entity db [:block/uuid value]))
+      (de/entity? value)
+      (db-property/property-value-content value)
+      (keyword? value)
+      (str value)
+      :else
+      value)))
+
+(defn- ^:large-vars/cleanup-todo row-matched?
+  [db row filters input]
+  (let [or? (:or? filters)
+        check-f (if or? some every?)]
+    (and
+     (if (string/blank? input)
+       true
+       (string/includes? (string/lower-case (:block/title row)) (string/lower-case input)))
+     (check-f
+      (fn [[property-ident operator match]]
+        (if (nil? match)
+          true
+          (let [value (get row property-ident)
+                value' (cond
+                         (set? value) value
+                         (nil? value) #{}
+                         :else #{value})
+                entity? (de/entity? (first value'))
+                result
+                (case operator
+                  :is
+                  (if (boolean? match)
+                    (= (boolean (get-property-value-content db (get row property-ident))) match)
+                    (cond
+                      (empty? match)
+                      true
+                      (and (empty? match) (empty? value'))
+                      true
+                      :else
+                      (if entity?
+                        (boolean (seq (set/intersection (set (map :block/uuid value')) match)))
+                        (boolean (seq (set/intersection (set value') match))))))
+
+                  :is-not
+                  (if (boolean? match)
+                    (not= (boolean (get-property-value-content db (get row property-ident))) match)
+                    (cond
+                      (and (empty? match) (seq value'))
+                      true
+                      (and (seq match) (empty? value'))
+                      true
+                      :else
+                      (if entity?
+                        (boolean (empty? (set/intersection (set (map :block/uuid value')) match)))
+                        (boolean (empty? (set/intersection (set value') match))))))
+
+                  :text-contains
+                  (some (fn [v]
+                          (if-let [property-value (get-property-value-content db v)]
+                            (string/includes? (string/lower-case property-value) (string/lower-case match))
+                            false))
+                        value')
+
+                  :text-not-contains
+                  (not-any? #(string/includes? (str (get-property-value-content db %)) match) value')
+
+                  :number-gt
+                  (if match (some #(> (get-property-value-content db %) match) value') true)
+                  :number-gte
+                  (if match (some #(>= (get-property-value-content db %) match) value') true)
+                  :number-lt
+                  (if match (some #(< (get-property-value-content db %) match) value') true)
+                  :number-lte
+                  (if match (some #(<= (get-property-value-content db %) match) value') true)
+
+                  :between
+                  (if (seq match)
+                    (some (fn [value-entity]
+                            (let [[start end] match
+                                  value (get-property-value-content db value-entity)
+                                  conditions [(if start (<= start value) true)
+                                              (if end (<= value end) true)]]
+                              (if (seq match) (every? true? conditions) true))) value')
+                    true)
+
+                  :date-before
+                  (if match (some #(< (:block/journal-day %) (:block/journal-day match)) value') true)
+
+                  :date-after
+                  (if match (some #(> (:block/journal-day %) (:block/journal-day match)) value') true)
+
+                  :before
+                  (let [search-value (common-util/get-timestamp match)]
+                    (if search-value (<= (get row property-ident) search-value) true))
+
+                  :after
+                  (let [search-value (common-util/get-timestamp match)]
+                    (if search-value (>= (get row property-ident) search-value) true))
+
+                  true)]
+            result)))
+      (:filters filters)))))
+
+(defn filter-blocks
+  [filters ref-blocks]
+  (let [exclude-ids (set (map :db/id (:excluded filters)))
+        include-ids (set (map :db/id (:included filters)))
+        get-ids (fn [block]
+                  (set (map :db/id (:block/path-refs block))))]
+    (cond->> ref-blocks
+      (seq exclude-ids)
+      (remove (fn [block]
+                (let [ids (get-ids block)]
+                  (seq (set/intersection exclude-ids ids)))))
+
+      (seq include-ids)
+      (filter (fn [block]
+                (let [ids (get-ids block)]
+                  (set/subset? include-ids ids)))))))
+
+(defn get-filters
+  [db page]
+  (let [db-based? (entity-plus/db-based-graph? db)]
+    (if db-based?
+      (let [included-pages (:logseq.property.linked-references/includes page)
+            excluded-pages (:logseq.property.linked-references/excludes page)]
+        (when (or (seq included-pages) (seq excluded-pages))
+          {:included included-pages
+           :excluded excluded-pages}))
+      (let [k :filters
+            properties (:block/properties page)
+            properties-str (or (get properties k) "{}")]
+        (try (let [result (reader/read-string properties-str)]
+               (when (seq result)
+                 (let [excluded-pages (->> (filter #(false? (second %)) result)
+                                           (keep first)
+                                           (keep #(ldb/get-page db %)))
+                       included-pages (->> (filter #(true? (second %)) result)
+                                           (keep first)
+                                           (keep #(ldb/get-page db %)))]
+                   {:included included-pages
+                    :excluded excluded-pages})))
+             (catch :default e
+               (log/error :syntax/filters e)))))))
+
+(defn- get-linked-references
+  [db id]
+  (let [db-based? (ldb/db-based-graph? db)
+        entity (d/entity db id)
+        ids (set (cons id (ldb/get-block-alias db id)))
+        refs (mapcat (fn [id] (:block/_refs (d/entity db id))) ids)
+        page-filters (get-filters db entity)
+        full-ref-blocks (->> refs
+                             (remove (fn [block]
+                                       (if db-based?
+                                         (or
+                                          (= (:db/id block) id)
+                                          (= id (:db/id (:block/page block)))
+                                          (ldb/hidden? (:block/page block))
+                                          (contains? (set (map :db/id (:block/tags block))) (:db/id entity))
+                                          (some? (get block (:db/ident entity))))
+                                         (or
+                                          (= (:db/id block) id)
+                                          (= id (:db/id (:block/page block)))))))
+                             (common-util/distinct-by :db/id))
+        ref-blocks (cond->> full-ref-blocks
+                     (seq page-filters)
+                     (filter-blocks page-filters))
+        ref-pages-count (->> (mapcat (fn [block]
+                                       (->>
+                                        (cons
+                                         (:block/title (:block/page block))
+                                         (map (fn [b]
+                                                (when (and (ldb/page? b) (not= (:db/id b) id))
+                                                  (:block/title b)))
+                                              (:block/refs block)))
+                                        distinct))
+                                     full-ref-blocks)
+                             (remove nil?)
+                             (frequencies))]
+    {:ref-pages-count ref-pages-count
+     :ref-blocks ref-blocks}))
+
+(defn- get-unlinked-references
+  [db id]
+  (let [entity (d/entity db id)
+        title (string/lower-case (:block/title entity))]
+    (when-not (string/blank? title)
+      (let [ids (->> (d/datoms db :avet :block/title)
+                     (keep (fn [d]
+                             (when (and (not= id (:e d)) (string/includes? (string/lower-case (:v d)) title))
+                               (:e d)))))]
+        (keep
+         (fn [eid]
+           (let [e (d/entity db eid)]
+             (when-not (or (some #(= id %) (map :db/id (:block/refs e)))
+                           (:block/link e)
+                           (ldb/page? e))
+               e)))
+         ids)))))
+
+(defn- get-exclude-page-ids
+  [db]
+  (->>
+   (concat
+    (d/datoms db :avet :logseq.property/hide? true)
+    (d/datoms db :avet :logseq.property/built-in? true)
+    (d/datoms db :avet :block/tags (:db/id (d/entity db :logseq.class/Property))))
+   (map :e)
+   set))
+
+(defn- get-entities-for-all-pages [db sorting property-ident {:keys [db-based?]}]
+  (let [refs-count? (and (coll? sorting) (some (fn [m] (= (:id m) :block.temp/refs-count)) sorting))
+        exclude-ids (when db-based? (get-exclude-page-ids db))]
+    (keep (fn [d]
+            (let [e (d/entity db (:e d))]
+              (when-not (if db-based?
+                          (exclude-ids (:db/id e))
+                          (or (ldb/hidden-or-internal-tag? e)
+                              (entity-util/property? e)
+                              (entity-util/built-in? e)
+                              (nil? (:block/title e))))
+                (cond-> e
+                  refs-count?
+                  (assoc :block.temp/refs-count (count (:block/_refs e)))))))
+          (d/datoms db :avet property-ident))))
+
+(defn- get-entities
+  [db view feat-type property-ident view-for-id* sorting]
+  (let [view-for (:logseq.property/view-for view)
+        view-for-id (or (:db/id view-for) view-for-id*)
+        non-hidden-e (fn [id] (let [e (d/entity db id)]
+                                (when-not (entity-util/hidden? e)
+                                  e)))
+        db-based? (entity-plus/db-based-graph? db)]
+    (case feat-type
+      :all-pages
+      (get-entities-for-all-pages db sorting property-ident {:db-based? db-based?})
+
+      :class-objects
+      (let [class-id view-for-id
+            class-children (db-class/get-structured-children db class-id)
+            class-ids (distinct (conj class-children class-id))
+            datoms (mapcat (fn [id] (d/datoms db :avet :block/tags id)) class-ids)]
+        (keep (fn [d] (non-hidden-e (:e d))) datoms))
+
+      :property-objects
+      (->>
+       (d/q
+        '[:find [?b ...]
+          :in $ % ?prop
+          :where
+          (has-property-or-default-value? ?b ?prop)]
+        db
+        (rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value]
+                             {:deps rules/rules-dependencies})
+        property-ident)
+       (keep (fn [id] (non-hidden-e id))))
+
+      :linked-references
+      (get-linked-references db view-for-id)
+
+      :unlinked-references
+      (get-unlinked-references db view-for-id)
+
+      :query-result
+      nil
+
+      nil)))
+
+(defn- get-view-entities
+  [db view-id & {:keys [view-for-id view-feature-type sorting]}]
+  (let [view (d/entity db view-id)
+        feat-type (or view-feature-type (:logseq.property.view/feature-type view))
+        sorting (or sorting (:logseq.property.table/sorting view))
+        index-attr (case feat-type
+                     :all-pages
+                     :block/name
+                     :class-objects
+                     :block/tags
+                     :property-objects
+                     (let [view-for (:logseq.property/view-for view)]
+                       (:db/ident view-for))
+                     nil)]
+    (get-entities db view feat-type index-attr view-for-id sorting)))
+
+(defn- get-view-property-values
+  [db property-ident {:keys [view-id query-entity-ids]}]
+  (let [empty-id (:db/id (d/entity db :logseq.property/empty-placeholder))
+        entities-result (get-view-entities db view-id)
+        entities (cond
+                   query-entity-ids
+                   (keep #(d/entity db %) query-entity-ids)
+                   (map? entities-result)
+                   (:ref-blocks entities-result)
+                   :else
+                   entities-result)]
+    (->> (mapcat (fn [entity]
+                   (let [v (get entity property-ident)]
+                     (if (set? v) v #{v})))
+                 entities)
+         (remove nil?)
+         (keep (fn [e]
+                 (when-let [label (get-property-value-content db e)]
+                   (when-not (or (string/blank? (str label))
+                                 (= empty-id (:db/id e)))
+                     {:label (str label)
+                      :value (if (de/entity? e)
+                               (select-keys e [:db/id :block/uuid])
+                               e)}))))
+         (common-util/distinct-by :label))))
+
+(defn ^:api get-property-values
+  [db property-ident {:keys [view-id _query-entity-ids] :as option}]
+  (let [property (d/entity db property-ident)
+        default-value (:logseq.property/default-value property)
+        ref-type? (= :db.type/ref (:db/valueType property))
+        values (if view-id
+                 (get-view-property-values db property-ident option)
+                 ;; get all values
+                 (->> (d/datoms db :avet property-ident)
+                      (map (fn [d]
+                             (:v d)))
+                      distinct
+                      (map (fn [v]
+                             (let [e (when ref-type? (d/entity db v))
+                                   [label value] (cond ref-type?
+                                                       [(db-property/property-value-content e)
+                                                        (select-keys e [:db/id :block/uuid])]
+                                                       (= :datetime (:logseq.property/type property))
+                                                       [v v]
+                                                       :else
+                                                       [(str v) v])]
+                               {:label label
+                                :value value})))
+                      (common-util/distinct-by :label)))]
+    (if default-value
+      (cons {:label (get-property-value-content db default-value)
+             :value (select-keys default-value [:db/id :block/uuid])}
+            values)
+      values)))
+
+(defn ^:api ^:large-vars/cleanup-todo get-view-data
+  [db view-id {:keys [journals? _view-for-id view-feature-type input query-entity-ids filters sorting]
+               :as opts}]
+  ;; TODO: create a view for journals maybe?
+  (cond
+    journals?
+    (let [ids (->> (ldb/get-latest-journals db)
+                   (mapv :db/id))]
+      {:count (count ids)
+       :data ids})
+    :else
+    (let [view (d/entity db view-id)
+          db-based? (ldb/db-based-graph? db)
+          group-by-property (:logseq.property.view/group-by-property view)
+          group-by-property-ident (if db-based?
+                                    (:db/ident group-by-property)
+                                    (when (contains? #{:linked-references :unlinked-references} view-feature-type)
+                                      :block/page))
+          group-by-closed-values? (some? (:property/closed-values group-by-property))
+          ref-property? (= (:db/valueType group-by-property) :db.type/ref)
+          filters (or (:logseq.property.table/filters view) filters)
+          list-view? (= :logseq.property.view/type.list (:db/ident (:logseq.property.view/type view)))
+          feat-type (or view-feature-type (:logseq.property.view/feature-type view))
+          query? (= feat-type :query-result)
+          entities-result (if query?
+                            (keep #(d/entity db %) query-entity-ids)
+                            (get-view-entities db view-id opts))
+          entities (if (= feat-type :linked-references)
+                     (:ref-blocks entities-result)
+                     entities-result)
+          sorting (let [sorting* (:logseq.property.table/sorting view)]
+                    (if (or (= sorting* :logseq.property/empty-placeholder) (empty? sorting*))
+                      (or sorting [{:id :block/updated-at, :asc? false}])
+                      sorting*))
+          filtered-entities (if (or (seq filters) (not (string/blank? input)))
+                              (filter (fn [row] (row-matched? db row filters input)) entities)
+                              entities)
+          group-by-page? (= group-by-property-ident :block/page)
+          result (if group-by-property-ident
+                   (->> filtered-entities
+                        (group-by group-by-property-ident)
+                        (seq)
+                        (sort-by (fn [[by-value _]]
+                                   (cond
+                                     group-by-page?
+                                     (:block/updated-at by-value)
+                                     group-by-closed-values?
+                                     (:block/order by-value)
+                                     ref-property?
+                                     (db-property/property-value-content by-value)
+                                     :else
+                                     by-value))
+                                 (if group-by-page? #(compare %2 %1) compare)))
+                   (sort-entities db sorting filtered-entities))
+          data' (if group-by-property-ident
+                  (map
+                   (fn [[by-value entities]]
+                     (let [by-value' (if (de/entity? by-value)
+                                       (select-keys by-value [:db/id :block/uuid :block/title :block/name :logseq.property/value :logseq.property/icon :block/tags])
+                                       by-value)
+                           pages? (not (some :block/page entities))
+                           group (if (and list-view? (not pages?))
+                                   (let [parent-groups (->> entities
+                                                            (group-by :block/parent)
+                                                            (sort-by (fn [[parent _]] (:block/order parent))))]
+                                     (map
+                                      (fn [[_parent blocks]]
+                                        [(:block/uuid (first blocks))
+                                         (map (fn [b]
+                                                {:db/id (:db/id b)
+                                                 :block/parent (:block/uuid (:block/parent b))}) blocks)])
+                                      parent-groups))
+                                   (map :db/id entities))]
+                       [by-value' group]))
+                   result)
+                  (map :db/id result))]
+      (cond->
+       {:count (count filtered-entities)
+        :data data'}
+        (= feat-type :linked-references)
+        (assoc :ref-pages-count (:ref-pages-count entities-result))))))

+ 1 - 1
deps/db/src/logseq/db/file_based/rules.cljc

@@ -90,4 +90,4 @@
    :page-ref
    :page-ref
    '[(page-ref ?b ?page-name)
    '[(page-ref ?b ?page-name)
      [?b :block/path-refs ?br]
      [?b :block/path-refs ?br]
-     [?br :block/name ?page-name]]})
+     [?br :block/name ?page-name]]})

+ 14 - 0
deps/db/src/logseq/db/frontend/class.cljs

@@ -1,9 +1,11 @@
 (ns logseq.db.frontend.class
 (ns logseq.db.frontend.class
   "Class related fns for DB graphs and frontend/datascript usage"
   "Class related fns for DB graphs and frontend/datascript usage"
   (:require [clojure.set :as set]
   (:require [clojure.set :as set]
+            [datascript.core :as d]
             [flatland.ordered.map :refer [ordered-map]]
             [flatland.ordered.map :refer [ordered-map]]
             [logseq.common.defkeywords :refer [defkeywords]]
             [logseq.common.defkeywords :refer [defkeywords]]
             [logseq.db.frontend.db-ident :as db-ident]
             [logseq.db.frontend.db-ident :as db-ident]
+            [logseq.db.frontend.rules :as rules]
             [logseq.db.sqlite.util :as sqlite-util]))
             [logseq.db.sqlite.util :as sqlite-util]))
 
 
 ;; Main class vars
 ;; Main class vars
@@ -115,6 +117,18 @@
   "Built-in classes that are hidden in a few contexts like property values"
   "Built-in classes that are hidden in a few contexts like property values"
   #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset})
   #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset})
 
 
+(defn get-structured-children
+  [db eid]
+  (->>
+   (d/q '[:find [?children ...]
+          :in $ ?parent %
+          :where
+          (parent ?parent ?children)]
+        db
+        eid
+        (:parent rules/rules))
+   (remove #{eid})))
+
 ;; Helper fns
 ;; Helper fns
 ;; ==========
 ;; ==========
 
 

+ 1 - 1
deps/db/src/logseq/db/frontend/content.cljs

@@ -161,4 +161,4 @@
             (common-util/replace-ignore-case (str "#" id-ref) id-ref))))
             (common-util/replace-ignore-case (str "#" id-ref) id-ref))))
     content
     content
     (sort-refs tags))
     (sort-refs tags))
-   (string/trim)))
+   (string/trim)))

+ 6 - 6
deps/db/src/logseq/db/frontend/entity_plus.cljc

@@ -20,7 +20,7 @@
     ;; File graph only attributes. Can these be removed if this is only called in db graphs?
     ;; File graph only attributes. Can these be removed if this is only called in db graphs?
     :block/pre-block? :block/scheduled :block/deadline :block/type :block/name :block/marker
     :block/pre-block? :block/scheduled :block/deadline :block/type :block/name :block/marker
 
 
-    :block.temp/ast-title :block.temp/top? :block.temp/bottom? :block.temp/search?
+    :block.temp/ast-title :block.temp/search?
     :block.temp/fully-loaded? :block.temp/ast-body
     :block.temp/fully-loaded? :block.temp/ast-body
 
 
     :db/valueType :db/cardinality :db/ident :db/index
     :db/valueType :db/cardinality :db/ident :db/index
@@ -77,7 +77,7 @@
   "Whether the current graph is db-only"
   "Whether the current graph is db-only"
   [db]
   [db]
   (when db
   (when db
-    (= "db" (:kv/value (entity-memoized db :logseq.kv/db-type)))))
+    (identical? "db" (:kv/value (entity-memoized db :logseq.kv/db-type)))))
 
 
 (defn- get-journal-title
 (defn- get-journal-title
   [db e]
   [db e]
@@ -92,7 +92,7 @@
       (get-journal-title db e)
       (get-journal-title db e)
       (let [search? (get (.-kv e) :block.temp/search?)]
       (let [search? (get (.-kv e) :block.temp/search?)]
         (or
         (or
-         (when-not (and search? (= k :block/title))
+         (when-not (and search? (keyword-identical? k :block/title))
            (get (.-kv e) k))
            (get (.-kv e) k))
          (let [result (lookup-entity e k default-value)
          (let [result (lookup-entity e k default-value)
                refs (:block/refs e)
                refs (:block/refs e)
@@ -115,7 +115,7 @@
        (when (qualified-keyword? k)
        (when (qualified-keyword? k)
          (when-let [property (entity-memoized db k)]
          (when-let [property (entity-memoized db k)]
            (let [property-type (lookup-entity property :logseq.property/type nil)]
            (let [property-type (lookup-entity property :logseq.property/type nil)]
-             (if (= :checkbox property-type)
+             (if (keyword-identical? :checkbox property-type)
                (lookup-entity property :logseq.property/scalar-default-value nil)
                (lookup-entity property :logseq.property/scalar-default-value nil)
                (lookup-entity property :logseq.property/default-value nil)))))))))
                (lookup-entity property :logseq.property/default-value nil)))))))))
 
 
@@ -177,8 +177,8 @@
            (lookup-entity e :block/_parent default-value)
            (lookup-entity e :block/_parent default-value)
 
 
            :property/closed-values
            :property/closed-values
-           (->> (lookup-entity e :block/_closed-value-property default-value)
-                (sort-by :block/order))
+           (some->> (lookup-entity e :block/_closed-value-property default-value)
+                    (sort-by :block/order))
 
 
            (lookup-kv-with-default-value db e k default-value))))
            (lookup-kv-with-default-value db e k default-value))))
      (catch :default e
      (catch :default e

+ 5 - 0
deps/db/src/logseq/db/frontend/entity_util.cljs

@@ -81,3 +81,8 @@
                      :logseq.class/Whiteboard :whiteboard
                      :logseq.class/Whiteboard :whiteboard
                      :logseq.class/Page :page}]
                      :logseq.class/Page :page}]
     (set (map #(ident->type (:db/ident %)) (:block/tags entity)))))
     (set (map #(ident->type (:db/ident %)) (:block/tags entity)))))
+
+(defn built-in?
+  "Built-in page or block"
+  [entity]
+  (:logseq.property/built-in? entity))

+ 1 - 1
deps/db/src/logseq/db/frontend/schema.cljs

@@ -37,7 +37,7 @@
          (map (juxt :major :minor)
          (map (juxt :major :minor)
               [(parse-schema-version x) (parse-schema-version y)])))
               [(parse-schema-version x) (parse-schema-version y)])))
 
 
-(def version (parse-schema-version "64.4"))
+(def version (parse-schema-version "64.5"))
 
 
 (defn major-version
 (defn major-version
   "Return a number.
   "Return a number.

+ 1 - 1
deps/db/src/logseq/db/sqlite/export.cljs

@@ -920,4 +920,4 @@
         (-> (sqlite-build/build-blocks-tx (dissoc export-map'' ::graph-files ::kv-values ::export-type))
         (-> (sqlite-build/build-blocks-tx (dissoc export-map'' ::graph-files ::kv-values ::export-type))
             (assoc :misc-tx (vec (concat (::graph-files export-map'')
             (assoc :misc-tx (vec (concat (::graph-files export-map'')
                                          (::kv-values export-map'')))))
                                          (::kv-values export-map'')))))
-        (sqlite-build/build-blocks-tx export-map'')))))
+        (sqlite-build/build-blocks-tx export-map'')))))

+ 20 - 34
deps/db/test/logseq/db/common/sqlite_test.cljs

@@ -1,12 +1,15 @@
 (ns logseq.db.common.sqlite-test
 (ns logseq.db.common.sqlite-test
-  (:require [cljs.test :refer [deftest async use-fixtures is testing]]
-            ["fs" :as fs]
+  "This ns is the only one to test against file based datascript connections.
+   These are useful integration tests"
+  (:require ["fs" :as fs]
             ["path" :as node-path]
             ["path" :as node-path]
+            [cljs.test :refer [deftest async use-fixtures is testing]]
             [datascript.core :as d]
             [datascript.core :as d]
             [logseq.db.common.sqlite :as sqlite-common-db]
             [logseq.db.common.sqlite :as sqlite-common-db]
-            [logseq.common.util.date-time :as date-time-util]
+            [logseq.db.sqlite.build :as sqlite-build]
             [logseq.db.sqlite.cli :as sqlite-cli]
             [logseq.db.sqlite.cli :as sqlite-cli]
-            [clojure.string :as string]))
+            [logseq.db.sqlite.create-graph :as sqlite-create-graph]
+            [logseq.db.test.helper :as db-test]))
 
 
 (use-fixtures
 (use-fixtures
   :each
   :each
@@ -41,34 +44,17 @@
           "Correct file with content is found"))))
           "Correct file with content is found"))))
 
 
 (deftest restore-initial-data
 (deftest restore-initial-data
-  (testing "Restore a journal page"
-    (create-graph-dir "tmp/graphs" "test-db")
-    (let [conn* (sqlite-cli/open-db! "tmp/graphs" "test-db")
-          page-uuid (random-uuid)
-          block-uuid (random-uuid)
-          created-at (js/Date.now)
-          date-int (date-time-util/date->int (js/Date.))
-          date-title (date-time-util/int->journal-title date-int "MMM do, yyyy")
-          blocks [{:db/id 100001
-                   :block/uuid page-uuid
-                   :block/journal-day date-int
-                   :block/name (string/lower-case date-title)
-                   :block/title date-title
-                   :block/created-at created-at
-                   :block/updated-at created-at}
-                  {:db/id 100002
-                   :block/title "test"
-                   :block/uuid block-uuid
-                   :block/page {:db/id 100001}
-                   :block/created-at created-at
-                   :block/updated-at created-at}]
-          _ (d/transact! conn* blocks)
+  (create-graph-dir "tmp/graphs" "test-db")
+  (let [conn* (sqlite-cli/open-db! "tmp/graphs" "test-db")
+        _ (d/transact! conn* (sqlite-create-graph/build-db-initial-data "{}"))
+        {:keys [init-tx]}
+        (sqlite-build/build-blocks-tx
+         {:pages-and-blocks
+          [{:page {:block/title "page1"}
+            :blocks [{:block/title "b1"}]}]})
+        _ (d/transact! conn* init-tx)
           ;; Simulate getting data from sqlite and restoring it for frontend
           ;; Simulate getting data from sqlite and restoring it for frontend
-          {:keys [schema initial-data]} (sqlite-common-db/get-initial-data @conn*)
-          conn (sqlite-common-db/restore-initial-data initial-data schema)]
-      (is (= (take 1 blocks)
-             (->> (d/q '[:find (pull ?b [*])
-                         :where [?b :block/created-at]]
-                       @conn)
-                  (map first)))
-          "Journal page is included in initial restore while its block is not"))))
+        {:keys [schema initial-data]} (sqlite-common-db/get-initial-data @conn*)
+        conn (sqlite-common-db/restore-initial-data initial-data schema)]
+    (is (some? (db-test/find-page-by-title @conn "page1"))
+        "Restores recently updated page")))

+ 3 - 3
deps/db/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 1 - 1
deps/graph-parser/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18",
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19",
     "better-sqlite3": "9.3.0"
     "better-sqlite3": "9.3.0"
   },
   },
   "dependencies": {
   "dependencies": {

+ 6 - 6
deps/graph-parser/src/logseq/graph_parser/exporter.cljs

@@ -927,12 +927,12 @@
                            (throw (ex-info (str "No parent page found for " (pr-str (:block/uuid (:logseq.property/parent n))))
                            (throw (ex-info (str "No parent page found for " (pr-str (:block/uuid (:logseq.property/parent n))))
                                            {:node n})))))]
                                            {:node n})))))]
     (when-let [parent (get-parent node)]
     (when-let [parent (get-parent node)]
-     (loop [current-parent parent
-            parents' []]
-       (if (and current-parent (not (contains? parents' current-parent)))
-         (recur (get-parent current-parent)
-                (conj parents' current-parent))
-         (vec (reverse parents')))))))
+      (loop [current-parent parent
+             parents' []]
+        (if (and current-parent (not (contains? parents' current-parent)))
+          (recur (get-parent current-parent)
+                 (conj parents' current-parent))
+          (vec (reverse parents')))))))
 
 
 (defn- get-all-existing-page-uuids
 (defn- get-all-existing-page-uuids
   "Returns a map of unique page names mapped to their uuids. The page names
   "Returns a map of unique page names mapped to their uuids. The page names

+ 2 - 2
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -10,6 +10,7 @@
             [logseq.common.util.date-time :as date-time-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.content :as db-content]
+            [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.validate :as db-validate]
             [logseq.db.frontend.validate :as db-validate]
@@ -19,8 +20,7 @@
             [logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
             [logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
             [logseq.graph-parser.test.helper :as test-helper :include-macros true :refer [deftest-async]]
             [logseq.graph-parser.test.helper :as test-helper :include-macros true :refer [deftest-async]]
             [logseq.outliner.db-pipeline :as db-pipeline]
             [logseq.outliner.db-pipeline :as db-pipeline]
-            [promesa.core :as p]
-            [logseq.db.frontend.entity-plus :as entity-plus]))
+            [promesa.core :as p]))
 
 
 ;; Helpers
 ;; Helpers
 ;; =======
 ;; =======

+ 3 - 3
deps/graph-parser/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 1 - 1
deps/outliner/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19"
   },
   },
   "dependencies": {
   "dependencies": {
     "better-sqlite3": "9.3.0",
     "better-sqlite3": "9.3.0",

+ 6 - 4
deps/outliner/src/logseq/outliner/core.cljs

@@ -138,7 +138,9 @@
 
 
 (defn- file-rebuild-block-refs
 (defn- file-rebuild-block-refs
   [repo db date-formatter {:block/keys [properties] :as block}]
   [repo db date-formatter {:block/keys [properties] :as block}]
-  (let [property-key-refs (keys properties)
+  (let [property-key-refs (->> (keys properties)
+                               (keep (fn [property-id]
+                                       (:block/uuid (ldb/get-page db (name property-id))))))
         property-value-refs (->> (vals properties)
         property-value-refs (->> (vals properties)
                                  (mapcat (fn [v]
                                  (mapcat (fn [v]
                                            (cond
                                            (cond
@@ -160,7 +162,6 @@
         property-refs (->> (concat property-key-refs property-value-refs)
         property-refs (->> (concat property-key-refs property-value-refs)
                            (map (fn [id-or-map] (if (uuid? id-or-map) {:block/uuid id-or-map} id-or-map)))
                            (map (fn [id-or-map] (if (uuid? id-or-map) {:block/uuid id-or-map} id-or-map)))
                            (remove (fn [b] (nil? (d/entity db [:block/uuid (:block/uuid b)])))))
                            (remove (fn [b] (nil? (d/entity db [:block/uuid (:block/uuid b)])))))
-
         content-refs (when-let [content (:block/title block)]
         content-refs (when-let [content (:block/title block)]
                        (gp-block/extract-refs-from-text repo db content date-formatter))]
                        (gp-block/extract-refs-from-text repo db content date-formatter))]
     (concat property-refs content-refs)))
     (concat property-refs content-refs)))
@@ -233,7 +234,7 @@
                   db-based?
                   db-based?
                   (dissoc :block/properties))
                   (dissoc :block/properties))
           m* (-> data'
           m* (-> data'
-                 (dissoc :block/children :block/meta :block.temp/top? :block.temp/bottom? :block/unordered
+                 (dissoc :block/children :block/meta :block/unordered
                          :block.temp/ast-title :block.temp/ast-body :block/level :block.temp/fully-loaded?)
                          :block.temp/ast-title :block.temp/ast-body :block/level :block.temp/fully-loaded?)
                  common-util/remove-nils
                  common-util/remove-nils
                  block-with-updated-at
                  block-with-updated-at
@@ -711,7 +712,8 @@
                               (map (fn [uuid'] {:block/uuid uuid'})))
                               (map (fn [uuid'] {:block/uuid uuid'})))
                 tx (assign-temp-id blocks-tx replace-empty-target? target-block)
                 tx (assign-temp-id blocks-tx replace-empty-target? target-block)
                 from-property (:logseq.property/created-from-property target-block)
                 from-property (:logseq.property/created-from-property target-block)
-                property-values-tx (when (and sibling? from-property)
+                many? (= :db.cardinality/many (:db/cardinality from-property))
+                property-values-tx (when (and sibling? from-property many?)
                                      (let [top-level-blocks (filter #(= 1 (:block/level %)) blocks')]
                                      (let [top-level-blocks (filter #(= 1 (:block/level %)) blocks')]
                                        (mapcat (fn [block]
                                        (mapcat (fn [block]
                                                  (when-let [new-id (or (id->new-uuid (:db/id block)) (:block/uuid block))]
                                                  (when-let [new-id (or (id->new-uuid (:db/id block)) (:block/uuid block))]

+ 2 - 2
deps/outliner/src/logseq/outliner/tree.cljs

@@ -53,7 +53,7 @@
 ;; TODO: entity can already be used as a tree
 ;; TODO: entity can already be used as a tree
 (defn blocks->vec-tree
 (defn blocks->vec-tree
   "`blocks` need to be in the same page."
   "`blocks` need to be in the same page."
-  [repo db blocks root-id]
+  [repo db blocks root-id & {:as option}]
   (let [blocks (map (fn [b] (if (de/entity? b)
   (let [blocks (map (fn [b] (if (de/entity? b)
                               (assoc (into {} b) :db/id (:db/id b))
                               (assoc (into {} b) :db/id (:db/id b))
                               b)) blocks)
                               b)) blocks)
@@ -61,7 +61,7 @@
     (if-not root ; custom query
     (if-not root ; custom query
       blocks
       blocks
       (let [result (blocks->vec-tree-aux repo db blocks root)]
       (let [result (blocks->vec-tree-aux repo db blocks root)]
-        (if page?
+        (if (and page? (not (:link option)))
           result
           result
           ;; include root block
           ;; include root block
           (let [root-block (some #(when (= (:db/id %) (:db/id root)) %) blocks)
           (let [root-block (some #(when (= (:db/id %) (:db/id root)) %) blocks)

+ 3 - 3
deps/outliner/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 1 - 1
deps/publishing/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18",
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19",
     "mldoc": "^1.5.9"
     "mldoc": "^1.5.9"
   },
   },
   "dependencies": {
   "dependencies": {

+ 3 - 3
deps/publishing/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 18 - 1
deps/shui/src/logseq/shui/hooks.cljs

@@ -1,7 +1,9 @@
 (ns logseq.shui.hooks
 (ns logseq.shui.hooks
   "React custom hooks."
   "React custom hooks."
   (:refer-clojure :exclude [ref deref])
   (:refer-clojure :exclude [ref deref])
-  (:require [goog.functions :as gfun]
+  (:require [frontend.common.missionary :as c.m]
+            [goog.functions :as gfun]
+            [missionary.core :as m]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (defn- memo-deps
 (defn- memo-deps
@@ -24,6 +26,7 @@
   "setup-fn will be invoked every render of component when no deps arg provided"
   "setup-fn will be invoked every render of component when no deps arg provided"
   ([setup-fn] (rum/use-effect! setup-fn))
   ([setup-fn] (rum/use-effect! setup-fn))
   ([setup-fn deps & {:keys [equal-fn]}]
   ([setup-fn deps & {:keys [equal-fn]}]
+   (assert (fn? setup-fn) "use-effect! setup-fn should be a function")
    (rum/use-effect! (fn [& deps]
    (rum/use-effect! (fn [& deps]
                       (let [result (apply setup-fn deps)]
                       (let [result (apply setup-fn deps)]
                         (when (fn? result) result)))
                         (when (fn? result) result)))
@@ -35,6 +38,7 @@
 (defn use-layout-effect!
 (defn use-layout-effect!
   ([setup-fn] (rum/use-layout-effect! setup-fn))
   ([setup-fn] (rum/use-layout-effect! setup-fn))
   ([setup-fn deps & {:keys [equal-fn]}]
   ([setup-fn deps & {:keys [equal-fn]}]
+   (assert (fn? setup-fn) "use-layout-effect! setup-fn should be a function")
    (rum/use-layout-effect! (fn [& deps]
    (rum/use-layout-effect! (fn [& deps]
                              (let [result (apply setup-fn deps)]
                              (let [result (apply setup-fn deps)]
                                (when (fn? result) result)))
                                (when (fn? result) result)))
@@ -67,3 +71,16 @@
         cb (use-callback (gfun/debounce set-value! msec) [])]
         cb (use-callback (gfun/debounce set-value! msec) [])]
     (use-effect! #(cb value) [value])
     (use-effect! #(cb value) [value])
     debounced-value))
     debounced-value))
+
+(defn use-flow-state
+  "Return values from `flow`, default init-value is nil"
+  ([flow] (use-flow-state nil flow))
+  ([init-value flow]
+   (let [[value set-value!] (use-state init-value)]
+     (use-effect!
+      #(c.m/run-task*
+        (m/reduce
+         (constantly nil)
+         (m/ap (set-value! (m/?> flow)))))
+      [])
+     value)))

+ 13 - 14
deps/shui/src/logseq/shui/table/core.cljc

@@ -16,7 +16,7 @@
 
 
 (defn- row-selected?
 (defn- row-selected?
   [row row-selection]
   [row row-selection]
-  (let [id (:id row)]
+  (let [id (:db/id row)]
     (or
     (or
      (and (:selected-all? row-selection)
      (and (:selected-all? row-selection)
           ;; exclude ids
           ;; exclude ids
@@ -30,14 +30,14 @@
   (boolean
   (boolean
    (or
    (or
     (and (seq (:selected-ids row-selection))
     (and (seq (:selected-ids row-selection))
-         (some (:selected-ids row-selection) (map :db/id rows)))
+         (some (:selected-ids row-selection) rows))
     (and (seq (:exclude-ids row-selection))
     (and (seq (:exclude-ids row-selection))
          (not= (count rows) (count (:exclude-ids row-selection)))))))
          (not= (count rows) (count (:exclude-ids row-selection)))))))
 
 
 (defn- select-all?
 (defn- select-all?
   [row-selection rows]
   [row-selection rows]
   (and (seq (:selected-ids row-selection))
   (and (seq (:selected-ids row-selection))
-       (set/subset? (set (map :db/id rows))
+       (set/subset? (set rows)
                     (:selected-ids row-selection))))
                     (:selected-ids row-selection))))
 
 
 (defn- toggle-selected-all!
 (defn- toggle-selected-all!
@@ -48,7 +48,7 @@
       (and group-by-property value)
       (and group-by-property value)
       (let [new-selection (update row-selection :selected-ids
       (let [new-selection (update row-selection :selected-ids
                                   (fn [ids]
                                   (fn [ids]
-                                    (set/union (set ids) (set (map :db/id (:rows table))))))]
+                                    (set/union (set ids) (set (:rows table)))))]
         (set-row-selection! new-selection))
         (set-row-selection! new-selection))
 
 
       value
       value
@@ -57,7 +57,7 @@
       group-by-property
       group-by-property
       (let [new-selection (update row-selection :selected-ids
       (let [new-selection (update row-selection :selected-ids
                                   (fn [ids]
                                   (fn [ids]
-                                    (set/difference (set ids) (set (map :db/id (:rows table))))))]
+                                    (set/difference (set ids) (set (:rows table)))))]
         (set-row-selection! new-selection))
         (set-row-selection! new-selection))
 
 
       :else
       :else
@@ -71,7 +71,7 @@
 
 
 (defn- row-toggle-selected!
 (defn- row-toggle-selected!
   [row value set-row-selection! row-selection]
   [row value set-row-selection! row-selection]
-  (let [id (:id row)
+  (let [id (:db/id row)
         new-selection (if (:selected-all? row-selection)
         new-selection (if (:selected-all? row-selection)
                         (update row-selection :excluded-ids (if value disj set-conj) id)
                         (update row-selection :excluded-ids (if value disj set-conj) id)
                         (update row-selection :selected-ids (if value set-conj disj) id))]
                         (update row-selection :selected-ids (if value set-conj disj) id))]
@@ -86,7 +86,7 @@
                        (remove (fn [item] (= (:id item) id)) sorting)
                        (remove (fn [item] (= (:id item) id)) sorting)
                        (map (fn [item] (if (= (:id item) id) (assoc item :asc? asc?) item)) sorting))
                        (map (fn [item] (if (= (:id item) id) (assoc item :asc? asc?) item)) sorting))
                      (when-not (nil? asc?)
                      (when-not (nil? asc?)
-                       (conj (if (vector? sorting) sorting (vec sorting)) {:id id :asc? asc?})))
+                       (into [{:id id :asc? asc?}] sorting)))
                    (remove nil?)
                    (remove nil?)
                    vec)]
                    vec)]
     (set-sorting! value)
     (set-sorting! value)
@@ -97,11 +97,11 @@
   (if (:selected-all? row-selection)
   (if (:selected-all? row-selection)
     (let [excluded-ids (:excluded-ids row-selection)]
     (let [excluded-ids (:excluded-ids row-selection)]
       (if (seq excluded-ids)
       (if (seq excluded-ids)
-        (remove #(excluded-ids (:id %)) rows)
+        (remove #(excluded-ids %) rows)
         rows))
         rows))
     (let [selected-ids (:selected-ids row-selection)]
     (let [selected-ids (:selected-ids row-selection)]
       (when (seq selected-ids)
       (when (seq selected-ids)
-        (filter #(selected-ids (:id %)) rows)))))
+        (filter #(selected-ids %) rows)))))
 
 
 (defn table-option
 (defn table-option
   [{:keys [data columns state data-fns]
   [{:keys [data columns state data-fns]
@@ -276,14 +276,15 @@
 (rum/defc table-row < rum/static
 (rum/defc table-row < rum/static
   [& prop-and-children]
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
   (let [[prop children] (get-prop-and-children prop-and-children)]
-    [:div.ls-table-row.flex.flex-row.items-center (merge {:class "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted bg-gray-01 items-stretch"}
-                                                         prop)
+    [:div.ls-table-row.flex.flex-row.items-center
+     (merge {:class "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted bg-gray-01 items-stretch"}
+            prop)
      children]))
      children]))
 
 
 (rum/defc table-cell < rum/static
 (rum/defc table-cell < rum/static
   [& prop-and-children]
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
   (let [[prop children] (get-prop-and-children prop-and-children)]
-    [:div.flex.relative prop
+    [:div.ls-table-cell.flex.relative.h-full (dissoc prop :select? :add-property?)
      [:div {:class (str "flex align-middle w-full overflow-x-clip items-center"
      [:div {:class (str "flex align-middle w-full overflow-x-clip items-center"
                         (cond
                         (cond
                           (:select? prop)
                           (:select? prop)
@@ -305,5 +306,3 @@
              :style {:z-index 101}}
              :style {:z-index 101}}
             prop)
             prop)
      children]))
      children]))
-
-(def table-sort-rows impl/sort-rows)

+ 0 - 41
deps/shui/src/logseq/shui/table/impl.cljc

@@ -15,47 +15,6 @@
   [columns visible-columns']
   [columns visible-columns']
   (filter #(column-visible? % visible-columns') columns))
   (filter #(column-visible? % visible-columns') columns))
 
 
-(defn sort-rows
-  "Support multiple sorts"
-  [rows sorting columns]
-  (let [column-id->get-value (zipmap (map column-id columns)
-                                     (map :get-value-for-sort columns))]
-    (loop [[sorting-item & other-sorting] (reverse sorting)
-           rows rows]
-      (if sorting-item
-        (let [{:keys [id asc?]} sorting-item
-              rows' (sort-by
-                     (fn [row]
-                       (let [sort-value (or (get column-id->get-value id)
-                                            (let [valid-type? (some-fn number? string? boolean?)]
-                                              ;; need to check value type, otherwise `compare` can be failed,
-                                              ;; then crash the UI.
-                                              (fn [row]
-                                                (let [v (get row id)]
-                                                  (when (valid-type? v)
-                                                    v)))))]
-                         (sort-value row)))
-                     (if asc? compare #(compare %2 %1))
-                     rows)]
-          (recur other-sorting rows'))
-        rows))))
-
-(comment
-  (def columns [{:id :author}
-                {:id :published-year}])
-  (def sorting [{:id :published-year :asc? true}
-                {:id :author :asc? false}])
-  (def rows [{:id :author-1
-              :author "Charlie"
-              :published-year 2014}
-             {:id :author-2
-              :author "Tienson"
-              :published-year 2014}
-             {:id :author-2
-              :author "Zhiyuan"
-              :published-year 2020}])
-  (sort-rows rows sorting columns))
-
 (defn rows
 (defn rows
   [{:keys [row-filter]
   [{:keys [row-filter]
     :as opts}]
     :as opts}]

+ 1 - 0
packages/ui/src/colors.css

@@ -35,6 +35,7 @@ html {
     }
     }
 
 
     &[data-theme=dark] {
     &[data-theme=dark] {
+      --lx-gray-01: var(--ls-primary-background-color, hsl(var(--background)));
       --background: 192 100% 11%;
       --background: 192 100% 11%;
       --foreground: 0 0% 95%;
       --foreground: 0 0% 95%;
       --accent: 192 80% 10%;
       --accent: 192 80% 10%;

+ 1 - 1
scripts/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "version": "1.0.0",
   "private": true,
   "private": true,
   "devDependencies": {
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v18"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v19"
   },
   },
   "dependencies": {
   "dependencies": {
     "better-sqlite3": "9.3.0",
     "better-sqlite3": "9.3.0",

+ 1 - 1
scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs

@@ -7,10 +7,10 @@
             ["os" :as os]
             ["os" :as os]
             ["path" :as node-path]
             ["path" :as node-path]
             [babashka.cli :as cli]
             [babashka.cli :as cli]
+            [cljs.pprint :as pprint]
             [clojure.edn :as edn]
             [clojure.edn :as edn]
             [clojure.set :as set]
             [clojure.set :as set]
             [clojure.string :as string]
             [clojure.string :as string]
-            [cljs.pprint :as pprint]
             [datascript.core :as d]
             [datascript.core :as d]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.common.util.date-time :as date-time-util]

+ 3 - 3
scripts/yarn.lock

@@ -2,9 +2,9 @@
 # yarn lockfile v1
 # yarn lockfile v1
 
 
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v18":
-  version "1.2.173-feat-db-v18"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/1cd15bf5beb77a1bc5c943a438681cb072eabf2c"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v19":
+  version "1.2.173-feat-db-v19"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/ca32553da41aff783d89264b2e1a753372721f2e"
   dependencies:
   dependencies:
     import-meta-resolve "^2.1.0"
     import-meta-resolve "^2.1.0"
 
 

+ 48 - 48
src/electron/electron/core.cljs

@@ -1,26 +1,26 @@
 (ns electron.core
 (ns electron.core
-  (:require [electron.handler :as handler]
+  (:require ["/electron/utils" :as js-utils]
+            ["electron" :refer [BrowserWindow Menu app protocol ipcMain dialog shell] :as electron]
+            ["electron-deeplink" :refer [Deeplink]]
+            ["os" :as os]
+            ["path" :as node-path]
+            [cljs-bean.core :as bean]
+            [clojure.string :as string]
             [electron.db :as db]
             [electron.db :as db]
+            [electron.exceptions :as exceptions]
+            [electron.fs-watcher :as fs-watcher]
+            [electron.git :as git]
+            [electron.handler :as handler]
+            [electron.logger :as logger]
+            [electron.server :as server]
             [electron.updater :refer [init-updater] :as updater]
             [electron.updater :refer [init-updater] :as updater]
+            [electron.url :refer [logseq-url-handler]]
             [electron.utils :refer [*win mac? linux? dev? get-win-from-sender
             [electron.utils :refer [*win mac? linux? dev? get-win-from-sender
                                     decode-protected-assets-schema-path send-to-renderer]
                                     decode-protected-assets-schema-path send-to-renderer]
              :as utils]
              :as utils]
-            [electron.url :refer [logseq-url-handler]]
-            [electron.logger :as logger]
-            [electron.server :as server]
-            [clojure.string :as string]
-            [promesa.core :as p]
-            [cljs-bean.core :as bean]
-            [electron.fs-watcher :as fs-watcher]
-            ["path" :as node-path]
-            ["os" :as os]
-            ["electron" :refer [BrowserWindow Menu app protocol ipcMain dialog shell] :as electron]
-            ["electron-deeplink" :refer [Deeplink]]
-            [electron.git :as git]
             [electron.window :as win]
             [electron.window :as win]
-            [electron.exceptions :as exceptions]
-            ["/electron/utils" :as js-utils]
-            [logseq.publishing.export :as publish-export]))
+            [logseq.publishing.export :as publish-export]
+            [promesa.core :as p]))
 
 
 ;; Keep same as main/frontend.util.url
 ;; Keep same as main/frontend.util.url
 (defonce LSP_SCHEME "logseq")
 (defonce LSP_SCHEME "logseq")
@@ -105,15 +105,15 @@
   (p/let [app-path (. app getAppPath)
   (p/let [app-path (. app getAppPath)
           asset-filenames (->> (js->clj asset-filenames) (remove nil?))
           asset-filenames (->> (js->clj asset-filenames) (remove nil?))
           root-dir (or output-path (handler/open-dir-dialog))]
           root-dir (or output-path (handler/open-dir-dialog))]
-         (when root-dir
-           (publish-export/create-export
-            html
-            app-path
-            repo-path
-            root-dir
-            {:asset-filenames asset-filenames
-             :log-error-fn logger/error
-             :notification-fn #(send-to-renderer :notification %)}))))
+    (when root-dir
+      (publish-export/create-export
+       html
+       app-path
+       repo-path
+       root-dir
+       {:asset-filenames asset-filenames
+        :log-error-fn logger/error
+        :notification-fn #(send-to-renderer :notification %)}))))
 
 
 (defn setup-app-manager!
 (defn setup-app-manager!
   [^js win]
   [^js win]
@@ -250,7 +250,7 @@
          ;; Add React developer tool
          ;; Add React developer tool
          (when-let [^js devtoolsInstaller (and dev? (js/require "electron-devtools-installer"))]
          (when-let [^js devtoolsInstaller (and dev? (js/require "electron-devtools-installer"))]
            (-> (.default devtoolsInstaller (.-REACT_DEVELOPER_TOOLS devtoolsInstaller))
            (-> (.default devtoolsInstaller (.-REACT_DEVELOPER_TOOLS devtoolsInstaller))
-             (.then #(js/console.log "Added Extension:" (.-REACT_DEVELOPER_TOOLS devtoolsInstaller)))))
+               (.then #(js/console.log "Added Extension:" (.-REACT_DEVELOPER_TOOLS devtoolsInstaller)))))
 
 
          (let [t0 (setup-interceptor! app')
          (let [t0 (setup-interceptor! app')
                ^js win (win/create-main-window!)
                ^js win (win/create-main-window!)
@@ -281,30 +281,30 @@
 
 
            ;; main window events
            ;; main window events
            (.on win "close" (fn [e]
            (.on win "close" (fn [e]
-                                  (git/before-graph-close-hook!)
-                                  (when @*quit-dirty? ;; when not updating
-                                    (.preventDefault e)
-
-                                    (let [windows (win/get-all-windows)
-                                          window @*win
-                                          multiple-windows? (> (count windows) 1)]
-                                      (cond
-                                        (or multiple-windows? (not mac?) @win/*quitting?)
-                                        (when window
-                                          (win/close-handler win handler/close-watcher-when-orphaned! e)
-                                          (reset! *win nil))
-
-                                        (and mac? (not multiple-windows?))
+                              (git/before-graph-close-hook!)
+                              (when @*quit-dirty? ;; when not updating
+                                (.preventDefault e)
+
+                                (let [windows (win/get-all-windows)
+                                      window @*win
+                                      multiple-windows? (> (count windows) 1)]
+                                  (cond
+                                    (or multiple-windows? (not mac?) @win/*quitting?)
+                                    (when window
+                                      (win/close-handler win handler/close-watcher-when-orphaned! e)
+                                      (reset! *win nil))
+
+                                    (and mac? (not multiple-windows?))
                                         ;; Just hiding - don't do any actual closing operation
                                         ;; Just hiding - don't do any actual closing operation
-                                        (do (.preventDefault ^js/Event e)
-                                            (if (and mac? (.isFullScreen win))
-                                              (do (.once win "leave-full-screen" #(.hide win))
-                                                  (.setFullScreen win false))
-                                              (.hide win)))
-                                        :else
-                                        nil)))))
+                                    (do (.preventDefault ^js/Event e)
+                                        (if (and mac? (.isFullScreen win))
+                                          (do (.once win "leave-full-screen" #(.hide win))
+                                              (.setFullScreen win false))
+                                          (.hide win)))
+                                    :else
+                                    nil)))))
            (.on app' "before-quit" (fn [_e]
            (.on app' "before-quit" (fn [_e]
-                                    (reset! win/*quitting? true)))
+                                     (reset! win/*quitting? true)))
 
 
            (.on app' "activate" #(when @*win (.show win)))))))
            (.on app' "activate" #(when @*win (.show win)))))))
 
 

+ 6 - 6
src/main/electron/listener.cljs

@@ -5,23 +5,23 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [dommy.core :as dom]
             [dommy.core :as dom]
             [electron.ipc :as ipc]
             [electron.ipc :as ipc]
+            [frontend.db :as db]
+            [frontend.db.async :as db-async]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
             [frontend.fs.sync :as sync]
             [frontend.fs.sync :as sync]
             [frontend.fs.watcher-handler :as watcher-handler]
             [frontend.fs.watcher-handler :as watcher-handler]
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
+            [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
+            [frontend.handler.search :as search-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user]
             [frontend.handler.user :as user]
-            [frontend.handler.search :as search-handler]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [logseq.common.path :as path]
             [logseq.common.path :as path]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
-            [promesa.core :as p]
-            [frontend.handler.property.util :as pu]
-            [frontend.db :as db]
-            [frontend.db.async :as db-async]))
+            [promesa.core :as p]))
 
 
 (defn- safe-api-call
 (defn- safe-api-call
   "Force the callback result to be nil, otherwise, ipc calls could lead to
   "Force the callback result to be nil, otherwise, ipc calls could lead to
@@ -87,7 +87,7 @@
                          (route-handler/redirect-to-page! page-name {:block-id block-id}))
                          (route-handler/redirect-to-page! page-name {:block-id block-id}))
 
 
                        block-id
                        block-id
-                       (p/let [block (db-async/<get-block (state/get-current-repo) block-id)]
+                       (p/let [block (db-async/<get-block (state/get-current-repo) block-id {:children? false})]
                          (if block
                          (if block
                            (if (pu/shape-block? block)
                            (if (pu/shape-block? block)
                              (route-handler/redirect-to-page! (get-in block [:block/page :block/uuid]) {:block-id block-id})
                              (route-handler/redirect-to-page! (get-in block [:block/page :block/uuid]) {:block-id block-id})

+ 1 - 1
src/main/frontend/common.css

@@ -325,7 +325,7 @@ h1.title, h1.title input, .ls-page-title-container {
 }
 }
 
 
 .block-highlight,
 .block-highlight,
-.content .selected,
+.ls-block.selected,
 .ls-dummy-block.selected {
 .ls-dummy-block.selected {
   transition: background-color 0.2s cubic-bezier(0, 1, 0, 1);
   transition: background-color 0.2s cubic-bezier(0, 1, 0, 1);
   background-color: var(--ls-block-highlight-color, var(--rx-gray-04));
   background-color: var(--ls-block-highlight-color, var(--rx-gray-04));

+ 19 - 0
src/main/frontend/common/cache.cljs

@@ -0,0 +1,19 @@
+(ns frontend.common.cache
+  "Utils about cache"
+  (:require [cljs.cache :as cache]))
+
+#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
+;; (def *profile (volatile! {}))
+
+(defn cache-fn
+  "Return a cached version of `f`.
+  cache-key&f-args-fn: return [<cache-key> <args-list-to-f>]"
+  [*cache cache-key&f-args-fn f]
+  (fn [& args]
+    (let [[cache-k f-args] (apply cache-key&f-args-fn args)
+          through-value-fn #(apply f f-args)
+          ;; hit? (cache/has? @*cache cache-k)
+          ;; _ (vswap! *profile update-in [[*cache (.-limit ^js @*cache)] (if hit? :hit :miss)] inc)
+          ;; _ (prn (if hit? :hit :miss) cache-k)
+          cache (vreset! *cache (cache/through through-value-fn @*cache cache-k))]
+      (cache/lookup cache cache-k))))

+ 3 - 3
src/main/frontend/common/file/core.cljs

@@ -99,7 +99,7 @@
                                   (-> (string/replace content #"^\s?#+\s+" "")
                                   (-> (string/replace content #"^\s?#+\s+" "")
                                       (string/replace #"^\s?#+\s?$" ""))
                                       (string/replace #"^\s?#+\s?$" ""))
                                   content)
                                   content)
-                        content (content-with-collapsed-state repo format content collapsed?)
+                        content (if db-based? content (content-with-collapsed-state repo format content collapsed?))
                         new-content (indented-block-content (string/trim content) spaces-tabs)
                         new-content (indented-block-content (string/trim content) spaces-tabs)
                         sep (if (or markdown-top-heading?
                         sep (if (or markdown-top-heading?
                                     (string/blank? new-content))
                                     (string/blank? new-content))
@@ -111,13 +111,13 @@
       content)))
       content)))
 
 
 (defn- tree->file-content-aux
 (defn- tree->file-content-aux
-  [repo db tree {:keys [init-level] :as opts} context]
+  [repo db tree {:keys [init-level link] :as opts} context]
   (let [block-contents (transient [])]
   (let [block-contents (transient [])]
     (loop [[f & r] tree level init-level]
     (loop [[f & r] tree level init-level]
       (if (nil? f)
       (if (nil? f)
         (->> block-contents persistent! flatten (remove nil?))
         (->> block-contents persistent! flatten (remove nil?))
         (let [page? (nil? (:block/page f))
         (let [page? (nil? (:block/page f))
-              content (if page? nil (transform-content repo db f level opts context))
+              content (if (and page? (not link)) nil (transform-content repo db f level opts context))
               new-content
               new-content
               (if-let [children (seq (:block/children f))]
               (if-let [children (seq (:block/children f))]
                 (cons content (tree->file-content-aux repo db children {:init-level (inc level)} context))
                 (cons content (tree->file-content-aux repo db children {:init-level (inc level)} context))

+ 240 - 0
src/main/frontend/common/graph_view.cljs

@@ -0,0 +1,240 @@
+(ns frontend.common.graph-view
+  "Main namespace for graph view fns."
+  (:require [clojure.set :as set]
+            [clojure.string :as string]
+            [datascript.core :as d]
+            [logseq.common.util :as common-util]
+            [logseq.db :as ldb]
+            [logseq.db.common.property-util :as db-property-util]
+            [logseq.db.frontend.entity-plus :as entity-plus]
+            [logseq.db.sqlite.create-graph :as sqlite-create-graph]
+            [logseq.graph-parser.db :as gp-db]))
+
+(defn- build-links
+  [links]
+  (keep (fn [[from to]]
+          (when (and from to)
+            {:source (str from)
+             :target (str to)}))
+        links))
+
+(defn- build-nodes
+  [dark? current-page page-links tags nodes namespaces]
+  (let [page-parents (set (map last namespaces))
+        current-page (or current-page "")
+        pages (common-util/distinct-by :db/id nodes)]
+    (->>
+     pages
+     (remove ldb/hidden?)
+     (remove nil?)
+     (mapv (fn [p]
+             (let [page-title (:block/title p)
+                   current-page? (= page-title current-page)
+                   color (case [dark? current-page?] ; FIXME: Put it into CSS
+                           [false false] "#999"
+                           [false true]  "#045591"
+                           [true false]  "#93a1a1"
+                           [true true]   "#ffffff")
+                   color (if (contains? tags (:db/id p))
+                           (if dark? "orange" "green")
+                           color)
+                   n (get page-links page-title 1)
+                   size (int (* 8 (max 1.0 (js/Math.cbrt n))))]
+               (cond->
+                {:id (str (:db/id p))
+                 :label page-title
+                 :size size
+                 :color color
+                 :block/created-at (:block/created-at p)}
+                 (contains? page-parents (:db/id p))
+                 (assoc :parent true))))))))
+
+                  ;; slow
+(defn- uuid-or-asset?
+  [label]
+  (or (common-util/uuid-string? label)
+      (string/starts-with? label "../assets/")
+      (= label "..")
+      (string/starts-with? label "assets/")
+      (string/ends-with? label ".gif")
+      (string/ends-with? label ".jpg")
+      (string/ends-with? label ".png")))
+
+(defn- remove-uuids-and-files!
+  [nodes]
+  (remove
+   (fn [node] (uuid-or-asset? (:label node)))
+   nodes))
+
+(defn- normalize-page-name
+  [{:keys [nodes links]}]
+  (let [nodes' (->> (remove-uuids-and-files! nodes)
+                    (common-util/distinct-by (fn [node] (:id node)))
+                    (remove nil?))]
+    {:nodes nodes'
+     :links links}))
+
+(defn- build-global-graph
+  [db {:keys [theme journal? orphan-pages? builtin-pages? excluded-pages? created-at-filter]}]
+  (let [dark? (= "dark" theme)
+        relation (ldb/get-pages-relation db journal?)
+        tagged-pages (ldb/get-all-tagged-pages db)
+        namespaces (ldb/get-all-namespace-relation db)
+        tags (set (map second tagged-pages))
+        full-pages (ldb/get-all-pages db)
+        db-based? (entity-plus/db-based-graph? db)
+        created-ats (map :block/created-at full-pages)
+
+            ;; build up nodes
+        full-pages'
+        (cond->> full-pages
+          created-at-filter
+          (filter #(<= (:block/created-at %) (+ (apply min created-ats) created-at-filter)))
+          (not journal?)
+          (remove ldb/journal?)
+          (not excluded-pages?)
+          (remove (fn [p] (true?
+                           (get p (db-property-util/get-pid-2 db :logseq.property/exclude-from-graph-view))))))
+        links (concat relation tagged-pages namespaces)
+        linked (set (mapcat identity links))
+        build-in-pages (->> (if db-based? sqlite-create-graph/built-in-pages-names gp-db/built-in-pages-names)
+                            (map string/lower-case)
+                            set)
+        nodes (cond->> full-pages'
+                (not builtin-pages?)
+                (remove #(contains? build-in-pages (:block/name %)))
+                (not orphan-pages?)
+                (filter #(contains? linked (:db/id %))))
+        links (map (fn [[x y]] [(str x) (str y)]) links)
+        page-links (reduce (fn [m [k v]] (-> (update m k inc)
+                                             (update v inc))) {} links)
+        links (build-links links)
+        nodes (build-nodes dark? nil page-links tags nodes namespaces)]
+    (-> {:nodes nodes
+         :links links}
+        normalize-page-name
+        (assoc :all-pages
+               {:created-at-min (apply min created-ats)
+                :created-at-max (apply max created-ats)}))))
+
+(defn get-pages-that-mentioned-page
+  [db page-id include-journals?]
+  (let [pages (ldb/page-alias-set db page-id)
+        mentioned-pages (->>
+                         (mapcat
+                          (fn [id]
+                            (let [page (d/entity db id)]
+                              (->> (:block/_refs page)
+                                   (keep (fn [ref]
+                                           (if (ldb/page? ref)
+                                             page
+                                             (:block/page ref)))))))
+                          pages)
+                         (common-util/distinct-by :db/id))]
+    (keep (fn [page]
+            (when-not (and (not include-journals?) (ldb/journal? page))
+              (:db/id page)))
+          mentioned-pages)))
+
+(defn get-page-referenced-pages
+  [db page-id]
+  (let [pages (ldb/page-alias-set db page-id)
+        ref-pages (d/q
+                   '[:find [?ref-page ...]
+                     :in $ ?pages
+                     :where
+                     [(untuple ?pages) [?page ...]]
+                     [?block :block/page ?page]
+                     [?block :block/refs ?ref-page]]
+                   db
+                   pages)]
+    ref-pages))
+
+(defn- build-page-graph-other-page-links [db other-pages* show-journal]
+  (let [other-pages (->> other-pages*
+                         (remove nil?)
+                         (set))]
+    (mapcat
+     (fn [page-id]
+       (let [ref-pages (-> (get-page-referenced-pages db page-id)
+                           (set)
+                           (set/intersection other-pages))
+             mentioned-pages (-> (get-pages-that-mentioned-page db page-id show-journal)
+                                 (set)
+                                 (set/intersection other-pages))]
+         (concat
+          (map (fn [p] [page-id p]) ref-pages)
+          (map (fn [p] [p page-id]) mentioned-pages))))
+     other-pages)))
+
+(defn- build-page-graph
+  [db page-uuid theme show-journal]
+  (let [dark? (= "dark" theme)
+        page-entity (d/entity db [:block/uuid page-uuid])
+        db-based? (entity-plus/db-based-graph? db)
+        page-id (:db/id page-entity)
+        tags (when db-based?
+               (set (map :db/id (:block/tags page-entity))))
+        tags (set (remove #(= page-id %) tags))
+        ref-pages (get-page-referenced-pages db page-id)
+        mentioned-pages (get-pages-that-mentioned-page db page-id show-journal)
+        namespaces (ldb/get-all-namespace-relation db)
+        links (concat
+               namespaces
+               (map (fn [ref-page]
+                      [page-id ref-page]) ref-pages)
+               (map (fn [page]
+                      [page-id page]) mentioned-pages)
+               (map (fn [tag]
+                      [page-id tag])
+                    tags))
+        other-pages-links (build-page-graph-other-page-links db (concat ref-pages mentioned-pages) show-journal)
+        links (->> (concat links other-pages-links)
+                   (remove nil?)
+                   (distinct)
+                   (build-links))
+        nodes (->> (concat
+                    [page-id]
+                    ref-pages
+                    mentioned-pages
+                    tags)
+                   (remove nil?)
+                   (map #(d/entity db %))
+                   (common-util/distinct-by :db/id))
+        nodes (build-nodes dark? (:block/title page-entity) links tags nodes namespaces)]
+    (normalize-page-name
+     {:nodes nodes
+      :links links})))
+
+(defn- build-block-graph
+  "Builds a citation/reference graph for a given block uuid."
+  [db block-uuid theme]
+  (when-let [block (and (uuid? block-uuid) (d/entity db [:block/uuid block-uuid]))]
+    (let [dark? (= "dark" theme)
+          ref-blocks (->> (concat (:block/_refs block) (:block/refs block))
+                          (map (fn [b]
+                                 (if (ldb/page? b) b (:block/page b))))
+                          (remove (fn [node] (= (:db/id block) (:db/id node))))
+                          (common-util/distinct-by :db/id))
+          namespaces (ldb/get-all-namespace-relation db)
+          links (->> (concat
+                      namespaces
+                      (map (fn [p] [(:db/id block) (:db/id p)]) ref-blocks))
+                     (remove nil?)
+                     (distinct)
+                     (build-links))
+          nodes (->> (cons block ref-blocks)
+                     distinct
+                       ;; FIXME: get block tags
+                     )
+          nodes (build-nodes dark? block links #{} nodes namespaces)]
+      (normalize-page-name
+       {:nodes nodes
+        :links links}))))
+
+(defn build-graph
+  [db opts]
+  (case (:type opts)
+    :global (build-global-graph db opts)
+    :block (build-block-graph db (:block/uuid opts) (:theme opts))
+    :page (build-page-graph db (:block/uuid opts) (:theme opts) (:show-journal? opts))))

+ 19 - 5
src/main/frontend/common/missionary.cljs

@@ -85,14 +85,28 @@
     (let [x (m/?> (m/relieve {} >in))]
     (let [x (m/?> (m/relieve {} >in))]
       (m/amb x (do (m/? (m/sleep dur-ms)) (m/amb))))))
       (m/amb x (do (m/? (m/sleep dur-ms)) (m/amb))))))
 
 
+(defn- fail-case-default-handler
+  [e]
+  (when-not (instance? Cancelled e)
+    (log/error :run-task*-failed e)))
+
 (defn run-task
 (defn run-task
   "Return the canceler"
   "Return the canceler"
-  [task key & {:keys [succ fail]}]
-  (task (or succ #(log/info :key key :succ %)) (or fail #(log/info :key key :stopped %))))
+  [key' task & {:keys [succ fail]}]
+  (let [cancel (task (or succ #(log/info :key key' :succ %)) (or fail fail-case-default-handler))]
+    #(cancel)))
+
+(defn run-task*
+  "Return the canceler"
+  [task & {:keys [succ fail]}]
+  (let [cancel (task (or succ (constantly nil)) (or fail fail-case-default-handler))]
+    #(cancel)))
 
 
 (defn run-task-throw
 (defn run-task-throw
-  [task key & {:keys [succ]}]
-  (task (or succ #(log/info :key key :succ %)) #(throw (ex-info "task stopped" {:key key :e %}))))
+  "Return the canceler"
+  [key' task & {:keys [succ]}]
+  (let [cancel (task (or succ #(log/info :key key' :succ %)) #(throw (ex-info "task stopped" {:key key' :e %})))]
+    #(cancel)))
 
 
 (defonce ^:private *background-task-cancelers ; key -> canceler
 (defonce ^:private *background-task-cancelers ; key -> canceler
   (volatile! {}))
   (volatile! {}))
@@ -105,7 +119,7 @@
     (canceler)
     (canceler)
     (vswap! *background-task-cancelers assoc key' nil))
     (vswap! *background-task-cancelers assoc key' nil))
   (prn :run-background-task key')
   (prn :run-background-task key')
-  (let [canceler (run-task task key')]
+  (let [canceler (run-task key' task)]
     (vswap! *background-task-cancelers assoc key' canceler)
     (vswap! *background-task-cancelers assoc key' canceler)
     nil))
     nil))
 
 

+ 5 - 1
src/main/frontend/common/thread_api.cljc

@@ -18,13 +18,17 @@
   (assert (vector? params) params)
   (assert (vector? params) params)
   `(vswap! *thread-apis assoc
   `(vswap! *thread-apis assoc
            ~qualified-keyword-name
            ~qualified-keyword-name
-           (fn ~params ~@body)))
+           (fn ~(symbol (str "thread-api--" (name qualified-keyword-name))) ~params ~@body)))
+
+
+#?(:cljs (def *profile (volatile! {})))
 
 
 #?(:cljs
 #?(:cljs
    (defn remote-function
    (defn remote-function
      "Return a promise whose value is transit-str."
      "Return a promise whose value is transit-str."
      [qualified-kw-str args-direct-passthrough? args-transit-str-or-args-array]
      [qualified-kw-str args-direct-passthrough? args-transit-str-or-args-array]
      (let [qkw (keyword qualified-kw-str)]
      (let [qkw (keyword qualified-kw-str)]
+       (vswap! *profile update qkw inc)
        (if-let [f (@*thread-apis qkw)]
        (if-let [f (@*thread-apis qkw)]
          (let [result (apply f (cond-> args-transit-str-or-args-array
          (let [result (apply f (cond-> args-transit-str-or-args-array
                                  (not args-direct-passthrough?) ldb/read-transit-str))
                                  (not args-direct-passthrough?) ldb/read-transit-str))

+ 7 - 34
src/main/frontend/components/all_pages.cljs

@@ -1,17 +1,12 @@
 (ns frontend.components.all-pages
 (ns frontend.components.all-pages
   "All pages"
   "All pages"
   (:require [frontend.components.block :as component-block]
   (:require [frontend.components.block :as component-block]
-            [frontend.components.page :as component-page]
             [frontend.components.views :as views]
             [frontend.components.views :as views]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db :as db]
-            [frontend.handler.page :as page-handler]
             [frontend.state :as state]
             [frontend.state :as state]
             [logseq.common.config :as common-config]
             [logseq.common.config :as common-config]
-            [logseq.shui.hooks :as hooks]
-            [logseq.shui.ui :as shui]
-            [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (defn- columns
 (defn- columns
@@ -19,7 +14,8 @@
   (->> [{:id :block/title
   (->> [{:id :block/title
          :name (t :block/name)
          :name (t :block/name)
          :cell (fn [_table row _column]
          :cell (fn [_table row _column]
-                 (component-block/page-cp {} row))
+                 (component-block/page-cp {:show-non-exists-page? true
+                                           :skip-async-load? true} row))
          :type :string}
          :type :string}
         (when (not (config/db-based-graph? (state/get-current-repo)))
         (when (not (config/db-based-graph? (state/get-current-repo)))
           {:id :block/type
           {:id :block/type
@@ -27,46 +23,23 @@
            :cell (fn [_table row _column]
            :cell (fn [_table row _column]
                    (let [type (get row :block/type)]
                    (let [type (get row :block/type)]
                      [:div.capitalize type]))
                      [:div.capitalize type]))
-           :get-value (fn [row] (get row :block/type))
            :type :string})
            :type :string})
         {:id :block.temp/refs-count
         {:id :block.temp/refs-count
          :name (t :page/backlinks)
          :name (t :page/backlinks)
-         :cell (fn [_table row _column] (:block.temp/refs-count row))
+         :cell (fn [_table row _column]
+                 (:block.temp/refs-count row))
          :type :number}]
          :type :number}]
        (remove nil?)
        (remove nil?)
        vec))
        vec))
 
 
-(defn- get-all-pages
-  []
-  (->> (page-handler/get-all-pages (state/get-current-repo))
-       (map (fn [p] (assoc p :id (:db/id p))))))
-
 (rum/defc all-pages < rum/static
 (rum/defc all-pages < rum/static
   []
   []
-  (let [[data set-data!] (rum/use-state nil)
-        columns' (views/build-columns {} (columns)
+  (let [columns' (views/build-columns {} (columns)
                                       {:with-object-name? false
                                       {:with-object-name? false
                                        :with-id? false})]
                                        :with-id? false})]
-    (hooks/use-effect!
-     (fn []
-       (p/let [result (state/<invoke-db-worker :thread-api/get-page-refs-count (state/get-current-repo))
-               data (get-all-pages)
-               data (map (fn [row] (assoc row :block.temp/refs-count (get result (:db/id row) 0))) data)]
-         (set-data! data)))
-     [])
     [:div.ls-all-pages.w-full.mx-auto
     [:div.ls-all-pages.w-full.mx-auto
-     (views/view {:data data
-                  :set-data! set-data!
-                  :view-parent (db/get-page common-config/views-page-name)
+     (views/view {:view-parent (db/get-page common-config/views-page-name)
                   :view-feature-type :all-pages
                   :view-feature-type :all-pages
                   :show-items-count? true
                   :show-items-count? true
                   :columns columns'
                   :columns columns'
-                  :title-key :all-pages/table-title
-                  :on-delete-rows (fn [table selected-rows]
-                                    (shui/dialog-open!
-                                     (component-page/batch-delete-dialog
-                                      selected-rows false
-                                      (fn []
-                                        (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                          (f {}))
-                                        (set-data! (get-all-pages))))))})]))
+                  :title-key :all-pages/table-title})]))

+ 389 - 311
src/main/frontend/components/block.cljs

@@ -678,7 +678,8 @@
                        (editor-handler/block->data-transfer! page-name e true))
                        (editor-handler/block->data-transfer! page-name e true))
       :on-mouse-over #(reset! *hover? true)
       :on-mouse-over #(reset! *hover? true)
       :on-mouse-leave #(reset! *hover? false)
       :on-mouse-leave #(reset! *hover? false)
-      :on-click (fn [e] (when stop-click-event? (util/stop e)))
+      :on-click (fn [e]
+                  (when stop-click-event? (util/stop e)))
       :on-pointer-down (fn [^js e]
       :on-pointer-down (fn [^js e]
                          (cond
                          (cond
                            (and meta-click? (util/meta-key? e))
                            (and meta-click? (util/meta-key? e))
@@ -697,7 +698,8 @@
       :on-pointer-up (fn [e]
       :on-pointer-up (fn [e]
                        (when @*mouse-down?
                        (when @*mouse-down?
                          (state/clear-edit!)
                          (state/clear-edit!)
-                         (when-not (:disable-click? config)
+                         (when-not (or (:disable-click? config)
+                                       (:disable-redirect? config))
                            (open-page-ref config page-entity e page-name contents-page?))
                            (open-page-ref config page-entity e page-name contents-page?))
                          (reset! *mouse-down? false)))
                          (reset! *mouse-down? false)))
       :on-key-up (fn [e] (when (and e (= (.-key e) "Enter") (not meta-click?))
       :on-key-up (fn [e] (when (and e (= (.-key e) "Enter") (not meta-click?))
@@ -895,7 +897,7 @@
   (let [db-based? (config/db-based-graph? (state/get-current-repo))
   (let [db-based? (config/db-based-graph? (state/get-current-repo))
         ->ref (if db-based? page-ref/->page-ref block-ref/->block-ref)]
         ->ref (if db-based? page-ref/->page-ref block-ref/->block-ref)]
     [:span.warning.mr-1 {:title "Node ref invalid"}
     [:span.warning.mr-1 {:title "Node ref invalid"}
-     (->ref id)]))
+     (->ref (str id))]))
 
 
 (defn inline-text
 (defn inline-text
   ([format v]
   ([format v]
@@ -907,27 +909,32 @@
 
 
 (rum/defcs page-cp-inner < db-mixins/query rum/reactive
 (rum/defcs page-cp-inner < db-mixins/query rum/reactive
   {:init (fn [state]
   {:init (fn [state]
-           (let [page (last (:rum/args state))
+           (let [args (:rum/args state)
+                 [config page] args
                  *result (atom nil)
                  *result (atom nil)
-                 page-name (or (:block/uuid page)
-                               (when-let [s (:block/name page)]
-                                 (string/trim s)))
-                 page-entity (if (e/entity? page) page (db/get-page page-name))]
-             (if page-entity
+                 page-id-or-name (or (:db/id page)
+                                     (:block/uuid page)
+                                     (when-let [s (:block/name page)]
+                                       (string/trim s)))
+                 page-entity (if (e/entity? page) page (db/get-page page-id-or-name))]
+             (cond
+               page-entity
                (reset! *result page-entity)
                (reset! *result page-entity)
-               (p/let [query-result (db-async/<get-block (state/get-current-repo) page-name {:children? false})
-                       result (if (e/entity? query-result)
-                                query-result
-                                (:block query-result))]
+               (or (:skip-async-load? config) (:table-view? config))
+               (reset! *result page)
+               :else
+               (p/let [result (db-async/<get-block (state/get-current-repo) page-id-or-name {:children? false
+                                                                                             :skip-refresh? true
+                                                                                             :including-property-vals? false})]
                  (reset! *result result)))
                  (reset! *result result)))
 
 
              (assoc state :*entity *result)))}
              (assoc state :*entity *result)))}
   "Component for a page. `page` argument contains :block/name which can be (un)sanitized page name.
   "Component for a page. `page` argument contains :block/name which can be (un)sanitized page name.
    Keys for `config`:
    Keys for `config`:
    - `:preview?`: Is this component under preview mode? (If true, `page-preview-trigger` won't be registered to this `page-cp`)"
    - `:preview?`: Is this component under preview mode? (If true, `page-preview-trigger` won't be registered to this `page-cp`)"
-  [state {:keys [label children preview? disable-preview? show-non-exists-page? tag?] :as config} page]
+  [state {:keys [label children preview? disable-preview? show-non-exists-page? table-view? tag? _skip-async-load?] :as config} page]
   (when-let [entity' (rum/react (:*entity state))]
   (when-let [entity' (rum/react (:*entity state))]
-    (let [entity (db/sub-block (:db/id entity'))]
+    (let [entity (or (db/sub-block (:db/id entity')) entity')]
       (cond
       (cond
         entity
         entity
         (if (or (ldb/page? entity) (:block/tags entity))
         (if (or (ldb/page? entity) (:block/tags entity))
@@ -954,9 +961,11 @@
         (and (:block/name page) (util/uuid-string? (:block/name page)))
         (and (:block/name page) (util/uuid-string? (:block/name page)))
         (invalid-node-ref (:block/name page))
         (invalid-node-ref (:block/name page))
 
 
-        (and (:block/name page) show-non-exists-page?)
-        (page-inner config {:block/title (:block/name page)
-                            :block/name (:block/name page)} children label)
+        (and (:block/name page) (or show-non-exists-page? table-view?))
+        (page-inner config (merge
+                            {:block/title (:block/name page)
+                             :block/name (:block/name page)}
+                            page) children label)
 
 
         (:block/name page)
         (:block/name page)
         [:span (str (when tag? "#")
         [:span (str (when tag? "#")
@@ -970,7 +979,7 @@
 (rum/defc page-cp
 (rum/defc page-cp
   [config page]
   [config page]
   (rum/with-key (page-cp-inner config page)
   (rum/with-key (page-cp-inner config page)
-    (or (str (:block/uuid page)) (:block/name page))))
+    (or (str (:db/id page)) (str (:block/uuid page)) (:block/name page))))
 
 
 (rum/defc asset-reference
 (rum/defc asset-reference
   [config title path]
   [config title path]
@@ -1124,63 +1133,73 @@
 
 
 (declare block-container)
 (declare block-container)
 
 
-(rum/defc block-embed < rum/reactive
-  {:init (fn [state]
-           (let [block-id (second (:rum/args state))]
-             (db-async/<get-block (state/get-current-repo) block-id))
-           state)}
-  [config uuid]
-  (if (state/sub-async-query-loading (str uuid))
-    [:span "Loading..."]
-    (when-let [block (db/entity [:block/uuid uuid])]
+(rum/defc block-embed
+  [config block-uuid]
+  (let [[block set-block!] (hooks/use-state (db/entity [:block/uuid block-uuid]))]
+    (hooks/use-effect!
+     (fn []
+       (p/let [block (db-async/<get-block (state/get-current-repo)
+                                          block-uuid
+                                          {:children? false
+                                           :skip-refresh? true})]
+         (set-block! block)))
+     [])
+    (when block
       [:div.color-level.embed-block.bg-base-2
       [:div.color-level.embed-block.bg-base-2
        {:style {:z-index 2}
        {:style {:z-index 2}
         :on-pointer-down (fn [e] (.stopPropagation e))}
         :on-pointer-down (fn [e] (.stopPropagation e))}
        [:div.px-3.pt-1.pb-2
        [:div.px-3.pt-1.pb-2
         (let [config' (assoc config
         (let [config' (assoc config
                              :db/id (:db/id block)
                              :db/id (:db/id block)
-                             :id (str uuid)
-                             :embed-id uuid
+                             :id (str block-uuid)
+                             :embed-id block-uuid
                              :embed? true
                              :embed? true
                              :embed-parent (:block config)
                              :embed-parent (:block config)
                              :ref? false)]
                              :ref? false)]
-          (blocks-container config' [block]))]])))
+          (block-container config' block))]])))
 
 
-(rum/defc page-embed < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (let [page-name (second (:rum/args state))
-                 page-name' (util/page-name-sanity-lc (string/trim page-name))]
-             (db-async/<get-block (state/get-current-repo) page-name'))
-           state)}
+(rum/defc page-embed-aux < rum/reactive db-mixins/query
+  [config block]
+  (let [current-page (state/get-current-page)
+        block (db/sub-block (:db/id block))
+        whiteboard-page? (model/whiteboard-page? block)
+        page-name (:block/name block)]
+    [:div.color-level.embed.embed-page.bg-base-2
+     {:class (when (:sidebar? config) "in-sidebar")
+      :on-pointer-down #(.stopPropagation %)}
+     [:section.flex.items-center.p-1.embed-header
+      [:div.mr-3 svg/page]
+      (page-cp config block)]
+     (when (and
+            (not= (util/page-name-sanity-lc (or current-page ""))
+                  page-name)
+            (not= (util/page-name-sanity-lc (get config :id ""))
+                  page-name))
+       (if whiteboard-page?
+         ((state/get-component :whiteboard/tldraw-preview) (:block/uuid block))
+         (let [blocks (ldb/get-children block)
+               config' (assoc config
+                              :db/id (:db/id block)
+                              :id page-name
+                              :embed? true
+                              :page-embed? true
+                              :ref? false)]
+           (blocks-container config' blocks))))]))
+
+(rum/defc page-embed
   [config page-name]
   [config page-name]
-  (let [page-name (util/page-name-sanity-lc (string/trim page-name))
-        current-page (state/get-current-page)]
-    (if (and page-name (state/sub-async-query-loading page-name))
-      (ui/loading "embed")
-      (let [block (model/get-page page-name)
-            block (db/sub-block (:db/id block))
-            whiteboard-page? (model/whiteboard-page? block)]
-        [:div.color-level.embed.embed-page.bg-base-2
-         {:class (when (:sidebar? config) "in-sidebar")
-          :on-pointer-down #(.stopPropagation %)}
-         [:section.flex.items-center.p-1.embed-header
-          [:div.mr-3 svg/page]
-          (page-cp config block)]
-         (when (and
-                (not= (util/page-name-sanity-lc (or current-page ""))
-                      page-name)
-                (not= (util/page-name-sanity-lc (get config :id ""))
-                      page-name))
-           (if whiteboard-page?
-             ((state/get-component :whiteboard/tldraw-preview) (:block/uuid block))
-             (let [blocks (ldb/get-children block)
-                   config' (assoc config
-                                  :db/id (:db/id block)
-                                  :id page-name
-                                  :embed? true
-                                  :page-embed? true
-                                  :ref? false)]
-               (blocks-container config' blocks))))]))))
+  (let [page-name (util/page-name-sanity-lc (string/trim page-name))]
+    (let [[block set-block!] (hooks/use-state nil)]
+      (hooks/use-effect!
+       (fn []
+         (p/let [block (db-async/<get-block (state/get-current-repo)
+                                            page-name
+                                            {:children? true
+                                             :skip-refresh? true})]
+           (set-block! block)))
+       [])
+      (when block
+        (page-embed-aux config block)))))
 
 
 (defn- get-label-text
 (defn- get-label-text
   [label]
   [label]
@@ -1230,85 +1249,95 @@
                          :*timer *timer :*timer1 *timer1
                          :*timer *timer :*timer1 *timer1
                          :render render})))
                          :render render})))
 
 
-(rum/defc block-reference < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (let [block-id (second (:rum/args state))]
-             (db-async/<get-block (state/get-current-repo) block-id :children? false))
-           state)}
-  [config id label]
-  (if (= (:block/uuid (:block config)) id)
-    [:span.warning.text-sm "Self reference"]
-    (if-let [block-id (and id (if (uuid? id) id (parse-uuid id)))]
-      (if (state/sub-async-query-loading (str block-id))
-        [:span "Loading..."]
-        (let [block (db/entity [:block/uuid block-id])
-              db-id (:db/id block)
-              block (when db-id (db/sub-block db-id))
-              block-type (keyword (pu/lookup block :logseq.property/ls-type))
-              hl-type (pu/lookup block :logseq.property.pdf/hl-type)
-              repo (state/get-current-repo)
-              stop-inner-events? (= block-type :whiteboard-shape)]
-          (if (and block (:block/title block))
-            (let [content-cp (block-content (assoc config :block-ref? true :stop-events? stop-inner-events?)
-                                            block nil (:block/uuid block)
-                                            (:slide? config))
-                  display-type (:logseq.property.node/display-type block)]
-              (if (and display-type (not (contains? #{:quote :math} display-type)))
-                content-cp
-                (let [title [:span.block-ref content-cp]
-                      inner (cond
-                              (seq label)
-                              (->elem
-                               :span.block-ref
-                               (map-inline config label))
-                              :else
-                              title)]
-                  [:div.block-ref-wrap.inline
-                   {:data-type    (name (or block-type :default))
-                    :data-hl-type hl-type
-                    :on-pointer-down
-                    (fn [^js/MouseEvent e]
-                      (if (util/right-click? e)
-                        (state/set-state! :block-ref/context {:block (:block config)
-                                                              :block-ref block-id})
-                        (when (and
-                               (or (gobj/get e "shiftKey")
-                                   (not (.. e -target (closest ".blank"))))
-                               (not (util/right-click? e)))
-                          (util/stop e)
-
-                          (cond
-                            (gobj/get e "shiftKey")
-                            (state/sidebar-add-block!
-                             (state/get-current-repo)
-                             (:db/id block)
-                             :block-ref)
-
-                            (and (util/meta-key? e) (whiteboard-handler/inside-portal? (.-target e)))
-                            (whiteboard-handler/add-new-block-portal-shape!
-                             (:block/uuid block)
-                             (whiteboard-handler/closest-shape (.-target e)))
-
-                            :else
-                            (match [block-type (util/electron?)]
+(rum/defc block-reference-aux < rum/reactive db-mixins/query
+  [config block-id label]
+  (let [block (db/entity [:block/uuid block-id])
+        db-id (:db/id block)
+        block (when db-id (db/sub-block db-id))
+        block-type (keyword (pu/lookup block :logseq.property/ls-type))
+        hl-type (pu/lookup block :logseq.property.pdf/hl-type)
+        repo (state/get-current-repo)
+        stop-inner-events? (= block-type :whiteboard-shape)]
+    (if (and block (:block/title block))
+      (let [content-cp (block-content (assoc config :block-ref? true :stop-events? stop-inner-events?)
+                                      block nil (:block/uuid block)
+                                      (:slide? config))
+            display-type (:logseq.property.node/display-type block)]
+        (if (and display-type (not (contains? #{:quote :math} display-type)))
+          content-cp
+          (let [title [:span.block-ref content-cp]
+                inner (cond
+                        (seq label)
+                        (->elem
+                         :span.block-ref
+                         (map-inline config label))
+                        :else
+                        title)]
+            [:div.block-ref-wrap.inline
+             {:data-type    (name (or block-type :default))
+              :data-hl-type hl-type
+              :on-pointer-down
+              (fn [^js/MouseEvent e]
+                (if (util/right-click? e)
+                  (state/set-state! :block-ref/context {:block (:block config)
+                                                        :block-ref block-id})
+                  (when (and
+                         (or (gobj/get e "shiftKey")
+                             (not (.. e -target (closest ".blank"))))
+                         (not (util/right-click? e)))
+                    (util/stop e)
+
+                    (cond
+                      (gobj/get e "shiftKey")
+                      (state/sidebar-add-block!
+                       (state/get-current-repo)
+                       (:db/id block)
+                       :block-ref)
+
+                      (and (util/meta-key? e) (whiteboard-handler/inside-portal? (.-target e)))
+                      (whiteboard-handler/add-new-block-portal-shape!
+                       (:block/uuid block)
+                       (whiteboard-handler/closest-shape (.-target e)))
+
+                      :else
+                      (match [block-type (util/electron?)]
                           ;; pdf annotation
                           ;; pdf annotation
-                              [:annotation true] (pdf-assets/open-block-ref! block)
+                        [:annotation true] (pdf-assets/open-block-ref! block)
 
 
-                              [:whiteboard-shape true] (route-handler/redirect-to-page!
-                                                        (get-in block [:block/page :block/uuid]) {:block-id block-id})
+                        [:whiteboard-shape true] (route-handler/redirect-to-page!
+                                                  (get-in block [:block/page :block/uuid]) {:block-id block-id})
 
 
                           ;; default open block page
                           ;; default open block page
-                              :else (route-handler/redirect-to-page! id))))))}
-
-                   (if (and (not (util/mobile?))
-                            (not (:preview? config))
-                            (not (shui-dialog/has-modal?))
-                            (nil? block-type))
-                     (block-reference-preview inner
-                                              {:repo repo :config config :id block-id})
-                     inner)])))
-            (invalid-node-ref id))))
-      (invalid-node-ref id))))
+                        :else (route-handler/redirect-to-page! block-id))))))}
+
+             (if (and (not (util/mobile?))
+                      (not (:preview? config))
+                      (not (shui-dialog/has-modal?))
+                      (nil? block-type))
+               (block-reference-preview inner
+                                        {:repo repo :config config :id block-id})
+               inner)])))
+      (do
+        (log/warn :invalid-node block)
+        (invalid-node-ref block-id)))))
+
+(rum/defc block-reference
+  [config id label]
+  (let [block-id (and id (if (uuid? id) id (parse-uuid id)))
+        [block set-block!] (hooks/use-state (db/entity [:block/uuid block-id]))]
+    (hooks/use-effect!
+     (fn []
+       (p/let [block (db-async/<get-block (state/get-current-repo)
+                                          block-id
+                                          {:children? false
+                                           :skip-refresh? true})]
+         (set-block! block)))
+     [])
+    (if (and block-id (= (:block/uuid (:block config)) block-id))
+      [:span.warning.text-sm "Self reference"]
+      (if block
+        (block-reference-aux config block-id label)
+        (invalid-node-ref block-id)))))
 
 
 (defn- render-macro
 (defn- render-macro
   [config name arguments macro-content format]
   [config name arguments macro-content format]
@@ -2073,21 +2102,12 @@
 
 
 (defn- block-content-empty?
 (defn- block-content-empty?
   [block]
   [block]
-  (let [ast-title (:block.temp/ast-title block)
-        ast-body (:block.temp/ast-body block)
-        db-based? (config/db-based-graph? (state/get-current-repo))]
-    (and
-     (or db-based?
-         (property-file/properties-hidden? (:block/properties block)))
+  (string/blank? (:block/title block)))
 
 
-     (empty? ast-title)
-
-     (every? #(= % ["Horizontal_Rule"]) ast-body))))
-
-(rum/defcs block-control < rum/reactive
+(rum/defcs ^:large-vars/cleanup-todo block-control < rum/reactive
   (rum/local false ::dragging?)
   (rum/local false ::dragging?)
-  [state config block {:keys [uuid block-id collapsed? *control-show? edit? selected?]}]
-  (let [*dragging?         (::dragging? state)
+  [state config block {:keys [uuid block-id collapsed? *control-show? edit? selected? top? bottom?]}]
+  (let [*bullet-dragging?         (::dragging? state)
         doc-mode?          (state/sub :document/mode?)
         doc-mode?          (state/sub :document/mode?)
         control-show?      (util/react *control-show?)
         control-show?      (util/react *control-show?)
         ref?               (:ref? config)
         ref?               (:ref? config)
@@ -2138,11 +2158,11 @@
                       {:id (str "dot-" uuid)
                       {:id (str "dot-" uuid)
                        :draggable true
                        :draggable true
                        :on-drag-start (fn [event]
                        :on-drag-start (fn [event]
-                                        (reset! *dragging? true)
+                                        (reset! *bullet-dragging? true)
                                         (util/stop-propagation event)
                                         (util/stop-propagation event)
                                         (bullet-drag-start event block uuid block-id))
                                         (bullet-drag-start event block uuid block-id))
                        :on-drag-end (fn [_]
                        :on-drag-end (fn [_]
-                                      (reset! *dragging? false))
+                                      (reset! *bullet-dragging? false))
                        :blockid (str uuid)
                        :blockid (str uuid)
                        :class (str (when collapsed? "bullet-closed")
                        :class (str (when collapsed? "bullet-closed")
                                    (when (and (:document/mode? config)
                                    (when (and (:document/mode? config)
@@ -2171,8 +2191,8 @@
                        (or
                        (or
                         (and empty-content?
                         (and empty-content?
                              (not edit?)
                              (not edit?)
-                             (not (:block.temp/top? block))
-                             (not (:block.temp/bottom? block))
+                             (not top?)
+                             (not bottom?)
                              (not (util/react *control-show?))
                              (not (util/react *control-show?))
                              (not (:logseq.property/created-from-property  block)))
                              (not (:logseq.property/created-from-property  block)))
                         (and doc-mode?
                         (and doc-mode?
@@ -2182,7 +2202,7 @@
 
 
                        :else
                        :else
                        bullet)]
                        bullet)]
-         (if (and (config/db-based-graph?) (not @*dragging?))
+         (if (and (config/db-based-graph?) (not @*bullet-dragging?))
            (ui/tooltip
            (ui/tooltip
             bullet'
             bullet'
             [:div.flex.flex-col.gap-1.p-2
             [:div.flex.flex-col.gap-1.p-2
@@ -2465,7 +2485,7 @@
       :else
       :else
       nil)))
       nil)))
 
 
-(rum/defcs db-properties-cp <
+(rum/defcs db-properties-cp < rum/static
   {:init (fn [state]
   {:init (fn [state]
            (let [container-id (or (:container-id (first (:rum/args state)))
            (let [container-id (or (:container-id (first (:rum/args state)))
                                   (state/get-next-container-id))]
                                   (state/get-next-container-id))]
@@ -2556,25 +2576,29 @@
             (let [block (or (db/entity [:block/uuid (:block/uuid block)]) block)]
             (let [block (or (db/entity [:block/uuid (:block/uuid block)]) block)]
               (editor-handler/clear-selection!)
               (editor-handler/clear-selection!)
               (editor-handler/unhighlight-blocks!)
               (editor-handler/unhighlight-blocks!)
-              (let [f #(let [cursor-range (some-> (gdom/getElement block-id)
-                                                  (dom/by-class "block-content-inner")
-                                                  first
-                                                  util/caret-range)
-                             {:block/keys [title format]} block
-                             content (if (config/db-based-graph? (state/get-current-repo))
-                                       (:block/title block)
-                                       (->> title
-                                            (property-file/remove-built-in-properties-when-file-based
-                                             (state/get-current-repo) format)
-                                            (drawer/remove-logbook)))]
-                         (state/set-editing!
-                          edit-input-id
-                          content
-                          block
-                          cursor-range
-                          {:db (db/get-db)
-                           :move-cursor? false
-                           :container-id (:container-id config)}))]
+              (let [f #(p/do!
+                        (when-not (:block.temp/fully-loaded? (db/entity (:db/id block)))
+                          (db-async/<get-block (state/get-current-repo) (:db/id block) {:children? false}))
+                        (let [cursor-range (some-> (gdom/getElement block-id)
+                                                   (dom/by-class "block-content-inner")
+                                                   first
+                                                   util/caret-range)
+                              block (db/entity (:db/id block))
+                              {:block/keys [title format]} block
+                              content (if (config/db-based-graph? (state/get-current-repo))
+                                        (:block/title block)
+                                        (->> title
+                                             (property-file/remove-built-in-properties-when-file-based
+                                              (state/get-current-repo) format)
+                                             (drawer/remove-logbook)))]
+                          (state/set-editing!
+                           edit-input-id
+                           content
+                           block
+                           cursor-range
+                           {:db (db/get-db)
+                            :move-cursor? false
+                            :container-id (:container-id config)})))]
                 ;; wait a while for the value of the caret range
                 ;; wait a while for the value of the caret range
                 (p/do!
                 (p/do!
                  (state/pub-event! [:editor/save-code-editor])
                  (state/pub-event! [:editor/save-code-editor])
@@ -2882,7 +2906,8 @@
        (merge attrs))
        (merge attrs))
 
 
      [:<>
      [:<>
-      (when (> (count content) (state/block-content-max-length (state/get-current-repo)))
+      (when (and (> (count content) (state/block-content-max-length (state/get-current-repo)))
+                 (not (contains? #{:code} (:logseq.property.node/display-type block))))
         [:div.warning.text-sm
         [:div.warning.text-sm
          "Large block will not be editable or searchable to not slow down the app, please use another editor to edit this block."])
          "Large block will not be editable or searchable to not slow down the app, please use another editor to edit this block."])
       [:div.flex.flex-row.justify-between.block-content-inner
       [:div.flex.flex-row.justify-between.block-content-inner
@@ -3088,7 +3113,9 @@
 
 
 (rum/defc breadcrumb-fragment
 (rum/defc breadcrumb-fragment
   [config block label opts]
   [config block label opts]
-  [:a {:on-pointer-up
+  [:a {:on-pointer-down (fn [e]
+                          (when (some? (:sidebar-key config)) (util/stop e)))
+       :on-pointer-up
        (fn [e]
        (fn [e]
          (cond
          (cond
            (gobj/get e "shiftKey")
            (gobj/get e "shiftKey")
@@ -3105,13 +3132,7 @@
              (reset! (:navigating-block opts) (:block/uuid block)))
              (reset! (:navigating-block opts) (:block/uuid block)))
 
 
            (some? (:sidebar-key config))
            (some? (:sidebar-key config))
-           (do
-             (util/stop e)
-             (state/sidebar-replace-block!
-              (:sidebar-key config)
-              [(state/get-current-repo)
-               (:db/id block)
-               (if (:block/name block) :page :block)]))
+           nil
 
 
            :else
            :else
            (when-let [uuid (:block/uuid block)]
            (when-let [uuid (:block/uuid block)]
@@ -3125,81 +3146,92 @@
                             :class "opacity-50 mx-1"}))
                             :class "opacity-50 mx-1"}))
 
 
 ;; "block-id - uuid of the target block of breadcrumb. page uuid is also acceptable"
 ;; "block-id - uuid of the target block of breadcrumb. page uuid is also acceptable"
-(rum/defc breadcrumb < rum/reactive
-  {:init (fn [state]
-           (let [args (:rum/args state)
-                 block-id (nth args 2)
-                 depth (:level-limit (last args))]
-             (p/let [id (:db/id (db/entity [:block/uuid block-id]))
-                     _block (db-async/<get-block (state/get-current-repo) block-id
-                                                 {:children? false})]
-               (when id (db-async/<get-block-parents (state/get-current-repo) id depth)))
-             state))}
+(rum/defc breadcrumb-aux < rum/reactive
   [config repo block-id {:keys [show-page? indent? end-separator? level-limit _navigating-block]
   [config repo block-id {:keys [show-page? indent? end-separator? level-limit _navigating-block]
                          :or {show-page? true
                          :or {show-page? true
                               level-limit 3}
                               level-limit 3}
                          :as opts}]
                          :as opts}]
-  (when block-id
-    (let [_ (state/sub-async-query-loading (str block-id "-parents"))
-          from-property (when (and block-id (config/db-based-graph? repo))
-                          (:logseq.property/created-from-property (db/entity [:block/uuid block-id])))
-          parents (db/get-block-parents repo block-id {:depth (inc level-limit)})
-          parents (remove nil? (concat parents [from-property]))
-          page (or (db/get-block-page repo block-id) ;; only return for block uuid
-                   (model/query-block-by-uuid block-id)) ;; return page entity when received page uuid
-          page-name (:block/name page)
-          page-title (:block/title page)
-          show? (or (seq parents) show-page? page-name)
-          parents (if (= page-name (:block/name (first parents)))
-                    (rest parents)
-                    parents)
-          more? (> (count parents) level-limit)
-          parents (if more? (take-last level-limit parents) parents)
-          config (assoc config :breadcrumb? true)]
-      (when show?
-        (let [page-name-props (when show-page?
-                                [page
-                                 (page-cp (dissoc config :breadcrumb? true) page)
-                                 {:block/name (or page-title page-name)}])
-              parents-props (doall
-                             (for [{:block/keys [uuid name title] :as block} parents]
-                               (if name
-                                 [block (page-cp {} block)]
-                                 (let [result (block/parse-title-and-body
-                                               uuid
-                                               (get block :block/format :markdown)
-                                               (:block/pre-block? block)
-                                               title)
-                                       ast-body (:block.temp/ast-body result)
-                                       ast-title (:block.temp/ast-title result)
-                                       config (assoc config :block/uuid uuid)]
-                                   [block
-                                    (when ast-title
-                                      (if (seq ast-title)
-                                        (->elem :span (map-inline config ast-title))
-                                        (->elem :div (markup-elements-cp config ast-body))))]))))
-              breadcrumbs (->> (into [] parents-props)
-                               (concat [page-name-props] (when more? [:more]))
-                               (filterv identity)
-                               (map (fn [x]
-                                      (if (and (vector? x) (second x))
-                                        (let [[block label] x]
-                                          (rum/with-key (breadcrumb-fragment config block label opts)
-                                            (str (:block/uuid block))))
-                                        [:span.opacity-70 {:key "dots"} "⋯"])))
-                               (interpose (rum/with-key (breadcrumb-separator) "icon")))]
-          (when (seq breadcrumbs)
-            [:div.breadcrumb.block-parents
-             {:class (when (seq breadcrumbs)
-                       (str (when-not (:search? config)
-                              " my-2")
-                            (when indent?
-                              " ml-4")))}
-             (when (and (false? (:top-level? config))
-                        (seq parents))
-               (breadcrumb-separator))
-             breadcrumbs
-             (when end-separator? (breadcrumb-separator))]))))))
+  (let [from-property (when (and block-id (config/db-based-graph? repo))
+                        (:logseq.property/created-from-property (db/entity [:block/uuid block-id])))
+        parents (db/get-block-parents repo block-id {:depth (inc level-limit)})
+        parents (remove nil? (concat parents [from-property]))
+        page (or (db/get-block-page repo block-id) ;; only return for block uuid
+                 (model/query-block-by-uuid block-id)) ;; return page entity when received page uuid
+        page-name (:block/name page)
+        page-title (:block/title page)
+        show? (or (seq parents) show-page? page-name)
+        parents (if (= page-name (:block/name (first parents)))
+                  (rest parents)
+                  parents)
+        more? (> (count parents) level-limit)
+        parents (if more? (take-last level-limit parents) parents)
+        config (assoc config
+                      :breadcrumb? true
+                      :disable-redirect? true
+                      :disable-preview? true
+                      :stop-click-event? false)]
+    (when show?
+      (let [page-name-props (when show-page?
+                              [page
+                               (page-cp (dissoc config :breadcrumb? true) page)
+                               {:block/name (or page-title page-name)}])
+            parents-props (doall
+                           (for [{:block/keys [uuid name title] :as block} parents]
+                             (if name
+                               [block (page-cp {} block)]
+                               (let [result (block/parse-title-and-body
+                                             uuid
+                                             (get block :block/format :markdown)
+                                             (:block/pre-block? block)
+                                             title)
+                                     ast-body (:block.temp/ast-body result)
+                                     ast-title (:block.temp/ast-title result)
+                                     config (assoc config :block/uuid uuid)]
+                                 [block
+                                  (when ast-title
+                                    (if (seq ast-title)
+                                      (->elem :span (map-inline config ast-title))
+                                      (->elem :div (markup-elements-cp config ast-body))))]))))
+            breadcrumbs (->> (into [] parents-props)
+                             (concat [page-name-props] (when more? [:more]))
+                             (filterv identity)
+                             (map (fn [x]
+                                    (if (and (vector? x) (second x))
+                                      (let [[block label] x]
+                                        (rum/with-key (breadcrumb-fragment config block label opts)
+                                          (str (:block/uuid block))))
+                                      [:span.opacity-70 {:key "dots"} "⋯"])))
+                             (interpose (rum/with-key (breadcrumb-separator) "icon")))]
+        (when (seq breadcrumbs)
+          [:div.breadcrumb.block-parents
+           {:class (when (seq breadcrumbs)
+                     (str (when-not (or (:search? config) (:list-view? config))
+                            " my-2")
+                          (when indent?
+                            " ml-4")))}
+           (when (and (false? (:top-level? config))
+                      (seq parents))
+             (breadcrumb-separator))
+           breadcrumbs
+           (when end-separator? (breadcrumb-separator))])))))
+
+(rum/defc breadcrumb
+  [config repo block-id {:keys [_show-page? _indent? _end-separator? level-limit _navigating-block]
+                         :or {level-limit 3}
+                         :as opts}]
+  (let [[block set-block!] (hooks/use-state (db/entity [:block/uuid block-id]))]
+    (hooks/use-effect!
+     (fn []
+       (p/let [block (db-async/<get-block (state/get-current-repo)
+                                          block-id
+                                          {:children? false
+                                           :skip-refresh? true})
+               _ (when-let [id (:db/id block)]
+                   (db-async/<get-block-parents (state/get-current-repo) id level-limit))]
+         (set-block! block)))
+     [])
+    (when block
+      (breadcrumb-aux config repo block-id opts))))
 
 
 (defn- block-drag-over
 (defn- block-drag-over
   [event uuid top? block-id *move-to']
   [event uuid top? block-id *move-to']
@@ -3359,7 +3391,8 @@
     (nil? (:level config))
     (nil? (:level config))
     (assoc :level 0)))
     (assoc :level 0)))
 
 
-(defn- build-block [config block* {:keys [navigating-block navigated?]}]
+(defn- build-block
+  [config block* {:keys [navigating-block navigated?]}]
   (let [linked-block (:block/link (db/entity (:db/id block*)))
   (let [linked-block (:block/link (db/entity (:db/id block*)))
         block (cond
         block (cond
                 (or (and (:custom-query? config)
                 (or (and (:custom-query? config)
@@ -3377,8 +3410,7 @@
 
 
                 :else
                 :else
                 block*)
                 block*)
-        result (merge (db/sub-block (:db/id block))
-                      (select-keys block [:block/level :block.temp/top? :block.temp/bottom?]))]
+        result (or (db/sub-block (:db/id block)) block*)]
     (if linked-block
     (if linked-block
       [block* result]
       [block* result]
       [nil result])))
       [nil result])))
@@ -3402,17 +3434,12 @@
           [:div.my-1 {:style {:margin-left 42}}
           [:div.my-1 {:style {:margin-left 42}}
            (block-container (assoc config :property? true) query)])))))
            (block-container (assoc config :property? true) query)])))))
 
 
-(rum/defcs ^:large-vars/cleanup-todo block-container-inner < rum/reactive db-mixins/query
+(rum/defcs ^:large-vars/cleanup-todo block-container-inner-aux < rum/reactive db-mixins/query
   {:init (fn [state]
   {:init (fn [state]
-           (let [*ref (atom nil)
-                 block (nth (:rum/args state) 3)
-                 block-id (:db/id block)
-                 repo (state/get-current-repo)]
-             (db-async/<get-block repo block-id :children? true)
+           (let [*ref (atom nil)]
              (assoc state ::ref *ref)))}
              (assoc state ::ref *ref)))}
-  [state container-state repo config* block {:keys [navigating-block navigated?]}]
+  [state container-state repo config* block {:keys [navigating-block navigated? editing? selected?] :as opts}]
   (let [*ref (::ref state)
   (let [*ref (::ref state)
-        _ (when (:block/uuid block) (state/sub-async-query-loading (:block/uuid block)))
         [original-block block] (build-block config* block {:navigating-block navigating-block :navigated? navigated?})
         [original-block block] (build-block config* block {:navigating-block navigating-block :navigated? navigated?})
         config* (if original-block
         config* (if original-block
                   (assoc config* :original-block original-block)
                   (assoc config* :original-block original-block)
@@ -3424,8 +3451,6 @@
                                (str (:block/uuid block))))
                                (str (:block/uuid block))))
         edit-input-id (str "edit-block-" (:block/uuid block))
         edit-input-id (str "edit-block-" (:block/uuid block))
         container-id (:container-id config*)
         container-id (:container-id config*)
-        editing? (or (state/sub-editing? [container-id (:block/uuid block)])
-                     (state/sub-editing? [:unknown-container (:block/uuid block)]))
         table? (:table? config*)
         table? (:table? config*)
         sidebar? (:sidebar? config*)
         sidebar? (:sidebar? config*)
         property? (:property? config*)
         property? (:property? config*)
@@ -3438,7 +3463,9 @@
         *control-show? (get container-state ::control-show?)
         *control-show? (get container-state ::control-show?)
         db-collapsed? (util/collapsed? block)
         db-collapsed? (util/collapsed? block)
         collapsed? (cond
         collapsed? (cond
-                     (or ref-or-custom-query? (root-block? config block)
+                     (or ref-or-custom-query?
+                         (:view? config)
+                         (root-block? config block)
                          (and (or (ldb/class? block) (ldb/property? block)) (:page-title? config)))
                          (and (or (ldb/class? block) (ldb/property? block)) (:page-title? config)))
                      (state/sub-block-collapsed uuid)
                      (state/sub-block-collapsed uuid)
 
 
@@ -3462,7 +3489,6 @@
         own-number-list? (:own-order-number-list? config)
         own-number-list? (:own-order-number-list? config)
         order-list? (boolean own-number-list?)
         order-list? (boolean own-number-list?)
         children (ldb/get-children block)
         children (ldb/get-children block)
-        selected? (when (uuid? (:block/uuid block)) (state/sub-block-selected? (:block/uuid block)))
         db-based? (config/db-based-graph? repo)
         db-based? (config/db-based-graph? repo)
         page-icon (when (:page-title? config)
         page-icon (when (:page-title? config)
                     (let [icon' (get block (pu/get-pid :logseq.property/icon))]
                     (let [icon' (get block (pu/get-pid :logseq.property/icon))]
@@ -3561,11 +3587,12 @@
                           (= uuid (:block/uuid (state/get-edit-block))))]
                           (= uuid (:block/uuid (state/get-edit-block))))]
             (block-control (assoc config :hide-bullet? (:page-title? config))
             (block-control (assoc config :hide-bullet? (:page-title? config))
                            block
                            block
-                           {:uuid uuid
-                            :block-id block-id
-                            :collapsed? collapsed?
-                            :*control-show? *control-show?
-                            :edit? edit?})))
+                           (merge opts
+                                  {:uuid uuid
+                                   :block-id block-id
+                                   :collapsed? collapsed?
+                                   :*control-show? *control-show?
+                                   :edit? edit?}))))
 
 
         (when (and @*show-left-menu? (not in-whiteboard?) (not (or table? property?)))
         (when (and @*show-left-menu? (not in-whiteboard?) (not (or table? property?)))
           (block-left-menu config block))
           (block-left-menu config block))
@@ -3630,32 +3657,64 @@
 
 
      (when-not (or in-whiteboard? table? property?) (dnd-separator-wrapper block children block-id slide? false false))]))
      (when-not (or in-whiteboard? table? property?) (dnd-separator-wrapper block children block-id slide? false false))]))
 
 
+(rum/defc ^:large-vars/cleanup-todo block-container-inner
+  [container-state repo config* block opts]
+  (let [container-id (:container-id config*)
+        block-id (:block/uuid block)
+        v1 (state/sub-editing? [container-id block-id])
+        v2 (state/sub-editing? [:unknown-container block-id])
+        selected? (state/sub-block-selected? block-id)
+        editing? (or v1 v2)]
+    (block-container-inner-aux container-state repo config* block (assoc opts
+                                                                         :editing? editing?
+                                                                         :selected? selected?))))
+
 (defn- block-changed?
 (defn- block-changed?
   [old-block new-block]
   [old-block new-block]
   (not= (:block/tx-id old-block) (:block/tx-id new-block)))
   (not= (:block/tx-id old-block) (:block/tx-id new-block)))
 
 
-(rum/defcs block-container < rum/reactive db-mixins/query
+(defn- config-block-should-update?
+  [old-state new-state]
+  (let [config-compare-keys [:show-cloze? :hide-children? :own-order-list-type :own-order-list-index :original-block :edit? :hide-bullet?]
+        b1                  (second (:rum/args old-state))
+        b2                  (second (:rum/args new-state))
+        result              (or
+                             (block-changed? b1 b2)
+                                               ;; config changed
+                             (not= (select-keys (first (:rum/args old-state)) config-compare-keys)
+                                   (select-keys (first (:rum/args new-state)) config-compare-keys)))]
+    (boolean result)))
+
+(defn- set-collapsed-block!
+  [block-id v]
+  (if (false? v)
+    (editor-handler/expand-block! block-id {:skip-db-collpsing? true})
+    (state/set-collapsed-block! block-id v)))
+
+(rum/defcs loaded-block-container < rum/reactive db-mixins/query
   (rum/local false ::show-block-left-menu?)
   (rum/local false ::show-block-left-menu?)
   (rum/local false ::show-block-right-menu?)
   (rum/local false ::show-block-right-menu?)
+  {:should-update config-block-should-update?}
   {:init (fn [state]
   {:init (fn [state]
            (let [[config block] (:rum/args state)
            (let [[config block] (:rum/args state)
                  block-id (:block/uuid block)
                  block-id (:block/uuid block)
                  linked-block? (or (:block/link block)
                  linked-block? (or (:block/link block)
                                    (:original-block config))]
                                    (:original-block config))]
-             (cond
-               (and (:page-title? config) (or (ldb/class? block) (ldb/property? block)) (not config/publishing?))
-               (let [collapsed? (state/get-block-collapsed block-id)]
-                 (state/set-collapsed-block! block-id (if (some? collapsed?) collapsed? true)))
+             (when-not (:property-block? config)
+               (cond
+                 (and (:page-title? config) (or (ldb/class? block) (ldb/property? block)) (not config/publishing?))
+                 (let [collapsed? (state/get-block-collapsed block-id)]
+                   (set-collapsed-block! block-id (if (some? collapsed?) collapsed? true)))
 
 
-               (root-block? config block)
-               (state/set-collapsed-block! block-id false)
+                 (root-block? config block)
+                 (set-collapsed-block! block-id false)
 
 
-               (or (:ref? config) (:custom-query? config))
-               (state/set-collapsed-block! block-id
-                                           (boolean (editor-handler/block-default-collapsed? block config)))
+                 (or (:view? config) (:ref? config) (:custom-query? config))
+                 (set-collapsed-block! block-id
+                                       (boolean (editor-handler/block-default-collapsed? block config)))
 
 
-               :else
-               nil)
+                 :else
+                 nil))
              (cond->
              (cond->
               (assoc state
               (assoc state
                      ::control-show? (atom false)
                      ::control-show? (atom false)
@@ -3667,9 +3726,9 @@
                    (let [[config block] (:rum/args state)
                    (let [[config block] (:rum/args state)
                          block-id (:block/uuid block)]
                          block-id (:block/uuid block)]
                      (when (root-block? config block)
                      (when (root-block? config block)
-                       (state/set-collapsed-block! block-id nil)))
+                       (set-collapsed-block! block-id nil)))
                    state)}
                    state)}
-  [state config block]
+  [state config block & {:as opts}]
   (let [repo (state/get-current-repo)
   (let [repo (state/get-current-repo)
         *navigating-block (get state ::navigating-block)
         *navigating-block (get state ::navigating-block)
         navigating-block (rum/react *navigating-block)
         navigating-block (rum/react *navigating-block)
@@ -3685,12 +3744,34 @@
           [:code.flex.p-1.text-red-rx-09 "Block render error: " (.-message error)]])
           [:code.flex.p-1.text-red-rx-09 "Block render error: " (.-message error)]])
        (rum/with-key
        (rum/with-key
          (block-container-inner state repo config' block
          (block-container-inner state repo config' block
-                                {:navigating-block navigating-block :navigated? navigated?})
+                                (merge
+                                 opts
+                                 {:navigating-block navigating-block :navigated? navigated?}))
          (str "block-inner-"
          (str "block-inner-"
               (:container-id config)
               (:container-id config)
               "-"
               "-"
               (:block/uuid block)))))))
               (:block/uuid block)))))))
 
 
+(rum/defc block-container
+  [config block* & {:as opts}]
+  (let [[block set-block!] (hooks/use-state block*)
+        id (or (:db/id block*) (:block/uuid block*))]
+    (when-not (or (:page-title? config)
+                  (:view? config))
+      (hooks/use-effect!
+       (fn []
+         (p/let [block (db-async/<get-block (state/get-current-repo)
+                                            id
+                                            {:children? (not
+                                                         (if-some [result (state/get-block-collapsed (:block/uuid block))]
+                                                           result
+                                                           (:block/collapsed? block)))
+                                             :skip-refresh? false})]
+           (set-block! block)))
+       []))
+    (when (or (:view? config) (:block/title block))
+      (loaded-block-container config block opts))))
+
 (defn divide-lists
 (defn divide-lists
   [[f & l]]
   [[f & l]]
   (loop [l        l
   (loop [l        l
@@ -3845,6 +3926,12 @@
   [config col]
   [config col]
   (map #(inline config %) col))
   (map #(inline config %) col))
 
 
+(rum/defc inline-title
+  [title]
+  (map-inline {}
+              (gp-mldoc/inline->edn title
+                                    (mldoc/get-default-config :markdown))))
+
 (declare ->hiccup)
 (declare ->hiccup)
 
 
 (defn- get-code-mode-by-lang
 (defn- get-code-mode-by-lang
@@ -3888,10 +3975,10 @@
             [:div.ui-fenced-code-editor.flex.w-full
             [:div.ui-fenced-code-editor.flex.w-full
              {:ref (fn [el]
              {:ref (fn [el]
                      (set-inside-portal? (and el (whiteboard-handler/inside-portal? el))))
                      (set-inside-portal? (and el (whiteboard-handler/inside-portal? el))))
-              :on-mouse-over #(dom/add-class! (hooks/deref *actions-ref) "opacity-100")
+              :on-mouse-over #(dom/add-class! (hooks/deref *actions-ref) "!opacity-100")
               :on-mouse-leave (fn [e]
               :on-mouse-leave (fn [e]
                                 (when (dom/has-class? (.-target e) "code-editor")
                                 (when (dom/has-class? (.-target e) "code-editor")
-                                  (dom/remove-class! (hooks/deref *actions-ref) "opacity-100")))}
+                                  (dom/remove-class! (hooks/deref *actions-ref) "!opacity-100")))}
              (cond
              (cond
                (nil? inside-portal?) nil
                (nil? inside-portal?) nil
 
 
@@ -3903,7 +3990,7 @@
 
 
                :else
                :else
                [:div.ls-code-editor-wrap
                [:div.ls-code-editor-wrap
-                [:div.code-block-actions.flex.flex-row.gap-1.opacity-0.transition-opacity.ease-in.duration-300
+                [:div.code-block-actions
                  {:ref *actions-ref}
                  {:ref *actions-ref}
                  (shui/button
                  (shui/button
                   {:variant :text
                   {:variant :text
@@ -4128,16 +4215,7 @@
   (map #(markup-element-cp config %) col))
   (map #(markup-element-cp config %) col))
 
 
 (rum/defc block-item <
 (rum/defc 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 :edit? :hide-bullet?]
-                          b1                  (second (:rum/args old-state))
-                          b2                  (second (:rum/args new-state))
-                          result              (or
-                                               (block-changed? b1 b2)
-                                               ;; config changed
-                                               (not= (select-keys (first (:rum/args old-state)) config-compare-keys)
-                                                     (select-keys (first (:rum/args new-state)) config-compare-keys)))]
-                      (boolean result)))}
+  {:should-update config-block-should-update?}
   [config item {:keys [top? bottom?]}]
   [config item {:keys [top? bottom?]}]
   (let [original-block item
   (let [original-block item
         linked-block (:block/link item)
         linked-block (:block/link item)
@@ -4147,15 +4225,15 @@
                      (update :links (fn [ids] (conj (or ids #{}) (:db/id linked-block)))))
                      (update :links (fn [ids] (conj (or ids #{}) (:db/id linked-block)))))
                  config)
                  config)
         item (or (if loop-linked? item linked-block) item)
         item (or (if loop-linked? item linked-block) item)
-        item (cond-> (dissoc item :block/meta)
-               (not (:block-children? config))
-               (assoc :block.temp/top? top?
-                      :block.temp/bottom? bottom?))
+        item (dissoc item :block/meta)
         config' (assoc config
         config' (assoc config
                        :block/uuid (:block/uuid item)
                        :block/uuid (:block/uuid item)
                        :loop-linked? loop-linked?)]
                        :loop-linked? loop-linked?)]
     (when-not (and loop-linked? (:block/name linked-block))
     (when-not (and loop-linked? (:block/name linked-block))
-      (rum/with-key (block-container config' item)
+      (rum/with-key (block-container config' item
+                                     (when (not (:block-children? config))
+                                       {:top? top?
+                                        :bottom? bottom?}))
         (str (:block/uuid item)
         (str (:block/uuid item)
              (when linked-block
              (when linked-block
                (str "-" (:block/uuid original-block))))))))
                (str "-" (:block/uuid original-block))))))))

+ 1 - 1
src/main/frontend/components/block.css

@@ -1040,7 +1040,7 @@ html.is-mac {
   }
   }
 
 
   > .code-block-actions {
   > .code-block-actions {
-    @apply absolute right-1 top-1 select-none z-[1] text-xs;
+    @apply flex flex-row gap-1 opacity-0 transition-opacity ease-in duration-300 absolute right-1 top-1 select-none z-[1] text-xs bg-gray-01;
 
 
     button {
     button {
       @apply !py-0 h-4 text-muted-foreground hover:text-foreground text-xs px-1;
       @apply !py-0 h-4 text-muted-foreground hover:text-foreground text-xs px-1;

+ 6 - 7
src/main/frontend/components/class.cljs

@@ -27,10 +27,9 @@
     (let [children-pages (set (model/get-structured-children (state/get-current-repo) (:db/id class)))
     (let [children-pages (set (model/get-structured-children (state/get-current-repo) (:db/id class)))
           ;; Expand children if there are about a pageful of total blocks to display
           ;; Expand children if there are about a pageful of total blocks to display
           default-collapsed? (> (count children-pages) 30)]
           default-collapsed? (> (count children-pages) 30)]
-      [:div.my-4
-       (ui/foldable
-        [:div.font-medium.opacity-50
-         (str "Children (" (count children-pages) ")")]
-        [:div.ml-1.mt-2 (class-children-aux class {:default-collapsed? default-collapsed?})]
-        {:default-collapsed? false
-         :title-trigger? true})])))
+      (ui/foldable
+       [:div.font-medium.opacity-50
+        (str "Children (" (count children-pages) ")")]
+       [:div.ml-1.mt-2 (class-children-aux class {:default-collapsed? default-collapsed?})]
+       {:default-collapsed? false
+        :title-trigger? true}))))

+ 15 - 18
src/main/frontend/components/cmdk/core.cljs

@@ -121,7 +121,6 @@
                                       (= input (util/page-name-sanity-lc (:block/title block))))) blocks-result))))
                                       (= input (util/page-name-sanity-lc (:block/title block))))) blocks-result))))
         include-slash? (or (string/includes? input "/")
         include-slash? (or (string/includes? input "/")
                            (string/starts-with? input "/"))
                            (string/starts-with? input "/"))
-        db-based? (config/db-based-graph?)
         order* (cond
         order* (cond
                  (= search-mode :graph)
                  (= search-mode :graph)
                  []
                  []
@@ -150,8 +149,7 @@
                      ["Create"         :create       (create-items input)])
                      ["Create"         :create       (create-items input)])
                    ["Current page"     :current-page   (visible-items :current-page)]
                    ["Current page"     :current-page   (visible-items :current-page)]
                    ["Nodes"            :nodes         (visible-items :nodes)]
                    ["Nodes"            :nodes         (visible-items :nodes)]
-                   (when (and db-based? (string/blank? input))
-                     ["Recently updated" :recently-updated-pages (visible-items :recently-updated-pages)])
+                   ["Recently updated" :recently-updated-pages (visible-items :recently-updated-pages)]
                    ["Commands"         :commands       (visible-items :commands)]
                    ["Commands"         :commands       (visible-items :commands)]
                    ["Files"            :files          (visible-items :files)]
                    ["Files"            :files          (visible-items :files)]
                    ["Filters"          :filters        (visible-items :filters)]]
                    ["Filters"          :filters        (visible-items :filters)]]
@@ -199,18 +197,16 @@
     "page"))
     "page"))
 
 
 (defmethod load-results :initial [_ state]
 (defmethod load-results :initial [_ state]
-  (let [!results (::results state)]
-    (if (config/db-based-graph?)
-      (let [recent-pages (map (fn [block]
-                                (let [text (block-handler/block-unique-title block)
-                                      icon (get-page-icon block)]
-                                  {:icon icon
-                                   :icon-theme :gray
-                                   :text text
-                                   :source-block block}))
-                              (ldb/get-recent-updated-pages (db/get-db)))]
-        (reset! !results (assoc-in default-results [:recently-updated-pages :items] recent-pages)))
-      !results)))
+  (let [!results (::results state)
+        recent-pages (map (fn [block]
+                            (let [text (block-handler/block-unique-title block)
+                                  icon (get-page-icon block)]
+                              {:icon icon
+                               :icon-theme :gray
+                               :text text
+                               :source-block block}))
+                          (ldb/get-recent-updated-pages (db/get-db)))]
+    (reset! !results (assoc-in default-results [:recently-updated-pages :items] recent-pages))))
 
 
 ;; The commands search uses the command-palette handler
 ;; The commands search uses the command-palette handler
 (defmethod load-results :commands [group state]
 (defmethod load-results :commands [group state]
@@ -409,8 +405,9 @@
 
 
 (defmethod handle-action :open-page [_ state _event]
 (defmethod handle-action :open-page [_ state _event]
   (when-let [page-name (get-highlighted-page-uuid-or-name state)]
   (when-let [page-name (get-highlighted-page-uuid-or-name state)]
-    (let [page (db/get-page page-name)]
-      (route-handler/redirect-to-page! (:block/uuid page)))
+    (let [page-uuid (get (db/get-page page-name) :block/uuid
+                         (when (uuid? page-name) page-name))]
+      (route-handler/redirect-to-page! page-uuid))
     (shui/dialog-close! :ls-dialog-cmdk)))
     (shui/dialog-close! :ls-dialog-cmdk)))
 
 
 (defmethod handle-action :open-block [_ state _event]
 (defmethod handle-action :open-block [_ state _event]
@@ -803,7 +800,7 @@
                                   (handle-input-change state e)
                                   (handle-input-change state e)
                                   (when-let [on-change (:on-input-change opts)]
                                   (when-let [on-change (:on-input-change opts)]
                                     (on-change new-value))))
                                     (on-change new-value))))
-                              100)
+                              200)
                              [])]
                              [])]
     ;; use-effect [results-ordered input] to check whether the highlighted item is still in the results,
     ;; use-effect [results-ordered input] to check whether the highlighted item is still in the results,
     ;; if not then clear that puppy out!
     ;; if not then clear that puppy out!

+ 8 - 4
src/main/frontend/components/container.cljs

@@ -20,6 +20,7 @@
             [frontend.context.i18n :refer [t tt]]
             [frontend.context.i18n :refer [t tt]]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
+            [frontend.db.async :as db-async]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
             [frontend.extensions.fsrs :as fsrs]
             [frontend.extensions.fsrs :as fsrs]
             [frontend.extensions.pdf.utils :as pdf-utils]
             [frontend.extensions.pdf.utils :as pdf-utils]
@@ -56,6 +57,7 @@
             [logseq.shui.toaster.core :as shui-toaster]
             [logseq.shui.toaster.core :as shui-toaster]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
+            [promesa.core :as p]
             [react-draggable]
             [react-draggable]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))
@@ -707,8 +709,7 @@
   (let [default-home (get-default-home-if-valid)
   (let [default-home (get-default-home-if-valid)
         current-repo (state/sub :git/current-repo)
         current-repo (state/sub :git/current-repo)
         loading-files? (when current-repo (state/sub [:repo/loading-files? current-repo]))
         loading-files? (when current-repo (state/sub [:repo/loading-files? current-repo]))
-        journals-length (state/sub :journals-length)
-        latest-journals (db/get-latest-journals (state/get-current-repo) journals-length)
+        latest-journals (db/get-latest-journals (state/get-current-repo) 1)
         graph-parsing-state (state/sub [:graph/parsing-state current-repo])]
         graph-parsing-state (state/sub [:graph/parsing-state current-repo])]
     (cond
     (cond
       (or
       (or
@@ -736,7 +737,7 @@
          (ui/loading (t :loading-files))
          (ui/loading (t :loading-files))
 
 
          (seq latest-journals)
          (seq latest-journals)
-         (journal/journals latest-journals)
+         (journal/all-journals)
 
 
          ;; FIXME: why will this happen?
          ;; FIXME: why will this happen?
          :else
          :else
@@ -748,6 +749,7 @@
   (when-not (or (gobj/get e "shiftKey")
   (when-not (or (gobj/get e "shiftKey")
                 (util/meta-key? e)
                 (util/meta-key? e)
                 (state/get-edit-input-id)
                 (state/get-edit-input-id)
+                (some-> (.-target e) util/input?)
                 (= (shui-dialog/get-last-modal-id) :property-dialog)
                 (= (shui-dialog/get-last-modal-id) :property-dialog)
                 (some-> (.-target e) (.closest ".ls-block"))
                 (some-> (.-target e) (.closest ".ls-block"))
                 (some-> (.-target e) (.closest "[data-keep-selection]")))
                 (some-> (.-target e) (.closest "[data-keep-selection]")))
@@ -916,7 +918,9 @@
                                   (when block
                                   (when block
                                     (state/clear-selection!)
                                     (state/clear-selection!)
                                     (state/conj-selection-block! block :down))
                                     (state/conj-selection-block! block :down))
-                                  (show! (cp-content/block-context-menu-content target (uuid block-id) property-default-value?)))
+                                  (p/do!
+                                   (db-async/<get-block (state/get-current-repo) (uuid block-id) {:children? false})
+                                   (show! (cp-content/block-context-menu-content target (uuid block-id) property-default-value?))))
 
 
                                 :else
                                 :else
                                 false)]
                                 false)]

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

@@ -417,7 +417,7 @@
      hiccup
      hiccup
      [:div.cursor (t :content/click-to-edit)])])
      [:div.cursor (t :content/click-to-edit)])])
 
 
-(rum/defc non-hiccup-content < rum/reactive
+(rum/defc non-hiccup-content
   [id content on-click on-hide config format]
   [id content on-click on-hide config format]
   (let [edit? (state/sub-editing? id)]
   (let [edit? (state/sub-editing? id)]
     (if edit?
     (if edit?

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

@@ -1,6 +1,5 @@
 (ns frontend.components.editor
 (ns frontend.components.editor
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
-            [datascript.impl.entity :as de]
             [dommy.core :as dom]
             [dommy.core :as dom]
             [frontend.commands :as commands :refer [*matched-commands]]
             [frontend.commands :as commands :refer [*matched-commands]]
             [frontend.components.file-based.datetime :as datetime-comp]
             [frontend.components.file-based.datetime :as datetime-comp]
@@ -138,16 +137,17 @@
 
 
 (rum/defc page-search-aux
 (rum/defc page-search-aux
   [id format embed? db-tag? q current-pos input pos]
   [id format embed? db-tag? q current-pos input pos]
-  (let [db? (config/db-based-graph? (state/get-current-repo))
+  (let [db-based? (config/db-based-graph? (state/get-current-repo))
         q (string/trim q)
         q (string/trim q)
         [matched-pages set-matched-pages!] (rum/use-state nil)
         [matched-pages set-matched-pages!] (rum/use-state nil)
         search-f (fn []
         search-f (fn []
                    (when-not (string/blank? q)
                    (when-not (string/blank? q)
                      (p/let [result (if db-tag?
                      (p/let [result (if db-tag?
                                       (editor-handler/get-matched-classes q)
                                       (editor-handler/get-matched-classes q)
-                                      (editor-handler/<get-matched-blocks q {:nlp-pages? true}))]
+                                      (editor-handler/<get-matched-blocks q {:nlp-pages? true
+                                                                             :page-only? (not db-based?)}))]
                        (set-matched-pages! result))))]
                        (set-matched-pages! result))))]
-    (hooks/use-effect! search-f [(hooks/use-debounced-value q 50)])
+    (hooks/use-effect! search-f [(hooks/use-debounced-value q 150)])
 
 
     (let [matched-pages' (if (string/blank? q)
     (let [matched-pages' (if (string/blank? q)
                            (if db-tag?
                            (if db-tag?
@@ -183,53 +183,53 @@
          :on-enter    (fn []
          :on-enter    (fn []
                         (page-handler/page-not-exists-handler input id q current-pos))
                         (page-handler/page-not-exists-handler input id q current-pos))
          :item-render (fn [block _chosen?]
          :item-render (fn [block _chosen?]
-                        (let [block (if (and (:block/uuid block) (not (de/entity? block)))
-                                      (db/entity [:block/uuid (:block/uuid block)])
-                                      block)]
+                        (let [block' (if-let [id (:block/uuid block)]
+                                       (or (db/entity [:block/uuid id]) block)
+                                       block)]
                           [:div.flex.flex-col
                           [:div.flex.flex-col
-                           (when-not (db/page? block)
+                           (when (and (not (:page? block)) (:block/uuid block'))
                              (when-let [breadcrumb (state/get-component :block/breadcrumb)]
                              (when-let [breadcrumb (state/get-component :block/breadcrumb)]
                                [:div.text-xs.opacity-70.mb-1 {:style {:margin-left 3}}
                                [:div.text-xs.opacity-70.mb-1 {:style {:margin-left 3}}
-                                (breadcrumb {:search? true} (state/get-current-repo) (:block/uuid block) {})]))
+                                (breadcrumb {:search? true} (state/get-current-repo) (:block/uuid block') {})]))
                            [:div.flex.flex-row.items-center.gap-1
                            [:div.flex.flex-row.items-center.gap-1
-                            (when-not db-tag?
+                            (when-not (or db-tag? (not db-based?))
                               [:div.flex.items-center
                               [:div.flex.items-center
                                (cond
                                (cond
-                                 (:nlp-date? block)
+                                 (:nlp-date? block')
                                  (ui/icon "calendar" {:size 14})
                                  (ui/icon "calendar" {:size 14})
 
 
-                                 (ldb/class? block)
+                                 (ldb/class? block')
                                  (ui/icon "hash" {:size 14})
                                  (ui/icon "hash" {:size 14})
 
 
-                                 (ldb/property? block)
+                                 (ldb/property? block')
                                  (ui/icon "letter-p" {:size 14})
                                  (ui/icon "letter-p" {:size 14})
 
 
-                                 (db-model/whiteboard-page? block)
+                                 (db-model/whiteboard-page? block')
                                  (ui/icon "whiteboard" {:extension? true})
                                  (ui/icon "whiteboard" {:extension? true})
 
 
-                                 (db/page? block)
+                                 (:page? block')
                                  (ui/icon "page" {:extension? true})
                                  (ui/icon "page" {:extension? true})
 
 
-                                 (or (string/starts-with? (str (:block/title block)) (t :new-tag))
-                                     (string/starts-with? (str (:block/title block)) (t :new-page)))
+                                 (or (string/starts-with? (str (:block/title block')) (t :new-tag))
+                                     (string/starts-with? (str (:block/title block')) (t :new-page)))
                                  (ui/icon "plus" {:size 14})
                                  (ui/icon "plus" {:size 14})
 
 
                                  :else
                                  :else
                                  (ui/icon "letter-n" {:size 14}))])
                                  (ui/icon "letter-n" {:size 14}))])
 
 
                             (let [title (if db-tag?
                             (let [title (if db-tag?
-                                          (let [target (first (:block/_alias block))]
+                                          (let [target (first (:block/_alias block'))]
                                             (if (ldb/class? target)
                                             (if (ldb/class? target)
-                                              (str (:block/title block) " -> alias: " (:block/title target))
-                                              (:block/title block)))
-                                          (block-handler/block-unique-title block))]
+                                              (str (:block/title block') " -> alias: " (:block/title target))
+                                              (:block/title block')))
+                                          (block-handler/block-unique-title block'))]
                               (search-handler/highlight-exact-query title q))]]))
                               (search-handler/highlight-exact-query title q))]]))
          :empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 (if db-tag?
          :empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 (if db-tag?
                                                                     "Search for a tag"
                                                                     "Search for a tag"
                                                                     "Search for a node")]
                                                                     "Search for a node")]
          :class "black"})
          :class "black"})
 
 
-       (when (and db? db-tag? (not (string/blank? q)))
+       (when (and db-based? db-tag? (not (string/blank? q)))
          [:p.px-1.opacity-50.text-sm
          [:p.px-1.opacity-50.text-sm
           [:code (if util/mac? "Cmd+Enter" "Ctrl+Enter")]
           [:code (if util/mac? "Cmd+Enter" "Ctrl+Enter")]
           [:span " to display this tag inline instead of at the end of this node."]])])))
           [:span " to display this tag inline instead of at the end of this node."]])])))

+ 42 - 35
src/main/frontend/components/export.cljs

@@ -5,6 +5,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db :as db]
+            [frontend.handler.block :as block-handler]
             [frontend.handler.db-based.export :as db-export-handler]
             [frontend.handler.db-based.export :as db-export-handler]
             [frontend.handler.export :as export]
             [frontend.handler.export :as export]
             [frontend.handler.export.html :as export-html]
             [frontend.handler.export.html :as export-html]
@@ -144,7 +145,7 @@
                                  :selected false}])
                                  :selected false}])
 
 
 (defn- export-helper
 (defn- export-helper
-  [block-uuids-or-page-name]
+  [top-level-ids]
   (let [current-repo (state/get-current-repo)
   (let [current-repo (state/get-current-repo)
         text-indent-style (state/get-export-block-text-indent-style)
         text-indent-style (state/get-export-block-text-indent-style)
         text-remove-options (set (state/get-export-block-text-remove-options))
         text-remove-options (set (state/get-export-block-text-remove-options))
@@ -152,19 +153,19 @@
         tp @*export-block-type]
         tp @*export-block-type]
     (case tp
     (case tp
       :text (export-text/export-blocks-as-markdown
       :text (export-text/export-blocks-as-markdown
-             current-repo block-uuids-or-page-name
+             current-repo top-level-ids
              {:indent-style text-indent-style :remove-options text-remove-options :other-options text-other-options})
              {:indent-style text-indent-style :remove-options text-remove-options :other-options text-other-options})
       :opml (export-opml/export-blocks-as-opml
       :opml (export-opml/export-blocks-as-opml
-             current-repo block-uuids-or-page-name {:remove-options text-remove-options :other-options text-other-options})
+             current-repo top-level-ids {:remove-options text-remove-options :other-options text-other-options})
       :html (export-html/export-blocks-as-html
       :html (export-html/export-blocks-as-html
-             current-repo block-uuids-or-page-name {:remove-options text-remove-options :other-options text-other-options})
+             current-repo top-level-ids {:remove-options text-remove-options :other-options text-other-options})
       "")))
       "")))
 
 
 (defn- <export-edn-helper
 (defn- <export-edn-helper
   [root-block-uuids-or-page-uuid export-type]
   [root-block-uuids-or-page-uuid export-type]
   (let [export-args (case export-type
   (let [export-args (case export-type
                       :page
                       :page
-                      {:page-id [:block/uuid root-block-uuids-or-page-uuid]}
+                      {:page-id [:block/uuid (first root-block-uuids-or-page-uuid)]}
                       :block
                       :block
                       {:block-id [:block/uuid (first root-block-uuids-or-page-uuid)]}
                       {:block-id [:block/uuid (first root-block-uuids-or-page-uuid)]}
                       :selected-nodes
                       :selected-nodes
@@ -214,6 +215,11 @@
                                                   (set! (.-src img) img-url)
                                                   (set! (.-src img) img-url)
                                                   (callback blob)))) "image/png"))))))
                                                   (callback blob)))) "image/png"))))))
 
 
+(defn- get-top-level-uuids
+  [selection-ids]
+  (->> (block-handler/get-top-level-blocks (map #(db/entity [:block/uuid %]) selection-ids))
+       (map :block/uuid)))
+
 (rum/defcs ^:large-vars/cleanup-todo
 (rum/defcs ^:large-vars/cleanup-todo
   export-blocks < rum/static
   export-blocks < rum/static
   (rum/local false ::copied?)
   (rum/local false ::copied?)
@@ -222,19 +228,21 @@
   (rum/local nil ::text-other-options)
   (rum/local nil ::text-other-options)
   (rum/local nil ::content)
   (rum/local nil ::content)
   {:will-mount (fn [state]
   {:will-mount (fn [state]
-                 (reset! *export-block-type (if (:whiteboard? (last (:rum/args state))) :png :text))
-                 (if (= @*export-block-type :png)
-                   (do (reset! (::content state) nil)
-                       (get-image-blob (first (:rum/args state))
-                                       (merge (second (:rum/args state)) {:transparent-bg? false})
-                                       (fn [blob] (reset! (::content state) blob))))
-                   (reset! (::content state) (export-helper (first (:rum/args state)))))
-                 (reset! (::text-remove-options state) (set (state/get-export-block-text-remove-options)))
-                 (reset! (::text-indent-style state) (state/get-export-block-text-indent-style))
-                 (reset! (::text-other-options state) (state/get-export-block-text-other-options))
-                 state)}
-  [state root-block-uuids-or-page-uuid {:keys [whiteboard? export-type] :as options}]
-  (let [tp @*export-block-type
+                 (let [top-level-uuids (get-top-level-uuids (first (:rum/args state)))]
+                   (reset! *export-block-type (if (:whiteboard? (last (:rum/args state))) :png :text))
+                   (if (= @*export-block-type :png)
+                     (do (reset! (::content state) nil)
+                         (get-image-blob top-level-uuids
+                                         (merge (second (:rum/args state)) {:transparent-bg? false})
+                                         (fn [blob] (reset! (::content state) blob))))
+                     (reset! (::content state) (export-helper top-level-uuids)))
+                   (reset! (::text-remove-options state) (set (state/get-export-block-text-remove-options)))
+                   (reset! (::text-indent-style state) (state/get-export-block-text-indent-style))
+                   (reset! (::text-other-options state) (state/get-export-block-text-other-options))
+                   (assoc state ::top-level-uuids top-level-uuids)))}
+  [state _selection-ids {:keys [whiteboard? export-type] :as options}]
+  (let [top-level-uuids (::top-level-uuids state)
+        tp @*export-block-type
         *text-other-options (::text-other-options state)
         *text-other-options (::text-other-options state)
         *text-remove-options (::text-remove-options state)
         *text-remove-options (::text-remove-options state)
         *text-indent-style (::text-indent-style state)
         *text-indent-style (::text-indent-style state)
@@ -248,30 +256,29 @@
          (ui/button "Text"
          (ui/button "Text"
                     :class "mr-4 w-20"
                     :class "mr-4 w-20"
                     :on-click #(do (reset! *export-block-type :text)
                     :on-click #(do (reset! *export-block-type :text)
-                                   (reset! *content (export-helper root-block-uuids-or-page-uuid))))
+                                   (reset! *content (export-helper top-level-uuids))))
          (ui/button "OPML"
          (ui/button "OPML"
                     :class "mr-4 w-20"
                     :class "mr-4 w-20"
                     :on-click #(do (reset! *export-block-type :opml)
                     :on-click #(do (reset! *export-block-type :opml)
-                                   (reset! *content (export-helper root-block-uuids-or-page-uuid))))
+                                   (reset! *content (export-helper top-level-uuids))))
          (ui/button "HTML"
          (ui/button "HTML"
                     :class "mr-4 w-20"
                     :class "mr-4 w-20"
                     :on-click #(do (reset! *export-block-type :html)
                     :on-click #(do (reset! *export-block-type :html)
-                                   (reset! *content (export-helper root-block-uuids-or-page-uuid))))
-         (when-not (seq? root-block-uuids-or-page-uuid)
+                                   (reset! *content (export-helper top-level-uuids))))
+         (when-not (seq? top-level-uuids)
            (ui/button "PNG"
            (ui/button "PNG"
                       :class "mr-4 w-20"
                       :class "mr-4 w-20"
                       :on-click #(do (reset! *export-block-type :png)
                       :on-click #(do (reset! *export-block-type :png)
                                      (reset! *content nil)
                                      (reset! *content nil)
-                                     (get-image-blob root-block-uuids-or-page-uuid (merge options {:transparent-bg? false}) (fn [blob] (reset! *content blob))))))
+                                     (get-image-blob top-level-uuids (merge options {:transparent-bg? false}) (fn [blob] (reset! *content blob))))))
          (when (config/db-based-graph?)
          (when (config/db-based-graph?)
            (ui/button "EDN"
            (ui/button "EDN"
                       :class "w-20"
                       :class "w-20"
                       :on-click #(do (reset! *export-block-type :edn)
                       :on-click #(do (reset! *export-block-type :edn)
-                                     (p/let [result (<export-edn-helper root-block-uuids-or-page-uuid export-type)
+                                     (p/let [result (<export-edn-helper top-level-uuids export-type)
                                              pull-data (with-out-str (pprint/pprint result))]
                                              pull-data (with-out-str (pprint/pprint result))]
                                        (when-not (= :export-edn-error result)
                                        (when-not (= :export-edn-error result)
                                          (reset! *content pull-data))))))])
                                          (reset! *content pull-data))))))])
-
       (if (= :png tp)
       (if (= :png tp)
         [:div.flex.items-center.justify-center.relative
         [:div.flex.items-center.justify-center.relative
          (when (not @*content) [:div.absolute (ui/loading "")])
          (when (not @*content) [:div.absolute (ui/loading "")])
@@ -285,7 +292,7 @@
          (ui/checkbox {:class "mr-2 ml-4"
          (ui/checkbox {:class "mr-2 ml-4"
                        :on-change (fn [e]
                        :on-change (fn [e]
                                     (reset! *content nil)
                                     (reset! *content nil)
-                                    (get-image-blob root-block-uuids-or-page-uuid (merge options {:transparent-bg? e.currentTarget.checked}) (fn [blob] (reset! *content blob))))})]
+                                    (get-image-blob top-level-uuids (merge options {:transparent-bg? e.currentTarget.checked}) (fn [blob] (reset! *content blob))))})]
         (let [options (->> text-indent-style-options
         (let [options (->> text-indent-style-options
                            (mapv (fn [opt]
                            (mapv (fn [opt]
                                    (if (= @*text-indent-style (:label opt))
                                    (if (= @*text-indent-style (:label opt))
@@ -301,7 +308,7 @@
                                 (let [value (util/evalue e)]
                                 (let [value (util/evalue e)]
                                   (state/set-export-block-text-indent-style! value)
                                   (state/set-export-block-text-indent-style! value)
                                   (reset! *text-indent-style value)
                                   (reset! *text-indent-style value)
-                                  (reset! *content (export-helper root-block-uuids-or-page-uuid))))}
+                                  (reset! *content (export-helper top-level-uuids))))}
                   (for [{:keys [label value selected]} options]
                   (for [{:keys [label value selected]} options]
                     [:option (cond->
                     [:option (cond->
                               {:key label
                               {:key label
@@ -316,7 +323,7 @@
                           :on-change (fn [e]
                           :on-change (fn [e]
                                        (state/update-export-block-text-remove-options! e :page-ref)
                                        (state/update-export-block-text-remove-options! e :page-ref)
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
-                                       (reset! *content (export-helper root-block-uuids-or-page-uuid)))})
+                                       (reset! *content (export-helper top-level-uuids)))})
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
              "[[text]] -> text"]
              "[[text]] -> text"]
 
 
@@ -326,7 +333,7 @@
                           :on-change (fn [e]
                           :on-change (fn [e]
                                        (state/update-export-block-text-remove-options! e :emphasis)
                                        (state/update-export-block-text-remove-options! e :emphasis)
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
-                                       (reset! *content (export-helper root-block-uuids-or-page-uuid)))})
+                                       (reset! *content (export-helper top-level-uuids)))})
 
 
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
              "remove emphasis"]
              "remove emphasis"]
@@ -337,7 +344,7 @@
                           :on-change (fn [e]
                           :on-change (fn [e]
                                        (state/update-export-block-text-remove-options! e :tag)
                                        (state/update-export-block-text-remove-options! e :tag)
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
-                                       (reset! *content (export-helper root-block-uuids-or-page-uuid)))})
+                                       (reset! *content (export-helper top-level-uuids)))})
 
 
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
             [:div {:style {:visibility (if (#{:text :html :opml} tp) "visible" "hidden")}}
              "remove #tags"]]
              "remove #tags"]]
@@ -350,7 +357,7 @@
                                        (state/update-export-block-text-other-options!
                                        (state/update-export-block-text-other-options!
                                         :newline-after-block (boolean (util/echecked? e)))
                                         :newline-after-block (boolean (util/echecked? e)))
                                        (reset! *text-other-options (state/get-export-block-text-other-options))
                                        (reset! *text-other-options (state/get-export-block-text-other-options))
-                                       (reset! *content (export-helper root-block-uuids-or-page-uuid)))})
+                                       (reset! *content (export-helper top-level-uuids)))})
             [:div {:style {:visibility (if (#{:text} tp) "visible" "hidden")}}
             [:div {:style {:visibility (if (#{:text} tp) "visible" "hidden")}}
              "newline after block"]
              "newline after block"]
 
 
@@ -360,7 +367,7 @@
                           :on-change (fn [e]
                           :on-change (fn [e]
                                        (state/update-export-block-text-remove-options! e :property)
                                        (state/update-export-block-text-remove-options! e :property)
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
                                        (reset! *text-remove-options (state/get-export-block-text-remove-options))
-                                       (reset! *content (export-helper root-block-uuids-or-page-uuid)))})
+                                       (reset! *content (export-helper top-level-uuids)))})
             [:div {:style {:visibility (if (#{:text} tp) "visible" "hidden")}}
             [:div {:style {:visibility (if (#{:text} tp) "visible" "hidden")}}
              "remove properties"]]
              "remove properties"]]
 
 
@@ -375,7 +382,7 @@
                                  level (if (= "all" value) :all (util/safe-parse-int value))]
                                  level (if (= "all" value) :all (util/safe-parse-int value))]
                              (state/update-export-block-text-other-options! :keep-only-level<=N level)
                              (state/update-export-block-text-other-options! :keep-only-level<=N level)
                              (reset! *text-other-options (state/get-export-block-text-other-options))
                              (reset! *text-other-options (state/get-export-block-text-other-options))
-                             (reset! *content (export-helper root-block-uuids-or-page-uuid))))}
+                             (reset! *content (export-helper top-level-uuids))))}
              (for [n (cons "all" (range 1 10))]
              (for [n (cons "all" (range 1 10))]
                [:option {:key n :value n} n])]]]))
                [:option {:key n :value n} n])]]]))
 
 
@@ -389,8 +396,8 @@
                                   (util/copy-to-clipboard! @*content :html (when (= tp :html) @*content)))
                                   (util/copy-to-clipboard! @*content :html (when (= tp :html) @*content)))
                                 (reset! *copied? true)))
                                 (reset! *copied? true)))
          (ui/button (t :export-save-to-file)
          (ui/button (t :export-save-to-file)
-                    :on-click #(let [file-name (if (uuid? root-block-uuids-or-page-uuid)
-                                                 (-> (db/get-page root-block-uuids-or-page-uuid)
+                    :on-click #(let [file-name (if (uuid? top-level-uuids)
+                                                 (-> (db/get-page top-level-uuids)
                                                      (util/get-page-title))
                                                      (util/get-page-title))
                                                  (t/now))]
                                                  (t/now))]
                                  (utils/saveToFile (js/Blob. [@*content]) (str "logseq_" file-name) (if (= tp :text) "txt" (name tp)))))])]]))
                                  (utils/saveToFile (js/Blob. [@*content]) (str "logseq_" file-name) (if (= tp :text) "txt" (name tp)))))])]]))

+ 4 - 4
src/main/frontend/components/file_based/hierarchy.cljs

@@ -4,11 +4,11 @@
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
             [frontend.state :as state]
             [frontend.state :as state]
-            [logseq.graph-parser.text :as text]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
+            [frontend.util :as util]
+            [logseq.graph-parser.text :as text]
             [medley.core :as medley]
             [medley.core :as medley]
-            [rum.core :as rum]
-            [frontend.util :as util]))
+            [rum.core :as rum]))
 
 
 (defn- get-relation
 (defn- get-relation
   "Get all parent pages along the namespace hierarchy path.
   "Get all parent pages along the namespace hierarchy path.
@@ -49,7 +49,7 @@
   [page]
   [page]
   (let [{:keys [namespaces]} (get-relation page)]
   (let [{:keys [namespaces]} (get-relation page)]
     (when (seq namespaces)
     (when (seq namespaces)
-      [:div.page-hierarchy.mt-6
+      [:div.page-hierarchy
        (ui/foldable
        (ui/foldable
         [:h2.font-bold.opacity-30 "Hierarchy"]
         [:h2.font-bold.opacity-30 "Hierarchy"]
         [:ul.namespaces {:style {:margin "12px 24px"}}
         [:ul.namespaces {:style {:margin "12px 24px"}}

+ 4 - 4
src/main/frontend/components/file_based/query.cljs

@@ -1,6 +1,6 @@
 (ns frontend.components.file-based.query
 (ns frontend.components.file-based.query
-  (:require [frontend.components.query.result :as query-result]
-            [frontend.components.file-based.query-table :as query-table]
+  (:require [frontend.components.file-based.query-table :as query-table]
+            [frontend.components.query.result :as query-result]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -31,7 +31,7 @@
 (rum/defc custom-query-header
 (rum/defc custom-query-header
   [{:keys [dsl-query?] :as config}
   [{:keys [dsl-query?] :as config}
    {:keys [title query] :as q}
    {:keys [title query] :as q}
-   {:keys [collapsed? result table? current-block view-f page-list? query-error-atom]}]
+   {:keys [collapsed? *result result table? current-block view-f page-list? query-error-atom]}]
   (let [dsl-page-query? (and dsl-query?
   (let [dsl-page-query? (and dsl-query?
                              (false? (:blocks? (query-dsl/parse-query query))))
                              (false? (:blocks? (query-dsl/parse-query query))))
         full-text-search? (and dsl-query?
         full-text-search? (and dsl-query?
@@ -82,4 +82,4 @@
            (query-refresh-button query-time {:full-text-search? full-text-search?
            (query-refresh-button query-time {:full-text-search? full-text-search?
                                              :on-pointer-down (fn [e]
                                              :on-pointer-down (fn [e]
                                                                 (util/stop e)
                                                                 (util/stop e)
-                                                                (query-result/trigger-custom-query! config q query-error-atom (fn [])))}))]])]))
+                                                                (query-result/run-custom-query config q *result query-error-atom))}))]])]))

+ 3 - 3
src/main/frontend/components/header.cljs

@@ -54,9 +54,9 @@
   {:will-mount (fn [state]
   {:will-mount (fn [state]
                  (reset!
                  (reset!
                   (::online-users-canceler state)
                   (::online-users-canceler state)
-                  (c.m/run-task
-                   (m/reduce (fn [_ v] (reset! (::online-users state) v)) rtc-flows/rtc-online-users-flow)
-                   :fetch-online-users :succ (constantly nil)))
+                  (c.m/run-task :fetch-online-users
+                    (m/reduce (fn [_ v] (reset! (::online-users state) v)) rtc-flows/rtc-online-users-flow)
+                    :succ (constantly nil)))
                  state)
                  state)
    :will-unmount (fn [state]
    :will-unmount (fn [state]
                    (when @(::online-users-canceler state) (@(::online-users-canceler state)))
                    (when @(::online-users-canceler state) (@(::online-users-canceler state)))

+ 0 - 1
src/main/frontend/components/imports.cljs

@@ -45,7 +45,6 @@
   []
   []
   (notification/show! "Import finished!" :success)
   (notification/show! "Import finished!" :success)
   (shui/dialog-close! :import-indicator)
   (shui/dialog-close! :import-indicator)
-  (state/clear-async-query-state!)
   (ui-handler/re-render-root!)
   (ui-handler/re-render-root!)
   (route-handler/redirect-to-home!)
   (route-handler/redirect-to-home!)
   (js/setTimeout ui-handler/re-render-root! 500))
   (js/setTimeout ui-handler/re-render-root! 500))

+ 34 - 39
src/main/frontend/components/journal.cljs

@@ -1,50 +1,45 @@
 (ns frontend.components.journal
 (ns frontend.components.journal
   (:require [frontend.components.page :as page]
   (:require [frontend.components.page :as page]
-            [frontend.db :as db]
+            [frontend.components.views :as views]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
-            [frontend.handler.page :as page-handler]
-            [frontend.mixins :as mixins]
+            [frontend.db.react :as react]
             [frontend.state :as state]
             [frontend.state :as state]
+            [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
-            [goog.functions :refer [debounce]]
+            [goog.dom :as gdom]
+            [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
-(rum/defc journal-cp < rum/reactive
-  [page]
-  [:div.journal-item.content {:key (:db/id page)}
-   (let [repo (state/sub :git/current-repo)]
-     (page/page-cp {:repo repo
-                    :page-name (str (:block/uuid page))}))])
+(rum/defc journal-cp < rum/static
+  [id last?]
+  [:div.journal-item.content
+   (when last?
+     {:class "journal-last-item"})
+   (page/page-cp {:db/id id})])
 
 
-(defn on-scroll
-  [node {:keys [threshold on-load]
-         :or {threshold 500}}]
-  (when (util/bottom-reached? node threshold)
-    (on-load)))
-
-(defn attach-listeners
-  "Attach scroll and resize listeners."
-  [state]
-  (let [node (js/document.getElementById "main-content-container")
-        opts {:on-load page-handler/load-more-journals!}
-        debounced-on-scroll (debounce #(on-scroll node opts) 100)]
-    (mixins/listen state node :scroll debounced-on-scroll)))
-
-(rum/defc journals < rum/reactive
-  (mixins/event-mixin attach-listeners)
-  {:will-unmount (fn [state]
-                   (state/set-journals-length! 3)
-                   state)}
-  [latest-journals]
-  (when (seq latest-journals)
-    [:div#journals
-     (for [journal latest-journals]
-       (rum/with-key
-         (journal-cp journal)
-         (str "journal-" (:db/id journal))))]))
+(defn- sub-journals
+  []
+  (-> (react/q (state/get-current-repo)
+               [:frontend.worker.react/journals]
+               {:async-query-fn (fn []
+                                  (p/let [{:keys [data]} (views/<load-view-data nil {:journals? true})]
+                                    (remove nil? data)))}
+               nil)
+      util/react))
 
 
 (rum/defc all-journals < rum/reactive db-mixins/query
 (rum/defc all-journals < rum/reactive db-mixins/query
   []
   []
-  (let [journals-length (state/sub :journals-length)
-        latest-journals (db/get-latest-journals (state/get-current-repo) journals-length)]
-    (journals latest-journals)))
+  (let [data (sub-journals)]
+    (when (seq data)
+      [:div#journals
+       (ui/virtualized-list
+        {:custom-scroll-parent (gdom/getElement "main-content-container")
+         :increase-viewport-by {:top 300 :bottom 300}
+         :compute-item-key (fn [idx]
+                             (let [id (util/nth-safe data idx)]
+                               (str "journal-" id)))
+         :total-count (count data)
+         :item-content (fn [idx]
+                         (let [id (util/nth-safe data idx)
+                               last? (= (inc idx) (count data))]
+                           (journal-cp id last?)))})])))

+ 3 - 3
src/main/frontend/components/journal.css

@@ -11,9 +11,9 @@
     &:first-child {
     &:first-child {
       @apply pt-0 min-h-[500px];
       @apply pt-0 min-h-[500px];
     }
     }
+  }
 
 
-    &:last-child {
-      @apply border-none;
-    }
+  .journal-last-item {
+    @apply border-none;
   }
   }
 }
 }

+ 31 - 20
src/main/frontend/components/lazy_editor.cljs

@@ -1,12 +1,12 @@
 (ns frontend.components.lazy-editor
 (ns frontend.components.lazy-editor
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
-            [rum.core :as rum]
-            [shadow.lazy :as lazy]
-            [frontend.ui :as ui]
             [frontend.config :as config]
             [frontend.config :as config]
-            [frontend.state :as state]
             [frontend.handler.plugin :refer [hook-extensions-enhancers-by-key]]
             [frontend.handler.plugin :refer [hook-extensions-enhancers-by-key]]
-            [promesa.core :as p]))
+            [frontend.state :as state]
+            [frontend.ui :as ui]
+            [promesa.core :as p]
+            [rum.core :as rum]
+            [shadow.lazy :as lazy]))
 
 
 ;; TODO: Why does shadow fail when code is required
 ;; TODO: Why does shadow fail when code is required
 #_:clj-kondo/ignore
 #_:clj-kondo/ignore
@@ -14,28 +14,39 @@
 
 
 (defonce loaded? (atom false))
 (defonce loaded? (atom false))
 
 
+(rum/defc editor-aux
+  [config id attr code theme options codemirror-loaded?]
+  (let [^js state (ui/useInView #js {:rootMargin "0px"})
+        in-view? (.-inView state)
+        placeholder [:div
+                     {:style {:height (min
+                                       (* 23.2 (count (string/split-lines code)))
+                                       600)}}]]
+    [:div {:ref (.-ref state)}
+     (if (and codemirror-loaded? in-view?)
+       (@lazy-editor config id attr code theme options)
+       placeholder)]))
+
 (rum/defc editor <
 (rum/defc editor <
   rum/reactive
   rum/reactive
   {:will-mount
   {:will-mount
    (fn [state]
    (fn [state]
-     (lazy/load lazy-editor
-                (fn []
-                  (if-not @loaded?
-                    (p/finally
-                     (p/all (when-let [enhancers (and config/lsp-enabled?
-                                                      (seq (hook-extensions-enhancers-by-key :codemirror)))]
-                              (for [{f :enhancer} enhancers]
-                                (when (fn? f) (f (. js/window -CodeMirror))))))
-                     (fn []
-                       (-> (p/delay 200)
-                           (p/then #(reset! loaded? true)))))
-                    (reset! loaded? true))))
+     (when-not @loaded?
+       (lazy/load lazy-editor
+                  (fn []
+                    (if-not @loaded?
+                      (p/finally
+                        (p/all (when-let [enhancers (and config/lsp-enabled?
+                                                         (seq (hook-extensions-enhancers-by-key :codemirror)))]
+                                 (for [{f :enhancer} enhancers]
+                                   (when (fn? f) (f (. js/window -CodeMirror))))))
+                        (fn []
+                          (reset! loaded? true)))
+                      (reset! loaded? true)))))
      state)}
      state)}
   [config id attr code options]
   [config id attr code options]
   (let [loaded?' (rum/react loaded?)
   (let [loaded?' (rum/react loaded?)
         theme   (state/sub :ui/theme)
         theme   (state/sub :ui/theme)
         code    (or code "")
         code    (or code "")
         code    (string/replace-first code #"\n$" "")]      ;; See-also: #3410
         code    (string/replace-first code #"\n$" "")]      ;; See-also: #3410
-    (if loaded?'
-      (@lazy-editor config id attr code theme options)
-      (ui/loading "CodeMirror"))))
+    (editor-aux config id attr code theme options loaded?')))

+ 36 - 118
src/main/frontend/components/objects.cljs

@@ -4,34 +4,21 @@
             [frontend.components.views :as views]
             [frontend.components.views :as views]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
-            [frontend.db.async :as db-async]
-            [frontend.db.model :as db-model]
-            [frontend.db.react :as react]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
-            [frontend.modules.outliner.op :as outliner-op]
-            [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
             [frontend.state :as state]
-            [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.outliner.property :as outliner-property]
             [logseq.outliner.property :as outliner-property]
-            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
-(defn- get-class-objects
-  [class]
-  (->> (db-model/get-class-objects (state/get-current-repo) (:db/id class))
-       (map (fn [row] (assoc row :id (:db/id row))))))
-
 (defn- add-new-class-object!
 (defn- add-new-class-object!
-  [class set-data! properties]
+  [class properties]
   (p/let [block (editor-handler/api-insert-new-block! ""
   (p/let [block (editor-handler/api-insert-new-block! ""
                                                       {:page (:block/uuid class)
                                                       {:page (:block/uuid class)
                                                        :properties (merge properties {:block/tags (:db/id class)})
                                                        :properties (merge properties {:block/tags (:db/id class)})
-                                                       :edit-block? false})
-          _ (set-data! (get-class-objects class))]
+                                                       :edit-block? false})]
     (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 {:container-id :unknown-container})
     (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 {:container-id :unknown-container})
     block))
     block))
 
 
@@ -47,13 +34,10 @@
    :disable-hide? true})
    :disable-hide? true})
 
 
 (rum/defc class-objects-inner < rum/static
 (rum/defc class-objects-inner < rum/static
-  [config class objects properties]
-  (let [[loading? set-loading?] (rum/use-state nil)
-        [data set-data!] (rum/use-state objects)
-        ;; Properties can be nil for published private graphs
+  [config class properties]
+  (let [;; Properties can be nil for published private graphs
         properties' (remove nil? properties)
         properties' (remove nil? properties)
-        columns* (views/build-columns config properties' {:add-tags-column? (or (= (:db/ident class) :logseq.class/Root)
-                                                                                (> (count (distinct (mapcat :block/tags objects))) 1))})
+        columns* (views/build-columns config properties' {:add-tags-column? true})
         columns (cond
         columns (cond
                   (= (:db/ident class) :logseq.class/Pdf-annotation)
                   (= (:db/ident class) :logseq.class/Pdf-annotation)
                   (remove #(contains? #{:logseq.property/ls-type} (:id %)) columns*)
                   (remove #(contains? #{:logseq.property/ls-type} (:id %)) columns*)
@@ -67,57 +51,31 @@
                     (concat before-cols [(build-asset-file-column config)] after-cols))
                     (concat before-cols [(build-asset-file-column config)] after-cols))
                   columns)]
                   columns)]
 
 
-    (hooks/use-effect!
-     (fn []
-       (when (nil? loading?)
-         (set-loading? true)
-         (p/let [_result (db-async/<get-tag-objects (state/get-current-repo) (:db/id class))]
-           (react/refresh! (state/get-current-repo)
-                           [[:frontend.worker.react/objects (:db/id class)]])
-           (set-data! (get-class-objects class))
-           (set-loading? false))))
-     [])
-
     (views/view {:config config
     (views/view {:config config
-                 :data data
-                 :set-data! set-data!
                  :view-parent class
                  :view-parent class
                  :view-feature-type :class-objects
                  :view-feature-type :class-objects
                  :columns columns
                  :columns columns
-                 :add-new-object! (fn [{:keys [properties]}]
-                                    (if (= :logseq.class/Asset (:db/ident class))
-                                      (shui/dialog-open!
-                                       (fn []
-                                         [:div.flex.flex-col.gap-2
-                                          [:div.font-medium "Add assets"]
-                                          (filepicker/picker
-                                           {:on-change (fn [_e files]
-                                                         (p/do!
-                                                          (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true)
-                                                          (set-data! (get-class-objects class))
-                                                          (shui/dialog-close!)))})]))
-                                      (add-new-class-object! class set-data! properties)))
+                 :add-new-object! (fn [_view table {:keys [properties]}]
+                                    (let [set-data! (get-in table [:data-fns :set-data!])
+                                          full-data (:full-data table)]
+                                      (if (= :logseq.class/Asset (:db/ident class))
+                                        (shui/dialog-open!
+                                         (fn []
+                                           [:div.flex.flex-col.gap-2
+                                            [:div.font-medium "Add assets"]
+                                            (filepicker/picker
+                                             {:on-change (fn [_e files]
+                                                           (p/let [entities (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true)]
+                                                             (shui/dialog-close!)
+                                                             (when (seq entities)
+                                                               (set-data! (concat full-data (map :db/id entities))))))})]))
+                                        (p/let [block (add-new-class-object! class properties)]
+                                          (set-data! (conj (vec full-data) (:db/id block)))))))
                  :show-add-property? true
                  :show-add-property? true
                  :show-items-count? true
                  :show-items-count? true
                  :add-property! (fn []
                  :add-property! (fn []
                                   (state/pub-event! [:editor/new-property {:block class
                                   (state/pub-event! [:editor/new-property {:block class
-                                                                           :class-schema? true}]))
-                 :on-delete-rows (fn [table selected-rows]
-                                     ;; Built-in objects must not be deleted e.g. Tag, Property and Root
-                                   (let [pages (->> selected-rows (filter entity-util/page?) (remove :logseq.property/built-in?))
-                                         blocks (->> selected-rows (remove entity-util/page?) (remove :logseq.property/built-in?))]
-                                     (p/do!
-                                      (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                        (f {}))
-                                      (ui-outliner-tx/transact!
-                                       {:outliner-op :delete-blocks}
-                                       (when (seq blocks)
-                                         (outliner-op/delete-blocks! blocks nil))
-                                       (let [page-ids (map :db/id pages)
-                                             tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)]
-                                         (when (seq tx-data)
-                                           (outliner-op/transact! tx-data {:outliner-op :save-block}))))
-                                      (set-data! (get-class-objects class)))))})))
+                                                                           :class-schema? true}]))})))
 
 
 (rum/defcs class-objects < rum/reactive db-mixins/query mixins/container-id
 (rum/defcs class-objects < rum/reactive db-mixins/query mixins/container-id
   [state class {:keys [current-page? sidebar?]}]
   [state class {:keys [current-page? sidebar?]}]
@@ -126,19 +84,12 @@
           config {:container-id (:container-id state)
           config {:container-id (:container-id state)
                   :current-page? current-page?
                   :current-page? current-page?
                   :sidebar? sidebar?}
                   :sidebar? sidebar?}
-          properties (outliner-property/get-class-properties class)
-          repo (state/get-current-repo)
-          objects (->> (db-model/sub-class-objects repo (:db/id class))
-                       (map (fn [row] (assoc row :id (:db/id row)))))]
+          properties (outliner-property/get-class-properties class)]
       [:div.ml-1
       [:div.ml-1
-       (class-objects-inner config class objects properties)])))
-
-(defn- get-property-related-objects [repo property]
-  (->> (db-model/get-property-related-objects repo (:db/id property))
-       (map (fn [row] (assoc row :id (:db/id row))))))
+       (class-objects-inner config class properties)])))
 
 
 (defn- add-new-property-object!
 (defn- add-new-property-object!
-  [property set-data! properties]
+  [property properties]
   (p/let [default-value (if (= :checkbox (:logseq.property/type property))
   (p/let [default-value (if (= :checkbox (:logseq.property/type property))
                           false
                           false
                           (:db/id (db/entity :logseq.property/empty-placeholder)))
                           (:db/id (db/entity :logseq.property/empty-placeholder)))
@@ -147,55 +98,24 @@
                                                        :properties (merge
                                                        :properties (merge
                                                                     {(:db/ident property) default-value}
                                                                     {(:db/ident property) default-value}
                                                                     properties)
                                                                     properties)
-                                                       :edit-block? false})
-          _ (set-data! (get-property-related-objects (state/get-current-repo) property))]
+                                                       :edit-block? false})]
     (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 {:container-id :unknown-container})
     (editor-handler/edit-block! (db/entity [:block/uuid (:block/uuid block)]) 0 {:container-id :unknown-container})
     block))
     block))
 
 
 (rum/defc property-related-objects-inner < rum/static
 (rum/defc property-related-objects-inner < rum/static
-  [config property objects properties]
-  (let [[loading? set-loading?] (rum/use-state nil)
-        [data set-data!] (rum/use-state objects)
-        columns (views/build-columns config properties)]
-
-    (hooks/use-effect!
-     (fn []
-       (when (nil? loading?)
-         (set-loading? true)
-         (p/let [result (db-async/<get-property-objects (state/get-current-repo) (:db/ident property))]
-           (set-data! (mapv (fn [m]
-                              (let [e (db/entity (:db/id m))]
-                                (assoc e :id (:db/id m)))) result))
-           (set-loading? false))))
-     [])
-
+  [config property properties]
+  (let [columns (views/build-columns config properties)]
     (views/view {:config config
     (views/view {:config config
-                 :data data
                  :view-parent property
                  :view-parent property
                  :view-feature-type :property-objects
                  :view-feature-type :property-objects
-                 :set-data! set-data!
                  :columns columns
                  :columns columns
-                 :add-new-object! (fn [{:keys [properties]}]
-                                    (add-new-property-object! property set-data! properties))
+                 :add-new-object! (fn [_view table {:keys [properties]}]
+                                    (p/let [set-data! (get-in table [:data-fns :set-data!])
+                                            full-data (:full-data table)
+                                            block (add-new-property-object! property properties)]
+                                      (set-data! (conj (vec full-data) (:db/id block)))))
                  ;; TODO: Add support for adding column
                  ;; TODO: Add support for adding column
-                 :show-add-property? false
-                 ;; Relationships with built-in properties must not be deleted e.g. built-in? or parent
-                 :on-delete-rows (when-not (:logseq.property/built-in? property)
-                                   (fn [table selected-rows]
-                                     (let [pages (->> selected-rows (filter entity-util/page?) (remove :logseq.property/built-in?))
-                                           blocks (->> selected-rows (remove entity-util/page?) (remove :logseq.property/built-in?))]
-                                       (p/do!
-                                        (set-data! (get-property-related-objects (state/get-current-repo) property))
-                                        (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                          (f {}))
-                                        (ui-outliner-tx/transact!
-                                         {:outliner-op :delete-blocks}
-                                         (when (seq blocks)
-                                           (outliner-op/delete-blocks! blocks nil))
-                                         (let [page-ids (map :db/id pages)
-                                               tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)]
-                                           (when (seq tx-data)
-                                             (outliner-op/transact! tx-data {:outliner-op :save-block}))))))))})))
+                 :show-add-property? false})))
 
 
 ;; Show all nodes containing the given property
 ;; Show all nodes containing the given property
 (rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id
 (rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id
@@ -205,8 +125,6 @@
           config {:container-id (:container-id state)
           config {:container-id (:container-id state)
                   :current-page? current-page?}
                   :current-page? current-page?}
           ;; Show tags to help differentiate property rows
           ;; Show tags to help differentiate property rows
-          properties [property' (db/entity :block/tags)]
-          repo (state/get-current-repo)
-          objects (get-property-related-objects repo property)]
+          properties [property' (db/entity :block/tags)]]
       [:div.ml-1
       [:div.ml-1
-       (property-related-objects-inner config property' objects properties)])))
+       (property-related-objects-inner config property' properties)])))

+ 186 - 179
src/main/frontend/components/page.cljs

@@ -73,12 +73,6 @@
            str)))
            str)))
   (def get-block-uuid-by-block-route-name (constantly nil)))
   (def get-block-uuid-by-block-route-name (constantly nil)))
 
 
-(defn- get-block
-  [page-name-or-uuid]
-  (when page-name-or-uuid
-    (when-let [block (model/get-page page-name-or-uuid)]
-      (model/sub-block (:db/id block)))))
-
 (defn- open-root-block!
 (defn- open-root-block!
   [state]
   [state]
   (let [[_ block _ sidebar? preview?] (:rum/args state)]
   (let [[_ block _ sidebar? preview?] (:rum/args state)]
@@ -94,7 +88,7 @@
   {:did-mount (fn [state]
   {:did-mount (fn [state]
                 (open-root-block! state)
                 (open-root-block! state)
                 state)}
                 state)}
-  [page-e blocks config sidebar? whiteboard? _block-uuid]
+  [page-e blocks config sidebar? _preview? _block-uuid]
   (when page-e
   (when page-e
     (let [hiccup (component-block/->hiccup blocks config {})]
     (let [hiccup (component-block/->hiccup blocks config {})]
       [:div.page-blocks-inner {:style {:min-height 29}}
       [:div.page-blocks-inner {:style {:min-height 29}}
@@ -151,7 +145,8 @@
                  ;; The same as .dnd-separator
                  ;; The same as .dnd-separator
                 :border-top (if hover
                 :border-top (if hover
                               "3px solid #ccc"
                               "3px solid #ccc"
-                              nil)}
+                              nil)
+                :margin-left 20}
         :ref *el-ref
         :ref *el-ref
         :tabIndex 0
         :tabIndex 0
         :on-click click-handler-fn
         :on-click click-handler-fn
@@ -164,7 +159,7 @@
          [:span.bullet-container.cursor
          [:span.bullet-container.cursor
           [:span.bullet]]]
           [:span.bullet]]]
 
 
-        [:div.flex.flex-1
+        [:div.flex.flex-1.cursor-text
          {:on-drag-enter #(set-hover! true)
          {:on-drag-enter #(set-hover! true)
           :on-drag-over #(util/stop %)
           :on-drag-over #(util/stop %)
           :on-drop drop-handler-fn
           :on-drop drop-handler-fn
@@ -204,12 +199,10 @@
                                   (date/journal-title->int (date/today))))
                                   (date/journal-title->int (date/today))))
                      (state/pub-event! [:journal/insert-template page-name])))
                      (state/pub-event! [:journal/insert-template page-name])))
                  state)}
                  state)}
-  [state page-e {:keys [sidebar? whiteboard?] :as config}]
-  (when page-e
-    (let [page-name (or (:block/name page-e)
-                        (str (:block/uuid page-e)))
-          block-id (parse-uuid page-name)
-          block (get-block (or (:block/uuid page-e) (:block/name page-e)))
+  [state block* {:keys [sidebar? whiteboard?] :as config}]
+  (when-let [id (:db/id block*)]
+    (let [block (db/sub-block id)
+          block-id (:block/uuid block)
           block? (not (db/page? block))
           block? (not (db/page? block))
           children (:block/_parent block)
           children (:block/_parent block)
           children (cond
           children (cond
@@ -224,13 +217,13 @@
       (cond
       (cond
         (and
         (and
          (not block?)
          (not block?)
-         (empty? children) page-e)
-        (dummy-block page-e)
+         (empty? children) block)
+        (dummy-block block)
 
 
         :else
         :else
         (let [document-mode? (state/sub :document/mode?)
         (let [document-mode? (state/sub :document/mode?)
               hiccup-config (merge
               hiccup-config (merge
-                             {:id (if block? (str block-id) page-name)
+                             {:id (str (:block/uuid block))
                               :db/id (:db/id block)
                               :db/id (:db/id block)
                               :block? block?
                               :block? block?
                               :editor-box editor/box
                               :editor-box editor/box
@@ -245,10 +238,8 @@
                                        (string/blank? (:block/title (or link block'))))))]
                                        (string/blank? (:block/title (or link block'))))))]
             [:div.relative
             [:div.relative
              {:class (when add-button? "show-add-button")}
              {:class (when add-button? "show-add-button")}
-             (page-blocks-inner page-e blocks config sidebar? whiteboard? block-id)
-             (let [args (if block-id
-                          {:block-uuid block-id}
-                          {:page page-name})]
+             (page-blocks-inner block blocks config sidebar? whiteboard? block-id)
+             (let [args {:block-uuid block-id}]
                (add-button args (:container-id config)))]))))))
                (add-button args (:container-id config)))]))))))
 
 
 (rum/defc today-queries < rum/reactive
 (rum/defc today-queries < rum/reactive
@@ -256,7 +247,7 @@
   (when (and today? (not sidebar?))
   (when (and today? (not sidebar?))
     (let [queries (get-in (state/sub-config repo) [:default-queries :journals])]
     (let [queries (get-in (state/sub-config repo) [:default-queries :journals])]
       (when (seq queries)
       (when (seq queries)
-        [:div#today-queries.mt-10
+        [:div#today-queries
          (for [query queries]
          (for [query queries]
            (let [query' (if (config/db-based-graph?)
            (let [query' (if (config/db-based-graph?)
                           (assoc query :collapsed? true)
                           (assoc query :collapsed? true)
@@ -280,7 +271,7 @@
          (set-pages! result)))
          (set-pages! result)))
      [tag])
      [tag])
     (when (seq pages)
     (when (seq pages)
-      [:div.references.page-tags.mt-6.flex-1.flex-row
+      [:div.references.page-tags.flex-1.flex-row
        [:div.content
        [:div.content
         (ui/foldable
         (ui/foldable
          [:h2.font-bold.opacity-50 (util/format "Pages tagged with \"%s\"" tag-title)]
          [:h2.font-bold.opacity-50 (util/format "Pages tagged with \"%s\"" tag-title)]
@@ -452,19 +443,9 @@
 
 
 (rum/defc db-page-title
 (rum/defc db-page-title
   [page whiteboard-page? sidebar? container-id]
   [page whiteboard-page? sidebar? container-id]
-  (let [[with-actions? set-with-actions!] (rum/use-state false)
-        *el (rum/use-ref nil)]
-
-    (hooks/use-effect!
-     (fn []
-       (when (and (not config/publishing?)
-                  (some-> (rum/deref *el) (.closest "#main-content-container")))
-         (set-with-actions! true)))
-     [])
-
+  (let [with-actions? (not config/publishing?)]
     [:div.ls-page-title.flex.flex-1.w-full.content.items-start.title
     [:div.ls-page-title.flex.flex-1.w-full.content.items-start.title
      {:class (when-not whiteboard-page? "title")
      {:class (when-not whiteboard-page? "title")
-      :ref *el
       :on-pointer-down (fn [e]
       :on-pointer-down (fn [e]
                          (when (util/right-click? e)
                          (when (util/right-click? e)
                            (state/set-state! :page-title/context {:page (:block/title page)
                            (state/set-state! :page-title/context {:page (:block/title page)
@@ -495,10 +476,11 @@
   [e *control-show? *all-collapsed?]
   [e *control-show? *all-collapsed?]
   (util/stop e)
   (util/stop e)
   (reset! *control-show? true)
   (reset! *control-show? true)
-  (let [all-collapsed?
-        (->> (editor-handler/all-blocks-with-level {:collapse? true})
-             (filter (fn [b] (editor-handler/collapsable? (:block/uuid b))))
-             (empty?))]
+  (p/let [blocks (editor-handler/<all-blocks-with-level {:collapse? true})
+          all-collapsed?
+          (->> blocks
+               (filter (fn [b] (editor-handler/collapsable? (:block/uuid b))))
+               (empty?))]
     (reset! *all-collapsed? all-collapsed?)))
     (reset! *all-collapsed? all-collapsed?)))
 
 
 (defn- page-mouse-leave
 (defn- page-mouse-leave
@@ -524,7 +506,7 @@
   [state page-name]
   [state page-name]
   (or page-name
   (or page-name
       (get-block-uuid-by-block-route-name state)
       (get-block-uuid-by-block-route-name state)
-    ;; is page name or uuid
+      ;; is page name or uuid
       (get-page-name state)
       (get-page-name state)
       (state/get-current-page)))
       (state/get-current-page)))
 
 
@@ -623,149 +605,149 @@
   (rum/local false ::control-show?)
   (rum/local false ::control-show?)
   (rum/local nil   ::current-page)
   (rum/local nil   ::current-page)
   (rum/local false ::tabs-rendered?)
   (rum/local false ::tabs-rendered?)
-  [state {:keys [repo page-name preview? sidebar? linked-refs? unlinked-refs? config] :as option}]
-  (when-let [path-page-name (get-path-page-name state page-name)]
-    (let [current-repo (state/sub :git/current-repo)
-          *tabs-rendered? (::tabs-rendered? state)
-          repo (or repo current-repo)
-          page-name (util/page-name-sanity-lc path-page-name)
-          page (get-page-entity page-name)
-          block-id (:block/uuid page)
-          block? (some? (:block/page page))
-          class-page? (ldb/class? page)
-          property-page? (ldb/property? page)
-          journal? (db/journal-page? page-name)
-          db-based? (config/db-based-graph? repo)
-          fmt-journal? (boolean (date/journal-title->int page-name))
-          whiteboard? (:whiteboard? option) ;; in a whiteboard portal shape?
-          whiteboard-page? (model/whiteboard-page? page) ;; is this page a whiteboard?
-          route-page-name path-page-name
-          page-name (:block/name page)
-          page-title (:block/title page)
-          title (or page-title page-name)
-          today? (and
-                  journal?
-                  (= page-name (util/page-name-sanity-lc (date/journal-name))))
-          *control-show? (::control-show? state)
-          *all-collapsed? (::all-collapsed? state)
-          block-or-whiteboard? (or block? whiteboard?)
-          home? (= :home (state/get-current-route))
-          show-tabs? (and db-based? (or class-page? (ldb/property? page)))
-          tabs-rendered? (rum/react *tabs-rendered?)]
-      (if page
-        (when (or page-name block-or-whiteboard?)
-          [:div.flex-1.page.relative.cp__page-inner-wrap
-           (merge (if (seq (:block/tags page))
-                    (let [page-names (map :block/title (:block/tags page))]
-                      (when (seq page-names)
-                        {:data-page-tags (text-util/build-data-value page-names)}))
-                    {})
-
-                  {:key path-page-name
-                   :class (util/classnames [{:is-journals (or journal? fmt-journal?)
-                                             :is-node-page (or class-page? property-page?)}])})
-
-           (if (and whiteboard-page? (not sidebar?))
-             [:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive
-             [:div.relative.grid.gap-6.page-inner
-              (when-not (or block? sidebar?)
-                [:div.flex.flex-row.space-between
-                 (when (and (or (mobile-util/native-platform?) (util/mobile?)) (not db-based?))
-                   [:div.flex.flex-row.pr-2
-                    {:style {:margin-left -15}
-                     :on-mouse-over (fn [e]
-                                      (page-mouse-over e *control-show? *all-collapsed?))
-                     :on-mouse-leave (fn [e]
-                                       (page-mouse-leave e *control-show?))}
-                    (page-blocks-collapse-control title *control-show? *all-collapsed?)])
-                 (when (and (not whiteboard?) (ldb/page? page))
-                   (if db-based?
-                     (db-page-title page whiteboard-page? sidebar? (:container-id state))
-                     (page-title-cp page {:journal? journal?
-                                          :fmt-journal? fmt-journal?
-                                          :preview? preview?})))
-                 (lsp-pagebar-slot)])
-
-              (when (and db-based? sidebar?)
-                [:div.-mb-8
-                 (sidebar-page-properties config page)])
-
-              (when (and block? (not sidebar?) (not whiteboard?))
-                (let [config (merge config {:id "block-parent"
-                                            :block? true})]
-                  [:div.mb-4
-                   (component-block/breadcrumb config repo block-id {:level-limit 3})]))
-
-              (when show-tabs?
-                (tabs page {:current-page? option :sidebar? sidebar? :*tabs-rendered? *tabs-rendered?}))
-
-              (when (or (not show-tabs?) tabs-rendered?)
-                [:div.ls-page-blocks
-                 {:style {:margin-left (if whiteboard? 0 -20)}}
-                 (page-blocks-cp page (merge option {:sidebar? sidebar?
-                                                     :container-id (:container-id state)
-                                                     :whiteboard? whiteboard?}))])])
-
-           (when (and (not preview?) (or (not show-tabs?) tabs-rendered?))
-             [:div.ml-1
-              (when today?
-                (today-queries repo today? sidebar?))
-
-              (when today?
-                (scheduled/scheduled-and-deadlines page-name))
-
-              (when (and (not block?) (not db-based?))
-                (tagged-pages repo page page-title))
-
-              (when (and (ldb/page? page) (:logseq.property/_parent page))
-                (class-component/class-children page))
+  [state {:keys [repo page preview? sidebar? linked-refs? unlinked-refs? config] :as option}]
+  (let [current-repo (state/sub :git/current-repo)
+        *tabs-rendered? (::tabs-rendered? state)
+        repo (or repo current-repo)
+        block-id (:block/uuid page)
+        block? (some? (:block/page page))
+        class-page? (ldb/class? page)
+        property-page? (ldb/property? page)
+        title (:block/title page)
+        journal? (db/journal-page? title)
+        db-based? (config/db-based-graph? repo)
+        fmt-journal? (boolean (date/journal-title->int title))
+        whiteboard? (:whiteboard? option) ;; in a whiteboard portal shape?
+        whiteboard-page? (model/whiteboard-page? page) ;; is this page a whiteboard?
+        today? (and
+                journal?
+                (= title (date/journal-name)))
+        *control-show? (::control-show? state)
+        *all-collapsed? (::all-collapsed? state)
+        block-or-whiteboard? (or block? whiteboard?)
+        home? (= :home (state/get-current-route))
+        show-tabs? (and db-based? (or class-page? (ldb/property? page)))
+        tabs-rendered? (rum/react *tabs-rendered?)]
+    (if page
+      (when (or title block-or-whiteboard?)
+        [:div.flex-1.page.relative.cp__page-inner-wrap
+         (merge (if (seq (:block/tags page))
+                  (let [page-names (map :block/title (:block/tags page))]
+                    (when (seq page-names)
+                      {:data-page-tags (text-util/build-data-value page-names)}))
+                  {})
+
+                {:key title
+                 :class (util/classnames [{:is-journals (or journal? fmt-journal?)
+                                           :is-node-page (or class-page? property-page?)}])})
+
+         (if (and whiteboard-page? (not sidebar?))
+           [:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive
+           [:div.relative.grid.gap-8.page-inner
+            (when-not (or block? sidebar?)
+              [:div.flex.flex-row.space-between
+               (when (and (or (mobile-util/native-platform?) (util/mobile?)) (not db-based?))
+                 [:div.flex.flex-row.pr-2
+                  {:style {:margin-left -15}
+                   :on-mouse-over (fn [e]
+                                    (page-mouse-over e *control-show? *all-collapsed?))
+                   :on-mouse-leave (fn [e]
+                                     (page-mouse-leave e *control-show?))}
+                  (page-blocks-collapse-control title *control-show? *all-collapsed?)])
+               (when (and (not whiteboard?) (ldb/page? page))
+                 (if db-based?
+                   (db-page-title page whiteboard-page? sidebar? (:container-id state))
+                   (page-title-cp page {:journal? journal?
+                                        :fmt-journal? fmt-journal?
+                                        :preview? preview?})))
+               (lsp-pagebar-slot)])
+
+            (when (and db-based? sidebar? (ldb/page? page))
+              [:div.-mb-8
+               (sidebar-page-properties config page)])
+
+            (when (and block? (not sidebar?) (not whiteboard?))
+              (let [config (merge config {:id "block-parent"
+                                          :block? true})]
+                [:div.mb-4
+                 (component-block/breadcrumb config repo block-id {:level-limit 3})]))
+
+            (when show-tabs?
+              (tabs page {:current-page? option :sidebar? sidebar? :*tabs-rendered? *tabs-rendered?}))
+
+            (when (or (not show-tabs?) tabs-rendered?)
+              [:div.ls-page-blocks
+               {:style {:margin-left (if whiteboard? 0 -20)}}
+               (page-blocks-cp page (merge option {:sidebar? sidebar?
+                                                   :container-id (:container-id state)
+                                                   :whiteboard? whiteboard?}))])])
+
+         (when (and (not preview?) (or (not show-tabs?) tabs-rendered?))
+           [:div.ml-1.flex.flex-col.gap-4
+            (when today?
+              (today-queries repo today? sidebar?))
+
+            (when today?
+              (scheduled/scheduled-and-deadlines title))
+
+            (when (and (not block?) (not db-based?))
+              (tagged-pages repo page title))
+
+            (when (and (ldb/page? page) (:logseq.property/_parent page))
+              (class-component/class-children page))
 
 
               ;; referenced blocks
               ;; referenced blocks
-              (when-not (or whiteboard? linked-refs? (and block? (not db-based?)))
-                [:div {:key "page-references"}
-                 (rum/with-key
-                   (reference/references page)
-                   (str route-page-name "-refs"))])
-
-              (when-not block-or-whiteboard?
-                (when (and (not journal?) (not db-based?))
-                  (hierarchy/structures (:block/title page))))
-
-              (when-not (or whiteboard? unlinked-refs?
-                            sidebar?
-                            home?
-                            (or class-page? property-page?)
-                            (and block? (not db-based?)))
-                [:div {:key "page-unlinked-references"}
-                 (reference/unlinked-references page)])])])
-        [:div.opacity-75 "Page not found"]))))
-
-(rum/defcs page-aux < rum/reactive db-mixins/query
+            (when-not (or whiteboard? linked-refs? (and block? (not db-based?)))
+              [:div {:key "page-references"}
+               (rum/with-key
+                 (reference/references page {:sidebar? sidebar?})
+                 (str title "-refs"))])
+
+            (when-not block-or-whiteboard?
+              (when (and (not journal?) (not db-based?))
+                (hierarchy/structures (:block/title page))))
+
+            (when-not (or whiteboard? unlinked-refs?
+                          sidebar?
+                          home?
+                          (or class-page? property-page?)
+                          (and block? (not db-based?)))
+              [:div {:key "page-unlinked-references"}
+               (reference/unlinked-references page {:sidebar? sidebar?})])])])
+      [:div.opacity-75 "Page not found"])))
+
+(rum/defcs page-aux < rum/reactive
   {:init (fn [state]
   {:init (fn [state]
-           (let [page-name (:page-name (first (:rum/args state)))
+           (let [page* (first (:rum/args state))
+                 page-name (:page-name page*)
+                 page-id-uuid-or-name (or (:db/id page*) (:block/uuid page*)
+                                          (get-sanity-page-name state page-name))
                  option (last (:rum/args state))
                  option (last (:rum/args state))
                  preview-or-sidebar? (or (:preview? option) (:sidebar? option))
                  preview-or-sidebar? (or (:preview? option) (:sidebar? option))
-                 page-name' (get-sanity-page-name state page-name)
-                 page-uuid? (util/uuid-string? page-name')
+                 page-uuid? (when page-name (util/uuid-string? page-name))
                  *loading? (atom true)
                  *loading? (atom true)
-                 page (db/get-page page-name')]
-             (when page (reset! *loading? false))
-             (p/let [page-block (db-async/<get-block (state/get-current-repo) page-name')]
+                 page (db/get-page page-id-uuid-or-name)
+                 *page (atom page)]
+             (when (:block.temp/fully-loaded? page) (reset! *loading? false))
+             (p/let [page-block (db-async/<get-block (state/get-current-repo) page-id-uuid-or-name)]
                (reset! *loading? false)
                (reset! *loading? false)
+               (reset! *page (db/entity (:db/id page-block)))
                (when page-block
                (when page-block
                  (when-not preview-or-sidebar?
                  (when-not preview-or-sidebar?
-                   (if-let [page-uuid (and (not page-uuid?) (:block/uuid page-block))]
+                   (if-let [page-uuid (and (not (:db/id page*)) (not page-uuid?) (:block/uuid page-block))]
                      (route-handler/redirect-to-page! (str page-uuid) {:push false})
                      (route-handler/redirect-to-page! (str page-uuid) {:push false})
                      (route-handler/update-page-title-and-label! (state/get-route-match))))))
                      (route-handler/update-page-title-and-label! (state/get-route-match))))))
              (assoc state
              (assoc state
-                    ::page-name page-name'
-                    ::loading? *loading?)))
+                    ::loading? *loading?
+                    ::*page *page)))
    :will-unmount (fn [state]
    :will-unmount (fn [state]
                    (state/set-state! :editor/virtualized-scroll-fn nil)
                    (state/set-state! :editor/virtualized-scroll-fn nil)
                    state)}
                    state)}
   [state option]
   [state option]
-  (when-not (rum/react (::loading? state))
-    (page-inner option)))
+  (let [loading? (rum/react (::loading? state))
+        page (rum/react (::*page state))]
+    (when (and page (not loading?))
+      (page-inner (assoc option :page page)))))
 
 
 (rum/defcs page-cp
 (rum/defcs page-cp
   [state option]
   [state option]
@@ -774,7 +756,8 @@
     (str
     (str
      (state/get-current-repo)
      (state/get-current-repo)
      "-"
      "-"
-     (or (:page-name option)
+     (or (:db/id option)
+         (:page-name option)
          (get-page-name state)))))
          (get-page-name state)))))
 
 
 (defonce layout (atom [js/window.innerWidth js/window.innerHeight]))
 (defonce layout (atom [js/window.innerWidth js/window.innerHeight]))
@@ -1096,6 +1079,21 @@
       (filter (fn [node] (some #(re-find % (:label node)) filter-patterns)) nodes))
       (filter (fn [node] (some #(re-find % (:label node)) filter-patterns)) nodes))
     nodes))
     nodes))
 
 
+(rum/defc graph-aux
+  [settings forcesettings theme search-graph-filters]
+  (let [[graph set-graph!] (hooks/use-state nil)]
+    (hooks/use-effect!
+     (fn []
+       (p/let [result (state/<invoke-db-worker :thread-api/build-graph (state/get-current-repo)
+                                               (assoc settings
+                                                      :type :global
+                                                      :theme theme))]
+         (set-graph! result)))
+     [theme settings])
+    (when graph
+      (let [graph' (update graph :nodes #(filter-graph-nodes % search-graph-filters))]
+        (global-graph-inner graph' settings forcesettings theme)))))
+
 (rum/defcs global-graph < rum/reactive
 (rum/defcs global-graph < rum/reactive
   (mixins/event-mixin
   (mixins/event-mixin
    (fn [state]
    (fn [state]
@@ -1113,10 +1111,8 @@
         theme (state/sub :ui/theme)
         theme (state/sub :ui/theme)
         ;; Needed for query to retrigger after reset
         ;; Needed for query to retrigger after reset
         _reset? (rum/react *graph-reset?)
         _reset? (rum/react *graph-reset?)
-        graph (graph-handler/build-global-graph theme settings)
-        search-graph-filters (state/sub :search/graph-filters)
-        graph (update graph :nodes #(filter-graph-nodes % search-graph-filters))]
-    (global-graph-inner graph settings forcesettings theme)))
+        search-graph-filters (state/sub :search/graph-filters)]
+    (graph-aux settings forcesettings theme search-graph-filters)))
 
 
 (rum/defc page-graph-inner < rum/reactive
 (rum/defc page-graph-inner < rum/reactive
   [_page graph dark?]
   [_page graph dark?]
@@ -1140,6 +1136,18 @@
                       (fn [graph]
                       (fn [graph]
                         (graph-register-handlers graph (atom nil) (atom nil) dark?))})]))
                         (graph-register-handlers graph (atom nil) (atom nil) dark?))})]))
 
 
+(rum/defc page-graph-aux
+  [page opts]
+  (let [[graph set-graph!] (hooks/use-state nil)
+        dark? (= (:theme opts) "dark")]
+    (hooks/use-effect!
+     (fn []
+       (p/let [result (state/<invoke-db-worker :thread-api/build-graph (state/get-current-repo) opts)]
+         (set-graph! result)))
+     [opts])
+    (when (seq (:nodes graph))
+      (page-graph-inner page graph dark?))))
+
 (rum/defc page-graph < db-mixins/query rum/reactive
 (rum/defc page-graph < db-mixins/query rum/reactive
   []
   []
   (let [page (or
   (let [page (or
@@ -1147,14 +1155,13 @@
                    (state/sub [:route-match :path-params :name]))
                    (state/sub [:route-match :path-params :name]))
               (date/today))
               (date/today))
         theme (:ui/theme @state/state)
         theme (:ui/theme @state/state)
-        dark? (= theme "dark")
         show-journals-in-page-graph (rum/react *show-journals-in-page-graph?)
         show-journals-in-page-graph (rum/react *show-journals-in-page-graph?)
-        page-entity (db/get-page page)
-        graph (if (ldb/page? page-entity)
-                (graph-handler/build-page-graph page theme show-journals-in-page-graph)
-                (graph-handler/build-block-graph (uuid page) theme))]
-    (when (seq (:nodes graph))
-      (page-graph-inner page graph dark?))))
+        page-entity (db/get-page page)]
+    (page-graph-aux page
+                    {:type (if (ldb/page? page-entity) :page :block)
+                     :block/uuid (:block/uuid page-entity)
+                     :theme theme
+                     :show-journals? show-journals-in-page-graph})))
 
 
 (defn batch-delete-dialog
 (defn batch-delete-dialog
   [pages orphaned-pages? refresh-fn]
   [pages orphaned-pages? refresh-fn]

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

@@ -132,8 +132,8 @@
             {:title   (t :export-page)
             {:title   (t :export-page)
              :options {:on-click #(shui/dialog-open!
              :options {:on-click #(shui/dialog-open!
                                    (fn []
                                    (fn []
-                                     (export/export-blocks (:block/uuid page) {:whiteboard? whiteboard?
-                                                                               :export-type :page}))
+                                     (export/export-blocks [(:block/uuid page)] {:whiteboard? whiteboard?
+                                                                                 :export-type :page}))
                                    {:class "w-auto md:max-w-4xl max-h-[80vh] overflow-y-auto"})}})
                                    {:class "w-auto md:max-w-4xl max-h-[80vh] overflow-y-auto"})}})
 
 
           (when (util/electron?)
           (when (util/electron?)

+ 31 - 4
src/main/frontend/components/profiler.cljs

@@ -1,6 +1,7 @@
 (ns frontend.components.profiler
 (ns frontend.components.profiler
   "Profiler UI"
   "Profiler UI"
-  (:require [fipp.edn :as fipp]
+  (:require [clojure.set :as set]
+            [fipp.edn :as fipp]
             [frontend.handler.profiler :as profiler-handler]
             [frontend.handler.profiler :as profiler-handler]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
@@ -8,13 +9,15 @@
 
 
 (rum/defcs profiler < rum/reactive
 (rum/defcs profiler < rum/reactive
   (rum/local nil ::reports)
   (rum/local nil ::reports)
+  (rum/local nil ::mem-leak-reports)
   (rum/local nil ::register-fn-name)
   (rum/local nil ::register-fn-name)
   [state]
   [state]
   (let [profiling-fns (keys (rum/react profiler-handler/*fn-symbol->origin-fn))
   (let [profiling-fns (keys (rum/react profiler-handler/*fn-symbol->origin-fn))
         *reports (get state ::reports)
         *reports (get state ::reports)
+        *mem-leak-reports (get state ::mem-leak-reports)
         *register-fn-name (get state ::register-fn-name)]
         *register-fn-name (get state ::register-fn-name)]
     [:div
     [:div
-     [:b "Profiling fns:"]
+     [:b "Profiling fns(Only support UI thread now):"]
      [:div.pb-4
      [:div.pb-4
       (for [f-name profiling-fns]
       (for [f-name profiling-fns]
         [:div.flex.flex-row.items-center.gap-2
         [:div.flex.flex-row.items-center.gap-2
@@ -38,7 +41,6 @@
                             (when (= v "input fn name here")
                             (when (= v "input fn name here")
                               (set! (.-value (.-target e)) ""))))
                               (set! (.-value (.-target e)) ""))))
         :placeholder "input fn name here"}]]
         :placeholder "input fn name here"}]]
-     [:hr]
      [:div.flex.gap-2.flex-wrap.items-center.pb-3
      [:div.flex.gap-2.flex-wrap.items-center.pb-3
       (shui/button
       (shui/button
        {:size :sm
        {:size :sm
@@ -57,4 +59,29 @@
            (-> @*reports
            (-> @*reports
                (update :time-sum update-time-sum)
                (update :time-sum update-time-sum)
                (fipp/pprint {:width 20})
                (fipp/pprint {:width 20})
-               with-out-str))]])]))
+               with-out-str))]])
+     [:hr]
+     [:b "Atom/Volatile Mem Leak Detect(Only support UI thread now):"]
+     [:pre "Only check atoms/volatiles with a value type of `coll`.
+The report shows refs with coll-size > 5k and atom's watches-count > 1k.
+`ref` means atom or volatile.
+`ref-hash` means `(hash ref)`."]
+     [:div.flex.flex-row.items-center.gap-2
+      (if (= 2 (count (set/difference #{'cljs.core/reset! 'cljs.core/vreset!} (set profiling-fns))))
+        (shui/button
+         {:on-click (fn []
+                      (profiler-handler/mem-leak-detect))}
+         "Start to detect")
+        (shui/button
+         {:size :sm
+          :on-click (fn [_] (reset! *mem-leak-reports (profiler-handler/mem-leak-report)))}
+         (shui/tabler-icon "refresh") "Refresh reports"))]
+     [:pre.select-text
+      (when @*mem-leak-reports
+        (let [ref-hash->ref (:ref-hash->ref @*mem-leak-reports)]
+          (-> @*mem-leak-reports
+              (dissoc :ref-hash->ref)
+              (assoc :ref-hash->take-3-items (update-vals ref-hash->ref (fn [ref] (take 3 @ref)))
+                     :ref-hash->take-3-watch-keys (update-vals ref-hash->ref (fn [ref] (take 3 (.-watches ^js ref)))))
+              (fipp/pprint {:width 20})
+              with-out-str)))]]))

+ 4 - 20
src/main/frontend/components/property.cljs

@@ -159,7 +159,7 @@
     (hooks/use-effect!
     (hooks/use-effect!
      (fn []
      (fn []
        (p/let [repo (state/get-current-repo)
        (p/let [repo (state/get-current-repo)
-               properties (db-async/<db-based-get-all-properties repo)
+               properties (db-async/db-based-get-all-properties repo)
                classes (->> (db-model/get-all-classes repo)
                classes (->> (db-model/get-all-classes repo)
                             (remove ldb/built-in?))]
                             (remove ldb/built-in?))]
          (set-classes! classes)
          (set-classes! classes)
@@ -294,6 +294,7 @@
                                                          (.focus input)))}
                                                          (.focus input)))}
                                    :align "start"
                                    :align "start"
                                    :as-dropdown? true}))}
                                    :as-dropdown? true}))}
+
    (:block/title property)))
    (:block/title property)))
 
 
 (rum/defc property-key-cp < rum/static
 (rum/defc property-key-cp < rum/static
@@ -570,7 +571,7 @@
                                                  {:db/id (:db/id block)})]
                                                  {:db/id (:db/id block)})]
                                                {:outliner-op :save-block})))})))
                                                {:outliner-op :save-block})))})))
 
 
-(rum/defc properties-section < rum/reactive db-mixins/query
+(rum/defc properties-section < rum/static
   [block properties opts]
   [block properties opts]
   (when (seq properties)
   (when (seq properties)
       ;; Sort properties by :block/order
       ;; Sort properties by :block/order
@@ -580,28 +581,13 @@
                                    (:block/order (db/entity k)))) properties)]
                                    (:block/order (db/entity k)))) properties)]
       (ordered-properties block properties' opts))))
       (ordered-properties block properties' opts))))
 
 
-(defn- async-load-classes!
-  [block]
-  (let [repo (state/get-current-repo)
-        classes (concat (:block/tags block) (outliner-property/get-classes-parents (:block/tags block)))]
-    (doseq [class classes]
-      (db-async/<get-block repo (:db/id class) :children? false))
-    (when (ldb/class? block)
-      (doseq [property (:logseq.property.class/properties block)]
-        (db-async/<get-block repo (:db/id property) :children? false)))
-    classes))
-
 (rum/defcs ^:large-vars/cleanup-todo properties-area < rum/reactive db-mixins/query
 (rum/defcs ^:large-vars/cleanup-todo properties-area < rum/reactive db-mixins/query
   {:init (fn [state]
   {:init (fn [state]
            (let [target-block (first (:rum/args state))
            (let [target-block (first (:rum/args state))
                  block (resolve-linked-block-if-exists target-block)]
                  block (resolve-linked-block-if-exists target-block)]
              (assoc state
              (assoc state
                     ::id (str (random-uuid))
                     ::id (str (random-uuid))
-                    ::block block
-                    ::classes (async-load-classes! block))))
-   :will-remount (fn [state]
-                   (let [block (db/entity (:db/id (::block state)))]
-                     (assoc state ::classes (async-load-classes! block))))}
+                    ::block block)))}
   [state _target-block {:keys [sidebar-properties?] :as opts}]
   [state _target-block {:keys [sidebar-properties?] :as opts}]
   (let [id (::id state)
   (let [id (::id state)
         db-id (:db/id (::block state))
         db-id (:db/id (::block state))
@@ -610,8 +596,6 @@
                                             (and show?
                                             (and show?
                                                  (or (= mode :global)
                                                  (or (= mode :global)
                                                      (and (set? ids) (contains? ids (:block/uuid block))))))
                                                      (and (set? ids) (contains? ids (:block/uuid block))))))
-        _ (doseq [class (::classes state)]
-            (db/sub-block (:db/id class)))
         class? (ldb/class? block)
         class? (ldb/class? block)
         properties (:block/properties block)
         properties (:block/properties block)
         remove-built-in-or-other-position-properties
         remove-built-in-or-other-position-properties

+ 36 - 46
src/main/frontend/components/property/config.cljs

@@ -363,27 +363,19 @@
 
 
 (rum/defc add-existing-values
 (rum/defc add-existing-values
   [property values {:keys [toggle-fn]}]
   [property values {:keys [toggle-fn]}]
-  (let [uuid-values? (every? uuid? values)
-        values' (if uuid-values?
-                  (let [values' (map #(db/entity [:block/uuid %]) values)]
-                    (->> values'
-                         (util/distinct-by db-property/closed-value-content)
-                         (map :block/uuid)))
-                  values)]
-    [:div.flex.flex-col.gap-1.w-64.p-4.overflow-y-auto
-     {:class "max-h-[50dvh]"}
-     [:div "Existing values:"]
-     [:ol
-      (for [value values']
-        [:li (if (uuid? value)
-               (let [result (db/entity [:block/uuid value])]
-                 (db-property/closed-value-content result))
-               (str value))])]
-     (shui/button
-      {:on-click (fn []
-                   (p/let [_ (db-property-handler/add-existing-values-to-closed-values! (:db/id property) values')]
-                     (toggle-fn)))}
-      "Add choices")]))
+  [:div.flex.flex-col.gap-1.w-64.p-4.overflow-y-auto
+   {:class "max-h-[50dvh]"}
+   [:div "Existing values:"]
+   [:ol
+    (for [value values]
+      [:li (:label value)])]
+   (shui/button
+    {:on-click (fn []
+                 (p/let [_ (db-property-handler/add-existing-values-to-closed-values! (:db/id property)
+                                                                                      (map (fn [{:keys [value]}]
+                                                                                             (:block/uuid value)) values))]
+                   (toggle-fn)))}
+    "Add choices")])
 
 
 (rum/defc choices-sub-pane < rum/reactive db-mixins/query
 (rum/defc choices-sub-pane < rum/reactive db-mixins/query
   [property {:keys [disabled?] :as opts}]
   [property {:keys [disabled?] :as opts}]
@@ -431,29 +423,29 @@
         {:icon :plus :title "Add choice"
         {:icon :plus :title "Add choice"
          :item-props {:on-click
          :item-props {:on-click
                       (fn [^js e]
                       (fn [^js e]
-                        (p/let [values (db-async/<get-block-property-values (state/get-current-repo) (:db/ident property))
+                        (p/let [values (db-async/<get-property-values (:db/ident property) {})
                                 existing-values (seq (:property/closed-values property))
                                 existing-values (seq (:property/closed-values property))
-                                values (if (seq existing-values)
-                                         (let [existing-ids (set (map :db/id existing-values))]
-                                           (remove (fn [id] (existing-ids id)) values))
-                                         values)]
-                          (shui/popup-show! (.-target e)
-                                            (fn [{:keys [id]}]
-                                              (let [opts {:toggle-fn (fn [] (shui/popup-hide! id))}
-                                                    values' (->> (if (contains? db-property-type/all-ref-property-types (:logseq.property/type property))
-                                                                   (->> values
-                                                                        (map db/entity)
-                                                                        (remove (fn [e]
-                                                                                  (let [value (db-property/property-value-content e)]
-                                                                                    (and (string? value) (string/blank? value)))))
-                                                                        (map :block/uuid))
-                                                                   (remove string/blank? values))
-                                                                 distinct)]
-                                                (if (seq values')
-                                                  (add-existing-values property values' opts)
-                                                  (choice-base-edit-form property {:create? true}))))
-                                            {:id :ls-base-edit-form
-                                             :align "start"})))}}))]))
+                                values' (if (seq existing-values)
+                                          (let [existing-ids (set (map :db/id existing-values))
+                                                existing-titles (set (map db-property/property-value-content existing-values))]
+                                            (remove (fn [{:keys [label value]}]
+                                                      (or (existing-ids (:db/id value))
+                                                          (existing-titles label)
+                                                          (string/blank? label))) values))
+                                          (remove (fn [{:keys [label _value]}]
+                                                    (string/blank? label))
+                                                  values))]
+                          (p/do!
+                           (when (seq values')
+                             (db-async/<get-blocks (state/get-current-repo) (map (fn [{:keys [value]}] (:db/id value)) values)))
+                           (shui/popup-show! (.-target e)
+                                             (fn [{:keys [id]}]
+                                               (let [opts {:toggle-fn (fn [] (shui/popup-hide! id))}]
+                                                 (if (seq values')
+                                                   (add-existing-values property values' opts)
+                                                   (choice-base-edit-form property {:create? true}))))
+                                             {:id :ls-base-edit-form
+                                              :align "start"}))))}}))]))
 
 
 (rum/defc checkbox-state-mapping
 (rum/defc checkbox-state-mapping
   [choices]
   [choices]
@@ -762,11 +754,9 @@
 (rum/defcs dropdown-editor < rum/reactive db-mixins/query
 (rum/defcs dropdown-editor < rum/reactive db-mixins/query
   {:init (fn [state]
   {:init (fn [state]
            (let [*values (atom :loading)
            (let [*values (atom :loading)
-                 repo (state/get-current-repo)
                  property (first (:rum/args state))
                  property (first (:rum/args state))
                  ident (:db/ident property)]
                  ident (:db/ident property)]
-             (p/let [_ (db-async/<get-block repo (:block/uuid property))
-                     result (db-async/<get-block-property-values repo ident)]
+             (p/let [result (db-async/<get-property-values ident)]
                (reset! *values result))
                (reset! *values result))
              (assoc state ::values *values)))}
              (assoc state ::values *values)))}
   [state property* owner-block opts]
   [state property* owner-block opts]

+ 201 - 199
src/main/frontend/components/property/value.cljs

@@ -4,7 +4,6 @@
             [cljs-time.local :as local]
             [cljs-time.local :as local]
             [clojure.set :as set]
             [clojure.set :as set]
             [clojure.string :as string]
             [clojure.string :as string]
-            [datascript.impl.entity :as de]
             [dommy.core :as d]
             [dommy.core :as d]
             [frontend.components.icon :as icon-component]
             [frontend.components.icon :as icon-component]
             [frontend.components.select :as select]
             [frontend.components.select :as select]
@@ -42,6 +41,10 @@
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
+(defn- entity-map?
+  [m]
+  (and (map? m) (:db/id m)))
+
 (rum/defc property-empty-btn-value
 (rum/defc property-empty-btn-value
   [property & opts]
   [property & opts]
   (let [text (cond
   (let [text (cond
@@ -56,14 +59,15 @@
                    text))))
                    text))))
 
 
 (rum/defc property-empty-text-value
 (rum/defc property-empty-text-value
-  [property {:keys [property-position]}]
-  [:span.inline-flex.items-center.cursor-pointer
+  [property {:keys [property-position table-view?]}]
+  [:span.inline-flex.items-center.cursor-pointer.w-full
    (merge {:class "empty-text-btn" :variant :text})
    (merge {:class "empty-text-btn" :variant :text})
-   (if property-position
-     (if-let [icon (:logseq.property/icon property)]
-       (icon-component/icon icon {:color? true})
-       (ui/icon "line-dashed"))
-     "Empty")])
+   (when-not table-view?
+     (if property-position
+       (if-let [icon (:logseq.property/icon property)]
+         (icon-component/icon icon {:color? true})
+         (ui/icon "line-dashed"))
+       "Empty"))])
 
 
 (defn- get-selected-blocks
 (defn- get-selected-blocks
   []
   []
@@ -235,8 +239,15 @@
 (defn- add-or-remove-property-value
 (defn- add-or-remove-property-value
   [block property value selected? {:keys [refresh-result-f] :as opts}]
   [block property value selected? {:keys [refresh-result-f] :as opts}]
   (let [many? (db-property/many? property)
   (let [many? (db-property/many? property)
-        blocks (get-operating-blocks block)]
+        blocks (get-operating-blocks block)
+        repo (state/get-current-repo)]
     (p/do!
     (p/do!
+     (db-async/<get-block repo (:db/id block) {:children? false})
+     (when (and selected?
+                (= :db.type/ref (:db/valueType property))
+                (number? value)
+                (not (db/entity value)))
+       (db-async/<get-block repo value {:children? false}))
      (if selected?
      (if selected?
        (<add-property! block (:db/ident property) value
        (<add-property! block (:db/ident property) value
                        {:selected? selected?
                        {:selected? selected?
@@ -632,10 +643,10 @@
         tags? (= :block/tags (:db/ident property))
         tags? (= :block/tags (:db/ident property))
         alias? (= :block/alias (:db/ident property))
         alias? (= :block/alias (:db/ident property))
         tags-or-alias? (or tags? alias?)
         tags-or-alias? (or tags? alias?)
-        block (db/entity (:db/id block))
+        block (or (db/entity (:db/id block)) block)
         selected-choices (when block
         selected-choices (when block
                            (when-let [v (get block (:db/ident property))]
                            (when-let [v (get block (:db/ident property))]
-                             (if (every? de/entity? v)
+                             (if (every? entity-map? v)
                                (map :db/id v)
                                (map :db/id v)
                                [(:db/id v)])))
                                [(:db/id v)])))
         parent-property? (= (:db/ident property) :logseq.property/parent)
         parent-property? (= (:db/ident property) :logseq.property/parent)
@@ -644,57 +655,51 @@
         get-all-classes-f (fn []
         get-all-classes-f (fn []
                             (model/get-all-classes repo {:except-root-class? true
                             (model/get-all-classes repo {:except-root-class? true
                                                          :except-private-tags? (not (contains? #{:logseq.property/template-applied-to} (:db/ident property)))}))
                                                          :except-private-tags? (not (contains? #{:logseq.property/template-applied-to} (:db/ident property)))}))
-        nodes
-        (->>
-         (cond
-           parent-property?
-           (let [;; Disallows cyclic hierarchies
-                 exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
-                                 (conj (:block/uuid block))) ; break cycle
-                 options (if (ldb/class? block)
-                           (model/get-all-classes repo)
-                           (when (ldb/internal-page? block)
-                             (cond->>
-                              (->> (model/get-all-pages repo)
-                                   (filter ldb/internal-page?)
-                                   (remove ldb/built-in?)))))
-                 excluded-options (remove (fn [e] (contains? exclude-ids (:block/uuid e))) options)]
-             excluded-options)
-
-           (= property-type :class)
-           (get-all-classes-f)
-
-           (seq classes)
-           (->>
-            (mapcat
-             (fn [class]
-               (model/get-class-objects repo (:db/id class)))
-             classes)
-            distinct)
-
-           :else
-           (if (empty? result)
-             (let [v (get block (:db/ident property))]
-               (remove #(= :logseq.property/empty-placeholder (:db/ident %))
-                       (if (every? de/entity? v) v [v])))
-             (remove (fn [node]
-                       (or (= (:db/id block) (:db/id node))
+        nodes (cond
+                parent-property?
+                (let [;; Disallows cyclic hierarchies
+                      exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
+                                      (conj (:block/uuid block))) ; break cycle
+                      options (if (ldb/class? block)
+                                (model/get-all-classes repo)
+                                result)
+                      excluded-options (remove (fn [e] (contains? exclude-ids (:block/uuid e))) options)]
+                  excluded-options)
+
+                (= property-type :class)
+                (get-all-classes-f)
+
+                (seq classes)
+                (->>
+                 (mapcat
+                  (fn [class]
+                    (model/get-class-objects repo (:db/id class)))
+                  classes)
+                 distinct)
+
+                :else
+                (if (empty? result)
+                  (let [v (get block (:db/ident property))]
+                    (remove #(= :logseq.property/empty-placeholder (:db/ident %))
+                            (if (every? entity-map? v) v [v])))
+                  (remove (fn [node]
+                            (or (= (:db/id block) (:db/id node))
                              ;; A page's alias can't be itself
                              ;; A page's alias can't be itself
-                           (and alias? (= (or (:db/id (:block/page block))
-                                              (:db/id block))
-                                          (:db/id node)))
-                           (cond
-                             (= property-type :class)
-                             (ldb/private-tags (:db/ident node))
-
-                             (and property-type (not= property-type :node))
-                             (if (= property-type :page)
-                               (not (db/page? node))
-                               (not (contains? (ldb/get-entity-types node) property-type)))
-
-                             :else
-                             false)))
-                     result))))
+                                (and alias? (= (or (:db/id (:block/page block))
+                                                   (:db/id block))
+                                               (:db/id node)))
+                                (cond
+                                  (= property-type :class)
+                                  (ldb/private-tags (:db/ident node))
+
+                                  (and property-type (not= property-type :node))
+                                  (if (= property-type :page)
+                                    (not (db/page? node))
+                                    (not (contains? (ldb/get-entity-types node) property-type)))
+
+                                  :else
+                                  false)))
+                          result)))
 
 
         options (map (fn [node]
         options (map (fn [node]
                        (let [id (or (:value node) (:db/id node))
                        (let [id (or (:value node) (:db/id node))
@@ -809,14 +814,27 @@
                            class?
                            class?
                            (conj (frontend.db/entity :logseq.class/Tag)))
                            (conj (frontend.db/entity :logseq.class/Tag)))
         parent-property? (= (:db/ident property) :logseq.property/parent)]
         parent-property? (= (:db/ident property) :logseq.property/parent)]
-    (when (and (not parent-property?) (seq non-root-classes))
-      ;; effect runs once
-      (hooks/use-effect!
-       (fn []
+
+    ;; effect runs once
+    (hooks/use-effect!
+     (fn []
+       (cond
+         (and parent-property? (not (ldb/class? block))
+              (ldb/internal-page? block))
+         (p/let [result (db-async/<get-tag-pages repo (:db/id (db/entity :logseq.class/Page)))
+                 result' (->> result
+                              (remove ldb/built-in?))]
+           (set-result! result'))
+
+         parent-property?
+         nil
+
+         (seq non-root-classes)
          (p/let [result (p/all (map (fn [class] (db-async/<get-tag-objects repo (:db/id class))) non-root-classes))
          (p/let [result (p/all (map (fn [class] (db-async/<get-tag-objects repo (:db/id class))) non-root-classes))
                  result' (distinct (apply concat result))]
                  result' (distinct (apply concat result))]
-           (set-result! result')))
-       []))
+           (set-result! result'))))
+     [])
+
     (select-node property opts' result)))
     (select-node property opts' result)))
 
 
 (rum/defcs select < rum/reactive db-mixins/query
 (rum/defcs select < rum/reactive db-mixins/query
@@ -827,8 +845,7 @@
                                       (p/let [property-ident (if (= :logseq.property/default-value (:db/ident property))
                                       (p/let [property-ident (if (= :logseq.property/default-value (:db/ident property))
                                                                (:db/ident block)
                                                                (:db/ident block)
                                                                (:db/ident property))
                                                                (:db/ident property))
-                                              result (db-async/<get-block-property-values (state/get-current-repo)
-                                                                                          property-ident)]
+                                              result (db-async/<get-property-values property-ident)]
                                         (reset! *values result))))]
                                         (reset! *values result))))]
              (refresh-result-f)
              (refresh-result-f)
              (assoc state
              (assoc state
@@ -839,12 +856,10 @@
    {:keys [*show-new-property-config? exit-edit?] :as opts}]
    {:keys [*show-new-property-config? exit-edit?] :as opts}]
   (let [*values (::values state)
   (let [*values (::values state)
         refresh-result-f (::refresh-result-f state)
         refresh-result-f (::refresh-result-f state)
-        values (rum/react *values)
-        block (db/sub-block (:db/id block))]
+        values (rum/react *values)]
     (when-not (= :loading values)
     (when-not (= :loading values)
       (let [type (:logseq.property/type property)
       (let [type (:logseq.property/type property)
             closed-values? (seq (:property/closed-values property))
             closed-values? (seq (:property/closed-values property))
-            ref-type? (db-property-type/all-ref-property-types type)
             items (if closed-values?
             items (if closed-values?
                     (let [date? (and
                     (let [date? (and
                                  (= (:db/ident property) :logseq.task/recur-unit)
                                  (= (:db/ident property) :logseq.task/recur-unit)
@@ -864,17 +879,9 @@
                                  :label-value value}))
                                  :label-value value}))
                             values))
                             values))
                     (->> values
                     (->> values
-                         (mapcat (fn [value]
-                                   (if (coll? value)
-                                     (map (fn [v] {:value v}) value)
-                                     [{:value value}])))
-                         (map (fn [{:keys [value]}]
-                                (if (and ref-type? (number? value))
-                                  (when-let [e (db/entity value)]
-                                    {:label (db-property/property-value-content e)
-                                     :value value})
-                                  {:label value
-                                   :value value})))
+                         (map (fn [{:keys [value label]}]
+                                {:label label
+                                 :value (:db/id value)}))
                          (distinct)))
                          (distinct)))
             items (->> (if (= :date type)
             items (->> (if (= :date type)
                          (map (fn [m] (let [label (:block/title (db/entity (:value m)))]
                          (map (fn [m] (let [label (:block/title (db/entity (:value m)))]
@@ -888,7 +895,7 @@
                                                         {:exit-edit? exit-edit?
                                                         {:exit-edit? exit-edit?
                                                          :refresh-result-f refresh-result-f})))
                                                          :refresh-result-f refresh-result-f})))
             selected-choices' (get block (:db/ident property))
             selected-choices' (get block (:db/ident property))
-            selected-choices (if (every? de/entity? selected-choices')
+            selected-choices (if (every? #(and (map? %) (:db/id %)) selected-choices')
                                (map :db/id selected-choices')
                                (map :db/id selected-choices')
                                [selected-choices'])]
                                [selected-choices'])]
         (select-aux block property
         (select-aux block property
@@ -918,100 +925,95 @@
 (rum/defcs property-normal-block-value <
 (rum/defcs property-normal-block-value <
   {:init (fn [state]
   {:init (fn [state]
            (assoc state :container-id (state/get-next-container-id)))}
            (assoc state :container-id (state/get-next-container-id)))}
-  [state block property value-block]
+  [state block property value-block opts]
   (let [container-id (:container-id state)
   (let [container-id (:container-id state)
         multiple-values? (db-property/many? property)
         multiple-values? (db-property/many? property)
         block-container (state/get-component :block/container)
         block-container (state/get-component :block/container)
         blocks-container (state/get-component :block/blocks-container)
         blocks-container (state/get-component :block/blocks-container)
-        value-block (if (and (coll? value-block) (every? de/entity? value-block))
+        value-block (if (and (coll? value-block) (every? entity-map? value-block))
                       (set (remove #(= (:db/ident %) :logseq.property/empty-placeholder) value-block))
                       (set (remove #(= (:db/ident %) :logseq.property/empty-placeholder) value-block))
                       value-block)
                       value-block)
         default-value (:logseq.property/default-value property)
         default-value (:logseq.property/default-value property)
         default-value? (and
         default-value? (and
                         (:db/id default-value)
                         (:db/id default-value)
                         (= (:db/id value-block) (:db/id default-value))
                         (= (:db/id value-block) (:db/id default-value))
-                        (not= (:db/ident property) :logseq.property/default-value))]
-    (if (seq value-block)
-      [:div.property-block-container.content.w-full
-       (let [config {:id (str (if multiple-values?
-                                (:block/uuid block)
-                                (:block/uuid value-block)))
-                     :container-id container-id
-                     :editor-box (state/get-component :editor/box)
-                     :property-block? true
-                     :on-block-content-pointer-down (when default-value?
-                                                      (fn [_e]
-                                                        (<create-new-block! block property (or (:block/title default-value) ""))))
-                     :p-block (:db/id block)
-                     :p-property (:db/id property)}]
-         (if (set? value-block)
-           (blocks-container config (ldb/sort-by-order value-block))
-           (rum/with-key
-             (block-container (assoc config
-                                     :property-default-value? default-value?) value-block)
-             (str (:db/id property) "-" (:block/uuid value-block)))))]
-      [:div
-       {:tabIndex 0
-        :on-click (fn [] (<create-new-block! block property ""))}
-       (property-empty-btn-value property)])))
+                        (not= (:db/ident property) :logseq.property/default-value))
+        table-text-property-render (:table-text-property-render opts)]
+    (if table-text-property-render
+      (table-text-property-render
+       (if multiple-values? (first value-block) value-block)
+       {:create-new-block #(<create-new-block! block property "")
+        :property-ident (:db/ident property)})
+      (cond
+        (seq value-block)
+        [:div.property-block-container.content.w-full
+         (let [config {:id (str (if multiple-values?
+                                  (:block/uuid block)
+                                  (:block/uuid value-block)))
+                       :container-id container-id
+                       :editor-box (state/get-component :editor/box)
+                       :property-block? true
+                       :on-block-content-pointer-down (when default-value?
+                                                        (fn [_e]
+                                                          (<create-new-block! block property (or (:block/title default-value) ""))))
+                       :p-block (:db/id block)
+                       :p-property (:db/id property)
+                       :view? (:view? opts)}]
+           (if (set? value-block)
+             (blocks-container config (ldb/sort-by-order value-block))
+             (rum/with-key
+               (block-container (assoc config
+                                       :property-default-value? default-value?) value-block)
+               (str (:db/id block) "-" (:db/id property) "-" (:db/id value-block)))))]
+
+        :else
+        [:div.w-full.h-full
+         {:tabIndex 0
+          :class (if (:table-view? opts) "cursor-pointer" "cursor-text")
+          :on-click #(<create-new-block! block property "")}]))))
 
 
-(rum/defcs property-block-value < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (let [block (first (:rum/args state))]
-             (when-let [block-id (or (:db/id block) (:block/uuid block))]
-               (db-async/<get-block (state/get-current-repo) block-id :children? true)))
-           state)}
-  [state value block property page-cp]
-  (when value
-    (if (state/sub-async-query-loading (:block/uuid value))
-      [:div.text-sm.opacity-70 "loading"]
-      (if-let [v-block (db/sub-block (:db/id value))]
-        (let [class? (ldb/class? v-block)
-              invalid-warning [:div.warning.text-sm
-                               "Invalid block value, please delete the current property."]]
-          (when v-block
-            (cond
-              (:block/page v-block)
-              (property-normal-block-value block property v-block)
-
-              ;; page/class/etc.
-              (entity-util/page? v-block)
-              (rum/with-key
-                (page-cp {:disable-preview? true
-                          :tag? class?} v-block)
-                (:db/id v-block))
-              :else
-              invalid-warning)))
-        (property-empty-btn-value property)))))
+(rum/defc property-block-value
+  [value block property page-cp opts]
+  (let [v-block value
+        class? (ldb/class? v-block)]
+    (cond
+      (entity-util/page? v-block)
+      (rum/with-key
+        (page-cp {:disable-preview? true
+                  :tag? class?} v-block)
+        (:db/id v-block))
+
+      :else
+      (property-normal-block-value block property v-block opts))))
 
 
 (rum/defc closed-value-item < rum/reactive db-mixins/query
 (rum/defc closed-value-item < rum/reactive db-mixins/query
   [value {:keys [inline-text icon?]}]
   [value {:keys [inline-text icon?]}]
   (when value
   (when value
-    (let [eid (if (de/entity? value) (:db/id value) [:block/uuid value])]
-      (when-let [block (db/sub-block (:db/id (db/entity eid)))]
-        (let [property-block? (db-property/property-created-block? block)
-              value' (db-property/closed-value-content block)
-              icon (pu/get-block-property-value block :logseq.property/icon)]
-          (cond
-            icon
-            (if icon?
-              (icon-component/icon icon {:color? true})
-              [:div.flex.flex-row.items-center.gap-1.h-6
-               (icon-component/icon icon {:color? true})
-               (when value'
-                 [:span value'])])
-
-            property-block?
-            value'
-
-            (= type :number)
-            [:span.number (str value')]
-
-            :else
-            (inline-text {} :markdown (str value'))))))))
+    (let [eid (if (entity-map? value) (:db/id value) [:block/uuid value])
+          block (or (db/sub-block (:db/id (db/entity eid))) value)
+          property-block? (db-property/property-created-block? block)
+          value' (db-property/closed-value-content block)
+          icon (pu/get-block-property-value block :logseq.property/icon)]
+      (cond
+        icon
+        (if icon?
+          (icon-component/icon icon {:color? true})
+          [:div.flex.flex-row.items-center.gap-1.h-6
+           (icon-component/icon icon {:color? true})
+           (when value'
+             [:span value'])])
+
+        property-block?
+        value'
+
+        (= type :number)
+        [:span.number (str value')]
+
+        :else
+        (inline-text {} :markdown (str value'))))))
 
 
 (rum/defc select-item
 (rum/defc select-item
-  [property type value {:keys [page-cp inline-text other-position? property-position _icon?] :as opts}]
+  [property type value {:keys [page-cp inline-text other-position? property-position table-view? _icon?] :as opts}]
   (let [closed-values? (seq (:property/closed-values property))
   (let [closed-values? (seq (:property/closed-values property))
         tag? (or (:tag? opts) (= (:db/ident property) :block/tags))
         tag? (or (:tag? opts) (= (:db/ident property) :block/tags))
         inline-text-cp (fn [content]
         inline-text-cp (fn [content]
@@ -1035,15 +1037,16 @@
            (page-cp {:disable-preview? true
            (page-cp {:disable-preview? true
                      :tag? tag?
                      :tag? tag?
                      :property-position property-position
                      :property-position property-position
-                     :meta-click? other-position?} value)
+                     :meta-click? other-position?
+                     :table-view? table-view?} value)
            (:db/id value)))
            (:db/id value)))
 
 
        (contains? #{:node :class :property :page} type)
        (contains? #{:node :class :property :page} type)
        (when-let [reference (state/get-component :block/reference)]
        (when-let [reference (state/get-component :block/reference)]
-         (reference {} (:block/uuid value)))
+         (reference {:table-view? table-view?} (:block/uuid value)))
 
 
-       (de/entity? value)
-       (when-some [content (str (db-property/property-value-content value))]
+       (and (map? value) (some? (db-property/property-value-content value)))
+       (let [content (str (db-property/property-value-content value))]
          (inline-text-cp content))
          (inline-text-cp content))
 
 
        :else
        :else
@@ -1093,7 +1096,8 @@
 
 
 (defn- property-value-inner
 (defn- property-value-inner
   [block property value {:keys [inline-text page-cp
   [block property value {:keys [inline-text page-cp
-                                dom-id row?]}]
+                                dom-id row?]
+                         :as opts}]
   (let [multiple-values? (db-property/many? property)
   (let [multiple-values? (db-property/many? property)
         class (str (when-not row? "flex flex-1 ")
         class (str (when-not row? "flex flex-1 ")
                    (when multiple-values? "property-value-content"))
                    (when multiple-values? "property-value-content"))
@@ -1103,37 +1107,28 @@
      {:id (or dom-id (random-uuid))
      {:id (or dom-id (random-uuid))
       :tabIndex 0
       :tabIndex 0
       :class (str class " " (when-not text-ref-type? "jtrigger"))
       :class (str class " " (when-not text-ref-type? "jtrigger"))
-      :style {:min-height 24}
-      :on-click (fn []
-                  (when (and text-ref-type? (nil? value))
-                    (<create-new-block! block property "")))}
+      :style {:min-height 24}}
      (cond
      (cond
        (and (= :logseq.property/default-value (:db/ident property)) (nil? (:block/title value)))
        (and (= :logseq.property/default-value (:db/ident property)) (nil? (:block/title value)))
        [:div.jtrigger.cursor-pointer.text-sm.px-2 "Set default value"]
        [:div.jtrigger.cursor-pointer.text-sm.px-2 "Set default value"]
 
 
-       (and text-ref-type? (nil? (:block/title value)))
-       [:div.jtrigger (property-empty-btn-value property)]
-
        text-ref-type?
        text-ref-type?
-       (property-block-value value block property page-cp)
+       (property-block-value value block property page-cp opts)
 
 
        :else
        :else
        (inline-text {} :markdown (macro-util/expand-value-if-macro (str value) (state/get-macros))))]))
        (inline-text {} :markdown (macro-util/expand-value-if-macro (str value) (state/get-macros))))]))
 
 
-(rum/defcs property-scalar-value < rum/static rum/reactive
-  [state block property value* {:keys [container-id editing? on-chosen]
+(rum/defcs property-scalar-value-aux < rum/static rum/reactive
+  [state block property value* {:keys [editing? on-chosen]
                                 :as opts}]
                                 :as opts}]
   (let [property (model/sub-block (:db/id property))
   (let [property (model/sub-block (:db/id property))
         type (:logseq.property/type property)
         type (:logseq.property/type property)
-        editing? (or editing?
-                     (and (state/sub-editing? [container-id (:block/uuid block)])
-                          (= (:db/id property) (:db/id (:property (state/get-editor-action-data))))))
         batch? (batch-operation?)
         batch? (batch-operation?)
         closed-values? (seq (:property/closed-values property))
         closed-values? (seq (:property/closed-values property))
         select-type?' (or (select-type? block property)
         select-type?' (or (select-type? block property)
                           (and editing? batch? (contains? #{:default :url} type) (not closed-values?)))
                           (and editing? batch? (contains? #{:default :url} type) (not closed-values?)))
         select-opts {:on-chosen on-chosen}
         select-opts {:on-chosen on-chosen}
-        value (if (and (de/entity? value*) (= (:db/ident value*) :logseq.property/empty-placeholder))
+        value (if (and (entity-map? value*) (= (:db/ident value*) :logseq.property/empty-placeholder))
                 nil
                 nil
                 value*)]
                 value*)]
     (if (= :logseq.property/icon (:db/ident property))
     (if (= :logseq.property/icon (:db/ident property))
@@ -1186,12 +1181,21 @@
           [:div.flex.flex-1
           [:div.flex.flex-1
            (property-value-inner block property value opts)])))))
            (property-value-inner block property value opts)])))))
 
 
+(rum/defc property-scalar-value
+  [block property value* {:keys [container-id editing?]
+                          :as opts}]
+  (let [block-editing? (state/sub-editing? [container-id (:block/uuid block)])
+        editing (or editing?
+                    (and block-editing?
+                         (= (:db/id property) (:db/id (:property (state/get-editor-action-data))))))]
+    (property-scalar-value-aux block property value* (assoc opts :editing? editing))))
+
 (rum/defc multiple-values-inner
 (rum/defc multiple-values-inner
   [block property v {:keys [on-chosen editing?] :as opts}]
   [block property v {:keys [on-chosen editing?] :as opts}]
   (let [type (:logseq.property/type property)
   (let [type (:logseq.property/type property)
         date? (= type :date)
         date? (= type :date)
         *el (rum/use-ref nil)
         *el (rum/use-ref nil)
-        items (cond->> (if (de/entity? v) #{v} v)
+        items (cond->> (if (entity-map? v) #{v} v)
                 (= (:db/ident property) :block/tags)
                 (= (:db/ident property) :block/tags)
                 (remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))
                 (remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))
         select-cp (fn [select-opts]
         select-cp (fn [select-opts]
@@ -1233,7 +1237,7 @@
              (concat
              (concat
               (->> (for [item items]
               (->> (for [item items]
                      (rum/with-key (select-item property type item opts) (or (:block/uuid item) (str item))))
                      (rum/with-key (select-item property type item opts) (or (:block/uuid item) (str item))))
-                   (interpose [:span.opacity-50.-ml-2 ","]))
+                   (interpose [:span.opacity-50.-ml-1 ","]))
               (when date?
               (when date?
                 [(property-value-date-picker block property nil {:toggle-fn toggle-fn})]))
                 [(property-value-date-picker block property nil {:toggle-fn toggle-fn})]))
              (if date?
              (if date?
@@ -1242,8 +1246,7 @@
 
 
 (rum/defc multiple-values < rum/reactive db-mixins/query
 (rum/defc multiple-values < rum/reactive db-mixins/query
   [block property opts]
   [block property opts]
-  (let [block (db/sub-block (:db/id block))
-        value (get block (:db/ident property))
+  (let [value (get block (:db/ident property))
         value' (if (coll? value) value
         value' (if (coll? value) value
                    (when (some? value) #{value}))]
                    (when (some? value) #{value}))]
     (multiple-values-inner block property value' opts)))
     (multiple-values-inner block property value' opts)))
@@ -1253,8 +1256,7 @@
                          :as opts}]
                          :as opts}]
   (ui/catch-error
   (ui/catch-error
    (ui/block-error "Something wrong" {})
    (ui/block-error "Something wrong" {})
-   (let [block (db/sub-block (:db/id block))
-         block-cp (state/get-component :block/blocks-container)
+   (let [block-cp (state/get-component :block/blocks-container)
          properties-cp (state/get-component :block/properties-cp)
          properties-cp (state/get-component :block/properties-cp)
          opts (merge opts
          opts (merge opts
                      {:page-cp (state/get-component :block/page-cp)
                      {:page-cp (state/get-component :block/page-cp)
@@ -1266,22 +1268,22 @@
          editor-id (str dom-id "-editor")
          editor-id (str dom-id "-editor")
          type (:logseq.property/type property)
          type (:logseq.property/type property)
          multiple-values? (db-property/many? property)
          multiple-values? (db-property/many? property)
-         v (get block (:db/ident property))
-         v (cond
-             (and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
-             v
-             multiple-values?
-             #{v}
-             (set? v)
-             (first v)
-             :else
-             v)
+         v (let [v (get block (:db/ident property))]
+             (cond
+               (and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
+               v
+               multiple-values?
+               #{v}
+               (set? v)
+               (first v)
+               :else
+               v))
          self-value-or-embedded? (fn [v]
          self-value-or-embedded? (fn [v]
                                    (or (= (:db/id v) (:db/id block))
                                    (or (= (:db/id v) (:db/id block))
                                        ;; property value self embedded
                                        ;; property value self embedded
                                        (= (:db/id (:block/link v)) (:db/id block))))]
                                        (= (:db/id (:block/link v)) (:db/id block))))]
-     (if (and (or (and (de/entity? v) (self-value-or-embedded? v))
-                  (and (coll? v) (every? de/entity? v)
+     (if (and (or (and (entity-map? v) (self-value-or-embedded? v))
+                  (and (coll? v) (every? entity-map? v)
                        (some self-value-or-embedded? v))
                        (some self-value-or-embedded? v))
                   (and (= p-block (:db/id block)) (= p-property (:db/id property))))
                   (and (= p-block (:db/id block)) (= p-property (:db/id property))))
               (not= :logseq.class/Tag (:db/ident block)))
               (not= :logseq.class/Tag (:db/ident block)))
@@ -1308,7 +1310,7 @@
                                                   :class-schema? true})
                                                   :class-schema? true})
 
 
                          (and multiple-values? (contains? #{:default :url} type) (not closed-values?) (not editing?))
                          (and multiple-values? (contains? #{:default :url} type) (not closed-values?) (not editing?))
-                         (property-normal-block-value block property v)
+                         (property-normal-block-value block property v opts)
 
 
                          multiple-values?
                          multiple-values?
                          (multiple-values block property opts)
                          (multiple-values block property opts)

+ 59 - 65
src/main/frontend/components/query.cljs

@@ -15,7 +15,6 @@
             [frontend.util :as util]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
-            [logseq.shui.hooks :as hooks]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (defn- built-in-custom-query?
 (defn- built-in-custom-query?
@@ -61,8 +60,10 @@
            (util/hiccup-keywordize result))
            (util/hiccup-keywordize result))
 
 
          (and db-graph? (not (:built-in-query? config)))
          (and db-graph? (not (:built-in-query? config)))
-         (query-view/query-result (assoc config :id (str (:block/uuid current-block)))
-                                  current-block result)
+         (when-let [query (:logseq.property/query current-block)]
+           (when-not (string/blank? (:block/title query))
+             (query-view/query-result (assoc config :id (str (:block/uuid current-block)))
+                                      current-block result)))
 
 
          (and (not db-graph?)
          (and (not db-graph?)
               (or page-list? table?))
               (or page-list? table?))
@@ -126,70 +127,63 @@
                           (:block/collapsed? current-block)))]
                           (:block/collapsed? current-block)))]
     collapsed?'))
     collapsed?'))
 
 
-(rum/defc custom-query* < rum/reactive db-mixins/query
-  [{:keys [*query-error db-graph? dsl-query? built-in-query? table? current-block] :as config}
-   {:keys [builder query view collapsed?] :as q}
-   *result]
-  (let [collapsed?' (:collapsed? config)
-        result' (rum/react *result)]
-    (let [result (when *result (query-result/transform-query-result config q result'))
-          ;; Remove hidden pages from result
-          result (if (and (coll? result) (not (map? result)))
-                   (->> result
-                        (remove (fn [b] (when (and (map? b) (:block/title b)) (ldb/hidden? (:block/title b)))))
-                        (remove (fn [b]
-                                  (when (and current-block (:db/id current-block)) (= (:db/id b) (:db/id current-block))))))
-                   result)
-          ;; Args for displaying query header and results
-          view-fn (if (keyword? view) (get-in (state/sub-config) [:query/views view]) view)
-          view-f (and view-fn (sci/eval-string (pr-str view-fn)))
-          page-list? (and (seq result) (some? (:block/name (first result))))
-          opts {:query-error-atom *query-error
-                :current-block current-block
-                :table? table?
-                :view-f view-f
-                :page-list? page-list?
-                :result result
-                :group-by-page? (query-result/get-group-by-page q {:table? table?})}]
-      (if (:custom-query? config)
+(rum/defcs custom-query* < rum/reactive db-mixins/query
+  {:init (fn [state]
+           (assoc state ::result (atom nil)))}
+  [state {:keys [*query-error db-graph? dsl-query? built-in-query? table? current-block] :as config}
+   {:keys [builder query view collapsed?] :as q}]
+  (let [*result (::result state)
+        collapsed?' (:collapsed? config)
+        result' (query-result/run-custom-query config q *result *query-error)
+        result (when result' (query-result/transform-query-result config q result'))
+        ;; Remove hidden pages from result
+        result (if (and (coll? result) (not (map? result)))
+                 (->> result
+                      (remove (fn [b] (when (and (map? b) (:block/title b)) (ldb/hidden? (:block/title b)))))
+                      (remove (fn [b]
+                                (when (and current-block (:db/id current-block)) (= (:db/id b) (:db/id current-block))))))
+                 result)
+        ;; Args for displaying query header and results
+        view-fn (if (keyword? view) (get-in (state/sub-config) [:query/views view]) view)
+        view-f (and view-fn (sci/eval-string (pr-str view-fn)))
+        page-list? (and (seq result) (some? (:block/name (first result))))
+        opts {:query-error-atom *query-error
+              :current-block current-block
+              :table? table?
+              :view-f view-f
+              :page-list? page-list?
+              :result result
+              :group-by-page? (query-result/get-group-by-page q {:table? table?})}]
+    (if (:custom-query? config)
       ;; Don't display recursive results when query blocks are a query result
       ;; Don't display recursive results when query blocks are a query result
-        [:code (if dsl-query? (str "Results for " (pr-str query)) "Advanced query results")]
-        (when-not (and built-in-query? (empty? result))
-          [:div.custom-query (get config :attr {})
-           (when (and (not db-graph?) (not built-in-query?))
-             (file-query/custom-query-header config
-                                             q
-                                             {:query-error-atom *query-error
-                                              :current-block current-block
-                                              :table? table?
-                                              :view-f view-f
-                                              :page-list? page-list?
-                                              :result result
-                                              :collapsed? collapsed?'}))
-
-           (when (and dsl-query? builder) builder)
+      [:code (if dsl-query? (str "Results for " (pr-str query)) "Advanced query results")]
+      (when-not (and built-in-query? (empty? result))
+        [:div.custom-query (get config :attr {})
+         (when (and (not db-graph?) (not built-in-query?))
+           (file-query/custom-query-header config
+                                           q
+                                           {:query-error-atom *query-error
+                                            :current-block current-block
+                                            :table? table?
+                                            :view-f view-f
+                                            :page-list? page-list?
+                                            :*result *result
+                                            :result result
+                                            :collapsed? collapsed?'}))
 
 
-           (if built-in-query?
-             [:div {:style {:margin-left 2}}
-              (ui/foldable
-               (query-title config (:title q) {:result-count (count result)})
-               (fn []
-                 (custom-query-inner config q opts))
-               {:default-collapsed? collapsed?
-                :title-trigger? true})]
-             [:div.bd
-              (when-not collapsed?'
-                (custom-query-inner config q opts))])])))))
+         (when (and dsl-query? builder) builder)
 
 
-(rum/defc trigger-custom-query
-  [config q]
-  (let [[result set-result!] (rum/use-state nil)]
-    (hooks/use-effect!
-     (fn []
-       (query-result/trigger-custom-query! config q (:*query-error config) set-result!))
-     [q])
-    (when (util/atom? result)
-      (custom-query* config q result))))
+         (if built-in-query?
+           [:div {:style {:margin-left 2}}
+            (ui/foldable
+             (query-title config (:title q) {:result-count (count result)})
+             (fn []
+               (custom-query-inner config q opts))
+             {:default-collapsed? collapsed?
+              :title-trigger? true})]
+           [:div.bd
+            (when-not collapsed?'
+              (custom-query-inner config q opts))])]))))
 
 
 (rum/defcs custom-query < rum/static
 (rum/defcs custom-query < rum/static
   {:init (fn [state]
   {:init (fn [state]
@@ -227,4 +221,4 @@
                         :built-in-query? (built-in-custom-query? (:title q))
                         :built-in-query? (built-in-custom-query? (:title q))
                         :*query-error *query-error)]
                         :*query-error *query-error)]
      (when (or built-in-collapsed? (not db-graph?) (not collapsed?'))
      (when (or built-in-collapsed? (not db-graph?) (not collapsed?'))
-       (trigger-custom-query config' q)))))
+       (custom-query* config' q)))))

+ 44 - 53
src/main/frontend/components/query/builder.cljs

@@ -19,7 +19,6 @@
             [logseq.common.util.page-ref :as page-ref]
             [logseq.common.util.page-ref :as page-ref]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
-            [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.graph-parser.db :as gp-db]
             [logseq.graph-parser.db :as gp-db]
             [logseq.shui.hooks :as hooks]
             [logseq.shui.hooks :as hooks]
@@ -172,55 +171,43 @@
 
 
 (rum/defc property-value-select-inner
 (rum/defc property-value-select-inner
   < rum/reactive db-mixins/query
   < rum/reactive db-mixins/query
-  [repo *property *private-property? *find *tree opts loc values {:keys [db-graph? ref-property? property-type]}]
-  (let [;; FIXME: lazy load property values consistently on first call
-        ;; Guard against non ref properties like :logseq.property/icon
-        _ (when (and db-graph? ref-property?)
-            (doseq [id values] (db/sub-block id)))
-        values' (if db-graph?
-                  (if ref-property?
-                    (map #(db-property/property-value-content (db/entity repo %)) values)
-                    (if (contains? #{:checkbox :keyword :raw-number :string} property-type)
-                      values
-                      ;; Don't display non-ref property values as they don't have display and query support
-                      []))
-                  values)
-        values'' (map #(hash-map :value (str %)
-                                   ;; Preserve original-value as some values like boolean do not display in select
-                                 :original-value %)
-                      (cons "Select all" values'))]
-    (select values''
-            (fn [{:keys [original-value]}]
+  [*property *private-property? *find *tree opts loc values {:keys [db-graph?]}]
+  (let [values' (cons {:label "Select all"
+                       :value "Select all"}
+                      values)
+        find' (rum/react *find)]
+    (select values'
+            (fn [{:keys [value]}]
               (let [k (cond
               (let [k (cond
                         db-graph? (if @*private-property? :private-property :property)
                         db-graph? (if @*private-property? :private-property :property)
-                        (= (rum/react *find) :page) :page-property
+                        (= find' :page) :page-property
                         :else :property)
                         :else :property)
-                    x (if (= original-value "Select all")
+                    x (if (= value "Select all")
                         [k @*property]
                         [k @*property]
-                        [k @*property original-value])]
+                        [k @*property value])]
                 (reset! *property nil)
                 (reset! *property nil)
                 (append-tree! *tree opts loc x))))))
                 (append-tree! *tree opts loc x))))))
 
 
 (rum/defc property-value-select
 (rum/defc property-value-select
   [repo *property *private-property? *find *tree opts loc]
   [repo *property *private-property? *find *tree opts loc]
   (let [db-graph? (sqlite-util/db-based-graph? repo)
   (let [db-graph? (sqlite-util/db-based-graph? repo)
-        property-type (when db-graph? (:logseq.property/type (db/entity repo @*property)))
-        ref-property? (and db-graph? (contains? db-property-type/all-ref-property-types property-type))
         [values set-values!] (rum/use-state nil)]
         [values set-values!] (rum/use-state nil)]
     (hooks/use-effect!
     (hooks/use-effect!
-     (fn []
+     (fn [_property]
        (p/let [result (if db-graph?
        (p/let [result (if db-graph?
-                        (db-async/<get-block-property-values repo @*property)
-                        (db-async/<file-get-property-values repo @*property))]
-         (when (and db-graph? ref-property?)
-           (doseq [db-id result]
-             (db-async/<get-block repo db-id :children? false)))
+                        (p/let [result (db-async/<get-property-values @*property)]
+                          (map (fn [{:keys [label _value]}]
+                                 {:label label
+                                  :value label})
+                               result))
+                        (p/let [result (db-async/<file-get-property-values repo @*property)]
+                          (map (fn [value]
+                                 {:label (str value)
+                                  :value value}) result)))]
          (set-values! result)))
          (set-values! result)))
      [@*property])
      [@*property])
-    (property-value-select-inner repo *property *private-property? *find *tree opts loc values
-                                 {:db-graph? db-graph?
-                                  :ref-property? ref-property?
-                                  :property-type property-type})))
+    (property-value-select-inner *property *private-property? *find *tree opts loc values
+                                 {:db-graph? db-graph?})))
 
 
 (rum/defc tags
 (rum/defc tags
   [repo *tree opts loc]
   [repo *tree opts loc]
@@ -238,6 +225,19 @@
               (fn [{:keys [value]}]
               (fn [{:keys [value]}]
                 (append-tree! *tree opts loc [(if db-based? :tags :page-tags) value]))))))
                 (append-tree! *tree opts loc [(if db-based? :tags :page-tags) value]))))))
 
 
+(rum/defc page-search
+  [on-chosen]
+  (let [[result set-result!] (hooks/use-state nil)
+        [loading? set-loading!] (hooks/use-state nil)]
+    (hooks/use-effect!
+     (fn []
+       (set-loading! true)
+       (p/let [result (state/<invoke-db-worker :thread-api/get-all-page-titles (state/get-current-repo))]
+         (set-result! result)
+         (set-loading! false)))
+     [])
+    (select result on-chosen {:loading? loading?})))
+
 (defn- db-based-query-filter-picker
 (defn- db-based-query-filter-picker
   [state *find *tree loc clause opts]
   [state *find *tree loc clause opts]
   (let [*mode (::mode state)
   (let [*mode (::mode state)
@@ -291,18 +291,14 @@
                               (append-tree! *tree opts loc (vec (cons :priority choices)))))})
                               (append-tree! *tree opts loc (vec (cons :priority choices)))))})
 
 
        "page"
        "page"
-       (let [pages (sort (db-model/get-all-page-titles repo))]
-         (select pages
-                 (fn [{:keys [value]}]
-                   (append-tree! *tree opts loc [:page value]))))
+       (page-search (fn [{:keys [value]}]
+                      (append-tree! *tree opts loc [:page value])))
 
 
        ;; TODO: replace with node reference
        ;; TODO: replace with node reference
        "page reference"
        "page reference"
-       (let [pages (sort (db-model/get-all-page-titles repo))]
-         (select pages
-                 (fn [{:keys [value]}]
-                   (append-tree! *tree opts loc [:page-ref value]))
-                 {}))
+
+       (page-search (fn [{:keys [value]}]
+                      (append-tree! *tree opts loc [:page-ref value])))
 
 
        "full text search"
        "full text search"
        (search (fn [v] (append-tree! *tree opts loc v))
        (search (fn [v] (append-tree! *tree opts loc v))
@@ -376,17 +372,12 @@
                               (append-tree! *tree opts loc (vec (cons :priority choices)))))})
                               (append-tree! *tree opts loc (vec (cons :priority choices)))))})
 
 
        "page"
        "page"
-       (let [pages (sort (db-model/get-all-page-titles repo))]
-         (select pages
-                 (fn [{:keys [value]}]
-                   (append-tree! *tree opts loc [:page value]))))
+       (page-search (fn [{:keys [value]}]
+                      (append-tree! *tree opts loc [:page value])))
 
 
        "page reference"
        "page reference"
-       (let [pages (sort (db-model/get-all-page-titles repo))]
-         (select pages
-                 (fn [{:keys [value]}]
-                   (append-tree! *tree opts loc [:page-ref value]))
-                 {}))
+       (page-search (fn [{:keys [value]}]
+                      (append-tree! *tree opts loc [:page-ref value])))
 
 
        "full text search"
        "full text search"
        (search (fn [v] (append-tree! *tree opts loc v))
        (search (fn [v] (append-tree! *tree opts loc v))

+ 21 - 17
src/main/frontend/components/query/result.cljs

@@ -10,12 +10,14 @@
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.template :as template]
             [frontend.template :as template]
+            [frontend.util :as util]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
+            [logseq.db :as ldb]
             [promesa.core :as p]
             [promesa.core :as p]
-            [logseq.db :as ldb]))
+            [rum.core :as rum]))
 
 
-(defn trigger-custom-query!
-  [config query *query-error set-result!]
+(defn run-custom-query
+  [config query *result *query-error]
   (let [repo (state/get-current-repo)
   (let [repo (state/get-current-repo)
         current-block-uuid (or (:block/uuid (:block config))
         current-block-uuid (or (:block/uuid (:block config))
                                (:block/uuid config))
                                (:block/uuid config))
@@ -27,29 +29,31 @@
               form (common-util/safe-read-string {:log-error? false} q)]
               form (common-util/safe-read-string {:log-error? false} q)]
           (cond
           (cond
             (and (symbol? form)
             (and (symbol? form)
-                                ;; Queries only containing template should trigger a query
+                 ;; Queries only containing template should trigger a query
                  (not (re-matches template/template-re (string/trim q))))
                  (not (re-matches template/template-re (string/trim q))))
             nil
             nil
 
 
             (re-matches #"^\".*\"$" q) ; full-text search
             (re-matches #"^\".*\"$" q) ; full-text search
-            (p/let [blocks (search/block-search repo (string/trim form) {:limit 30})]
-              (when (seq blocks)
-                (let [result (->> blocks
-                                  (keep (fn [b]
-                                          (when-not (= (:block/uuid b) current-block-uuid)
-                                            (let [entity (db/entity [:block/uuid (:block/uuid b)])]
-                                              (when-not (ldb/hidden? entity)
-                                                entity))))))]
-                  (set-result! (atom result)))))
+            (do
+              (p/let [blocks (search/block-search repo (string/trim form) {:limit 30})]
+                (when (seq blocks)
+                  (let [result (->> blocks
+                                    (keep (fn [b]
+                                            (when-not (= (:block/uuid b) current-block-uuid)
+                                              (let [entity (or (db/entity [:block/uuid (:block/uuid b)]) b)]
+                                                (when-not (ldb/hidden? entity)
+                                                  entity))))))]
+                    (reset! *result result))))
+              (rum/react *result))
 
 
             :else
             :else
             (let [result (query-dsl/query (state/get-current-repo) q {:cards? (:cards? config)})]
             (let [result (query-dsl/query (state/get-current-repo) q {:cards? (:cards? config)})]
-              (set-result! (or result (atom []))))))
+              (when (util/atom? result)
+                (rum/react result)))))
 
 
         :else
         :else
-        (set-result! (query-custom/custom-query query {:current-block-uuid current-block-uuid
-                                                       ;; FIXME: Remove this temporary workaround for reactivity not working
-                                                       :use-cache? false})))
+        (util/react (query-custom/custom-query query {:current-block-uuid current-block-uuid
+                                                      :built-in-query? (:built-in-query? config)})))
       (catch :default e
       (catch :default e
         (reset! *query-error e)))))
         (reset! *query-error e)))))
 
 

+ 17 - 39
src/main/frontend/components/query/view.cljs

@@ -2,13 +2,9 @@
   "DB query result view"
   "DB query result view"
   (:require [frontend.components.views :as views]
   (:require [frontend.components.views :as views]
             [frontend.db :as db]
             [frontend.db :as db]
-            [frontend.mixins :as mixins]
-            [frontend.modules.outliner.op :as outliner-op]
-            [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state]
             [frontend.state]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
-            [logseq.db.frontend.entity-util :as entity-util]
-            [promesa.core :as p]
+            [logseq.shui.hooks :as hooks]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (defn- columns
 (defn- columns
@@ -21,8 +17,7 @@
 
 
 (defn- result->entities
 (defn- result->entities
   [result]
   [result]
-  (map (fn [b]
-         (assoc (db/entity (:db/id b)) :id (:db/id b))) result))
+  (map (fn [b] (or (db/entity (:db/id b)) b)) result))
 
 
 (defn- init-result
 (defn- init-result
   [result view-entity]
   [result view-entity]
@@ -32,42 +27,25 @@
     (->> (result->entities result')
     (->> (result->entities result')
          (remove (fn [b] (contains?
          (remove (fn [b] (contains?
                           #{(:db/id view-entity) (:db/id (:logseq.property/query view-entity))}
                           #{(:db/id view-entity) (:db/id (:logseq.property/query view-entity))}
-                          (:db/id b)))))))
+                          (:db/id b))))
+         (remove :logseq.property/view-for))))
 
 
-(rum/defcs query-result < rum/static mixins/container-id
-  (rum/local nil ::result)
-  {:will-remount (fn [old-state new-state]
-                   (let [*result (::result new-state)
-                         [_config view-entity old-result] (:rum/args old-state)
-                         [_config _view-entity new-result] (:rum/args old-state)]
-                     (when-not (= old-result new-result)
-                       (reset! *result (init-result new-result view-entity))))
-                   new-state)}
-  [state config view-entity result]
-  (let [*result (::result state)
-        result' (or @*result (init-result result view-entity))
-        columns' (columns (assoc config :container-id (::container-id state)) result')
-        set-data! (fn [data] (reset! *result data))]
+(rum/defc query-result
+  [config view-entity result*]
+  (let [[data set-data!] (rum/use-state (init-result result* view-entity))
+        ids (mapv :db/id data)
+        columns' (columns config data)]
+    (hooks/use-effect!
+     (fn []
+       (set-data! (init-result result* view-entity)))
+     [result*])
     [:div.query-result.w-full
     [:div.query-result.w-full
      (views/view
      (views/view
-      {:config {:custom-query? true}
+      {:config (assoc {:custom-query? true} :sidebar? (:sidebar? config))
        :title-key :views.table/live-query-title
        :title-key :views.table/live-query-title
        :view-entity view-entity
        :view-entity view-entity
        :view-feature-type :query-result
        :view-feature-type :query-result
-       :data result'
+       :data ids
        :set-data! set-data!
        :set-data! set-data!
-       :columns columns'
-       :on-delete-rows (fn [table selected-rows]
-                         (let [pages (filter entity-util/page? selected-rows)
-                               blocks (remove entity-util/page? selected-rows)
-                               selected (set (map :id (remove :logseq.property/built-in? selected-rows)))
-                               data' (remove (fn [row] (contains? selected (:id row))) (:data table))]
-                           (p/do!
-                            (set-data! data')
-                            (ui-outliner-tx/transact!
-                             {:outliner-op :delete-blocks}
-                             (when (seq blocks)
-                               (outliner-op/delete-blocks! blocks nil))
-                             (doseq [page pages]
-                               (when-let [id (:block/uuid page)]
-                                 (outliner-op/delete-page! id)))))))})]))
+       :query-entity-ids ids
+       :columns columns'})]))

+ 86 - 246
src/main/frontend/components/reference.cljs

@@ -1,77 +1,59 @@
 (ns frontend.components.reference
 (ns frontend.components.reference
-  (:require [frontend.components.block :as block]
+  (:require [frontend.common.missionary :as c.m]
+            [frontend.components.block :as block]
             [frontend.components.content :as content]
             [frontend.components.content :as content]
             [frontend.components.editor :as editor]
             [frontend.components.editor :as editor]
             [frontend.components.reference-filters :as filters]
             [frontend.components.reference-filters :as filters]
             [frontend.components.views :as views]
             [frontend.components.views :as views]
             [frontend.config :as config]
             [frontend.config :as config]
-            [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
             [frontend.db.async :as db-async]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
-            [frontend.handler.block :as block-handler]
-            [frontend.handler.page :as page-handler]
-            [frontend.modules.outliner.tree :as tree]
-            [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
-            [frontend.util :as util]
-            [logseq.db :as ldb]
+            [logseq.db.common.view :as db-view]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
+            [missionary.core :as m]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
-(rum/defc block-linked-references < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (when-let [e (db/entity [:block/uuid (first (:rum/args state))])]
-             (db-async/<get-block-refs (state/get-current-repo) (:db/id e)))
-           state)}
+;; TODO: merge both page and block linked refs
+(rum/defc block-linked-references-aux < rum/reactive db-mixins/query
+  [e]
+  (let [block-id (:block/uuid e)
+        ref-blocks (-> (db/get-referenced-blocks (:db/id e))
+                       db-utils/group-by-page)]
+    (when (> (count ref-blocks) 0)
+      (let [ref-hiccup (block/->hiccup ref-blocks
+                                       {:id (str block-id)
+                                        :ref? true
+                                        :breadcrumb-show? true
+                                        :group-by-page? true
+                                        :editor-box editor/box}
+                                       {})]
+        [:div.references-blocks
+         (content/content block-id
+                          {:hiccup ref-hiccup})]))))
+
+(rum/defc block-linked-references
   [block-id]
   [block-id]
   (when-let [e (db/entity [:block/uuid block-id])]
   (when-let [e (db/entity [:block/uuid block-id])]
-    (when-not (state/sub-async-query-loading (str (:db/id e) "-refs"))
-      (let [ref-blocks (-> (db/get-referenced-blocks (:db/id e))
-                           db-utils/group-by-page)]
-        (when (> (count ref-blocks) 0)
-          (let [ref-hiccup (block/->hiccup ref-blocks
-                                           {:id (str block-id)
-                                            :ref? true
-                                            :breadcrumb-show? true
-                                            :group-by-page? true
-                                            :editor-box editor/box}
-                                           {})]
-            [:div.references-blocks
-             (content/content block-id
-                              {:hiccup ref-hiccup})]))))))
-
-(rum/defc references-inner
-  [page-entity filters filtered-ref-blocks]
-  (let [*ref (rum/use-ref nil)]
-    [:div.references-blocks.faster.fade-in {:ref *ref}
-     (let [ref-hiccup (block/->hiccup filtered-ref-blocks
-                                      {:id (str (:block/uuid page-entity))
-                                       :ref? true
-                                       :breadcrumb-show? true
-                                       :group-by-page? true
-                                       :editor-box editor/box
-                                       :filters filters}
-                                      {})]
-       (content/content (str (:block/uuid page-entity)) {:hiccup ref-hiccup}))]))
-
-(defn- columns
-  [config result]
-  (->> (mapcat :block.temp/property-keys result)
-       distinct
-       (map db/entity)
-       (ldb/sort-by-order)
-       ((fn [cs] (views/build-columns config cs {:add-tags-column? false})))))
-
-(rum/defc references-cp
-  [page-entity *filters total filter-n filtered-ref-blocks *ref-pages]
-  (let [filters @*filters
-        *collapsed? (atom nil)
-        db-based? (config/db-based-graph?)
-        reference-filter (if db-based?
+    (let [[loading? set-loading!] (hooks/use-state true)]
+      (hooks/use-effect!
+       (fn []
+         (p/do!
+          (db-async/<get-block-refs (state/get-current-repo) (:db/id e))
+          (set-loading! false)))
+       [])
+      (when-not loading?
+        (block-linked-references-aux e)))))
+
+(rum/defc references-aux
+  [page-entity config]
+  (let [filters (db-view/get-filters (db/get-db) page-entity)
+        reference-filter (fn [{:keys [ref-pages-count]}]
                            (shui/button
                            (shui/button
                             {:title "Page filter"
                             {:title "Page filter"
                              :variant "ghost"
                              :variant "ghost"
@@ -81,7 +63,7 @@
                                          (shui/popup-show! (.-target e)
                                          (shui/popup-show! (.-target e)
                                                            (fn []
                                                            (fn []
                                                              [:div.p-4
                                                              [:div.p-4
-                                                              (filters/filter-dialog page-entity *filters *ref-pages)])
+                                                              (filters/filter-dialog page-entity ref-pages-count)])
                                                            {:align "end"}))}
                                                            {:align "end"}))}
                             (ui/icon "filter-cog"
                             (ui/icon "filter-cog"
                                      {:class (cond
                                      {:class (cond
@@ -94,194 +76,52 @@
                                                (and (empty? (:included filters)) (seq (:excluded filters)))
                                                (and (empty? (:included filters)) (seq (:excluded filters)))
                                                "text-error"
                                                "text-error"
                                                :else
                                                :else
-                                               "text-warning")}))
-                           [:a.filter.fade-link
-                            {:title (t :linked-references/filter-heading)
-                             :on-mouse-over (fn [_e]
-                                              (when @*collapsed? ; collapsed
-                           ;; expand
-                                                (reset! @*collapsed? false)))
-                             :on-pointer-down (fn [e]
-                                                (util/stop-propagation e)
-                                                (shui/popup-show! (.-target e)
-                                                                  (fn []
-                                                                    [:div.p-4
-                                                                     (filters/filter-dialog page-entity *filters *ref-pages)])
-                                                                  {:align "end"}))}
-                            (ui/icon "filter" {:class (cond
-                                                        (and (empty? (:included filters)) (empty? (:excluded filters)))
-                                                        "opacity-60 hover:opacity-100"
-
-                                                        (and (seq (:included filters)) (empty? (:excluded filters)))
-                                                        "text-success"
-
-                                                        (and (empty? (:included filters)) (seq (:excluded filters)))
-                                                        "text-error"
-                                                        :else
-                                                        "text-warning")
-                                               :size  22})])]
-    (if db-based?
-      (let [blocks (->> (mapcat second filtered-ref-blocks)
-                        (map (fn [b] (assoc (db/entity (:db/id b)) :id (:db/id b)))))
-            columns' (columns {} blocks)]
-        (when (or (seq blocks)
-                  (seq (:included filters))
-                  (seq (:excluded filters)))
-          (views/view
-           {:view-parent page-entity
-            :view-feature-type :linked-references
-            :additional-actions [reference-filter]
-            :data blocks
-            :columns columns'})))
-      (let [threshold (state/get-linked-references-collapsed-threshold)
-            default-collapsed? (or (>= total threshold) (ldb/class? page-entity))]
-        (ui/foldable
-         [:div.flex.flex-row.flex-1.justify-between.items-center
-          [:div.font-medium.opacity-50
-           (t :linked-references/reference-count (when (or (seq (:included filters))
-                                                           (seq (:excluded filters))) filter-n) total)]
-          reference-filter]
-
-         (fn []
-           (references-inner page-entity filters filtered-ref-blocks))
-
-         {:default-collapsed? default-collapsed?
-          :title-trigger? true
-          :init-collapsed (fn [collapsed-atom]
-                            (reset! *collapsed? collapsed-atom))})))))
-
-(defn- get-filtered-children
-  [block parent->blocks]
-  (let [children (get parent->blocks (:db/id block))]
-    (set
-     (loop [blocks children
-            result (vec children)]
-       (if (empty? blocks)
-         result
-         (let [fb (first blocks)
-               children (get parent->blocks (:db/id fb))]
-           (recur
-            (concat children (rest blocks))
-            (conj result fb))))))))
-
-(rum/defc references-aux < rum/reactive db-mixins/query
-  {:should-update (fn [old-state new-state]
-                    ;; Re-render if only filters update
-                    (not= (last (:rum/args old-state))
-                          (last (:rum/args new-state))))}
-  [state repo page-entity *filters filters]
-  (let [*ref-pages (::ref-pages state)
-        page-id (:db/id page-entity)
-        ref-blocks (db/get-referenced-blocks page-id)
-        aliases (db/page-alias-set repo page-id)
-        aliases-exclude-self (set (remove #{page-id} aliases))
-        top-level-blocks (filter (fn [b] (some aliases (set (map :db/id (:block/refs b))))) ref-blocks)
-        top-level-blocks-ids (set (map :db/id top-level-blocks))
-        filtered-ref-blocks (->> (block-handler/filter-blocks ref-blocks filters)
-                                 (block-handler/get-filtered-ref-blocks-with-parents ref-blocks))
-        total (count top-level-blocks)
-        filtered-top-blocks (filter (fn [b] (top-level-blocks-ids (:db/id b))) filtered-ref-blocks)
-        filter-n (count filtered-top-blocks)
-        parent->blocks (group-by (fn [x] (:db/id (x :block/parent))) filtered-ref-blocks)
-        result (->> (group-by :block/page filtered-top-blocks)
-                    (map (fn [[page blocks]]
-                           (let [blocks (sort-by (fn [b] (not= (:db/id page) (:db/id (:block/parent b)))) blocks)
-                                 result (map (fn [block]
-                                               (let [filtered-children (get-filtered-children block parent->blocks)
-                                                     refs (when-not (contains? top-level-blocks-ids (:db/id (:block/parent block)))
-                                                            (block-handler/get-blocks-refed-pages aliases (cons block filtered-children)))
-                                                     block' (assoc (tree/block-entity->map block) :block/children filtered-children)]
-                                                 [block' refs])) blocks)
-                                 blocks' (map first result)
-                                 page' (if (contains? aliases-exclude-self (:db/id page))
-                                         {:db/id (:db/id page)
-                                          :block/alias? true
-                                          :block/journal-day (:block/journal-day page)}
-                                         page)]
-                             [[page' blocks'] (mapcat second result)]))))
-        filtered-ref-blocks' (map first result)
-        ref-pages (->>
-                   (mapcat second result)
-                   (map :block/title)
-                   frequencies)]
-    (reset! *ref-pages ref-pages)
-    (when (or (seq (:included filters)) (seq (:excluded filters)) (> filter-n 0))
-      [:div.references.page-linked.flex-1.flex-row
-       [:div.content.pt-2
-        (references-cp page-entity *filters total filter-n filtered-ref-blocks' *ref-pages)]])))
-
-(rum/defcs references* < rum/reactive db-mixins/query
-  (rum/local nil ::ref-pages)
-  {:init (fn [state]
-           (let [page (first (:rum/args state))]
-             (when page (db-async/<get-block-refs (state/get-current-repo) (:db/id page))))
-           (assoc state ::filters (atom nil)))}
-  [state block-entity]
-  (when block-entity
-    (let [repo (state/get-current-repo)
-          *filters (::filters state)]
-      (when block-entity
-        (when-not (state/sub-async-query-loading (str (:db/id block-entity) "-refs"))
-          (let [block-entity (db/sub-block (:db/id block-entity))
-                filters (page-handler/get-filters block-entity)
-                _ (when-not (= filters @*filters)
-                    (reset! *filters filters))]
-            (references-aux state repo block-entity *filters filters)))))))
+                                               "text-warning")})))]
+    (views/view
+     {:view-parent page-entity
+      :view-feature-type :linked-references
+      :additional-actions [reference-filter]
+      :columns (views/build-columns config [] {})
+      :config config})))
+
+(rum/defc references-cp < rum/reactive db-mixins/query
+  [page-entity config]
+  (let [page (db/sub-block (:db/id page-entity))]
+    (references-aux page config)))
 
 
 (rum/defc references
 (rum/defc references
-  [entity]
-  (ui/catch-error
-   (ui/component-error (if (config/db-based-graph? (state/get-current-repo))
-                         "Linked References: Unexpected error."
-                         "Linked References: Unexpected error. Please re-index your graph first."))
-   (references* entity)))
-
-(rum/defcs unlinked-references-aux
-  < rum/reactive db-mixins/query
-  {:init
-   (fn [state]
-     (let [*result (atom nil)
-           [page *n-ref] (:rum/args state)]
-       (p/let [result (search/get-unlinked-refs (:db/id page))
-               result' (remove nil? result)]
-         (reset! *n-ref (count result'))
-         (reset! *result result'))
-       (assoc state ::result *result)))}
-  [state page _n-ref]
-  (let [ref-blocks (rum/react (::result state))]
-    (when (seq ref-blocks)
-      (if (config/db-based-graph?)
-        (let [blocks (->> (mapcat val ref-blocks)
-                          (map (fn [b] (assoc (db/entity (:db/id b)) :id (:db/id b)))))
-              columns' (columns {} blocks)]
-          (views/view
-           {:view-parent page
-            :view-feature-type :unlinked-references
-            :data blocks
-            :columns columns'
-            :foldable-options {:default-collapsed? true}}))
-        [:div.references-blocks
-         (let [ref-hiccup (block/->hiccup ref-blocks
-                                          {:id (str (:block/title page) "-unlinked-")
-                                           :ref? true
-                                           :group-by-page? true
-                                           :editor-box editor/box}
-                                          {})]
-           (content/content (:block/name page)
-                            {:hiccup ref-hiccup}))]))))
-
-(rum/defcs unlinked-references < rum/reactive
-  (rum/local nil ::n-ref)
-  [state page]
-  (let [n-ref (get state ::n-ref)]
-    (when page
-      [:div.references.page-unlinked.mt-6.flex-1.flex-row.faster.fade-in
-       [:div.content.flex-1
-        (if (config/db-based-graph?)
-          (unlinked-references-aux page n-ref)
-          (ui/foldable
-           [:div.font-medium.opacity-50
-            (t :unlinked-references/reference-count @n-ref)]
-           (fn [] (unlinked-references-aux page n-ref))
-           {:default-collapsed? true
-            :title-trigger? true}))]])))
+  [entity config]
+  (when-let [id (:db/id entity)]
+    (let [[has-references? set-has-references!] (hooks/use-state nil)]
+      (hooks/use-effect!
+       #(c.m/run-task*
+         (m/sp
+           (let [result (c.m/<? (state/<invoke-db-worker :thread-api/block-refs-check
+                                                         (state/get-current-repo) id {}))]
+             (set-has-references! result))))
+       [])
+      (when has-references?
+        (ui/catch-error
+         (ui/component-error (if (config/db-based-graph? (state/get-current-repo))
+                               "Linked References: Unexpected error."
+                               "Linked References: Unexpected error. Please re-index your graph first."))
+         (references-cp entity config))))))
+
+(rum/defc unlinked-references
+  [entity config]
+  (when-let [id (:db/id entity)]
+    (let [[has-references? set-has-references!] (hooks/use-state nil)]
+      (hooks/use-effect!
+       #(c.m/run-task*
+         (m/sp
+           (let [result (c.m/<? (state/<invoke-db-worker :thread-api/block-refs-check
+                                                         (state/get-current-repo) id {:unlinked? true}))]
+             (set-has-references! result))))
+       [])
+      (when has-references?
+        (views/view
+         {:view-parent entity
+          :view-feature-type :unlinked-references
+          :columns (views/build-columns config [] {})
+          :foldable-options {:default-collapsed? true}
+          :config config})))))

+ 10 - 9
src/main/frontend/components/reference_filters.cljs

@@ -1,17 +1,18 @@
 (ns frontend.components.reference-filters
 (ns frontend.components.reference-filters
   "References filters"
   "References filters"
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
+            [datascript.impl.entity :as de]
+            [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.search :as search]
             [frontend.search :as search]
+            [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.db.common.view :as db-view]
             [promesa.core :as p]
             [promesa.core :as p]
-            [rum.core :as rum]
-            [frontend.config :as config]
-            [frontend.state :as state]
-            [frontend.db :as db]
-            [datascript.impl.entity :as de]))
+            [rum.core :as rum]))
 
 
 (defn- frequencies-sort
 (defn- frequencies-sort
   [references]
   [references]
@@ -51,11 +52,11 @@
                :variant :outline
                :variant :outline
                :key ref-name)))))])
                :key ref-name)))))])
 
 
-(rum/defcs filter-dialog < rum/reactive (rum/local "" ::filterSearch)
-  [state page-entity *filters *references]
-  (let [filters (rum/react *filters)
+(rum/defcs filter-dialog < (rum/local "" ::filterSearch) rum/reactive
+  [state page references]
+  (let [page-entity (db/sub-block (:db/id page))
         filter-search (get state ::filterSearch)
         filter-search (get state ::filterSearch)
-        references (rum/react *references)
+        filters (db-view/get-filters (db/get-db) page-entity)
         filtered-references  (frequencies-sort
         filtered-references  (frequencies-sort
                               (if (= @filter-search "")
                               (if (= @filter-search "")
                                 references
                                 references

+ 174 - 150
src/main/frontend/components/right_sidebar.cljs

@@ -11,6 +11,7 @@
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db :as db]
+            [frontend.db.async :as db-async]
             [frontend.db.rtc.debug-ui :as rtc-debug-ui]
             [frontend.db.rtc.debug-ui :as rtc-debug-ui]
             [frontend.extensions.slide :as slide]
             [frontend.extensions.slide :as slide]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
@@ -23,6 +24,7 @@
             [logseq.shui.hooks :as hooks]
             [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
+            [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
@@ -68,78 +70,84 @@
                          :sidebar-key sidebar-key} repo block-id {:indent? false})]
                          :sidebar-key sidebar-key} repo block-id {:indent? false})]
      (block-cp repo idx block)]))
      (block-cp repo idx block)]))
 
 
-(defn build-sidebar-item
+(defn- <build-sidebar-item
   [repo idx db-id block-type *db-id init-key]
   [repo idx db-id block-type *db-id init-key]
-  (case (keyword block-type)
-    :contents
-    [[:.flex.items-center (ui/icon "list-details" {:class "text-md mr-2"}) (t :right-side-bar/contents)]
-     (page-cp repo "contents")]
-
-    :help
-    [[:.flex.items-center (ui/icon "help" {:class "text-md mr-2"}) (t :right-side-bar/help)] (onboarding/help)]
-
-    :page-graph
-    [[:.flex.items-center (ui/icon "hierarchy" {:class "text-md mr-2"}) (t :right-side-bar/page-graph)]
-     (page/page-graph)]
-
-    :block-ref
-    #_:clj-kondo/ignore
-    (let [lookup (if (integer? db-id) db-id [:block/uuid db-id])]
-      (when-let [block (db/entity repo lookup)]
-        [(t :right-side-bar/block-ref)
-         (block-with-breadcrumb repo block idx [repo db-id block-type] true)]))
-
-    :block
-    #_:clj-kondo/ignore
-    (let [lookup (if (integer? db-id) db-id [:block/uuid db-id])]
-      (when-let [block (db/entity repo lookup)]
-        (block-with-breadcrumb repo block idx [repo db-id block-type] false)))
-
-    :page
-    (let [lookup (if (integer? db-id) db-id [:block/uuid db-id])
-          page (db/entity repo lookup)]
-      (if (ldb/page? page)
-        [[:.flex.items-center.page-title.gap-1
-          (icon/get-node-icon-cp page {:class "text-md"})
-          [:span.overflow-hidden.text-ellipsis (:block/title page)]]
-         (page-cp repo (str (:block/uuid page)))]
-        (block-with-breadcrumb repo page idx [repo db-id block-type] false)))
-
-    :search
-    [[:.flex.items-center.page-title
-      (ui/icon "search" {:class "text-md mr-2"})
-      (let [input (rum/react *db-id)
-            input' (if (string/blank? input) "Blank input" input)]
-        [:span.overflow-hidden.text-ellipsis input'])]
-     (rum/with-key
-       (cmdk/cmdk-block {:initial-input db-id
-                         :sidebar? true
-                         :on-input-change (fn [new-value]
-                                            (reset! *db-id new-value))
-                         :on-input-blur (fn [new-value]
-                                          (state/sidebar-replace-block! [repo db-id block-type]
-                                                                        [repo new-value block-type]))})
-       (str init-key))]
-
-    :page-slide-view
-    (let [page (db/entity db-id)]
-      [[:a.page-title {:href (rfe/href :page {:name (str (:block/uuid page))})}
-        (:block/title page)]
-       [:div.ml-2.slide.mt-2
-        (slide/slide page)]])
-
-    :shortcut-settings
-    [[:.flex.items-center (ui/icon "command" {:class "text-md mr-2"}) (t :help/shortcuts)]
-     (shortcut-settings)]
-    :rtc
-    [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) RTC"]
-     (rtc-debug-ui/rtc-debug-ui)]
-
-    :profiler
-    [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) Profiler"]
-     (profiler/profiler)]
-
-    ["" [:span]]))
+  (p/do!
+   (db-async/<get-block repo db-id)
+   (let [lookup (cond
+                  (integer? db-id) db-id
+                  (uuid? db-id) [:block/uuid db-id]
+                  :else nil)
+         entity (when lookup (db/entity repo lookup))
+         page? (ldb/page? entity)
+         block-render (fn []
+                        (when entity
+                          (if page?
+                            [[:.flex.items-center.page-title.gap-1
+                              (icon/get-node-icon-cp entity {:class "text-md"})
+                              [:span.overflow-hidden.text-ellipsis (:block/title entity)]]
+                             (page-cp repo (str (:block/uuid entity)))]
+                            (block-with-breadcrumb repo entity idx [repo db-id block-type] false))))]
+     (case (keyword block-type)
+       :contents
+       (when-let [page (db/get-page "Contents")]
+         [[:.flex.items-center (ui/icon "list-details" {:class "text-md mr-2"}) (t :right-side-bar/contents)]
+          (page-cp repo (str (:block/uuid page)))])
+
+       :help
+       [[:.flex.items-center (ui/icon "help" {:class "text-md mr-2"}) (t :right-side-bar/help)] (onboarding/help)]
+
+       :page-graph
+       [[:.flex.items-center (ui/icon "hierarchy" {:class "text-md mr-2"}) (t :right-side-bar/page-graph)]
+        (page/page-graph)]
+
+       :block-ref
+       (let [lookup (if (integer? db-id) db-id [:block/uuid db-id])]
+         (when-let [block (db/entity repo lookup)]
+           [(t :right-side-bar/block-ref)
+            (block-with-breadcrumb repo block idx [repo db-id block-type] true)]))
+
+       :block
+       (block-render)
+
+       :page
+       (block-render)
+
+       :search
+       [[:.flex.items-center.page-title
+         (ui/icon "search" {:class "text-md mr-2"})
+         (let [input (rum/react *db-id)
+               input' (if (string/blank? input) "Blank input" input)]
+           [:span.overflow-hidden.text-ellipsis input'])]
+        (rum/with-key
+          (cmdk/cmdk-block {:initial-input db-id
+                            :sidebar? true
+                            :on-input-change (fn [new-value]
+                                               (reset! *db-id new-value))
+                            :on-input-blur (fn [new-value]
+                                             (state/sidebar-replace-block! [repo db-id block-type]
+                                                                           [repo new-value block-type]))})
+          (str init-key))]
+
+       :page-slide-view
+       (when entity
+         [[:a.page-title {:href (rfe/href :page {:name (str (:block/uuid entity))})}
+           (:block/title entity)]
+          [:div.ml-2.slide.mt-2
+           (slide/slide entity)]])
+
+       :shortcut-settings
+       [[:.flex.items-center (ui/icon "command" {:class "text-md mr-2"}) (t :help/shortcuts)]
+        (shortcut-settings)]
+       :rtc
+       [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) RTC"]
+        (rtc-debug-ui/rtc-debug-ui)]
+
+       :profiler
+       [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) Profiler"]
+        (profiler/profiler)]
+
+       ["" [:span]]))))
 
 
 (defonce *drag-to
 (defonce *drag-to
   (atom nil))
   (atom nil))
@@ -150,7 +158,9 @@
 (rum/defc actions-menu-content
 (rum/defc actions-menu-content
   [db-id idx type collapsed? block-count]
   [db-id idx type collapsed? block-count]
   (let [multi-items? (> block-count 1)
   (let [multi-items? (> block-count 1)
-        menu-item shui/dropdown-menu-item]
+        menu-item shui/dropdown-menu-item
+        block (when (integer? db-id) (db/entity db-id))
+        page? (or (contains? #{:page :contents} type) (ldb/page? block))]
     [:<>
     [:<>
      (menu-item {:on-click #(state/sidebar-remove-block! idx)} (t :right-side-bar/pane-close))
      (menu-item {:on-click #(state/sidebar-remove-block! idx)} (t :right-side-bar/pane-close))
      (when multi-items? (menu-item {:on-click #(state/sidebar-remove-rest! db-id)} (t :right-side-bar/pane-close-others)))
      (when multi-items? (menu-item {:on-click #(state/sidebar-remove-rest! db-id)} (t :right-side-bar/pane-close-others)))
@@ -164,11 +174,10 @@
      (when (and collapsed? multi-items?) [:hr.menu-separator])
      (when (and collapsed? multi-items?) [:hr.menu-separator])
      (when collapsed? (menu-item {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-expand)))
      (when collapsed? (menu-item {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-expand)))
      (when multi-items? (menu-item {:on-click #(state/sidebar-block-set-collapsed-all! false)} (t :right-side-bar/pane-expand-all)))
      (when multi-items? (menu-item {:on-click #(state/sidebar-block-set-collapsed-all! false)} (t :right-side-bar/pane-expand-all)))
-     (when (= type :page) [:hr.menu-separator])
-     (when (= type :page)
-       (let [page  (db/entity db-id)]
-         (menu-item {:on-click (fn [] (route-handler/redirect-to-page! (:block/uuid page)))}
-                    (t :right-side-bar/pane-open-as-page))))]))
+     (when page? [:hr.menu-separator])
+     (when page?
+       (menu-item {:on-click (fn [] (route-handler/redirect-to-page! (:block/uuid block)))}
+                  (t :right-side-bar/pane-open-as-page)))]))
 
 
 (rum/defc drop-indicator
 (rum/defc drop-indicator
   [idx drag-to]
   [idx drag-to]
@@ -190,85 +199,100 @@
   [component _should-update?]
   [component _should-update?]
   component)
   component)
 
 
+(rum/defc sidebar-item-inner
+  [db-id {:keys [repo idx block-type collapsed? drag-from drag-to block-count *db-id init-key]}]
+  (let [[item set-item!] (hooks/use-state nil)]
+    (hooks/use-effect!
+     (fn []
+       (p/let [item (<build-sidebar-item repo idx db-id block-type *db-id init-key)]
+         (set-item! item)))
+     [])
+    (when item
+      [:<>
+       (when (zero? idx) (drop-indicator (dec idx) drag-to))
+       [:div.flex.sidebar-item.content.color-level.rounded-md.shadow-lg
+        {:class [(str "item-type-" (name block-type))
+                 (when collapsed? "collapsed")]}
+        (let [[title component] item]
+          [:div.flex.flex-col.w-full.relative
+           [:.flex.flex-row.justify-between.pr-2.sidebar-item-header.color-level.rounded-t-md
+            {:class         (when collapsed? "rounded-b-md")
+             :draggable     true
+             :on-context-menu (fn [e]
+                                (util/stop e)
+                                (shui/popup-show! e
+                                                  (actions-menu-content db-id idx block-type collapsed? block-count)
+                                                  {:as-dropdown? true
+                                                   :content-props {:on-click (fn [] (shui/popup-hide!))}}))
+             :on-drag-start (fn [event]
+                              (editor-handler/block->data-transfer! (:block/name (db/entity db-id)) event true)
+                              (reset! *drag-from idx))
+             :on-drag-end   (fn [_event]
+                              (when drag-to (state/sidebar-move-block! idx drag-to))
+                              (reset! *drag-to nil)
+                              (reset! *drag-from nil))
+             :on-pointer-up   (fn [event]
+                                (when (= (.-which (.-nativeEvent event)) 2)
+                                  (state/sidebar-remove-block! idx)))}
+
+            [:button.flex.flex-row.p-2.items-center.w-full.overflow-hidden
+             {:aria-expanded (str (not collapsed?))
+              :id            (str "sidebar-panel-header-" idx)
+              :aria-controls (str "sidebar-panel-content-" idx)
+              :on-click      (fn [event]
+                               (util/stop event)
+                               (state/sidebar-block-toggle-collapse! db-id))}
+             [:span.opacity-50.hover:opacity-100.flex.items-center.pr-1
+              (ui/rotating-arrow collapsed?)]
+             [:div.ml-1.font-medium.overflow-hidden.whitespace-nowrap
+              title]]
+            [:.item-actions.flex.items-center
+             (shui/button
+              {:title (t :right-side-bar/pane-more)
+               :class "px-3"
+               :variant :text
+               :on-click #(shui/popup-show!
+                           (.-target %)
+                           (actions-menu-content db-id idx block-type collapsed? block-count)
+                           {:as-dropdown? true
+                            :content-props {:on-click (fn [] (shui/popup-hide!))}})}
+              (ui/icon "dots"))
+
+             (shui/button
+              {:title (t :right-side-bar/pane-close)
+               :variant :text
+               :class "px-3"
+               :on-click #(state/sidebar-remove-block! idx)}
+              (ui/icon "x"))]]
+
+           [:div {:role "region"
+                  :id (str "sidebar-panel-content-" idx)
+                  :aria-labelledby (str "sidebar-panel-header-" idx)
+                  :class           (util/classnames [{:hidden  collapsed?
+                                                      :initial (not collapsed?)
+                                                      :sidebar-panel-content true
+                                                      :px-2    (not (contains? #{:search :shortcut-settings} block-type))}])}
+            (inner-component component (not drag-from))]
+           (when drag-from (drop-area idx))])]
+       (drop-indicator idx drag-to)])))
+
 (rum/defcs sidebar-item < rum/reactive
 (rum/defcs sidebar-item < rum/reactive
   {:init (fn [state] (assoc state
   {:init (fn [state] (assoc state
                             ::db-id (atom (nth (:rum/args state) 2))
                             ::db-id (atom (nth (:rum/args state) 2))
                             ::init-key (random-uuid)))}
                             ::init-key (random-uuid)))}
   [state repo idx db-id block-type block-count]
   [state repo idx db-id block-type block-count]
   (let [drag-from (rum/react *drag-from)
   (let [drag-from (rum/react *drag-from)
-        drag-to (rum/react *drag-to)
-        item (build-sidebar-item repo idx db-id block-type
-                                 (::db-id state)
-                                 (::init-key state))]
-    (when item
-      (let [collapsed? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
-        [:<>
-         (when (zero? idx) (drop-indicator (dec idx) drag-to))
-         [:div.flex.sidebar-item.content.color-level.rounded-md.shadow-lg
-          {:class [(str "item-type-" (name block-type))
-                   (when collapsed? "collapsed")]}
-          (let [[title component] item]
-            [:div.flex.flex-col.w-full.relative
-             [:.flex.flex-row.justify-between.pr-2.sidebar-item-header.color-level.rounded-t-md
-              {:class         (when collapsed? "rounded-b-md")
-               :draggable     true
-               :on-context-menu (fn [e]
-                                  (util/stop e)
-                                  (shui/popup-show! e
-                                                    (actions-menu-content db-id idx block-type collapsed? block-count)
-                                                    {:as-dropdown? true
-                                                     :content-props {:on-click (fn [] (shui/popup-hide!))}}))
-               :on-drag-start (fn [event]
-                                (editor-handler/block->data-transfer! (:block/name (db/entity db-id)) event true)
-                                (reset! *drag-from idx))
-               :on-drag-end   (fn [_event]
-                                (when drag-to (state/sidebar-move-block! idx drag-to))
-                                (reset! *drag-to nil)
-                                (reset! *drag-from nil))
-               :on-pointer-up   (fn [event]
-                                  (when (= (.-which (.-nativeEvent event)) 2)
-                                    (state/sidebar-remove-block! idx)))}
-
-              [:button.flex.flex-row.p-2.items-center.w-full.overflow-hidden
-               {:aria-expanded (str (not collapsed?))
-                :id            (str "sidebar-panel-header-" idx)
-                :aria-controls (str "sidebar-panel-content-" idx)
-                :on-click      (fn [event]
-                                 (util/stop event)
-                                 (state/sidebar-block-toggle-collapse! db-id))}
-               [:span.opacity-50.hover:opacity-100.flex.items-center.pr-1
-                (ui/rotating-arrow collapsed?)]
-               [:div.ml-1.font-medium.overflow-hidden.whitespace-nowrap
-                title]]
-              [:.item-actions.flex.items-center
-               (shui/button
-                {:title (t :right-side-bar/pane-more)
-                 :class "px-3"
-                 :variant :text
-                 :on-click #(shui/popup-show!
-                             (.-target %)
-                             (actions-menu-content db-id idx block-type collapsed? block-count)
-                             {:as-dropdown? true
-                              :content-props {:on-click (fn [] (shui/popup-hide!))}})}
-                (ui/icon "dots"))
-
-               (shui/button
-                {:title (t :right-side-bar/pane-close)
-                 :variant :text
-                 :class "px-3"
-                 :on-click #(state/sidebar-remove-block! idx)}
-                (ui/icon "x"))]]
-
-             [:div {:role "region"
-                    :id (str "sidebar-panel-content-" idx)
-                    :aria-labelledby (str "sidebar-panel-header-" idx)
-                    :class           (util/classnames [{:hidden  collapsed?
-                                                        :initial (not collapsed?)
-                                                        :sidebar-panel-content true
-                                                        :px-2    (not (contains? #{:search :shortcut-settings} block-type))}])}
-              (inner-component component (not drag-from))]
-             (when drag-from (drop-area idx))])]
-         (drop-indicator idx drag-to)]))))
+        drag-to (rum/react *drag-to)]
+    (let [collapsed? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
+      (sidebar-item-inner db-id {:repo repo
+                                 :idx idx
+                                 :block-type block-type
+                                 :collapsed? collapsed?
+                                 :drag-from drag-from
+                                 :drag-to drag-to
+                                 :block-count block-count
+                                 :*db-id (::db-id state)
+                                 :init-key (::init-key state)}))))
 
 
 (defn- get-page
 (defn- get-page
   [match]
   [match]

+ 15 - 16
src/main/frontend/components/rtc/indicator.cljs

@@ -2,12 +2,12 @@
   "RTC state indicator"
   "RTC state indicator"
   (:require [cljs-time.core :as t]
   (:require [cljs-time.core :as t]
             [clojure.pprint :as pprint]
             [clojure.pprint :as pprint]
+            [frontend.common.missionary :as c.m]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.handler.db-based.rtc-flows :as rtc-flows]
             [frontend.handler.db-based.rtc-flows :as rtc-flows]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.common.missionary :as c.m]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [missionary.core :as m]
             [missionary.core :as m]
             [rum.core :as rum]))
             [rum.core :as rum]))
@@ -38,21 +38,20 @@
                (when log
                (when log
                  (swap! *detail-info update k (fn [logs] (take 5 (conj logs log))))))
                  (swap! *detail-info update k (fn [logs] (take 5 (conj logs log))))))
              flow))]
              flow))]
-    (let [canceler (c.m/run-task
-                    (m/join
-                     (constantly nil)
-                     (update-log-task rtc-flows/rtc-download-log-flow :download-logs)
-                     (update-log-task rtc-flows/rtc-upload-log-flow :upload-logs)
-                     (update-log-task rtc-flows/rtc-misc-log-flow :misc-logs)
-                     (m/reduce (fn [_ state]
-                                 (swap! *detail-info assoc
-                                        :pending-local-ops (:unpushed-block-update-count state)
-                                        :graph-uuid (:graph-uuid state)
-                                        :local-tx (:local-tx state)
-                                        :remote-tx (:remote-tx state)
-                                        :rtc-state (if (:rtc-lock state) :open :close)))
-                               rtc-flows/rtc-state-stream-flow))
-                    ::update-detail-info)]
+    (let [canceler (c.m/run-task ::update-detail-info
+                     (m/join
+                      (constantly nil)
+                      (update-log-task rtc-flows/rtc-download-log-flow :download-logs)
+                      (update-log-task rtc-flows/rtc-upload-log-flow :upload-logs)
+                      (update-log-task rtc-flows/rtc-misc-log-flow :misc-logs)
+                      (m/reduce (fn [_ state]
+                                  (swap! *detail-info assoc
+                                         :pending-local-ops (:unpushed-block-update-count state)
+                                         :graph-uuid (:graph-uuid state)
+                                         :local-tx (:local-tx state)
+                                         :remote-tx (:remote-tx state)
+                                         :rtc-state (if (:rtc-lock state) :open :close)))
+                                rtc-flows/rtc-state-stream-flow)))]
       (reset! *update-detail-info-canceler canceler))))
       (reset! *update-detail-info-canceler canceler))))
 (run-task--update-detail-info)
 (run-task--update-detail-info)
 
 

+ 69 - 50
src/main/frontend/components/select.cljs

@@ -14,6 +14,7 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))
@@ -43,6 +44,27 @@
        row]
        row]
       row)))
       row)))
 
 
+(rum/defc search-input
+  [*input {:keys [prompt-key input-default-placeholder input-opts on-input]}]
+  (let [[input set-input!] (hooks/use-state @*input)]
+    (hooks/use-effect!
+     (fn []
+       (reset! *input input)
+       (when (fn? on-input) (on-input input)))
+     [(hooks/use-debounced-value input 100)])
+    [:div.input-wrap
+     {:style {:margin-bottom "-2px"}}
+     [:input.cp__select-input.w-full
+      (merge {:type        "text"
+              :class "!p-1.5"
+              :placeholder (or input-default-placeholder (t prompt-key))
+              :auto-focus  true
+              :value       input
+              :on-change   (fn [e]
+                             (let [v (util/evalue e)]
+                               (set-input! v)))}
+             input-opts)]]))
+
 (rum/defcs ^:large-vars/cleanup-todo select
 (rum/defcs ^:large-vars/cleanup-todo select
   "Provides a select dropdown powered by a fuzzy search. Takes the following options:
   "Provides a select dropdown powered by a fuzzy search. Takes the following options:
    * :items - Vec of things to select from. Assumes a vec of maps with :value key by default. Required option
    * :items - Vec of things to select from. Assumes a vec of maps with :value key by default. Required option
@@ -54,6 +76,7 @@
    * :exact-match-exclude-items - A set of strings that can't be added as a new item. Default is #{}
    * :exact-match-exclude-items - A set of strings that can't be added as a new item. Default is #{}
    * :transform-fn - Optional fn to transform search results given results and current input
    * :transform-fn - Optional fn to transform search results given results and current input
    * :new-case-sensitive? - Boolean to allow new values to be case sensitive
    * :new-case-sensitive? - Boolean to allow new values to be case sensitive
+   * :loading? - whether it's loading the items
    TODO: Describe more options"
    TODO: Describe more options"
   < rum/reactive
   < rum/reactive
   shortcut/disable-all-shortcuts
   shortcut/disable-all-shortcuts
@@ -76,7 +99,7 @@
                  item-cp transform-fn tap-*input-val
                  item-cp transform-fn tap-*input-val
                  multiple-choices? on-apply new-case-sensitive?
                  multiple-choices? on-apply new-case-sensitive?
                  dropdown? show-new-when-not-exact-match? exact-match-exclude-items
                  dropdown? show-new-when-not-exact-match? exact-match-exclude-items
-                 input-container initial-open?]
+                 input-container initial-open? loading?]
           :or {limit 100
           :or {limit 100
                prompt-key :select/default-prompt
                prompt-key :select/default-prompt
                empty-placeholder (fn [_t] [:div])
                empty-placeholder (fn [_t] [:div])
@@ -123,54 +146,50 @@
         input-opts' (if (fn? input-opts) (input-opts (empty? search-result)) input-opts)
         input-opts' (if (fn? input-opts) (input-opts (empty? search-result)) input-opts)
         input-container (or
         input-container (or
                          input-container
                          input-container
-                         [:div.input-wrap
-                          {:style {:margin-bottom "-2px"}}
-                          [:input.cp__select-input.w-full
-                           (merge {:type        "text"
-                                   :class "!p-1.5"
-                                   :placeholder (or input-default-placeholder (t prompt-key))
-                                   :auto-focus  true
-                                   :value       @input
-                                   :on-change   (fn [e]
-                                                  (let [v (util/evalue e)]
-                                                    (reset! input v)
-                                                    (and (fn? on-input) (on-input v))))}
-                                  input-opts')]])
-        results-container [:div
-                           {:class (when (seq search-result) "py-1")}
-                           [:div.item-results-wrap
-                            (ui/auto-complete
-                             search-result
-                             {:grouped? grouped?
-                              :item-render       (or item-cp (fn [result chosen?]
-                                                               (render-item result chosen? multiple-choices? *selected-choices)))
-                              :class             "cp__select-results"
-                              :on-chosen         (fn [raw-chosen e]
-                                                   (reset! input "")
-                                                   (let [chosen (extract-chosen-fn raw-chosen)]
-                                                     (if multiple-choices?
-                                                       (if (selected-choices chosen)
-                                                         (do
-                                                           (swap! *selected-choices disj chosen)
-                                                           (when on-chosen (on-chosen chosen false @*selected-choices e)))
-                                                         (do
-                                                           (swap! *selected-choices conj chosen)
-                                                           (when on-chosen (on-chosen chosen true @*selected-choices e))))
-                                                       (do
-                                                         (when (and close-modal? (not multiple-choices?))
-                                                           (state/close-modal!))
-                                                         (when on-chosen
-                                                           (on-chosen chosen true @*selected-choices e))))))
-                              :empty-placeholder (empty-placeholder t)})]
+                         (search-input input
+                                       {:prompt-key prompt-key
+                                        :input-default-placeholder input-default-placeholder
+                                        :input-opts input-opts'
+                                        :on-input on-input}))
+        results-container-f (fn []
+                              (if loading?
+                                [:div.px-1.py-2
+                                 (ui/loading "Loading ...")]
+                                [:div
+                                 {:class (when (seq search-result) "py-1")}
+                                 [:div.item-results-wrap
+                                  (ui/auto-complete
+                                   search-result
+                                   {:grouped? grouped?
+                                    :item-render       (or item-cp (fn [result chosen?]
+                                                                     (render-item result chosen? multiple-choices? *selected-choices)))
+                                    :class             "cp__select-results"
+                                    :on-chosen         (fn [raw-chosen e]
+                                                         (reset! input "")
+                                                         (let [chosen (extract-chosen-fn raw-chosen)]
+                                                           (if multiple-choices?
+                                                             (if (selected-choices chosen)
+                                                               (do
+                                                                 (swap! *selected-choices disj chosen)
+                                                                 (when on-chosen (on-chosen chosen false @*selected-choices e)))
+                                                               (do
+                                                                 (swap! *selected-choices conj chosen)
+                                                                 (when on-chosen (on-chosen chosen true @*selected-choices e))))
+                                                             (do
+                                                               (when (and close-modal? (not multiple-choices?))
+                                                                 (state/close-modal!))
+                                                               (when on-chosen
+                                                                 (on-chosen chosen true @*selected-choices e))))))
+                                    :empty-placeholder (empty-placeholder t)})]
 
 
-                           (when (and multiple-choices? (fn? on-apply))
-                             [:div.p-4 (ui/button "Apply"
-                                                  {:small? true
-                                                   :on-pointer-down (fn [e]
-                                                                      (util/stop e)
-                                                                      (when @*toggle (@*toggle))
-                                                                      (on-apply selected-choices)
-                                                                      (when close-modal? (state/close-modal!)))})])]]
+                                 (when (and multiple-choices? (fn? on-apply))
+                                   [:div.p-4 (ui/button "Apply"
+                                                        {:small? true
+                                                         :on-pointer-down (fn [e]
+                                                                            (util/stop e)
+                                                                            (when @*toggle (@*toggle))
+                                                                            (on-apply selected-choices)
+                                                                            (when close-modal? (state/close-modal!)))})])]))]
     (when (fn? tap-*input-val)
     (when (fn? tap-*input-val)
       (tap-*input-val input))
       (tap-*input-val input))
     [:div.cp__select
     [:div.cp__select
@@ -180,12 +199,12 @@
      (if dropdown?
      (if dropdown?
        (ui/dropdown
        (ui/dropdown
         (if (fn? input-container) input-container (fn [] input-container))
         (if (fn? input-container) input-container (fn [] input-container))
-        (fn [] results-container)
+        results-container-f
         {:initial-open? initial-open?
         {:initial-open? initial-open?
          :*toggle-fn *toggle})
          :*toggle-fn *toggle})
        [:<>
        [:<>
         (if (fn? input-container) (input-container) input-container)
         (if (fn? input-container) (input-container) input-container)
-        results-container])]))
+        (results-container-f)])]))
 
 
 (defn select-config
 (defn select-config
   "Config that supports multiple types (uses) of this component. To add a new
   "Config that supports multiple types (uses) of this component. To add a new

+ 5 - 6
src/main/frontend/components/selection.cljs

@@ -1,6 +1,7 @@
 (ns frontend.components.selection
 (ns frontend.components.selection
   "Block selection"
   "Block selection"
   (:require [frontend.config :as config]
   (:require [frontend.config :as config]
+            [frontend.db :as db]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
@@ -11,9 +12,9 @@
 (rum/defc action-bar
 (rum/defc action-bar
   [& {:keys [on-cut on-copy selected-blocks hide-dots? button-border?]
   [& {:keys [on-cut on-copy selected-blocks hide-dots? button-border?]
       :or {on-cut #(editor-handler/cut-selection-blocks true)}}]
       :or {on-cut #(editor-handler/cut-selection-blocks true)}}]
-  (let [on-copy (if (and selected-blocks (nil? on-copy))
-                  #(editor-handler/copy-selection-blocks true {:selected-blocks selected-blocks
-                                                               :page-title-only? true})
+  (let [selected-blocks (map (fn [block] (if (number? block) (db/entity block) block)) selected-blocks)
+        on-copy (if (and selected-blocks (nil? on-copy))
+                  #(editor-handler/copy-selection-blocks true {:selected-blocks selected-blocks})
                   (or on-copy #(editor-handler/copy-selection-blocks true)))
                   (or on-copy #(editor-handler/copy-selection-blocks true)))
         button-opts {:variant :outline
         button-opts {:variant :outline
                      :size :sm
                      :size :sm
@@ -80,8 +81,6 @@
                                                        [:div {:on-click #(shui/popup-hide! id)
                                                        [:div {:on-click #(shui/popup-hide! id)
                                                               :data-keep-selection true}
                                                               :data-keep-selection true}
                                                         ((state/get-component :selection/context-menu))])
                                                         ((state/get-component :selection/context-menu))])
-                                                     {:on-before-hide state/dom-clear-selection!
-                                                      :on-after-hide state/state-clear-selection!
-                                                      :content-props {:class "w-[280px] ls-context-menu-content"}
+                                                     {:content-props {:class "w-[280px] ls-context-menu-content"}
                                                       :as-dropdown? true})))
                                                       :as-dropdown? true})))
          (ui/icon "dots" {:size 13}))))]))
          (ui/icon "dots" {:size 13}))))]))

+ 12 - 1
src/main/frontend/components/table.css

@@ -30,7 +30,18 @@
   }
   }
 
 
   .ls-table-row {
   .ls-table-row {
-    @apply min-h-[33px];
+    @apply h-[33px] min-h-[33px] max-h-[33px];
+    div, span, a {
+      @apply whitespace-nowrap;
+    }
+
+    .table-block-title, .block-head-wrap a {
+      @apply text-ellipsis overflow-hidden;
+    }
+
+    .multi-values {
+      @apply !flex-nowrap;
+    }
   }
   }
 
 
   .ls-table-header {
   .ls-table-header {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 468 - 448
src/main/frontend/components/views.cljs


+ 8 - 0
src/main/frontend/components/views.css

@@ -3,3 +3,11 @@
         width: 107px;
         width: 107px;
     }
     }
 }
 }
+
+.group-list-view div[data-testid='virtuoso-item-list'] {
+    @apply flex flex-col gap-2;
+}
+
+.group-list-view div[data-testid='virtuoso-item-list'] div[data-testid='virtuoso-item-list'] {
+    @apply gap-0;
+}

+ 21 - 19
src/main/frontend/components/whiteboard.cljs

@@ -4,8 +4,11 @@
             [frontend.components.onboarding.quick-tour :as quick-tour]
             [frontend.components.onboarding.quick-tour :as quick-tour]
             [frontend.components.page :as page]
             [frontend.components.page :as page]
             [frontend.components.reference :as reference]
             [frontend.components.reference :as reference]
+            [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
+            [frontend.db.async :as db-async]
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
@@ -14,14 +17,12 @@
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.common.util :as common-util]
+            [logseq.shui.hooks :as hooks]
+            [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]
             [rum.core :as rum]
-            [shadow.loader :as loader]
-            [frontend.config :as config]
-            [frontend.db.async :as db-async]
-            [logseq.common.util :as common-util]
-            [frontend.db :as db]
-            [logseq.shui.ui :as shui]))
+            [shadow.loader :as loader]))
 
 
 (defonce tldraw-loaded? (atom false))
 (defonce tldraw-loaded? (atom false))
 (rum/defc tldraw-app < rum/reactive
 (rum/defc tldraw-app < rum/reactive
@@ -36,21 +37,22 @@
     (when draw-component
     (when draw-component
       (draw-component page-uuid shape-id))))
       (draw-component page-uuid shape-id))))
 
 
-(rum/defc tldraw-preview < rum/reactive
-  {:init (fn [state]
-           (p/let [_ (loader/load :tldraw)]
-             (reset! tldraw-loaded? true))
-           (let [page-uuid (first (:rum/args state))]
-             (db-async/<get-block (state/get-current-repo) page-uuid))
-           state)}
+(rum/defc tldraw-preview
   [page-uuid]
   [page-uuid]
   (when page-uuid
   (when page-uuid
-    (let [loaded? (rum/react tldraw-loaded?)
-          tldr (whiteboard-handler/get-page-tldr page-uuid)
-          generate-preview (when loaded?
-                             (resolve 'frontend.extensions.tldraw/generate-preview))]
-      (when (and generate-preview (not (state/sub-async-query-loading page-uuid)))
-        (generate-preview tldr)))))
+    (let [[loading? set-loading!] (hooks/use-state true)]
+      (hooks/use-effect!
+       (fn []
+         (p/do!
+          (loader/load :tldraw)
+          (db-async/<get-block (state/get-current-repo) page-uuid)
+          (set-loading! false)))
+       [])
+      (when-not loading?
+        (let [tldr (whiteboard-handler/get-page-tldr page-uuid)
+              generate-preview (resolve 'frontend.extensions.tldraw/generate-preview)]
+          (when generate-preview
+            (generate-preview tldr)))))))
 
 
 ;; TODO: move to frontend.components.reference
 ;; TODO: move to frontend.components.reference
 (rum/defcs references-count < rum/reactive db-mixins/query
 (rum/defcs references-count < rum/reactive db-mixins/query

+ 3 - 4
src/main/frontend/db.cljs

@@ -35,16 +35,15 @@
   get-files-blocks get-files-full get-journals-length
   get-files-blocks get-files-full get-journals-length
   get-latest-journals get-page get-case-page get-page-alias-names
   get-latest-journals get-page get-case-page get-page-alias-names
   get-page-blocks-count get-page-blocks-no-cache get-page-file get-page-format
   get-page-blocks-count get-page-blocks-no-cache get-page-file get-page-format
-  get-referenced-blocks get-page-referenced-blocks-full get-page-referenced-pages
-  get-all-pages get-pages-relation get-pages-that-mentioned-page
-  journal-page? page? page-alias-set sub-block
+  get-referenced-blocks get-page-referenced-blocks-full
+  journal-page? page? page-alias-set sub-block sub-entity
   page-empty? page-exists? get-alias-source-page
   page-empty? page-exists? get-alias-source-page
   has-children? whiteboard-page?
   has-children? whiteboard-page?
   get-namespace-pages get-all-namespace-relation]
   get-namespace-pages get-all-namespace-relation]
 
 
  [frontend.db.react
  [frontend.db.react
   get-current-page
   get-current-page
-  remove-q! remove-query-component! add-q! add-query-component! clear-query-state!
+  remove-query-component! add-q! add-query-component! clear-query-state!
   q
   q
   query-state component->query-key set-new-result!])
   query-state component->query-key set-new-result!])
 
 

+ 60 - 95
src/main/frontend/db/async.cljs

@@ -17,7 +17,6 @@
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
-            [logseq.db.frontend.rules :as rules]
             [promesa.core :as p]))
             [promesa.core :as p]))
 
 
 (def <q db-async-util/<q)
 (def <q db-async-util/<q)
@@ -50,7 +49,7 @@
     (p/let [templates (<get-all-templates repo)]
     (p/let [templates (<get-all-templates repo)]
       (get templates name))))
       (get templates name))))
 
 
-(defn <db-based-get-all-properties
+(defn db-based-get-all-properties
   "Return seq of all property names except for private built-in properties."
   "Return seq of all property names except for private built-in properties."
   [graph & {:keys [remove-built-in-property? remove-non-queryable-built-in-property?]
   [graph & {:keys [remove-built-in-property? remove-non-queryable-built-in-property?]
             :or {remove-built-in-property? true
             :or {remove-built-in-property? true
@@ -79,7 +78,7 @@
   [& {:as opts}]
   [& {:as opts}]
   (when-let [graph (state/get-current-repo)]
   (when-let [graph (state/get-current-repo)]
     (if (config/db-based-graph? graph)
     (if (config/db-based-graph? graph)
-      (<db-based-get-all-properties graph opts)
+      (db-based-get-all-properties graph opts)
       (p/let [properties (file-async/<file-based-get-all-properties graph)
       (p/let [properties (file-async/<file-based-get-all-properties graph)
               hidden-properties (set (map name (property-util/hidden-properties)))]
               hidden-properties (set (map name (property-util/hidden-properties)))]
         (remove #(hidden-properties (:block/title %)) properties)))))
         (remove #(hidden-properties (:block/title %)) properties)))))
@@ -90,109 +89,86 @@
   (when-not (config/db-based-graph? graph)
   (when-not (config/db-based-graph? graph)
     (file-async/<get-file-based-property-values graph property)))
     (file-async/<get-file-based-property-values graph property)))
 
 
-(defn <get-block-property-values
-  "For db graphs, returns property value ids for given property db-ident.
-   Separate from file version because values are lazy loaded"
-  [graph property-id]
-  (let [default-value-id (:db/id (:logseq.property/default-value (db/entity property-id)))
-        empty-id (:db/id (db/entity :logseq.property/empty-placeholder))]
-    (p/let [result (<q graph {:transact-db? false}
-                       '[:find [?v ...]
-                         :in $ ?property-id ?empty-id
-                         :where
-                         [?b ?property-id ?v]
-                         [(not= ?v ?empty-id)]]
-                       property-id
-                       empty-id)]
-      (if default-value-id
-        ;; put default value the first
-        (concat [default-value-id] result)
-        result))))
+(defn <get-property-values
+  "For db graphs, returns a vec of property value maps for given property
+  db-ident.  The map contains a :label key which can be a string or number (for
+  query builder) and a :value key which contains the entity or scalar property value"
+  [property-id & {:as opts}]
+  (when property-id
+    (state/<invoke-db-worker :thread-api/get-property-values (state/get-current-repo)
+                             (assoc opts :property-ident property-id))))
 
 
-(comment
-  (defn <get-block-property-value-entity
-    [graph property-id value]
-    (p/let [result (<q graph {}
-                       '[:find [(pull ?vid [*]) ...]
-                         :in $ ?property-id ?value
-                         :where
-                         [?b ?property-id ?vid]
-                         [(not= ?vid :logseq.property/empty-placeholder)]
-                         (or
-                          [?vid :logseq.property/value ?value]
-                          [?vid :block/title ?value])]
-                       property-id
-                       value)]
-      (db/entity (:db/id (first result))))))
-
-;; TODO: batch queries for better performance and UX
 (defn <get-block
 (defn <get-block
-  [graph name-or-uuid & {:keys [children? nested-children?]
-                         :or {children? true
-                              nested-children? false}
-                         :as opts}]
-  (let [name' (str name-or-uuid)
-        *async-queries (:db/async-queries @state/state)
-        async-requested? (get @*async-queries [name' opts])
+  [graph id-uuid-or-name & {:keys [children? nested-children? skip-transact? skip-refresh? children-only? properties]
+                            :or {children? true}
+                            :as opts}]
+
+  ;; (prn :debug :<get-block id-uuid-or-name)
+  ;; (js/console.trace)
+  (let [name' (str id-uuid-or-name)
+        opts (assoc opts :children? children?)
         e (cond
         e (cond
-            (number? name-or-uuid)
-            (db/entity name-or-uuid)
+            (number? id-uuid-or-name)
+            (db/entity id-uuid-or-name)
             (util/uuid-string? name')
             (util/uuid-string? name')
             (db/entity [:block/uuid (uuid name')])
             (db/entity [:block/uuid (uuid name')])
             :else
             :else
             (db/get-page name'))
             (db/get-page name'))
         id (or (and (:block/uuid e) (str (:block/uuid e)))
         id (or (and (:block/uuid e) (str (:block/uuid e)))
                (and (util/uuid-string? name') name')
                (and (util/uuid-string? name') name')
-               name-or-uuid)]
-    (if (or (:block.temp/fully-loaded? e) async-requested?)
-      e
-      (do
-        (swap! *async-queries assoc [name' opts] true)
-        (state/update-state! :db/async-query-loading (fn [s] (conj s name')))
-        (p/let [{:keys [properties block children] :as result'}
-                (state/<invoke-db-worker :thread-api/get-block-and-children
-                                         graph id {:children? children?
-                                                   :nested-children? nested-children?})
-                conn (db/get-db graph false)
-                block-and-children (concat properties [block] children)
-                _ (d/transact! conn block-and-children)
-                affected-keys (->> (keep :db/id block-and-children)
-                                   (map #(vector :frontend.worker.react/block %)))]
-          (react/refresh-affected-queries! graph affected-keys)
-          (state/update-state! :db/async-query-loading (fn [s] (disj s name')))
-          (if children?
-            block
-            result'))))))
+               id-uuid-or-name)]
+    (cond
+      (and (:block.temp/fully-loaded? e) ; children may not be fully loaded
+           (not children-only?)
+           (not nested-children?)
+           (not (some #{:block.temp/refs-count} properties)))
+      (p/promise e)
+
+      :else
+      (p/let [result (state/<invoke-db-worker :thread-api/get-blocks graph
+                                              [{:id id :opts opts}])
+              {:keys [block children]} (first result)]
+        (when-not skip-transact?
+          (let [conn (db/get-db graph false)
+                block-and-children (if block (cons block children) children)
+                affected-keys [[:frontend.worker.react/block (:db/id block)]]
+                tx-data (remove (fn [b] (:block.temp/fully-loaded? (db/entity (:db/id b)))) block-and-children)]
+            (when (seq tx-data) (d/transact! conn tx-data))
+            (when-not skip-refresh?
+              (react/refresh-affected-queries! graph affected-keys))))
+
+        (if children-only? children block)))))
+
+(defn <get-blocks
+  [graph ids* & {:as opts}]
+  (let [ids (remove (fn [id] (:block.temp/fully-loaded? (db/entity id))) ids*)]
+    (when (seq ids)
+      (p/let [result (state/<invoke-db-worker :thread-api/get-blocks graph
+                                              (map (fn [id]
+                                                     {:id id :opts (assoc opts :children? false)})
+                                                   ids))]
+        (let [conn (db/get-db graph false)
+              result' (map :block result)]
+          (when (seq result')
+            (let [result'' (map (fn [b] (assoc b :block.temp/fully-loaded? true)) result')]
+              (d/transact! conn result'')))
+          result')))))
 
 
 (defn <get-block-parents
 (defn <get-block-parents
   [graph id depth]
   [graph id depth]
   (assert (integer? id))
   (assert (integer? id))
-  (when-let [block-id (:block/uuid (db/entity graph id))]
-    (state/update-state! :db/async-query-loading (fn [s] (conj s (str block-id "-parents"))))
+  (when (:block/uuid (db/entity graph id))
     (p/let [result (state/<invoke-db-worker :thread-api/get-block-parents graph id depth)
     (p/let [result (state/<invoke-db-worker :thread-api/get-block-parents graph id depth)
             conn (db/get-db graph false)
             conn (db/get-db graph false)
             _ (d/transact! conn result)]
             _ (d/transact! conn result)]
-      (state/update-state! :db/async-query-loading (fn [s] (disj s (str block-id "-parents"))))
       result)))
       result)))
 
 
-(defn <get-page-all-blocks
-  [page-name]
-  (when-let [page (some-> page-name (db-model/get-page))]
-    (p/let [result (state/<invoke-db-worker :thread-api/get-block-and-children
-                                            (state/get-current-repo)
-                                            (:block/uuid page)
-                                            {:children? true
-                                             :nested-children? false})]
-      (:children result))))
-
 (defn <get-block-refs
 (defn <get-block-refs
   [graph eid]
   [graph eid]
   (assert (integer? eid))
   (assert (integer? eid))
-  (state/update-state! :db/async-query-loading (fn [s] (conj s (str eid "-refs"))))
   (p/let [result (state/<invoke-db-worker :thread-api/get-block-refs graph eid)
   (p/let [result (state/<invoke-db-worker :thread-api/get-block-refs graph eid)
           conn (db/get-db graph false)
           conn (db/get-db graph false)
           _ (d/transact! conn result)]
           _ (d/transact! conn result)]
-    (state/update-state! :db/async-query-loading (fn [s] (disj s (str eid "-refs"))))
     result))
     result))
 
 
 (defn <get-block-refs-count
 (defn <get-block-refs-count
@@ -271,25 +247,14 @@
 
 
 (defn <get-tag-pages
 (defn <get-tag-pages
   [graph tag-id]
   [graph tag-id]
-  (<q graph {:transact-db? true}
-      '[:find [(pull ?page [:db/id :block/uuid :block/name :block/title :block/created-at :block/updated-at])]
+  (<q graph {:transact-db? false}
+      '[:find [(pull ?page [:db/id :block/uuid :block/name :block/title :block/created-at :block/updated-at]) ...]
         :in $ ?tag-id
         :in $ ?tag-id
         :where
         :where
         [?page :block/tags ?tag-id]
         [?page :block/tags ?tag-id]
         [?page :block/name]]
         [?page :block/name]]
       tag-id))
       tag-id))
 
 
-(defn <get-property-objects
-  [graph property-ident]
-  (<q graph {:transact-db? true}
-      '[:find [(pull ?b [*]) ...]
-        :in $ % ?prop
-        :where
-        (has-property-or-default-value? ?b ?prop)]
-      (rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value]
-                           {:deps rules/rules-dependencies})
-      property-ident))
-
 (defn <get-tag-objects
 (defn <get-tag-objects
   [graph class-id]
   [graph class-id]
   (let [class-children (db-model/get-structured-children graph class-id)
   (let [class-children (db-model/get-structured-children graph class-id)

+ 4 - 3
src/main/frontend/db/async/util.cljs

@@ -12,9 +12,10 @@
   (assert (not-any? fn? inputs) "Async query inputs can't include fns because fn can't be serialized")
   (assert (not-any? fn? inputs) "Async query inputs can't include fns because fn can't be serialized")
   (let [*async-queries (:db/async-queries @state/state)
   (let [*async-queries (:db/async-queries @state/state)
         async-requested? (get @*async-queries [inputs opts])]
         async-requested? (get @*async-queries [inputs opts])]
-    (if async-requested?
-      (let [db (db-conn/get-db graph)]
-        (apply d/q (first inputs) db (rest inputs)))
+    (if (and async-requested? transact-db?)
+      (p/promise
+       (let [db (db-conn/get-db graph)]
+         (apply d/q (first inputs) db (rest inputs))))
       (p/let [result (state/<invoke-db-worker :thread-api/q graph inputs)]
       (p/let [result (state/<invoke-db-worker :thread-api/q graph inputs)]
         (swap! *async-queries assoc [inputs opts] true)
         (swap! *async-queries assoc [inputs opts] true)
         (when result
         (when result

+ 39 - 147
src/main/frontend/db/model.cljs

@@ -7,6 +7,7 @@
             [clojure.walk :as walk]
             [clojure.walk :as walk]
             [datascript.core :as d]
             [datascript.core :as d]
             [frontend.common.file-based.db :as common-file-db]
             [frontend.common.file-based.db :as common-file-db]
+            [frontend.common.graph-view :as graph-view]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.date :as date]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
@@ -17,9 +18,11 @@
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.rules :as rules]
-            [logseq.graph-parser.db :as gp-db]))
+            [logseq.graph-parser.db :as gp-db]
+            [logseq.shui.hooks :as hooks]))
 
 
 ;; TODO: extract to specific models and move data transform logic to the
 ;; TODO: extract to specific models and move data transform logic to the
 ;; corresponding handlers.
 ;; corresponding handlers.
@@ -53,28 +56,11 @@
     :block/heading-level
     :block/heading-level
     :block/file
     :block/file
     :logseq.property/parent
     :logseq.property/parent
-    {:block/page [:db/id :block/name :block/title :block/journal-day]}
+    {:block/page [:db/id :block/name :block/title :block/uuid :block/journal-day :block/type]}
     {:block/_parent ...}])
     {:block/_parent ...}])
 
 
 (def hidden-page? ldb/hidden?)
 (def hidden-page? ldb/hidden?)
 
 
-(defn get-all-tagged-pages
-  [repo]
-  (d/q '[:find ?page ?tag
-         :where
-         [?page :block/tags ?tag]]
-       (conn/get-db repo)))
-
-(defn get-all-pages
-  [repo]
-  (when-let [db (conn/get-db repo)]
-    (ldb/get-all-pages db)))
-
-(defn get-all-page-titles
-  [repo]
-  (->> (get-all-pages repo)
-       (map :block/title)))
-
 (defn get-alias-source-page
 (defn get-alias-source-page
   "return the source page of an alias"
   "return the source page of an alias"
   [repo alias-id]
   [repo alias-id]
@@ -214,10 +200,7 @@ independent of format as format specific heading characters are stripped"
 
 
 (defn page-alias-set
 (defn page-alias-set
   [repo-url page-id]
   [repo-url page-id]
-  (->>
-   (ldb/get-block-alias (conn/get-db repo-url) page-id)
-   (set)
-   (set/union #{page-id})))
+  (ldb/page-alias-set (conn/get-db repo-url) page-id))
 
 
 (defn get-page-alias-names
 (defn get-page-alias-names
   [repo page-id]
   [repo page-id]
@@ -244,17 +227,34 @@ independent of format as format specific heading characters are stripped"
 (def sort-by-order ldb/sort-by-order)
 (def sort-by-order ldb/sort-by-order)
 
 
 (defn sub-block
 (defn sub-block
-  [id]
+  "Used together with rum/reactive db-mixins/query"
+  [id & {:keys [ref?]
+         :or {ref? false}}]
   (when-let [repo (state/get-current-repo)]
   (when-let [repo (state/get-current-repo)]
     (when id
     (when id
       (let [ref (react/q repo [:frontend.worker.react/block id]
       (let [ref (react/q repo [:frontend.worker.react/block id]
                          {:query-fn (fn [_]
                          {:query-fn (fn [_]
-                                      (let [e (db-utils/entity id)]
-                                        [e (:block/tx-id e)]))}
-                         nil)
-            e (-> ref react first)]
-        (when-let [id (:db/id e)]
-          (db-utils/entity id))))))
+                                      (db-utils/entity id))}
+                         nil)]
+        (if ref? ref
+            (let [e (-> ref react)]
+              (when-let [id (:db/id e)]
+                (db-utils/entity id))))))))
+
+(defn sub-entity
+  "Used for react function components"
+  [entity* watch-id]
+  (let [id (:db/id entity*)
+        *ref (sub-block id {:ref? true})
+        [entity set-entity!] (hooks/use-state @*ref)]
+    (add-watch *ref watch-id (fn [_ _ _ new-value]
+                               (set-entity! new-value)))
+    (hooks/use-effect!
+     (fn []
+       #(remove-watch *ref watch-id))
+     [])
+
+    [entity set-entity!]))
 
 
 (defn sort-by-order-recursive
 (defn sort-by-order-recursive
   [form]
   [form]
@@ -482,9 +482,9 @@ independent of format as format specific heading characters are stripped"
        first))))
        first))))
 
 
 (defn get-page
 (defn get-page
-  [page-name-or-uuid]
-  (when page-name-or-uuid
-    (ldb/get-page (conn/get-db) page-name-or-uuid)))
+  [page-id-name-or-uuid]
+  (when page-id-name-or-uuid
+    (ldb/get-page (conn/get-db) page-id-name-or-uuid)))
 
 
 (defn get-case-page
 (defn get-case-page
   [page-name-or-uuid]
   [page-name-or-uuid]
@@ -554,60 +554,14 @@ independent of format as format specific heading characters are stripped"
   ([n]
   ([n]
    (get-latest-journals (state/get-current-repo) n))
    (get-latest-journals (state/get-current-repo) n))
   ([repo-url n]
   ([repo-url n]
-   (when (conn/get-db repo-url)
-     (let [date (js/Date.)
-           _ (.setDate date (- (.getDate date) (dec n)))
-           today (date-time-util/date->int (js/Date.))]
-       (->>
-        (react/q repo-url [:frontend.worker.react/journals] {:use-cache? false}
-                 '[:find [(pull ?page [*]) ...]
-                   :in $ ?today
-                   :where
-                   [?page :block/name ?page-name]
-                   [?page :block/journal-day ?journal-day]
-                   [(<= ?journal-day ?today)]]
-                 today)
-        (react)
-        (sort-by :block/journal-day)
-        (reverse)
-        (take n))))))
-
-;; get pages that this page referenced
-(defn get-page-referenced-pages
-  [repo page-id]
-  (when-let [db (conn/get-db repo)]
-    (let [pages (page-alias-set repo page-id)
-          ref-pages (d/q
-                     '[:find [?ref-page ...]
-                       :in $ ?pages
-                       :where
-                       [(untuple ?pages) [?page ...]]
-                       [?block :block/page ?page]
-                       [?block :block/refs ?ref-page]]
-                     db
-                     pages)]
-      ref-pages)))
+   (when-let [db (conn/get-db repo-url)]
+     (take n (ldb/get-latest-journals db)))))
 
 
 ;; get pages who mentioned this page
 ;; get pages who mentioned this page
 (defn get-pages-that-mentioned-page
 (defn get-pages-that-mentioned-page
   [repo page-id include-journals?]
   [repo page-id include-journals?]
-  (when (conn/get-db repo)
-    (let [pages (page-alias-set repo page-id)
-          mentioned-pages (->>
-                           (mapcat
-                            (fn [id]
-                              (let [page (db-utils/entity repo id)]
-                                (->> (:block/_refs page)
-                                     (keep (fn [ref]
-                                             (if (ldb/page? ref)
-                                               page
-                                               (:block/page ref)))))))
-                            pages)
-                           (util/distinct-by :db/id))]
-      (keep (fn [page]
-              (when-not (and (not include-journals?) (ldb/journal? page))
-                (:db/id page)))
-            mentioned-pages))))
+  (when-let [db (conn/get-db repo)]
+    (graph-view/get-pages-that-mentioned-page db page-id include-journals?)))
 
 
 (defn get-page-referenced-blocks-full
 (defn get-page-referenced-blocks-full
   ([page-id]
   ([page-id]
@@ -822,15 +776,7 @@ independent of format as format specific heading characters are stripped"
 
 
 (defn get-structured-children
 (defn get-structured-children
   [repo eid]
   [repo eid]
-  (->>
-   (d/q '[:find [?children ...]
-          :in $ ?parent %
-          :where
-          (parent ?parent ?children)]
-        (conn/get-db repo)
-        eid
-        (:parent rules/rules))
-   (remove #{eid})))
+  (db-class/get-structured-children (conn/get-db repo) eid))
 
 
 (defn get-class-objects
 (defn get-class-objects
   [repo class-id]
   [repo class-id]
@@ -845,34 +791,9 @@ independent of format as format specific heading characters are stripped"
        (:block/_tags class))
        (:block/_tags class))
      (remove ldb/hidden?))))
      (remove ldb/hidden?))))
 
 
-(defn sub-class-objects
-  [repo class-id]
-  (when class-id
-    (-> (react/q repo [:frontend.worker.react/objects class-id]
-                 {:query-fn (fn [_] (get-class-objects repo class-id))}
-                 nil)
-        react)))
-
-(defn get-property-related-objects
-  [repo property-id]
-  (when-let [property (db-utils/entity repo property-id)]
-    (->> (d/q '[:find [?b ...]
-                :in $ % ?prop
-                :where
-                (has-property-or-default-value? ?b ?prop)]
-              (conn/get-db repo)
-              (rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value]
-                                   {:deps rules/rules-dependencies})
-              (:db/ident property))
-         (map #(db-utils/entity repo %))
-         (remove ldb/hidden?))))
-
 (defn get-all-namespace-relation
 (defn get-all-namespace-relation
   [repo]
   [repo]
-  (d/q '[:find ?page ?parent
-         :where
-         [?page :block/namespace ?parent]]
-       (conn/get-db repo)))
+  (ldb/get-all-namespace-relation (conn/get-db repo)))
 
 
 (defn get-all-namespace-parents
 (defn get-all-namespace-parents
   [repo]
   [repo]
@@ -881,35 +802,6 @@ independent of format as format specific heading characters are stripped"
          (map (fn [[_ ?parent]]
          (map (fn [[_ ?parent]]
                 (db-utils/entity db ?parent))))))
                 (db-utils/entity db ?parent))))))
 
 
-;; Ignore files with empty blocks for now
-(defn get-pages-relation
-  [repo with-journal?]
-  (when-let [db (conn/get-db repo)]
-    (if (config/db-based-graph?)
-      (let [q (if with-journal?
-                '[:find ?p ?ref-page
-                  :where
-                  [?block :block/page ?p]
-                  [?block :block/refs ?ref-page]]
-                '[:find ?p ?ref-page
-                  :where
-                  [?block :block/page ?p]
-                  [?p :block/tags]
-                  (not [?p :block/tags :logseq.class/Journal])
-                  [?block :block/refs ?ref-page]])]
-        (d/q q db))
-      (let [q (if with-journal?
-                '[:find ?p ?ref-page
-                  :where
-                  [?block :block/page ?p]
-                  [?block :block/refs ?ref-page]]
-                '[:find ?p ?ref-page
-                  :where
-                  [?block :block/page ?p]
-                  (not [?p :block/type "journal"])
-                  [?block :block/refs ?ref-page]])]
-        (d/q q db)))))
-
 (defn get-namespace-pages
 (defn get-namespace-pages
   "Accepts both sanitized and unsanitized namespaces"
   "Accepts both sanitized and unsanitized namespaces"
   [repo namespace]
   [repo namespace]

+ 5 - 4
src/main/frontend/db/query_react.cljs

@@ -3,18 +3,18 @@
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [clojure.walk :as walk]
             [clojure.walk :as walk]
             [frontend.config :as config]
             [frontend.config :as config]
+            [frontend.date :as date]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.db.react :as react]
             [frontend.db.react :as react]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
             [frontend.extensions.sci :as sci]
             [frontend.extensions.sci :as sci]
             [frontend.state :as state]
             [frontend.state :as state]
-            [logseq.db.frontend.inputs :as db-inputs]
-            [logseq.common.util.page-ref :as page-ref]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.date :as date]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
-            [logseq.db :as ldb]))
+            [logseq.common.util.page-ref :as page-ref]
+            [logseq.db :as ldb]
+            [logseq.db.frontend.inputs :as db-inputs]))
 
 
 (defn resolve-input
 (defn resolve-input
   "Wrapper around db-inputs/resolve-input which provides editor-specific state"
   "Wrapper around db-inputs/resolve-input which provides editor-specific state"
@@ -100,6 +100,7 @@
     (pprint "================")
     (pprint "================")
     (pprint "Use the following to debug your datalog queries:")
     (pprint "Use the following to debug your datalog queries:")
     (pprint query')
     (pprint query')
+
     (let [query (resolve-query query)
     (let [query (resolve-query query)
           repo (or repo (state/get-current-repo))
           repo (or repo (state/get-current-repo))
           db (conn/get-db repo)
           db (conn/get-db repo)

+ 21 - 11
src/main/frontend/db/react.cljs

@@ -42,12 +42,13 @@
   (reset! query-state {}))
   (reset! query-state {}))
 
 
 (defn add-q!
 (defn add-q!
-  [k query inputs result-atom transform-fn query-fn inputs-fn]
+  [k query inputs result-atom transform-fn query-fn async-query-fn inputs-fn]
   (swap! query-state assoc k {:query query
   (swap! query-state assoc k {:query query
                               :inputs inputs
                               :inputs inputs
                               :result result-atom
                               :result result-atom
                               :transform-fn transform-fn
                               :transform-fn transform-fn
                               :query-fn query-fn
                               :query-fn query-fn
+                              :async-query-fn async-query-fn
                               :inputs-fn inputs-fn})
                               :inputs-fn inputs-fn})
   result-atom)
   result-atom)
 
 
@@ -85,14 +86,22 @@
     (:result result)))
     (:result result)))
 
 
 (defn- <q-aux
 (defn- <q-aux
-  [repo db query-fn inputs-fn k query inputs]
+  [repo db query-fn async-query-fn inputs-fn k query inputs built-in-query?]
   (let [kv? (and (vector? k) (= :kv (second k)))
   (let [kv? (and (vector? k) (= :kv (second k)))
-        journals? (and (vector? k) (= :frontend.worker.react/journals (last k)))
-        q (if (or journals? util/node-test?)
+        q (if util/node-test?
             (fn [query inputs] (apply d/q query db inputs))
             (fn [query inputs] (apply d/q query db inputs))
-            (fn [query inputs] (apply db-async-util/<q repo {} (cons query inputs))))]
-    (when (or query-fn query kv?)
+            (fn [query inputs]
+              (let [q-f #(apply db-async-util/<q repo {} (cons query inputs))]
+                (if built-in-query?
+                  ;; delay built-in-queries to not block journal rendering
+                  (p/let [_ (p/delay 100)]
+                    (q-f))
+                  (q-f)))))]
+    (when (or query-fn async-query-fn query kv?)
       (cond
       (cond
+        async-query-fn
+        (async-query-fn)
+
         query-fn
         query-fn
         (query-fn db nil)
         (query-fn db nil)
 
 
@@ -110,7 +119,8 @@
         (q query nil)))))
         (q query nil)))))
 
 
 (defn q
 (defn q
-  [repo k {:keys [use-cache? transform-fn query-fn inputs-fn disable-reactive? return-promise?]
+  [repo k {:keys [use-cache? transform-fn query-fn async-query-fn inputs-fn
+                  disable-reactive? return-promise? built-in-query?]
            :or {use-cache? true
            :or {use-cache? true
                 transform-fn identity}} query & inputs]
                 transform-fn identity}} query & inputs]
   ;; {:pre [(s/valid? :frontend.worker.react/block k)]}
   ;; {:pre [(s/valid? :frontend.worker.react/block k)]}
@@ -125,9 +135,9 @@
         (if (and use-cache? result-atom)
         (if (and use-cache? result-atom)
           result-atom
           result-atom
           (let [result-atom (or result-atom (atom nil))
           (let [result-atom (or result-atom (atom nil))
-                p-or-value (<q-aux repo db query-fn inputs-fn k query inputs)]
+                p-or-value (<q-aux repo db query-fn async-query-fn inputs-fn k query inputs built-in-query?)]
             (when-not disable-reactive?
             (when-not disable-reactive?
-              (add-q! k query inputs result-atom transform-fn query-fn inputs-fn))
+              (add-q! k query inputs result-atom transform-fn query-fn async-query-fn inputs-fn))
             (cond
             (cond
               return-promise?
               return-promise?
               p-or-value
               p-or-value
@@ -159,9 +169,9 @@
         (ldb/get-page (conn/get-db) page)))))
         (ldb/get-page (conn/get-db) page)))))
 
 
 (defn- execute-query!
 (defn- execute-query!
-  [graph db k {:keys [query inputs transform-fn query-fn inputs-fn result]
+  [graph db k {:keys [query inputs transform-fn query-fn async-query-fn inputs-fn result built-in-query?]
                :or {transform-fn identity}}]
                :or {transform-fn identity}}]
-  (p/let [p-or-value (<q-aux graph db query-fn inputs-fn k query inputs)
+  (p/let [p-or-value (<q-aux graph db query-fn async-query-fn inputs-fn k query inputs built-in-query?)
           result' (transform-fn p-or-value)]
           result' (transform-fn p-or-value)]
     (when-not (= result' result)
     (when-not (= result' result)
       (set-new-result! k result'))))
       (set-new-result! k result'))))

+ 1 - 6
src/main/frontend/db/restore.cljs

@@ -28,9 +28,4 @@
 
 
     (state/pub-event! [:graph/restored repo])
     (state/pub-event! [:graph/restored repo])
     (state/set-state! :graph/loading? false)
     (state/set-state! :graph/loading? false)
-    (state/pub-event! [:ui/re-render-root])
-
-    ;; (async/go
-    ;;   (async/<! (async/timeout 100))
-    ;;   (db-async/<fetch-all-pages repo))
-    ))
+    (state/pub-event! [:ui/re-render-root])))

+ 11 - 13
src/main/frontend/db/rtc/debug_ui.cljs

@@ -28,16 +28,15 @@
   (rum/local nil ::keys-state)
   (rum/local nil ::keys-state)
   {:will-mount (fn [state]
   {:will-mount (fn [state]
                  (let [canceler
                  (let [canceler
-                       (c.m/run-task
-                        (m/reduce
-                         (fn [logs log]
-                           (let [logs* (if log
-                                         (take 10 (conj logs log))
-                                         logs)]
-                             (reset! (get state ::logs) logs*)
-                             logs*))
-                         nil rtc-flows/rtc-log-flow)
-                        ::sub-logs)]
+                       (c.m/run-task ::sub-logs
+                         (m/reduce
+                          (fn [logs log]
+                            (let [logs* (if log
+                                          (take 10 (conj logs log))
+                                          logs)]
+                              (reset! (get state ::logs) logs*)
+                              logs*))
+                          nil rtc-flows/rtc-log-flow))]
                    (reset! (get state ::sub-log-canceler) canceler)
                    (reset! (get state ::sub-log-canceler) canceler)
                    state))
                    state))
    :will-unmount (fn [state]
    :will-unmount (fn [state]
@@ -83,9 +82,8 @@
        (shui/tabler-icon "download") "graph-list")
        (shui/tabler-icon "download") "graph-list")
       (shui/button
       (shui/button
        {:size :sm
        {:size :sm
-        :on-click #(c.m/run-task
-                    (user/new-task--upload-user-avatar "TEST_AVATAR")
-                    :upload-test-avatar)}
+        :on-click #(c.m/run-task :upload-test-avatar
+                     (user/new-task--upload-user-avatar "TEST_AVATAR"))}
        (shui/tabler-icon "upload") "upload-test-avatar")]
        (shui/tabler-icon "upload") "upload-test-avatar")]
 
 
      [:div.pb-4
      [:div.pb-4

+ 14 - 11
src/main/frontend/extensions/code.cljs

@@ -1,11 +1,9 @@
 (ns frontend.extensions.code
 (ns frontend.extensions.code
-  (:require [cljs-bean.core :as bean]
-            [clojure.string :as string]
-            ["codemirror" :as CodeMirror]
+  (:require ["codemirror" :as CodeMirror]
             ["codemirror/addon/edit/closebrackets"]
             ["codemirror/addon/edit/closebrackets"]
             ["codemirror/addon/edit/matchbrackets"]
             ["codemirror/addon/edit/matchbrackets"]
-            ["codemirror/addon/selection/active-line"]
             ["codemirror/addon/hint/show-hint"]
             ["codemirror/addon/hint/show-hint"]
+            ["codemirror/addon/selection/active-line"]
             ["codemirror/mode/apl/apl"]
             ["codemirror/mode/apl/apl"]
             ["codemirror/mode/asciiarmor/asciiarmor"]
             ["codemirror/mode/asciiarmor/asciiarmor"]
             ["codemirror/mode/asn.1/asn.1"]
             ["codemirror/mode/asn.1/asn.1"]
@@ -128,20 +126,22 @@
             ["codemirror/mode/yaml-frontmatter/yaml-frontmatter"]
             ["codemirror/mode/yaml-frontmatter/yaml-frontmatter"]
             ["codemirror/mode/yaml/yaml"]
             ["codemirror/mode/yaml/yaml"]
             ["codemirror/mode/z80/z80"]
             ["codemirror/mode/z80/z80"]
+            [cljs-bean.core :as bean]
+            [clojure.string :as string]
             [frontend.commands :as commands]
             [frontend.commands :as commands]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.extensions.calc :as calc]
             [frontend.extensions.calc :as calc]
-            [frontend.handler.editor :as editor-handler]
             [frontend.handler.code :as code-handler]
             [frontend.handler.code :as code-handler]
+            [frontend.handler.editor :as editor-handler]
+            [frontend.schema.handler.common-config :refer [Config-edn]]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.config :as config]
             [goog.dom :as gdom]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [goog.object :as gobj]
-            [frontend.schema.handler.common-config :refer [Config-edn]]
             [malli.core :as m]
             [malli.core :as m]
-            [rum.core :as rum]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [rum.core :as rum]))
 
 
 ;; codemirror
 ;; codemirror
 
 
@@ -466,11 +466,14 @@
                              (when e (util/stop e))
                              (when e (util/stop e))
                              (let [esc? (gobj/get cm "escPressed")]
                              (let [esc? (gobj/get cm "escPressed")]
                                (when (or (= :file (state/get-current-route))
                                (when (or (= :file (state/get-current-route))
-                                       (not esc?))
+                                         (not esc?))
                                  (code-handler/save-code-editor!))
                                  (code-handler/save-code-editor!))
                                (state/set-block-component-editing-mode! false)
                                (state/set-block-component-editing-mode! false)
                                (state/set-state! :editor/code-block-context nil)
                                (state/set-state! :editor/code-block-context nil)
-                               (when (not esc?) (state/clear-edit!))
+                               (when (and (not esc?)
+                                          (= (:db/id (state/get-edit-block))
+                                             (:db/id edit-block)))
+                                 (state/clear-edit!))
                                (vreset! *cursor-curr nil)
                                (vreset! *cursor-curr nil)
                                (vreset! *cursor-prev nil))))
                                (vreset! *cursor-prev nil))))
         (.on editor "focus" (fn [_e]
         (.on editor "focus" (fn [_e]

+ 1 - 5
src/main/frontend/extensions/code.css

@@ -40,16 +40,12 @@
 
 
 .CodeMirror {
 .CodeMirror {
   height: auto;
   height: auto;
+  max-height: 1024px;
   width: 100%;
   width: 100%;
   font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
   font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
   border-radius: 2px;
   border-radius: 2px;
   line-height: 1.45em;
   line-height: 1.45em;
 
 
-  &-scroll {
-    padding-top: 14px;
-    padding-bottom: 62px;
-  }
-
   &:not(.CodeMirror-focused) {
   &:not(.CodeMirror-focused) {
     .CodeMirror-activeline-background {
     .CodeMirror-activeline-background {
       background: unset !important;
       background: unset !important;

+ 4 - 3
src/main/frontend/extensions/fsrs.cljs

@@ -1,6 +1,7 @@
 (ns frontend.extensions.fsrs
 (ns frontend.extensions.fsrs
   "Flashcards functions based on FSRS, only works in db-based graphs"
   "Flashcards functions based on FSRS, only works in db-based graphs"
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
+            [frontend.common.missionary :as c.m]
             [frontend.components.block :as component-block]
             [frontend.components.block :as component-block]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
@@ -16,7 +17,6 @@
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.common.missionary :as c.m]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
@@ -189,7 +189,7 @@
 (rum/defcs ^:private card-view < rum/reactive db-mixins/query
 (rum/defcs ^:private card-view < rum/reactive db-mixins/query
   {:will-mount (fn [state]
   {:will-mount (fn [state]
                  (when-let [[repo block-id _] (:rum/args state)]
                  (when-let [[repo block-id _] (:rum/args state)]
-                   (db-async/<get-block repo block-id))
+                   (db-async/<get-block repo block-id {:children? false}))
                  state)}
                  state)}
   [state repo block-id *card-index *phase]
   [state repo block-id *card-index *phase]
   (when-let [block-entity (db/sub-block block-id)]
   (when-let [block-entity (db/sub-block block-id)]
@@ -310,7 +310,8 @@
   (when-let [canceler @*last-update-due-cards-count-canceler]
   (when-let [canceler @*last-update-due-cards-count-canceler]
     (canceler)
     (canceler)
     (reset! *last-update-due-cards-count-canceler nil))
     (reset! *last-update-due-cards-count-canceler nil))
-  (let [canceler (c.m/run-task new-task--update-due-cards-count :update-due-cards-count)]
+  (let [canceler (c.m/run-task :update-due-cards-count
+                   new-task--update-due-cards-count)]
     (reset! *last-update-due-cards-count-canceler canceler)
     (reset! *last-update-due-cards-count-canceler canceler)
     nil))
     nil))
 
 

+ 1 - 1
src/main/frontend/extensions/handbooks/core.cljs

@@ -534,7 +534,7 @@
 
 
         [scrolled?, set-scrolled!] (rum/use-state false)
         [scrolled?, set-scrolled!] (rum/use-state false)
         on-scroll (hooks/use-memo
         on-scroll (hooks/use-memo
-                   #(util/debounce 100 (fn [^js e] (set-scrolled! (not (< (.. e -target -scrollTop) 10)))))
+                   #(util/debounce (fn [^js e] (set-scrolled! (not (< (.. e -target -scrollTop) 10)))) 100)
                    [])]
                    [])]
 
 
     ;; load handbooks
     ;; load handbooks

+ 21 - 18
src/main/frontend/extensions/pdf/assets.cljs

@@ -1,35 +1,35 @@
 (ns frontend.extensions.pdf.assets
 (ns frontend.extensions.pdf.assets
   (:require [cljs.reader :as reader]
   (:require [cljs.reader :as reader]
             [clojure.string :as string]
             [clojure.string :as string]
+            [fipp.edn :refer [pprint]]
             [frontend.config :as config]
             [frontend.config :as config]
-            [frontend.db.conn :as conn]
+            [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db :as db]
+            [frontend.db.async :as db-async]
+            [frontend.db.conn :as conn]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
-            [frontend.db.async :as db-async]
+            [frontend.extensions.lightbox :as lightbox]
+            [frontend.extensions.pdf.windows :as pdf-windows]
             [frontend.fs :as fs]
             [frontend.fs :as fs]
-            [frontend.handler.editor :as editor-handler]
-            [frontend.handler.property :as property-handler]
-            [frontend.handler.page :as page-handler]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.assets :as assets-handler]
+            [frontend.handler.editor :as editor-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
-            [frontend.handler.route :as route-handler]
+            [frontend.handler.page :as page-handler]
+            [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
-            [frontend.ui :as ui]
-            [frontend.context.i18n :refer [t]]
-            [frontend.extensions.lightbox :as lightbox]
+            [frontend.handler.route :as route-handler]
             [frontend.state :as state]
             [frontend.state :as state]
+            [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
-            [logseq.publishing.db :as publish-db]
-            [frontend.extensions.pdf.windows :as pdf-windows]
-            [logseq.common.path :as path]
             [logseq.common.config :as common-config]
             [logseq.common.config :as common-config]
+            [logseq.common.path :as path]
             [logseq.common.util.block-ref :as block-ref]
             [logseq.common.util.block-ref :as block-ref]
+            [logseq.publishing.db :as publish-db]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [fipp.edn :refer [pprint]]))
+            [rum.core :as rum]))
 
 
 (defn get-in-repo-assets-full-filename
 (defn get-in-repo-assets-full-filename
   [url]
   [url]
@@ -190,8 +190,11 @@
 
 
 (defn construct-highlights-from-hls-page
 (defn construct-highlights-from-hls-page
   [hls-page]
   [hls-page]
-  (p/let [blocks (db-async/<get-page-all-blocks (:block/uuid hls-page))]
-    {:highlights (keep :logseq.property.pdf/hl-value blocks)}))
+  (p/let [result (db-async/<get-block (state/get-current-repo)
+                                      (:block/uuid hls-page)
+                                      {:children? true
+                                       :nested-children? false})]
+    {:highlights (keep :logseq.property.pdf/hl-value result)}))
 
 
 (defn file-based-load-hls-data$
 (defn file-based-load-hls-data$
   [{:keys [hls-file]}]
   [{:keys [hls-file]}]
@@ -423,7 +426,7 @@
             {:style {:width (if style "100%" "auto")}}
             {:style {:width (if style "100%" "auto")}}
             [:span.asset-action-bar
             [:span.asset-action-bar
              (when-let [asset-uuid (and (config/db-based-graph?)
              (when-let [asset-uuid (and (config/db-based-graph?)
-                                     (some-> asset-block (:block/uuid)))]
+                                        (some-> asset-block (:block/uuid)))]
                [:button.asset-action-btn
                [:button.asset-action-btn
                 {:title (t :asset/ref-block)
                 {:title (t :asset/ref-block)
                  :tabIndex "-1"
                  :tabIndex "-1"
@@ -439,7 +442,7 @@
                  :on-click (fn [e]
                  :on-click (fn [e]
                              (util/stop e)
                              (util/stop e)
                              (-> (util/copy-image-to-clipboard (common-config/remove-asset-protocol @*src))
                              (-> (util/copy-image-to-clipboard (common-config/remove-asset-protocol @*src))
-                               (p/then #(notification/show! "Copied!" :success))))}
+                                 (p/then #(notification/show! "Copied!" :success))))}
                 (ui/icon "copy")])
                 (ui/icon "copy")])
 
 
              [:button.asset-action-btn
              [:button.asset-action-btn

+ 3 - 3
src/main/frontend/extensions/pdf/core.cljs

@@ -690,12 +690,12 @@
            (when-let [^js/HTMLDivElement hls-layer (pdf-utils/resolve-hls-layer! viewer page)]
            (when-let [^js/HTMLDivElement hls-layer (pdf-utils/resolve-hls-layer! viewer page)]
              (let [page-hls (get grouped-hls page)
              (let [page-hls (get grouped-hls page)
                    hls-render (pdf-highlights-region-container
                    hls-render (pdf-highlights-region-container
-                                   viewer page-hls {:show-ctx-menu! show-ctx-menu!
-                                                    :upd-hl! upd-hl!})
+                               viewer page-hls {:show-ctx-menu! show-ctx-menu!
+                                                :upd-hl! upd-hl!})
                    ^js mounted-root (.-mountedRoot hls-layer)]
                    ^js mounted-root (.-mountedRoot hls-layer)]
                (if (nil? mounted-root)
                (if (nil? mounted-root)
                  (->> (rum/mount hls-render hls-layer)
                  (->> (rum/mount hls-render hls-layer)
-                   (set! (. hls-layer -mountedRoot)))
+                      (set! (. hls-layer -mountedRoot)))
                  (.render mounted-root hls-render))))))
                  (.render mounted-root hls-render))))))
        ;; destroy
        ;; destroy
        #())
        #())

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно