Преглед изворни кода

Merge branch 'feat/db' into refactor/db-properties-schema

Tienson Qin пре 1 година
родитељ
комит
958507262e
81 измењених фајлова са 1679 додато и 1254 уклоњено
  1. 2 2
      .github/workflows/build-desktop-release.yml
  2. 1 1
      .github/workflows/db.yml
  3. 2 2
      capacitor.config.ts
  4. 2 2
      deps.edn
  5. 2 1
      deps/db/src/logseq/db.cljs
  6. 7 9
      deps/db/src/logseq/db/frontend/class.cljs
  7. 23 10
      deps/db/src/logseq/db/frontend/malli_schema.cljs
  8. 183 169
      deps/db/src/logseq/db/frontend/property.cljs
  9. 7 0
      deps/db/src/logseq/db/frontend/rules.cljc
  10. 4 4
      deps/db/src/logseq/db/frontend/schema.cljs
  11. 2 2
      deps/db/src/logseq/db/sqlite/common_db.cljs
  12. 21 23
      deps/db/src/logseq/db/sqlite/create_graph.cljs
  13. 4 3
      deps/db/src/logseq/db/sqlite/db.cljs
  14. 0 1
      deps/db/test/logseq/db/sqlite/common_db_test.cljs
  15. 55 26
      deps/db/test/logseq/db/sqlite/create_graph_test.cljs
  16. 20 10
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  17. 2 2
      deps/graph-parser/src/logseq/graph_parser/whiteboard.cljs
  18. 2 2
      deps/outliner/src/logseq/outliner/core.cljs
  19. 4 3
      deps/shui/src/logseq/shui/dialog/core.cljs
  20. 3 0
      deps/shui/src/logseq/shui/popup/core.cljs
  21. 2 2
      e2e-tests/utils.ts
  22. 1 2
      package.json
  23. 3 0
      packages/ui/@/components/ui/dropdown-menu.tsx
  24. 3 1
      packages/ui/@/components/ui/popover.tsx
  25. 2 2
      packages/ui/src/vars-classic.css
  26. 2 1
      resources/forge.config.js
  27. 6 6
      resources/package.json
  28. 1 1
      scripts/src/logseq/tasks/db_graph/create_graph_with_properties.cljs
  29. 2 2
      scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs
  30. 1 0
      src/main/frontend/common.css
  31. 27 25
      src/main/frontend/components/block.cljs
  32. 1 1
      src/main/frontend/components/block/macros.cljs
  33. 39 11
      src/main/frontend/components/class.cljs
  34. 2 2
      src/main/frontend/components/container.cljs
  35. 6 2
      src/main/frontend/components/container.css
  36. 1 1
      src/main/frontend/components/content.cljs
  37. 1 1
      src/main/frontend/components/editor.cljs
  38. 85 61
      src/main/frontend/components/header.cljs
  39. 0 5
      src/main/frontend/components/header.css
  40. 20 58
      src/main/frontend/components/hierarchy.cljs
  41. 2 8
      src/main/frontend/components/journal.css
  42. 55 12
      src/main/frontend/components/page.css
  43. 2 3
      src/main/frontend/components/page_menu.cljs
  44. 2 2
      src/main/frontend/components/plugins.cljs
  45. 3 8
      src/main/frontend/components/plugins.css
  46. 99 80
      src/main/frontend/components/property.cljs
  47. 1 1
      src/main/frontend/components/property.css
  48. 5 5
      src/main/frontend/components/query_table.cljs
  49. 134 103
      src/main/frontend/components/repo.cljs
  50. 19 2
      src/main/frontend/components/repo.css
  51. 1 1
      src/main/frontend/config.cljs
  52. 13 7
      src/main/frontend/db/async.cljs
  53. 7 7
      src/main/frontend/db/model.cljs
  54. 71 60
      src/main/frontend/db/rtc/debug_ui.cljs
  55. 35 8
      src/main/frontend/db_worker.cljs
  56. 7 7
      src/main/frontend/extensions/pdf/assets.cljs
  57. 1 1
      src/main/frontend/extensions/pdf/utils.cljs
  58. 2 2
      src/main/frontend/handler/block.cljs
  59. 3 2
      src/main/frontend/handler/db_based/editor.cljs
  60. 12 12
      src/main/frontend/handler/db_based/property.cljs
  61. 3 4
      src/main/frontend/handler/db_based/property/util.cljs
  62. 33 12
      src/main/frontend/handler/db_based/rtc.cljs
  63. 4 4
      src/main/frontend/handler/editor.cljs
  64. 6 6
      src/main/frontend/handler/events.cljs
  65. 1 1
      src/main/frontend/handler/graph.cljs
  66. 2 3
      src/main/frontend/handler/page.cljs
  67. 25 9
      src/main/frontend/handler/property/util.cljs
  68. 1 1
      src/main/frontend/handler/route.cljs
  69. 7 9
      src/main/frontend/handler/user.cljs
  70. 10 10
      src/main/frontend/handler/whiteboard.cljs
  71. 5 7
      src/main/frontend/search.cljs
  72. 3 2
      src/main/frontend/shui.cljs
  73. 16 0
      src/main/frontend/worker/rtc/const.cljs
  74. 62 47
      src/main/frontend/worker/rtc/core.cljs
  75. 79 14
      src/main/frontend/worker/rtc/full_upload_download_graph.cljs
  76. 1 2
      src/main/logseq/api.cljs
  77. 5 5
      src/test/frontend/db/db_based_model_test.cljs
  78. 3 4
      src/test/frontend/handler/db_based/property_test.cljs
  79. 31 1
      src/test/frontend/worker/rtc/rtc_fns_test.cljs
  80. 353 317
      static/yarn.lock
  81. 4 9
      yarn.lock

+ 2 - 2
.github/workflows/build-desktop-release.yml

@@ -392,7 +392,7 @@ jobs:
         env:
           APPLE_ID: ${{ secrets.APPLE_ID_EMAIL }}
           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
-          APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
+          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 
       - name: Save x64 artifacts
         run: |
@@ -469,7 +469,7 @@ jobs:
         env:
           APPLE_ID: ${{ secrets.APPLE_ID_EMAIL }}
           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
-          APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
+          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 
       - name: Save arm64 artifacts
         run: |

+ 1 - 1
.github/workflows/db.yml

@@ -83,7 +83,7 @@ jobs:
           bb: ${{ env.BABASHKA_VERSION }}
 
       - name: Run clj-kondo lint
-        run: clojure -M:clj-kondo --parallel --lint src
+        run: clojure -M:clj-kondo --lint src test
 
       - name: Carve lint for unused vars
         run: bb lint:carve 2>/dev/null

+ 2 - 2
capacitor.config.ts

@@ -40,10 +40,10 @@ const config: CapacitorConfig = {
   }
 }
 
-if (process.env.LOGSEQ_APP_SERVER_URL) {
+if ("http://192.168.199.216:3001") {
   Object.assign(config, {
     server: {
-      url: process.env.LOGSEQ_APP_SERVER_URL,
+      url: "http://192.168.199.216:3001",
       cleartext: true
     }
   })

+ 2 - 2
deps.edn

@@ -42,7 +42,7 @@
  :aliases {:cljs {:extra-paths ["src/dev-cljs/" "src/test/" "src/electron/"]
                   :extra-deps  {org.clojure/clojurescript        {:mvn/version "1.11.54"}
                                 org.clojure/tools.namespace      {:mvn/version "0.2.11"}
-                                cider/cider-nrepl                {:mvn/version "0.44.0"}
+                                cider/cider-nrepl                {:mvn/version "0.47.0"}
                                 org.clojars.knubie/cljs-run-test {:mvn/version "1.0.1"}
                                 tortue/spy                       {:mvn/version "2.14.0"}}
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
@@ -53,7 +53,7 @@
                                 pjstadig/humane-test-output      {:mvn/version "0.11.0"}
                                 org.clojars.knubie/cljs-run-test {:mvn/version "1.0.1"}
                                 tortue/spy                       {:mvn/version "2.14.0"}
-                                cider/cider-nrepl                {:mvn/version "0.44.0"}}
+                                cider/cider-nrepl                {:mvn/version "0.47.0"}}
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
 
            :bench {:extra-paths ["src/bench/"]

+ 2 - 1
deps/db/src/logseq/db.cljs

@@ -41,6 +41,7 @@
     ;; TODO: remove this in later releases
     :block/heading-level
     :block/file
+    :class/parent
     {:block/page [:db/id :block/name :block/original-name :block/journal-day]}
     {:block/_parent ...}])
 
@@ -599,7 +600,7 @@
 
 (defn get-graph-rtc-uuid
   [db]
-  (when db (:graph/uuid (d/entity db :graph/uuid))))
+  (when db (:graph/uuid (d/entity db :logseq.kv/graph-uuid))))
 
 (comment
   (defn db-based-graph?

+ 7 - 9
deps/db/src/logseq/db/frontend/class.cljs

@@ -1,15 +1,13 @@
 (ns logseq.db.frontend.class
   "Class related fns for DB graphs and frontend/datascript usage")
 
-;; TODO: disable name changes for those built-in page/class names and their properties names
 (def ^:large-vars/data-var built-in-classes
-  {:task {:original-name "Task"
-          :schema {:properties #{:logseq.property/status
-                                 :logseq.property/priority
-                                 :logseq.property/scheduled
-                                 :logseq.property/deadline}}}
-   :card {:original-name "card"
-          ;; :schema {:property []}
-          }
+  "Map of built-in classes for db graphs with their :db/ident as keys"
+  {:logseq.class/task
+   {:original-name "Task"
+    :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/scheduled :logseq.task/deadline]}}
+   :logseq.class/card {:original-name "card"
+                       ;; :schema {:property []}
+                       }
    ;; TODO: Add more classes such as :book, :paper, :movie, :music, :project
    })

+ 23 - 10
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -90,6 +90,7 @@
    [:block/original-name :string]
    [:block/type {:optional true} [:enum #{"property"} #{"class"} #{"whiteboard"} #{"hidden"}]]
    [:block/journal? :boolean]
+   [:block/namespace {:optional true} :int]
    [:block/alias {:optional true} [:set :int]]
     ;; TODO: Should this be here or in common?
    [:block/path-refs {:optional true} [:set :int]]])
@@ -101,20 +102,31 @@
      ;; Only for linked pages
      [:block/collapsed? {:optional true} :boolean]
      ;; journal-day is only set for journal pages
-     [:block/journal-day {:optional true} :int]
-     [:block/namespace {:optional true} :int]]
+     [:block/journal-day {:optional true} :int]]
     page-attrs
     page-or-block-attrs)))
 
 (def class-attrs
   [[:db/ident {:optional true} :keyword]
+   [:class/parent {:optional true} :int]
    [:class/schema.properties {:optional true} [:set :int]]])
 
+(def logseq-ident-namespaces
+  "Set of all namespaces Logseq uses for :db/ident. It's important to grow this
+  list purposefully and have it start with 'logseq' to allow for users and 3rd
+  party plugins to provide their own namespaces to core concepts."
+  #{"logseq.property" "logseq.property.table" "logseq.property.tldraw"
+    "logseq.class" "logseq.task" "logseq.kv"})
+
+(def logseq-ident
+  [:and :keyword [:fn
+                  {:error/message "should be a valid :db/ident namespace"}
+                  (fn logseq-namespace? [k]
+                    (contains? logseq-ident-namespaces (namespace k)))]])
 (def class-page
   (vec
    (concat
     [:map
-     [:block/namespace {:optional true} :int]
      [:block/schema
       {:optional true}
       [:map
@@ -143,13 +155,15 @@
   (vec
    (concat
     [:map
-     [:db/ident :keyword]
+     [:db/ident logseq-ident]
      [:block/schema
       (vec
        (concat
         [:map
          [:type (apply vector :enum (into db-property-type/internal-built-in-property-types
-                                          db-property-type/user-built-in-property-types))]]
+                                          db-property-type/user-built-in-property-types))]
+         [:public? {:optional true} :boolean]
+         [:view-context {:optional true} [:enum :page :block]]]
         property-common-schema-attrs
         property-type-schema-attrs))]]
     page-attrs
@@ -235,7 +249,7 @@
     [:map]
     [[:block/type [:= #{"closed value"}]]
      ;; for built-in properties
-     [:db/ident {:optional true} :keyword]
+     [:db/ident {:optional true} logseq-ident]
      [:block/schema {:optional true}
       [:map
        [:value [:or :string :double]]
@@ -283,7 +297,7 @@
   (into [:or]
         (map (fn [kv]
                [:map
-                [:db/ident :keyword]
+                [:db/ident logseq-ident]
                 kv
                 [:block/tx-id {:optional true} :int]])
              db-ident-keys)))
@@ -337,9 +351,8 @@
                                        db-ident-keys (rest class-page))
                                (remove #(= (last %) [:set :int]))
                                (map first)
-                               set)
-      attrs-to-ignore #{:ast/version}]
-  (when-let [undeclared-attrs (seq (remove (some-fn malli-non-ref-attrs attrs-to-ignore) db-schema/db-non-ref-attributes))]
+                               set)]
+  (when-let [undeclared-attrs (seq (remove malli-non-ref-attrs db-schema/db-non-ref-attributes))]
     (throw (ex-info (str "The malli DB schema is missing the following non ref attributes from datascript's schema: "
                          (string/join ", " undeclared-attrs))
                     {}))))

+ 183 - 169
deps/db/src/logseq/db/frontend/property.cljs

@@ -1,169 +1,188 @@
 (ns logseq.db.frontend.property
   "Property related fns for DB graphs and frontend/datascript usage"
-  (:require [clojure.set :as set]
-            [logseq.db.sqlite.util :as sqlite-util]
+  (:require [logseq.db.sqlite.util :as sqlite-util]
             [datascript.core :as d]
             [logseq.common.util :as common-util]
             [clojure.string :as string]))
 
-;; FIXME: no support for built-in-extended-properties
-(def ^:large-vars/data-var built-in-properties
-  "Map of built in properties for db graphs. Each property has a config map with
-  the following keys:
-   * :schema - Property's schema. Required key
+(def ^:large-vars/data-var built-in-properties*
+  "Map of built in properties for db graphs with their :db/ident as keys.
+   Each property has a config map with the following keys:
+   * :schema - Property's schema. Required key. Has the following common keys:
+     * :type - Property type
+     * :cardinality - property cardinality. Default to one/single cardinality if not set
+     * :hide? - Boolean which hides property when set on a block
+     * :public? - Boolean which allows property to be used by user e.g. add and remove property to blocks/pages
+     * :view-context - Keyword to indicate which view contexts a property can be
+       seen in when :public? is set. Valid values are :page and :block. Property can
+       be viewed in any context if not set
    * :original-name - Property's :block/original-name
+   * :name - Property's :block/name as a keyword. If none given, one is derived from the db/ident
    * :attribute - Property keyword that is saved to a datascript attribute outside of :block/properties
-   * :visible - Boolean to indicate user can see and use this property"
-  {:alias {:original-name "Alias"
-           :attribute :block/alias
-           :db-ident :block/alias
-           :visible true
-           :schema {:type :page
-                    :cardinality :many}}
-   :tags {:original-name "Tags"
-          :attribute :block/tags
-          :db-ident :block/tags
-          :visible true
-          :schema {:type :page
-                   :cardinality :many
-                   :classes #{:logseq.class}}}
-   :pagetags {:original-name "pageTags"
-              :visible true
-              :schema {:type :page
-                       :cardinality :many}}
-   :background-color {:schema {:type :default :hide? true}}
-   :background-image {:schema {:type :default :hide? true}
-                      :visible true}
-   :heading {:schema {:type :any :hide? true}}      ; number (1-6) or boolean for auto heading
-   :created-from-block    {:schema {:db-attr-type :ref
-                                    :type :number}}
-   :created-from-property {:schema {:db-attr-type :ref
-                                    :type :number}}
-   :created-from-template {:schema {:db-attr-type :ref
-                                    :type :number}}
-   :source-page-id        {:schema {:type :ref}}
-   :built-in?             {:schema {:type :checkbox}}
-   :hide-properties?      {:schema {:type :checkbox}}
-   :query-table {:schema {:type :checkbox}}
+   * :closed-values - Vec of closed-value maps for properties with choices. Map
+     has keys :value, :db-ident, :uuid and :icon
+   * :db-ident - Keyword to set :db/ident and give property unique id in db"
+  {:block/alias           {:original-name "Alias"
+                           :attribute :block/alias
+                           :schema {:type :page
+                                    :cardinality :many
+                                    :view-context :page
+                                    :public? true}}
+   :block/tags           {:original-name "Tags"
+                          :attribute :block/tags
+                          :schema {:type :page
+                                   :cardinality :many
+                                   :public? true
+                                   :classes #{:logseq.class}}}
+   :logseq.property/page-tags {:original-name "pageTags"
+                               :schema {:type :page
+                                        :public? true
+                                        :view-context :page
+                                        :cardinality :many}}
+   :logseq.property/background-color {:schema {:type :default :hide? true}}
+   :logseq.property/background-image {:schema
+                                      {:type :default
+                                       :hide? true
+                                       :view-context :block
+                                       :public? true}}
+   ;; number (1-6) or boolean for auto heading
+   :logseq.property/heading {:schema {:type :any :hide? true}}
+   :logseq.property/created-from-block    {:schema {:type :uuid}}
+   :logseq.property/created-from-property {:schema {:type :uuid}}
+   :logseq.property/created-from-template {:schema {:type :uuid}}
+   :logseq.property/source-page-id        {:schema {:type :uuid}}
+   :logseq.property/built-in?             {:schema {:type :checkbox}}
+   :logseq.property/hide-properties?      {:schema {:type :checkbox}}
+   :logseq.property/query-table {:schema {:type :checkbox}}
    ;; query-properties is a coll of property uuids and keywords where keywords are special frontend keywords
-   :query-properties {:schema {:type :coll}}
+   :logseq.property/query-properties {:schema {:type :coll}}
    ;; query-sort-by is either a property uuid or a keyword where keyword is a special frontend keyword
-   :query-sort-by {:schema {:type :any}}
-   :query-sort-desc {:schema {:type :checkbox}}
-   :ls-type {:schema {:type :keyword}}
-   :hl-type {:schema {:type :keyword}}
-   :hl-page {:schema {:type :number}}
-   :hl-stamp {:schema {:type :number}}
-   :hl-color {:schema {:type :default}}
-   :logseq.macro-name {:schema {:type :default}}
-   :logseq.macro-arguments {:schema {:type :coll}}
-   :logseq.order-list-type {:schema {:type :default}}
-   :logseq.tldraw.page {:schema {:type :map}}
-   :logseq.tldraw.shape {:schema {:type :map}}
+   :logseq.property/query-sort-by {:schema {:type :any}}
+   :logseq.property/query-sort-desc {:schema {:type :checkbox}}
+   :logseq.property/ls-type {:schema {:type :keyword}}
+   :logseq.property/hl-type {:schema {:type :keyword}}
+   :logseq.property/hl-page {:schema {:type :number}}
+   :logseq.property/hl-stamp {:schema {:type :number}}
+   :logseq.property/hl-color {:schema {:type :default}}
+   :logseq.property/macro-name {:name :logseq.macro-name
+                                :schema {:type :default}}
+   :logseq.property/macro-arguments {:name :logseq.macro-arguments
+                                     :schema {:type :coll}}
+   :logseq.property/order-list-type {:name :logseq.order-list-type
+                                     :schema {:type :default}}
+   :logseq.property.tldraw/page {:name :logseq.tldraw.page
+                                 :schema {:type :map}}
+   :logseq.property.tldraw/shape {:name :logseq.tldraw.shape
+                                  :schema {:type :map}}
 
    ;; Task props
-   :status {:db-ident :logseq.property/status
-            :original-name "Status"
-            :schema
-            {:type :default}
-            :closed-values
-            (mapv (fn [[db-ident value icon]]
-                    {:db-ident db-ident
-                     :value value
-                     :uuid (random-uuid)
-                     :icon {:type :tabler-icon :id icon :name icon}})
-                  [[:logseq.property/status.backlog "Backlog" "Backlog"]
-                   [:logseq.property/status.todo "Todo" "Todo"]
-                   [:logseq.property/status.doing "Doing" "InProgress50"]
-                   [:logseq.property/status.in-review "In Review" "InReview"]
-                   [:logseq.property/status.done "Done" "Done"]
-                   [:logseq.property/status.canceled "Canceled" "Cancelled"]])
-            :visible true}
-   :priority {:db-ident :logseq.property/priority
-              :original-name "Priority"
-              :schema
-              {:type :default}
-              :closed-values
-              (mapv (fn [[db-ident value]]
-                      {:db-ident db-ident
-                       :value value
-                       :uuid (random-uuid)})
-                    [[:logseq.property/priority.urgent "Urgent"]
-                     [:logseq.property/priority.high "High"]
-                     [:logseq.property/priority.medium "Medium"]
-                     [:logseq.property/priority.low "Low"]])
-              :visible true}
-   :scheduled {:db-ident :logseq.property/scheduled
-               :original-name "Scheduled"
-               :schema {:db-attr-type :ref
-                        :type :date}
-               :visible true}
-   :deadline {:db-ident :logseq.property/deadline
-              :original-name "Deadline"
-              :schema {:db-attr-type :ref
-                       :type :date}
-              :visible true}
+   :logseq.task/status
+   {:original-name "Status"
+    :schema
+    {:type :default
+     :public? true}
+    :closed-values
+    (mapv (fn [[db-ident value icon]]
+            {:db-ident db-ident
+             :value value
+             :uuid (random-uuid)
+             :icon {:type :tabler-icon :id icon :name icon}})
+          [[:logseq.task/status.backlog "Backlog" "Backlog"]
+           [:logseq.task/status.todo "Todo" "Todo"]
+           [:logseq.task/status.doing "Doing" "InProgress50"]
+           [:logseq.task/status.in-review "In Review" "InReview"]
+           [:logseq.task/status.done "Done" "Done"]
+           [:logseq.task/status.canceled "Canceled" "Cancelled"]])}
+   :logseq.task/priority
+   {:original-name "Priority"
+    :schema
+    {:type :default
+     :public? true}
+    :closed-values
+    (mapv (fn [[db-ident value]]
+            {:db-ident db-ident
+             :value value
+             :uuid (random-uuid)})
+          [[:logseq.task/priority.urgent "Urgent"]
+           [:logseq.task/priority.high "High"]
+           [:logseq.task/priority.medium "Medium"]
+           [:logseq.task/priority.low "Low"]])}
+   :logseq.task/scheduled
+   {:original-name "Scheduled"
+    :schema {:type :date
+             :public? true}}
+   :logseq.task/deadline
+   {:original-name "Deadline"
+    :schema {:type :date
+             :public? true}}
 
    ;; TODO: Add more props :Assignee, :Estimate, :Cycle, :Project
 
    ;; color props
-   :logseq.color {:schema
-                  {:type :default :hide? true}
-                  :closed-values
-                  (mapv #(hash-map :db-ident (keyword "logseq.property" (str "color." %))
-                                   :value %
-                                   :uuid (random-uuid))
-                        ;; Stringified version of frontend.colors/COLORS. Too basic to couple
-                        ["tomato" "red" "crimson" "pink" "plum" "purple" "violet" "indigo" "blue" "cyan" "teal" "green" "grass" "orange" "brown"])
-                  :visible true}
+   :logseq.property/color
+   {:name :logseq.color
+    :schema
+    {:type :default :hide? true :public? true}
+    :closed-values
+    (mapv #(hash-map :db-ident (keyword "logseq.property" (str "color." %))
+                     :value %
+                     :uuid (random-uuid))
+          ;; Stringified version of frontend.colors/COLORS. Too basic to couple
+          ["tomato" "red" "crimson" "pink" "plum" "purple" "violet" "indigo" "blue" "cyan" "teal" "green" "grass" "orange" "brown"])}
    ;; table-v2 props
-   :logseq.table.version {:schema {:type :number :hide? true}
-                          :visible true}
-   :logseq.table.compact {:schema {:type :checkbox :hide? true}
-                          :visible true}
-   :logseq.table.headers {:schema
-                          {:type :default :hide? true}
-                          :closed-values
-                          (mapv #(hash-map :db-ident (keyword "logseq.property.table" (str "headers." %))
-                                           :value %
-                                           :uuid (random-uuid))
-                                ["uppercase" "capitalize" "capitalize-first" "lowercase"])
-                          :visible true}
-   :logseq.table.hover {:schema
-                        {:type :default :hide? true}
-                        :closed-values
-                        (mapv #(hash-map :db-ident (keyword "logseq.property.table" (str "hover." %))
-                                         :value %
-                                         :uuid (random-uuid))
-                              ["row" "col" "both" "none"])
-                        :visible true}
-   :logseq.table.borders {:schema {:type :checkbox :hide? true}
-                          :visible true}
-   :logseq.table.stripes {:schema {:type :checkbox :hide? true}
-                          :visible true}
-   :logseq.table.max-width {:schema {:type :number :hide? true}
-                            :visible true}
+   :logseq.property.table/version {:name :logseq.table.version
+                                   :schema {:type :number :hide? true :public? true :view-context :block}}
+   :logseq.property.table/compact {:name :logseq.table.compact
+                                   :schema {:type :checkbox :hide? true :public? true :view-context :block}}
+   :logseq.property.table/headers
+   {:name :logseq.table.headers
+    :schema
+    {:type :default :hide? true :public? true :view-context :block}
+    :closed-values
+    (mapv #(hash-map :db-ident (keyword "logseq.property.table" (str "headers." %))
+                     :value %
+                     :uuid (random-uuid))
+          ["uppercase" "capitalize" "capitalize-first" "lowercase"])}
+   :logseq.property.table/hover
+   {:name :logseq.table.hover
+    :schema
+    {:type :default :hide? true :public? true :view-context :block}
+    :closed-values
+    (mapv #(hash-map :db-ident (keyword "logseq.property.table" (str "hover." %))
+                     :value %
+                     :uuid (random-uuid))
+          ["row" "col" "both" "none"])}
+   :logseq.property.table/borders {:name :logseq.table.borders
+                                   :schema {:type :checkbox :hide? true :public? true :view-context :block}}
+   :logseq.property.table/stripes {:name :logseq.table.stripes
+                                   :schema {:type :checkbox :hide? true :public? true :view-context :block}}
+   :logseq.property.table/max-width {:name :logseq.table.max-width
+                                     :schema {:type :number :hide? true :public? true :view-context :block}}
 
-   :icon {:original-name "Icon"
-          :schema {:type :map}}
-   :public {:schema {:type :checkbox :hide? true}
-            :visible true}
-   :filters {:schema {:type :map}}
-   :exclude-from-graph-view {:schema {:type :checkbox :hide? true}
-                             :visible true}})
+   :logseq.property/icon {:original-name "Icon"
+                          :schema {:type :map}}
+   :logseq.property/public {:schema
+                            {:type :checkbox
+                             :hide? true
+                             :view-context :page
+                             :public? true}}
+   :logseq.property/filters {:schema {:type :map}}
+   :logseq.property/exclude-from-graph-view {:schema
+                                             {:type :checkbox
+                                              :hide? true
+                                              :view-context :page
+                                              :public? true}}})
 
-(def visible-built-in-properties
-  "These are built-in properties that users can see and use"
-  (set (keep (fn [[k v]] (when (:visible v) k)) built-in-properties)))
-
-(defonce built-in-properties-keys
-  (set (keys built-in-properties)))
-
-(def hidden-built-in-properties
-  (set/difference built-in-properties-keys visible-built-in-properties))
-
-(defonce built-in-properties-keys-str
-  (set (map name (keys built-in-properties))))
+(def built-in-properties
+  (->> built-in-properties*
+       (map (fn [[k v]]
+              (assert (and (keyword? k) (namespace k)))
+              [k
+               ;; All built-ins must have a :name
+               (if (:name v)
+                 v
+                 (assoc v :name (keyword (string/lower-case (name k)))))]))
+       (into {})))
 
 (defn valid-property-name?
   [s]
@@ -171,25 +190,27 @@
   ;; Disallow tags or page refs as they would create unreferenceable page names
   (not (re-find #"^(#|\[\[)" s)))
 
+(defn get-pid
+  "Get a built-in property's id (keyword name for file graph and uuid for db graph)
+  given its db-ident. Use this fn on a file or db graph. Use
+  db-pu/get-built-in-property-uuid if only in a db graph context"
+  [repo db db-ident]
+  (if (sqlite-util/db-based-graph? repo)
+    db-ident
+    (get-in built-in-properties [db-ident :name])))
+
 (defn lookup
-  "Get the value of coll's (a map) `key`. For file and db graphs"
-  [repo db coll key]
-  (when db
-    (let [property-name (if (keyword? key)
-                          (name key)
-                          key)]
-      (if (sqlite-util/db-based-graph? repo)
-        (when-let [property (d/entity db [:block/name (common-util/page-name-sanity-lc property-name)])]
-          (get coll (:db/ident property)))
-        (get coll key)))))
+  "Get the value of coll by db-ident. For file and db graphs"
+  [repo db coll db-ident]
+  (get coll (get-pid repo db db-ident)))
 
 (defn get-block-property-value
-  "Get the value of block's property `key`"
-  [repo db block key]
+  "Get the value of built-in block's property by its db-ident"
+  [repo db block db-ident]
   (when db
     (let [block (or (d/entity db (:db/id block)) block)]
       (when-let [properties (:block/properties block)]
-        (lookup repo db properties key)))))
+        (lookup repo db properties db-ident)))))
 
 (defn name->db-ident
   "Converts a built-in property's keyword name to its :db/ident equivalent.
@@ -201,19 +222,12 @@
     (keyword (str "logseq.property" additional-ns) prop-name)
     (keyword "logseq.property" (name legacy-name))))
 
-(defn get-pid
-  "Get a property's id (name or uuid) given its name. For file and db graphs"
-  [repo db property-name]
-  (if (sqlite-util/db-based-graph? repo)
-    (:block/uuid (d/entity db [:block/name (common-util/page-name-sanity-lc (name property-name))]))
-    property-name))
-
 (defn shape-block?
   [repo db block]
-  (= :whiteboard-shape (get-block-property-value repo db block :ls-type)))
+  (= :whiteboard-shape (get-block-property-value repo db block :logseq.property/ls-type)))
 
 (defn get-by-ident-or-name
-  "Gets a property by ident or name"
+  "Gets a property by db-ident or name if it's a user property"
   [db ident-or-name]
   (if (and (keyword? ident-or-name) (namespace ident-or-name))
     (d/entity db ident-or-name)

+ 7 - 0
deps/db/src/logseq/db/frontend/rules.cljc

@@ -11,6 +11,13 @@
       [?t :block/namespace ?p]
       (namespace ?t ?c)]]
 
