Ver código fonte

refactor: allow a block to have multiple types

For example, a whiteboard page can be used both as a property and a class.
Tienson Qin 2 anos atrás
pai
commit
0e8653985d

+ 3 - 3
deps/db/src/logseq/db/schema.cljs

@@ -13,12 +13,13 @@
 
    :recent/pages {}
 
-   ;; :block/type is a string type of the current block
+   ;; :block/type is a string type or multiple types of the current block
    ;; "whiteboard" for whiteboards
    ;; "macros" for macro
    ;; "property" for property blocks
    ;; "class" for structured page
-   :block/type {:db/index true}
+   :block/type {:db/index true
+                :db/cardinality :db.cardinality/many}
    :block/schema {}
    :block/uuid {:db/unique :db.unique/identity}
    :block/parent {:db/valueType :db.type/ref
@@ -132,7 +133,6 @@
     :block/deadline
     :block/repeated?
     :block/pre-block?
-    :block/type
     :block/properties
     :block/properties-order
     :block/properties-text-values

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

@@ -24,7 +24,7 @@
   (cond
     (:block/page block) 1
     (:file/content block) 3
-    (= "property" (:block/type block)) 6
+    (contains? (:block/type block) "property") 6
     (:block/name block) 2
     :else 5))
 

+ 3 - 3
deps/graph-parser/src/logseq/graph_parser/extract.cljc

@@ -145,9 +145,9 @@
 ;; TODO: performance improvement
 (defn- extract-pages-and-blocks
   "uri-encoded? - if is true, apply URL decode on the file path
-   options - 
+   options -
      :extracted-block-ids - An atom that contains all block ids that have been extracted in the current page (not yet saved to db)
-     :resolve-uuid-fn - Optional fn which is called to resolve uuids of each block. Enables diff-merge 
+     :resolve-uuid-fn - Optional fn which is called to resolve uuids of each block. Enables diff-merge
        (2 ways diff) based uuid resolution upon external editing.
        returns a list of the uuids, given the receiving ast, or nil if not able to resolve.
        Implemented in file-common-handler/diff-merge-uuids for IoC
@@ -169,7 +169,7 @@
                       (vec))
           ref-pages (atom #{})
           blocks (map (fn [block]
-                        (if (contains? #{"macro"} (:block/type block))
+                        (if (contains? (:block/type block) "macro")
                           block
                           (let [block-ref-pages (seq (:block/refs block))
                                 page-lookup-ref [:block/name page-name]

+ 1 - 1
deps/graph-parser/test/logseq/graph_parser/extract_test.cljs

@@ -103,6 +103,6 @@
         page (first pages)]
     (is (= (get-in page [:block/file :file/path]) "/whiteboards/foo.edn"))
     (is (= (:block/name page) "foo"))
-    (is (= (:block/type page) "whiteboard"))
+    (is (true? (contains? (:block/type page) "whiteboard")))
     (is (= (:block/original-name page) "Foo"))
     (is (every? #(= (:block/parent %) {:block/name "foo"}) blocks))))

+ 1 - 1
deps/graph-parser/test/logseq/graph_parser_test.cljs

@@ -95,7 +95,7 @@
             parent (:block/page (ffirst blocks))]
         (is (= {:block/name "foo"
                 :block/original-name "Foo"
-                :block/type "whiteboard"
+                :block/type #{"whiteboard"}
                 :block/file {:file/path "/whiteboards/foo.edn"}}
                parent)
             "parsed block in the whiteboard page has correct parent page"))))

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

@@ -20,7 +20,7 @@
     [:block/uuid :uuid]
     [:block/name {:optional true} :string]
     [:block/original-name {:optional true} :string]
-    [:block/type {:optional true} [:enum "property" "class" "object"]]
+    [:block/type {:optional true} :any] ;;TODO
     [:block/content {:optional true} :string]
     [:block/properties {:optional true}
      [:map-of :uuid [:or
@@ -121,4 +121,4 @@
     (validate-client-db ent-maps options)))
 
 (when (= nbb/*file* (:file (meta #'-main)))
-  (-main *command-line-args*))
+  (-main *command-line-args*))

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

@@ -358,16 +358,16 @@
                      (state/set-modal! #(property/property-config repo property {})))}
         (t :context-menu/configure-property)
         nil))
-    (ui/menu-link
-     {:key "Delete this property"
-      :on-click (fn []
-                  (let [class? (= "class" (:block/type block))
-                        f (if (and class? class-schema?)
-                            property-handler/class-remove-property!
-                            property-handler/remove-block-property!)]
-                    (f repo (:block/uuid block) (:block/uuid property))))}
-     (t :context-menu/delete-property)
-     nil)]))
+     (ui/menu-link
+      {:key "Delete this property"
+       :on-click (fn []
+                   (let [class? (contains? (:block/type block) "class")
+                         f (if (and class? class-schema?)
+                             property-handler/class-remove-property!
+                             property-handler/remove-block-property!)]
+                     (f repo (:block/uuid block) (:block/uuid property))))}
+      (t :context-menu/delete-property)
+      nil)]))
 
 ;; TODO: content could be changed
 ;; Also, keyboard bindings should only be activated after

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

@@ -331,7 +331,7 @@
                          (when (and (not hls-page?)
                                     (not fmt-journal?)
                                     (not config/publishing?)
-                                    (not (and (= "property" (:block/type page))
+                                    (not (and (contains? (:block/type page) "property")
                                               (contains? db-property/built-in-properties-keys-str page-name))))
                            (reset! *input-value (if untitled? "" old-name))
                            (reset! *edit? true)))))}
@@ -441,31 +441,11 @@
   [state page {:keys [journal?]}]
   (let [page-id (:db/id page)
         page (when page-id (db/sub-block page-id))
-        type (:block/type page)
-        class? (= type "class")
+        types (:block/type page)
+        class? (contains? types "class")
         parent-changed? (::parent-changed? state)]
     (when page
       [:div.property-configure.grid.gap-2
-       (when (and (not journal?)
-                  (if config/publishing?
-                    ;; Looks weird in read-only to show blank block type
-                    (contains? #{"property" "class"} type)
-                    (contains? #{"property" "class" nil} type)))
-         [:div.grid.grid-cols-4.gap-1
-          [:div.col-span-1 "Block type:"]
-          [:div.col-span-1
-           (ui/select (->> ["" "class" "property"]
-                           (map (fn [block-type]
-                                  {:label (if (seq block-type) (string/capitalize block-type) "Choose a block type")
-                                   :selected (= block-type type)
-                                   :disabled config/publishing?
-                                   :value block-type})))
-                      (fn [_e value]
-                        (if (seq value)
-                          (db/transact! [{:db/id (:db/id page)
-                                          :block/type value}])
-                          (db/transact! [[:db/retract (:db/id page) :block/type]]))))]])
-
        (when class?
          [:div.grid.grid-cols-4.gap-1.items-center.class-parent
           [:div.col-span-1 "Parent class:"]
@@ -505,13 +485,13 @@
                                  [:span class-name]
                                  [:a {:on-click #(route-handler/redirect-to-page! class-name)} class-name]))
                              class-ancestors)))]])
-       (when (and config/publishing? (= type "property"))
+       (when (and config/publishing? (contains? types "property"))
          (property/property-config (state/get-current-repo) page {}))])))
 
 (rum/defc page-properties < rum/reactive
   [page *configure-show?]
-  (let [type (:block/type page)
-        class? (= type "class")
+  (let [types (:block/type page)
+        class? (contains? types "class")
         configure? (rum/react *configure-show?)
         opts {:selected? false
               :page-configure? configure?}]
@@ -556,17 +536,17 @@
       (db/entity [:block/name page-name]))))
 
 (if config/publishing?
-    (defn- page-inner-init [state]
+  (defn- page-inner-init [state]
       ;; Duplicated from component
-      (let [repo (or (:repo (first (:rum/args state))) (state/get-current-repo))
-            path-page-name (get-path-page-name state (:page-name (first (:rum/args state))))
-            page-ent (some->> path-page-name util/page-name-sanity-lc (get-page-entity repo path-page-name))
+    (let [repo (or (:repo (first (:rum/args state))) (state/get-current-repo))
+          path-page-name (get-path-page-name state (:page-name (first (:rum/args state))))
+          page-ent (some->> path-page-name util/page-name-sanity-lc (get-page-entity repo path-page-name))
             ;; Only make class + property pages reader friendly by default as they usually have no content
-            default-configure (contains? #{"class" "property"} (:block/type page-ent))]
-        (assoc state ::configure-show? (atom default-configure))))
+          default-configure? (boolean (some #{"class" "property"} (:block/type page-ent)))]
+      (assoc state ::configure-show? (atom default-configure?))))
 
-    (defn- page-inner-init [state]
-      (assoc state ::configure-show? (atom false))))
+  (defn- page-inner-init [state]
+    (assoc state ::configure-show? (atom false))))
 
 ;; A page is just a logical block
 (rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query
@@ -586,7 +566,7 @@
           block? (some? (:block/page page))
           journal? (db/journal-page? page-name)
           db-based? (config/db-based-graph? repo)
-          built-in-property? (and (= "property" (:block/type page))
+          built-in-property? (and (contains? (:block/type page) "property")
                                   (contains? db-property/built-in-properties-keys-str page-name))
           fmt-journal? (boolean (date/journal-title->int page-name))
           whiteboard? (:whiteboard? option) ;; in a whiteboard portal shape?
@@ -639,7 +619,7 @@
                   (plugins/hook-ui-items :pagebar)]))])
 
           (when (and db-based? configure-show?)
-            (if (and (= "property" (:block/type page)) (not config/publishing?))
+            (if (and (contains? (:block/type page) "property") (not config/publishing?))
               (do
                 (state/set-modal! #(property/property-config repo page {}))
                 (swap! *configure-show? not))
@@ -1168,7 +1148,7 @@
                                              (or (boolean journal?)
                                                  (= false (boolean (:block/journal? %))))
                                              (or (boolean whiteboard?)
-                                                 (not= "whiteboard" (:block/type %)))))
+                                                 (contains? (:block/type %) "whiteboard"))))
                                    (sort-pages-by sort-by-item desc?)))))
            (reset! *pages pages)))
 

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

@@ -68,7 +68,7 @@
           repo (state/sub :git/current-repo)
           page (db/entity repo [:block/name page-name])
           page-original-name (:block/original-name page)
-          whiteboard? (= "whiteboard" (:block/type page))
+          whiteboard? (contains? (:block/type page) "whiteboard")
           block? (and page (util/uuid-string? page-name) (not whiteboard?))
           contents? (= page-name "contents")
           properties (:block/properties page)
@@ -82,7 +82,7 @@
           file-sync-graph-uuid (and (user-handler/logged-in?)
                                     (file-sync-handler/enable-sync?)
                                     (file-sync-handler/get-current-graph-uuid))
-          built-in-property? (and (= "property" (:block/type page))
+          built-in-property? (and (contains? (:block/type page) "property")
                                   (contains? db-property/built-in-properties-keys-str page-name))]
       (when (and page (not block?))
         (->>

+ 25 - 25
src/main/frontend/components/property.cljs

@@ -210,7 +210,7 @@
       (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)
             (pv/exit-edit-property))
-        (if (= "class" (:block/type entity))
+        (if (contains? (:block/type entity) "class")
           (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
@@ -219,7 +219,7 @@
             (pv/set-editing! property editor-id "" ""))))
       ;; new property entered
       (if (db-property/valid-property-name? property-name)
-        (if (= "class" (:block/type entity))
+        (if (contains? (:block/type entity) "class")
           (pv/add-property! entity property-name "" {:class-schema? class-schema? :exit-edit? page-configure?})
           (do
             (db-property-handler/upsert-property! repo property-name {:type :default} {})
@@ -410,14 +410,14 @@
 
 (defn- get-namespace-parents
   [tags]
-  (let [tags' (filter (fn [tag] (= "class" (:block/type tag))) tags)
+  (let [tags' (filter (fn [tag] (contains? (:block/type tag) "class")) tags)
         *namespaces (atom #{})]
     (doseq [tag tags']
       (when-let [ns (:block/namespace tag)]
         (loop [current-ns ns]
           (when (and
                  current-ns
-                 (= "class" (:block/type ns))
+                 (contains? (:block/type ns) "class")
                  (not (contains? @*namespaces (:db/id ns))))
             (swap! *namespaces conj current-ns)
             (recur (:block/namespace current-ns))))))
@@ -489,7 +489,7 @@
                                              properties))
         classes (->> (:block/tags block)
                      (sort-by :block/name)
-                     (filter (fn [tag] (= "class" (:block/type tag)))))
+                     (filter (fn [tag] (contains? (:block/type tag) "class"))))
         one-class? (= 1 (count classes))
         namespace-parents (get-namespace-parents classes)
         all-classes (->> (concat classes namespace-parents)
@@ -549,26 +549,26 @@
                    (not new-property?)
                    (not (:page-configure? opts)))
       [:div.ls-properties-area (cond->
-                                 {}
-                                  (:selected? opts)
-                                  (assoc :class "select-none"))
-        (properties-section block (if class-schema? properties own-properties) opts)
+                                {}
+                                 (:selected? opts)
+                                 (assoc :class "select-none"))
+       (properties-section block (if class-schema? properties own-properties) opts)
 
-        (when (and (seq full-hidden-properties) (not class-schema?) (not config/publishing?))
-          (hidden-properties block full-hidden-properties opts))
+       (when (and (seq full-hidden-properties) (not class-schema?) (not config/publishing?))
+         (hidden-properties block full-hidden-properties opts))
 
-        (when (or new-property? (not in-block-container?))
-          (new-property block edit-input-id properties new-property? opts))
+       (when (or new-property? (not in-block-container?))
+         (new-property block edit-input-id properties new-property? opts))
 
-        (when (and (seq class->properties) (not one-class?))
-          (let [page-cp (:page-cp opts)]
-            [:div.parent-properties.flex.flex-1.flex-col.gap-1
-             (for [[class class-properties] class->properties]
-               (let [id-properties (->> class-properties
-                                        remove-built-in-properties
-                                        (map (fn [id] [id (get block-properties id)])))]
-                 (when (seq id-properties)
-                   [:div
-                    (when page-cp
-                      [:span.text-sm (page-cp {} class)])
-                    (properties-section block id-properties opts)])))]))])))
+       (when (and (seq class->properties) (not one-class?))
+         (let [page-cp (:page-cp opts)]
+           [:div.parent-properties.flex.flex-1.flex-col.gap-1
+            (for [[class class-properties] class->properties]
+              (let [id-properties (->> class-properties
+                                       remove-built-in-properties
+                                       (map (fn [id] [id (get block-properties id)])))]
+                (when (seq id-properties)
+                  [:div
+                   (when page-cp
+                     [:span.text-sm (page-cp {} class)])
+                   (properties-section block id-properties opts)])))]))])))

+ 1 - 1
src/main/frontend/components/property/value.cljs

@@ -40,7 +40,7 @@
   ([block property-key property-value {:keys [exit-edit? class-schema?]
                                        :or {exit-edit? true}}]
    (let [repo (state/get-current-repo)
-         class? (= (:block/type block) "class")]
+         class? (contains? (:block/type block) "class")]
      (when property-key
        (if (and class? class-schema?)
          (property-handler/class-add-property! repo (:block/uuid block) property-key)

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

@@ -151,7 +151,7 @@
       [[:.flex.items-center.page-title
         (if-let [icon (pu/lookup (:block/properties page) :icon)]
           [:.text-md.mr-2 icon]
-          (ui/icon (if (= "whiteboard" (:block/type page)) "whiteboard" "page") {:class "text-md mr-2"}))
+          (ui/icon (if (contains? (:block/type page) "whiteboard") "whiteboard" "page") {:class "text-md mr-2"}))
         [:span.overflow-hidden.text-ellipsis (db-model/get-page-original-name page-name)]]
        (page-cp repo page-name)])
 

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

@@ -432,7 +432,7 @@ independent of format as format specific heading characters are stripped"
                                  children (map :db/id (sort-by-left (:block/_parent e) e))]
                              [e {:original-name (:block/original-name e)
                                  :link (:block/link e)
-                                 :type (:block/type e)
+                                 :types (:block/type e)
                                  :schema (:block/schema e)
                                  :content (:block/content e)
                                  :marker (:block/marker e)
@@ -1386,13 +1386,13 @@ independent of format as format specific heading characters are stripped"
     (string? page)
     (let [page (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)])]
       (or
-       (= "whiteboard" (:block/type page))
+       (contains? (:block/type page) "whiteboard")
        (when-let [file (:block/file page)]
          (when-let [path (:file/path (db-utils/entity (:db/id file)))]
            (gp-config/whiteboard? path)))))
 
     (seq page)
-    (= "whiteboard" (:block/type page))
+    (contains? (:block/type page) "whiteboard")
 
     :else false))
 
@@ -1421,7 +1421,7 @@ independent of format as format specific heading characters are stripped"
                                 (not (contains? built-in-pages name))
                                 (not (whiteboard-page? page))
                                 (not (:block/_namespace page))
-                                (not (contains? #{"property"} (:block/type page)))
+                                (not (contains? (:block/type page) "property"))
                                  ;; a/b/c might be deleted but a/b/c/d still exists (for backward compatibility)
                                 (not (and (string/includes? name "/")
                                           (not (:block/journal? page))))

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

@@ -102,12 +102,12 @@
   (let [property (db/entity [:block/name (gp-util/page-name-sanity-lc k-name)])
         k-name (name k-name)
         property-uuid (or (:block/uuid property) property-uuid (db/new-block-id))]
-    (when (and property (nil? (:block/type property)))
+    (when property
       (db/transact! repo [(outliner-core/block-with-updated-at
                            {:block/schema schema
                             :block/uuid property-uuid
                             :block/type "property"})]
-        {:outliner-op :save-block}))
+                    {:outliner-op :save-block}))
     (when (nil? property) ;if property not exists yet
       (db/transact! repo [(outliner-core/block-with-timestamps
                            {:block/schema schema
@@ -115,7 +115,7 @@
                             :block/name (util/page-name-sanity-lc k-name)
                             :block/uuid property-uuid
                             :block/type "property"})]
-        {:outliner-op :insert-blocks}))))
+                    {:outliner-op :insert-blocks}))))
 
 (defn set-block-property!
   [repo block-id k-name v {:keys [old-value]}]
@@ -222,7 +222,7 @@
 
 (defn class-add-property!
   [repo class k-name]
-  (when (= "class" (:block/type class))
+  (when (contains? (:block/type class) "class")
     (let [k-name (name k-name)
           property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)])
           property-uuid (or (:block/uuid property) (db/new-block-id))
@@ -239,7 +239,7 @@
 
 (defn class-remove-property!
   [repo class k-uuid]
-  (when (= "class" (:block/type class))
+  (when (contains? (:block/type class) "class")
     (when-let [property (db/pull repo '[*] [:block/uuid k-uuid])]
       (let [property-uuid (:block/uuid property)
             {:keys [properties] :as class-schema} (:block/schema class)
@@ -247,7 +247,7 @@
             class-new-schema (assoc class-schema :properties new-properties)]
         (db/transact! repo [{:db/id (:db/id class)
                              :block/schema class-new-schema}]
-          {:outliner-op :save-block})))))
+                      {:outliner-op :save-block})))))
 
 (defn batch-set-property!
   "Notice that this works only for properties with cardinality equals to `one`."

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

@@ -1219,7 +1219,7 @@
   [block value opts]
   (let [entity (db/entity [:block/uuid (:block/uuid block)])]
     (when (and (:db/id entity)
-               (not (contains? #{"property"} (:block/type entity))))
+               (not (contains? (:block/type entity) "property")))
       (let [value (string/trim value)]
         ;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
         ;; maybe we shouldn't save the block/file in "will-unmount" event?

+ 8 - 8
src/main/frontend/handler/export.cljs

@@ -313,7 +313,7 @@
                [?b :block/name]] db)
 
         (map (fn [[{:block/keys [name] :as page}]]
-               (let [whiteboard? (= "whiteboard" (:block/type page))
+               (let [whiteboard? (contains? (:block/type page) "whiteboard")
                      blocks (db/get-page-blocks-no-cache
                              (state/get-current-repo)
                              name
@@ -321,13 +321,13 @@
                      blocks' (if whiteboard?
                                blocks
                                (map (fn [b]
-                                     (let [b' (if (seq (:block/properties b))
-                                                (update b :block/content
-                                                        (fn [content]
-                                                          (file-property/remove-properties-when-file-based
-                                                           repo (:block/format b) content)))
-                                                b)]
-                                       (safe-keywordize b'))) blocks))
+                                      (let [b' (if (seq (:block/properties b))
+                                                 (update b :block/content
+                                                         (fn [content]
+                                                           (file-property/remove-properties-when-file-based
+                                                            repo (:block/format b) content)))
+                                                 b)]
+                                        (safe-keywordize b'))) blocks))
                      children (if whiteboard?
                                 blocks'
                                 (outliner-tree/blocks->vec-tree blocks' name))

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

@@ -480,10 +480,10 @@
   "If a page is unable to delete, returns a map with more information. Otherwise returns nil"
   [repo page]
   (cond
-    (and (= "class" (:block/type page))
+    (and (contains? (:block/type page) "class")
          (seq (model/get-tag-blocks repo (:block/name page))))
     {:msg "Unable to delete this page because blocks are tagged with this page"}
-    (= "property" (:block/type page))
+    (contains? (:block/type page) "property")
     {:msg "Unable to delete this page because this page is a property"}))
 
 (defn delete!
@@ -981,7 +981,7 @@
                        (gp-config/draw? name)
                        (db/built-in-pages-names (string/upper-case name))
                        (db-property/built-in-properties-keys-str name)
-                       (contains? #{"macro"} (:block/type p))))))
+                       (contains? (:block/type p) "macro")))))
        (common-handler/fix-pages-timestamps)))
 
 (defn get-filters

+ 1 - 1
src/main/frontend/modules/file/core.cljs

@@ -149,7 +149,7 @@
         file-db-id (-> page-block :block/file :db/id)
         file-path (-> (db-utils/entity file-db-id) :file/path)]
     (if (and (string? file-path) (not-empty file-path))
-      (let [new-content (if (= "whiteboard" (:block/type page-block))
+      (let [new-content (if (contains? (:block/type page-block) "whiteboard")
                           (->
                            (up/ugly-pr-str {:blocks tree
                                             :pages (list (remove-transit-ids page-block))})

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

@@ -47,7 +47,7 @@
   [repo page-db-id outliner-op]
   (let [page-block (db/pull repo '[*] page-db-id)
         page-db-id (:db/id page-block)
-        whiteboard? (= "whiteboard" (:block/type page-block))
+        whiteboard? (contains? (:block/type page-block) "whiteboard")
         blocks-count (model/get-page-blocks-count repo page-db-id)
         blocks-just-deleted? (and (zero? blocks-count)
                                   (contains? #{:delete-blocks :move-blocks} outliner-op))]

+ 1 - 1
src/main/frontend/persist_db/browser.cljs

@@ -57,7 +57,7 @@
   (cond
     (:block/page block) 1
     (:file/content block) 3
-    (= "property" (:block/type block)) 6
+    (contains? (:block/type block) "property") 6
     (:block/name block) 2
     :else 5))