+   :class-parent
+   '[[(class-parent ?p ?c)
+      [?c :class/parent ?p]]
+     [(class-parent ?p ?c)
+      [?t :class/parent ?p]
+      (class-parent ?t ?c)]]
+
    :alias
    '[[(alias ?e2 ?e1)
       [?e2 :block/alias ?e1]]

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

@@ -3,11 +3,9 @@
   (:require [clojure.set :as set]))
 
 (defonce version 2)
-(defonce ast-version 1)
 ;; A page is a special block, a page can corresponds to multiple files with the same ":block/name".
 (def ^:large-vars/data-var schema
   {:schema/version  {}
-   :ast/version     {}
    :db/type         {}
    :db/ident        {:db/unique :db.unique/identity}
 
@@ -128,8 +126,10 @@
    (dissoc schema
            :block/properties-text-values :block/pre-block? :recent/pages :file/handle :block/file
            :block/properties-order)
-   {:file/last-modified-at {}}
-   {:asset/uuid {:db/unique :db.unique/identity}
+   {:class/parent {:db/valueType :db.type/ref
+                   :db/index true}
+    :file/last-modified-at {}
+    :asset/uuid {:db/unique :db.unique/identity}
     :asset/meta {}}))
 
 (def retract-attributes

+ 2 - 2
deps/db/src/logseq/db/sqlite/common_db.cljs

@@ -156,11 +156,11 @@
   [db]
   (let [schema (:schema db)
         idents (remove nil?
-                       (let [e (d/entity db :graph/uuid)
+                       (let [e (d/entity db :logseq.kv/graph-uuid)
                              id (:graph/uuid e)]
                          (when id
                            [{:db/id (:db/id e)
-                             :db/ident :graph/uuid
+                             :db/ident :logseq.kv/graph-uuid
                              :graph/uuid id}])))
         favorites (get-favorites db)
         latest-journals (get-latest-journals db 3)

+ 21 - 23
deps/db/src/logseq/db/sqlite/create_graph.cljs

@@ -15,15 +15,12 @@
   (let [built-in-properties (->>
                              (map (fn [[k v]]
                                     (assert (keyword? k))
-                                    [k (assoc v
-                                              :db-ident
-                                              (get v :db-ident (db-property/name->db-ident k)))])
+                                    [k v])
                                   db-property/built-in-properties)
                              (into {}))]
     (mapcat
-     (fn [[k-keyword {:keys [schema original-name closed-values db-ident]}]]
-       (let [k-name (name k-keyword)
-             name (or original-name k-name)
+     (fn [[db-ident {:keys [schema original-name closed-values]}]]
+       (let [name (or original-name (name db-ident))
              blocks (if closed-values
                       (db-property-util/build-closed-values
                        name
@@ -36,11 +33,12 @@
          (update blocks 0 default-db/mark-block-as-built-in)))
      built-in-properties)))
 
+
 (defn kv
   "Creates a key-value pair tx with the key under the :db/ident namespace :logseq.kv.
    For example, the :db/type key is stored under an entity with ident :logseq.kv.db/type"
   [key value]
-  {:db/ident (keyword (str "logseq.kv." (namespace key)) (name key))
+  {:db/ident (keyword "logseq.kv" (str (namespace key) "-" (name key)))
    key value})
 
 (defn build-db-initial-data
@@ -66,23 +64,23 @@
                               (map :db/ident default-properties)
                               default-properties)
         default-classes (map
-                         (fn [[k-keyword {:keys [schema original-name]}]]
-                           (let [db-ident (name k-keyword)]
+                         (fn [[db-ident {:keys [schema original-name]}]]
+                           (let [original-name' (or original-name (name db-ident))]
                              (default-db/mark-block-as-built-in
-                              (sqlite-util/build-new-class
-                               (let [properties (mapv
-                                                 (fn [db-ident]
-                                                   (let [property (get db-ident->properties db-ident)]
-                                                     (assert property (str "Built-in property " db-ident " is not defined yet"))
-                                                     db-ident))
-                                                 (:properties schema))]
-                                 (cond->
-                                  {:block/original-name (or original-name db-ident)
-                                   :block/name (common-util/page-name-sanity-lc db-ident)
-                                   :db/ident (keyword "logseq.class" db-ident)
-                                   :block/uuid (d/squuid)}
-                                   (seq properties)
-                                   (assoc :class/schema.properties properties)))))))
+                             (sqlite-util/build-new-class
+                              (let [properties (mapv
+                                                (fn [db-ident]
+                                                  (let [property (get db-ident->properties db-ident)]
+                                                    (assert property (str "Built-in property " db-ident " is not defined yet"))
+                                                    db-ident))
+                                                (:properties schema))]
+                                (cond->
+                                 {:block/original-name original-name'
+                                  :block/name (common-util/page-name-sanity-lc original-name')
+                                  :db/ident db-ident
+                                  :block/uuid (d/squuid)}
+                                  (seq properties)
+                                  (assoc :class/schema.properties properties)))))))
                          db-class/built-in-classes)
         db-idents (keep (fn [x] (when-let [ident (:db/ident x)]
                                   {:db/ident ident}))

+ 4 - 3
deps/db/src/logseq/db/sqlite/db.cljs

@@ -89,9 +89,10 @@
   [graphs-dir db-name]
   (let [[db-sanitized-name db-full-path] (get-db-full-path graphs-dir db-name)
         db (new sqlite db-full-path nil)
-        schema (if (sqlite-util/db-based-graph? db-name)
-                 db-schema/schema-for-db-based-graph
-                 db-schema/schema)]
+        ;; For both desktop and CLI, only file graphs have db-name that indicate their db type
+        schema (if (sqlite-util/local-file-based-graph? db-name)
+                 db-schema/schema
+                 db-schema/schema-for-db-based-graph)]
     (sqlite-common-db/create-kvs-table! db)
     (swap! databases assoc db-sanitized-name db)
     (let [storage (new-sqlite-storage db)

+ 0 - 1
deps/db/test/logseq/db/sqlite/common_db_test.cljs

@@ -4,7 +4,6 @@
             ["path" :as node-path]
             [datascript.core :as d]
             [logseq.db.sqlite.common-db :as sqlite-common-db]
-            [logseq.db.frontend.schema :as db-schema]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.db.sqlite.db :as sqlite-db]
             [clojure.string :as string]))

+ 55 - 26
deps/db/test/logseq/db/sqlite/create_graph_test.cljs

@@ -4,9 +4,11 @@
             [clojure.set :as set]
             [datascript.core :as d]
             [logseq.db.frontend.schema :as db-schema]
-            [logseq.db.sqlite.create-graph :as sqlite-create-graph]))
+            [logseq.db.sqlite.create-graph :as sqlite-create-graph]
+            [logseq.db.frontend.validate :as db-validate]
+            [logseq.db :as ldb]))
 
-(deftest build-db-initial-data
+(deftest new-graph-db-idents
   (testing "a new graph follows :db/ident conventions for"
     (let [conn (d/create-conn db-schema/schema-for-db-based-graph)
           _ (d/transact! conn (sqlite-create-graph/build-db-initial-data @conn "{}"))
@@ -15,30 +17,57 @@
                                @conn)
                           (map first))
           default-idents (map :db/ident ident-ents)]
-     (testing "namespaces"
-       (is (= '() (remove namespace default-idents))
-           "All default :db/ident's have namespaces")
-       (is (= []
-              (->> (keep namespace default-idents)
-                   (remove #(string/starts-with? % "logseq."))))
-           "All default :db/ident namespaces start with logseq.")
-       (is (= #{"logseq.property" "logseq.class" "logseq.task" "logseq.kv"}
-              (->> (keep namespace default-idents)
-                   ;; only pull 1st and 2nd level namespaces e.g. logseq and logseq.property
-                   (keep #(re-find #"^([^.]+\.?([^.]+)?)" %))
-                   (map first)
-                   set))
-           "All default :db/ident's top-level namespaces are known"))
+      (is (> (count default-idents) 75)
+          "Approximate number of default idents is correct")
+
+      (testing "namespaces"
+        (is (= '() (remove namespace default-idents))
+            "All default :db/ident's have namespaces")
+        (is (= []
+               (->> (keep namespace default-idents)
+                    (remove #(string/starts-with? % "logseq."))))
+            "All default :db/ident namespaces start with logseq."))
 
       (testing "closed values"
         (let [closed-value-ents (filter #(string/includes? (name (:db/ident %)) ".") ident-ents)
-             closed-value-properties (->> closed-value-ents
-                                          (map :db/ident)
-                                          (map #(keyword (namespace %) (string/replace (name %) #".[^.]+$" "")))
-                                          set)]
-         (is (= []
-                (remove #(= ["closed value"] (:block/type %)) closed-value-ents))
-             "All property names that contain a '.' are closed values")
-         (is (= #{}
-                (set/difference closed-value-properties (set default-idents)))
-             "All closed values start with a prefix that is a property name"))))))
+              closed-value-properties (->> closed-value-ents
+                                           (map :db/ident)
+                                           (map #(keyword (namespace %) (string/replace (name %) #".[^.]+$" "")))
+                                           set)]
+          (is (= []
+                 (remove #(= ["closed value"] (:block/type %)) closed-value-ents))
+              "All property names that contain a '.' are closed values")
+          (is (= #{}
+                 (set/difference closed-value-properties (set default-idents)))
+              "All closed values start with a prefix that is a property name"))))))
+
+(deftest new-graph-marks-built-ins
+  (let [conn (d/create-conn db-schema/schema-for-db-based-graph)
+        _ (d/transact! conn (sqlite-create-graph/build-db-initial-data @conn "{}"))
+        idents (->> (d/q '[:find [(pull ?b [:db/ident :block/properties]) ...]
+                           :where [?b :db/ident]]
+                         @conn)
+                    ;; only kv's don't have built-in property
+                    (remove #(= "logseq.kv" (namespace (:db/ident %)))))]
+    (is (= []
+           (remove #(ldb/built-in? @conn %) idents))
+        "All entities with :db/ident have built-in property (except for kv idents)")))
+
+(deftest new-graph-creates-class
+  (let [conn (d/create-conn db-schema/schema-for-db-based-graph)
+        _ (d/transact! conn (sqlite-create-graph/build-db-initial-data @conn "{}"))
+        task (d/entity @conn :logseq.class/task)]
+    (is (contains? (:block/type task) "class")
+        "Task class has correct type")
+    (is (= 4 (count (get-in task [:block/schema :properties])))
+        "Has correct number of task properties")
+    (is (every? #(contains? (:block/type (d/entity @conn [:block/uuid %])) "property")
+                (get-in task [:schema :properties]))
+        "Each task property has correct type")))
+
+(deftest new-graph-is-valid
+  (let [conn (d/create-conn db-schema/schema-for-db-based-graph)
+        _ (d/transact! conn (sqlite-create-graph/build-db-initial-data @conn "{}"))
+        validation (db-validate/validate-db! @conn)]
+    (is (nil? (:errors validation))
+        "New graph has no validation errors")))

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

@@ -254,6 +254,14 @@
       (throw (ex-info (str "No uuid found for page " (pr-str k))
                       {:page k}))))
 
+(def built-in-property-names
+  "Set of all built-in property names as keywords. Using in-memory property
+  names because these are legacy names already in a user's file graph"
+  (->> db-property/built-in-properties
+       vals
+       (map :name)
+       set))
+
 (defn- update-properties
   "Updates block property names and values"
   [props db page-names-to-uuids
@@ -266,7 +274,7 @@
                                                 {:page k}))))
                           (fn prop-name->uuid [k]
                             (cached-prop-name->uuid db page-names-to-uuids k)))
-        user-properties (apply dissoc props db-property/built-in-properties-keys)]
+        user-properties (apply dissoc props built-in-property-names)]
     (when (seq user-properties)
       (swap! (:block-properties-text-values import-state)
              assoc
@@ -277,7 +285,7 @@
     (if (contains? props :template)
       {}
       (-> (update-built-in-property-values
-           (select-keys props db-property/built-in-properties-keys)
+           (select-keys props built-in-property-names)
            db
            (:ignored-properties import-state)
            (select-keys block [:block/name :block/content]))
@@ -289,7 +297,9 @@
   ;; Already imported via a datascript attribute i.e. have :attribute on property config
   [:tags :alias :collapsed
    ;; Not supported as they have been ignored for a long time and cause invalid built-in pages
-   :now :later :doing :done :canceled :cancelled :in-progress :todo :wait :waiting])
+   :now :later :doing :done :canceled :cancelled :in-progress :todo :wait :waiting
+   ;; deprecated in db graphs
+   :macros :logseq.query/nlp-date])
 
 (defn- pre-update-properties
   "Updates page and block properties before their property types are inferred"
@@ -302,7 +312,7 @@
                                property-classes)]
     (->> (apply dissoc properties dissoced-props)
          (keep (fn [[prop val]]
-                 (if (not (contains? db-property/built-in-properties-keys prop))
+                 (if (not (contains? built-in-property-names prop))
                   ;; only update user properties
                    (if (string? val)
                     ;; Ignore blank values as they were usually generated by templates
@@ -330,7 +340,7 @@
               properties-to-infer (if (:template properties')
                                     ;; Ignore template properties as they don't consistently have representative property values
                                     {}
-                                    (apply dissoc properties' db-property/built-in-properties-keys))
+                                    (apply dissoc properties' built-in-property-names))
               property-changes
               (->> properties-to-infer
                    (keep (fn [[prop val]]
@@ -562,7 +572,7 @@
     :tag-classes (set (map string/lower-case (:tag-classes user-options)))
     :property-classes (set/difference
                        (set (map (comp keyword string/lower-case) (:property-classes user-options)))
-                       db-property/built-in-properties-keys)}))
+                       built-in-property-names)}))
 
 (defn add-file-to-db-graph
   "Parse file and save parsed data to the given db graph. Options available:
@@ -737,7 +747,7 @@
                     [(get ?bp ?prop-uuid) ?_v]]
                   @conn
                   (set (map :block/name user-classes)))
-             (remove #(db-property/built-in-properties-keys-str (second %)))
+             (remove #(ldb/built-in? (d/entity @conn [:block/name (second %)])))
              (reduce (fn [acc [class-id _prop-name prop-uuid]]
                        (update acc class-id (fnil conj #{}) prop-uuid))
                      {}))
@@ -811,7 +821,7 @@
                        :filename-format (or (:file/name-format config) :legacy)
                        :verbose (:verbose options)}
      :user-options (select-keys options [:tag-classes :property-classes])
-     :page-tags-uuid (:block/uuid (d/entity @conn :logseq.property/pagetags))
+     :page-tags-uuid (:block/uuid (d/entity @conn :logseq.property/page-tags))
      :import-state (new-import-state)
      :macros (or (:macros options) (:macros config))}
     (merge (select-keys options [:set-ui-state :export-file :notify-user]))))
@@ -831,7 +841,7 @@
    * :<save-config-file - fn which saves a config file
    * :<save-logseq-file - fn which saves a logseq file
    * :<copy-asset - fn which copies asset file
-   
+
    Note: See export-doc-files for additional options that are only for it"
   [repo-or-conn conn config-file *files {:keys [<read-file <copy-asset rpath-key log-fn]
                                  :or {rpath-key :path log-fn println}
@@ -858,4 +868,4 @@
        (export-favorites-from-config-edn conn repo-or-conn config {})
        (export-class-properties conn repo-or-conn)
        {:import-state (:import-state doc-options)
-        :files files}))))
+        :files files}))))

+ 2 - 2
deps/graph-parser/src/logseq/graph_parser/whiteboard.cljs

@@ -88,8 +88,8 @@
            {:block/page default-page-ref})))
 
 (defn shape->block [repo db shape page-name]
-  (let [properties {(db-property/get-pid repo db :ls-type) :whiteboard-shape
-                    (db-property/get-pid repo db :logseq.tldraw.shape) shape}
+  (let [properties {(db-property/get-pid repo db :logseq.property/ls-type) :whiteboard-shape
+                    (db-property/get-pid repo db :logseq.property.tldraw/shape) shape}
         page-name (common-util/page-name-sanity-lc page-name)
         block {:block/uuid (if (uuid? (:id shape)) (:id shape) (uuid (:id shape)))
                :block/page {:block/name page-name}

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

@@ -641,8 +641,8 @@
   (let [db @conn
         tb (when target-block (block db target-block))
         target-block (if sibling? target-block (when tb (:block (otree/-get-down tb conn))))
-        list-type-fn (fn [block] (db-property/get-block-property-value repo db block :logseq.order-list-type))
-        k (db-property/get-pid repo db :logseq.order-list-type)]
+        list-type-fn (fn [block] (db-property/get-block-property-value repo db block :logseq.property/order-list-type))
+        k (db-property/get-pid repo db :logseq.property/order-list-type)]
     (if-let [list-type (and target-block (list-type-fn target-block))]
       (mapv
        (fn [{:block/keys [content format] :as block}]

+ 4 - 3
deps/shui/src/logseq/shui/dialog/core.cljs

@@ -122,9 +122,10 @@
                              (on-open-change {:value v :set-open! set-open!})
                              (set-open! v))))}
       (dialog-content props
-        (dialog-header
-          (when title (dialog-title title))
-          (when description (dialog-description description)))
+        (when title
+          (dialog-header
+            (when title (dialog-title title))
+            (when description (dialog-description description))))
         (when content
           [:div.ui__dialog-main-content content])
         (when footer

+ 3 - 0
deps/shui/src/logseq/shui/popup/core.cljs

@@ -9,10 +9,13 @@
 (def popover (util/lsui-wrap "Popover"))
 (def popover-trigger (util/lsui-wrap "PopoverTrigger"))
 (def popover-content (util/lsui-wrap "PopoverContent"))
+(def popover-arrow (util/lsui-wrap "PopoverArrow"))
+(def popover-close (util/lsui-wrap "PopoverClose"))
 (def popover-remove-scroll (util/lsui-wrap "PopoverRemoveScroll"))
 (def dropdown-menu (util/lsui-wrap "DropdownMenu"))
 (def dropdown-menu-trigger (util/lsui-wrap "DropdownMenuTrigger"))
 (def dropdown-menu-content (util/lsui-wrap "DropdownMenuContent"))
+(def dropdown-menu-arrow (util/lsui-wrap "DropdownMenuArrow"))
 (def dropdown-menu-group (util/lsui-wrap "DropdownMenuGroup"))
 (def dropdown-menu-item (util/lsui-wrap "DropdownMenuItem"))
 (def dropdown-menu-checkbox-item (util/lsui-wrap "DropdownMenuCheckboxItem"))

+ 2 - 2
e2e-tests/utils.ts

@@ -140,12 +140,12 @@ export async function loadLocalGraph(page: Page, path: string): Promise<void> {
       await expect(sidebar).toHaveClass(/is-open/)
     }
 
-    await page.click('#left-sidebar #repo-switch');
+    await page.click('#left-sidebar .repo-switch');
     await page.waitForSelector('#left-sidebar .dropdown-wrapper >> text="Add new graph"',
       { state: 'visible', timeout: 5000 })
     await page.click('text=Add new graph')
 
-    expect(page.locator('#repo-name')).toHaveText(pathlib.basename(path))
+    expect(page.locator('.repo-name')).toHaveText(pathlib.basename(path))
   }
 
   setMockedOpenDirPath(page, ''); // reset it

+ 1 - 2
package.json

@@ -9,7 +9,6 @@
         "@playwright/test": "=1.31.0",
         "@tailwindcss/aspect-ratio": "0.4.2",
         "@tailwindcss/forms": "0.5.3",
-        "@tailwindcss/line-clamp": "0.4.2",
         "@tailwindcss/typography": "0.5.7",
         "@types/gulp": "^4.0.7",
         "autoprefixer": "^10.4.13",
@@ -134,7 +133,7 @@
         "inter-ui": "^3.19.3",
         "interactjs": "^1.10.17",
         "jszip": "3.8.0",
-        "katex": "^0.16.7",
+        "katex": "^0.16.10",
         "marked": "^5.1.2",
         "mldoc": "^1.5.8",
         "path": "0.12.7",

+ 3 - 0
packages/ui/@/components/ui/dropdown-menu.tsx

@@ -8,6 +8,8 @@ const DropdownMenu = DropdownMenuPrimitive.Root
 
 const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
 
+const DropdownMenuArrow = DropdownMenuPrimitive.Arrow
+
 const DropdownMenuGroup = DropdownMenuPrimitive.Group
 
 const DropdownMenuPortal = DropdownMenuPrimitive.Portal
@@ -188,6 +190,7 @@ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
 export {
   DropdownMenu,
   DropdownMenuTrigger,
+  DropdownMenuArrow,
   DropdownMenuContent,
   DropdownMenuItem,
   DropdownMenuCheckboxItem,

+ 3 - 1
packages/ui/@/components/ui/popover.tsx

@@ -11,6 +11,8 @@ const PopoverTrigger = PopoverPrimitive.Trigger
 
 const PopoverArrow = PopoverPrimitive.Arrow
 
+const PopoverClose = PopoverPrimitive.Close
+
 const PopoverContent = React.forwardRef<
   React.ElementRef<typeof PopoverPrimitive.Content>,
   React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
@@ -33,4 +35,4 @@ PopoverContent.displayName = PopoverPrimitive.Content.displayName
 
 const PopoverRemoveScroll = RemoveScroll
 
-export { Popover, PopoverTrigger, PopoverRemoveScroll, PopoverContent, PopoverArrow }
+export { Popover, PopoverTrigger, PopoverRemoveScroll, PopoverContent, PopoverArrow, PopoverClose }

+ 2 - 2
packages/ui/src/vars-classic.css

@@ -3,8 +3,8 @@
   --ls-tag-text-hover-opacity: 1;
   --ls-page-text-size: 1em;
   --ls-page-title-size: 36px;
-  --ls-main-content-max-width: 810px;
-  --ls-main-content-max-width-wide: 1280px;
+  --ls-main-content-max-width: 960px;
+  --ls-main-content-max-width-wide: 1440px;
   --ls-font-family: Inter;
   --ls-scrollbar-width: 6px;
   --ls-border-radius-low: 4px;

+ 2 - 1
resources/forge.config.js

@@ -20,9 +20,10 @@ module.exports = {
       'signature-flags': 'library'
     },
     osxNotarize: process.env['APPLE_ID'] ? {
+      tool: 'notarytool',
       appleId: process.env['APPLE_ID'],
       appleIdPassword: process.env['APPLE_ID_PASSWORD'],
-      ascProvider: process.env['APPLE_ASC_PROVIDER']
+      teamId: process.env['APPLE_TEAM_ID']
     } : undefined,
   },
   makers: [

+ 6 - 6
resources/package.json

@@ -46,12 +46,12 @@
     "update-electron-app": "2.0.1"
   },
   "devDependencies": {
-    "@electron-forge/cli": "^6.0.4",
-    "@electron-forge/maker-deb": "^6.0.4",
-    "@electron-forge/maker-dmg": "^6.0.4",
-    "@electron-forge/maker-rpm": "^6.0.4",
-    "@electron-forge/maker-squirrel": "^6.0.4",
-    "@electron-forge/maker-zip": "^6.0.4",
+    "@electron-forge/cli": "^7.3.1",
+    "@electron-forge/maker-deb": "^7.3.1",
+    "@electron-forge/maker-dmg": "^7.3.1",
+    "@electron-forge/maker-rpm": "^7.3.1",
+    "@electron-forge/maker-squirrel": "^7.3.1",
+    "@electron-forge/maker-zip": "^7.3.1",
     "@electron/rebuild": "3.2.10",
     "electron": "27.1.3",
     "electron-builder": "^22.11.7",

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

@@ -187,7 +187,7 @@
         blocks-tx (create-graph/create-blocks-tx
                    @conn
                    (create-init-data)
-                   {:property-uuids {:icon (:block/uuid (d/entity @conn [:block/name "icon"]))}})]
+                   {:property-uuids {:icon (:block/uuid (d/entity @conn :logseq.property/icon))}})]
     (println "Generating" (count (filter :block/name blocks-tx)) "pages and"
              (count (filter :block/content blocks-tx)) "blocks ...")
     (d/transact! conn blocks-tx)

+ 2 - 2
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -75,7 +75,7 @@
                            (class-m "rdfs:comment")
                            (assoc :description (get-comment-string (class-m "rdfs:comment") renamed-pages)))}
       parent-class
-      (assoc :block/namespace {:db/id (get-class-db-id class-db-ids parent-class)})
+      (assoc :class/parent {:db/id (get-class-db-id class-db-ids parent-class)})
       (seq properties)
       (assoc :block/schema {:properties (mapv property-uuids properties)}))))
 
@@ -252,7 +252,7 @@
         pages (mapv #(hash-map :page
                                (->class-page % class-db-ids class-uuids class-to-properties property-uuids options))
                     select-classes)]
-    (assert (= ["Thing"] (keep #(when-not (:block/namespace (:page %))
+    (assert (= ["Thing"] (keep #(when-not (:class/parent (:page %))
                                   (:block/original-name (:page %)))
                                pages))
             "Thing is the only class that doesn't have a parent class")

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

@@ -31,6 +31,7 @@ body {
   line-height: 1.5;
   min-height: 100%;
   word-break: break-word; /* compatible for overflow-wrap: anywhere */
+  overflow-y: overlay;
 }
 
 @layer base {

+ 27 - 25
src/main/frontend/components/block.cljs

@@ -775,8 +775,8 @@
 
 (rum/defc page-reference < rum/reactive
   "Component for page reference"
-  [html-export? s {:keys [nested-link? id] :as config} label]
-  (let [show-brackets? (state/show-brackets?)
+  [html-export? s {:keys [nested-link? show-brackets? id] :as config} label]
+  (let [show-brackets? (if (some? show-brackets?) show-brackets? (state/show-brackets?))
         block-uuid (:block/uuid config)
         contents-page? (= "contents" (string/lower-case (str id)))]
     (if (string/ends-with? s ".excalidraw")
@@ -911,8 +911,8 @@
             db-id (:db/id block)
             block (when db-id (db/sub-block db-id))
             properties (:block/properties block)
-            block-type (keyword (pu/lookup properties :ls-type))
-            hl-type (pu/lookup properties :hl-type)
+            block-type (keyword (pu/lookup properties :logseq.property/ls-type))
+            hl-type (pu/lookup properties :logseq.property/hl-type)
             repo (state/get-current-repo)
             stop-inner-events? (= block-type :whiteboard-shape)]
         (if (and block (:block/content block))
@@ -1424,16 +1424,18 @@
   [name config arguments]
   (if-let [block-uuid (:block/uuid config)]
     (let [format (get-in config [:block :block/format] :markdown)
-          properties (-> (db/entity [:block/uuid block-uuid])
-                         (:block/page)
-                         (:db/id)
-                         (db/entity)
-                         :block/properties)
-          macros (pu/lookup properties :macros)
-          macro-content (or
-                         (get macros name)
-                         (get (state/get-macros) name)
-                         (get (state/get-macros) (keyword name)))
+          ;; :macros is deprecated for db graphs
+          macros-from-property (when (config/local-file-based-graph? (state/get-current-repo))
+                                 (-> (db/entity [:block/uuid block-uuid])
+                                     (:block/page)
+                                     (:db/id)
+                                     (db/entity)
+                                     :block/properties
+                                     :macros
+                                     (get name)))
+          macro-content (or macros-from-property
+                            (get (state/get-macros) name)
+                            (get (state/get-macros) (keyword name)))
           macro-content (cond
                           (= (str name) "img")
                           (case (count arguments)
@@ -1969,7 +1971,7 @@
         slide? (boolean (:slide? config))
         block-ref? (:block-ref? config)
         block-type (or (keyword
-                        (pu/lookup properties :ls-type))
+                        (pu/lookup properties :logseq.property/ls-type))
                        :default)
         html-export? (:html-export? config)
         checkbox (when (and (not pre-block?)
@@ -1980,14 +1982,14 @@
                         (marker-switch t))
         marker-cp (marker-cp t)
         priority (priority-cp t)
-        bg-color (pu/lookup properties :background-color)
+        bg-color (pu/lookup properties :logseq.property/background-color)
         ;; `heading-level` is for backward compatibility, will remove it in later releases
         heading-level (:block/heading-level t)
         heading (or
                  (and heading-level
                       (<= heading-level 6)
                       heading-level)
-                 (pu/lookup properties :heading))
+                 (pu/lookup properties :logseq.property/heading))
         heading (if (true? heading) (min (inc level) 6) heading)
         elem (if heading
                (keyword (str "h" heading
@@ -1996,7 +1998,7 @@
     (->elem
      elem
      (merge
-      {:data-hl-type (pu/lookup properties :hl-type)}
+      {:data-hl-type (pu/lookup properties :logseq.property/hl-type)}
       (when (and marker
                  (not (string/blank? marker))
                  (not= "nil" marker))
@@ -2010,7 +2012,7 @@
            :class "px-1 with-bg-color"})))
 
      ;; children
-     (let [area?  (= :area (keyword (pu/lookup properties :hl-type)))
+     (let [area?  (= :area (keyword (pu/lookup properties :logseq.property/hl-type)))
            hl-ref #(when (and (or config/publishing? (util/electron?))
                               (not (#{:default :whiteboard-shape} block-type)))
                      [:div.prefix-link
@@ -2030,12 +2032,12 @@
 
                       [:span.hl-page
                        [:strong.forbid-edit (str "P" (or
-                                                      (pu/lookup properties :hl-page)
+                                                      (pu/lookup properties :logseq.property/hl-page)
                                                       "?"))]
                        [:label.blank " "]]
 
                       (when (and area?
-                                 (pu/lookup properties :hl-stamp))
+                                 (pu/lookup properties :logseq.property/hl-stamp))
                         (pdf-assets/area-display t))])]
        (remove-nils
         (concat
@@ -2374,7 +2376,7 @@
         stop-events? (:stop-events? config)
         block-ref-with-title? (and block-ref? (not (state/show-full-blocks?)) (seq title))
         block-type (or
-                    (pu/lookup properties :ls-type)
+                    (pu/lookup properties :logseq.property/ls-type)
                     :default)
         content (if (string? content) (string/trim content) "")
         mouse-down-key (if (util/ios?)
@@ -2387,9 +2389,9 @@
                 :style {:width "100%" :pointer-events (when stop-events? "none")}}
 
                 (not (string/blank?
-                      (pu/lookup properties :hl-color)))
+                      (pu/lookup properties :logseq.property/hl-color)))
                 (assoc :data-hl-color
-                       (pu/lookup properties :hl-color))
+                       (pu/lookup properties :logseq.property/hl-color))
 
                 (not block-ref?)
                 (assoc mouse-down-key (fn [e]
@@ -3014,7 +3016,7 @@
         {:block/keys [uuid pre-block? content properties]} block
         config (build-config config* block {:navigated? navigated? :navigating-block navigating-block})
         level (:level config)
-        heading? (pu/lookup properties :heading)
+        heading? (pu/lookup properties :logseq.property/heading)
         *control-show? (get container-state ::control-show?)
         db-collapsed? (util/collapsed? block)
         collapsed? (cond

+ 1 - 1
src/main/frontend/components/block/macros.cljs

@@ -45,7 +45,7 @@
            :updated-at
            :block/updated-at
 
-           (let [vals (map #(pu/lookup (:block/properties %) f) result)
+           (let [vals (map #(pu/lookup-by-name (:block/properties %) f) result)
                  int? (some integer? vals)
                  repo (state/get-current-repo)
                  prop-key (if (config/db-based-graph? repo)

+ 39 - 11
src/main/frontend/components/class.cljs

@@ -6,12 +6,13 @@
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
-            [rum.core :as rum]))
+            [rum.core :as rum]
+            [frontend.components.block :as block]))
 
 (rum/defc class-select
   [page class on-select]
   (let [repo (state/get-current-repo)
-        children-pages (model/get-namespace-children repo (:db/id page))
+        children-pages (model/get-class-children repo (:db/id page))
         ;; Disallows cyclic hierarchies
         exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
                         (conj (:block/uuid page))) ; break cycle
@@ -53,9 +54,9 @@
                                       (if (seq value)
                                         (db/transact!
                                          [{:db/id (:db/id page)
-                                           :block/namespace [:block/uuid (uuid value)]}])
+                                           :class/parent [:block/uuid (uuid value)]}])
                                         (db/transact!
-                                         [[:db.fn/retractAttribute (:db/id page) :block/namespace]]))))]
+                                          [[:db.fn/retractAttribute (:db/id page) :class/parent]]))))]
       [:div.opacity-50.pointer.text-sm.cursor-pointer {:on-click #(reset! *show? true)}
        "Empty"])))
 
@@ -73,22 +74,22 @@
         [:div.col-span-2 "Parent class:"]
         (if config/publishing?
           [:div.col-span-3
-           (if-let [parent-class (some-> (:db/id (:block/namespace page))
+           (if-let [parent-class (some-> (:db/id (:class/parent page))
                                          db/entity
                                          :block/original-name)]
              [:a {:on-click #(route-handler/redirect-to-page! parent-class)}
               parent-class]
              "None")]
           [:div.col-span-3
-           (let [parent (some-> (:db/id (:block/namespace page))
+           (let [parent (some-> (:db/id (:class/parent page))
                                 db/entity)]
              (page-parent page parent))])]
 
-       (when (:block/namespace page)
-         (let [ancestor-pages (loop [namespaces [page]]
-                                (if-let [parent (:block/namespace (last namespaces))]
-                                  (recur (conj namespaces parent))
-                                  namespaces))
+       (when (:class/parent page)
+         (let [ancestor-pages (loop [parents [page]]
+                                (if-let [parent (:class/parent (last parents))]
+                                  (recur (conj parents parent))
+                                  parents))
                class-ancestors (map :block/original-name (reverse ancestor-pages))]
            (when (> (count class-ancestors) 2)
              [:div.grid.grid-cols-5.gap-1.items-center.class-ancestors
@@ -100,3 +101,30 @@
                                    [:span class-name]
                                    [:a {:on-click #(route-handler/redirect-to-page! class-name)} class-name]))
                                class-ancestors))]])))])))
+
+(defn class-children-aux
+  [class {:keys [default-collapsed?] :as opts}]
+  (let [children (:class/_parent class)]
+    (when (seq children)
+      [:ul
+       (for [child (sort-by :block/original-name children)]
+         (let [title [:li.ml-2 (block/page-reference false (:block/original-name child) {:show-brackets? false} nil)]]
+           (if (seq (:class/_parent child))
+             (ui/foldable
+              title
+              (class-children-aux child opts)
+              {:default-collapsed? default-collapsed?})
+             title)))])))
+
+(rum/defc class-children
+  [class]
+  (when (seq (:class/_parent class))
+    (let [children-pages (model/get-class-children (state/get-current-repo) (:db/id class))
+          ;; Expand children if there are about a pageful of total blocks to display
+          default-collapsed? (> (count children-pages) 30)]
+      [:div.mt-4
+       (ui/foldable
+        [:h2.font-medium "Child classes (" (count children-pages) ")"]
+        [:div.mt-2.ml-1 (class-children-aux class {:default-collapsed? default-collapsed?})]
+        {:default-collapsed? false
+         :title-trigger? true})])))

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

@@ -367,7 +367,7 @@
         {:aria-label "Navigation menu"}
         (repo/repos-dropdown)
 
-        [:div.nav-header.flex.flex-col.mt-2
+        [:div.nav-header.flex.flex-col.mt-1
          (let [page (:page default-home)]
            (if (and page (not (state/enable-journals? (state/get-current-repo))))
              (sidebar-item
@@ -955,7 +955,7 @@
       [:button#skip-to-main
        {:on-click #(ui/focus-element (ui/main-node))
         :on-key-up (fn [e]
-                     (when (= (.-key e) "Enter")
+                     (when (= "Enter" (.-key e))
                        (ui/focus-element (ui/main-node))))}
        (t :accessibility/skip-to-main-content)]
       [:div.#app-container

+ 6 - 2
src/main/frontend/components/container.css

@@ -534,7 +534,7 @@
   @apply w-auto md:max-w-4xl overflow-hidden;
 
   .settings-modal {
-    @apply -mx-6 -mt-10 -mb-6 rounded-xl;
+    @apply -mx-6 -mt-6 -mb-6 rounded-xl;
   }
 }
 
@@ -542,6 +542,10 @@
   width: 100%;
   max-width: var(--ls-main-content-max-width);
   flex: 1;
+
+  .page {
+    @apply px-6;
+  }
 }
 
 .cp__sidebar-help {
@@ -774,7 +778,7 @@
 
 /* Workaround for Linux Intel GPU text rendering issue GH#7233 */
 .is-electron.is-linux .cp__menubar-repos {
-  #repo-switch, .nav-header .flex-1 {
+  .repo-switch, .nav-header .flex-1 {
     position: relative;
   }
 }

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

@@ -186,7 +186,7 @@
         db? (config/db-based-graph? repo)]
     (when-let [block (db/entity [:block/uuid block-id])]
       (let [properties (:block/properties block)
-            heading (or (pu/lookup properties :heading)
+            heading (or (pu/lookup properties :logseq.property/heading)
                         false)]
         [:.menu-links-wrapper
          (ui/menu-background-color #(property-handler/set-block-property! repo block-id :logseq.property/background-color %)

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

@@ -584,7 +584,7 @@
   [block content format]
   (let [content (if content (str content) "")
         properties (:block/properties block)
-        heading (pu/lookup properties :heading)
+        heading (pu/lookup properties :logseq.property/heading)
         heading (if (true? heading)
                   (min (inc (:block/level block)) 6)
                   heading)]

+ 85 - 61
src/main/frontend/components/header.cljs

@@ -130,70 +130,94 @@
          "platform="
          (js/encodeURIComponent platform))))
 
-(rum/defc dropdown-menu < rum/reactive
-  < {:key-fn #(identity "repos-dropdown-menu")}
+(rum/defc toolbar-dots-menu < rum/reactive
   [{:keys [current-repo t]}]
   (let [page-menu (page-menu/page-menu nil)
         page-menu-and-hr (when (seq page-menu)
                            (concat page-menu [{:hr true}]))
-        login? (and (state/sub :auth/id-token) (user-handler/logged-in?))]
-    (ui/dropdown-with-links
-     (fn [{:keys [toggle-fn]}]
-       [:button.button.icon.toolbar-dots-btn
-        {:on-click toggle-fn
-         :title (t :header/more)}
-        (ui/icon "dots" {:size ui/icon-size})])
-     (->>
-      [(when (state/enable-editing?)
-         {:title (t :settings)
-          :options {:on-click state/open-settings!}
-          :icon (ui/icon "settings")})
-
-       (when config/lsp-enabled?
-         {:title (t :plugins)
-          :options {:on-click #(plugin-handler/goto-plugins-dashboard!)}
-          :icon (ui/icon "apps")})
-
-       (when config/lsp-enabled?
-         {:title (t :themes)
-          :options {:on-click #(plugins/open-select-theme!)}
-          :icon (ui/icon "palette")})
-
-       (when current-repo
-         {:title (t :export-graph)
-          :options {:on-click #(shui/dialog-open! export/export)}
-          :icon (ui/icon "database-export")})
-
-       (when (and current-repo (state/enable-editing?))
-         {:title (t :import)
-          :options {:href (rfe/href :import)}
-          :icon (ui/icon "file-upload")})
-
-       (when-not config/publishing?
-         {:title [:div.flex-row.flex.justify-between.items-center
-                  [:span (t :join-community)]]
-          :options {:href "https://discuss.logseq.com"
-                    :title (t :discourse-title)
-                    :target "_blank"}
-          :icon (ui/icon "brand-discord")})
-
-       (when config/publishing?
-         {:title (t :toggle-theme)
-          :options {:on-click #(state/toggle-theme!)}
-          :icon (ui/icon "bulb")})
-
-       (when login? {:hr true})
-       (when login?
-         {:item [:span.flex.flex-col.relative.group.pt-1
-                 [:b.leading-none (user-handler/username)]
-                 [:small.opacity-70 (user-handler/email)]
-                 [:i.absolute.opacity-0.group-hover:opacity-100.text-red-rx-09
-                  {:class "right-1 top-3" :title (t :logout)}
-                  (ui/icon "logout")]]
-          :options {:on-click #(user-handler/logout)}})]
-       (concat page-menu-and-hr)
-       (remove nil?))
-      {})))
+        login? (and (state/sub :auth/id-token) (user-handler/logged-in?))
+        items (fn []
+                (->>
+                  [(when (state/enable-editing?)
+                     {:title (t :settings)
+                      :options {:on-click state/open-settings!}
+                      :icon (ui/icon "settings")})
+
+                   (when config/lsp-enabled?
+                     {:title (t :plugins)
+                      :options {:on-click #(plugin-handler/goto-plugins-dashboard!)}
+                      :icon (ui/icon "apps")})
+
+                   (when config/lsp-enabled?
+                     {:title (t :themes)
+                      :options {:on-click #(plugins/open-select-theme!)}
+                      :icon (ui/icon "palette")})
+
+                   (when current-repo
+                     {:title (t :export-graph)
+                      :options {:on-click #(shui/dialog-open! export/export)}
+                      :icon (ui/icon "database-export")})
+
+                   (when (and current-repo (state/enable-editing?))
+                     {:title (t :import)
+                      :options {:href (rfe/href :import)}
+                      :icon (ui/icon "file-upload")})
+
+                   (when-not config/publishing?
+                     {:title [:div.flex-row.flex.justify-between.items-center
+                              [:span (t :join-community)]]
+                      :options {:href "https://discuss.logseq.com"
+                                :title (t :discourse-title)
+                                :target "_blank"}
+                      :icon (ui/icon "brand-discord")})
+
+                   (when config/publishing?
+                     {:title (t :toggle-theme)
+                      :options {:on-click #(state/toggle-theme!)}
+                      :icon (ui/icon "bulb")})
+
+                   (when login? {:hr true})
+                   (when login?
+                     {:item [:span.flex.flex-col.relative.group.pt-1.w-full
+                             [:b.leading-none (user-handler/username)]
+                             [:small.opacity-70 (user-handler/email)]
+                             [:i.absolute.opacity-0.group-hover:opacity-100.text-red-rx-09
+                              {:class "right-1 top-3" :title (t :logout)}
+                              (ui/icon "logout")]]
+                      :options {:on-click #(user-handler/logout)
+                                :class "w-full"}})]
+                  (concat page-menu-and-hr)
+                  (remove nil?)))]
+    [:button.button.icon.toolbar-dots-btn
+     {:title (t :header/more)
+      :on-click (fn [^js e]
+                  (shui/popup-show! (.-target e)
+                    (fn [{:keys [id]}]
+                      (for [{:keys [hr item title options icon]} (items)]
+                        (let [on-click' (:on-click options)
+                              href (:href options)]
+                          (if hr
+                            (shui/dropdown-menu-separator)
+                            (shui/dropdown-menu-item
+                             (assoc options
+                                    :on-click (fn [^js e]
+                                                (when on-click'
+                                                  (when-not (false? (on-click' e))
+                                                    (shui/popup-hide! id)))))
+                             (or item
+                                 (if href
+                                   [:a.flex.items-center.w-full
+                                    {:href href :on-click #(shui/popup-hide! id)
+                                     :style {:color "inherit"}}
+                                    [:span.flex.items-center.gap-1.w-full
+                                     icon [:div title]]]
+                                   [:span.flex.items-center.gap-1.w-full
+                                    icon [:div title]])))))))
+                    {:align "end"
+                     :as-dropdown? true
+                     :content-props {:class "w-64"
+                                     :align-offset -32}}))}
+     (ui/icon "dots" {:size ui/icon-size})]))
 
 (rum/defc back-and-forward
   < {:key-fn #(identity "nav-history-buttons")}
@@ -333,7 +357,7 @@
         [:a.text-sm.font-medium.button {:href (rfe/href :graph)}
          (t :graph)])
 
-      (dropdown-menu {:t            t
+      (toolbar-dots-menu {:t            t
                       :current-repo current-repo
                       :default-home default-home})
 

+ 0 - 5
src/main/frontend/components/header.css

@@ -172,11 +172,6 @@
   transform: scale(0.9);
 }
 
-#repo-name {
-  @apply inline-flex items-center whitespace-nowrap;
-  max-width: 16ch;
-}
-
 .button {
   @apply h-8 px-2.5 py-1 rounded-md opacity-90 block select-none hover:opacity-100 active:opacity-80;
 

+ 20 - 58
src/main/frontend/components/hierarchy.cljs

@@ -8,8 +8,7 @@
             [frontend.ui :as ui]
             [medley.core :as medley]
             [rum.core :as rum]
-            [frontend.util :as util]
-            [frontend.config :as config]))
+            [frontend.util :as util]))
 
 (defn- get-relation
   "Get all parent pages along the namespace hierarchy path.
@@ -44,62 +43,25 @@
             :else
             nil))))))
 
-(rum/defc page-children
-  [page-id parent-children-map namespace-page-map options]
-  [:.ml-4.mb-2
-   {:key (str "page-children-" page-id)}
-   (->> (parent-children-map page-id)
-        (sort-by #(get-in namespace-page-map [% :block/original-name]))
-        (map #(let [child-name (get-in namespace-page-map [% :block/original-name])]
-                (if (seq (parent-children-map %))
-                  (ui/foldable (block/page-reference false child-name {} child-name)
-                               (page-children % parent-children-map namespace-page-map options)
-                               (select-keys options [:default-collapsed?]))
-                  [:div
-                   (block/page-reference false child-name {} child-name)]))))])
-
-(rum/defc db-version-hierarchy
-  [page namespace-pages]
-  (let [parent-children-map (reduce (fn [acc m]
-                                      (update acc
-                                              (get-in m [:block/namespace :db/id])
-                                              (fnil conj [])
-                                              (:db/id m)))
-                                    {}
-                                    namespace-pages)
-        namespace-page-map (into {} (map (juxt :db/id identity) namespace-pages))
-        page-id (:db/id (db/entity [:block/name (util/page-name-sanity-lc page)]))
-        ;; Expand children if there are about a page-ful of total blocks to display
-        default-collapsed? (> (count namespace-pages) 30)]
-    [:div.page-hierarchy.mt-6
-     (ui/foldable
-      [:h2.font-bold.opacity-30 (str "Hierarchy (" (count namespace-pages) ")")]
-      [:div.p-4
-       (page-children page-id parent-children-map namespace-page-map {:default-collapsed? default-collapsed?})]
-      {:default-collapsed? false
-       :title-trigger? true})]))
-
 (rum/defc structures
   [page]
-  (let [{:keys [namespaces namespace-pages]} (get-relation page)]
+  (let [{:keys [namespaces]} (get-relation page)]
     (when (seq namespaces)
-      (if (config/db-based-graph? (state/get-current-repo))
-        (db-version-hierarchy page namespace-pages)
-        [:div.page-hierarchy.mt-6
-         (ui/foldable
-          [:h2.font-bold.opacity-30 "Hierarchy"]
-          [:ul.namespaces {:style {:margin "12px 24px"}}
-           (for [namespace namespaces]
-             [:li.my-2
-              (->>
-               (for [[idx page] (medley/indexed namespace)]
-                 (when (and (string? page) page)
-                   (let [full-page (->> (take (inc idx) namespace)
-                                        util/string-join-path)]
-                     (block/page-reference false
-                                           full-page
-                                           {}
-                                           page))))
-               (interpose [:span.mx-2.opacity-30 "/"]))])]
-          {:default-collapsed? false
-           :title-trigger? true})]))))
+      [:div.page-hierarchy.mt-6
+       (ui/foldable
+        [:h2.font-bold.opacity-30 "Hierarchy"]
+        [:ul.namespaces {:style {:margin "12px 24px"}}
+         (for [namespace namespaces]
+           [:li.my-2
+            (->>
+             (for [[idx page] (medley/indexed namespace)]
+               (when (and (string? page) page)
+                 (let [full-page (->> (take (inc idx) namespace)
+                                      util/string-join-path)]
+                   (block/page-reference false
+                                         full-page
+                                         {}
+                                         page))))
+             (interpose [:span.mx-2.opacity-30 "/"]))])]
+        {:default-collapsed? false
+         :title-trigger? true})])))

+ 2 - 8
src/main/frontend/components/journal.css

@@ -6,16 +6,10 @@
   }
 
   .journal-item {
-    border-top: 1px solid;
-    border-top-color: var(--lx-gray-07, var(--ls-border-color, var(--rx-gray-06)));
-    margin: 24px 0;
-    padding: 24px 0;
-    min-height: 250px;
+    @apply border-t min-h-[250px];
 
     &:first-child {
-      padding-top: 0;
-      border-top: none;
-      min-height: 500px;
+      @apply pt-0 border-none min-h-[500px];
     }
   }
 }

+ 55 - 12
src/main/frontend/components/page.css

@@ -229,13 +229,10 @@
 }
 
 .ls-page-title {
-  @apply rounded-sm;
-
-  padding: 5px 8px 10px 8px;
-  margin: 0 -6px;
+  @apply rounded-sm -mx-1.5 px-2 pt-1.5;
 
   &.title {
-    margin-bottom: 12px;
+    @apply mb-3;
   }
 
   .edit-input {
@@ -318,8 +315,24 @@ html.is-native-iphone-without-notch {
   }
 }
 
-.cp__right-sidebar .add-button-link {
-  margin-left: 21px;
+.cp__right-sidebar  {
+  .add-button-link {
+    margin-left: 21px;
+  }
+
+  .page-info {
+    @apply mx-2;
+
+    .ls-page-properties {}
+
+    &.is-collapsed {
+      @apply !py-0 -mb-1.5;
+
+      &:not(:has(.select-item)) {
+        @apply hidden;
+      }
+    }
+  }
 }
 
 html.is-native-android,
@@ -388,9 +401,43 @@ html.is-native-ios {
 }
 
 .page-info {
-  @apply min-h-[46px] ml-[-15px] mt-[-6px];
+  @apply ml-[-10px] mb-[12px] border rounded-md;
+
+  &-inner {
+    @apply py-2;
+  }
+
+  .ls-page-properties {
+    @apply bg-gray-03 rounded-md px-3 gap-1;
+  }
+
+  .configure-wrap {
+    @apply px-2;
+  }
+
+  .ls-new-property {
+    @apply mt-1;
+  }
 
   &.is-collapsed {
+    @apply border-transparent mt-[-4px] -mb-[14px];
+
+    &:has(.select-item) {
+      @apply py-2 relative -left-1;
+
+      .info-title {
+        @apply relative -top-1;
+      }
+    }
+
+    .page-info-inner {
+      @apply py-0 relative -top-1.5 min-h-[26px];
+
+      &:has(.ls-page-properties) {
+        @apply mb-3 opacity-90;
+      }
+    }
+
     .ls-new-property {
       @apply hidden;
     }
@@ -407,10 +454,6 @@ html.is-native-ios {
   }
 }
 
-.sidebar-item .page-info {
-  margin: 0 8px;
-}
-
 .page-info-title-placeholder {
   min-height: 28px;
 }

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

@@ -18,8 +18,7 @@
             [frontend.config :as config]
             [frontend.handler.user :as user-handler]
             [frontend.handler.file-sync :as file-sync-handler]
-            [logseq.common.path :as path]
-            [frontend.handler.property.util :as pu]))
+            [logseq.common.path :as path]))
 
 (defn- delete-page!
   [page-name]
@@ -57,7 +56,7 @@
           whiteboard? (contains? (set (:block/type page)) "whiteboard")
           block? (and page (util/uuid-string? page-name) (not whiteboard?))
           contents? (= page-name "contents")
-          public? (true? (pu/lookup (:block/properties page) :public))
+          public? (:logseq.property/public page)
           _favorites-updated? (state/sub :favorites/updated?)
           favorited? (page-handler/favorited? page-name)
           developer-mode? (state/sub [:ui/developer-mode?])

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

@@ -1405,8 +1405,8 @@
 
 (defn open-waiting-updates-modal!
   []
-  (state/set-sub-modal!
-    (fn [_close!]
+  (shui/dialog-open!
+    (fn []
       (waiting-coming-updates))
     {:center? true}))
 

+ 3 - 8
src/main/frontend/components/plugins.css

@@ -451,21 +451,16 @@
   }
 
   &-waiting-updates {
-    margin: -15px;
-
     > ul {
       li {
-        user-select: none;
-        justify-content: space-between;
-        opacity: .9;
+        @apply select-none justify-between opacity-90;
 
         sup {
-          padding-left: 8px;
-          font-weight: 400;
+          @apply pl-2 font-medium;
         }
 
         &:hover, &.checked {
-          opacity: 1;
+          @apply opacity-100;
         }
       }
     }

+ 99 - 80
src/main/frontend/components/property.cljs

@@ -15,7 +15,6 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.page :as page-handler]
             [frontend.modules.shortcut.core :as shortcut]
-            [frontend.search :as search]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -42,7 +41,7 @@
           (:block/uuid page))))))
 
 (rum/defc class-select
-  [*property-schema schema-classes {:keys [multiple-choices? save-property-fn]
+  [*property-schema schema-classes {:keys [multiple-choices? save-property-fn disabled?]
                                     :or {multiple-choices? true}}]
   [:div.flex.flex-1.col-span-3
    (let [content-fn
@@ -93,7 +92,9 @@
              (select/select opts)))]
 
     [:div.flex.flex-1.cursor-pointer
-     {:on-click #(shui/popup-show! (.-target %) content-fn)}
+     {:on-click (if disabled?
+                  (constantly nil)
+                  #(shui/popup-show! (.-target %) content-fn))}
      (if (seq schema-classes)
        [:div.flex.flex-1.flex-row.items-center.flex-wrap.gap-2
         (for [class schema-classes]
@@ -127,27 +128,27 @@
 
 (rum/defc schema-type <
   shortcut/disable-all-shortcuts
-  [property {:keys [*property-name *property-schema built-in-property? disabled?
+  [property {:keys [*property-name *property-schema built-in? disabled?
                     show-type-change-hints? in-block-container? block *show-new-property-config?
                     default-open?]}]
   (let [property-name (or (and *property-name @*property-name) (:block/original-name property))
         property-schema (or (and *property-schema @*property-schema) (:block/schema property))
         schema-types (->> (concat db-property-type/user-built-in-property-types
-                                  (when built-in-property?
+                                  (when built-in?
                                     db-property-type/internal-built-in-property-types))
                           (map (fn [type]
                                  {:label (property-type-label type)
-                                  :disabled disabled?
-                                  :value type
-                                  :selected (= type (:type property-schema))})))]
+                                  :value type})))]
     [:div {:class (if in-block-container? "flex flex-1" "flex items-center col-span-2")}
      (shui/select
-      {:default-open (boolean default-open?)
-       :on-value-change
-       (fn [v]
-         (let [type (keyword (string/lower-case v))
-               update-schema-fn (apply comp
-                                       #(assoc % :type type)
+      (cond->
+       {:default-open (boolean default-open?)
+        :disabled disabled?
+        :on-value-change
+        (fn [v]
+          (let [type (keyword (string/lower-case v))
+                update-schema-fn (apply comp
+                                        #(assoc % :type type)
                                              ;; always delete previous closed values as they
                                              ;; are not valid for the new type
                                        #(dissoc % :values)
@@ -164,7 +165,8 @@
              (p/do!
               (when block
                 (pv/exit-edit-property))
-              (reset! *show-new-property-config? false)
+              (when *show-new-property-config?
+                (reset! *show-new-property-config? false))
               (components-pu/update-property! property property-name schema)
               (when block
                 (let [id (str "ls-property-" (:db/id block) "-" (:db/id property) "-editor")]
@@ -179,15 +181,15 @@
         {:placeholder "Select a schema type"}))
       (shui/select-content
        (shui/select-group
-        (for [{:keys [label value disabled]} schema-types]
-          (shui/select-item {:value value :disabled disabled} label)))))
+        (for [{:keys [label value]} schema-types]
+          (shui/select-item {:value value} label)))))
 
      (when show-type-change-hints?
        (ui/tippy {:html        "Changing the property type clears some property configurations."
                   :class       "tippy-hover ml-2"
                   :interactive true
                   :disabled    false}
-                 (svg/info)))]))
+                 (svg/info))))]))
 
 (rum/defcs ^:large-vars/cleanup-todo property-config
   "All changes to a property must update the db and the *property-schema. Failure to do
@@ -215,12 +217,11 @@
                    (when-let [*show-property-config? (:*show-new-property-config? (last (:rum/args state)))]
                      (reset! *show-property-config? false))
                    state)}
-  [state property {:keys [inline-text add-new-property? _*show-new-property-config?] :as opts}]
+  [state property {:keys [inline-text add-new-property?] :as opts}]
   (let [values (rum/react (::values state))]
     (when-not (= :loading values)
       (let [*property-name (::property-name state)
             *property-schema (::property-schema state)
-            built-in-property? (contains? db-property/built-in-properties-keys-str (:block/original-name property))
             property (db/sub-block (:db/id property))
             built-in? (ldb/built-in? property)
             disabled? (or built-in? config/publishing?)
@@ -275,7 +276,7 @@
                         (svg/help-circle))]
              (schema-type property {:*property-name *property-name
                                     :*property-schema *property-schema
-                                    :built-in-property? built-in-property?
+                                    :built-in? built-in?
                                     :disabled? disabled?
                                     :show-type-change-hints? true}))]
 
@@ -364,18 +365,21 @@
                                     (swap! *property-schema assoc :hide? (not hide?))
                                     (save-property-fn))})])
 
-          [:div.grid.grid-cols-4.gap-1.items-start.leading-8
-           [:label "Description:"]
-           [:div.col-span-3
-            (if (and disabled? inline-text)
-              (inline-text {} :markdown (:description @*property-schema))
-              [:div.mt-1
-               (shui/textarea
-                {:on-change     (fn [e]
-                                  (swap! *property-schema assoc :description (util/evalue e)))
-                 :on-blur       save-property-fn
-                 :disabled      disabled?
-                 :default-value (:description @*property-schema)})])]]]]))))
+          (let [description (:description @*property-schema)]
+            (when (or (not disabled?)
+                    (and disabled? (not (string/blank? description))))
+              [:div.grid.grid-cols-4.gap-1.items-start.leading-8
+               [:label "Description:"]
+               [:div.col-span-3
+                (if (and disabled? inline-text)
+                  (inline-text {} :markdown description)
+                  [:div.mt-1
+                   (shui/textarea
+                     {:on-change (fn [e]
+                                   (swap! *property-schema assoc :description (util/evalue e)))
+                      :on-blur save-property-fn
+                      :disabled disabled?
+                      :default-value description})])]]))]]))))
 
 (defn- get-property-from-db [name]
   (when-not (string/blank? name)
@@ -388,15 +392,16 @@
   (let [repo (state/get-current-repo)]
     ;; existing property selected or entered
     (if-let [property (get-property-from-db property-name)]
-      (if (contains? db-property/hidden-built-in-properties (keyword property-name))
-        (do (notification/show! "This is a built-in property that can't be used." :error)
+      (if (and (not (get-in property [:block/schema :public?]))
+               (ldb/built-in? property))
+        (do (notification/show! "This is a private built-in property that can't be used." :error)
             (pv/exit-edit-property))
         ;; Both conditions necessary so that a class can add its own page properties
         (when (and (contains? (:block/type entity) "class") class-schema?)
-          (pv/<add-property! entity (:db/ident property) "" {:class-schema? class-schema?
-                                                             ;; Only enter property names from sub-modal as inputting
-                                                             ;; property values is buggy in sub-modal
-                                                             :exit-edit? page-configure?})))
+          (pv/<add-property! entity property-name "" {:class-schema? class-schema?
+                                                     ;; Only enter property names from sub-modal as inputting
+                                                     ;; property values is buggy in sub-modal
+                                                      :exit-edit? page-configure?})))
       ;; new property entered
       (if (db-property/valid-property-name? property-name)
         (if (and (contains? (:block/type entity) "class") page-configure?)
@@ -409,11 +414,17 @@
 
 (rum/defc property-select
   [exclude-properties on-chosen input-opts]
-  (let [[properties set-properties!] (rum/use-state nil)]
+  (let [[properties set-properties!] (rum/use-state nil)
+        [excluded-properties set-excluded-properties!] (rum/use-state nil)]
     (rum/use-effect!
      (fn []
-       (p/let [properties (search/get-all-properties)]
-         (set-properties! (remove exclude-properties properties))))
+       (p/let [properties (db-async/<db-based-get-all-properties (state/get-current-repo))]
+         (set-properties! (map :block/original-name (remove exclude-properties properties)))
+         (set-excluded-properties! (->> properties
+                                        (filter exclude-properties)
+                                        ;; lower case b/c of case insensitive name lookups
+                                        (map (comp string/lower-case :block/original-name))
+                                        set))))
      [])
     [:div.ls-property-add.flex.flex-row.items-center
     [:span.bullet-container.cursor [:span.bullet]]
@@ -423,7 +434,7 @@
                      :dropdown? true
                      :close-modal? false
                      :show-new-when-not-exact-match? true
-                     :exact-match-exclude-items exclude-properties
+                     :exact-match-exclude-items (fn [s] (contains? excluded-properties (string/lower-case s)))
                      :input-default-placeholder "Add property"
                      :on-chosen on-chosen
                      :input-opts input-opts})]]))
@@ -435,23 +446,23 @@
                      (reset! *property-key nil))
                    state)}
   shortcut/disable-all-shortcuts
-  [state entity *property-key *property-value {:keys [class-schema? _page-configure? in-block-container?]
+  [state entity *property-key *property-value {:keys [class-schema? in-block-container? page?]
                                                :as opts}]
   (let [*show-new-property-config? (::show-new-property-config? state)
         entity-properties (->> (keys (:block/properties entity))
                                (map #(:block/original-name (db/entity %)))
                                (remove nil?)
                                (set))
-        existing-tag-alias (reduce (fn [acc prop]
-                                     (if (seq (get entity (get-in db-property/built-in-properties [prop :attribute])))
-                                       (if-let [name (get-in db-property/built-in-properties [prop :original-name])]
-                                         (conj acc name)
-                                         acc)
-                                       acc))
-                                   #{}
-                                   [:tags :alias])
-        exclude-properties* (set/union entity-properties existing-tag-alias)
-        exclude-properties (set/union exclude-properties* (set (map string/lower-case exclude-properties*)))]
+        existing-tag-alias (->> [:block/tags :block/alias]
+                                (map db-property/built-in-properties)
+                                (keep #(when (get entity (:attribute %)) (:original-name %)))
+                                set)
+        exclude-property-names (set/union entity-properties existing-tag-alias)
+        exclude-properties (fn [m]
+                             (or (contains? exclude-property-names (:block/original-name m))
+                                 ;; Filters out properties from being in wrong :view-context
+                                 (and in-block-container? (= :page (get-in m [:block/schema :view-context])))
+                                 (and page? (= :block (get-in m [:block/schema :view-context])))))]
     [:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1
      (if in-block-container? {:style {:padding-left 22}} {})
      (if @*property-key
@@ -519,7 +530,7 @@
                          (reset! *new-property? true))}
             [:div.flex.flex-row.items-center {:style {:padding-left 1}}
              (ui/icon "plus" {:size 15})
-             [:div.ml-1.text-sm {:style {:padding-left 2}} "Add property"]]]
+             [:div.ml-1 {:style {:padding-left 2}} "Add property"]]]
 
            :else
            [:div {:style {:height 28}}]))])))
@@ -580,6 +591,7 @@
                                                                                (:db/ident property)
                                                                                {:properties {:logseq.property/icon icon}})]
                                 (shui/popup-hide! id))))}))]
+
        (shui/trigger-as :button
                         (-> (when-not config/publishing?
                               {:on-click #(shui/popup-show! (.-target %) content-fn {:as-dropdown? true :auto-focus? true})})
@@ -595,29 +607,34 @@
         (:block/original-name property)]
 
        (shui/trigger-as :a
-                        {:tabIndex 0
-                         :title (str "Configure property: " (:block/original-name property))
-                         :class "property-k flex select-none jtrigger pl-2"
-                         :on-pointer-down (fn [^js e]
-                                            (when (util/meta-key? e)
-                                              (route-handler/redirect-to-page! (:block/name property))
-                                              (.preventDefault e)))
-                         :on-click (fn [^js e]
-                                     (shui/popup-show!
-                                      (.-target e)
-                                      (fn [_]
-                                        [:div.p-2
-                                         [:h2.text-lg.font-medium.mb-2.p-1 "Configure property"]
-                                         (property-config property
-                                                          {:inline-text inline-text
-                                                           :page-cp page-cp})])
-                                      {:content-props {:class "property-configure-popup-content"
-                                                       :collisionPadding {:bottom 10 :top 10}
-                                                       :avoidCollisions true
-                                                       :align "start"}
-                                       :auto-side? true
-                                       :auto-focus? true}))}
-                        (:block/original-name property)))]))
+         {:tabIndex 0
+          :title (str "Configure property: " (:block/original-name property))
+          :class "property-k flex select-none jtrigger pl-2"
+          :on-pointer-down (fn [^js e]
+                             (when (util/meta-key? e)
+                               (route-handler/redirect-to-page! (:block/name property))
+                               (.preventDefault e)))
+          :on-click (fn [^js e]
+                      (shui/popup-show!
+                        (.-target e)
+                        (fn [{:keys [id]}]
+                          [:div.p-2
+                           [:h2.text-lg.font-medium.mb-2.p-1 "Configure property"]
+                           [:span.close.absolute.right-2.top-2
+                            (shui/button
+                              {:variant :ghost :size :sm :class "!w-4 !h-6"
+                               :on-click #(shui/popup-hide! id)}
+                              (shui/tabler-icon "x" {:size 16}))]
+                           (property-config property
+                             {:inline-text inline-text
+                              :page-cp page-cp})])
+                        {:content-props {:class "property-configure-popup-content"
+                                         :collisionPadding {:bottom 10 :top 10}
+                                         :avoidCollisions true
+                                         :align "start"}
+                         :auto-side? true
+                         :auto-focus? true}))}
+         (:block/original-name property)))]))
 
 (defn- resolve-linked-block-if-exists
   "Properties will be updated for the linked page instead of the refed block.
@@ -708,8 +725,10 @@
         alias-properties (when (seq alias)
                            [[:block/alias alias]])
         remove-built-in-properties (fn [properties]
-                                     (remove (fn [property-id]
-                                               (contains? db-property/hidden-built-in-properties property-id))
+                                     (remove (fn [id]
+                                               (when-let [ent (db/entity id)]
+                                                   (and (not (get-in ent [:block/schema :public?]))
+                                                        (ldb/built-in? ent))))
                                              properties))
         {:keys [classes all-classes classes-properties]} (db-property-handler/get-block-classes-properties (:db/id block))
         one-class? (= 1 (count classes))

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

@@ -47,7 +47,7 @@
 }
 
 .ls-properties-area {
-    @apply grid gap-2 py-2;
+    @apply grid gap-0.5 pt-2 pb-1.5;
 
     .property-pair {
         @apply grid grid-cols-5 gap-1;

+ 5 - 5
src/main/frontend/components/query_table.cljs

@@ -72,12 +72,12 @@
   done"
   [current-block {:keys [db-graph?]}]
   (let [properties (:block/properties current-block)
-        p-desc? (pu/lookup properties :query-sort-desc)
+        p-desc? (pu/lookup properties :logseq.property/query-sort-desc)
         desc? (if (some? p-desc?) p-desc? true)
         properties (:block/properties current-block)
-        query-sort-by (pu/lookup properties :query-sort-by)
+        query-sort-by (pu/lookup properties :logseq.property/query-sort-by)
         ;; Starting with #6105, we started putting properties under namespaces.
-        nlp-date? (and (not db-graph?) (pu/lookup properties :logseq.query/nlp-date))
+        nlp-date? (and (not db-graph?) (pu/lookup-by-name properties :logseq.query/nlp-date))
         sort-by-column (or (if (uuid? query-sort-by) query-sort-by (keyword query-sort-by))
                            (if (query-dsl/query-contains-filter? (:block/content current-block) "sort-by")
                              nil
@@ -113,7 +113,7 @@
                             ;; TODO: Support additional hidden properties e.g. from user config
                             ;; or gp-property/built-in-extended properties
                             (set (map #(db-pu/get-built-in-property-uuid repo %)
-                                      db-property/built-in-properties-keys-str))
+                                      (keys db-property/built-in-properties)))
                             (conj (file-property-handler/built-in-properties) :template))
         prop-keys* (->> (distinct (mapcat keys (map :block/properties result)))
                         (remove hidden-properties))
@@ -124,7 +124,7 @@
 
 (defn get-columns [current-block result {:keys [page?]}]
   (let [properties (:block/properties current-block)
-        query-properties (pu/lookup properties :query-properties)
+        query-properties (pu/lookup properties :logseq.property/query-properties)
         query-properties (if (config/db-based-graph? (state/get-current-repo))
                            query-properties
                            (some-> query-properties

+ 134 - 103
src/main/frontend/components/repo.cljs

@@ -65,10 +65,9 @@
 
      [:div.controls
       [:div.flex.flex-row.items-center
-       (let [current-repo (state/get-current-repo)
-             db-graph? (config/db-based-graph? current-repo)
-             manager? (and db-graph? (user-handler/manager? current-repo))]
-         (when-not (and db-graph? only-cloud? (not manager?))
+       (let [db-graph? (config/db-based-graph? url)
+             manager? (and db-graph? (user-handler/manager? url))]
+         (when-not (and only-cloud? (not manager?))
            (ui/tippy {:html [:div.text-sm.max-w-xs
                              (cond
                                only-cloud?
@@ -90,10 +89,10 @@
                                                           (str "Are you sure to permanently delete the graph \"" url "\" from Logseq?")
                                                           :else
                                                           "")
-                                         unlink-or-remote-fn (fn []
+                                         unlink-or-remote-fn! (fn []
                                                                (repo-handler/remove-repo! repo)
-                                                               (state/pub-event! [:graph/unlinked repo current-repo]))
-                                         action-confirm-fn (if only-cloud?
+                                                               (state/pub-event! [:graph/unlinked repo (state/get-current-repo)]))
+                                         action-confirm-fn! (if only-cloud?
                                                              (fn []
                                                                (when (or manager? (not db-graph?))
                                                                  (let [delete-graph (if db-graph?
@@ -104,21 +103,18 @@
                                                                        (state/delete-repo! repo)
                                                                        (state/delete-remote-graph! repo)
                                                                        (state/set-state! [:file-sync/remote-graphs :loading] false)))))
-                                                             unlink-or-remote-fn)
-                                         confirm-fn
+                                                             unlink-or-remote-fn!)
+                                         confirm-fn!
                                          (fn []
-                                           (ui/make-confirm-modal
-                                            {:title      [:div
-                                                          {:style {:max-width 700}}
-                                                          prompt-str]
-                                             :sub-title   [:div.small.mt-1
-                                                           "Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."]
-                                             :on-confirm (fn [_ {:keys [close-fn]}]
-                                                           (close-fn)
-                                                           (action-confirm-fn))}))]
+                                           (-> (shui/dialog-confirm!
+                                                 [:p.font-medium.-my-4 prompt-str
+                                                  [:span.mt-1.flex.font-normal.opacity-40
+                                                   [:small "Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."]]])
+                                             (p/then #(action-confirm-fn!))))]
+
                                      (if has-prompt?
-                                       (state/set-modal! (confirm-fn))
-                                       (unlink-or-remote-fn))))}
+                                       (confirm-fn!)
+                                       (unlink-or-remote-fn!))))}
                       (if only-cloud? "Remove (server)" "Unlink (local)")])))]]]))
 
 (rum/defc repos < rum/reactive
@@ -127,8 +123,8 @@
         repos (state/sub [:me :repos])
         repos (util/distinct-by :url repos)
         remotes (concat
-                 (state/sub :rtc/graphs)
-                 (state/sub [:file-sync/remote-graphs :graphs]))
+                  (state/sub :rtc/graphs)
+                  (state/sub [:file-sync/remote-graphs :graphs]))
         remotes-loading? (state/sub [:file-sync/remote-graphs :loading])
         repos (if (and login? (seq remotes))
                 (repo-handler/combine-local-&-remote-graphs repos remotes) repos)
@@ -147,11 +143,11 @@
 
          [:div.flex.flex-row.my-4
           (when (or (nfs-handler/supported?)
-                    (mobile-util/native-platform?))
+                  (mobile-util/native-platform?))
             [:div.mr-8
              (ui/button
-              (t :open-a-directory)
-              :on-click #(state/pub-event! [:graph/setup-a-repo]))])]]
+               (t :open-a-directory)
+               :on-click #(state/pub-event! [:graph/setup-a-repo]))])]]
 
         (when (and (file-sync/enable-sync?) login?)
           [:div
@@ -160,13 +156,13 @@
             [:h2.text-lg.font-medium.my-4 (t :graph/remote-graphs)]
             [:div
              (ui/button
-              [:span.flex.items-center "Refresh"
-               (when remotes-loading? [:small.pl-2 (ui/loading nil)])]
-              :background "gray"
-              :disabled remotes-loading?
-              :on-click (fn []
-                          (file-sync/load-session-graphs)
-                          (rtc-handler/<get-remote-graphs)))]]
+               [:span.flex.items-center "Refresh"
+                (when remotes-loading? [:small.pl-2 (ui/loading nil)])]
+               :background "gray"
+               :disabled remotes-loading?
+               :on-click (fn []
+                           (file-sync/load-session-graphs)
+                           (rtc-handler/<get-remote-graphs)))]]
            (repos-inner remote-graphs)])]]
       (widgets/add-graph))))
 
@@ -180,19 +176,19 @@
   (let [switch-repos (if-not (nil? current-repo)
                        (remove (fn [repo] (= current-repo (:url repo))) repos) repos) ; exclude current repo
         repo-links (mapv
-                    (fn [{:keys [url remote? rtc-graph? GraphName GraphUUID] :as graph}]
-                      (let [local? (config/local-file-based-graph? url)
-                            db-only? (config/db-based-graph? url)
-                            repo-url (cond
-                                       local? (db/get-repo-name url)
-                                       db-only? url
+                     (fn [{:keys [url remote? rtc-graph? GraphName GraphUUID] :as graph}]
+                       (let [local? (config/local-file-based-graph? url)
+                             db-only? (config/db-based-graph? url)
+                             repo-url (cond
+                                        local? (db/get-repo-name url)
+                                        db-only? url
                                        :else GraphName)
                             short-repo-name (if (or local? db-only?)
                                               (text-util/get-graph-name-from-path repo-url)
                                               GraphName)
                             downloading? (and downloading-graph-id (= GraphUUID downloading-graph-id))]
                         (when short-repo-name
-                          {:title        [:span.flex.items-center.whitespace-nowrap short-repo-name
+                          {:title        [:span.flex.items-center.title-wrap short-repo-name
                                           (when remote? [:span.pl-1.flex.items-center
                                                          {:title (str "<" GraphName "> #" GraphUUID)}
                                                          (ui/icon "cloud" {:size 18})
@@ -259,68 +255,93 @@
             downloading-graph-id (state/sub :rtc/downloading-graph-uuid)
             repos (if (and (or (seq remotes) (seq rtc-graphs)) login?)
                     (repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos)
-            links (repos-dropdown-links repos current-repo downloading-graph-id multiple-windows? opts)
-            render-content (fn [{:keys [toggle-fn]}]
-                             (let [remote? (:remote? (first (filter #(= current-repo (:url %)) repos)))
-                                   repo-name (db/get-repo-name current-repo)
-                                   short-repo-name (if repo-name
-                                                     (db/get-short-repo-name repo-name)
-                                                     "Select a Graph")]
-                               [:a.item.group.flex.items-center.p-2.text-sm.font-medium.rounded-md
-
-                                {:on-click (fn [_e]
-                                             (check-multiple-windows? state)
-                                             (toggle-fn))
-                                 :title    repo-name}       ;; show full path on hover
-                                [:div.flex.flex-row.items-center
-                                 [:div.flex.relative.graph-icon.rounded
-                                  (let [icon "database"
-                                        opts {:size 14}]
-                                    (ui/icon icon opts))]
+            items-fn #(repos-dropdown-links repos current-repo downloading-graph-id multiple-windows? opts)
+            header-fn #(when (> (count repos) 1)            ; show switch to if there are multiple repos
+                         [:div.font-medium.text-sm.opacity-50.px-1.py-1.flex.flex-row.justify-between.items-center
+                          [:div (t :left-side-bar/switch)]
 
-                                 [:div.graphs
-                                  [:span#repo-switch.block.pr-2.whitespace-nowrap
-                                   [:span [:span#repo-name.font-medium
-                                           [:span.overflow-hidden.text-ellipsis (if (= config/demo-repo short-repo-name) "Demo" short-repo-name)]
-                                           (when remote? [:span.pl-1 (ui/icon "cloud")])]]
-                                   [:span.dropdown-caret.ml-2 {:style {:border-top-color "#6b7280"}}]]]]]))
-            links-header (cond->
-                          {:z-index 1000
-                           :modal-class (util/hiccup->class
-                                         "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg")}
-                           (> (count repos) 1)              ; show switch to if there are multiple repos
-                           (assoc :links-header [:div.font-medium.text-sm.opacity-70.px-4.pt-2.pb-1.flex.flex-row.justify-between.items-center
-                                                 [:div (t :left-side-bar/switch)]
-                                                 (when (and (file-sync/enable-sync?) login?)
-                                                   (if remotes-loading?
-                                                     (ui/loading "")
-                                                     [:a.flex {:title "Refresh remote graphs"
-                                                               :on-click (fn []
-                                                                           (file-sync/load-session-graphs)
-                                                                           (rtc-handler/<get-remote-graphs))}
-                                                      (ui/icon "refresh")]))]))]
+                          (when (and (file-sync/enable-sync?) login?)
+                            (if remotes-loading?
+                              (ui/loading "")
+                              (shui/button
+                                {:variant :ghost
+                                 :size :sm
+                                 :title "Refresh remote graphs"
+                                 :class "!h-6 !px-1 relative right-[-4px]"
+                                 :on-click (fn []
+                                             (file-sync/load-session-graphs)
+                                             (rtc-handler/<get-remote-graphs))}
+                                (ui/icon "refresh" {:size 15}))))])]
         (when (seq repos)
-          (ui/dropdown-with-links render-content links links-header))))))
+          (let [remote? (and current-repo (:remote? (first (filter #(= current-repo (:url %)) repos))))
+                repo-name (when current-repo (db/get-repo-name current-repo))
+                short-repo-name (if current-repo
+                                  (db/get-short-repo-name repo-name)
+                                  "Select a Graph")]
+            (shui/trigger-as :a
+              {:tab-index 0
+               :class "item cp__repos-select-trigger"
+               :on-click (fn [^js e]
+                           (check-multiple-windows? state)
+                           (some-> (.-target e)
+                             (.closest "a.item")
+                             (shui/popup-show!
+                               (fn [{:keys [id]}]
+                                 [:<>
+                                  (header-fn)
+                                  (for [{:keys [hr item hover-detail title options icon]} (items-fn)]
+                                    (let [on-click' (:on-click options)
+                                          href' (:href options)]
+                                      (if hr
+                                        (shui/dropdown-menu-separator)
+                                        (shui/dropdown-menu-item
+                                          (assoc options
+                                            :title hover-detail
+                                            :on-click (fn [^js e]
+                                                        (when on-click'
+                                                          (when-not (false? (on-click' e))
+                                                            (shui/popup-hide! id)))))
+                                          (or item
+                                            (if href'
+                                              [:a.flex.items-center.w-full
+                                               {:href href' :on-click #(shui/popup-hide! id)
+                                                :style {:color "inherit"}} title]
+                                              [:span.flex.items-center.gap-1.w-full
+                                               icon [:div title]]))))))])
+                               {:as-dropdown? true
+                                :auto-focus? false
+                                :align "start"
+                                :content-props {:class "repos-list"}})))
+               :title repo-name}                            ;; show full path on hover
+              [:div.flex.flex-row.items-center
+               [:div.flex.relative.graph-icon.rounded
+                (shui/tabler-icon "database" {:size 15})]
+
+               [:div.repo-switch.block.pr-2.whitespace-nowrap
+                [:span.repo-name.font-medium
+                 [:span.overflow-hidden.text-ellipsis (if (= config/demo-repo short-repo-name) "Demo" short-repo-name)]
+                 (when remote? [:span.pl-1 (ui/icon "cloud")])]
+                [:span.dropdown-caret.ml-2 {:style {:border-top-color "#6b7280"}}]]])))))))
 
 (defn invalid-graph-name-warning
   []
   (notification/show!
-   [:div
-    [:p "Graph name can't contain following reserved characters:"]
-    [:ul
-     [:li "< (less than)"]
-     [:li "> (greater than)"]
-     [:li ": (colon)"]
-     [:li "\" (double quote)"]
-     [:li "/ (forward slash)"]
-     [:li "\\ (backslash)"]
-     [:li "| (vertical bar or pipe)"]
-     [:li "? (question mark)"]
-     [:li "* (asterisk)"]
-     [:li "# (hash)"]
-     ;; `+` is used to encode path that includes `:` or `/`
-     [:li "+ (plus)"]]]
-   :warning false))
+    [:div
+     [:p "Graph name can't contain following reserved characters:"]
+     [:ul
+      [:li "< (less than)"]
+      [:li "> (greater than)"]
+      [:li ": (colon)"]
+      [:li "\" (double quote)"]
+      [:li "/ (forward slash)"]
+      [:li "\\ (backslash)"]
+      [:li "| (vertical bar or pipe)"]
+      [:li "? (question mark)"]
+      [:li "* (asterisk)"]
+      [:li "# (hash)"]
+      ;; `+` is used to encode path that includes `:` or `/`
+      [:li "+ (plus)"]]]
+    :warning false))
 
 (defn invalid-graph-name?
   "Returns boolean indicating if DB graph name is invalid. Must be kept in sync with invalid-graph-name-warning"
@@ -332,10 +353,17 @@
   (rum/local "" ::graph-name)
   (rum/local false ::cloud?)
   (rum/local false ::creating-db?)
+  (rum/local (rum/create-ref) ::input-ref)
+  {:did-mount (fn [s]
+                (when-let [^js input (some-> @(::input-ref s)
+                                       (rum/deref))]
+                  (js/setTimeout #(.focus input) 32))
+                s)}
   [state]
   (let [*creating-db? (::creating-db? state)
         *graph-name (::graph-name state)
         *cloud? (::cloud? state)
+        input-ref @(::input-ref state)
         new-db-f (fn []
                    (when-not (or (string/blank? @*graph-name)
                                  @*creating-db?)
@@ -359,15 +387,18 @@
                                          (prn :debug :create-db-failed)
                                          (js/console.error error)))))
                            (reset! *creating-db? false)
-                           (state/close-modal! {:force? true}))))))]
+                           (shui/dialog-close!))))))]
     [:div.new-graph.flex.flex-col.p-4.gap-4
-     [:h1.title.mb-4 "Create new graph: "]
-     [:input.form-input {:value @*graph-name
-                         :auto-focus true
-                         :on-change #(reset! *graph-name (util/evalue %))
-                         :on-key-down (fn [^js e]
-                                        (when (= (gobj/get e "key") "Enter")
-                                          (new-db-f)))}]
+     (shui/input
+       {:value @*graph-name
+        :disabled @*creating-db?
+        :ref input-ref
+        :placeholder "your graph name"
+        :auto-focus true
+        :on-change #(reset! *graph-name (util/evalue %))
+        :on-key-down (fn [^js e]
+                       (when (= (gobj/get e "key") "Enter")
+                         (new-db-f)))})
      (when (user-handler/logged-in?)
        [:div.flex.flex-row.items-center.gap-1
         (shui/checkbox

+ 19 - 2
src/main/frontend/components/repo.css

@@ -10,6 +10,23 @@
 }
 
 .repo-plus svg {
-    display: inline;
-    transform: scale(0.7);
+  display: inline;
+  transform: scale(0.7);
 }
+
+.ui__dropdown-menu-content {
+  &.repos-list {
+    @apply px-2;
+    @apply min-w-[260px] sm:max-w-[380px] max-h-[70vh];
+
+    .ui__dropdown-menu-item {
+      @apply overflow-hidden overflow-ellipsis;
+    }
+  }
+}
+
+.cp__repos-select {
+  &-trigger {
+    @apply flex items-center p-2 text-sm font-medium rounded-md;
+  }
+}

+ 1 - 1
src/main/frontend/config.cljs

@@ -28,7 +28,7 @@
 (goog-define TEST false)
 (def test? TEST)
 
-(goog-define ENABLE-FILE-SYNC-PRODUCTION false)
+(def ENABLE-FILE-SYNC-PRODUCTION false)
 
 ;; this is a feature flag to enable the account tab
 ;; when it launches (when pro plan launches) it should be removed

+ 13 - 7
src/main/frontend/db/async.cljs

@@ -15,7 +15,8 @@
             [frontend.date :as date]
             [cljs-time.core :as t]
             [cljs-time.format :as tf]
-            [logseq.db :as ldb]))
+            [logseq.db :as ldb]
+            [clojure.string :as string]))
 
 (def <q db-async-util/<q)
 (def <pull db-async-util/<pull)
@@ -46,22 +47,27 @@
     (p/let [templates (<get-all-templates repo)]
       (get templates name))))
 
-(defn- <db-based-get-all-properties
-  "Return seq of property names. :block/type could be one of [property, class]."
+(defn <db-based-get-all-properties
+  "Return seq of all property names except for private built-in properties."
   [graph]
   (p/let [result (<q graph
-                     '[:find [(pull ?e [:block/original-name]) ...]
+                     '[:find [(pull ?e [:block/original-name :block/schema :db/ident]) ...]
                        :where
                        [?e :block/type "property"]
                        [?e :block/original-name]])]
-    (map :block/original-name result)))
+    (->> result
+         ;; remove private built-in properties
+         (remove #(and (:db/ident %)
+                       (string/starts-with? (namespace (:db/ident %)) "logseq.")
+                       (not (get-in % [:block/schema :public?])))))))
 
-(defn <get-all-properties
+(defn <get-all-property-names
   "Returns a seq of property name strings"
   []
   (when-let [graph (state/get-current-repo)]
     (if (config/db-based-graph? graph)
-      (<db-based-get-all-properties graph)
+      (p/let [properties (<db-based-get-all-properties graph)]
+        (map :block/original-name properties))
       (file-async/<file-based-get-all-properties graph))))
 
 (comment

+ 7 - 7
src/main/frontend/db/model.cljs

@@ -980,7 +980,7 @@ independent of format as format specific heading characters are stripped"
 (defn get-whiteboard-id-nonces
   [repo page-name]
   (let [key (if (config/db-based-graph? repo)
-              (:block/uuid (db-utils/entity [:block/name "logseq.tldraw.shape"]))
+              (:block/uuid (db-utils/entity :logseq.property.tldraw/shape))
               :logseq.tldraw.shape)
         page (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])]
     (->> (:block/_page page)
@@ -1000,33 +1000,33 @@ independent of format as format specific heading characters are stripped"
      [?page :block/uuid ?id]]
     (conn/get-db repo)))
 
-(defn get-namespace-children
+(defn get-class-children
   [repo eid]
   (->>
    (d/q '[:find [?children ...]
           :in $ ?parent %
           :where
-          (namespace ?parent ?children)]
+          (class-parent ?parent ?children)]
         (conn/get-db repo)
         eid
-        (:namespace rules/rules))
+        (:class-parent rules/rules))
    distinct))
 
 ;; FIXME: async query
 (defn get-class-objects
   [repo class-id]
   (when-let [class (db-utils/entity repo class-id)]
-    (if (first (:block/_namespace class))        ; has children classes
+    (if (first (:class/_parent class))        ; has children classes
       (d/q
        '[:find [?object ...]
          :in $ % ?parent
          :where
-         (namespace ?parent ?c)
+         (class-parent ?parent ?c)
          (or-join [?object ?c]
           [?object :block/tags ?parent]
           [?object :block/tags ?c])]
        (conn/get-db repo)
-       (:namespace rules/rules)
+       (:class-parent rules/rules)
        class-id)
       (map :db/id (:block/_tags class)))))
 

+ 71 - 60
src/main/frontend/db/rtc/debug_ui.cljs

@@ -38,35 +38,35 @@
                     (js/setTimeout #(.removeAttribute btn "disabled") 2000)))}
      [:div.flex.gap-2.flex-wrap.items-center.pb-3
       (shui/button
-        {:size :sm
-         :on-click (fn [_]
-                     (let [repo (state/get-current-repo)
-                           ^object worker @db-browser/*worker]
-                       (p/let [result (.rtc-get-debug-state worker repo)
-                               new-state (ldb/read-transit-str result)]
-                         (swap! debug-state (fn [old] (merge old new-state))))))}
-        (shui/tabler-icon "refresh") "local-state")
+       {:size :sm
+        :on-click (fn [_]
+                    (let [repo (state/get-current-repo)
+                          ^object worker @db-browser/*worker]
+                      (p/let [result (.rtc-get-debug-state worker repo)
+                              new-state (ldb/read-transit-str result)]
+                        (swap! debug-state (fn [old] (merge old new-state))))))}
+       (shui/tabler-icon "refresh") "local-state")
 
       (shui/button
-        {:size :sm
-         :on-click
-         (fn [_]
-           (let [repo (state/get-current-repo)
-                 token (state/get-auth-id-token)
-                 ^object worker @db-browser/*worker]
-             (p/let [result (.rtc-get-graphs worker repo token)
-                     graph-list (bean/->clj result)]
-               (swap! debug-state assoc
-                 :remote-graphs
-                 (map
-                   #(into {}
-                      (filter second
-                        (select-keys % [:graph-uuid :graph-name
-                                        :graph-status
-                                        :graph<->user-user-type
-                                        :graph<->user-grant-by-user])))
-                   graph-list)))))}
-        (shui/tabler-icon "download") "graph-list")
+       {:size :sm
+        :on-click
+        (fn [_]
+          (let [repo (state/get-current-repo)
+                token (state/get-auth-id-token)
+                ^object worker @db-browser/*worker]
+            (p/let [result (.rtc-get-graphs worker repo token)
+                    graph-list (bean/->clj result)]
+              (swap! debug-state assoc
+                     :remote-graphs
+                     (map
+                      #(into {}
+                             (filter second
+                                     (select-keys % [:graph-uuid :graph-name
+                                                     :graph-status
+                                                     :graph<->user-user-type
+                                                     :graph<->user-grant-by-user])))
+                      graph-list)))))}
+       (shui/tabler-icon "download") "graph-list")
 
       (shui/button
        {:size :sm
@@ -90,20 +90,20 @@
             :current-page (state/get-current-page)
             :blocks-count (when-let [page (state/get-current-page)]
                             (count (:block/_page (db/entity [:block/name (util/page-name-sanity-lc page)]))))}
-         (fipp/pprint {:width 20})
-         with-out-str)]]
+           (fipp/pprint {:width 20})
+           with-out-str)]]
 
      (if (or (nil? rtc-state)
              (= :closed rtc-state))
        (shui/button
-         {:variant :outline
-          :class "text-green-rx-09 border-green-rx-10 hover:text-green-rx-10"
-          :on-click (fn []
-                      (let [token (state/get-auth-id-token)
-                            ^object worker @db-browser/*worker]
-                        (.rtc-start worker (state/get-current-repo) token
-                          (state/sub [:ui/developer-mode?]))))}
-         (shui/tabler-icon "player-play") "start")
+        {:variant :outline
+         :class "text-green-rx-09 border-green-rx-10 hover:text-green-rx-10"
+         :on-click (fn []
+                     (let [token (state/get-auth-id-token)
+                           ^object worker @db-browser/*worker]
+                       (.rtc-start worker (state/get-current-repo) token
+                                   (state/sub [:ui/developer-mode?]))))}
+        (shui/tabler-icon "player-play") "start")
 
        [:div.my-2.flex
         [:div.mr-2 (ui/button (str "send pending ops")
@@ -118,11 +118,11 @@
                                    (p/let [result (.rtc-toggle-sync worker (state/get-current-repo))]
                                      (swap! debug-state assoc :auto-push-updates? result))))})]
         [:div (shui/button
-                {:variant :outline
-                 :class "text-red-rx-09 border-red-rx-08 hover:text-red-rx-10"
-                 :size :sm
-                 :on-click (fn [] (stop))}
-                (shui/tabler-icon "player-stop") "stop")]])
+               {:variant :outline
+                :class "text-red-rx-09 border-red-rx-08 hover:text-red-rx-10"
+                :size :sm
+                :on-click (fn [] (stop))}
+               (shui/tabler-icon "player-stop") "stop")]])
 
      (when (some? state)
        [:hr]
@@ -153,12 +153,23 @@
                  {:icon "download"
                   :class "mr-2"
                   :on-click (fn []
-                              (when-let [repo (:download-graph-to-repo state)]
+                              (when-let [graph-name (:download-graph-to-repo state)]
                                 (when-let [graph-uuid (:graph-uuid-to-download state)]
-                                  (prn :download-graph graph-uuid :to repo)
-                                  (let [token (state/get-auth-id-token)
-                                        ^object worker @db-browser/*worker]
-                                    (.rtc-download-graph worker repo token graph-uuid)))))})
+                                  (prn :download-graph graph-uuid :to graph-name)
+                                  (p/let [token (state/get-auth-id-token)
+                                          ^object worker @db-browser/*worker
+                                          download-info-uuid (.rtc-request-download-graph worker nil token graph-uuid)
+                                          result (.rtc-wait-download-graph-info-ready
+                                                  worker nil token download-info-uuid graph-uuid 60000)
+                                          {:keys [_download-info-uuid
+                                                  download-info-s3-url
+                                                  _download-info-tx-instant
+                                                  _download-info-t
+                                                  _download-info-created-at]
+                                           :as result} (ldb/read-transit-str result)]
+                                    (when (not= result :timeout)
+                                      (assert (some? download-info-s3-url) result)
+                                      (.rtc-download-graph-from-s3 worker graph-uuid graph-name download-info-s3-url))))))})
 
       [:b "➡"]
       [:div.flex.flex-row.items-center.gap-2
@@ -192,7 +203,7 @@
                                     token (state/get-auth-id-token)
                                     remote-graph-name (:upload-as-graph-name state)
                                     ^js worker @db-browser/*worker]
-                                (.rtc-upload-graph worker repo token remote-graph-name)))})
+                                (.rtc-async-upload-graph worker repo token remote-graph-name)))})
       [:b "➡️"]
       [:input.form-input.my-2.py-1.w-32
        {:on-change (fn [e] (swap! debug-state assoc :upload-as-graph-name (util/evalue e)))
@@ -206,20 +217,20 @@
                  {:icon "trash"
                   :on-click (fn []
                               (-> (shui/dialog-confirm!
-                                    {:title [:p.flex.flex-col.gap-1
-                                             [:b "Are you sure delete current graph?"]
-                                             [:small.line-through.opacity-80 (state/get-current-repo)]]})
-                                (p/then #((when-let [graph-uuid (:graph-uuid-to-delete state)]
-                                            (let [token (state/get-auth-id-token)
-                                                  ^object worker @db-browser/*worker]
-                                              (prn ::delete-graph graph-uuid)
-                                              (.rtc-delete-graph worker token graph-uuid)))))))})
+                                   {:title [:p.flex.flex-col.gap-1
+                                            [:b "Are you sure delete current graph?"]
+                                            [:small.line-through.opacity-80 (state/get-current-repo)]]})
+                                  (p/then #((when-let [graph-uuid (:graph-uuid-to-delete state)]
+                                              (let [token (state/get-auth-id-token)
+                                                    ^object worker @db-browser/*worker]
+                                                (prn ::delete-graph graph-uuid)
+                                                (.rtc-delete-graph worker token graph-uuid)))))))})
 
       (shui/select
-        {:on-value-change (fn [v]
-                            (some->> (parse-uuid v)
-                              str
-                              (swap! debug-state assoc :graph-uuid-to-delete)))}
+       {:on-value-change (fn [v]
+                           (some->> (parse-uuid v)
+                                    str
+                                    (swap! debug-state assoc :graph-uuid-to-delete)))}
        (shui/select-trigger
         {:class "!px-2 !py-0 !h-8"}
         (shui/select-value

+ 35 - 8
src/main/frontend/db_worker.cljs

@@ -575,25 +575,21 @@
          :target-user-uuids target-user-uuids
          :target-user-emails target-user-emails)))))
 
-  (rtc-upload-graph
+  (rtc-async-upload-graph
    [this repo token remote-graph-name]
    (let [d (p/deferred)]
      (when-let [conn (worker-state/get-datascript-conn repo)]
        (async/go
          (try
-           (let [state (<? (rtc-core/<init-state repo token false))]
-             (<? (rtc-updown/<upload-graph state repo conn remote-graph-name))
+           (let [state (<? (rtc-core/<init-state repo token false))
+                 r (<? (rtc-updown/<async-upload-graph state repo conn remote-graph-name))]
              (rtc-db-listener/listen-db-to-generate-ops repo conn)
-             (p/resolve! d :success))
-           (worker-util/post-message :notification
-                                     [[:div
-                                       [:p "Upload graph successfully"]]])
+             (p/resolve! d r))
            (catch :default e
              (worker-util/post-message :notification
                                        [[:div
                                          [:p "upload graph failed"]]
                                         :error])
-             (prn ::download-graph-failed e)
              (p/reject! d e)))))
      d))
 
@@ -614,6 +610,37 @@
                                        :error])
             (prn ::download-graph-failed e)))))))
 
+  (rtc-request-download-graph
+   [this repo token graph-uuid]
+   (async-util/c->p
+    (async/go
+      (let [state (or @rtc-core/*state
+                      (<! (rtc-core/<init-state repo token false)))]
+        (<? (rtc-updown/<request-download-graph state graph-uuid))))))
+
+  (rtc-wait-download-graph-info-ready
+   [this repo token download-info-uuid graph-uuid timeout-ms]
+   (async-util/c->p
+    (async/go
+      (let [state (or @rtc-core/*state
+                      (<! (rtc-core/<init-state repo token false)))]
+        (ldb/write-transit-str
+         (<? (rtc-updown/<wait-download-info-ready state download-info-uuid graph-uuid timeout-ms)))))))
+
+  (rtc-download-graph-from-s3
+   [this graph-uuid graph-name s3-url]
+   (async-util/c->p
+    (async/go
+      (rtc-updown/<download-graph-from-s3 graph-uuid graph-name s3-url))))
+
+  (rtc-download-info-list
+   [this repo token graph-uuid]
+   (async-util/c->p
+    (async/go
+      (let [state (or @rtc-core/*state
+                      (<! (rtc-core/<init-state repo token false)))]
+        (<? (rtc-updown/<download-info-list state graph-uuid))))))
+
   (rtc-push-pending-ops
    [_this]
    (async/put! (:force-push-client-ops-chan @rtc-core/*state) true))

+ 7 - 7
src/main/frontend/extensions/pdf/assets.cljs

@@ -195,16 +195,16 @@
            (let [text       (:text content)
                  wrap-props #(if-let [stamp (:image content)]
                                (assoc %
-                                      (pu/get-pid :hl-type) :area
-                                      (pu/get-pid :hl-stamp) stamp)
+                                      (pu/get-pid :logseq.property/hl-type) :area
+                                      (pu/get-pid :logseq.property/hl-stamp) stamp)
                                %)
                  props (cond->
-                        {(pu/get-pid :ls-type)  :annotation
-                         (pu/get-pid :hl-page)  page
-                         (pu/get-pid :hl-color) (:color properties)}
+                        {(pu/get-pid :logseq.property/ls-type)  :annotation
+                         (pu/get-pid :logseq.property/hl-page)  page
+                         (pu/get-pid :logseq.property/hl-color) (:color properties)}
                          (not (config/db-based-graph? (state/get-current-repo)))
                        ;; force custom uuid
-                         (assoc (pu/get-pid :id) (str id)))
+                         (assoc :id (str id)))
                  properties (->>
                              (wrap-props props)
                              (property-handler/replace-key-with-id (state/get-current-repo)))]
@@ -237,7 +237,7 @@
         page-name (:block/original-name page)
         ;; FIXME: file-path property for db version
         file-path (:file-path (:block/properties page))
-        hl-page   (pu/get-block-property-value block :hl-page)]
+        hl-page   (pu/get-block-property-value block :logseq.property/hl-page)]
     (when-let [target-key (and page-name (subs page-name 5))]
       (p/let [hls (resolve-hls-data-by-key$ target-key)
               hls (and hls (:highlights hls))]

+ 1 - 1
src/main/frontend/extensions/pdf/utils.cljs

@@ -17,7 +17,7 @@
   (and filename (string? filename) (string/starts-with? filename "hls__")))
 
 (def get-area-block-asset-url
-  #(publish-db/get-area-block-asset-url %1 %2 {:prop-lookup-fn pu/lookup}))
+  #(publish-db/get-area-block-asset-url %1 %2 {:prop-lookup-fn pu/lookup-by-name}))
 
 (defn get-bounding-rect
   [rects]

+ 2 - 2
src/main/frontend/handler/block.cljs

@@ -116,7 +116,7 @@
   [block order-list-type]
   (let [order-block-fn? (fn [block]
                           (let [properties (:block/properties block)
-                                type (pu/lookup properties :logseq.order-list-type)]
+                                type (pu/lookup properties :logseq.property/order-list-type)]
                             (= type order-list-type)))
         prev-block-fn   #(some->> (:db/id %) (db-model/get-prev-sibling (db/get-db)))
         prev-block      (prev-block-fn block)]
@@ -145,7 +145,7 @@
 (defn attach-order-list-state
   [config block]
   (let [properties (:block/properties block)
-        type (pu/lookup properties :logseq.order-list-type)
+        type (pu/lookup properties :logseq.property/order-list-type)
         own-order-list-type  (some-> type str string/lower-case)
         own-order-list-index (some->> own-order-list-type (get-idx-of-order-list-block block))]
     (assoc config :own-order-list-type own-order-list-type

+ 3 - 2
src/main/frontend/handler/db_based/editor.cljs

@@ -12,6 +12,7 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.common.config-edn :as config-edn-common-handler]
             [frontend.handler.property :as property-handler]
+            [frontend.handler.property.util :as pu]
             [frontend.handler.repo-config :as repo-config-handler]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.op :as outliner-op]
@@ -123,7 +124,7 @@
 (defn- set-heading-aux!
   [block-id heading]
   (let [block (db/pull [:block/uuid block-id])
-        old-heading (:logseq.property/heading block)]
+        old-heading (pu/lookup (:block/properties block) :logseq.property/heading)]
     (cond
       ;; nothing changed for first two cases
       (or (and (nil? old-heading) (nil? heading))
@@ -162,4 +163,4 @@
    {:outliner-op :save-block}
    (doseq [block (keep #(set-heading-aux! % heading) block-ids)]
      (outliner-op/save-block! block))
-   (property-handler/batch-set-block-property! repo block-ids :logseq.property/heading heading)))
+   (property-handler/batch-set-block-property! repo block-ids :heading heading)))

+ 12 - 12
src/main/frontend/handler/db_based/property.cljs

@@ -410,20 +410,20 @@
                   [[f (:db/id block) :block/collapsed-properties (:db/id property)]]
                   {:outliner-op :save-block})))
 
-(defn- get-namespace-parents
+(defn- get-class-parents
   [tags]
   (let [tags' (filter (fn [tag] (contains? (:block/type tag) "class")) tags)
-        *namespaces (atom #{})]
+        *classes (atom #{})]
     (doseq [tag tags']
-      (when-let [ns (:block/namespace tag)]
-        (loop [current-ns ns]
+      (when-let [parent (:class/parent tag)]
+        (loop [current-parent parent]
           (when (and
-                 current-ns
-                 (contains? (:block/type ns) "class")
-                 (not (contains? @*namespaces (:db/id ns))))
-            (swap! *namespaces conj current-ns)
-            (recur (:block/namespace current-ns))))))
-    @*namespaces))
+                 current-parent
+                 (contains? (:block/type parent) "class")
+                 (not (contains? @*classes (:db/id parent))))
+            (swap! *classes conj current-parent)
+            (recur (:class/parent current-parent))))))
+    @*classes))
 
 (defn get-block-classes-properties
   [eid]
@@ -431,8 +431,8 @@
         classes (->> (:block/tags block)
                      (sort-by :block/name)
                      (filter (fn [tag] (contains? (:block/type tag) "class"))))
-        namespace-parents (get-namespace-parents classes)
-        all-classes (->> (concat classes namespace-parents)
+        class-parents (get-class-parents classes)
+        all-classes (->> (concat classes class-parents)
                          (filter (fn [class]
                                    (seq (:class/schema.properties class)))))
         all-properties (-> (mapcat (fn [class]

+ 3 - 4
src/main/frontend/handler/db_based/property/util.cljs

@@ -13,10 +13,9 @@
 
 ;; FIXME: property no long has `:block/name` attribute
 (defn get-built-in-property-uuid
-  "Get a built-in property's uuid given its name"
-  ([property-name] (get-built-in-property-uuid (state/get-current-repo) property-name))
-  ([repo property-name]
-   (:block/uuid (db-utils/entity repo [:block/name (name property-name)]))))
+  "Get a built-in property's uuid given its db-ident"
+  ([db-ident] (get-built-in-property-uuid (state/get-current-repo) db-ident))
+  ([repo db-ident] (:block/uuid (db-utils/entity repo db-ident))))
 
 ;; FIXME: property no long has `:block/name` attribute
 (defn get-user-property-uuid

+ 33 - 12
src/main/frontend/handler/db_based/rtc.cljs

@@ -10,13 +10,14 @@
             [logseq.db.sqlite.common-db :as sqlite-common-db]
             [frontend.handler.notification :as notification]))
 
+
 (defn <rtc-create-graph!
   [repo]
   (when-let [^js worker @state/*db-worker]
     (user-handler/<wrap-ensure-id&access-token
      (let [token (state/get-auth-id-token)
            repo-name (sqlite-common-db/sanitize-db-name repo)]
-       (.rtc-upload-graph worker repo token repo-name)))))
+       (.rtc-async-upload-graph worker repo token repo-name)))))
 
 (defn <rtc-delete-graph!
   [graph-uuid]
@@ -26,16 +27,25 @@
        (.rtc-delete-graph worker token graph-uuid)))))
 
 (defn <rtc-download-graph!
-  [repo graph-uuid]
+  [graph-name graph-uuid timeout-ms]
   (when-let [^js worker @state/*db-worker]
     (state/set-state! :rtc/downloading-graph-uuid graph-uuid)
     (user-handler/<wrap-ensure-id&access-token
-     (let [token (state/get-auth-id-token)]
+     (p/let [token (state/get-auth-id-token)
+             download-info-uuid (.rtc-request-download-graph worker nil token graph-uuid)
+             result (.rtc-wait-download-graph-info-ready worker nil token download-info-uuid graph-uuid timeout-ms)
+             {:keys [_download-info-uuid
+                     download-info-s3-url
+                     _download-info-tx-instant
+                     _download-info-t
+                     _download-info-created-at]
+              :as result} (ldb/read-transit-str result)]
        (->
-        (.rtc-download-graph worker repo token graph-uuid)
+        (when (not= result :timeout)
+          (assert (some? download-info-s3-url) result)
+          (.rtc-download-graph-from-s3 worker graph-uuid graph-name download-info-s3-url))
         (p/finally
-          (fn []
-            (state/set-state! :rtc/downloading-graph-uuid nil))))))))
+          #(state/set-state! :rtc/downloading-graph-uuid nil)))))))
 
 (defn <rtc-stop!
   []
@@ -43,18 +53,29 @@
     (.rtc-stop worker)))
 
 (defn <rtc-start!
-  [repo]
+  [repo & {:keys [retry] :or {retry 0}}]
   (when-let [^js worker @state/*db-worker]
     (when (ldb/get-graph-rtc-uuid (db/get-db repo))
       (user-handler/<wrap-ensure-id&access-token
         ;; TODO: `<rtc-stop!` can return a chan so that we can remove timeout
        (<rtc-stop!)
        (let [token (state/get-auth-id-token)]
-         (-> (.rtc-start worker repo token
-                         (state/sub [:ui/developer-mode?]))
-             (p/then (fn [result]
-                       (when (= "rtc-not-closed-yet" result)
-                         (js/setTimeout #(<rtc-start! repo) 200))))))))))
+         (p/let [result (.rtc-start worker repo token (state/sub [:ui/developer-mode?]))
+                 _ (case result
+                     "rtc-not-closed-yet"
+                     (js/setTimeout #(<rtc-start! repo) 200)
+                     ":graph-not-ready"
+                     (when (< retry 3)
+                       (let [delay (* 2000 (inc retry))]
+                         (prn "graph still creating, retry rtc-start in " delay "ms")
+                         (p/do! (p/delay delay)
+                                (<rtc-start! repo :retry (inc retry)))))
+
+                     (":break-rtc-loop" ":stop-rtc-loop")
+                     nil
+                     ;; else
+                     nil)]
+           nil))))))
 
 (defn <get-remote-graphs
   []

+ 4 - 4
src/main/frontend/handler/editor.cljs

@@ -85,7 +85,7 @@
 (defn get-block-own-order-list-type
   [block]
   (let [properties (:block/properties block)]
-    (pu/lookup properties :logseq.order-list-type)))
+    (pu/lookup properties :logseq.property/order-list-type)))
 
 (defn set-block-own-order-list-type!
   [block type]
@@ -892,7 +892,7 @@
 (defn set-block-query-properties!
   [block-id all-properties key add?]
   (when-let [block (db/entity [:block/uuid block-id])]
-    (let [query-properties (:query-properties (:block/properties block))
+    (let [query-properties (:logseq.property/query-properties block)
           repo (state/get-current-repo)
           db-based? (config/db-based-graph? repo)
           query-properties (if db-based?
@@ -3415,8 +3415,8 @@
   (->> (:block/macros (db/entity (:db/id block)))
        (some (fn [macro]
                (let [properties (:block/properties macro)
-                     macro-name (pu/lookup properties :logseq.macro-name)
-                     macro-arguments (pu/lookup properties :logseq.macro-arguments)]
+                     macro-name (pu/lookup properties :logseq.property/macro-name)
+                     macro-arguments (pu/lookup properties :logseq.property/macro-arguments)]
                  (when-let [query-body (and (= "query" macro-name) (not-empty (string/join " " macro-arguments)))]
                    (seq (:query
                          (try

+ 6 - 6
src/main/frontend/handler/events.cljs

@@ -73,7 +73,6 @@
             [frontend.util :as util]
             [frontend.util.persist-var :as persist-var]
             [goog.dom :as gdom]
-            [logseq.db.frontend.schema :as db-schema]
             [logseq.common.config :as common-config]
             [promesa.core :as p]
             [lambdaisland.glogi :as log]
@@ -142,7 +141,6 @@
     (login/open-login-modal!)))
 
 (defmethod handle :graph/added [[_ repo {:keys [empty-graph?]}]]
-  (db/set-key-value repo :ast/version db-schema/ast-version)
   (search-handler/rebuild-indices!)
   (plugin-handler/hook-plugin-app :graph-after-indexed {:repo repo :empty-graph? empty-graph?})
   (when (state/setups-picker?)
@@ -193,7 +191,8 @@
    (graph-switch graph)
    (state/set-state! :sync-graph/init? false)
    (when (:rtc-download? opts)
-     (and (search-handler/rebuild-indices!) true))))
+     (and (search-handler/rebuild-indices!) true)
+     (repo-handler/refresh-repos!))))
 
 (defmethod handle :graph/switch [[_ graph opts]]
   (state/set-state! :db/async-queries #{})
@@ -330,7 +329,7 @@
 
 (defmethod handle :modal/set-query-properties [[_ block all-properties]]
   (let [properties (:block/properties block)
-        query-properties (pu/lookup properties :query-properties)
+        query-properties (pu/lookup properties :logseq.property/query-properties)
         block-properties (if (config/db-based-graph? (state/get-current-repo))
                            query-properties
                            (some-> query-properties
@@ -813,9 +812,10 @@
       (page-handler/ls-dir-files! st/refresh! opts'))))
 
 (defmethod handle :graph/new-db-graph [[_ _opts]]
-  (state/set-modal!
+  (shui/dialog-open!
     repo/new-db-graph
     {:id :new-db-graph
+     :title [:h2 "Create a new graph"]
      :label "graph-setup"}))
 
 (defmethod handle :search/transact-data [[_ repo data]]
@@ -977,7 +977,7 @@
 (defmethod handle :rtc/download-remote-graph [[_ graph-name graph-uuid]]
   (->
    (p/do!
-    (rtc-handler/<rtc-download-graph! graph-name graph-uuid))
+    (rtc-handler/<rtc-download-graph! graph-name graph-uuid 60000))
    (p/catch (fn [e]
               (println "RTC download graph failed, error:")
               (js/console.error e)))))

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

@@ -107,7 +107,7 @@
               (not journal?)
               (remove :block/journal?)
               (not excluded-pages?)
-              (remove (fn [p] (true? (pu/get-block-property-value p :exclude-from-graph-view)))))
+              (remove (fn [p] (true? (pu/get-block-property-value p :logseq.property/exclude-from-graph-view)))))
             links (concat (seq relation)
                           (seq tagged-pages)
                           (seq namespaces))

+ 2 - 3
src/main/frontend/handler/page.cljs

@@ -26,7 +26,6 @@
             [goog.functions :refer [debounce]]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
-            [logseq.db.frontend.property :as db-property]
             [logseq.common.config :as common-config]
             [logseq.common.util :as common-util]
             [logseq.common.util.page-ref :as page-ref]
@@ -234,14 +233,14 @@
                    (or (util/uuid-string? name)
                        (common-config/draw? name)
                        (db/built-in-pages-names (string/upper-case name))
-                       (db-property/built-in-properties-keys-str name)))))
+                       (and (contains? (set (:block/type p)) "property") (ldb/built-in? p))))))
        (common-handler/fix-pages-timestamps)))
 
 (defn get-filters
   [page-name]
   (let [properties (db/get-page-properties page-name)]
     (if (config/db-based-graph? (state/get-current-repo))
-      (pu/lookup properties :filters)
+      (pu/lookup properties :logseq.property/filters)
       (let [properties-str (or (:filters properties) "{}")]
         (try (reader/read-string properties-str)
              (catch :default e

+ 25 - 9
src/main/frontend/handler/property/util.cljs

@@ -4,34 +4,50 @@
   compatible with file graphs"
   (:require [frontend.state :as state]
             [frontend.db :as db]
+            [datascript.core :as d]
+            [logseq.db.sqlite.util :as sqlite-util]
             [logseq.db.frontend.property :as db-property]))
 
 (defn lookup
-  "Get the value of coll's (a map) `key`. For file and db graphs"
+  "Get the value of coll's (a map) by db-ident. For file and db graphs"
   [coll key]
   (let [repo (state/get-current-repo)
         db (db/get-db repo)]
     (db-property/lookup repo db coll key)))
 
+(defn lookup-by-name
+  "Get the value of coll's (a map) by name. Only use this
+   for file graphs or for db graphs when user properties are involved"
+  [coll key]
+  (let [repo (state/get-current-repo)
+        db (db/get-db repo)
+        property-name (if (keyword? key)
+                        (name key)
+                        key)]
+    (if (sqlite-util/db-based-graph? repo)
+      (when-let [property (d/entity db (db-property/get-db-ident-from-name property-name))]
+        (get coll (:block/uuid property)))
+      (get coll key))))
+
 (defn get-block-property-value
-  "Get the value of block's property `key`"
-  [block key]
+  "Get the value of a built-in block's property by its db-ident"
+  [block db-ident]
   (let [repo (state/get-current-repo)
         db (db/get-db repo)]
-    (db-property/get-block-property-value repo db block key)))
+    (db-property/get-block-property-value repo db block db-ident)))
 
 (defn get-pid
-  "Get a property's id (name or uuid) given its name. For file and db graphs"
-  [property-name]
+  "Get a built-in property's id (db-ident or name) given its db-ident. For file and db graphs"
+  [db-ident]
   (let [repo (state/get-current-repo)
         db (db/get-db repo)]
-    (db-property/get-pid repo db property-name)))
+    (db-property/get-pid repo db db-ident)))
 
 (defn block->shape [block]
-  (get-block-property-value block :logseq.tldraw.shape))
+  (get-block-property-value block :logseq.property.tldraw/shape))
 
 (defn page-block->tldr-page [block]
-  (get-block-property-value block :logseq.tldraw.page))
+  (get-block-property-value block :logseq.property.tldraw/page))
 
 (defn shape-block?
   [block]

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

@@ -55,7 +55,7 @@
     (let [block (when (uuid? page-name-or-block-uuid)
                   (model/get-block-by-uuid page-name-or-block-uuid))
           properties (:block/properties block)]
-      (if (pu/lookup properties :heading)
+      (if (pu/lookup properties :logseq.property/heading)
         {:to :page-block
          :path-params {:name (get-in block [:block/page :block/name])
                        :block-route-name (model/heading-content->route-name (:block/content block))}}

+ 7 - 9
src/main/frontend/handler/user.cljs

@@ -13,9 +13,7 @@
             [frontend.state :as state]
             [goog.crypt :as crypt]
             [goog.crypt.Hmac]
-            [goog.crypt.Sha256]
-            [logseq.db :as ldb]
-            [frontend.db :as db]))
+            [goog.crypt.Sha256]))
 
 (defn set-preferred-format!
   [format]
@@ -280,17 +278,17 @@
 
 (defn get-user-type
   [repo]
-  (when-let [uuid (ldb/get-graph-rtc-uuid (db/get-db repo))]
-    (-> (some #(when (= uuid (:GraphUUID %)) %) (:rtc/graphs @state/state))
-        :graph<->user-user-type)))
+  (-> (some #(when (= repo (:url %)) %) (:rtc/graphs @state/state))
+      :graph<->user-user-type))
 
 (defn manager?
   [repo]
   (= (get-user-type repo) "manager"))
 
-(defn member?
-  [repo]
-  (= (get-user-type repo) "member"))
+;; TODO: Remove if still unused
+#_(defn member?
+    [repo]
+    (= (get-user-type repo) "member"))
 
 (comment
   ;; We probably need this for some new features later

+ 10 - 10
src/main/frontend/handler/whiteboard.cljs

@@ -31,7 +31,7 @@
 
 (defn- build-shapes
   [page-block blocks]
-  (let [page-metadata (pu/get-block-property-value page-block :logseq.tldraw.page)
+  (let [page-metadata (pu/get-block-property-value page-block :logseq.property.tldraw/page)
         shapes-index (:shapes-index page-metadata)
         shape-id->index (zipmap shapes-index (range 0 (count shapes-index)))]
     (->> blocks
@@ -61,10 +61,10 @@
     {:block/original-name page-name
      :block/name (util/page-name-sanity-lc page-name)
      :block/type "whiteboard"
-     :block/properties {(pu/get-pid :ls-type)
+     :block/properties {(pu/get-pid :logseq.property/ls-type)
                         :whiteboard-page
 
-                        (pu/get-pid :logseq.tldraw.page)
+                        (pu/get-pid :logseq.property.tldraw/page)
                         {:id (get-k "id")
                          :name (get-k "name")
                          :bindings (js->clj-keywordize (get-k "bindings"))
@@ -98,7 +98,7 @@
         repo (state/get-current-repo)
         deleted-shapes (when (seq deleted-ids)
                          (->> (db/pull-many repo '[*] (mapv (fn [id] [:block/uuid (uuid id)]) deleted-ids))
-                              (mapv (fn [b] (pu/get-block-property-value b :logseq.tldraw.shape)))
+                              (mapv (fn [b] (pu/get-block-property-value b :logseq.property.tldraw/shape)))
                               (remove nil?)))
         deleted-shapes-tx (mapv (fn [id] [:db/retractEntity [:block/uuid (uuid id)]]) deleted-ids)
         upserted-blocks (->> (map #(shape->block % page-name) upsert-shapes)
@@ -106,11 +106,11 @@
                                        (= (:nonce
                                            (pu/get-block-property-value
                                             (db/entity [:block/uuid (:block/uuid b)])
-                                            :logseq.tldraw.shape))
+                                            :logseq.property.tldraw/shape))
                                           (:nonce
                                            (pu/get-block-property-value
                                             b
-                                            :logseq.tldraw.shape))))))
+                                            :logseq.property.tldraw/shape))))))
         page-entity (model/get-page page-name)
         page-block (build-page-block page-entity page-name tl-page assets shapes-index)]
     (when (or (seq upserted-blocks)
@@ -133,7 +133,7 @@
   (let [tl-page ^js (second (first (.-pages app)))
         shapes (.-shapes ^js tl-page)
         page-block (model/get-page page-name)
-        prev-page-metadata (pu/get-block-property-value page-block :logseq.tldraw.page)
+        prev-page-metadata (pu/get-block-property-value page-block :logseq.property.tldraw/page)
         prev-shapes-index (:shapes-index prev-page-metadata)
         shape-id->prev-index (zipmap prev-shapes-index (range (count prev-shapes-index)))
         new-id-nonces (set (map-indexed (fn [idx shape]
@@ -180,8 +180,8 @@
 
 (defn get-default-new-whiteboard-tx
   [page-name id]
-  (let [properties {(pu/get-pid :ls-type) :whiteboard-page,
-                    (pu/get-pid :logseq.tldraw.page)
+  (let [properties {(pu/get-pid :logseq.property/ls-type) :whiteboard-page,
+                    (pu/get-pid :logseq.property.tldraw/page)
                     {:id (str id),
                      :name page-name,
                      :ls-type :whiteboard-page,
@@ -318,7 +318,7 @@
     (let [tl-page ^js (second (first (.-pages app)))]
       (when tl-page
         (when-let [page (db/entity [:block/name page-name])]
-         (let [page-metadata (pu/get-block-property-value page :logseq.tldraw.page)
+         (let [page-metadata (pu/get-block-property-value page :logseq.property.tldraw/page)
                shapes-index (:shapes-index page-metadata)]
            (when (seq shapes-index)
              (.updateShapesIndex tl-page (bean/->js shapes-index)))))))))

+ 5 - 7
src/main/frontend/search.cljs

@@ -12,7 +12,6 @@
             [logseq.common.config :as common-config]
             [frontend.db.async :as db-async]
             [frontend.config :as config]
-            [logseq.db.frontend.property :as db-property]
             [frontend.handler.file-based.property.util :as property-util]
             [cljs-bean.core :as bean]
             [frontend.db :as db]
@@ -73,12 +72,11 @@
   []
   (when-let [repo (state/get-current-repo)]
     (let [hidden-props (if (config/db-based-graph? repo)
-                        (set (map #(or (get-in db-property/built-in-properties [% :original-name])
-                                       (name %))
-                                  db-property/hidden-built-in-properties))
-                        (set (map name (property-util/hidden-properties))))]
-     (p/let [properties (db-async/<get-all-properties)]
-       (remove hidden-props properties)))))
+                         ;; no-op since already removed
+                         (constantly false)
+                         (set (map name (property-util/hidden-properties))))]
+      (p/let [properties (db-async/<get-all-property-names)]
+        (remove hidden-props properties)))))
 
 (defn property-search
   ([q]

+ 3 - 2
src/main/frontend/shui.cljs

@@ -11,13 +11,14 @@
     [logseq.shui.context :refer [make-context]]))
 
 
-(def default-versions {:logseq.table.version 1})
+(def default-versions {:logseq.property.table/version 1})
 
 (defn get-shui-component-version
   "Returns the version of the shui component, checking first
   the block properties, then the global config, then the defaults."
   [component-name block-config]
-  (let [version-key (keyword (str "logseq." (name component-name) ".version"))]
+  (let [version-key (keyword (str "logseq.property." (name component-name))
+                             "version")]
     (js/parseFloat
       (or (pu/lookup (get-in block-config [:block :block/properties]) version-key)
           (get-in (state/get-config) [version-key])

+ 16 - 0
src/main/frontend/worker/rtc/const.cljs

@@ -169,6 +169,22 @@
       [:action :string]
       [:s3-key :string]
       [:graph-name :string]]]
+    ["upload-graph"
+     [:map
+      [:req-id :string]
+      [:action :string]
+      [:s3-key :string]
+      [:graph-name :string]]]
+    ["download-graph"
+     [:map
+      [:req-id :string]
+      [:action :string]
+      [:graph-uuid :string]]]
+    ["download-info-list"
+     [:map
+      [:req-id :string]
+      [:action :string]
+      [:graph-uuid :string]]]
     ["grant-access"
      [:map
       [:req-id :string]

+ 62 - 47
src/main/frontend/worker/rtc/core.cljs

@@ -53,24 +53,25 @@
 ;;; exceptions ================================================================
 
 (def ex-break-rtc-loop (ex-info "break rtc loop" {:type ::break-rtc-loop}))
+(def ex-graph-not-ready (ex-info "graph still creating" {:type ::graph-not-ready}))
 
 ;;; exceptions (ends)
 
 
 (def state-schema
   "
-  | :*graph-uuid                      | atom of graph-uuid syncing now                           |
-  | :*repo                            | atom of repo name syncing now                            |
-  | :data-from-ws-chan                | channel for receive messages from server websocket       |
-  | :data-from-ws-pub                 | pub of :data-from-ws-chan, dispatch by :req-id           |
-  | :*stop-rtc-loop-chan              | atom of chan to stop <loop-for-rtc                       |
-  | :*ws                              | atom of websocket                                        |
-  | :*rtc-state                       | atom of state of current rtc progress                    |
-  | :toggle-auto-push-client-ops-chan | channel to toggle pushing client ops automatically       |
-  | :*auto-push-client-ops?           | atom to show if it's push client-ops automatically       |
-  | :force-push-client-ops-chan       | chan used to force push client-ops                       |
-  | :dev-mode?                        | when not nil, will update :block-update-log              |
-  | :block-update-log                 | map of block-uuid-> coll of local-op and remote-updates  |
+  | :*graph-uuid                      | atom of graph-uuid syncing now                          |
+  | :*repo                            | atom of repo name syncing now                           |
+  | :data-from-ws-chan                | channel for receive messages from server websocket      |
+  | :data-from-ws-pub                 | pub of :data-from-ws-chan, dispatch by :req-id          |
+  | :*stop-rtc-loop-chan              | atom of chan to stop <loop-for-rtc                      |
+  | :*ws                              | atom of websocket                                       |
+  | :*rtc-state                       | atom of state of current rtc progress                   |
+  | :toggle-auto-push-client-ops-chan | channel to toggle pushing client ops automatically      |
+  | :*auto-push-client-ops?           | atom to show if it's push client-ops automatically      |
+  | :force-push-client-ops-chan       | chan used to force push client-ops                      |
+  | :dev-mode?                        | when not nil, will update :block-update-log             |
+  | :block-update-log                 | map of block-uuid-> coll of local-op and remote-updates |
 "
   [:map {:closed true}
    [:*graph-uuid :any]
@@ -344,7 +345,7 @@
     (when-let [local-parent (d/entity db [:block/uuid first-remote-parent])]
       (let [page-name (:block/name local-parent)
             properties* (transit/read transit-r properties)
-            shape-property-id (db-property/get-pid repo db :logseq.tldraw.shape)
+            shape-property-id (db-property/get-pid repo db :logseq.property.tldraw/shape)
             shape (and (map? properties*)
                        (get properties* shape-property-id))]
         (assert (some? page-name) local-parent)
@@ -918,6 +919,11 @@
     (stop-rtc state)
     (throw ex-break-rtc-loop)))
 
+(defmethod handle-remote-genernal-exception :graph-not-ready [_ state]
+  (stop-rtc-helper state)
+  (stop-rtc state)
+  (throw ex-graph-not-ready))
+
 
 (defmethod handle-remote-genernal-exception nil [resp & _]
   (throw (ex-info "unknown exception from remote" {:resp resp})))
@@ -1005,7 +1011,8 @@
 (declare notify-main-thread)
 
 (defn <loop-for-rtc
-  ":loop-started-ch used to notify that rtc-loop started"
+  ":loop-started-ch used to notify that rtc-loop started.
+  return `:stop-rtc-loop`, `:break-rtc-loop`, `:graph-not-ready`"
   [state graph-uuid repo conn date-formatter & {:keys [loop-started-ch token]}]
   {:pre [(state-validator state)
          (some? graph-uuid)
@@ -1027,49 +1034,54 @@
       (reset! (:*graph-uuid state) graph-uuid)
       (let [resp (<? (ws/<send&receive state {:action "register-graph-updates"
                                               :graph-uuid graph-uuid}))]
+
         (try
-          (when (:ex-data resp) (handle-remote-genernal-exception resp state))
+          (when (:ex-data resp)
+            (handle-remote-genernal-exception resp state))
           (async/sub data-from-ws-pub "push-updates" push-data-from-ws-ch)
           (when loop-started-ch (async/close! loop-started-ch))
           (<?
            (go-try
-             (loop [push-client-ops-ch
-                    (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))]
-               (let [{:keys [push-data-from-ws client-op-update stop continue]}
-                     (async/alt!
-                       toggle-auto-push-client-ops-ch {:continue true}
-                       force-push-client-ops-ch {:client-op-update true}
-                       push-client-ops-ch ([v] (if (and @*auto-push-client-ops? (true? v))
-                                                 {:client-op-update true}
-                                                 {:continue true}))
-                       push-data-from-ws-ch ([v] {:push-data-from-ws v})
-                       stop-rtc-loop-chan {:stop true}
-                       :priority true)]
-                 (cond
-                   continue
-                   (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?)))
-
-                   push-data-from-ws
-                   (let [r (<! (<push-data-from-ws-handler state repo conn date-formatter push-data-from-ws))]
-                     (when (= r ::need-pull-remote-data)
+            (loop [push-client-ops-ch
+                   (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))]
+              (let [{:keys [push-data-from-ws client-op-update stop continue]}
+                    (async/alt!
+                      toggle-auto-push-client-ops-ch {:continue true}
+                      force-push-client-ops-ch {:client-op-update true}
+                      push-client-ops-ch ([v] (if (and @*auto-push-client-ops? (true? v))
+                                                {:client-op-update true}
+                                                {:continue true}))
+                      push-data-from-ws-ch ([v] {:push-data-from-ws v})
+                      stop-rtc-loop-chan {:stop true}
+                      :priority true)]
+                (cond
+                  continue
+                  (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?)))
+
+                  push-data-from-ws
+                  (let [r (<! (<push-data-from-ws-handler state repo conn date-formatter push-data-from-ws))]
+                    (when (= r ::need-pull-remote-data)
                        ;; trigger a force push, which can pull remote-diff-data from local-t to remote-t
-                       (async/put! force-push-client-ops-ch true))
-                     (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))))
+                      (async/put! force-push-client-ops-ch true))
+                    (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))))
 
-                   client-op-update
+                  client-op-update
                    ;; FIXME: access token expired
-                   (let [_ (<? (<client-op-update-handler state token))]
-                     (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))))
+                  (let [_ (<? (<client-op-update-handler state token))]
+                    (recur (make-push-client-ops-timeout-ch repo (not @*auto-push-client-ops?))))
 
-                   stop
-                   (stop-rtc-helper state)
+                  stop
+                  (stop-rtc-helper state)
 
-                   :else
-                   nil)))))
+                  :else
+                  nil)))))
           (async/unsub data-from-ws-pub "push-updates" push-data-from-ws-ch)
+          :stop-rtc-loop
           (catch :default e
             (case (:type (ex-data e))
-              ::break-rtc-loop (prn :break-rtc-loop)
+              ::break-rtc-loop
+              (do (prn :break-rtc-loop)
+                  :break-rtc-loop)
               ;; else
               (prn ::unknown-ex e))))))))
 
@@ -1162,6 +1174,8 @@
 
 
 ;; FIXME: token might be expired
+;;; TODO: `repo` shouldn't be required when init-state.
+;;;       state isn't related to one repo, e.g. `<get-graphs` is user-scope api, not repo-scope
 (defn <init-state
   [repo token reset-*state? & {:keys [dev-mode?]
                                :or {dev-mode? false}}]
@@ -1189,9 +1203,10 @@
               _ (reset! asset-sync/*asset-sync-state state-for-asset-sync)
               config (worker-state/get-config repo)
               c1 (<loop-for-rtc state graph-uuid repo conn (common-config/get-date-formatter config))
-              c2 (asset-sync/<loop-for-assets-sync state-for-asset-sync graph-uuid repo conn)]
-          (<! c1)
-          (<! c2)))
+              c2 (asset-sync/<loop-for-assets-sync state-for-asset-sync graph-uuid repo conn)
+              rtc-loop-result (<! c1)
+              _ (<! c2)]
+          (str rtc-loop-result)))
       (worker-util/post-message :notification
                                 [[:div
                                   [:p "RTC is not supported for this graph"]]

+ 79 - 14
src/main/frontend/worker/rtc/full_upload_download_graph.cljs

@@ -3,8 +3,9 @@
   - download remote graph"
   (:require-macros [frontend.worker.rtc.macro :refer [with-sub-data-from-ws get-req-id get-result-ch]])
   (:require [cljs-http.client :as http]
-            [cljs.core.async :as async :refer [<! go]]
+            [cljs.core.async :as async :refer [<! go go-loop]]
             [cljs.core.async.interop :refer [p->c]]
+            [clojure.string :as string]
             [cognitect.transit :as transit]
             [datascript.core :as d]
             [frontend.worker.async-util :include-macros true :refer [<? go-try]]
@@ -12,12 +13,11 @@
             [frontend.worker.rtc.ws :as ws :refer [<send!]]
             [frontend.worker.state :as worker-state]
             [frontend.worker.util :as worker-util]
+            [logseq.common.util.page-ref :as page-ref]
+            [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.schema :as db-schema]
             [logseq.outliner.core :as outliner-core]
-            [logseq.db.frontend.content :as db-content]
-            [promesa.core :as p]
-            [clojure.string :as string]
-            [logseq.common.util.page-ref :as page-ref]))
+            [promesa.core :as p]))
 
 (def transit-r (transit/reader :json))
 
@@ -39,8 +39,7 @@
                     {:db/id (:e (first datoms))}
                     datoms)))))))
 
-(defn <upload-graph
-  "Upload current repo to remote, return remote {:req-id xxx :graph-uuid <new-remote-graph-uuid>}"
+(defn <async-upload-graph
   [state repo conn remote-graph-name]
   (go
     (let [{:keys [url key all-blocks-str]}
@@ -50,21 +49,21 @@
                   all-blocks-str (transit/write (transit/writer :json) all-blocks)]
               (merge (<! (get-result-ch)) {:all-blocks-str all-blocks-str})))]
       (<! (http/put url {:body all-blocks-str}))
-      (let [r (<? (ws/<send&receive state {:action "full-upload-graph"
+      (let [r (<? (ws/<send&receive state {:action "upload-graph"
                                            :s3-key key
                                            :graph-name remote-graph-name}))]
         (if-not (:graph-uuid r)
           (ex-info "upload graph failed" r)
           (let [^js worker-obj (:worker/object @worker-state/*state)]
             (d/transact! conn
-                         [{:db/ident :graph/uuid :graph/uuid (:graph-uuid r)}
-                          {:db/ident :graph/local-tx :graph/local-tx (:graph-uuid r)}])
+                         [{:db/ident :logseq.kv/graph-uuid :graph/uuid (:graph-uuid r)}
+                          {:db/ident :logseq.kv/graph-local-tx :graph/local-tx "0"}])
             (<! (p->c
                  (p/do!
                   (.storeMetadata worker-obj repo (pr-str {:graph/uuid (:graph-uuid r)})))))
             (op-mem-layer/init-empty-ops-store! repo)
             (op-mem-layer/update-graph-uuid! repo (:graph-uuid r))
-            (op-mem-layer/update-local-tx! repo (:t r))
+            (op-mem-layer/update-local-tx! repo 8)
             (<! (op-mem-layer/<sync-to-idb-layer! repo))
             r))))))
 
@@ -158,8 +157,9 @@
          blocks* (replace-db-id-with-temp-id blocks)
          blocks-with-page-id (fill-block-fields blocks*)
          tx-data (concat blocks-with-page-id
-                       [{:db/ident :graph/uuid :graph/uuid graph-uuid}])
+                         [{:db/ident :logseq.kv/graph-uuid :graph/uuid graph-uuid}])
          ^js worker-obj (:worker/object @worker-state/*state)
+         _ (op-mem-layer/update-local-tx! repo t)
          work (p/do!
                (.createOrOpenDB worker-obj repo {:close-other-db? false})
                (.exportDB worker-obj repo)
@@ -167,8 +167,7 @@
                (transact-block-refs! repo))]
      (<? (p->c work))
 
-     (worker-util/post-message :add-repo {:repo repo})
-     (op-mem-layer/update-local-tx! repo t))))
+     (worker-util/post-message :add-repo {:repo repo}))))
 
 (defn <download-graph
   [state repo graph-uuid]
@@ -190,3 +189,69 @@
            (<! (op-mem-layer/<sync-to-idb-layer! repo))
            (<! (p->c (.storeMetadata worker-obj repo (pr-str {:graph/uuid graph-uuid}))))
            (worker-state/set-rtc-downloading-graph! false)))))))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; async download-graph ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn <request-download-graph
+  [state graph-uuid]
+  (go-try
+   (let [{:keys [download-info-uuid]}
+         (<? (ws/<send&receive state {:action "download-graph"
+                                      :graph-uuid graph-uuid}))]
+     download-info-uuid)))
+
+
+(defn <wait-download-info-ready
+  [state download-info-uuid graph-uuid timeout-ms]
+  (let [init-interval 1000
+        interval      3000
+        timeout-ch    (async/timeout timeout-ms)]
+    (go-loop [interval-ch (async/timeout init-interval)]
+      (let [{:keys [timeout retry]}
+            (async/alt!
+              timeout-ch {:timeout true}
+              interval-ch {:retry true}
+              :priority true)]
+        (cond
+          timeout :timeout
+          retry
+          (let [{:keys [download-info-list]}
+                (<? (ws/<send&receive state {:action     "download-info-list"
+                                             :graph-uuid graph-uuid}))
+                finished-download-info
+                (some
+                 (fn [download-info]
+                   (when (and (= download-info-uuid (:download-info-uuid download-info))
+                              (:download-info-s3-url download-info))
+                     download-info))
+                 download-info-list)]
+            (if finished-download-info
+              finished-download-info
+              (recur (async/timeout interval)))))))))
+
+(defn <download-graph-from-s3
+  [graph-uuid graph-name s3-url]
+  (let [^js worker-obj              (:worker/object @worker-state/*state)]
+    (go-try
+     (let [{:keys [status body] :as r} (<! (http/get s3-url))
+           repo                        (str "logseq_db_" graph-name)]
+       (if (not= 200 status)
+         (ex-info "<download-graph failed" r)
+         (let [all-blocks (transit/read transit-r body)]
+           (worker-state/set-rtc-downloading-graph! true)
+           (op-mem-layer/init-empty-ops-store! repo)
+           (<? (<transact-remote-all-blocks-to-sqlite all-blocks repo graph-uuid))
+           (op-mem-layer/update-graph-uuid! repo graph-uuid)
+           (<! (op-mem-layer/<sync-to-idb-layer! repo))
+           (<! (p->c (.storeMetadata worker-obj repo (pr-str {:graph/uuid graph-uuid}))))
+           (worker-state/set-rtc-downloading-graph! false)))))))
+
+(defn <download-info-list
+  [state graph-uuid]
+  (go-try
+    (<? (ws/<send&receive state {:action "download-info-list"
+                                 :graph-uuid graph-uuid}))))

+ 1 - 2
src/main/logseq/api.cljs

@@ -801,8 +801,7 @@
     (p/let [block-uuid (sdk-utils/uuid-or-throw-error block-uuid)
             _ (db-async/<get-block (state/get-current-repo) block-uuid :children? false)]
       (when-let [block (db-model/query-block-by-uuid block-uuid)]
-        (let [property-id (pu/get-pid key)]
-          (get (:block/properties block) (if (string? property-id) (keyword property-id) property-id)))))))
+        (pu/lookup-by-name (:block/properties block) key)))))
 
 (def ^:export get_block_properties
   (fn [block-uuid]

+ 5 - 5
src/test/frontend/db/db_based_model_test.cljs

@@ -63,7 +63,7 @@
       ;; set class2's parent to class1
         (let [class2 (db/entity [:block/name "class2"])]
           (db/transact! [{:db/id (:db/id class2)
-                          :block/namespace (:db/id class)}]))
+                          :class/parent (:db/id class)}]))
         (test-helper/save-block! repo sbid "Block 2" {:tags ["class2"]})
         (is (= (model/get-class-objects repo (:db/id class))
                [(:db/id (db/entity [:block/uuid fbid]))
@@ -99,7 +99,7 @@
     (is (false? (model/hidden-page? "$$$test")))
     (is (true? (model/hidden-page? (str "$$$" (random-uuid)))))))
 
-(deftest get-namespace-children-test
+(deftest get-class-children-test
   (let [opts {:redirect? false :create-first-block? false :class? true}
         _ (page-handler/create! "class1" opts)
         _ (page-handler/create! "class2" opts)
@@ -108,9 +108,9 @@
         class2 (db/entity [:block/name "class2"])
         class3 (db/entity [:block/name "class3"])
         _ (db/transact! [{:db/id (:db/id class2)
-                          :block/namespace (:db/id class1)}
+                          :class/parent (:db/id class1)}
                          {:db/id (:db/id class3)
-                          :block/namespace (:db/id class2)}])]
+                          :class/parent (:db/id class2)}])]
     (is
-     (= (model/get-namespace-children repo (:db/id (db/entity [:block/name "class1"])))
+     (= (model/get-class-children repo (:db/id (db/entity [:block/name "class1"])))
         [(:db/id class2) (:db/id class3)]))))

+ 3 - 4
src/test/frontend/handler/db_based/property_test.cljs

@@ -4,7 +4,6 @@
             [clojure.test :refer [deftest is testing are use-fixtures]]
             [frontend.test.helper :as test-helper]
             [datascript.core :as d]
-            [frontend.handler.property.util :as pu]
             [frontend.state :as state]
             [frontend.handler.page :as page-handler]
             [frontend.handler.editor :as editor-handler]))
@@ -153,9 +152,9 @@
       (let [fb (db/entity [:block/uuid fbid])
             sb (db/entity [:block/uuid sbid])]
         (are [x y] (= x y)
-          (pu/get-block-property-value fb k)
+          (get (:block/properties fb) (:block/uuid (db/entity [:block/name k])))
           v
-          (pu/get-block-property-value sb k)
+          (get (:block/properties sb) (:block/uuid (db/entity [:block/name k])))
           v))))
 
   (testing "Batch remove properties"
@@ -214,7 +213,7 @@
       ;; set c2 as parent of c3
       (let [c3 (db/entity [:block/name "class3"])]
         (db/transact! [{:db/id (:db/id c3)
-                        :block/namespace (:db/id c2)}]))
+                        :class/parent (:db/id c2)}]))
       (db-property-handler/class-add-property! repo c2id "property-3")
       (db-property-handler/class-add-property! repo c2id "property-4")
       (is (= 3 (count (:classes-properties

+ 31 - 1
src/test/frontend/worker/rtc/rtc_fns_test.cljs

@@ -405,7 +405,8 @@ server: ;; remove 2
   (let [repo (state/get-current-repo)
         conn (conn/get-db repo false)
         date-formatter (common-config/get-date-formatter (worker-state/get-config repo))
-        [page1-uuid] (repeatedly random-uuid)]
+        [page1-uuid ;; page2-uuid page3-uuid page4-uuid
+         ] (repeatedly random-uuid)]
     (testing "apply-remote-update-page-ops-test1"
       (let [data-from-ws {:req-id "req-id" :t 1 :t-before 0
                           :affected-blocks
@@ -434,6 +435,35 @@ server: ;; remove 2
         (rtc-core/apply-remote-update-page-ops repo conn date-formatter update-page-ops)
         (is (= (str page1-uuid "-rename") (:block/name (d/entity @conn [:block/uuid page1-uuid]))))))
 
+    ;; TODO: add this test back when fixed
+    ;; (testing "apply-remote-update-page-ops-test3: create namespace-page"
+    ;;   (let [data-from-ws {:req-id "req-id" :t 1 :t-before 0
+    ;;                       :affected-blocks
+    ;;                       {page2-uuid {:op :update-page
+    ;;                                    :self page2-uuid
+    ;;                                    :page-name "aaa/bbb/ccc"
+    ;;                                    :original-name "aaa/bbb/ccc"}
+    ;;                        page3-uuid {:op :update-page
+    ;;                                    :self page3-uuid
+    ;;                                    :page-name "aaa/bbb"
+    ;;                                    :original-name "aaa/bbb"}
+    ;;                        page4-uuid {:op :update-page
+    ;;                                    :self page4-uuid
+    ;;                                    :page-name "aaa"
+    ;;                                    :original-name "aaa"}}}
+    ;;         update-page-ops (vals
+    ;;                          (:update-page-ops-map
+    ;;                           (#'rtc-core/affected-blocks->diff-type-ops repo (:affected-blocks data-from-ws))))]
+    ;;     (is (rtc-const/data-from-ws-validator data-from-ws))
+    ;;     (rtc-core/apply-remote-update-page-ops repo conn date-formatter update-page-ops)
+    ;;     (prn ::x
+    ;;          (into {} (d/entity @conn [:block/uuid page2-uuid]))
+    ;;          (into {} (d/entity @conn [:block/uuid page3-uuid]))
+    ;;          (into {} (d/entity @conn [:block/uuid page4-uuid]))
+    ;;          (into {} (d/entity @conn [:block/name "aaa"]))
+    ;;          (into {} (d/entity @conn [:block/name "aaa/bbb"]))
+    ;;          (into {} (d/entity @conn [:block/name "aaa/bbb/ccc"])))
+    ;;     ))
     (testing "apply-remote-remove-page-ops-test1"
       (let [data-from-ws {:req-id "req-id" :t 1 :t-before 0
                           :affected-blocks

Разлика између датотеке није приказан због своје велике величине
+ 353 - 317
static/yarn.lock


+ 4 - 9
yarn.lock

@@ -932,11 +932,6 @@
   dependencies:
     mini-svg-data-uri "^1.2.3"
 
-"@tailwindcss/[email protected]":
-  version "0.4.2"
-  resolved "https://registry.yarnpkg.com/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz#f353c5a8ab2c939c6267ac5b907f012e5ee130f9"
-  integrity sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==
-
 "@tailwindcss/[email protected]":
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.7.tgz#e0b95bea787ee14c5a34a74fc824e6fe86ea8855"
@@ -4530,10 +4525,10 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/just-once/-/just-once-1.1.0.tgz#fe81a185ebaeeb0947a7e705bf01cb6808db0ad8"
   integrity sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==
 
-katex@^0.16.7:
-  version "0.16.8"
-  resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.8.tgz#89b453f40e8557f423f31a1009e9298dd99d5ceb"
-  integrity sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==
+katex@^0.16.10:
+  version "0.16.10"
+  resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.10.tgz#6f81b71ac37ff4ec7556861160f53bc5f058b185"
+  integrity sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==
   dependencies:
     commander "^8.3.0"
 

Неке датотеке нису приказане због велике количине промена