From 9e10dc17377fae11184770f390f9dd2c0b28f57b Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 1 Dec 2024 10:59:11 +0800 Subject: [PATCH 001/105] wip: use :block/tags to represent block types --- deps/db/src/logseq/db.cljs | 47 ++++---- deps/db/src/logseq/db/frontend/class.cljs | 6 ++ .../src/logseq/db/frontend/entity_util.cljs | 38 ++++--- .../src/logseq/db/frontend/malli_schema.cljs | 4 +- deps/db/src/logseq/db/frontend/order.cljs | 8 +- deps/db/src/logseq/db/frontend/property.cljs | 8 +- .../logseq/db/frontend/property/build.cljs | 2 +- .../src/logseq/db/frontend/property/type.cljs | 2 +- .../src/logseq/graph_parser/block.cljs | 16 ++- src/main/frontend/components/all_pages.cljs | 7 -- src/main/frontend/components/editor.cljs | 2 +- src/main/frontend/components/page.cljs | 5 +- src/main/frontend/db/async.cljs | 2 +- src/main/frontend/db/model.cljs | 101 ++++++++++++------ src/main/frontend/db/query_dsl.cljs | 2 +- src/main/frontend/handler/journal.cljs | 2 +- src/main/frontend/worker/db_worker.cljs | 2 +- src/main/frontend/worker/handler/page.cljs | 4 +- .../worker/handler/page/db_based/page.cljs | 61 +++++------ src/main/frontend/worker/rtc/db_listener.cljs | 2 +- .../frontend/worker/rtc/remote_update.cljs | 1 - src/main/frontend/worker/search.cljs | 7 -- 22 files changed, 181 insertions(+), 148 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index f7a2086ba38..dc38ca37344 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -176,33 +176,34 @@ (def db-based-graph? entity-util/db-based-graph?) (defn page-exists? - "Whether a page exists with the `type`." - [db page-name type'] + "Whether a page exists with the `tags`." + [db page-name tags] (when page-name (if (db-based-graph? db) ;; Classes and properties are case sensitive - (if (#{"class" "property"} type') - (seq - (d/q - '[:find [?p ...] - :in $ ?name ?type - :where - [?p :block/title ?name] - [?p :block/type ?type]] - db - page-name - type')) + (let [tags (if (coll? tags) (set tags) #{tags})] + (if (set/intersection #{:logseq.class/Class :logseq.class/Property} tags) + (seq + (d/q + '[:find [?p ...] + :in $ ?name [?tag ...] + :where + [?p :block/title ?name] + [?p :block/tags ?tag]] + db + page-name + tags)) ;; TODO: Decouple db graphs from file specific :block/name - (seq - (d/q - '[:find [?p ...] - :in $ ?name ?type - :where - [?p :block/name ?name] - [?p :block/type ?type]] - db - (common-util/page-name-sanity-lc page-name) - type'))) + (seq + (d/q + '[:find [?p ...] + :in $ ?name [?tag ...] + :where + [?p :block/name ?name] + [?p :block/tags ?tag]] + db + (common-util/page-name-sanity-lc page-name) + tags)))) (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)])))) (defn get-page diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 70aff139a23..f99eef3f869 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -9,6 +9,12 @@ (ordered-map :logseq.class/Root {:title "Root Tag"} + :logseq.class/Page {:title "Page"} + :logseq.class/Whiteboard {:title "Whiteboard"} + :logseq.class/Class {:title "Tag"} + :logseq.class/Property {:title "Property"} + :logseq.class/Closed-Value {:title "Closed Value"} + :logseq.class/Task {:title "Task" :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/deadline]}} diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 84c57d3358b..1ccfe669abb 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -10,36 +10,44 @@ (when db (= "db" (:kv/value (d/entity db :logseq.kv/db-type))))) -(defn page? - [block] - (contains? #{"page" "journal" "whiteboard" "class" "property"} - (:block/type block))) +(defn- has-tag? + [entity tag-ident] + (some (fn [t] (or (= (:db/ident t) tag-ident) + (= t tag-ident))) (:block/tags entity))) (defn internal-page? [entity] - (= (:block/type entity) "page")) + (has-tag? entity :logseq.class/Page)) (defn class? [entity] - (= (:block/type entity) "class")) + (has-tag? entity :logseq.class/Class)) (defn property? [entity] - (= (:block/type entity) "property")) - -(defn closed-value? - [entity] - (= (:block/type entity) "closed value")) + (has-tag? entity :logseq.class/Property)) (defn whiteboard? "Given a page entity or map, check if it is a whiteboard page" - [page] - (= (:block/type page) "whiteboard")) + [entity] + (has-tag? entity :logseq.class/Whiteboard)) + +(defn closed-value? + [entity] + (has-tag? entity :logseq.class/Closed-Value)) (defn journal? "Given a page entity or map, check if it is a journal page" - [page] - (= (:block/type page) "journal")) + [entity] + (has-tag? entity :logseq.class/Journal)) + +(defn page? + [entity] + (or (internal-page? entity) + (class? entity) + (property? entity) + (whiteboard? entity) + (journal? entity))) (defn asset? "Given an entity or map, check if it is an asset block" diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 3872bd57875..1bcd42522a4 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -188,7 +188,6 @@ ;; ================== ;; These schemas should be data vars to remain as simple and reusable as possible - (def ^:dynamic *db-for-validate-fns* "Used by validate-fns which need db as input" nil) @@ -228,7 +227,6 @@ "Common attributes for pages" [[:block/name :string] [:block/title :string] - [:block/type [:enum "page" "class" "property" "whiteboard" "journal"]] [:block/alias {:optional true} [:set :int]] ;; TODO: Should this be here or in common? [:block/path-refs {:optional true} [:set :int]] @@ -364,7 +362,7 @@ (vec (concat [:map] - [[:block/type [:= "closed value"]] + [;; [:block/tags [:= :logseq.class/Closed-Value]] ;; for built-in properties [:db/ident {:optional true} logseq-property-ident] [:block/title {:optional true} :string] diff --git a/deps/db/src/logseq/db/frontend/order.cljs b/deps/db/src/logseq/db/frontend/order.cljs index 958dd51ff8e..34008c48af3 100644 --- a/deps/db/src/logseq/db/frontend/order.cljs +++ b/deps/db/src/logseq/db/frontend/order.cljs @@ -10,8 +10,8 @@ (reset-max-key! *max-key key)) ([max-key-atom key] (when (and key (or (nil? @max-key-atom) - (> (compare key @max-key-atom) 0))) - (reset! max-key-atom key)))) + (> (compare key @max-key-atom) 0))) + (reset! max-key-atom key)))) (defn gen-key ([] @@ -50,7 +50,7 @@ (when (and (< (compare (:block/order e) (:block/order value)) 0) (not= (:db/id e) (:db/id value))) (:block/order e))) values)) - (let [properties (->> (d/datoms db :avet :block/type "property") + (let [properties (->> (d/datoms db :avet :block/tags :logseq.class/Property) (map (fn [d] (d/entity db (:e d)))) (sort-by :block/order) reverse)] @@ -68,7 +68,7 @@ (when (and (> (compare (:block/order e) (:block/order value)) 0) (not= (:db/id e) (:db/id value))) (:block/order e))) values)) - (let [properties (->> (d/datoms db :avet :block/type "property") + (let [properties (->> (d/datoms db :avet :block/tags :logseq.class/Property) (map (fn [d] (d/entity db (:e d)))) (sort-by :block/order))] (some (fn [property] diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index 0dfe68749c0..ba29612d1df 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -48,12 +48,6 @@ :schema {:type :any :public? false :hide? true}} - :block/type {:title "Node Type" - :attribute :block/type - :schema {:type :string - :public? false - :hide? true} - :queryable? true} :block/schema {:title "Node schema" :attribute :block/schema :schema {:type :map @@ -436,7 +430,7 @@ (def db-attribute-properties "Internal properties that are also db schema attributes" - #{:block/alias :block/tags :block/type :block/schema :block/parent + #{:block/alias :block/tags :block/schema :block/parent :block/order :block/collapsed? :block/page :block/refs :block/path-refs :block/link :block/title :block/closed-value-property diff --git a/deps/db/src/logseq/db/frontend/property/build.cljs b/deps/db/src/logseq/db/frontend/property/build.cljs index 4a4b260967d..fc2cd99a963 100644 --- a/deps/db/src/logseq/db/frontend/property/build.cljs +++ b/deps/db/src/logseq/db/frontend/property/build.cljs @@ -8,7 +8,7 @@ (defn- closed-value-new-block [block-id block-type value property] (let [property-id (:db/ident property)] - (merge {:block/type "closed value" + (merge {:block/tags [:logseq.class/Closed-Value] :block/format :markdown :block/uuid block-id :block/page property-id diff --git a/deps/db/src/logseq/db/frontend/property/type.cljs b/deps/db/src/logseq/db/frontend/property/type.cljs index e0d13f11bce..e7bc716de18 100644 --- a/deps/db/src/logseq/db/frontend/property/type.cljs +++ b/deps/db/src/logseq/db/frontend/property/type.cljs @@ -138,7 +138,7 @@ [db val] (when-let [ent (d/entity db val)] (and (some? (:block/title ent)) - (= (:block/type ent) "journal")))) + (entity-util/journal? ent)))) (def built-in-validation-schemas "Map of types to malli validation schemas that validate a property value for that type" diff --git a/deps/graph-parser/src/logseq/graph_parser/block.cljs b/deps/graph-parser/src/logseq/graph_parser/block.cljs index 29153b94551..3f16fa4d34d 100644 --- a/deps/graph-parser/src/logseq/graph_parser/block.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/block.cljs @@ -347,10 +347,11 @@ {:block/created-at current-ms :block/updated-at current-ms})) (if journal-day - (cond-> {:block/type "journal" - :block/journal-day journal-day} + (cond-> {:block/journal-day journal-day} db-based? - (assoc :block/tags [:logseq.class/Journal])) + (assoc :block/tags [:logseq.class/Journal]) + (not db-based?) + (assoc :block/type "journal")) {}))] [page page-entity])) @@ -388,8 +389,13 @@ nil)] [page nil]))] (when page - (let [type (if class? "class" (or (:block/type page) "page"))] - (assoc page :block/type type)))))) + (if (ldb/db-based-graph? db) + (let [tags (if class? [:logseq.class/Class] + (or (:block/tags page) + [:logseq.class/Page]))] + (assoc page :block/tags tags)) + (let [type (if class? "class" (or (:block/type page) "page"))] + (assoc page :block/type type))))))) (defn- db-namespace-page? "Namespace page that're not journal pages" diff --git a/src/main/frontend/components/all_pages.cljs b/src/main/frontend/components/all_pages.cljs index 6bdd459a90d..2971c953938 100644 --- a/src/main/frontend/components/all_pages.cljs +++ b/src/main/frontend/components/all_pages.cljs @@ -21,13 +21,6 @@ :cell (fn [_table row _column] (component-block/page-cp {} row)) :type :string} - {:id :block/type - :name "Type" - :cell (fn [_table row _column] - (let [type (get row :block/type)] - [:div.capitalize (if (= type "class") "tag" type)])) - :get-value (fn [row] (get row :block/type)) - :type :string} {:id :block.temp/refs-count :name (t :page/backlinks) :cell (fn [_table row _column] (:block.temp/refs-count row)) diff --git a/src/main/frontend/components/editor.cljs b/src/main/frontend/components/editor.cljs index 7292c7f1c87..8b7f99080b1 100644 --- a/src/main/frontend/components/editor.cljs +++ b/src/main/frontend/components/editor.cljs @@ -156,7 +156,7 @@ ;; reorder, shortest and starts-with first. (let [matched-pages-with-new-page (fn [partial-matched-pages] - (if (or (db/page-exists? q (if db-tag? "class" "page")) + (if (or (db/page-exists? q (if db-tag? #{:logseq.class/Class} #{:logseq.class/Page})) (and db-tag? (some ldb/class? (:block/_alias (db/get-page q))))) partial-matched-pages (if db-tag? diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index c5305b44a65..883d2bde822 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -301,10 +301,11 @@ (rum/defc page-title-editor < rum/reactive [page {:keys [*input-value *title-value *edit? untitled? page-name old-name whiteboard-page?]}] (let [input-ref (rum/create-ref) + tag-idents (map :db/ident (:block/tags page)) collide? #(and (not= (util/page-name-sanity-lc page-name) (util/page-name-sanity-lc @*title-value)) - (db/page-exists? page-name (:block/type page)) - (db/page-exists? @*title-value (:block/type page))) + (db/page-exists? page-name tag-idents) + (db/page-exists? @*title-value tag-idents)) rollback-fn #(let [old-name (if untitled? "" old-name)] (reset! *title-value old-name) (gobj/set (rum/deref input-ref) "value" old-name) diff --git a/src/main/frontend/db/async.cljs b/src/main/frontend/db/async.cljs index a142fa7b314..a259e007322 100644 --- a/src/main/frontend/db/async.cljs +++ b/src/main/frontend/db/async.cljs @@ -55,7 +55,7 @@ [graph & {:keys [remove-built-in-property? remove-non-queryable-built-in-property?] :or {remove-built-in-property? true remove-non-queryable-built-in-property? false}}] - (let [result (->> (d/datoms (db/get-db graph) :avet :block/type "property") + (let [result (->> (d/datoms (db/get-db graph) :avet :block/tags :logseq.class/Property) (map (fn [datom] (db/entity (:e datom)))) (sort-by (juxt ldb/built-in? :block/title)))] (cond->> result diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index cba6aa6ad8d..8375fc3b771 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -383,10 +383,10 @@ independent of format as format specific heading characters are stripped" (defn page-exists? "Whether a page exists." - [page-name type] + [page-name tags] (let [repo (state/get-current-repo)] (when-let [db (conn/get-db repo)] - (ldb/page-exists? db page-name type)))) + (ldb/page-exists? db page-name tags)))) (defn page-empty? "Whether a page is empty. Does it has a non-page block? @@ -531,14 +531,23 @@ independent of format as format specific heading characters are stripped" (defn get-journals-length [] (let [today (date-time-util/date->int (js/Date.))] - (d/q '[:find (count ?page) . - :in $ ?today - :where - [?page :block/type "journal"] - [?page :block/journal-day ?journal-day] - [(<= ?journal-day ?today)]] - (conn/get-db (state/get-current-repo)) - today))) + (if (config/db-based-graph?) + (d/q '[:find (count ?page) . + :in $ ?today + :where + [?page :block/tags :logseq.class/Journal] + [?page :block/journal-day ?journal-day] + [(<= ?journal-day ?today)]] + (conn/get-db (state/get-current-repo)) + today) + (d/q '[:find (count ?page) . + :in $ ?today + :where + [?page :block/type "journal"] + [?page :block/journal-day ?journal-day] + [(<= ?journal-day ?today)]] + (conn/get-db (state/get-current-repo)) + today)))) (defn get-latest-journals ([n] @@ -749,17 +758,29 @@ independent of format as format specific heading characters are stripped" (defn get-all-whiteboards [repo] - (d/q - '[:find [(pull ?page [:db/id - :block/uuid - :block/name - :block/title - :block/created-at - :block/updated-at]) ...] - :where - [?page :block/name] - [?page :block/type "whiteboard"]] - (conn/get-db repo))) + (if (config/db-based-graph?) + (d/q + '[:find [(pull ?page [:db/id + :block/uuid + :block/name + :block/title + :block/created-at + :block/updated-at]) ...] + :where + [?page :block/name] + [?page :block/tags :logseq.class/Whiteboard]] + (conn/get-db repo)) + (d/q + '[:find [(pull ?page [:db/id + :block/uuid + :block/name + :block/title + :block/created-at + :block/updated-at]) ...] + :where + [?page :block/name] + [?page :block/type "whiteboard"]] + (conn/get-db repo)))) (defn get-whiteboard-id-nonces [repo page-id] @@ -780,7 +801,7 @@ independent of format as format specific heading characters are stripped" [repo & {:keys [except-root-class?] :or {except-root-class? false}}] (let [db (conn/get-db repo) - classes (->> (d/datoms db :avet :block/type "class") + classes (->> (d/datoms db :avet :block/tags :logseq.class/Class) (map (fn [d] (db-utils/entity db (:e d)))))] (if except-root-class? @@ -849,18 +870,30 @@ independent of format as format specific heading characters are stripped" (defn get-pages-relation [repo with-journal?] (when-let [db (conn/get-db repo)] - (let [q (if with-journal? - '[:find ?p ?ref-page - :where - [?block :block/page ?p] - [?block :block/refs ?ref-page]] - '[:find ?p ?ref-page - :where - [?block :block/page ?p] - [(get-else $ ?p :block/type "N/A") ?type] - [(not= ?type "journal")] - [?block :block/refs ?ref-page]])] - (d/q q db)))) + (if (config/db-based-graph?) + (let [q (if with-journal? + '[:find ?p ?ref-page + :where + [?block :block/page ?p] + [?block :block/refs ?ref-page]] + '[:find ?p ?ref-page + :where + [?block :block/page ?p] + [?p :block/tags] + (not [?p :block/tags :logseq.class/Journal]) + [?block :block/refs ?ref-page]])] + (d/q q db)) + (let [q (if with-journal? + '[:find ?p ?ref-page + :where + [?block :block/page ?p] + [?block :block/refs ?ref-page]] + '[:find ?p ?ref-page + :where + [?block :block/page ?p] + (not [?p :block/type "journal"]) + [?block :block/refs ?ref-page]])] + (d/q q db))))) (defn get-namespace-pages "Accepts both sanitized and unsanitized namespaces" diff --git a/src/main/frontend/db/query_dsl.cljs b/src/main/frontend/db/query_dsl.cljs index d462cc771c0..96d3d8074af 100644 --- a/src/main/frontend/db/query_dsl.cljs +++ b/src/main/frontend/db/query_dsl.cljs @@ -323,7 +323,7 @@ (or (some->> (name property-name) (db-utils/q '[:find [(pull ?b [:db/ident]) ...] :in $ ?title - :where [?b :block/type "property"] [?b :block/title ?title]]) + :where [?b :block/tags :logseq.class/Property] [?b :block/title ?title]]) first :db/ident) ;; Don't return nil as that incorrectly matches all properties diff --git a/src/main/frontend/handler/journal.cljs b/src/main/frontend/handler/journal.cljs index 8ea60a4d07b..dfd2818093f 100644 --- a/src/main/frontend/handler/journal.cljs +++ b/src/main/frontend/handler/journal.cljs @@ -15,7 +15,7 @@ (when (and page (state/enable-journals? (state/get-current-repo))) (p/do! (db-async/ page - (seq tags) - (update :block/tags - (fnil into []) - (mapv (fn [tag] - (let [v (if (uuid? tag) - (d/entity @conn [:block/uuid tag]) - tag)] - (cond - (de/entity? v) - (:db/id v) - (map? v) - (:db/id v) - :else - v))) - tags))) + (let [type-tag (cond class? :logseq.class/Class + whiteboard? :logseq.class/Whiteboard + :else :logseq.class/Page) + tags' (conj tags type-tag) + page' (update page :block/tags + (fnil into []) + (mapv (fn [tag] + (let [v (if (uuid? tag) + (d/entity @conn [:block/uuid tag]) + tag)] + (cond + (de/entity? v) + (:db/id v) + (map? v) + (:db/id v) + :else + v))) + tags')) property-vals-tx-m ;; Builds property values for built-in properties like logseq.property.pdf/file (db-property-build/build-property-values-tx-m @@ -95,10 +94,12 @@ (defn- split-namespace-pages [db page date-formatter] - (let [{:block/keys [title] block-uuid :block/uuid block-type :block/type} page] + (let [{:block/keys [title] block-uuid :block/uuid} page] (->> - (if (and (contains? #{"page" "class"} block-type) (ns-util/namespace-page? title)) - (let [class? (= block-type "class") + (if (and (or (entity-util/class? page) + (entity-util/page? page)) + (ns-util/namespace-page? title)) + (let [class? (entity-util/class? page) parts (->> (string/split title ns-util/parent-re) (map string/trim) (remove string/blank?)) @@ -169,14 +170,14 @@ date-formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal)) title (sanitize-title title*) type (cond class? - "class" + :logseq.class/Class whiteboard? - "whiteboard" + :logseq.class/Whiteboard today-journal? - "journal" + :logseq.class/Journal :else - "page")] - (when-not (ldb/page-exists? db title type) + :logseq.class/Page)] + (when-not (ldb/page-exists? db title #{type}) (let [format :markdown page (-> (gp-block/page-name->map title @conn true date-formatter {:class? class? @@ -191,7 +192,7 @@ [page nil])] (when page ;; Don't validate journal names because they can have '/' - (when (not= "journal" type) + (when (not= :logseq.class/Journal type) (outliner-validate/validate-page-title-characters (str (:block/title page)) {:node page}) (doseq [parent parents] (outliner-validate/validate-page-title-characters (str (:block/title parent)) {:node parent}))) diff --git a/src/main/frontend/worker/rtc/db_listener.cljs b/src/main/frontend/worker/rtc/db_listener.cljs index ca6a001a681..f7f388e7071 100644 --- a/src/main/frontend/worker/rtc/db_listener.cljs +++ b/src/main/frontend/worker/rtc/db_listener.cljs @@ -22,7 +22,7 @@ (def ^:private watched-attrs #{:block/title :block/created-at :block/updated-at :block/alias - :block/tags :block/type :block/schema :block/link :block/journal-day + :block/tags :block/schema :block/link :block/journal-day :property/schema.classes :property.value/content :db/index :db/valueType :db/cardinality}) diff --git a/src/main/frontend/worker/rtc/remote_update.cljs b/src/main/frontend/worker/rtc/remote_update.cljs index 264847d2bb5..046c5ff6fa0 100644 --- a/src/main/frontend/worker/rtc/remote_update.cljs +++ b/src/main/frontend/worker/rtc/remote_update.cljs @@ -353,7 +353,6 @@ :block/updated-at :block/created-at :block/alias - :block/type :block/schema :block/tags :block/link diff --git a/src/main/frontend/worker/search.cljs b/src/main/frontend/worker/search.cljs index 266359fc68b..93ee92a2519 100644 --- a/src/main/frontend/worker/search.cljs +++ b/src/main/frontend/worker/search.cljs @@ -332,13 +332,6 @@ DROP TRIGGER IF EXISTS blocks_au; (drop-tables-and-triggers! db) (create-tables-and-triggers! db)) -(comment - (defn- property-value-when-closed - "Returns property value if the given entity is type 'closed value' or nil" - [ent] - (when (= (:block/type ent) "closed value") - (:block/title ent)))) - (comment (defn- get-db-properties-str "Similar to db-pu/readable-properties but with a focus on making property values searchable" From c5fa699db068654fb51482f801f2491baac7ca6c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 2 Dec 2024 12:55:42 +0800 Subject: [PATCH 002/105] Replace more :block/type --- deps/db/src/logseq/db.cljs | 4 +- .../src/logseq/db/frontend/entity_plus.cljc | 4 +- .../src/logseq/db/frontend/entity_util.cljs | 6 ++- deps/db/src/logseq/db/frontend/rules.cljc | 24 +++++++---- deps/db/src/logseq/db/frontend/schema.cljs | 2 +- deps/db/src/logseq/db/frontend/validate.cljs | 25 +----------- deps/db/src/logseq/db/sqlite/build.cljs | 8 ++-- deps/db/src/logseq/db/sqlite/common_db.cljs | 11 ++--- .../db/src/logseq/db/sqlite/create_graph.cljs | 12 +++--- deps/db/src/logseq/db/sqlite/util.cljs | 6 +-- deps/db/test/logseq/db/sqlite/build_test.cljs | 8 ++-- .../logseq/db/sqlite/create_graph_test.cljs | 6 +-- deps/db/test/logseq/db_test.cljs | 8 ++-- .../src/logseq/graph_parser/exporter.cljs | 18 ++++----- .../graph_parser/test/docs_graph_helper.cljs | 3 +- .../logseq/graph_parser/exporter_test.cljs | 40 +++++++++++-------- .../src/logseq/outliner/validate.cljs | 10 ++--- .../test/logseq/outliner/pipeline_test.cljs | 7 ++-- .../test/logseq/outliner/property_test.cljs | 5 ++- .../create_graph_with_schema_org.cljs | 2 +- 20 files changed, 100 insertions(+), 109 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index dc38ca37344..1ba7934d973 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -536,7 +536,7 @@ (defn get-all-properties [db] - (->> (d/datoms db :avet :block/type "property") + (->> (d/datoms db :avet :block/tags :logseq.class/Property) (map (fn [d] (d/entity db (:e d)))))) @@ -555,7 +555,7 @@ (defn get-title-with-parents [entity] - (if (contains? #{"page" "class"} (:block/type entity)) + (if (or (entity-util/class? entity) (entity-util/internal-page? entity)) (let [parents' (->> (get-page-parents entity) (remove (fn [e] (= :logseq.class/Root (:db/ident e)))) vec)] diff --git a/deps/db/src/logseq/db/frontend/entity_plus.cljc b/deps/db/src/logseq/db/frontend/entity_plus.cljc index 8eb511f86d5..7d5bd212ead 100644 --- a/deps/db/src/logseq/db/frontend/entity_plus.cljc +++ b/deps/db/src/logseq/db/frontend/entity_plus.cljc @@ -27,7 +27,7 @@ [^Entity e k default-value] (let [db (.-db e) db-based? (db-based-graph? db)] - (if (and db-based? (= "journal" (:block/type e))) + (if (and db-based? (entity-util/journal? e)) (get-journal-title db e) (let [search? (get (.-kv e) :block.temp/search?)] (or @@ -65,7 +65,7 @@ (let [db (.-db e)] (case k :block/raw-title - (if (and (db-based-graph? db) (= "journal" (:block/type e))) + (if (and (db-based-graph? db) (entity-util/journal? e)) (get-journal-title db e) (lookup-entity e :block/title default-value)) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 1ccfe669abb..42c1cf37ec3 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -12,8 +12,10 @@ (defn- has-tag? [entity tag-ident] - (some (fn [t] (or (= (:db/ident t) tag-ident) - (= t tag-ident))) (:block/tags entity))) + (let [tags (:block/tags entity)] + (some (fn [t] (or (= (:db/ident t) tag-ident) + (= t tag-ident))) + (if (coll? tags) tags [tags])))) (defn internal-page? [entity] diff --git a/deps/db/src/logseq/db/frontend/rules.cljc b/deps/db/src/logseq/db/frontend/rules.cljc index 3e7ab7e579a..3226596ebea 100644 --- a/deps/db/src/logseq/db/frontend/rules.cljc +++ b/deps/db/src/logseq/db/frontend/rules.cljc @@ -155,8 +155,18 @@ (dissoc query-dsl-rules :namespace :page-property :has-page-property :page-tags :all-page-tags) + (dissoc rules :namespace) - {:existing-property-value + + {:between + '[(between ?b ?start ?end) + [?b :block/page ?p] + [?p :block/tags :logseq.class/Journal] + [?p :block/journal-day ?d] + [(>= ?d ?start)] + [(<= ?d ?end)]] + + :existing-property-value '[;; non-ref value [(existing-property-value ?b ?prop ?val) [?prop-e :db/ident ?prop] @@ -229,7 +239,7 @@ :has-simple-query-property '[(has-simple-query-property ?b ?prop) [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] (has-property-or-default-value? ?b ?prop) [?prop-e :block/schema ?prop-schema] [(get ?prop-schema :public? true) ?public] @@ -239,7 +249,7 @@ :has-private-simple-query-property '[(has-private-simple-query-property ?b ?prop) [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] (has-property-or-default-value? ?b ?prop)] ;; Checks if a property exists for any features that are not simple queries @@ -247,7 +257,7 @@ '[(has-property ?b ?prop) [?b ?prop _] [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] [?prop-e :block/schema ?prop-schema] [(get ?prop-schema :public? true) ?public] [(= true ?public)]] @@ -256,7 +266,7 @@ :property '[(property ?b ?prop ?val) [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] [?prop-e :block/schema ?prop-schema] [(get ?prop-schema :public? true) ?public] [(= true ?public)] @@ -276,7 +286,7 @@ :simple-query-property '[(simple-query-property ?b ?prop ?val) [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] [?prop-e :block/schema ?prop-schema] [(get ?prop-schema :public? true) ?public] [(get ?prop-schema :type) ?type] @@ -287,7 +297,7 @@ :private-simple-query-property '[(private-simple-query-property ?b ?prop ?val) [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"] + [?prop-e :block/tags :logseq.class/Property] (property-value ?b ?prop-e ?val)] :tags diff --git a/deps/db/src/logseq/db/frontend/schema.cljs b/deps/db/src/logseq/db/frontend/schema.cljs index 633543011de..fb023f8bbc8 100644 --- a/deps/db/src/logseq/db/frontend/schema.cljs +++ b/deps/db/src/logseq/db/frontend/schema.cljs @@ -111,7 +111,7 @@ (dissoc schema :block/namespace :block/properties-text-values :block/pre-block? :recent/pages :block/file :block/properties :block/properties-order :block/repeated? :block/deadline :block/scheduled :block/priority - :block/marker :block/macros) + :block/marker :block/macros :block/type) {:block/name {:db/index true} ; remove db/unique for :block/name ;; closed value :block/closed-value-property {:db/valueType :db.type/ref diff --git a/deps/db/src/logseq/db/frontend/validate.cljs b/deps/db/src/logseq/db/frontend/validate.cljs index 00d326f2466..487db90d3ba 100644 --- a/deps/db/src/logseq/db/frontend/validate.cljs +++ b/deps/db/src/logseq/db/frontend/validate.cljs @@ -19,32 +19,11 @@ [closed-schema?] (if closed-schema? closed-db-schema-explainer db-schema-explainer)) -(defn validate-ents-before-after! - [changed-ids db-before db-after tx-data tx-meta] - (let [id->ent-before (into {} - (keep (fn [id] (when-let [ent (d/entity db-before id)] [id ent]))) - changed-ids) - id->ent-after (keep (fn [id] (when-let [ent (d/entity db-after id)] [id ent])) changed-ids) - ent-before+ent-after-coll - (reduce - (fn [acc [id ent-after]] - (if-let [ent-before (id->ent-before id)] - (conj acc [ent-before ent-after]) - acc)) - [] id->ent-after)] - (doseq [[ent-before ent-after] ent-before+ent-after-coll] - (let [[type-before type-after] [(:block/type ent-before) (:block/type ent-after)]] - (when (and (some? type-before) - (nil? type-after)) - (js/console.error "Illegal :block/type change, entity id:" (:db/id ent-after)) - (prn :ent-before ent-before :ent-after ent-after :tx-data tx-data :tx-meta tx-meta)))))) - (defn validate-tx-report! "Validates the datascript tx-report for entities that have changed. Returns boolean indicating if db is valid" - [{:keys [db-before db-after tx-data tx-meta]} validate-options] + [{:keys [db-after tx-data tx-meta]} validate-options] (let [changed-ids (->> tx-data (keep :e) distinct) - _ (validate-ents-before-after! changed-ids db-before db-after tx-data tx-meta) tx-datoms (mapcat #(d/datoms db-after :eavt %) changed-ids) ent-maps* (map (fn [[db-id m]] ;; Add :db/id for debugging @@ -88,7 +67,7 @@ (:block/page ent) (update :block/page (fn [id] (select-keys (d/entity db id) - [:block/name :block/type :db/id :block/created-at]))))) + [:block/name :block/tags :db/id :block/created-at]))))) :errors errors' ;; Group by type to reduce verbosity ;; TODO: Move/remove this to another fn if unused diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index a6bff24110f..084be5390c2 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -357,7 +357,7 @@ {:db/id (or (:db/id page) (new-db-id)) :block/title (or (:block/title page) (string/capitalize (:block/name page))) :block/name (or (:block/name page) (common-util/page-name-sanity-lc (:block/title page))) - :block/type "page" + :block/tags #{:logseq.class/Page} :block/format :markdown} (dissoc page :build/properties :db/id :block/name :block/title :build/tags)) pvalue-tx-m (->property-value-tx-m new-page (:build/properties page) properties all-idents)] @@ -376,8 +376,9 @@ page-uuids all-idents)) (when-let [tags (:build/tags page)] - {:block/tags (mapv #(hash-map :db/ident (get-ident all-idents %)) - tags)}))))) + {:block/tags (-> (mapv #(hash-map :db/ident (get-ident all-idents %)) + tags) + (conj :logseq.class/Page))}))))) ;; blocks tx (reduce (fn [acc m] (into acc @@ -480,7 +481,6 @@ :block/title page-name :block/uuid (common-uuid/gen-uuid :journal-page-uuid date-int) - :block/type "journal" :block/tags :logseq.class/Journal}))))) m))] ;; Order matters as some steps depend on previous step having prepared blocks or pages in a certain way diff --git a/deps/db/src/logseq/db/sqlite/common_db.cljs b/deps/db/src/logseq/db/sqlite/common_db.cljs index 63e4f0fc473..783d6af4aaa 100644 --- a/deps/db/src/logseq/db/sqlite/common_db.cljs +++ b/deps/db/src/logseq/db/sqlite/common_db.cljs @@ -186,7 +186,6 @@ :in $ ?today :where [?page :block/name ?page-name] - ;; [?page :block/type "journal"] [?page :block/journal-day ?journal-day] [(<= ?journal-day ?today)]] db @@ -213,13 +212,9 @@ (defn get-structured-datoms [db] - (mapcat (fn [type'] - (->> (d/datoms db :avet :block/type type') - (mapcat (fn [d] - (d/datoms db :eavt (:e d)))))) - [;; property and class pages are pulled from `get-all-pages` already - ;; "property" "class" - "closed value"])) + (->> (d/datoms db :avet :block/tags :logseq.class/Closed-Value) + (mapcat (fn [d] + (d/datoms db :eavt (:e d)))))) (defn get-favorites "Favorites page and its blocks" diff --git a/deps/db/src/logseq/db/sqlite/create_graph.cljs b/deps/db/src/logseq/db/sqlite/create_graph.cljs index 218cb093484..95a3e704c8a 100644 --- a/deps/db/src/logseq/db/sqlite/create_graph.cljs +++ b/deps/db/src/logseq/db/sqlite/create_graph.cljs @@ -10,7 +10,8 @@ [logseq.db.frontend.schema :as db-schema] [logseq.db.sqlite.util :as sqlite-util] [logseq.common.config :as common-config] - [logseq.db.frontend.order :as db-order])) + [logseq.db.frontend.order :as db-order] + [logseq.db.frontend.entity-util :as entity-util])) (defn- mark-block-as-built-in [block] (assoc block :logseq.property/built-in? true)) @@ -52,14 +53,15 @@ ;; Adding built-ins must come after initial properties [(mark-block-as-built-in' built-in-property)] (map mark-block-as-built-in' properties) - (keep #(when (= "closed value" (:block/type %)) (mark-block-as-built-in' %)) + (keep #(when (entity-util/closed-value? %) + (mark-block-as-built-in' %)) properties))] (doseq [m tx] (when-let [block-uuid (and (:db/ident m) (:block/uuid m))] (assert (string/starts-with? (str block-uuid) "00000002") m))) {:tx tx - :properties (filter #(= (:block/type %) "property") properties)})) + :properties (filter entity-util/property? properties)})) (def built-in-pages-names #{"Contents"}) @@ -111,7 +113,7 @@ {:block/uuid page-id :block/name common-config/views-page-name :block/title common-config/views-page-name - :block/type "page" + :block/tags [:logseq.class/Page] :block/schema {:public? false} :block/format :markdown :logseq.property/built-in? true}) @@ -131,7 +133,7 @@ {:block/uuid (common-uuid/gen-uuid) :block/name common-config/favorites-page-name :block/title common-config/favorites-page-name - :block/type "page" + :block/tags [:logseq.class/Page] :block/schema {:public? false} :block/format :markdown :logseq.property/built-in? true})]) diff --git a/deps/db/src/logseq/db/sqlite/util.cljs b/deps/db/src/logseq/db/sqlite/util.cljs index 93b1e500eb9..7b0eb481ba6 100644 --- a/deps/db/src/logseq/db/sqlite/util.cljs +++ b/deps/db/src/logseq/db/sqlite/util.cljs @@ -91,7 +91,7 @@ (block-with-timestamps (cond-> {:db/ident db-ident' - :block/type "property" + :block/tags #{:logseq.class/Property} :block/format :markdown :block/schema (merge {:type :default} (dissoc prop-schema :classes :cardinality)) :block/name (common-util/page-name-sanity-lc (name prop-name)) @@ -115,7 +115,7 @@ {:pre [(qualified-keyword? (:db/ident block))]} (block-with-timestamps (cond-> (merge block - {:block/type "class" + {:block/tags #{:logseq.class/Class} :block/format :markdown}) (and (not= (:db/ident block) :logseq.class/Root) (nil? (:logseq.property/parent block))) @@ -129,7 +129,7 @@ :block/title page-name :block/uuid (d/squuid) :block/format :markdown - :block/type "page"})) + :block/tags #{:logseq.class/Page}})) (defn kv "Creates a key-value pair tx with the key and value respectively stored under diff --git a/deps/db/test/logseq/db/sqlite/build_test.cljs b/deps/db/test/logseq/db/sqlite/build_test.cljs index 4e0417cc703..b460f624ff4 100644 --- a/deps/db/test/logseq/db/sqlite/build_test.cljs +++ b/deps/db/test/logseq/db/sqlite/build_test.cljs @@ -12,14 +12,14 @@ [{:page {:block/title "page1"} :blocks [{:block/title "Jrue Holiday" :build/tags [:Person]}]} {:page {:block/title "Jayson Tatum" :build/tags [:Person]}}])] - (is (= {:block/tags [{:block/title "Person", :block/type "class"}]} - (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/type]}]) ...] + (is (= {:block/tags [{:block/title "Person", :block/tags [{:block/title "Tag"}]}]} + (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/tags]}]) ...] :where [?b :block/title "Jrue Holiday"]] @conn))) "Person class is created and correctly associated to a block") - (is (= {:block/tags [{:block/title "Person", :block/type "class"}]} - (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/type]}]) ...] + (is (= {:block/tags [{:block/title "Person", :block/tags [{:block/title "Tag"}]}]} + (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/tags]}]) ...] :where [?b :block/title "Jayson Tatum"]] @conn))) "Person class is created and correctly associated to a page"))) diff --git a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs index 37c15754b99..813d73ccfb7 100644 --- a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs +++ b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs @@ -14,7 +14,7 @@ (deftest new-graph-db-idents (testing "a new graph follows :db/ident conventions for" (let [conn (db-test/create-conn) - ident-ents (->> (d/q '[:find (pull ?b [:db/ident :block/type]) + ident-ents (->> (d/q '[:find (pull ?b [:db/ident :block/tags]) :where [?b :db/ident]] @conn) (map first)) @@ -38,7 +38,7 @@ (map #(keyword (namespace %) (string/replace (name %) #".[^.]+$" ""))) set)] (is (= [] - (remove #(= "closed value" (:block/type %)) closed-value-ents)) + (remove ldb/closed-value? closed-value-ents)) "All property names that contain a '.' are closed values") (is (= #{} (set/difference @@ -56,7 +56,7 @@ (remove #(or (= "logseq.kv" (namespace (:db/ident %))) (= :logseq.property/empty-placeholder (:db/ident %))))) pages (d/q '[:find [(pull ?b [:logseq.property/built-in? :block/title]) ...] - :where [?b :block/type "page"]] + :where [?b :block/tags :logseq.class/Page]] @conn)] (is (= [] (remove :logseq.property/built-in? idents)) "All entities with :db/ident have built-in property (except for kv idents)") diff --git a/deps/db/test/logseq/db_test.cljs b/deps/db/test/logseq/db_test.cljs index 9d8735bdc22..45320dd63d6 100644 --- a/deps/db/test/logseq/db_test.cljs +++ b/deps/db/test/logseq/db_test.cljs @@ -29,16 +29,16 @@ (ex-message e))))))) (def class-parents-data - [{:block/type "class" + [{:block/tags :logseq.class/Class :block/title "x" :block/name "x" :block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"} - {:block/type "class" + {:block/tags :logseq.class/Class :block/title "y" :block/name "y" :block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99" :logseq.property/parent [:block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"]} - {:block/type "class" + {:block/tags :logseq.class/Class :block/title "z" :block/name "z" :block/uuid #uuid "d95f2912-a7af-41b9-8ed5-28861f7fc0be" @@ -63,4 +63,4 @@ (is (= "Foo" (:block/title (ldb/get-case-page @conn "Foo")))) ;; Case sensitive classes (is (= "movie" (:block/title (ldb/get-case-page @conn "movie")))) - (is (= "Movie" (:block/title (ldb/get-case-page @conn "Movie")))))) \ No newline at end of file + (is (= "Movie" (:block/title (ldb/get-case-page @conn "Movie")))))) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index aba14d3d17b..37a5bdd4e70 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -113,7 +113,7 @@ (if block-ns (->> (d/q '[:find [?b ...] :in $ ?name - :where [?b :block/uuid ?uuid] [?b :block/type "class"] [?b :block/name ?name]] + :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Class] [?b :block/name ?name]] db (ns-util/get-last-part full-name)) (map #(d/entity db %)) @@ -125,7 +125,7 @@ (first (d/q '[:find [?uuid ...] :in $ ?name - :where [?b :block/uuid ?uuid] [?b :block/type "class"] [?b :block/name ?name]] + :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Class] [?b :block/name ?name]] db full-name)))) @@ -295,7 +295,6 @@ (date-time-util/int->journal-title date-int (common-config/get-date-formatter user-config)))] (assoc page-m :block/uuid (common-uuid/gen-uuid :journal-page-uuid date-int) - :block/type "journal" :block/journal-day date-int))) (assoc :block/tags :logseq.class/Journal))] {:block @@ -907,7 +906,7 @@ (:block/name %)) (or (:block/uuid %) (throw (ex-info (str "No uuid for existing page " (pr-str (:block/name %))) - (select-keys % [:block/name :block/type])))))) + (select-keys % [:block/name :block/tags])))))) (into {}))) (defn- build-existing-page @@ -915,13 +914,10 @@ (let [;; These attributes are not allowed to be transacted because they must not change across files disallowed-attributes [:block/name :block/uuid :block/format :block/title :block/journal-day :block/created-at :block/updated-at] - allowed-attributes (into [:block/tags :block/alias :logseq.property/parent :block/type :db/ident] + allowed-attributes (into [:block/tags :block/alias :logseq.property/parent :db/ident] (keep #(when (db-malli-schema/user-property? (key %)) (key %)) m)) - block-changes (cond-> (select-keys m allowed-attributes) - ;; disallow any type -> "page" but do allow any conversion to a non-page type - (ldb/internal-page? m) - (dissoc :block/type))] + block-changes (select-keys m allowed-attributes)] (when-let [ignored-attrs (not-empty (apply dissoc m (into disallowed-attributes allowed-attributes)))] (notify-user {:msg (str "Import ignored the following attributes on page " (pr-str (:block/title m)) ": " ignored-attrs)})) @@ -952,8 +948,8 @@ (assoc :block/uuid (d/squuid)) ;; only happens for few file built-ins like tags and alias (and (contains? all-built-in-names (keyword (:block/name page))) - (not (:block/type page))) - (assoc :block/type "page")))] + (not (:block/tags page))) + (assoc :block/tags :logseq.class/Page)))] (cond-> page' (:block/namespace page) ((fn [block'] diff --git a/deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs b/deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs index 9658abdafab..cba874234d1 100644 --- a/deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs @@ -23,7 +23,6 @@ (sh ["git" "clone" "--depth" "1" "-b" branch "-c" "advice.detachedHead=false" "https://github.com/logseq/docs" dir] {}))) - ;; Fns for common test assertions ;; ============================== (defn get-top-block-properties @@ -62,7 +61,7 @@ (defn- get-journal-page-count [db] (->> (d/q '[:find (count ?b) :where - [?b :block/type "journal"] + [?b :block/journal-day] [?b :block/name] [?b :block/file]] db) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index f80efe95fe3..9d948b07a16 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -33,14 +33,14 @@ (d/q '[:find [(pull ?b [*]) ...] :in $ ?pattern :where [?b :block/title ?content] - [(missing? $ ?b :block/type)] + [(missing? $ ?b :block/tags)] [(re-find ?pattern ?content)]] db) first) (->> content (d/q '[:find [(pull ?b [*]) ...] :in $ ?content - :where [?b :block/title ?content] [(missing? $ ?b :block/type)]] + :where [?b :block/title ?content] [(missing? $ ?b :block/tags)]] db) first))) @@ -195,7 +195,6 @@ ;; Counts ;; Includes journals as property values e.g. :logseq.task/deadline - (is (= 24 (count (d/q '[:find ?b :where [?b :block/type "journal"]] @conn)))) (is (= 24 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Journal]] @conn)))) (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Task]] @conn)))) @@ -204,16 +203,16 @@ ;; Don't count pages like url.md that have properties but no content (is (= 10 - (count (->> (d/q '[:find [(pull ?b [:block/title :block/type]) ...] + (count (->> (d/q '[:find [(pull ?b [:block/title :block/tags]) ...] :where [?b :block/title] [_ :block/page ?b] (not [?b :logseq.property/built-in?])] @conn) (filter ldb/internal-page?)))) "Correct number of pages with block content") (is (= 11 (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/type "class"] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) count)) "Correct number of user classes") - (is (= 4 (count (d/datoms @conn :avet :block/type "whiteboard")))) + (is (= 4 (count (d/datoms @conn :avet :block/tags :logseq.class/Whiteboard)))) (is (= 0 (count @(:ignored-properties import-state))) ":filters should be the only ignored property") (is (= 1 (count @assets)))) @@ -236,7 +235,7 @@ (is (= 18 (->> @conn (d/q '[:find [(pull ?b [:db/ident]) ...] - :where [?b :block/type "property"]]) + :where [?b :block/tags :logseq.class/Property]]) (remove #(db-malli-schema/internal-ident? (:db/ident %))) count)) "Correct number of user properties") @@ -248,7 +247,7 @@ {:db/ident :user.property/startedat :block/schema {:type :date}}} (->> @conn (d/q '[:find [(pull ?b [:db/ident :block/schema]) ...] - :where [?b :block/type "property"]]) + :where [?b :block/tags :logseq.class/Property]]) (filter #(contains? #{:prop-bool :prop-string :prop-num :rangeincludes :sameas :startedat} (keyword (name (:db/ident %))))) set)) @@ -362,8 +361,12 @@ (:db/ident (find-page-by-name @conn "life"))) "Namespaced tag's ident has hierarchy to make it unique") - (is (= [{:block/type "class"}] - (d/q '[:find [(pull ?b [:block/type]) ...] :where [?b :block/name "life"]] @conn)) + (is (= ["Tag"] + (d/q '[:find [?t-title ...] + :where + [?b :block/name "life"] + [?b :block/tags ?t] + [?t :block/title ?t-title]] @conn)) "When a class is used and referenced on the same page, there should only be one instance of it") (is (= ["life"] @@ -442,8 +445,11 @@ (is (= #{:logseq.property/description :user.property/description} (set (d/q '[:find [?ident ...] :where [?b :db/ident ?ident] [?b :block/name "description"]] @conn))) "user description property is separate from built-in one") - (is (= #{"page" "class"} - (set (d/q '[:find [?type ...] :where [?b :block/type ?type] [?b :block/name "task"]] @conn))) + (is (= #{"Page" "Class"} + (set (d/q '[:find [?t-title ...] :where + [?b :block/tags ?t] + [?b :block/name "task"] + [?t :block/title ?t-title]] @conn))) "user page is separate from built-in class")) (testing "multiline blocks" @@ -495,7 +501,7 @@ (is (= 0 (count @(:ignored-properties import-state))) "No ignored properties") (is (= 0 (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/type "class"] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) count)) "Correct number of user classes") @@ -556,7 +562,7 @@ (:block/tags (readable-properties @conn block))) "tagged block has configured tag imported as a class") - (is (= "class" (:block/type tag-page)) + (is (= :logseq.class/Class (:db/ident (first (:block/tags tag-page)))) "configured tag page in :tag-classes is a class") (is (and another-tag-page (not (ldb/class? another-tag-page))) "unconfigured tag page is not a class") @@ -580,7 +586,7 @@ (is (= #{:user.class/Property :user.class/Movie :user.class/Class :user.class/Tool} (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/type "class"] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) set)) "All classes are correctly defined by :type") @@ -602,7 +608,7 @@ "tagged block can have another property that references the same class it is tagged with, without creating a duplicate class") - (is (= "class" (:block/type tag-page)) + (is (= :logseq.class/Class (:db/ident (first (:block/tags tag-page)))) "configured tag page derived from :property-classes is a class") (is (nil? (find-page-by-name @conn "type")) "No page exists for configured property") @@ -648,7 +654,7 @@ :user.class/Class :user.class/Tool :user.class/Whiteboard___Tool} (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/type "class"] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) set)) "All classes are correctly defined by :type") diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index b3b1d05bfc0..d691924dbd4 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -49,13 +49,13 @@ :in $ ?eid ?title :where [?b :block/title ?title] - [?b :block/type "property"] + [?b :block/tags :logseq.class/Property] [(not= ?b ?eid)]] '[:find [?b ...] :in $ ?eid ?title :where [?b :block/title ?title] - [?b :block/type "property"] + [?b :block/tags :logseq.class/Property] [(missing? $ ?b :logseq.property/built-in?)] [(not= ?b ?eid)]]) db @@ -113,14 +113,14 @@ :else (when-let [_res (seq (d/q '[:find [?b ...] - :in $ ?eid ?type ?title + :in $ ?eid [?tag ...] ?title :where [?b :block/title ?title] - [?b :block/type ?type] + [?b :block/tags ?tag] [(not= ?b ?eid)]] db (:db/id entity) - (:block/type entity) + (map :db/id (:block/tags entity)) new-title))] (throw (ex-info "Duplicate page without tag" {:type :notification diff --git a/deps/outliner/test/logseq/outliner/pipeline_test.cljs b/deps/outliner/test/logseq/outliner/pipeline_test.cljs index e6aa7e7a3f4..d242f7a62f6 100644 --- a/deps/outliner/test/logseq/outliner/pipeline_test.cljs +++ b/deps/outliner/test/logseq/outliner/pipeline_test.cljs @@ -13,9 +13,10 @@ (defn- get-blocks [db] (->> (d/q '[:find (pull ?b [* {:block/path-refs [:block/name :db/id]}]) :in $ - :where [?b :block/title] - [(missing? $ ?b :logseq.property/built-in?)] - [(missing? $ ?b :block/type)]] + :where + [?b :block/page] + [?b :block/title] + [(missing? $ ?b :logseq.property/built-in?)]] db) (map first))) diff --git a/deps/outliner/test/logseq/outliner/property_test.cljs b/deps/outliner/test/logseq/outliner/property_test.cljs index 172daf05419..8a36bc12322 100644 --- a/deps/outliner/test/logseq/outliner/property_test.cljs +++ b/deps/outliner/test/logseq/outliner/property_test.cljs @@ -3,7 +3,8 @@ [datascript.core :as d] [logseq.outliner.property :as outliner-property] [logseq.db.frontend.property :as db-property] - [logseq.db.test.helper :as db-test])) + [logseq.db.test.helper :as db-test] + [logseq.db :as ldb])) (deftest upsert-property! (testing "Creates a property" @@ -228,7 +229,7 @@ (testing "Add choice successfully" (let [_ (outliner-property/upsert-closed-value! conn :user.property/num {:value 3}) b (first (d/q '[:find [(pull ?b [*]) ...] :where [?b :property.value/content 3]] @conn))] - (is (= (:block/type b) "closed value")) + (is (ldb/closed-value? (d/entity @conn (:db/id b)))) (is (= [2 3] (map db-property/closed-value-content (:block/_closed-value-property (d/entity @conn :user.property/num))))))) diff --git a/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs b/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs index 1d5fabc6148..973bea7e670 100644 --- a/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs +++ b/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs @@ -376,7 +376,7 @@ (map (fn [m] (let [props (->> (db-property/properties m) (into {}))] - (cond-> (select-keys m [:block/name :block/type :block/title :block/schema :db/ident + (cond-> (select-keys m [:block/name :block/tags :block/title :block/schema :db/ident :logseq.property.class/properties :logseq.property/parent :db/cardinality :property/schema.classes :block/refs]) (seq props) From c428b87dce9e19c971dc1107ff65c26d5aaf94f3 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 3 Dec 2024 00:42:26 +0800 Subject: [PATCH 003/105] wip: schema migration --- deps/db/src/logseq/db.cljs | 2 +- deps/db/src/logseq/db/frontend/class.cljs | 19 ++++- .../src/logseq/db/frontend/entity_util.cljs | 2 +- deps/db/src/logseq/db/frontend/schema.cljs | 2 +- deps/db/src/logseq/db/sqlite/util.cljs | 2 +- .../src/logseq/graph_parser/block.cljs | 2 +- .../src/logseq/graph_parser/exporter.cljs | 14 ++-- deps/outliner/src/logseq/outliner/core.cljs | 3 +- .../src/logseq/outliner/property.cljs | 2 +- .../test/logseq/outliner/validate_test.cljs | 21 ++++-- src/main/frontend/components/editor.cljs | 2 +- src/main/frontend/components/views.cljs | 74 +++++++++---------- src/main/frontend/db/model.cljs | 2 +- src/main/frontend/handler/whiteboard.cljs | 2 +- src/main/frontend/worker/db/migrate.cljs | 25 ++++++- src/main/frontend/worker/export.cljs | 18 ++--- .../worker/handler/page/db_based/page.cljs | 4 +- .../handler/page/db_based/page_test.cljs | 11 ++- 18 files changed, 120 insertions(+), 87 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 1ba7934d973..d62f84707fc 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -182,7 +182,7 @@ (if (db-based-graph? db) ;; Classes and properties are case sensitive (let [tags (if (coll? tags) (set tags) #{tags})] - (if (set/intersection #{:logseq.class/Class :logseq.class/Property} tags) + (if (set/intersection #{:logseq.class/Tag :logseq.class/Property} tags) (seq (d/q '[:find [?p ...] diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index f99eef3f869..5da768fea18 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -10,9 +10,19 @@ :logseq.class/Root {:title "Root Tag"} :logseq.class/Page {:title "Page"} - :logseq.class/Whiteboard {:title "Whiteboard"} - :logseq.class/Class {:title "Tag"} - :logseq.class/Property {:title "Property"} + + :logseq.class/Tag + {:title "Tag" + :properties {:block/tags :logseq.class/Page}} + + :logseq.class/Whiteboard + {:title "Whiteboard" + :properties {:block/tags :logseq.class/Page}} + + :logseq.class/Property + {:title "Property" + :properties {:block/tags :logseq.class/Page}} + :logseq.class/Closed-Value {:title "Closed Value"} :logseq.class/Task @@ -21,7 +31,8 @@ :logseq.class/Journal {:title "Journal" - :properties {:logseq.property.journal/title-format "MMM do, yyyy"}} + :properties {:block/tags :logseq.class/Page + :logseq.property.journal/title-format "MMM do, yyyy"}} :logseq.class/Query {:title "Query" diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 42c1cf37ec3..ac95d41a6d4 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -23,7 +23,7 @@ (defn class? [entity] - (has-tag? entity :logseq.class/Class)) + (has-tag? entity :logseq.class/Tag)) (defn property? [entity] diff --git a/deps/db/src/logseq/db/frontend/schema.cljs b/deps/db/src/logseq/db/frontend/schema.cljs index fb023f8bbc8..77d708f810f 100644 --- a/deps/db/src/logseq/db/frontend/schema.cljs +++ b/deps/db/src/logseq/db/frontend/schema.cljs @@ -2,7 +2,7 @@ "Main datascript schemas for the Logseq app" (:require [clojure.set :as set])) -(def version 49) +(def version 51) ;; A page is a special block, a page can corresponds to multiple files with the same ":block/name". (def ^:large-vars/data-var schema diff --git a/deps/db/src/logseq/db/sqlite/util.cljs b/deps/db/src/logseq/db/sqlite/util.cljs index 7b0eb481ba6..123428f4c89 100644 --- a/deps/db/src/logseq/db/sqlite/util.cljs +++ b/deps/db/src/logseq/db/sqlite/util.cljs @@ -115,7 +115,7 @@ {:pre [(qualified-keyword? (:db/ident block))]} (block-with-timestamps (cond-> (merge block - {:block/tags #{:logseq.class/Class} + {:block/tags #{:logseq.class/Tag} :block/format :markdown}) (and (not= (:db/ident block) :logseq.class/Root) (nil? (:logseq.property/parent block))) diff --git a/deps/graph-parser/src/logseq/graph_parser/block.cljs b/deps/graph-parser/src/logseq/graph_parser/block.cljs index 3f16fa4d34d..84ee71356f4 100644 --- a/deps/graph-parser/src/logseq/graph_parser/block.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/block.cljs @@ -390,7 +390,7 @@ [page nil]))] (when page (if (ldb/db-based-graph? db) - (let [tags (if class? [:logseq.class/Class] + (let [tags (if class? [:logseq.class/Tag] (or (:block/tags page) [:logseq.class/Page]))] (assoc page :block/tags tags)) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 37a5bdd4e70..5cc591ec777 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -113,7 +113,7 @@ (if block-ns (->> (d/q '[:find [?b ...] :in $ ?name - :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Class] [?b :block/name ?name]] + :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Tag] [?b :block/name ?name]] db (ns-util/get-last-part full-name)) (map #(d/entity db %)) @@ -125,7 +125,7 @@ (first (d/q '[:find [?uuid ...] :in $ ?name - :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Class] [?b :block/name ?name]] + :where [?b :block/uuid ?uuid] [?b :block/tags :logseq.class/Tag] [?b :block/name ?name]] db full-name)))) @@ -989,7 +989,7 @@ (all-existing-page-uuids (::original-name m)) (all-existing-page-uuids (:block/name m)))] (build-existing-page (dissoc m ::original-name ::original-title) @conn page-uuid per-file-state options) - (when (or (= "class" (:block/type m)) + (when (or (ldb/class? m) ;; Don't build a new page if it overwrites an existing class (not (some-> (get @(:all-idents import-state) (some-> (or (::original-title m) (:block/title m)) @@ -1129,7 +1129,7 @@ (get-property-schema @(:property-schemas import-state) kw-name) {:title (name kw-name)})] (assert existing-page-uuid) - (merge (select-keys new-prop [:block/type :block/schema :db/ident :db/index :db/cardinality :db/valueType]) + (merge (select-keys new-prop [:block/tags :block/schema :db/ident :db/index :db/cardinality :db/valueType]) {:block/uuid existing-page-uuid}))) (set/intersection new-properties (set (map keyword (keys existing-pages))))) ;; Save properties on new property pages separately as they can contain new properties and thus need to be @@ -1137,7 +1137,7 @@ property-page-properties-tx (keep (fn [b] (when-let [page-properties (not-empty (db-property/properties b))] (merge page-properties {:block/uuid (:block/uuid b) - :block/type "property"}))) + :block/tags (conj (:block/tags page-properties) :logseq.class/Property)}))) properties-tx)] {:pages-tx pages-tx' :property-pages-tx (concat property-pages-tx converted-property-pages-tx) @@ -1384,7 +1384,7 @@ (defn- export-class-properties [conn repo-or-conn] (let [user-classes (->> (d/q '[:find (pull ?b [:db/id :db/ident]) - :where [?b :block/type "class"]] @conn) + :where [?b :block/tags :logseq.class/Tag]] @conn) (map first) (remove #(db-class/built-in-classes (:db/ident %)))) class-to-prop-uuids @@ -1396,7 +1396,7 @@ [(contains? ?user-classes ?class)] [?b ?prop _] [?prop-e :db/ident ?prop] - [?prop-e :block/type "property"]] + [?prop-e :block/tags :logseq.class/Property]] @conn (set (map :db/ident user-classes))) (remove #(ldb/built-in? (d/entity @conn (second %)))) diff --git a/deps/outliner/src/logseq/outliner/core.cljs b/deps/outliner/src/logseq/outliner/core.cljs index 2259fb821df..4afec519615 100644 --- a/deps/outliner/src/logseq/outliner/core.cljs +++ b/deps/outliner/src/logseq/outliner/core.cljs @@ -80,7 +80,8 @@ (let [refs (:block/_refs page)] (and (or (zero? (count refs)) (= #{db-id} (set (map :db/id refs)))) - (not (some #{"class" "property"} (:block/type page))))))}))] + (not (ldb/class? page)) + (not (ldb/property? page)))))}))] (when (seq orphaned-pages) (let [tx (mapv (fn [page] [:db/retractEntity (:db/id page)]) orphaned-pages)] (swap! txs-state (fn [state] (vec (concat state tx))))))))) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 84ee0774636..f416a6c3574 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -553,7 +553,7 @@ (when (seq values) (let [value-property-tx (map (fn [id] {:db/id id - :block/type "closed value" + :block/tags :logseq.class/Closed-Value :block/closed-value-property (:db/id property)}) (map :db/id values)) property-tx (outliner-core/block-with-updated-at {:db/id (:db/id property)})] diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index 16657c7ba50..92111687208 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -107,7 +107,15 @@ (let [conn (db-test/create-conn)] (testing "Validate pages" - (let [pages (d/q '[:find [(pull ?b [*]) ...] :where [?b :block/title] [?b :block/type]] @conn) + (let [pages (->> (d/q '[:find [?b ...] :where + [?b :block/title] + (or [?b :block/tags :logseq.class/Class] + [?b :block/tags :logseq.class/Property] + [?b :block/tags :logseq.class/Page] + [?b :block/tags :logseq.class/Journal] + [?b :block/tags :logseq.class/Whiteboard])] @conn) + (map (fn [id] + (d/entity @conn id)))) page-errors (atom {})] (doseq [page pages] (try @@ -123,9 +131,10 @@ "Default pages shouldn't have any validation errors"))) (testing "Validate property relationships" - (let [parent-child-pairs (d/q '[:find (pull ?parent [:block/title :block/type]) - (pull ?child [:block/title :block/type]) + (let [parent-child-pairs (d/q '[:find ?parent ?child :where [?child :logseq.property/parent ?parent]] @conn)] - (doseq [[parent child] parent-child-pairs] - (is (nil? (outliner-validate/validate-parent-property parent [child])) - (str "Parent and child page is valid: " (pr-str (:block/title parent)) " " (pr-str (:block/title child))))))))) \ No newline at end of file + (doseq [[parent-id child-id] parent-child-pairs] + (let [parent (d/entity @conn parent-id) + child (d/entity @conn child-id)] + (is (nil? (outliner-validate/validate-parent-property parent [child])) + (str "Parent and child page is valid: " (pr-str (:block/title parent)) " " (pr-str (:block/title child)))))))))) diff --git a/src/main/frontend/components/editor.cljs b/src/main/frontend/components/editor.cljs index 8b7f99080b1..8fe43764cd0 100644 --- a/src/main/frontend/components/editor.cljs +++ b/src/main/frontend/components/editor.cljs @@ -156,7 +156,7 @@ ;; reorder, shortest and starts-with first. (let [matched-pages-with-new-page (fn [partial-matched-pages] - (if (or (db/page-exists? q (if db-tag? #{:logseq.class/Class} #{:logseq.class/Page})) + (if (or (db/page-exists? q (if db-tag? #{:logseq.class/Tag} #{:logseq.class/Page})) (and db-tag? (some ldb/class? (:block/_alias (db/get-page q))))) partial-matched-pages (if db-tag? diff --git a/src/main/frontend/components/views.cljs b/src/main/frontend/components/views.cljs index 4b12784dd45..f7ed468f133 100644 --- a/src/main/frontend/components/views.cljs +++ b/src/main/frontend/components/views.cljs @@ -529,7 +529,6 @@ (defn- get-property-values [rows property] (let [property-ident (:db/ident property) - block-type? (= property-ident :block/type) values (->> (mapcat (fn [e] (let [e' (db/entity (:db/id e)) v (get e' property-ident)] (if (set? v) v #{v}))) rows) @@ -537,9 +536,8 @@ (distinct))] (->> (map (fn [e] - (let [label (get-property-value-content e) - label' (if (and block-type? (= label "class")) "tag" label)] - {:label (str label') :value e})) + (let [label (get-property-value-content e)] + {:label (str label) :value e})) values) (sort-by :label)))) @@ -607,7 +605,7 @@ (do (shui/popup-hide!) (let [property internal-property - new-filter [(:db/ident property) (if (= (:db/ident property) :block/type) :is :text-contains)] + new-filter [(:db/ident property) :text-contains] filters' (if (seq filters) (conj filters new-filter) [new-filter])] @@ -685,15 +683,14 @@ [:before :after] (concat [:is :is-not] - (when-not (= :block/type (:db/ident property)) - (case (get-in property [:block/schema :type]) - (:default :url :node) - [:text-contains :text-not-contains] - (:date) - [:date-before :date-after] - :number - [:number-gt :number-lt :number-gte :number-lte :between] - nil))))) + (case (get-in property [:block/schema :type]) + (:default :url :node) + [:text-contains :text-not-contains] + (:date) + [:date-before :date-after] + :number + [:number-gt :number-lt :number-gte :number-lte :between] + nil)))) (defn- get-filter-with-changed-operator [_property operator value] @@ -821,16 +818,11 @@ {:class "!px-2 rounded-none border-r" :variant "ghost" :size :sm} - (let [block-type? (= (:db/ident property) :block/type) - value (cond + (let [value (cond (uuid? value) (db/entity [:block/uuid value]) (and (coll? value) (every? uuid? value)) (set (map #(db/entity [:block/uuid %]) value)) - (and block-type? (coll? value)) - (map (fn [v] (if (= v "class") "tag" v)) value) - (and block-type? (= value "class")) - "tag" :else value)] [:div.flex.flex-row.items-center.gap-1.text-xs @@ -1012,15 +1004,15 @@ (rum/defc new-record-button < rum/static [table view-entity] (let [asset? (and (:logseq.property/built-in? view-entity) - (= (:block/name view-entity) "asset"))] + (= (:block/name view-entity) "asset"))] (ui/tooltip - (shui/button - {:variant "ghost" - :class "!px-1 text-muted-foreground" - :size :sm - :on-click (get-in table [:data-fns :add-new-object!])} - (ui/icon (if asset? "upload" "plus"))) - [:div "New record"]))) + (shui/button + {:variant "ghost" + :class "!px-1 text-muted-foreground" + :size :sm + :on-click (get-in table [:data-fns :add-new-object!])} + (ui/icon (if asset? "upload" "plus"))) + [:div "New record"]))) (rum/defc add-new-row < rum/static [table] @@ -1084,8 +1076,8 @@ *rows-wrap (rum/use-ref nil)] (rum/use-effect! - (fn [] (set-ready? true)) - []) + (fn [] (set-ready? true)) + []) (shui/table (let [columns' (:columns table) @@ -1097,17 +1089,17 @@ (table-header table columns' option selected-rows) (ui/virtualized-list - {:ref #(reset! *scroller-ref %) - :custom-scroll-parent (or (some-> (rum/deref *rows-wrap) (.closest ".sidebar-item-list")) - (gdom/getElement "main-content-container")) - :increase-viewport-by {:top 300 :bottom 300} - :compute-item-key (fn [idx] - (let [block (nth rows idx)] - (str "table-row-" (:db/id block)))) - :total-count (count rows) - :item-content (fn [idx] - (let [row (nth rows idx)] - (table-row table row columns' {} option)))}) + {:ref #(reset! *scroller-ref %) + :custom-scroll-parent (or (some-> (rum/deref *rows-wrap) (.closest ".sidebar-item-list")) + (gdom/getElement "main-content-container")) + :increase-viewport-by {:top 300 :bottom 300} + :compute-item-key (fn [idx] + (let [block (nth rows idx)] + (str "table-row-" (:db/id block)))) + :total-count (count rows) + :item-content (fn [idx] + (let [row (nth rows idx)] + (table-row table row columns' {} option)))}) (when add-new-object! (shui/table-footer (add-new-row table)))])])))) diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index 8375fc3b771..50fb3b1bd83 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -801,7 +801,7 @@ independent of format as format specific heading characters are stripped" [repo & {:keys [except-root-class?] :or {except-root-class? false}}] (let [db (conn/get-db repo) - classes (->> (d/datoms db :avet :block/tags :logseq.class/Class) + classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag) (map (fn [d] (db-utils/entity db (:e d)))))] (if except-root-class? diff --git a/src/main/frontend/handler/whiteboard.cljs b/src/main/frontend/handler/whiteboard.cljs index 990a8539fa2..30b83c0fdbb 100644 --- a/src/main/frontend/handler/whiteboard.cljs +++ b/src/main/frontend/handler/whiteboard.cljs @@ -69,7 +69,7 @@ {:db/id (:db/id page-entity) :block/title page-name :block/name (util/page-name-sanity-lc page-name) - :block/type "whiteboard" + :block/tags :logseq.class/Whiteboard :block/format :markdown :logseq.property/ls-type :whiteboard-page :logseq.property.tldraw/page tldraw-page diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index 03b0f44502e..2ff535ac27c 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -417,6 +417,27 @@ :block/title title'}))))) datoms))) +(defn- replace-block-type-with-tags + [conn _search-db] + (let [db @conn + datoms (d/datoms db :block/type) + journal-entity (d/entity db :logseq.class/Journal) + tx-data (map (fn [{:keys [e _a v]}] + (let [tag (case v + "page" :logseq.class/Page + "class" :logseq.class/Tag + "property" :logseq.class/Property + "journal" :logseq.class/Journal + "whiteboard" :logseq.class/Whiteboard + "closed value" :logseq.class/Closed-Value + (throw (ex-info "unsupported block/type" {:type v})))] + [[:db/retract e :block/type] + [:db/add e :block/tags tag]])) datoms)] + (concat + ;; set journal's parent to `#Page` + [[:db/add (:db/id journal-entity) :logseq.property/parent :logseq.class/Page]] + tx-data))) + (def schema-version->updates "A vec of tuples defining datascript migrations. Each tuple consists of the schema version integer and a migration map. A migration map can have keys of :properties, :classes @@ -494,7 +515,9 @@ :logseq.property.attribute/property-schema-classes :logseq.property.attribute/property-value-content]}] [47 {:fix replace-hidden-type-with-schema}] [48 {:properties [:logseq.property/default-value :logseq.property/scalar-default-value]}] - [49 {:fix replace-special-id-ref-with-id-ref}]]) + [49 {:fix replace-special-id-ref-with-id-ref}] + [50 {:classes [:logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard :logseq.class/Property :logseq.class/Closed-Value]}] + [51 {:fix replace-block-type-with-tags}]]) (let [max-schema-version (apply max (map first schema-version->updates))] (assert (<= db-schema/version max-schema-version)) diff --git a/src/main/frontend/worker/export.cljs b/src/main/frontend/worker/export.cljs index b037eb73fc8..1f5fc062f51 100644 --- a/src/main/frontend/worker/export.cljs +++ b/src/main/frontend/worker/export.cljs @@ -64,23 +64,17 @@ (:keys result))))) (group-by first) (mapcat (fn [[_id col]] - (let [type (some (fn [[_e a v _t]] - (when (= a :block/type) - v)) col) - ident (some (fn [[_e a v _t]] + (let [ident (some (fn [[_e a v _t]] (when (= a :db/ident) - v)) col)] + v)) col) + journal (some (fn [[_e a v _t]] + (when (= a :block/journal-day) + v)) col)] (map (fn [[e a v t]] (cond (and (contains? #{:block/title :block/name} a) - (or - ;; normal page or block - (not (contains? #{"class" "property" "journal" "closed value"} type)) - ;; class/property created by user - (and ident - (contains? #{"class" "property"} type) - (not (string/starts-with? (namespace ident) "logseq"))))) + (not (or ident journal))) [e a (str "debug " e) t] (= a :block/uuid) diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index 0424ccce363..67540ade861 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -18,7 +18,7 @@ (defn- build-page-tx [conn properties page {:keys [whiteboard? class? tags]}] (when (:block/uuid page) - (let [type-tag (cond class? :logseq.class/Class + (let [type-tag (cond class? :logseq.class/Tag whiteboard? :logseq.class/Whiteboard :else :logseq.class/Page) tags' (conj tags type-tag) @@ -170,7 +170,7 @@ date-formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal)) title (sanitize-title title*) type (cond class? - :logseq.class/Class + :logseq.class/Tag whiteboard? :logseq.class/Whiteboard today-journal? diff --git a/src/test/frontend/worker/handler/page/db_based/page_test.cljs b/src/test/frontend/worker/handler/page/db_based/page_test.cljs index 68de2dabbf4..2f0ef3ee82d 100644 --- a/src/test/frontend/worker/handler/page/db_based/page_test.cljs +++ b/src/test/frontend/worker/handler/page/db_based/page_test.cljs @@ -44,9 +44,12 @@ "Child class with new parent has correct parents") (worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true}) - (is (= #{"class" "page"} - (set (d/q '[:find [?type ...] - :where [?b :block/type ?type] [?b :block/title "class1"]] @conn))) + (is (= #{"Class" "Page"} + (set (d/q '[:find [?tag-type ...] + :where + [?b :block/title "class1"] + [?b :block/tags ?t] + [?t :block/title ?tag-title]] @conn))) "Using an existing class page in a multi-parent namespace doesn't allow a page to have a class parent and instead creates a new page"))) (testing "Child pages with same name and different parents" @@ -85,4 +88,4 @@ js/Error #"can't include \"/" (worker-db-page/create! conn "foo/bar" {})) - "Page can't have '/'n title"))) \ No newline at end of file + "Page can't have '/'n title"))) From 4e9d071fa00ae3ef2123260e864a871abc2371af Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 3 Dec 2024 14:20:31 +0800 Subject: [PATCH 004/105] fix: migration --- deps/db/src/logseq/db/frontend/class.cljs | 6 ++-- deps/db/src/logseq/db/frontend/schema.cljs | 4 ++- deps/db/src/logseq/db/sqlite/util.cljs | 6 ++-- src/main/frontend/handler/db_based/page.cljs | 16 +++++----- src/main/frontend/worker/db/migrate.cljs | 32 +++++++++++--------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 5da768fea18..e377ceeff53 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -7,14 +7,12 @@ (def ^:large-vars/data-var built-in-classes "Map of built-in classes for db graphs with their :db/ident as keys" (ordered-map + :logseq.class/Tag {:title "Tag"} + :logseq.class/Root {:title "Root Tag"} :logseq.class/Page {:title "Page"} - :logseq.class/Tag - {:title "Tag" - :properties {:block/tags :logseq.class/Page}} - :logseq.class/Whiteboard {:title "Whiteboard" :properties {:block/tags :logseq.class/Page}} diff --git a/deps/db/src/logseq/db/frontend/schema.cljs b/deps/db/src/logseq/db/frontend/schema.cljs index 77d708f810f..7004a556687 100644 --- a/deps/db/src/logseq/db/frontend/schema.cljs +++ b/deps/db/src/logseq/db/frontend/schema.cljs @@ -111,7 +111,9 @@ (dissoc schema :block/namespace :block/properties-text-values :block/pre-block? :recent/pages :block/file :block/properties :block/properties-order :block/repeated? :block/deadline :block/scheduled :block/priority - :block/marker :block/macros :block/type) + :block/marker :block/macros + ;; :block/type + ) {:block/name {:db/index true} ; remove db/unique for :block/name ;; closed value :block/closed-value-property {:db/valueType :db.type/ref diff --git a/deps/db/src/logseq/db/sqlite/util.cljs b/deps/db/src/logseq/db/sqlite/util.cljs index 123428f4c89..071f1dbbc2a 100644 --- a/deps/db/src/logseq/db/sqlite/util.cljs +++ b/deps/db/src/logseq/db/sqlite/util.cljs @@ -115,8 +115,10 @@ {:pre [(qualified-keyword? (:db/ident block))]} (block-with-timestamps (cond-> (merge block - {:block/tags #{:logseq.class/Tag} - :block/format :markdown}) + (cond-> + {:block/format :markdown} + (not= (:db/ident block) :logseq.class/Tag) + (assoc :block/tags #{:logseq.class/Tag}))) (and (not= (:db/ident block) :logseq.class/Root) (nil? (:logseq.property/parent block))) (assoc :logseq.property/parent :logseq.class/Root)))) diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index e12671abe83..397e86606e4 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -32,16 +32,16 @@ (throw e))))) (defn add-tag [repo block-id tag-entity] - (ui-outliner-tx/transact! - {:outliner-op :save-block} - (p/do! - (editor-handler/save-current-block!) + (let [opts {:outliner-op :save-block}] + (ui-outliner-tx/transact! opts + (p/do! + (editor-handler/save-current-block!) ;; Check after save-current-block to get most up to date block content - (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) - (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] + (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) + (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] ;; TODO: Move this to outliner.core to consistently add refs for tags - [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] - (db/transact! repo tx-data {:outliner-op :save-block})))))) + [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] + (db/transact! repo tx-data {:outliner-op :save-block}))))))) (defn convert-to-tag! [page-entity] diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index 2ff535ac27c..cbe1067829a 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -420,23 +420,25 @@ (defn- replace-block-type-with-tags [conn _search-db] (let [db @conn - datoms (d/datoms db :block/type) + block-type-entity (d/entity db :block/type) + datoms (d/datoms db :avet :block/type) journal-entity (d/entity db :logseq.class/Journal) - tx-data (map (fn [{:keys [e _a v]}] - (let [tag (case v - "page" :logseq.class/Page - "class" :logseq.class/Tag - "property" :logseq.class/Property - "journal" :logseq.class/Journal - "whiteboard" :logseq.class/Whiteboard - "closed value" :logseq.class/Closed-Value - (throw (ex-info "unsupported block/type" {:type v})))] - [[:db/retract e :block/type] - [:db/add e :block/tags tag]])) datoms)] + tx-data (mapcat (fn [{:keys [e _a v]}] + (let [tag (case v + "page" :logseq.class/Page + "class" :logseq.class/Tag + "property" :logseq.class/Property + "journal" :logseq.class/Journal + "whiteboard" :logseq.class/Whiteboard + "closed value" :logseq.class/Closed-Value + (throw (ex-info "unsupported block/type" {:type v})))] + [[:db/retract e :block/type] + [:db/add e :block/tags tag]])) datoms)] (concat - ;; set journal's parent to `#Page` - [[:db/add (:db/id journal-entity) :logseq.property/parent :logseq.class/Page]] - tx-data))) + ;; set journal's tag to `#Page` + [[:db/add (:db/id journal-entity) :block/tags :logseq.class/Page]] + tx-data + [[:db/retractEntity (:db/id block-type-entity)]]))) (def schema-version->updates "A vec of tuples defining datascript migrations. Each tuple consists of the From c7e26a671ee415df6e73f51a40cacda0dc6d98b8 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 3 Dec 2024 14:56:08 +0800 Subject: [PATCH 005/105] fix: schema validate --- .../src/logseq/db/frontend/entity_util.cljs | 3 +- .../src/logseq/db/frontend/malli_schema.cljs | 44 ++++++++++--------- deps/db/src/logseq/db/frontend/schema.cljs | 3 +- deps/db/src/logseq/db/frontend/validate.cljs | 7 +-- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index ac95d41a6d4..0c6db8c674b 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -23,7 +23,8 @@ (defn class? [entity] - (has-tag? entity :logseq.class/Tag)) + (or (= (:db/ident entity) :logseq.class/Tag) + (has-tag? entity :logseq.class/Tag))) (defn property? [entity] diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 1bcd42522a4..8d95b7d5fac 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -434,27 +434,29 @@ (into [:multi {:dispatch (fn [d] ;; order matters as some block types are a subset of others e.g. :whiteboard - (cond - (entity-util/property? d) - :property - (entity-util/class? d) - :class - (entity-util/hidden? d) - :hidden - (entity-util/whiteboard? d) - :normal-page - (entity-util/page? d) - :normal-page - (entity-util/asset? d) - :asset-block - (:file/path d) - :file-block - (:block/uuid d) - :block - (= (:db/ident d) :logseq.property/empty-placeholder) - :property-value-placeholder - (:db/ident d) - :db-ident-key-value))}] + (let [db *db-for-validate-fns* + d (if (:block/uuid d) (d/entity db [:block/uuid (:block/uuid d)]) d)] + (cond + (entity-util/property? d) + :property + (entity-util/class? d) + :class + (entity-util/hidden? d) + :hidden + (entity-util/whiteboard? d) + :normal-page + (entity-util/page? d) + :normal-page + (entity-util/asset? d) + :asset-block + (:file/path d) + :file-block + (:block/uuid d) + :block + (= (:db/ident d) :logseq.property/empty-placeholder) + :property-value-placeholder + (:db/ident d) + :db-ident-key-value)))}] {:property property-page :class class-page :hidden hidden-page diff --git a/deps/db/src/logseq/db/frontend/schema.cljs b/deps/db/src/logseq/db/frontend/schema.cljs index 7004a556687..4914eb3a160 100644 --- a/deps/db/src/logseq/db/frontend/schema.cljs +++ b/deps/db/src/logseq/db/frontend/schema.cljs @@ -112,8 +112,7 @@ :block/namespace :block/properties-text-values :block/pre-block? :recent/pages :block/file :block/properties :block/properties-order :block/repeated? :block/deadline :block/scheduled :block/priority :block/marker :block/macros - ;; :block/type - ) + :block/type) {:block/name {:db/index true} ; remove db/unique for :block/name ;; closed value :block/closed-value-property {:db/valueType :db.type/ref diff --git a/deps/db/src/logseq/db/frontend/validate.cljs b/deps/db/src/logseq/db/frontend/validate.cljs index 487db90d3ba..0acabff1ac6 100644 --- a/deps/db/src/logseq/db/frontend/validate.cljs +++ b/deps/db/src/logseq/db/frontend/validate.cljs @@ -41,13 +41,8 @@ (let [explainer (get-schema-explainer (:closed-schema? validate-options))] (js/console.error "Invalid datascript entities detected amongst changed entity ids:" changed-ids) (doseq [m invalid-ent-maps] - (prn {:entity-map m - :errors (me/humanize (explainer [m]))}) - ;; FIXME: pprint fails sometime - ;; (pprint/pprint {;; :entity-map (map #(into {} %) m) - ;; :errors (me/humanize (m/explain db-schema [m]))}) - ) + :errors (me/humanize (explainer [m]))})) false) true))))) From f2fa4848bb4cb26b19ce9d6af4909c2987db2979 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 3 Dec 2024 16:52:03 +0800 Subject: [PATCH 006/105] fix: property type --- deps/db/src/logseq/db.cljs | 1 + deps/db/src/logseq/db/frontend/entity_util.cljs | 10 ++++++++++ src/main/frontend/components/property.cljs | 12 +++++++----- src/main/frontend/components/property/value.cljs | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index d62f84707fc..5f928d3b4c8 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -85,6 +85,7 @@ (def object? entity-util/object?) (def asset? entity-util/asset?) (def public-built-in-property? db-property/public-built-in-property?) +(def get-entity-types entity-util/get-entity-types) (defn sort-by-order [blocks] diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 0c6db8c674b..156c62e0f43 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -69,3 +69,13 @@ (defn object? [node] (seq (:block/tags node))) + +(defn get-entity-types + "Get entity types from :block/tags" + [entity] + (let [ident->type {:logseq.class/Class :class + :logseq.class/Property :property + :logseq.class/Journal :journal + :logseq.class/Whiteboard :whiteboard + :logseq.class/Page :page}] + (set (map ident->type (:block/tags entity))))) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index 36a71b533d1..65278d762da 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -333,13 +333,15 @@ *show-new-property-config? (::show-new-property-config? state) *show-class-select? (::show-class-select? state) *property-schema (::property-schema state) - block-type (keyword (get block :block/type :block)) page? (ldb/page? block) + block-types (let [types (ldb/get-entity-types block)] + (cond-> types + (and page? (not (contains? types :page))) + (conj :page) + (empty? types) + #{:block})) exclude-properties (fn [m] - (let [view-context (get-in m [:block/schema :view-context] :all) - block-types (if (and page? (not= block-type :page)) - #{:page block-type} - #{block-type})] + (let [view-context (get-in m [:block/schema :view-context] :all)] (or (contains? #{:logseq.property/query} (:db/ident m)) (and (not page?) (contains? #{:block/alias} (:db/ident m))) ;; Filters out properties from being in wrong :view-context and :never view-contexts diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 03075ca1cc8..14def664818 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -506,7 +506,7 @@ (when (and property-type (not= property-type :node)) (if (= property-type :page) (not (db/page? node)) - (not= property-type (some-> (:block/type node) keyword)))))) + (not (contains? (ldb/get-entity-types node) property-type)))))) result))))) options (map (fn [node] From 41bf1cf5b57e3cd504878b065fca6dcde774a24f Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 3 Dec 2024 17:11:23 +0800 Subject: [PATCH 007/105] enhance: show title without tags if a property has specified tags --- .../frontend/components/property/value.cljs | 5 +++- src/main/frontend/handler/graph.cljs | 27 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 14def664818..7bbbd6c2b66 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -512,7 +512,10 @@ options (map (fn [node] (let [id (or (:value node) (:db/id node)) [header label] (if (integer? id) - (let [title (subs (title/block-unique-title node) 0 256) + (let [node-title (if (seq (:property/schema.classes property)) + (:block/title node) + (title/block-unique-title node)) + title (subs node-title 0 256) node (or (db/entity id) node) icon (get-node-icon node) header (when-not (db/page? node) diff --git a/src/main/frontend/handler/graph.cljs b/src/main/frontend/handler/graph.cljs index 1292c6dfaf4..32d0eea5f23 100644 --- a/src/main/frontend/handler/graph.cljs +++ b/src/main/frontend/handler/graph.cljs @@ -11,8 +11,7 @@ [frontend.storage :as storage] [logseq.graph-parser.db :as gp-db] [logseq.db.sqlite.create-graph :as sqlite-create-graph] - [logseq.db :as ldb] - [frontend.components.title :as title])) + [logseq.db :as ldb])) (defn- build-links [links] @@ -45,7 +44,7 @@ size (int (* 8 (max 1.0 (js/Math.cbrt n))))] (cond-> {:id (str (:db/id p)) - :label (title/block-unique-title p) + :label page-title :size size :color color :block/created-at (:block/created-at p)} @@ -72,8 +71,8 @@ (defn- normalize-page-name [{:keys [nodes links]}] (let [nodes' (->> (remove-uuids-and-files! nodes) - (util/distinct-by (fn [node] (:id node))) - (remove nil?))] + (util/distinct-by (fn [node] (:id node))) + (remove nil?))] {:nodes nodes' :links links})) @@ -207,15 +206,15 @@ (let [search-nodes (fn [forward?] (let [links (group-by (if forward? :source :target) links)] (loop [nodes nodes - level level] - (if (zero? level) - nodes - (recur (distinct (apply concat nodes - (map - (fn [id] - (->> (get links id) (map (if forward? :target :source)))) - nodes))) - (dec level)))))) + level level] + (if (zero? level) + nodes + (recur (distinct (apply concat nodes + (map + (fn [id] + (->> (get links id) (map (if forward? :target :source)))) + nodes))) + (dec level)))))) nodes (concat (search-nodes true) (search-nodes false)) nodes (set nodes)] (update graph :nodes From 6e2839c36a6eaae9df5d04f5725057c3fed0d692 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 4 Dec 2024 20:46:39 +0800 Subject: [PATCH 008/105] fix: transact depend classes ahead of properties Because properties rely on typed classes such as :logseq.property/Property, :logseq.property/Tag. --- deps/db/src/logseq/db/sqlite/create_graph.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db/sqlite/create_graph.cljs b/deps/db/src/logseq/db/sqlite/create_graph.cljs index 95a3e704c8a..fbba6121d6a 100644 --- a/deps/db/src/logseq/db/sqlite/create_graph.cljs +++ b/deps/db/src/logseq/db/sqlite/create_graph.cljs @@ -174,7 +174,10 @@ default-pages (->> (map sqlite-util/build-new-page built-in-pages-names) (map mark-block-as-built-in)) hidden-pages (concat (build-initial-views) (build-favorites-page)) - tx (vec (concat initial-data properties-tx default-classes + depend-class? (fn [c] (when (contains? #{:logseq.class/Property :logseq.class/Tag :logseq.class/Closed-Value} (:db/ident c)) c)) + depend-classes (filter depend-class? default-classes) + other-classes (remove depend-class? default-classes) + tx (vec (concat initial-data depend-classes properties-tx other-classes initial-files default-pages hidden-pages))] (validate-tx-for-duplicate-idents tx) tx)) From 16f0491104b30189121d6e1c8dea60343c767432 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 14:52:20 +0800 Subject: [PATCH 009/105] hide internal tags for node types --- deps/db/src/logseq/db.cljs | 1 + deps/db/src/logseq/db/frontend/entity_util.cljs | 6 +++++- src/main/frontend/components/block.cljs | 3 ++- src/main/frontend/components/property/value.cljs | 4 +++- src/main/frontend/db/model.cljs | 5 ++++- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 5f928d3b4c8..f83725feb2d 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -86,6 +86,7 @@ (def asset? entity-util/asset?) (def public-built-in-property? db-property/public-built-in-property?) (def get-entity-types entity-util/get-entity-types) +(def internal-tags entity-util/internal-tags) (defn sort-by-order [blocks] diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 156c62e0f43..fb99671ebad 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -73,9 +73,13 @@ (defn get-entity-types "Get entity types from :block/tags" [entity] - (let [ident->type {:logseq.class/Class :class + (let [ident->type {:logseq.class/Tag :class :logseq.class/Property :property :logseq.class/Journal :journal :logseq.class/Whiteboard :whiteboard :logseq.class/Page :page}] (set (map ident->type (:block/tags entity))))) + +(def internal-tags + #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag + :logseq.class/Closed-Value}) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index ea9c2e7356e..579856a0753 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2635,7 +2635,8 @@ (:block/tags block) (remove (fn [t] (or (ldb/inline-tag? (:block/raw-title block) t) - (:logseq.property.class/hide-from-node t))))) + (:logseq.property.class/hide-from-node t) + (= :logseq.class/Page (:db/ident t)))))) popup-opts {:align :end :content-props {:on-click (fn [] (shui/popup-hide!)) :class "w-60"}} diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 7bbbd6c2b66..04dba8fa65c 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -969,7 +969,9 @@ (let [type (get schema :type :default) date? (= type :date) *el (rum/use-ref nil) - items (if (de/entity? v) #{v} v)] + items (cond->> (if (de/entity? v) #{v} v) + (= (:db/ident property) :block/tags) + (remove (fn [v] (contains? ldb/internal-tags (:db/ident v)))))] (rum/use-effect! (fn [] (when editing? diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index 50fb3b1bd83..ddf8bd1438b 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -14,6 +14,7 @@ [frontend.util :as util :refer [react]] [logseq.db.frontend.rules :as rules] [logseq.db.frontend.content :as db-content] + [logseq.db.frontend.entity-util :as entity-util] [logseq.graph-parser.db :as gp-db] [logseq.common.util :as common-util] [logseq.common.util.date-time :as date-time-util] @@ -803,7 +804,9 @@ independent of format as format specific heading characters are stripped" (let [db (conn/get-db repo) classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag) (map (fn [d] - (db-utils/entity db (:e d)))))] + (db-utils/entity db (:e d)))) + (remove (fn [d] + (contains? entity-util/internal-tags (:db/ident d)))))] (if except-root-class? (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes) classes))) From 77c514e3debf3ed197b0a680316a3451d33a174a Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 16:06:05 +0800 Subject: [PATCH 010/105] Disable users to add or delete internal tags --- .../src/logseq/db/frontend/entity_util.cljs | 2 +- src/main/frontend/components/block.cljs | 32 ++++++++++--------- .../frontend/components/property/value.cljs | 8 +++-- src/main/frontend/components/select.cljs | 3 +- src/main/frontend/ui.cljs | 7 ++-- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index fb99671ebad..9b212d01bf4 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -82,4 +82,4 @@ (def internal-tags #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag - :logseq.class/Closed-Value}) + :logseq.class/Closed-Value :logseq.class/Asset}) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 579856a0753..66b38947771 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2618,14 +2618,15 @@ :tag? true :disable-preview? true) tag) - [:a.close.flex.transition-opacity.duration-300.ease-in - {:class (if @*hover? "!opacity-100" "!opacity-0") - :title "Remove this tag" - :on-pointer-down - (fn [e] - (util/stop e) - (db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag)))} - (ui/icon "x" {:size 15})]])) + (when-not (ldb/internal-tags (:db/ident tag)) + [:a.close.flex.transition-opacity.duration-300.ease-in + {:class (if @*hover? "!opacity-100" "!opacity-0") + :title "Remove this tag" + :on-pointer-down + (fn [e] + (util/stop e) + (db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag)))} + (ui/icon "x" {:size 15})])])) (rum/defc tags-cp "Tags without inline or hidden tags" @@ -2654,13 +2655,14 @@ (fn [] (for [tag block-tags] [:div.flex.flex-row.items-center.gap-1 - (shui/button - {:title "Remove tag" - :variant :ghost - :class "!p-1 text-muted-foreground" - :size :sm - :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))} - (ui/icon "X" {:size 14})) + (when-not (ldb/internal-tags (:db/ident tag)) + (shui/button + {:title "Remove tag" + :variant :ghost + :class "!p-1 text-muted-foreground" + :size :sm + :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))} + (ui/icon "X" {:size 14}))) (page-cp (assoc config :tag? true :disable-preview? true diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 04dba8fa65c..c5577301112 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -34,7 +34,8 @@ [logseq.db.frontend.property.type :as db-property-type] [logseq.shui.ui :as shui] [promesa.core :as p] - [rum.core :as rum])) + [rum.core :as rum] + [clojure.set :as set])) (rum/defc property-empty-btn-value [property & opts] @@ -532,7 +533,10 @@ :header header :label-value (:block/title node) :label label - :value id))) nodes) + :value id + :disabled? (and tags? (contains? + (set/union #{:logseq.class/Journal :logseq.class/Whiteboard} ldb/internal-tags) + (:db/ident node)))))) nodes) classes' (remove (fn [class] (= :logseq.class/Root (:db/ident class))) classes) opts' (cond-> (merge diff --git a/src/main/frontend/components/select.cljs b/src/main/frontend/components/select.cljs index 296abc11d7c..ea95770ccb9 100644 --- a/src/main/frontend/components/select.cljs +++ b/src/main/frontend/components/select.cljs @@ -29,7 +29,8 @@ (when multiple-choices? (ui/checkbox {:checked (boolean (selected-choices (:value result))) :on-click (fn [e] - (.preventDefault e))})) + (.preventDefault e)) + :disabled (:disabled? result)})) value] (when (and (map? result) (:id result)) [:div.tip.flex diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index eb9321cb9a5..1083ed525e2 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -534,9 +534,10 @@ ;:on-pointer-down #(util/stop %) :on-click (fn [e] (util/stop e) - (if (and (gobj/get e "shiftKey") on-shift-chosen) - (on-shift-chosen item) - (on-chosen item e)))} + (when-not (:disabled? item) + (if (and (gobj/get e "shiftKey") on-shift-chosen) + (on-shift-chosen item) + (on-chosen item e))))} (if item-render (item-render item chosen?) item)))]] (let [group-name (and (fn? get-group-name) (get-group-name item))] From a2bba9e27a77e00f7b3b0596ce18abbc768e2ddc Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 16:49:12 +0800 Subject: [PATCH 011/105] fix: entity type --- deps/db/src/logseq/db/frontend/entity_util.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 9b212d01bf4..92be33d52ab 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -78,7 +78,7 @@ :logseq.class/Journal :journal :logseq.class/Whiteboard :whiteboard :logseq.class/Page :page}] - (set (map ident->type (:block/tags entity))))) + (set (map #(ident->type (:db/ident %)) (:block/tags entity))))) (def internal-tags #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag From e6a495bc56702c668c8032b090645b1b16901d15 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 16:59:49 +0800 Subject: [PATCH 012/105] fix: closed value select ui --- src/main/frontend/components/property/value.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index c5577301112..47719bd70e0 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -822,6 +822,9 @@ (= value :logseq.property/empty-placeholder) (property-empty-btn-value property) + closed-values? + (closed-value-item value opts) + (or (ldb/page? value) (and (seq (:block/tags value)) ;; FIXME: page-cp should be renamed to node-cp and @@ -839,9 +842,6 @@ (when-let [reference (state/get-component :block/reference)] (reference {} (:block/uuid value))) - closed-values? - (closed-value-item value opts) - (de/entity? value) (when-some [content (str (db-property/property-value-content value))] (inline-text-cp content)) From 93ae0047bef5ad6c51e8049d73cfc2ff5a80bc9d Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 18:48:18 +0800 Subject: [PATCH 013/105] feat: allow tags to be used as properties --- deps/db/src/logseq/db/frontend/property.cljs | 6 ++ src/main/frontend/components/property.cljs | 68 +++++++++++++----- src/main/frontend/components/select.cljs | 5 +- src/main/frontend/ui.cljs | 75 +++++++++++--------- src/main/frontend/worker/export.cljs | 3 +- 5 files changed, 102 insertions(+), 55 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index ba29612d1df..2428bcfa77e 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -474,6 +474,11 @@ [s] (string/includes? s ".property")) +(defn user-class-namespace? + "Determines if namespace string is a user class" + [s] + (string/includes? s ".class")) + (defn property? "Determines if ident kw is a property visible to user" [k] @@ -481,6 +486,7 @@ (and k-name (or (contains? logseq-property-namespaces k-name) (user-property-namespace? k-name) + (user-class-namespace? k-name) ;; disallow private db-attribute-properties as they cause unwanted refs ;; and appear noisily in debugging contexts (and (keyword? k) (contains? public-db-attribute-properties k)))))) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index 65278d762da..add8e09469f 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -13,6 +13,7 @@ [frontend.handler.property.util :as pu] [frontend.config :as config] [frontend.db :as db] + [frontend.db.model :as db-model] [frontend.db-mixins :as db-mixins] [frontend.db.async :as db-async] [frontend.handler.db-based.property :as db-property-handler] @@ -99,7 +100,8 @@ add-class-property? (and (ldb/class? block) class-schema?)] (when *property (reset! *property property)) (p/do! - (when *show-new-property-config? (reset! *show-new-property-config? false)) + (when *show-new-property-config? + (reset! *show-new-property-config? false)) (when (= (:type schema) :node) (reset! *show-class-select? true)) (components-pu/update-property! property property-name schema) (cond @@ -142,30 +144,42 @@ (rum/defc property-select [exclude-properties select-opts] (let [[properties set-properties!] (rum/use-state nil) + [classes set-classes!] (rum/use-state nil) [excluded-properties set-excluded-properties!] (rum/use-state nil)] (rum/use-effect! (fn [] - (p/let [properties (db-async/> (db-model/get-all-classes repo) + (remove ldb/built-in?))] + (set-classes! classes) (set-properties! (remove exclude-properties properties)) (set-excluded-properties! (->> properties (filter exclude-properties) (map :block/title) set)))) []) - [:div.ls-property-add.flex.flex-row.items-center.property-key - [:div.ls-property-key - (select/select (merge - {:items (map (fn [x] - {:label (:block/title x) - :value (:block/uuid x)}) properties) - :extract-fn :label - :dropdown? false - :close-modal? false - :new-case-sensitive? true - :show-new-when-not-exact-match? true - :exact-match-exclude-items (fn [s] (contains? excluded-properties s)) - :input-default-placeholder "Add or change property"} - select-opts))]])) + (let [items (concat + (map (fn [x] + {:label (:block/title x) + :value (:block/uuid x)}) properties) + (map (fn [x] + {:label (:block/title x) + :value (:block/uuid x) + :group "Tags"}) classes))] + [:div.ls-property-add.flex.flex-row.items-center.property-key + [:div.ls-property-key + (select/select (merge + {:items items + :grouped? true + :extract-fn :label + :dropdown? false + :close-modal? false + :new-case-sensitive? true + :show-new-when-not-exact-match? true + :exact-match-exclude-items (fn [s] (contains? excluded-properties s)) + :input-default-placeholder "Add or change property"} + select-opts))]]))) (rum/defc property-icon [property property-type] @@ -194,7 +208,7 @@ (fn [{:keys [value label]}] (reset! *property-key (if (uuid? value) label value)) (let [property (when (uuid? value) (db/entity [:block/uuid value]))] - (when (and *show-new-property-config? (not property)) + (when (and *show-new-property-config? (not (ldb/property? property))) (reset! *show-new-property-config? true)) (reset! *property property) (when property @@ -221,10 +235,26 @@ (not (seq (:property/closed-values property)))) (pv/ Date: Thu, 5 Dec 2024 21:31:30 +0800 Subject: [PATCH 014/105] Add shadcn tabs --- deps/shui/src/logseq/shui/demo.cljs | 887 ++++++++++++++------------- deps/shui/src/logseq/shui/ui.cljs | 6 + packages/ui/@/components/ui/tabs.tsx | 53 ++ packages/ui/package.json | 1 + packages/ui/src/ui.ts | 8 +- packages/ui/yarn.lock | 144 ++++- 6 files changed, 653 insertions(+), 446 deletions(-) create mode 100644 packages/ui/@/components/ui/tabs.tsx diff --git a/deps/shui/src/logseq/shui/demo.cljs b/deps/shui/src/logseq/shui/demo.cljs index 3663fdcab7e..861f734699c 100644 --- a/deps/shui/src/logseq/shui/demo.cljs +++ b/deps/shui/src/logseq/shui/demo.cljs @@ -16,475 +16,488 @@ [] (let [icon #(ui/tabler-icon (name %1) {:class "scale-90 pr-1 opacity-80"})] (ui/dropdown-menu-content - {:class "w-56" - :on-click (fn [^js e] (some-> (.-target e) (.-innerText) - (#(identity ["You select: " [:b.text-red-700 %1]])) (ui/toast! :info)))} - (ui/dropdown-menu-label "My Account") - (ui/dropdown-menu-separator) - (ui/dropdown-menu-group + {:class "w-56" + :on-click (fn [^js e] (some-> (.-target e) (.-innerText) + (#(identity ["You select: " [:b.text-red-700 %1]])) (ui/toast! :info)))} + (ui/dropdown-menu-label "My Account") + (ui/dropdown-menu-separator) + (ui/dropdown-menu-group ;; items - (ui/dropdown-menu-item (icon :user) "Profile" (ui/dropdown-menu-shortcut "⌘P")) - (ui/dropdown-menu-item (icon :brand-mastercard) [:span "Billing"] (ui/dropdown-menu-shortcut "⌘B")) - (ui/dropdown-menu-item (icon :adjustments-alt) [:span "Settings"] (ui/dropdown-menu-shortcut "⌘,")) - (ui/dropdown-menu-item (icon :keyboard) [:span "Keyboard shortcuts"])) - (ui/dropdown-menu-separator) + (ui/dropdown-menu-item (icon :user) "Profile" (ui/dropdown-menu-shortcut "⌘P")) + (ui/dropdown-menu-item (icon :brand-mastercard) [:span "Billing"] (ui/dropdown-menu-shortcut "⌘B")) + (ui/dropdown-menu-item (icon :adjustments-alt) [:span "Settings"] (ui/dropdown-menu-shortcut "⌘,")) + (ui/dropdown-menu-item (icon :keyboard) [:span "Keyboard shortcuts"])) + (ui/dropdown-menu-separator) ;; group - (ui/dropdown-menu-group + (ui/dropdown-menu-group ;; items - (ui/dropdown-menu-item (icon :users) "Team") + (ui/dropdown-menu-item (icon :users) "Team") ;; sub menu - (ui/dropdown-menu-sub - (ui/dropdown-menu-sub-trigger - (icon :user-plus) [:span "Invite users"]) - (ui/dropdown-menu-sub-content - (ui/dropdown-menu-item (icon :mail) "Email") - (ui/dropdown-menu-item (icon :message) "Message") - (ui/dropdown-menu-item (icon :dots-circle-horizontal) "More..."))) + (ui/dropdown-menu-sub + (ui/dropdown-menu-sub-trigger + (icon :user-plus) [:span "Invite users"]) + (ui/dropdown-menu-sub-content + (ui/dropdown-menu-item (icon :mail) "Email") + (ui/dropdown-menu-item (icon :message) "Message") + (ui/dropdown-menu-item (icon :dots-circle-horizontal) "More..."))) ;; menu item - (ui/dropdown-menu-item (icon :plus) "New Team" (ui/dropdown-menu-shortcut "⌘+T"))) - (ui/dropdown-menu-separator) - (ui/dropdown-menu-item (icon :brand-github) "GitHub") - (ui/dropdown-menu-item {:disabled true} (icon :cloud) "Cloud API") - (ui/dropdown-menu-separator) - (ui/dropdown-menu-item (icon :logout) "Logout" (ui/dropdown-menu-shortcut "⌘+Q")) - ))) + (ui/dropdown-menu-item (icon :plus) "New Team" (ui/dropdown-menu-shortcut "⌘+T"))) + (ui/dropdown-menu-separator) + (ui/dropdown-menu-item (icon :brand-github) "GitHub") + (ui/dropdown-menu-item {:disabled true} (icon :cloud) "Cloud API") + (ui/dropdown-menu-separator) + (ui/dropdown-menu-item (icon :logout) "Logout" (ui/dropdown-menu-shortcut "⌘+Q"))))) (rum/defc sample-context-menu-content [] (let [icon #(ui/tabler-icon (name %1) {:class "scale-90 pr-1 opacity-80"})] (ui/context-menu ;; trigger - (ui/context-menu-trigger - [:div.border.px-6.py-12.border-dashed.rounded.text-center.select-none - {:key "ctx-menu-click"} - [:span.opacity-50 "Right click here"]]) + (ui/context-menu-trigger + [:div.border.px-6.py-12.border-dashed.rounded.text-center.select-none + {:key "ctx-menu-click"} + [:span.opacity-50 "Right click here"]]) ;; content - (ui/context-menu-content - {:class "w-60 max-h-[80vh] overflow-auto"} - (ui/context-menu-item - (icon "arrow-left") - "Back" - (ui/context-menu-shortcut "⌘[")) - (ui/context-menu-item {:disabled true} - (icon "arrow-right") - "Forward" - (ui/context-menu-shortcut "⌘]")) - (ui/context-menu-item - (icon "refresh") - "Reload" - (ui/context-menu-shortcut "⌘R")) + (ui/context-menu-content + {:class "w-60 max-h-[80vh] overflow-auto"} + (ui/context-menu-item + (icon "arrow-left") + "Back" + (ui/context-menu-shortcut "⌘[")) + (ui/context-menu-item {:disabled true} + (icon "arrow-right") + "Forward" + (ui/context-menu-shortcut "⌘]")) + (ui/context-menu-item + (icon "refresh") + "Reload" + (ui/context-menu-shortcut "⌘R")) ;; Sub menu - (ui/context-menu-sub - (ui/context-menu-sub-trigger {:inset true} "More tools") - (ui/context-menu-sub-content {:class "w-48"} - (ui/context-menu-item "Save page As..." - (ui/context-menu-shortcut "⇧⌘S")) - (ui/context-menu-item "Create Shortcut...") - (ui/context-menu-item "Name Window...") - (ui/context-menu-separator) - (ui/context-menu-item "Developer Tools"))) + (ui/context-menu-sub + (ui/context-menu-sub-trigger {:inset true} "More tools") + (ui/context-menu-sub-content {:class "w-48"} + (ui/context-menu-item "Save page As..." + (ui/context-menu-shortcut "⇧⌘S")) + (ui/context-menu-item "Create Shortcut...") + (ui/context-menu-item "Name Window...") + (ui/context-menu-separator) + (ui/context-menu-item "Developer Tools"))) ;; more - (ui/context-menu-separator) - (ui/context-menu-checkbox-item {:checked true} - "Show Bookmarks Bar" (ui/context-menu-shortcut "⌘⇧B")) - (ui/context-menu-checkbox-item "Show Full URLs") - (ui/context-menu-separator) - (ui/context-menu-radio-group {:value "pedro"} - (ui/context-menu-label {:inset true} "People") - (ui/context-menu-separator) - (ui/context-menu-radio-item {:value "pedro"} "Pedro Duarte") - (ui/context-menu-radio-item {:value "colm"} "Colm Tuite")))))) + (ui/context-menu-separator) + (ui/context-menu-checkbox-item {:checked true} + "Show Bookmarks Bar" (ui/context-menu-shortcut "⌘⇧B")) + (ui/context-menu-checkbox-item "Show Full URLs") + (ui/context-menu-separator) + (ui/context-menu-radio-group {:value "pedro"} + (ui/context-menu-label {:inset true} "People") + (ui/context-menu-separator) + (ui/context-menu-radio-item {:value "pedro"} "Pedro Duarte") + (ui/context-menu-radio-item {:value "colm"} "Colm Tuite")))))) + +(rum/defc sample-tabs + [] + (ui/tabs + {:defaultValue "account" + :className "w-[400px]"} + (ui/tabs-list + (ui/tabs-trigger + {:value "account"} + "Account") + (ui/tabs-trigger + {:value "password"} + "Password")) + (ui/tabs-content + {:value "account"} + "Make changes to your account here.") + (ui/tabs-content + {:value "password"} + "Change your password here."))) (rum/defc sample-form-basic [] [:div.border.p-6.rounded.bg-gray-01 (let [form-ctx (form-core/use-form - {:defaultValues {:username "" - :agreement true - :notification "all" - :bio ""} - :yupSchema (-> (.object yup) - (.shape #js {:username (-> (.string yup) (.required))}) - (.required))}) + {:defaultValues {:username "" + :agreement true + :notification "all" + :bio ""} + :yupSchema (-> (.object yup) + (.shape #js {:username (-> (.string yup) (.required))}) + (.required))}) handle-submit (:handleSubmit form-ctx) on-submit-valid (handle-submit - (fn [^js e] - (js/console.log "[form] submit: " e) - (js/alert (js/JSON.stringify e nil 2))))] + (fn [^js e] + (js/console.log "[form] submit: " e) + (js/alert (js/JSON.stringify e nil 2))))] (ui/form-provider form-ctx - [:form - {:on-submit on-submit-valid} + [:form + {:on-submit on-submit-valid} ;; field item - (ui/form-field {:name "username"} - (fn [field error] - (ui/form-item - (ui/form-label "Username") - (ui/form-control - (ui/input (merge {:placeholder "Username"} field))) - (ui/form-description - (if error - [:b.text-red-800 (:message error)] - "This is your public display name."))))) - - (ui/form-field {:name "bio"} - (fn [field error] - (ui/form-item - {:class "pt-4"} - (ui/form-control - (ui/textarea (merge {:placeholder "Bio text..."} field)))))) + (ui/form-field {:name "username"} + (fn [field error] + (ui/form-item + (ui/form-label "Username") + (ui/form-control + (ui/input (merge {:placeholder "Username"} field))) + (ui/form-description + (if error + [:b.text-red-800 (:message error)] + "This is your public display name."))))) + + (ui/form-field {:name "bio"} + (fn [field error] + (ui/form-item + {:class "pt-4"} + (ui/form-control + (ui/textarea (merge {:placeholder "Bio text..."} field)))))) ;; radio - (ui/form-field {:name "notification"} + (ui/form-field {:name "notification"} ;; item render - (fn [field] - (ui/form-item - {:class "space-y-3 my-4"} - (ui/form-label "Notify me about...") - (ui/form-control - (ui/radio-group - {:value (:value field) - :on-value-change (:onChange field) - :class "flex flex-col space-y-3"} - (ui/form-item - {:class "flex flex-row space-x-3 items-center space-y-0"} - (ui/form-control - (ui/radio-group-item {:value "all"})) - (ui/form-label "All")) - - (ui/form-item - {:class "flex flex-row space-x-3 items-center space-y-0"} - (ui/form-control - (ui/radio-group-item {:value "direct"})) - (ui/form-label "Direct messages and mentions"))))))) - - [:hr] + (fn [field] + (ui/form-item + {:class "space-y-3 my-4"} + (ui/form-label "Notify me about...") + (ui/form-control + (ui/radio-group + {:value (:value field) + :on-value-change (:onChange field) + :class "flex flex-col space-y-3"} + (ui/form-item + {:class "flex flex-row space-x-3 items-center space-y-0"} + (ui/form-control + (ui/radio-group-item {:value "all"})) + (ui/form-label "All")) + + (ui/form-item + {:class "flex flex-row space-x-3 items-center space-y-0"} + (ui/form-control + (ui/radio-group-item {:value "direct"})) + (ui/form-label "Direct messages and mentions"))))))) + + [:hr] ;; checkbox - (ui/form-field {:name "agreement"} - (fn [field] - (ui/form-item - {:class "flex justify-start items-center space-x-3 space-y-0 my-3 pr-3"} - (ui/form-control - (ui/checkbox {:checked (:value field) - :on-checked-change (:onChange field)})) - (ui/form-label {:class "font-normal cursor-pointer"} "Agreement terms")))) + (ui/form-field {:name "agreement"} + (fn [field] + (ui/form-item + {:class "flex justify-start items-center space-x-3 space-y-0 my-3 pr-3"} + (ui/form-control + (ui/checkbox {:checked (:value field) + :on-checked-change (:onChange field)})) + (ui/form-label {:class "font-normal cursor-pointer"} "Agreement terms")))) ;; actions - [:div.relative.px-2 - (ui/button {:type "submit" :class "!absolute right-0 top-[-40px]"} "Submit")]]))]) + [:div.relative.px-2 + (ui/button {:type "submit" :class "!absolute right-0 top-[-40px]"} "Submit")]]))]) (rum/defc sample-date-picker [] (let [[open? set-open!] (rum/use-state false) [date set-date!] (rum/use-state (js/Date.))] (ui/popover - {:open open? - :on-open-change (fn [o] (set-open! o))} + {:open open? + :on-open-change (fn [o] (set-open! o))} ;; trigger - (ui/popover-trigger - {:as-child true - :class "w-2/3"} - (ui/input - {:type :text - :placeholder "pick a date" - :default-value (.toDateString date)})) + (ui/popover-trigger + {:as-child true + :class "w-2/3"} + (ui/input + {:type :text + :placeholder "pick a date" + :default-value (.toDateString date)})) ;; content - (ui/popover-content - {:on-open-auto-focus #(.preventDefault %) - :side-offset 8 - :class "p-0"} - (ui/calendar - {:selected date - :on-day-click - (fn [^js d] - (set-date! d) - (set-open! false))}))))) + (ui/popover-content + {:on-open-auto-focus #(.preventDefault %) + :side-offset 8 + :class "p-0"} + (ui/calendar + {:selected date + :on-day-click + (fn [^js d] + (set-date! d) + (set-open! false))}))))) (rum/defc sample-dialog-basic [] (let [[open? set-open!] (rum/use-state false)] (ui/dialog - {:open open? - :on-open-change #(set-open! %)} - (ui/dialog-trigger - {:as-child true} - (ui/button {:variant :outline} - (ui/tabler-icon "notification") "Open as modal locally")) - (ui/dialog-content - (ui/dialog-header - (ui/dialog-title "Header") - (ui/dialog-description - "Description")) - [:div.max-h-96.overflow-y-auto - {:class "-mx-6"} - [:section.px-6 - (repeat 8 [:p "Your custom content"])]] - (ui/dialog-footer - (ui/button - {:on-click #(set-open! false) - :size :md} "🍄 * Footer")))))) - + {:open open? + :on-open-change #(set-open! %)} + (ui/dialog-trigger + {:as-child true} + (ui/button {:variant :outline} + (ui/tabler-icon "notification") "Open as modal locally")) + (ui/dialog-content + (ui/dialog-header + (ui/dialog-title "Header") + (ui/dialog-description + "Description")) + [:div.max-h-96.overflow-y-auto + {:class "-mx-6"} + [:section.px-6 + (repeat 8 [:p "Your custom content"])]] + (ui/dialog-footer + (ui/button + {:on-click #(set-open! false) + :size :md} "🍄 * Footer")))))) (rum/defc page [] (ui/tooltip-provider - [:div.sm:p-10 - [:hr] - [:input - {:type "checkbox" :on-change #(js/console.log "===>> onChange:" % (.-value (.-target %)))}] - (ui/checkbox {:on-click - (fn [^js e] (js/console.log "==>> click:" - (set! (. (.-target e) -checked) (.-state (.-dataset (.-target e)))) - (.-checked (.-target e)) - )) - :on-checked-change #(js/console.log "==>> on checked change:" %) - } "abc") - - [:h1.text-3xl.font-bold "Logseq UI"] - [:hr] + [:div.sm:p-10 + [:hr] + [:input + {:type "checkbox" :on-change #(js/console.log "===>> onChange:" % (.-value (.-target %)))}] + (ui/checkbox {:on-click + (fn [^js e] (js/console.log "==>> click:" + (set! (. (.-target e) -checked) (.-state (.-dataset (.-target e)))) + (.-checked (.-target e)))) + :on-checked-change #(js/console.log "==>> on checked change:" %)} "abc") + + [:h1.text-3xl.font-bold "Logseq UI"] + [:hr] ;; Button - (section-item "Button" - [:div.flex.flex-row.flex-wrap.gap-2 - (let [[loading? set-loading!] (rum/use-state false)] - (ui/button - {:size :sm - :on-click (fn [] - (set-loading! true) - (js/setTimeout #(set-loading! false) 5000)) - :disabled loading?} - (when loading? - (ui/tabler-icon "loader2" {:class "animate-spin"})) - "Logseq Classic Button" - (ui/tabler-icon "arrow-right"))) - - (ui/button {:variant :outline :size :sm} "Outline") - (ui/button {:variant :secondary :size :sm} "Secondary") - (ui/button {:disabled true :size :sm} "Disabled") - (ui/button {:variant :destructive :size :sm} "Destructive") - (ui/button {:class "primary-green" :size :sm} "Custom (.primary-green)") - (ui/button {:variant :ghost :size :sm} "Ghost") - (ui/button {:variant :link :size :sm} "Link") - (ui/button - {:variant :icon - :size :sm} - [:a.flex.items-center.text-blue-rx-10.hover:text-blue-rx-10-alpha - {:href "https://x.com/logseq" :target "_blank"} - (ui/tabler-icon "brand-twitter" {:size 15})] - )]) - - ;; Toast - (section-item "Toast" - [:div.flex.flex-row.flex-wrap.gap-2 - (ui/button - {:size :md - :variant :outline - :on-click #(ui/toast! - "Check for updates ..." - (nth [:success :error :default :info :warning] (rand-int 3)) - {:title (if (odd? (js/Date.now)) "History of China" "") - :duration 3000})} - "Open random toast" - (ui/tabler-icon "arrow-right")) - - (ui/button - {:variant :secondary - :size :md - :on-click (fn [] - (ui/toast! - (fn [{:keys [id dismiss! update!]}] - [:b.text-red-700 - [:div.flex.items-center.gap-2 - (ui/tabler-icon "info-circle") - (str "#(" id ") ") - (.toLocaleString (js/Date.))] - [:div.flex.flex-row.gap-2 - (ui/button - {:on-click #(dismiss! id) :size :sm} - "x close") - - (ui/button - {:on-click #(update! {:title (js/Date.now) - :action [:b (ui/button {:on-click (fn [] (ui/toast-dismiss!))} "clear all")]}) - :size :sm} - "x update")]]) - :default - {:duration 3000 :onDismiss #(js/console.log "===>> dismiss?:" %1)}))} - (ui/tabler-icon "apps") - "Toast callback handle") - - (ui/button - {:on-click #(ui/toast! "A message from SoundCloud..." - {:class "text-orange-rx-10" - :icon [:b.pl-1 (ui/tabler-icon "brand-soundcloud" {:size 20})] - :duration 3000}) - :class "primary-orange" - :size :md} - "Custom icon")]) - - [:div.flex.flex-row.space-x-16.items-center + (section-item "Button" + [:div.flex.flex-row.flex-wrap.gap-2 + (let [[loading? set-loading!] (rum/use-state false)] + (ui/button + {:size :sm + :on-click (fn [] + (set-loading! true) + (js/setTimeout #(set-loading! false) 5000)) + :disabled loading?} + (when loading? + (ui/tabler-icon "loader2" {:class "animate-spin"})) + "Logseq Classic Button" + (ui/tabler-icon "arrow-right"))) + + (ui/button {:variant :outline :size :sm} "Outline") + (ui/button {:variant :secondary :size :sm} "Secondary") + (ui/button {:disabled true :size :sm} "Disabled") + (ui/button {:variant :destructive :size :sm} "Destructive") + (ui/button {:class "primary-green" :size :sm} "Custom (.primary-green)") + (ui/button {:variant :ghost :size :sm} "Ghost") + (ui/button {:variant :link :size :sm} "Link") + (ui/button + {:variant :icon + :size :sm} + [:a.flex.items-center.text-blue-rx-10.hover:text-blue-rx-10-alpha + {:href "https://x.com/logseq" :target "_blank"} + (ui/tabler-icon "brand-twitter" {:size 15})])]) + +;; Toast + (section-item "Toast" + [:div.flex.flex-row.flex-wrap.gap-2 + (ui/button + {:size :md + :variant :outline + :on-click #(ui/toast! + "Check for updates ..." + (nth [:success :error :default :info :warning] (rand-int 3)) + {:title (if (odd? (js/Date.now)) "History of China" "") + :duration 3000})} + "Open random toast" + (ui/tabler-icon "arrow-right")) + + (ui/button + {:variant :secondary + :size :md + :on-click (fn [] + (ui/toast! + (fn [{:keys [id dismiss! update!]}] + [:b.text-red-700 + [:div.flex.items-center.gap-2 + (ui/tabler-icon "info-circle") + (str "#(" id ") ") + (.toLocaleString (js/Date.))] + [:div.flex.flex-row.gap-2 + (ui/button + {:on-click #(dismiss! id) :size :sm} + "x close") + + (ui/button + {:on-click #(update! {:title (js/Date.now) + :action [:b (ui/button {:on-click (fn [] (ui/toast-dismiss!))} "clear all")]}) + :size :sm} + "x update")]]) + :default + {:duration 3000 :onDismiss #(js/console.log "===>> dismiss?:" %1)}))} + (ui/tabler-icon "apps") + "Toast callback handle") + + (ui/button + {:on-click #(ui/toast! "A message from SoundCloud..." + {:class "text-orange-rx-10" + :icon [:b.pl-1 (ui/tabler-icon "brand-soundcloud" {:size 20})] + :duration 3000}) + :class "primary-orange" + :size :md} + "Custom icon")]) + + [:div.flex.flex-row.space-x-16.items-center ;; Tips - (section-item "Tips" - [:div.flex.flex-row.flex-wrap.gap-2 - (ui/tooltip-provider - (ui/tooltip - (ui/tooltip-trigger - (ui/button - {:variant :outline - :on-click #(dialog-core/open! [:h1.text-9xl.text-center.scale-110 "🍄"])} - "Tip for hint?")) - (ui/tooltip-content - {:class "w-42 px-8 py-4 text-xl border-green-rx-08 bg-green-rx-07-alpha"} - "🍄")))]) + (section-item "Tips" + [:div.flex.flex-row.flex-wrap.gap-2 + (ui/tooltip-provider + (ui/tooltip + (ui/tooltip-trigger + (ui/button + {:variant :outline + :on-click #(dialog-core/open! [:h1.text-9xl.text-center.scale-110 "🍄"])} + "Tip for hint?")) + (ui/tooltip-content + {:class "w-42 px-8 py-4 text-xl border-green-rx-08 bg-green-rx-07-alpha"} + "🍄")))]) ;; Avatar - (section-item "Avatar" - [:div.flex.flex-row.space-x-6.items-center - (ui/avatar - (ui/avatar-image {:src "https://avatars.githubusercontent.com/u/63385289?s=200&v=4"}) - (ui/avatar-fallback "L")) - (ui/avatar - (ui/avatar-fallback "CH"))])] + (section-item "Avatar" + [:div.flex.flex-row.space-x-6.items-center + (ui/avatar + (ui/avatar-image {:src "https://avatars.githubusercontent.com/u/63385289?s=200&v=4"}) + (ui/avatar-fallback "L")) + (ui/avatar + (ui/avatar-fallback "CH"))])] ;; Badge - (section-item "Badge" - [:div.flex.flex-row.flex-wrap.gap-2 - (ui/badge "Default") - (ui/badge {:variant :outline} "Outline") - (ui/badge {:variant :secondary} "Secondary") - (ui/badge {:variant :destructive} "Destructive") - (ui/badge {:class "primary-yellow"} "Custom (.primary-yellow)")]) - - [:div.grid.sm:grid-cols-3.sm:gap-8 + (section-item "Badge" + [:div.flex.flex-row.flex-wrap.gap-2 + (ui/badge "Default") + (ui/badge {:variant :outline} "Outline") + (ui/badge {:variant :secondary} "Secondary") + (ui/badge {:variant :destructive} "Destructive") + (ui/badge {:class "primary-yellow"} "Custom (.primary-yellow)")]) + + [:div.grid.sm:grid-cols-3.sm:gap-8 ;; Dropdown - (section-item "Dropdown" - (ui/dropdown-menu - (ui/tooltip - (ui/tooltip-trigger - (ui/dropdown-menu-trigger - {:as-child true} - (ui/button {:variant :outline} - (ui/tabler-icon "list") "Open dropdown menu"))) - (ui/tooltip-content "test hide?")) - - (sample-dropdown-menu-content))) + (section-item "Dropdown" + (ui/dropdown-menu + (ui/tooltip + (ui/tooltip-trigger + (ui/dropdown-menu-trigger + {:as-child true} + (ui/button {:variant :outline} + (ui/tabler-icon "list") "Open dropdown menu"))) + (ui/tooltip-content "test hide?")) + + (sample-dropdown-menu-content))) ;; Context menu - [:div.col-span-2 - (section-item "Context Menu" - (sample-context-menu-content))]] + [:div.col-span-2 + (section-item "Context Menu" + (sample-context-menu-content))]] + + (section-item "Tabs" (sample-tabs)) ;; Dialog - (section-item "Dialog" - [:div.flex.flex-row.flex-wrap.gap-2 - (sample-dialog-basic) - (ui/button - {:on-click #(dialog-core/open! "a modal dialog from `open!`" {:title "Title"})} - "Imperative API: open!") - - (ui/button - {:class "primary-yellow" - :on-click (fn [] - (-> (dialog-core/alert! - "a alert dialog from `alert!`" - {:title [:div.flex.flex-row.space-x-2.items-center - (ui/tabler-icon "alert-triangle" {:size 18}) - [:span "Alert"]]}) - (p/then #(js/console.log "=> alert (promise): " %))))} - "Imperative API: alert!") - - (ui/button - {:class "primary-green" - :on-click (fn [] - (-> (dialog-core/confirm! - "a alert dialog from `confirm!`" - {:title [:div.flex.flex-row.space-x-2.items-center - (ui/tabler-icon "alert-triangle" {:size 18}) - [:span "Confirm"]]}) - (p/then #(js/console.log "=> confirm (promise): " %)) - (p/catch #(js/console.log "=> confirm (promise): " %))))} - "Imperative API: confirm!")]) + (section-item "Dialog" + [:div.flex.flex-row.flex-wrap.gap-2 + (sample-dialog-basic) + (ui/button + {:on-click #(dialog-core/open! "a modal dialog from `open!`" {:title "Title"})} + "Imperative API: open!") + + (ui/button + {:class "primary-yellow" + :on-click (fn [] + (-> (dialog-core/alert! + "a alert dialog from `alert!`" + {:title [:div.flex.flex-row.space-x-2.items-center + (ui/tabler-icon "alert-triangle" {:size 18}) + [:span "Alert"]]}) + (p/then #(js/console.log "=> alert (promise): " %))))} + "Imperative API: alert!") + + (ui/button + {:class "primary-green" + :on-click (fn [] + (-> (dialog-core/confirm! + "a alert dialog from `confirm!`" + {:title [:div.flex.flex-row.space-x-2.items-center + (ui/tabler-icon "alert-triangle" {:size 18}) + [:span "Confirm"]]}) + (p/then #(js/console.log "=> confirm (promise): " %)) + (p/catch #(js/console.log "=> confirm (promise): " %))))} + "Imperative API: confirm!")]) ;; Alert - (section-item "Alert" - [:<> - (ui/alert - {:class "text-orange-rx-09 border-orange-rx-07-alpha mb-4"} - (ui/tabler-icon "brand-soundcloud") - (ui/alert-title "Title is SoundCloud") - (ui/alert-description - "content: radix colors for Logseq")) - (ui/alert - (ui/tabler-icon "brand-github") - (ui/alert-title "GitHub") - (ui/alert-description - "content: radix colors for Logseq"))]) + (section-item "Alert" + [:<> + (ui/alert + {:class "text-orange-rx-09 border-orange-rx-07-alpha mb-4"} + (ui/tabler-icon "brand-soundcloud") + (ui/alert-title "Title is SoundCloud") + (ui/alert-description + "content: radix colors for Logseq")) + (ui/alert + (ui/tabler-icon "brand-github") + (ui/alert-title "GitHub") + (ui/alert-description + "content: radix colors for Logseq"))]) ;; Slider - [:div.grid.sm:grid-cols-8.gap-4 - [:div.col-span-4.mr-6 - (section-item "Slider" (ui/slider))] - [:div.col-span-1 - (section-item "Switch" - (ui/switch {:size :sm :class "relative top-[-8px]"}))] - [:div.col-span-3.pl-4.pr-2 - (section-item "Select" - (ui/select - {:on-value-change (fn [v] (ui/toast! v :info))} + [:div.grid.sm:grid-cols-8.gap-4 + [:div.col-span-4.mr-6 + (section-item "Slider" (ui/slider))] + [:div.col-span-1 + (section-item "Switch" + (ui/switch {:size :sm :class "relative top-[-8px]"}))] + [:div.col-span-3.pl-4.pr-2 + (section-item "Select" + (ui/select + {:on-value-change (fn [v] (ui/toast! v :info))} ;; trigger - (ui/select-trigger - (ui/select-value {:placeholder "Select a fruit"})) + (ui/select-trigger + (ui/select-value {:placeholder "Select a fruit"})) ;; content - (ui/select-content - (ui/select-group - (ui/select-label "Fruits") - (ui/select-item {:value "apple"} "Apple") - (ui/select-item {:value "pear"} "Pear") - (ui/select-item {:value "grapes"} "Grapes") - - ))))]] - - ;; Form - (section-item "Form" - [:<> - (sample-form-basic)]) + (ui/select-content + (ui/select-group + (ui/select-label "Fruits") + (ui/select-item {:value "apple"} "Apple") + (ui/select-item {:value "pear"} "Pear") + (ui/select-item {:value "grapes"} "Grapes")))))]] + +;; Form + (section-item "Form" + [:<> + (sample-form-basic)]) ;; Card - [:div.grid.sm:grid-cols-2.sm:gap-8 - (section-item "Card" - (ui/card - (ui/card-header - (ui/card-title "Title") - (ui/card-description "Description")) - (ui/card-content "This is content") - (ui/card-footer "Footer"))) - - (section-item "Skeleton" - (ui/card - (ui/card-header - (ui/card-title - (ui/skeleton {:class "h-4 w-1/2"})) - (ui/card-description - (ui/skeleton {:class "h-2 w-full"}))) - (ui/card-content - (ui/skeleton {:class "h-3 mb-1"}) - (ui/skeleton {:class "h-3 mb-1"}) - (ui/skeleton {:class "h-3 w-2/3"})) - - (ui/card-footer - (ui/skeleton {:class "h-4 w-full mb-2"}))))] + [:div.grid.sm:grid-cols-2.sm:gap-8 + (section-item "Card" + (ui/card + (ui/card-header + (ui/card-title "Title") + (ui/card-description "Description")) + (ui/card-content "This is content") + (ui/card-footer "Footer"))) + + (section-item "Skeleton" + (ui/card + (ui/card-header + (ui/card-title + (ui/skeleton {:class "h-4 w-1/2"})) + (ui/card-description + (ui/skeleton {:class "h-2 w-full"}))) + (ui/card-content + (ui/skeleton {:class "h-3 mb-1"}) + (ui/skeleton {:class "h-3 mb-1"}) + (ui/skeleton {:class "h-3 w-2/3"})) + + (ui/card-footer + (ui/skeleton {:class "h-4 w-full mb-2"}))))] ;; Calendar - [:div.grid.sm:grid-cols-2.sm:gap-8 - (section-item "Calendar" - (ui/card - {:class "inline-flex"} - (ui/calendar {:on-day-click #(ui/toast! (.toString %) :success)}))) - (section-item "Date Picker" - (sample-date-picker))] - - [:hr.mb-80]])) + [:div.grid.sm:grid-cols-2.sm:gap-8 + (section-item "Calendar" + (ui/card + {:class "inline-flex"} + (ui/calendar {:on-day-click #(ui/toast! (.toString %) :success)}))) + (section-item "Date Picker" + (sample-date-picker))] + [:hr.mb-80]])) (defn- get-head-container [] @@ -499,44 +512,44 @@ (let [el-ref (rum/use-ref nil)] (rum/use-effect! - (fn [] - (let [^js container (get-main-scroll-container) - ^js el (rum/deref el-ref) - ^js cls (.-classList el) - *ticking? (volatile! false) - el-top (-> el (.getBoundingClientRect) (.-top)) - head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt)) - translate (fn [offset] - (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)")) - (if (zero? offset) - (.remove cls "translated") - (.add cls "translated"))) - *last-offset (volatile! 0) - handle (fn [] - (let [scroll-top (js/parseInt (.-scrollTop container)) - offset (if (> (+ scroll-top head-top) el-top) - (+ (- scroll-top el-top) head-top 1) 0) - offset (js/parseInt offset) - last-offset @*last-offset] - (if (and (not (zero? last-offset)) - (not= offset last-offset)) - (let [dir (if (neg? (- offset last-offset)) -1 1)] - (loop [offset' (+ last-offset dir)] - (translate offset') - (if (and (not= offset offset') - (< (abs (- offset offset')) 100)) - (recur (+ offset' dir)) - (translate offset)))) - (translate offset)) - (vreset! *last-offset offset))) - handler (fn [^js e] - (when (not @*ticking?) - (js/window.requestAnimationFrame - #(do (handle) (vreset! *ticking? false))) - (vreset! *ticking? true)))] - (.addEventListener container "scroll" handler) - #(.removeEventListener container "scroll" handler))) - []) + (fn [] + (let [^js container (get-main-scroll-container) + ^js el (rum/deref el-ref) + ^js cls (.-classList el) + *ticking? (volatile! false) + el-top (-> el (.getBoundingClientRect) (.-top)) + head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt)) + translate (fn [offset] + (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)")) + (if (zero? offset) + (.remove cls "translated") + (.add cls "translated"))) + *last-offset (volatile! 0) + handle (fn [] + (let [scroll-top (js/parseInt (.-scrollTop container)) + offset (if (> (+ scroll-top head-top) el-top) + (+ (- scroll-top el-top) head-top 1) 0) + offset (js/parseInt offset) + last-offset @*last-offset] + (if (and (not (zero? last-offset)) + (not= offset last-offset)) + (let [dir (if (neg? (- offset last-offset)) -1 1)] + (loop [offset' (+ last-offset dir)] + (translate offset') + (if (and (not= offset offset') + (< (abs (- offset offset')) 100)) + (recur (+ offset' dir)) + (translate offset)))) + (translate offset)) + (vreset! *last-offset offset))) + handler (fn [^js e] + (when (not @*ticking?) + (js/window.requestAnimationFrame + #(do (handle) (vreset! *ticking? false))) + (vreset! *ticking? true)))] + (.addEventListener container "scroll" handler) + #(.removeEventListener container "scroll" handler))) + []) [:div.charlie-table [:div.charlie-table-header diff --git a/deps/shui/src/logseq/shui/ui.cljs b/deps/shui/src/logseq/shui/ui.cljs index 3ef4c22e812..4a978eb2a0c 100644 --- a/deps/shui/src/logseq/shui/ui.cljs +++ b/deps/shui/src/logseq/shui/ui.cljs @@ -106,6 +106,12 @@ (def context-menu-sub-trigger (util/lsui-wrap "ContextMenuSubTrigger")) (def context-menu-radio-group (util/lsui-wrap "ContextMenuRadioGroup")) +;; tabs +(def tabs (util/lsui-wrap "Tabs")) +(def tabs-list (util/lsui-wrap "TabsList")) +(def tabs-trigger (util/lsui-wrap "TabsTrigger")) +(def tabs-content (util/lsui-wrap "TabsContent")) + (def dialog dialog-core/dialog) (def dialog-portal dialog-core/dialog-portal) (def dialog-overlay dialog-core/dialog-overlay) diff --git a/packages/ui/@/components/ui/tabs.tsx b/packages/ui/@/components/ui/tabs.tsx new file mode 100644 index 00000000000..f57fffdb5a0 --- /dev/null +++ b/packages/ui/@/components/ui/tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/packages/ui/package.json b/packages/ui/package.json index 5fe8f1310c4..7b5492c19a7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -30,6 +30,7 @@ "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-toggle-group": "^1.0.4", diff --git a/packages/ui/src/ui.ts b/packages/ui/src/ui.ts index a4a06f157d8..ba5ed812903 100644 --- a/packages/ui/src/ui.ts +++ b/packages/ui/src/ui.ts @@ -91,6 +91,7 @@ import { Toggle } from '@/components/ui/toggle' import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import * as uniqolor from 'uniqolor' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' declare global { var LSUI: any @@ -98,7 +99,7 @@ declare global { } const shadui = { - Link, Button, + Link, Button, Slider, SliderTrack, SliderRange, SliderThumb, DropdownMenu, DropdownMenuContent, @@ -184,7 +185,8 @@ const shadui = { Tooltip, TooltipTrigger, TooltipArrow, TooltipContent, TooltipProvider, TooltipPortal, Toggle, ToggleGroup, ToggleGroupItem, - Avatar, AvatarImage, AvatarFallback + Avatar, AvatarImage, AvatarFallback, + Tabs, TabsContent, TabsList, TabsTrigger } function setupGlobals() { @@ -203,4 +205,4 @@ setupGlobals() export { setupGlobals -} \ No newline at end of file +} diff --git a/packages/ui/yarn.lock b/packages/ui/yarn.lock index b2db1425f76..0c715129640 100644 --- a/packages/ui/yarn.lock +++ b/packages/ui/yarn.lock @@ -2197,6 +2197,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/primitive@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" + integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== + "@radix-ui/react-alert-dialog@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz#70dd529cbf1e4bff386814d3776901fcaa131b8c" @@ -2255,6 +2260,16 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-collection@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed" + integrity sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-compose-refs@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae" @@ -2269,6 +2284,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-compose-refs@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" + integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw== + "@radix-ui/react-context-menu@^2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.1.5.tgz#1bdbd72761439f9166f75dc4598f276265785c83" @@ -2296,6 +2316,16 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-context@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" + integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== + +"@radix-ui/react-context@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" + integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== + "@radix-ui/react-dialog@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.0.tgz#997e97cb183bc90bd888b26b8e23a355ac9fe5f0" @@ -2345,6 +2375,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-direction@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" + integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== + "@radix-ui/react-dismissable-layer@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b" @@ -2455,6 +2490,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-id@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" + integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-label@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.0.2.tgz#9c72f1d334aac996fdc27b48a8bdddd82108fb6d" @@ -2586,6 +2628,14 @@ "@radix-ui/react-compose-refs" "1.0.1" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-presence@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1" + integrity sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-primitive@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.0.tgz#376cd72b0fcd5e0e04d252ed33eb1b1f025af2b0" @@ -2602,6 +2652,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-primitive@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" + integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw== + dependencies: + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-radio-group@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz#3197f5dcce143bcbf961471bf89320735c0212d3" @@ -2635,6 +2692,21 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-controllable-state" "1.0.1" +"@radix-ui/react-roving-focus@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e" + integrity sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-collection" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-select@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-1.2.2.tgz#caa981fa0d672cf3c1b2a5240135524e69b32181" @@ -2733,6 +2805,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" +"@radix-ui/react-slot@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" + integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-switch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.0.3.tgz#6119f16656a9eafb4424c600fdb36efa5ec5837e" @@ -2747,6 +2826,20 @@ "@radix-ui/react-use-previous" "1.0.1" "@radix-ui/react-use-size" "1.0.1" +"@radix-ui/react-tabs@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz#698bd97923f6bcd629738198a73beebcc4c88b30" + integrity sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-presence" "1.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-roving-focus" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-toast@^1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.5.tgz#f5788761c0142a5ae9eb97f0051fd3c48106d9e6" @@ -2837,6 +2930,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-callback-ref@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" + integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== + "@radix-ui/react-use-controllable-state@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f" @@ -2853,6 +2951,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-controllable-state@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" + integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1" @@ -2883,6 +2988,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-layout-effect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" + integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== + "@radix-ui/react-use-previous@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" @@ -8751,8 +8861,7 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4, string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -8770,6 +8879,15 @@ string-width@4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4, string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -8784,8 +8902,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: - name strip-ansi-cjs +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9445,8 +9569,16 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - name wrap-ansi-cjs +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 646566399f17a97634de3aa2e810f1ce7e1bc0b6 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 5 Dec 2024 22:25:38 +0800 Subject: [PATCH 015/105] enhance: put tagged nodes and nodes with property into tabs --- .../frontend/components/db_based/page.cljs | 2 +- src/main/frontend/components/objects.cljs | 139 ++++++++---------- src/main/frontend/components/page.cljs | 42 +++++- 3 files changed, 102 insertions(+), 81 deletions(-) diff --git a/src/main/frontend/components/db_based/page.cljs b/src/main/frontend/components/db_based/page.cljs index a049a08b0bd..65b661c8d8f 100644 --- a/src/main/frontend/components/db_based/page.cljs +++ b/src/main/frontend/components/db_based/page.cljs @@ -9,7 +9,7 @@ (rum/defc configure-property < rum/reactive db-mixins/query [page] (let [page (db/sub-block (:db/id page))] - [:div.pb-4.-ml-1 + [:div.-ml-1 (shui/button {:variant "ghost" :class "opacity-50 hover:opacity-90" diff --git a/src/main/frontend/components/objects.cljs b/src/main/frontend/components/objects.cljs index c9f037dcef2..626d617c71a 100644 --- a/src/main/frontend/components/objects.cljs +++ b/src/main/frontend/components/objects.cljs @@ -154,53 +154,45 @@ (if loading? (ui/skeleton) - [:div.flex.flex-col.gap-2.mt-2 - - (ui/foldable - [:div.font-medium.opacity-60.as-toggle - "Tagged Nodes"] - (fn [] - [:div.mt-2 - (views/view view-entity {:config config - :data data - :set-data! set-data! - :views-title (class-views class views view-entity {:set-view-entity! set-view-entity! - :set-views! set-views!}) - :columns columns - :add-new-object! (if (= :logseq.class/Asset (:db/ident class)) - (fn [_e] - (shui/dialog-open! - (fn [] - [:div.flex.flex-col.gap-2 - [:div.font-medium "Add assets"] - (filepicker/picker - {:on-change (fn [_e files] - (p/do! - (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true) - (set-data! (get-class-objects class)) - (shui/dialog-close!)))})]))) - #(add-new-class-object! class set-data!)) - :show-add-property? true - :add-property! (fn [] - (state/pub-event! [:editor/new-property {:block class - :class-schema? true}])) - :on-delete-rows (fn [table selected-rows] - (let [pages (filter ldb/page? selected-rows) - blocks (remove ldb/page? selected-rows)] - (p/do! - (ui-outliner-tx/transact! - {:outliner-op :delete-blocks} - (when (seq blocks) - (outliner-op/delete-blocks! blocks nil)) - (let [page-ids (map :db/id pages) - tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)] - (when (seq tx-data) - (outliner-op/transact! tx-data {:outliner-op :save-block})))) - (set-data! (get-class-objects class)) - (when-let [f (get-in table [:data-fns :set-row-selection!])] - (f {})))))})]) - {:disable-on-pointer-down? true - :default-collapsed? (:sidebar? config)})]))) + (views/view view-entity + {:config config + :data data + :set-data! set-data! + :views-title (class-views class views view-entity {:set-view-entity! set-view-entity! + :set-views! set-views!}) + :columns columns + :add-new-object! (if (= :logseq.class/Asset (:db/ident class)) + (fn [_e] + (shui/dialog-open! + (fn [] + [:div.flex.flex-col.gap-2 + [:div.font-medium "Add assets"] + (filepicker/picker + {:on-change (fn [_e files] + (p/do! + (editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true) + (set-data! (get-class-objects class)) + (shui/dialog-close!)))})]))) + #(add-new-class-object! class set-data!)) + :show-add-property? true + :add-property! (fn [] + (state/pub-event! [:editor/new-property {:block class + :class-schema? true}])) + :on-delete-rows (fn [table selected-rows] + (let [pages (filter ldb/page? selected-rows) + blocks (remove ldb/page? selected-rows)] + (p/do! + (ui-outliner-tx/transact! + {:outliner-op :delete-blocks} + (when (seq blocks) + (outliner-op/delete-blocks! blocks nil)) + (let [page-ids (map :db/id pages) + tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)] + (when (seq tx-data) + (outliner-op/transact! tx-data {:outliner-op :save-block})))) + (set-data! (get-class-objects class)) + (when-let [f (get-in table [:data-fns :set-row-selection!])] + (f {})))))})))) (rum/defcs class-objects < rum/reactive db-mixins/query mixins/container-id [state class {:keys [current-page? sidebar?]}] @@ -251,35 +243,32 @@ []) (when (false? loading?) - (ui/foldable - [:div.font-medium.opacity-50 "Nodes with Property"] - [:div.mt-2 - (views/view view-entity {:config config - :data data - :set-data! set-data! - :title-key :views.table/property-nodes - :columns columns - :add-new-object! #(add-new-property-object! property set-data!) + (views/view view-entity + {:config config + :data data + :set-data! set-data! + :title-key :views.table/property-nodes + :columns columns + :add-new-object! #(add-new-property-object! property set-data!) ;; TODO: Add support for adding column - :show-add-property? false - :on-delete-rows (when-not (contains? #{:logseq.property/built-in? :logseq.property/parent} - (:db/ident property)) - (fn [table selected-rows] - (let [pages (filter ldb/page? selected-rows) - blocks (remove ldb/page? selected-rows)] - (p/do! - (ui-outliner-tx/transact! - {:outliner-op :delete-blocks} - (when (seq blocks) - (outliner-op/delete-blocks! blocks nil)) - (let [page-ids (map :db/id pages) - tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)] - (when (seq tx-data) - (outliner-op/transact! tx-data {:outliner-op :save-block})))) - (set-data! (get-property-related-objects (state/get-current-repo) property)) - (when-let [f (get-in table [:data-fns :set-row-selection!])] - (f {}))))))})] - {:disable-on-pointer-down? true})))) + :show-add-property? false + :on-delete-rows (when-not (contains? #{:logseq.property/built-in? :logseq.property/parent} + (:db/ident property)) + (fn [table selected-rows] + (let [pages (filter ldb/page? selected-rows) + blocks (remove ldb/page? selected-rows)] + (p/do! + (ui-outliner-tx/transact! + {:outliner-op :delete-blocks} + (when (seq blocks) + (outliner-op/delete-blocks! blocks nil)) + (let [page-ids (map :db/id pages) + tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)] + (when (seq tx-data) + (outliner-op/transact! tx-data {:outliner-op :save-block})))) + (set-data! (get-property-related-objects (state/get-current-repo) property)) + (when-let [f (get-in table [:data-fns :set-row-selection!])] + (f {}))))))})))) ;; Show all nodes containing the given property (rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 883d2bde822..fc254084253 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -560,6 +560,41 @@ (plugins/hook-ui-slot :page-head-actions-slotted nil) (plugins/hook-ui-items :pagebar)]))) +(rum/defc tabs + [page opts] + (let [class? (ldb/class? page) + property? (ldb/property? page) + both? (and class? property?) + default-tab (cond + both? + "tag" + class? + "tag" + :else + "property")] + (shui/tabs + {:defaultValue default-tab + :class (str "w-full")} + (when both? + (shui/tabs-list + {:class "h-8"} + (shui/tabs-trigger + {:value "tag" + :class "py-1 text-xs"} + "Tagged nodes") + (shui/tabs-trigger + {:value "property" + :class "py-1 text-xs"} + "Nodes with property"))) + (when class? + (shui/tabs-content + {:value "tag"} + (objects/class-objects page opts))) + (when property? + (shui/tabs-content + {:value "property"} + (objects/property-related-objects page (:current-page? opts))))))) + ;; A page is just a logical block (rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query mixins/container-id (rum/local false ::all-collapsed?) @@ -629,11 +664,8 @@ (when (and db-based? (ldb/property? page)) (db-page/configure-property page)) - (when (and db-based? class-page?) - (objects/class-objects page {:current-page? option :sidebar? sidebar?})) - - (when (and db-based? (ldb/property? page)) - (objects/property-related-objects page (:current-page? option))) + (when (and db-based? (or class-page? (ldb/property? page))) + (tabs page {:current-page? option :sidebar? sidebar?})) (when (and block? (not sidebar?) (not whiteboard?)) (let [config (merge config {:id "block-parent" From 8714df3d8ca00fc384155161cdc3e9328f44fdf2 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 15:00:02 +0800 Subject: [PATCH 016/105] enhance(ux): put property configure in tabs --- src/main/frontend/components/block.cljs | 2 +- .../frontend/components/db_based/page.cljs | 29 ++++++++++--------- src/main/frontend/components/page.cljs | 25 ++++++++-------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 66b38947771..4702c46c3ab 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2637,7 +2637,7 @@ (remove (fn [t] (or (ldb/inline-tag? (:block/raw-title block) t) (:logseq.property.class/hide-from-node t) - (= :logseq.class/Page (:db/ident t)))))) + (contains? ldb/internal-tags (:db/ident t)))))) popup-opts {:align :end :content-props {:on-click (fn [] (shui/popup-hide!)) :class "w-60"}} diff --git a/src/main/frontend/components/db_based/page.cljs b/src/main/frontend/components/db_based/page.cljs index 65b661c8d8f..4f3c619e36a 100644 --- a/src/main/frontend/components/db_based/page.cljs +++ b/src/main/frontend/components/db_based/page.cljs @@ -4,21 +4,22 @@ [frontend.db :as db] [frontend.db-mixins :as db-mixins] [logseq.shui.ui :as shui] - [rum.core :as rum])) + [rum.core :as rum] + [frontend.util :as util])) (rum/defc configure-property < rum/reactive db-mixins/query [page] (let [page (db/sub-block (:db/id page))] - [:div.-ml-1 - (shui/button - {:variant "ghost" - :class "opacity-50 hover:opacity-90" - :size :sm - :on-click (fn [^js e] - (shui/popup-show! (.-target e) - (fn [] - (property-config/dropdown-editor page nil {:debug? (.-altKey e)})) - {:content-props {:class "ls-property-dropdown-editor as-root"} - :align "start" - :as-dropdown? true}))} - "Configure property")])) + (shui/tabs-trigger + {:value "configure" + :class "py-1 text-xs" + :on-pointer-down (fn [e] + (util/stop e)) + :on-click (fn [^js e] + (shui/popup-show! (.-target e) + (fn [] + (property-config/dropdown-editor page nil {:debug? (.-altKey e)})) + {:content-props {:class "ls-property-dropdown-editor as-root"} + :align "start" + :as-dropdown? true}))} + "Configure property"))) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index fc254084253..e277eb46dbc 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -575,17 +575,21 @@ (shui/tabs {:defaultValue default-tab :class (str "w-full")} - (when both? + (when (or both? property?) (shui/tabs-list {:class "h-8"} - (shui/tabs-trigger - {:value "tag" - :class "py-1 text-xs"} - "Tagged nodes") - (shui/tabs-trigger - {:value "property" - :class "py-1 text-xs"} - "Nodes with property"))) + (when class? + (shui/tabs-trigger + {:value "tag" + :class "py-1 text-xs"} + "Tagged nodes")) + (when property? + (shui/tabs-trigger + {:value "property" + :class "py-1 text-xs"} + "Nodes with property")) + (when property? + (db-page/configure-property page)))) (when class? (shui/tabs-content {:value "tag"} @@ -661,9 +665,6 @@ :preview? preview?}))) (lsp-pagebar-slot)]) - (when (and db-based? (ldb/property? page)) - (db-page/configure-property page)) - (when (and db-based? (or class-page? (ldb/property? page))) (tabs page {:current-page? option :sidebar? sidebar?})) From b5b7c36a8f1faf3e30c1dc0003e23c3ae9d60dd1 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 15:41:36 +0800 Subject: [PATCH 017/105] enhance(ux): able to collapse tabs --- src/main/frontend/components/page.cljs | 158 +++++++++++++------------ 1 file changed, 85 insertions(+), 73 deletions(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index e277eb46dbc..ca294a342f5 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -196,7 +196,7 @@ (rum/defcs page-blocks-cp < rum/reactive db-mixins/query {:will-mount (fn [state] - (let [page-e (second (:rum/args state)) + (let [page-e (first (:rum/args state)) page-name (:block/name page-e)] (when (and page-name (db/journal-page? page-name) @@ -204,7 +204,7 @@ (date/journal-title->int (date/today)))) (state/pub-event! [:journal/insert-template page-name]))) state)} - [state repo page-e {:keys [sidebar? whiteboard?] :as config}] + [state page-e {:keys [sidebar? whiteboard?] :as config}] (when page-e (let [page-name (or (:block/name page-e) (str (:block/uuid page-e))) @@ -220,44 +220,35 @@ (remove (fn [b] (some? (get b (:db/ident block)))) children) :else - children) - db-based? (config/db-based-graph? repo)] - [:<> - (let [blocks (cond - (and - (not block?) - (empty? children) page-e) - (dummy-block page-e) - - :else - (let [document-mode? (state/sub :document/mode?) - hiccup-config (merge - {:id (if block? (str block-id) page-name) - :db/id (:db/id block) - :block? block? - :editor-box editor/box - :document/mode? document-mode?} - config) - config (common-handler/config-with-document-mode hiccup-config) - blocks (if block? [block] (db/sort-by-order children block))] - (let [add-button? (not (or config/publishing? - (let [last-child-id (model/get-block-deep-last-open-child-id (db/get-db) (:db/id (last blocks))) - block' (if last-child-id (db/entity last-child-id) (last blocks))] - (string/blank? (:block/title block')))))] - [:div - {:class (when add-button? "show-add-button")} - (page-blocks-inner page-e blocks config sidebar? whiteboard? block-id) - (let [args (if block-id - {:block-uuid block-id} - {:page page-name})] - (add-button args (:container-id config)))])))] - (if (and db-based? (or (ldb/class? block) (ldb/property? block))) - [:div.mt-4.ml-2.-mb-1 - (ui/foldable - [:div.font-medium.as-toggle {:class "pl-0.5"} "Notes"] - [:div.ml-1.-mb-2 blocks] - {:disable-on-pointer-down? true})] - blocks))]))) + children)] + (cond + (and + (not block?) + (empty? children) page-e) + (dummy-block page-e) + + :else + (let [document-mode? (state/sub :document/mode?) + hiccup-config (merge + {:id (if block? (str block-id) page-name) + :db/id (:db/id block) + :block? block? + :editor-box editor/box + :document/mode? document-mode?} + config) + config (common-handler/config-with-document-mode hiccup-config) + blocks (if block? [block] (db/sort-by-order children block))] + (let [add-button? (not (or config/publishing? + (let [last-child-id (model/get-block-deep-last-open-child-id (db/get-db) (:db/id (last blocks))) + block' (if last-child-id (db/entity last-child-id) (last blocks))] + (string/blank? (:block/title block')))))] + [:div + {:class (when add-button? "show-add-button")} + (page-blocks-inner page-e blocks config sidebar? whiteboard? block-id) + (let [args (if block-id + {:block-uuid block-id} + {:page page-name})] + (add-button args (:container-id config)))])))))) (rum/defc today-queries < rum/reactive [repo today? sidebar?] @@ -560,9 +551,17 @@ (plugins/hook-ui-slot :page-head-actions-slotted nil) (plugins/hook-ui-items :pagebar)]))) +(rum/defc rotating-arrow + [collapsed?] + [:span + {:class (if collapsed? "rotating-arrow collapsed" "rotating-arrow not-collapsed")} + (svg/caret-right)]) + (rum/defc tabs [page opts] - (let [class? (ldb/class? page) + (let [[collapsed? set-collapsed!] (rum/use-state false) + [control-display? set-control-display!] (rum/use-state false) + class? (ldb/class? page) property? (ldb/property? page) both? (and class? property?) default-tab (cond @@ -572,32 +571,45 @@ "tag" :else "property")] - (shui/tabs - {:defaultValue default-tab - :class (str "w-full")} - (when (or both? property?) - (shui/tabs-list - {:class "h-8"} - (when class? - (shui/tabs-trigger - {:value "tag" - :class "py-1 text-xs"} - "Tagged nodes")) - (when property? - (shui/tabs-trigger - {:value "property" - :class "py-1 text-xs"} - "Nodes with property")) - (when property? - (db-page/configure-property page)))) - (when class? - (shui/tabs-content - {:value "tag"} - (objects/class-objects page opts))) - (when property? - (shui/tabs-content - {:value "property"} - (objects/property-related-objects page (:current-page? opts))))))) + [:div.page-tabs + (shui/tabs + {:defaultValue default-tab + :class (str "w-full")} + (when (or both? property?) + [:div.flex.flex-row.gap-1.items-center.-ml-4 + {:on-mouse-over #(set-control-display! true) + :on-mouse-out #(set-control-display! false)} + [:a + {:class (if (or control-display? collapsed?) + "opacity-50 hover:opacity-100" + "opacity-0") + :on-click #(set-collapsed! (not collapsed?))} + (rotating-arrow collapsed?)] + (shui/tabs-list + {:class "h-8"} + (when class? + (shui/tabs-trigger + {:value "tag" + :class "py-1 text-xs"} + "Tagged nodes")) + (when property? + (shui/tabs-trigger + {:value "property" + :class "py-1 text-xs"} + "Nodes with property")) + (when property? + (db-page/configure-property page)))]) + + (when-not collapsed? + [:<> + (when class? + (shui/tabs-content + {:value "tag"} + (objects/class-objects page opts))) + (when property? + (shui/tabs-content + {:value "property"} + (objects/property-related-objects page (:current-page? opts))))]))])) ;; A page is just a logical block (rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query mixins/container-id @@ -665,19 +677,19 @@ :preview? preview?}))) (lsp-pagebar-slot)]) - (when (and db-based? (or class-page? (ldb/property? page))) - (tabs page {:current-page? option :sidebar? sidebar?})) - (when (and block? (not sidebar?) (not whiteboard?)) (let [config (merge config {:id "block-parent" :block? true})] [:div.mb-4 (component-block/breadcrumb config repo block-id {:level-limit 3})])) + (when (and db-based? (or class-page? (ldb/property? page))) + (tabs page {:current-page? option :sidebar? sidebar?})) + [:div.ls-page-blocks - (page-blocks-cp repo page (merge option {:sidebar? sidebar? - :container-id (:container-id state) - :whiteboard? whiteboard?}))]]) + (page-blocks-cp page (merge option {:sidebar? sidebar? + :container-id (:container-id state) + :whiteboard? whiteboard?}))]]) (when (not preview?) [:div {:style {:padding-left 9}} From cb01d6363b8327b35e90bee8786ee916a7d8f534 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 15:56:35 +0800 Subject: [PATCH 018/105] Use gap instead of margin-bottom --- src/main/frontend/components/page.cljs | 2 +- src/main/frontend/components/page.css | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index ca294a342f5..5e7edd9c50f 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -657,7 +657,7 @@ (if (and whiteboard-page? (not sidebar?)) [:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive - [:div.relative.page-inner + [:div.relative.grid.gap-4.page-inner (when (or (and db-based? (not block?)) (and (not db-based?) (not sidebar?) (not block?))) [:div.flex.flex-row.space-between diff --git a/src/main/frontend/components/page.css b/src/main/frontend/components/page.css index c21815df6b1..d46e8a1c504 100644 --- a/src/main/frontend/components/page.css +++ b/src/main/frontend/components/page.css @@ -65,8 +65,6 @@ @apply rounded-sm; &.title { - @apply mb-3; - .block-main-container { @apply gap-2; } @@ -258,5 +256,5 @@ html.is-native-ios { } .ls-page-blocks { - @apply min-h-[60px] -mb-2; -} \ No newline at end of file + @apply min-h-[60px]; +} From e54ab479ebbae80a8330d57349784d5175491ceb Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 16:17:24 +0800 Subject: [PATCH 019/105] chore: remove class icon from page reference --- src/main/frontend/components/block.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 4702c46c3ab..f20f59d048d 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -701,9 +701,9 @@ (when (and show-icon? (not tag?)) (let [own-icon (get page-entity (pu/get-pid :logseq.property/icon)) emoji? (and (map? own-icon) (= (:type own-icon) :emoji))] - (when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true :not-text-or-page? true})] + (when own-icon [:span {:class (str "icon-emoji-wrap " (when emoji? "as-emoji"))} - icon]))) + own-icon]))) [:span (if (and (coll? children) (seq children)) (for [child children] From 57912bda1500a14f24595db0d2e8a3ec37c3d4bb Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 18:20:21 +0800 Subject: [PATCH 020/105] chore: hide internal tags from all pages and graph view --- deps/db/src/logseq/db.cljs | 2 +- deps/db/src/logseq/db/frontend/entity_util.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index f83725feb2d..af2d652e55b 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -451,7 +451,7 @@ (d/datoms db :avet :block/name) (keep (fn [d] (let [e (d/entity db (:e d))] - (when-not (hidden? e) + (when-not (or (hidden? e) (internal-tags (:db/ident e))) e)))))) (defn built-in? diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 92be33d52ab..93f40f164b6 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -81,5 +81,5 @@ (set (map #(ident->type (:db/ident %)) (:block/tags entity))))) (def internal-tags - #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag + #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root :logseq.class/Closed-Value :logseq.class/Asset}) From a73606155bb0867bdb09ac627e82488a8a8d4e29 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 18:51:39 +0800 Subject: [PATCH 021/105] enhance: allow existing page and property to be used as tags --- deps/db/src/logseq/db/frontend/class.cljs | 3 ++- src/main/frontend/handler/db_based/page.cljs | 19 ++++++------- src/main/frontend/modules/outliner/ui.cljc | 3 ++- .../worker/handler/page/db_based/page.cljs | 27 ++++++++++++------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index e377ceeff53..9faa9485330 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -93,5 +93,6 @@ [db page-m] {:pre [(string? (:block/title page-m))]} (let [db-ident (create-user-class-ident-from-name (:block/title page-m)) - db-ident' (db-ident/ensure-unique-db-ident db db-ident)] + db-ident' (or (:db/ident page-m) + (db-ident/ensure-unique-db-ident db db-ident))] (sqlite-util/build-new-class (assoc page-m :db/ident db-ident')))) diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index 397e86606e4..57aa0fad8eb 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -33,15 +33,16 @@ (defn add-tag [repo block-id tag-entity] (let [opts {:outliner-op :save-block}] - (ui-outliner-tx/transact! opts - (p/do! - (editor-handler/save-current-block!) - ;; Check after save-current-block to get most up to date block content - (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) - (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] - ;; TODO: Move this to outliner.core to consistently add refs for tags - [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] - (db/transact! repo tx-data {:outliner-op :save-block}))))))) + (ui-outliner-tx/transact! + opts + (p/do! + (editor-handler/save-current-block!) + ;; Check after save-current-block to get most up to date block content + (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) + (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] + ;; TODO: Move this to outliner.core to consistently add refs for tags + [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] + (db/transact! repo tx-data {:outliner-op :save-block}))))))) (defn convert-to-tag! [page-entity] diff --git a/src/main/frontend/modules/outliner/ui.cljc b/src/main/frontend/modules/outliner/ui.cljc index 2b0be76bab5..b1dd689a933 100644 --- a/src/main/frontend/modules/outliner/ui.cljc +++ b/src/main/frontend/modules/outliner/ui.cljc @@ -4,7 +4,8 @@ [frontend.db.transact] [frontend.db.conn] [logseq.outliner.op] - [frontend.modules.outliner.op]))) + [frontend.modules.outliner.op] + [logseq.db]))) (defmacro transact! [opts & body] diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index 67540ade861..cc05465cffd 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -169,15 +169,24 @@ (let [db @conn date-formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal)) title (sanitize-title title*) - type (cond class? - :logseq.class/Tag - whiteboard? - :logseq.class/Whiteboard - today-journal? - :logseq.class/Journal - :else - :logseq.class/Page)] - (when-not (ldb/page-exists? db title #{type}) + types (cond class? + #{:logseq.class/Tag :logseq.class/Property :logseq.class/Page} + whiteboard? + #{:logseq.class/Whiteboard} + today-journal? + #{:logseq.class/Journal} + :else + #{:logseq.class/Page})] + (if-let [existing-page-id (first (ldb/page-exists? db title types))] + (let [existing-page (d/entity db existing-page-id) + tx-meta {:persist-op? persist-op? + :outliner-op :save-block}] + (when (and class? + (not (ldb/class? existing-page)) + (or (ldb/property? existing-page) (ldb/internal-page? existing-page))) + ;; convert existing property or page to class + (let [tx-data (db-class/build-new-class db (select-keys existing-page [:block/title :block/uuid :db/ident :block/created-at]))] + (ldb/transact! conn tx-data tx-meta)))) (let [format :markdown page (-> (gp-block/page-name->map title @conn true date-formatter {:class? class? From 253d068e3392530cbb30bfd59badde9e95d625d1 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Fri, 6 Dec 2024 22:48:02 +0800 Subject: [PATCH 022/105] fix: set internal classes parent to :logseq.class/Page --- deps/db/src/logseq/db/frontend/class.cljs | 23 ++++++++++---------- src/main/frontend/handler/db_based/page.cljs | 20 ++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 9faa9485330..dffb697b757 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -7,19 +7,25 @@ (def ^:large-vars/data-var built-in-classes "Map of built-in classes for db graphs with their :db/ident as keys" (ordered-map - :logseq.class/Tag {:title "Tag"} - :logseq.class/Root {:title "Root Tag"} :logseq.class/Page {:title "Page"} - :logseq.class/Whiteboard - {:title "Whiteboard" - :properties {:block/tags :logseq.class/Page}} + :logseq.class/Tag {:title "Tag" + :properties {:logseq.property/parent :logseq.class/Page}} :logseq.class/Property {:title "Property" - :properties {:block/tags :logseq.class/Page}} + :properties {:logseq.property/parent :logseq.class/Page}} + + :logseq.class/Journal + {:title "Journal" + :properties {:logseq.property/parent :logseq.class/Page + :logseq.property.journal/title-format "MMM do, yyyy"}} + + :logseq.class/Whiteboard + {:title "Whiteboard" + :properties {:logseq.property/parent :logseq.class/Page}} :logseq.class/Closed-Value {:title "Closed Value"} @@ -27,11 +33,6 @@ {:title "Task" :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/deadline]}} - :logseq.class/Journal - {:title "Journal" - :properties {:block/tags :logseq.class/Page - :logseq.property.journal/title-format "MMM do, yyyy"}} - :logseq.class/Query {:title "Query" :properties {:logseq.property/icon {:type :tabler-icon :id "search"}} diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index 57aa0fad8eb..cf8c36b7437 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -12,7 +12,8 @@ [logseq.common.util :as common-util] [logseq.common.util.page-ref :as page-ref] [datascript.impl.entity :as de] - [promesa.core :as p])) + [promesa.core :as p] + [logseq.db])) (defn- valid-tag? "Returns a boolean indicating whether the new tag passes all valid checks. @@ -32,17 +33,16 @@ (throw e))))) (defn add-tag [repo block-id tag-entity] - (let [opts {:outliner-op :save-block}] - (ui-outliner-tx/transact! - opts - (p/do! - (editor-handler/save-current-block!) + (ui-outliner-tx/transact! + {:outliner-op :save-block} + (p/do! + (editor-handler/save-current-block!) ;; Check after save-current-block to get most up to date block content - (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) - (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] + (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) + (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] ;; TODO: Move this to outliner.core to consistently add refs for tags - [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] - (db/transact! repo tx-data {:outliner-op :save-block}))))))) + [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] + (db/transact! repo tx-data {:outliner-op :save-block})))))) (defn convert-to-tag! [page-entity] From 3d84993d6de9c320f5c222ea3480276f50011a44 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 8 Dec 2024 12:12:41 +0800 Subject: [PATCH 023/105] remove :logseq.class/Closed-Value Because we know a entity is a closed value if it has the property `block/closed-value-property`. --- deps/db/src/logseq/db/frontend/class.cljs | 2 -- deps/db/src/logseq/db/frontend/entity_util.cljs | 4 ++-- deps/db/src/logseq/db/frontend/malli_schema.cljs | 3 +-- deps/db/src/logseq/db/frontend/property/build.cljs | 3 +-- deps/db/src/logseq/db/sqlite/common_db.cljs | 2 +- deps/db/src/logseq/db/sqlite/create_graph.cljs | 2 +- deps/outliner/src/logseq/outliner/property.cljs | 1 - src/main/frontend/worker/db/migrate.cljs | 10 ++++++---- 8 files changed, 12 insertions(+), 15 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index dffb697b757..00c4ec7bc7e 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -27,8 +27,6 @@ {:title "Whiteboard" :properties {:logseq.property/parent :logseq.class/Page}} - :logseq.class/Closed-Value {:title "Closed Value"} - :logseq.class/Task {:title "Task" :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/deadline]}} diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 93f40f164b6..6d952d8912a 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -37,7 +37,7 @@ (defn closed-value? [entity] - (has-tag? entity :logseq.class/Closed-Value)) + (some? (:block/closed-value-property entity))) (defn journal? "Given a page entity or map, check if it is a journal page" @@ -82,4 +82,4 @@ (def internal-tags #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root - :logseq.class/Closed-Value :logseq.class/Asset}) + :logseq.class/Asset}) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 8d95b7d5fac..57a7a6fdfc0 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -362,8 +362,7 @@ (vec (concat [:map] - [;; [:block/tags [:= :logseq.class/Closed-Value]] - ;; for built-in properties + [;; for built-in properties [:db/ident {:optional true} logseq-property-ident] [:block/title {:optional true} :string] [:property.value/content {:optional true} [:or :string :double]] diff --git a/deps/db/src/logseq/db/frontend/property/build.cljs b/deps/db/src/logseq/db/frontend/property/build.cljs index fc2cd99a963..a615864bdc0 100644 --- a/deps/db/src/logseq/db/frontend/property/build.cljs +++ b/deps/db/src/logseq/db/frontend/property/build.cljs @@ -8,8 +8,7 @@ (defn- closed-value-new-block [block-id block-type value property] (let [property-id (:db/ident property)] - (merge {:block/tags [:logseq.class/Closed-Value] - :block/format :markdown + (merge {:block/format :markdown :block/uuid block-id :block/page property-id :block/closed-value-property property-id diff --git a/deps/db/src/logseq/db/sqlite/common_db.cljs b/deps/db/src/logseq/db/sqlite/common_db.cljs index 783d6af4aaa..32dc57e0c41 100644 --- a/deps/db/src/logseq/db/sqlite/common_db.cljs +++ b/deps/db/src/logseq/db/sqlite/common_db.cljs @@ -212,7 +212,7 @@ (defn get-structured-datoms [db] - (->> (d/datoms db :avet :block/tags :logseq.class/Closed-Value) + (->> (d/datoms db :avet :block/closed-value-property) (mapcat (fn [d] (d/datoms db :eavt (:e d)))))) diff --git a/deps/db/src/logseq/db/sqlite/create_graph.cljs b/deps/db/src/logseq/db/sqlite/create_graph.cljs index fbba6121d6a..e648d9a5a30 100644 --- a/deps/db/src/logseq/db/sqlite/create_graph.cljs +++ b/deps/db/src/logseq/db/sqlite/create_graph.cljs @@ -174,7 +174,7 @@ default-pages (->> (map sqlite-util/build-new-page built-in-pages-names) (map mark-block-as-built-in)) hidden-pages (concat (build-initial-views) (build-favorites-page)) - depend-class? (fn [c] (when (contains? #{:logseq.class/Property :logseq.class/Tag :logseq.class/Closed-Value} (:db/ident c)) c)) + depend-class? (fn [c] (when (contains? #{:logseq.class/Property :logseq.class/Tag} (:db/ident c)) c)) depend-classes (filter depend-class? default-classes) other-classes (remove depend-class? default-classes) tx (vec (concat initial-data depend-classes properties-tx other-classes diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index f416a6c3574..461c2bb6d14 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -553,7 +553,6 @@ (when (seq values) (let [value-property-tx (map (fn [id] {:db/id id - :block/tags :logseq.class/Closed-Value :block/closed-value-property (:db/id property)}) (map :db/id values)) property-tx (outliner-core/block-with-updated-at {:db/id (:db/id property)})] diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index cbe1067829a..6f79d8786b4 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -430,10 +430,12 @@ "property" :logseq.class/Property "journal" :logseq.class/Journal "whiteboard" :logseq.class/Whiteboard - "closed value" :logseq.class/Closed-Value + "closed value" nil (throw (ex-info "unsupported block/type" {:type v})))] - [[:db/retract e :block/type] - [:db/add e :block/tags tag]])) datoms)] + (cond-> + [[:db/retract e :block/type]] + (some? tag) + (conj [:db/add e :block/tags tag])))) datoms)] (concat ;; set journal's tag to `#Page` [[:db/add (:db/id journal-entity) :block/tags :logseq.class/Page]] @@ -518,7 +520,7 @@ [47 {:fix replace-hidden-type-with-schema}] [48 {:properties [:logseq.property/default-value :logseq.property/scalar-default-value]}] [49 {:fix replace-special-id-ref-with-id-ref}] - [50 {:classes [:logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard :logseq.class/Property :logseq.class/Closed-Value]}] + [50 {:classes [:logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard :logseq.class/Property]}] [51 {:fix replace-block-type-with-tags}]]) (let [max-schema-version (apply max (map first schema-version->updates))] From a97714e7fe0ffe126718d3ce94f14fa0d2348d02 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 8 Dec 2024 12:30:28 +0800 Subject: [PATCH 024/105] fix: tests --- deps/db/test/logseq/db/sqlite/build_test.cljs | 8 ++++---- deps/db/test/logseq/db/sqlite/create_graph_test.cljs | 4 ++-- deps/db/test/logseq/db_test.cljs | 6 +++--- .../test/logseq/graph_parser/exporter_test.cljs | 12 ++++++------ .../outliner/test/logseq/outliner/validate_test.cljs | 2 +- src/main/frontend/worker/handler/page.cljs | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/deps/db/test/logseq/db/sqlite/build_test.cljs b/deps/db/test/logseq/db/sqlite/build_test.cljs index b460f624ff4..0f838f86b44 100644 --- a/deps/db/test/logseq/db/sqlite/build_test.cljs +++ b/deps/db/test/logseq/db/sqlite/build_test.cljs @@ -12,14 +12,14 @@ [{:page {:block/title "page1"} :blocks [{:block/title "Jrue Holiday" :build/tags [:Person]}]} {:page {:block/title "Jayson Tatum" :build/tags [:Person]}}])] - (is (= {:block/tags [{:block/title "Person", :block/tags [{:block/title "Tag"}]}]} - (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/tags]}]) ...] + (is (= {:block/tags [{:block/title "Person"}]} + (first (d/q '[:find [(pull ?b [{:block/tags [:block/title]}]) ...] :where [?b :block/title "Jrue Holiday"]] @conn))) "Person class is created and correctly associated to a block") - (is (= {:block/tags [{:block/title "Person", :block/tags [{:block/title "Tag"}]}]} - (first (d/q '[:find [(pull ?b [{:block/tags [:block/title :block/tags]}]) ...] + (is (= {:block/tags [{:block/title "Page"} {:block/title "Person"}]} + (first (d/q '[:find [(pull ?b [{:block/tags [:block/title]}]) ...] :where [?b :block/title "Jayson Tatum"]] @conn))) "Person class is created and correctly associated to a page"))) diff --git a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs index 813d73ccfb7..2a67f5c6e66 100644 --- a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs +++ b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs @@ -14,10 +14,10 @@ (deftest new-graph-db-idents (testing "a new graph follows :db/ident conventions for" (let [conn (db-test/create-conn) - ident-ents (->> (d/q '[:find (pull ?b [:db/ident :block/tags]) + ident-ents (->> (d/q '[:find [?b ...] :where [?b :db/ident]] @conn) - (map first)) + (map (fn [id] (d/entity @conn id)))) default-idents (map :db/ident ident-ents)] (is (> (count default-idents) 45) "Approximate number of default idents is correct") diff --git a/deps/db/test/logseq/db_test.cljs b/deps/db/test/logseq/db_test.cljs index 45320dd63d6..a3e8ddfd9aa 100644 --- a/deps/db/test/logseq/db_test.cljs +++ b/deps/db/test/logseq/db_test.cljs @@ -29,16 +29,16 @@ (ex-message e))))))) (def class-parents-data - [{:block/tags :logseq.class/Class + [{:block/tags :logseq.class/Tag :block/title "x" :block/name "x" :block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"} - {:block/tags :logseq.class/Class + {:block/tags :logseq.class/Tag :block/title "y" :block/name "y" :block/uuid #uuid "7008db08-ba0c-4aa9-afc6-7e4783e40a99" :logseq.property/parent [:block/uuid #uuid "6c353967-f79b-4785-b804-a39b81d72461"]} - {:block/tags :logseq.class/Class + {:block/tags :logseq.class/Tag :block/title "z" :block/name "z" :block/uuid #uuid "d95f2912-a7af-41b9-8ed5-28861f7fc0be" diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 9d948b07a16..73cdc855d7b 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -209,7 +209,7 @@ "Correct number of pages with block content") (is (= 11 (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Tag] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) count)) "Correct number of user classes") (is (= 4 (count (d/datoms @conn :avet :block/tags :logseq.class/Whiteboard)))) @@ -501,7 +501,7 @@ (is (= 0 (count @(:ignored-properties import-state))) "No ignored properties") (is (= 0 (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Tag] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) count)) "Correct number of user classes") @@ -562,7 +562,7 @@ (:block/tags (readable-properties @conn block))) "tagged block has configured tag imported as a class") - (is (= :logseq.class/Class (:db/ident (first (:block/tags tag-page)))) + (is (= :logseq.class/Tag (:db/ident (first (:block/tags tag-page)))) "configured tag page in :tag-classes is a class") (is (and another-tag-page (not (ldb/class? another-tag-page))) "unconfigured tag page is not a class") @@ -586,7 +586,7 @@ (is (= #{:user.class/Property :user.class/Movie :user.class/Class :user.class/Tool} (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Tag] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) set)) "All classes are correctly defined by :type") @@ -608,7 +608,7 @@ "tagged block can have another property that references the same class it is tagged with, without creating a duplicate class") - (is (= :logseq.class/Class (:db/ident (first (:block/tags tag-page)))) + (is (= :logseq.class/Tag (:db/ident (first (:block/tags tag-page)))) "configured tag page derived from :property-classes is a class") (is (nil? (find-page-by-name @conn "type")) "No page exists for configured property") @@ -654,7 +654,7 @@ :user.class/Class :user.class/Tool :user.class/Whiteboard___Tool} (->> @conn (d/q '[:find [?ident ...] - :where [?b :block/tags :logseq.class/Class] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) + :where [?b :block/tags :logseq.class/Tag] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])]) set)) "All classes are correctly defined by :type") diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index 92111687208..aa6fec6e61b 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -109,7 +109,7 @@ (testing "Validate pages" (let [pages (->> (d/q '[:find [?b ...] :where [?b :block/title] - (or [?b :block/tags :logseq.class/Class] + (or [?b :block/tags :logseq.class/Tag] [?b :block/tags :logseq.class/Property] [?b :block/tags :logseq.class/Page] [?b :block/tags :logseq.class/Journal] diff --git a/src/main/frontend/worker/handler/page.cljs b/src/main/frontend/worker/handler/page.cljs index aa83bcf47e6..9ab11c02317 100644 --- a/src/main/frontend/worker/handler/page.cljs +++ b/src/main/frontend/worker/handler/page.cljs @@ -30,7 +30,7 @@ * :create-first-block? - when true, create an empty block if the page is empty. * :uuid - when set, use this uuid instead of generating a new one. - * :class? - when true, adds a :block/tags ':logseq.class/Class' + * :class? - when true, adds a :block/tags ':logseq.class/Tag' * :whiteboard? - when true, adds a :block/tags ':logseq.class/Whiteboard' * :tags - tag uuids that are added to :block/tags * :persist-op? - when true, add an update-page op From 1979b70b328ead076b0820f61082e3d4faf46a85 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 10:09:55 +0800 Subject: [PATCH 025/105] fix: db tests --- .../src/logseq/db/frontend/entity_util.cljs | 5 ++- .../src/logseq/db/frontend/malli_schema.cljs | 45 ++++++++++--------- .../src/logseq/db/frontend/property/type.cljs | 16 +++++-- .../test/logseq/db/frontend/inputs_test.cljs | 1 + .../test/logseq/db/frontend/rules_test.cljs | 34 +++++++------- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 6d952d8912a..6d077788617 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -1,7 +1,8 @@ (ns logseq.db.frontend.entity-util "Lower level entity util fns used across db namespaces" (:require [datascript.core :as d] - [clojure.string :as string]) + [clojure.string :as string] + [datascript.impl.entity :as de]) (:refer-clojure :exclude [object?])) (defn db-based-graph? @@ -63,7 +64,7 @@ (when page (if (string? page) (string/starts-with? page "$$$") - (when (map? page) + (when (or (map? page) (de/entity? page)) (false? (get-in page [:block/schema :public?])))))) (defn object? diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 57a7a6fdfc0..4330525d614 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -434,28 +434,29 @@ [:multi {:dispatch (fn [d] ;; order matters as some block types are a subset of others e.g. :whiteboard (let [db *db-for-validate-fns* - d (if (:block/uuid d) (d/entity db [:block/uuid (:block/uuid d)]) d)] - (cond - (entity-util/property? d) - :property - (entity-util/class? d) - :class - (entity-util/hidden? d) - :hidden - (entity-util/whiteboard? d) - :normal-page - (entity-util/page? d) - :normal-page - (entity-util/asset? d) - :asset-block - (:file/path d) - :file-block - (:block/uuid d) - :block - (= (:db/ident d) :logseq.property/empty-placeholder) - :property-value-placeholder - (:db/ident d) - :db-ident-key-value)))}] + d (if (:block/uuid d) (d/entity db [:block/uuid (:block/uuid d)]) d) + dispatch-key (cond + (entity-util/property? d) + :property + (entity-util/class? d) + :class + (entity-util/hidden? (:block/title d)) + :hidden + (entity-util/whiteboard? d) + :normal-page + (entity-util/page? d) + :normal-page + (entity-util/asset? d) + :asset-block + (:file/path d) + :file-block + (:block/uuid d) + :block + (= (:db/ident d) :logseq.property/empty-placeholder) + :property-value-placeholder + (:db/ident d) + :db-ident-key-value)] + dispatch-key))}] {:property property-page :class class-page :hidden hidden-page diff --git a/deps/db/src/logseq/db/frontend/property/type.cljs b/deps/db/src/logseq/db/frontend/property/type.cljs index e7bc716de18..163d63f80dc 100644 --- a/deps/db/src/logseq/db/frontend/property/type.cljs +++ b/deps/db/src/logseq/db/frontend/property/type.cljs @@ -167,10 +167,18 @@ :string string? :raw-number number? - :entity entity? - :class class-entity? - :property property-entity? - :page page-entity? + :entity [:fn + {:error/message "should be an Entity"} + entity?] + :class [:fn + {:error/message "should be a Class"} + class-entity?] + :property [:fn + {:error/message "should be a Property"} + property-entity?] + :page [:fn + {:error/message "should be a Page"} + page-entity?] :keyword keyword? :map map? ;; coll elements are ordered as it's saved as a vec diff --git a/deps/db/test/logseq/db/frontend/inputs_test.cljs b/deps/db/test/logseq/db/frontend/inputs_test.cljs index 96c9d61c29c..a48de9a7ad6 100644 --- a/deps/db/test/logseq/db/frontend/inputs_test.cljs +++ b/deps/db/test/logseq/db/frontend/inputs_test.cljs @@ -18,6 +18,7 @@ (deftest resolve-input-for-page-and-block-inputs (let [conn (d/create-conn db-schema/schema-for-db-based-graph) + _ (d/transact! conn [{:db/ident :logseq.class/Page}]) _ (sqlite-build/create-blocks conn [{:page {:block/title "page1"} diff --git a/deps/db/test/logseq/db/frontend/rules_test.cljs b/deps/db/test/logseq/db/frontend/rules_test.cljs index 5e9e4938b3e..95fa194889d 100644 --- a/deps/db/test/logseq/db/frontend/rules_test.cljs +++ b/deps/db/test/logseq/db/frontend/rules_test.cljs @@ -2,7 +2,8 @@ (:require [cljs.test :refer [deftest is testing are]] [datascript.core :as d] [logseq.db.frontend.rules :as rules] - [logseq.db.test.helper :as db-test])) + [logseq.db.test.helper :as db-test] + [logseq.db :as ldb])) (defn q-with-rules [query db] ;; query assumes no :in given @@ -31,10 +32,10 @@ {:properties {:foo {:block/schema {:type :default}} :foo2 {:block/schema {:type :default}}} :pages-and-blocks - [{:page {:block/title "Page" + [{:page {:block/title "Page1" :build/properties {:foo "bar"}}}]})] - (is (= ["Page"] + (is (= ["Page1"] (->> (q-with-rules '[:find (pull ?b [:block/title]) :where (has-property ?b :user.property/foo)] @conn) (map (comp :block/title first)))) @@ -44,9 +45,9 @@ @conn) (map (comp :block/title first)))) "has-property returns no result when block doesn't have property") - (is (= [:user.property/foo] + (is (= [:user.property/foo :block/tags] (q-with-rules '[:find [?p ...] - :where (has-property ?b ?p) [?b :block/title "Page"]] + :where (has-property ?b ?p) [?b :block/title "Page1"]] @conn)) "has-property can bind to property arg"))) @@ -57,12 +58,12 @@ :number-many {:block/schema {:type :number :cardinality :many}} :page-many {:block/schema {:type :node :cardinality :many}}} :pages-and-blocks - [{:page {:block/title "Page" + [{:page {:block/title "Page1" :build/properties {:foo "bar" :number-many #{5 10} :page-many #{[:page "Page A"]}}}} {:page {:block/title "Page A" :build/properties {:foo "bar A"}}}]})] (testing "cardinality :one property" - (is (= ["Page"] + (is (= ["Page1"] (->> (q-with-rules '[:find (pull ?b [:block/title]) :where (property ?b :user.property/foo "bar")] @conn) (map (comp :block/title first)))) @@ -74,13 +75,13 @@ "property returns no result when page doesn't have property value") (is (= #{:user.property/foo} (->> (q-with-rules '[:find [?p ...] - :where (property ?b ?p "bar") [?b :block/title "Page"]] + :where (property ?b ?p "bar") [?b :block/title "Page1"]] @conn) set)) "property can bind to property arg with bound property value")) (testing "cardinality :many property" - (is (= ["Page"] + (is (= ["Page1"] (->> (q-with-rules '[:find (pull ?b [:block/title]) :where (property ?b :user.property/number-many 5)] @conn) (map (comp :block/title first)))) @@ -92,14 +93,14 @@ "property returns no result when page doesn't have property value") (is (= #{:user.property/number-many} (->> (q-with-rules '[:find [?p ...] - :where (property ?b ?p 5) [?b :block/title "Page"]] + :where (property ?b ?p 5) [?b :block/title "Page1"]] @conn) set)) "property can bind to property arg with bound property value")) ;; NOTE: Querying a ref's name is different than before and requires more than just the rule (testing ":ref property" - (is (= ["Page"] + (is (= ["Page1"] (->> (q-with-rules '[:find (pull ?b [:block/title]) :where (property ?b :user.property/page-many "Page A")] @conn) @@ -113,22 +114,23 @@ "property returns no result when page doesn't have property value")) (testing "bindings with property value" - (is (= #{:user.property/foo :user.property/number-many :user.property/page-many} + (is (= #{:user.property/foo :user.property/number-many :user.property/page-many :block/tags} (->> (q-with-rules '[:find [?p ...] - :where (property ?b ?p _) [?b :block/title "Page"]] + :where (property ?b ?p _) [?b :block/title "Page1"]] @conn) set)) "property can bind to property arg with unbound property value") (is (= #{[:user.property/number-many 10] [:user.property/number-many 5] [:user.property/foo "bar"] - [:user.property/page-many "Page A"]} + [:user.property/page-many "Page A"] + [:block/tags "Page"]} (->> (q-with-rules '[:find ?p ?val - :where (property ?b ?p ?val) [?b :block/title "Page"]] + :where (property ?b ?p ?val) [?b :block/title "Page1"]] @conn) set)) "property can bind to property and property value args") - (is (= #{"Page"} + (is (= #{"Page1"} (->> (q-with-rules '[:find (pull ?b [:block/title]) :where [?b :user.property/page-many ?pv] From caab95af522996b84e496acc512fc3d3d3cebe8d Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 10:13:53 +0800 Subject: [PATCH 026/105] fix: Page tag in exporter --- deps/graph-parser/src/logseq/graph_parser/exporter.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 5cc591ec777..a4fa8bec93e 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -949,7 +949,7 @@ ;; only happens for few file built-ins like tags and alias (and (contains? all-built-in-names (keyword (:block/name page))) (not (:block/tags page))) - (assoc :block/tags :logseq.class/Page)))] + (assoc :block/tags [:logseq.class/Page])))] (cond-> page' (:block/namespace page) ((fn [block'] From 37747507d11aeaf6f063757f423649c6d7c48ca7 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 10:21:13 +0800 Subject: [PATCH 027/105] fix: lint --- deps/db/test/logseq/db/frontend/rules_test.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deps/db/test/logseq/db/frontend/rules_test.cljs b/deps/db/test/logseq/db/frontend/rules_test.cljs index 95fa194889d..10c38579acc 100644 --- a/deps/db/test/logseq/db/frontend/rules_test.cljs +++ b/deps/db/test/logseq/db/frontend/rules_test.cljs @@ -2,8 +2,7 @@ (:require [cljs.test :refer [deftest is testing are]] [datascript.core :as d] [logseq.db.frontend.rules :as rules] - [logseq.db.test.helper :as db-test] - [logseq.db :as ldb])) + [logseq.db.test.helper :as db-test])) (defn q-with-rules [query db] ;; query assumes no :in given From 86f510ea6ce0c5bded71d7e507e3cf81e14b8102 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 10:24:19 +0800 Subject: [PATCH 028/105] fix: lint --- src/main/frontend/components/file_sync.cljs | 2 +- src/main/frontend/ui.cljs | 3 +-- src/test/frontend/worker/handler/page/db_based/page_test.cljs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/frontend/components/file_sync.cljs b/src/main/frontend/components/file_sync.cljs index 66810ca5cc1..171cafc1a74 100644 --- a/src/main/frontend/components/file_sync.cljs +++ b/src/main/frontend/components/file_sync.cljs @@ -693,7 +693,7 @@ [:div.cp__file-sync-page-histories-right [:h1.title.text-xl "Current version"] - (page/page-blocks-cp (state/get-current-repo) page-entity nil)] + (page/page-blocks-cp page-entity nil)] ;; ready loading [:div.flex.items-center.h-full.justify-center.w-full.absolute.ready-loading diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index 4b354fbcd22..23827b716f0 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -556,8 +556,7 @@ [:div [:div.ui__ac-group-name group] (render-f matched)] - (render-f matched)) - (render-f matched)) + (render-f matched))) (render-f matched))] (when empty-placeholder empty-placeholder))])) diff --git a/src/test/frontend/worker/handler/page/db_based/page_test.cljs b/src/test/frontend/worker/handler/page/db_based/page_test.cljs index 2f0ef3ee82d..13ff732dd63 100644 --- a/src/test/frontend/worker/handler/page/db_based/page_test.cljs +++ b/src/test/frontend/worker/handler/page/db_based/page_test.cljs @@ -45,7 +45,7 @@ (worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true}) (is (= #{"Class" "Page"} - (set (d/q '[:find [?tag-type ...] + (set (d/q '[:find [?tag-title ...] :where [?b :block/title "class1"] [?b :block/tags ?t] From c02270f34916732513f9704be6929128391d6488 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 11:59:22 +0800 Subject: [PATCH 029/105] fix: outliner tests --- .../src/logseq/outliner/validate.cljs | 93 ++++++------------- .../test/logseq/outliner/pipeline_test.cljs | 18 ++-- .../test/logseq/outliner/validate_test.cljs | 8 +- 3 files changed, 44 insertions(+), 75 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index d691924dbd4..bd88048004e 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -5,7 +5,9 @@ [datascript.core :as d] [logseq.db :as ldb] [logseq.common.date :as common-date] - [logseq.common.util.namespace :as ns-util])) + [logseq.common.util.namespace :as ns-util] + [datascript.impl.entity :as de] + [clojure.set :as set])) (defn ^:api validate-page-title-characters "Validates characters that must not be in a page title" @@ -42,30 +44,6 @@ :payload {:message "Built-in pages can't be edited" :type :warning}})))) -(defn- validate-unique-for-property-page - [entity db new-title] - (when-let [_res (seq (d/q (if (:logseq.property/built-in? entity) - '[:find [?b ...] - :in $ ?eid ?title - :where - [?b :block/title ?title] - [?b :block/tags :logseq.class/Property] - [(not= ?b ?eid)]] - '[:find [?b ...] - :in $ ?eid ?title - :where - [?b :block/title ?title] - [?b :block/tags :logseq.class/Property] - [(missing? $ ?b :logseq.property/built-in?)] - [(not= ?b ?eid)]]) - db - (:db/id entity) - new-title))] - (throw (ex-info "Duplicate property" - {:type :notification - :payload {:message (str "Another property named " (pr-str new-title) " already exists") - :type :warning}})))) - (defn- validate-unique-by-parent-and-name [db entity new-title] (when-let [_res (seq (d/q '[:find [?b ...] :in $ ?eid ?type ?title @@ -88,44 +66,33 @@ (defn- validate-unique-for-page [db new-title {:block/keys [tags] :as entity}] (cond - (and (seq tags) (ldb/internal-page? entity)) - (when-let [res (seq (d/q '[:find [?b ...] - :in $ ?eid ?title [?tag-id ...] - :where - [?b :block/title ?title] - [?b :block/tags ?tag-id] - [(not= ?b ?eid)]] - db - (:db/id entity) - new-title - (map :db/id tags)))] - (throw (ex-info "Duplicate page by tag" - {:type :notification - :payload {:message (str "Another page named " (pr-str new-title) " already exists for tag " - (pr-str (->> res first (d/entity db) :block/tags first :block/title))) - :type :warning}}))) - - (ldb/property? entity) - (validate-unique-for-property-page entity db new-title) + (seq tags) + (when-let [another-id (first (d/q '[:find [?b ...] + :in $ ?eid ?title [?tag-id ...] + :where + [?b :block/title ?title] + [?b :block/tags ?tag-id] + [(not= ?b ?eid)]] + db + (:db/id entity) + new-title + (map :db/id tags)))] + (let [another (d/entity db another-id) + this-tags (set (map :db/ident tags)) + another-tags (set (map :db/ident (:block/tags another))) + common-tag-ids (set/intersection this-tags another-tags)] + (when-not (and (= common-tag-ids #{:logseq.class/Page}) + (> (count this-tags) 1) + (> (count another-tags) 1)) + (throw (ex-info "Duplicate page" + {:type :notification + :payload {:message (str "Another page named " (pr-str new-title) " already exists for tags " + (pr-str + (map (fn [id] (:block/title (d/entity db id))) common-tag-ids))) + :type :warning}}))))) (:logseq.property/parent entity) - (validate-unique-by-parent-and-name db entity new-title) - - :else - (when-let [_res (seq (d/q '[:find [?b ...] - :in $ ?eid [?tag ...] ?title - :where - [?b :block/title ?title] - [?b :block/tags ?tag] - [(not= ?b ?eid)]] - db - (:db/id entity) - (map :db/id (:block/tags entity)) - new-title))] - (throw (ex-info "Duplicate page without tag" - {:type :notification - :payload {:message (str "Another page named " (pr-str new-title) " already exists") - :type :warning}}))))) + (validate-unique-by-parent-and-name db entity new-title))) (defn ^:api validate-unique-by-name-tag-and-block-type "Validates uniqueness of nodes for the following cases: @@ -133,8 +100,8 @@ - Page names of other types are unique for their type e.g. their can be #Journal ('class') and Journal ('page') - Property names are unique and don't consider built-in property names" [db new-title entity] - (when (ldb/page? entity) - (validate-unique-for-page db new-title entity))) + (assert (ldb/page? entity) "`entity` is not a page") + (validate-unique-for-page db new-title entity)) (defn ^:api validate-disallow-page-with-journal-name "Validates a non-journal page renamed to journal format" diff --git a/deps/outliner/test/logseq/outliner/pipeline_test.cljs b/deps/outliner/test/logseq/outliner/pipeline_test.cljs index d242f7a62f6..d096dda27f1 100644 --- a/deps/outliner/test/logseq/outliner/pipeline_test.cljs +++ b/deps/outliner/test/logseq/outliner/pipeline_test.cljs @@ -8,7 +8,8 @@ [logseq.outliner.pipeline :as outliner-pipeline] [clojure.string :as string] [logseq.db.test.helper :as db-test] - [logseq.common.util.page-ref :as page-ref])) + [logseq.common.util.page-ref :as page-ref] + [clojure.set :as set])) (defn- get-blocks [db] (->> (d/q '[:find (pull ?b [* {:block/path-refs [:block/name :db/id]}]) @@ -50,13 +51,14 @@ ;; Only keep enough of content to uniquely identify block (map #(hash-map :block/title (re-find #"\w+" (:block/title %)) :path-ref-names (set (map :block/name (:block/path-refs %))))))] - (is (= [{:block/title "parent" - :path-ref-names #{"page1" "bar"}} - {:block/title "child" - :path-ref-names #{"page1" "bar" "baz"}} - {:block/title "grandchild" - :path-ref-names #{"page1" "bar" "baz" "bing"}}] - updated-blocks))))) + (let [page-tag-refs #{"tags" "page"}] + (is (= [{:block/title "parent" + :path-ref-names (set/union page-tag-refs #{"page1" "bar"})} + {:block/title "child" + :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz"})} + {:block/title "grandchild" + :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz" "bing"})}] + updated-blocks)))))) (deftest block-content-refs (let [conn (db-test/create-conn-with-blocks diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index aa6fec6e61b..09e398ffdce 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -6,7 +6,7 @@ (defn- find-block-by-content [conn content] (->> content - (d/q '[:find [(pull ?b [*]) ...] + (d/q '[:find [(pull ?b [* {:block/tags [:db/id :block/title :db/ident]}]) ...] :in $ ?content :where [?b :block/title ?content] [(missing? $ ?b :logseq.property/built-in?)]] @conn) @@ -26,7 +26,7 @@ (is (thrown-with-msg? js/Error - #"Duplicate property" + #"Duplicate page" (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "background-image" @@ -41,7 +41,7 @@ (is (thrown-with-msg? js/Error - #"Duplicate page by tag" + #"Duplicate page" (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "Apple" @@ -56,7 +56,7 @@ (is (thrown-with-msg? js/Error - #"Duplicate page without tag" + #"Duplicate page" (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "page1" From c78d0193894bf8763c84d09990e55686bc8ecf4f Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 18:08:57 +0800 Subject: [PATCH 030/105] fix: don't overwrite internal tags when importing md files --- .../src/logseq/graph_parser/exporter.cljs | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index a4fa8bec93e..992065ffd1f 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -142,21 +142,29 @@ (assert (:block/uuid class-m') "Class must have a :block/uuid") [:block/uuid (:block/uuid class-m')]) (when (convert-tag? (:block/name tag-block) user-options) - (if-let [existing-tag-uuid (find-existing-class db tag-block)] - [:block/uuid existing-tag-uuid] - ;; Creates or updates page within same tx - (let [class-m (find-or-create-class db (:block/title tag-block) all-idents tag-block) - class-m' (-> (merge tag-block class-m - (when-not (:block/uuid tag-block) - {:block/uuid (find-or-gen-class-uuid page-names-to-uuids (:block/name tag-block) (:db/ident class-m))})) + (let [existing-tag-uuid (find-existing-class db tag-block)] + (cond + ;; Don't overwrite internal tags + (contains? #{"tag" "property" "page" "journal" "asset"} (:block/name tag-block)) + [:block/uuid (common-uuid/gen-uuid)] + + existing-tag-uuid + [:block/uuid existing-tag-uuid] + + :else + ;; Creates or updates page within same tx + (let [class-m (find-or-create-class db (:block/title tag-block) all-idents tag-block) + class-m' (-> (merge tag-block class-m + (when-not (:block/uuid tag-block) + {:block/uuid (find-or-gen-class-uuid page-names-to-uuids (:block/name tag-block) (:db/ident class-m))})) ;; override with imported timestamps - (dissoc :block/created-at :block/updated-at) - (merge (add-missing-timestamps - (select-keys tag-block [:block/created-at :block/updated-at]))) - (replace-namespace-with-parent page-names-to-uuids))] - (when (:new-class? (meta class-m)) (swap! classes-tx conj class-m')) - (assert (:block/uuid class-m') "Class must have a :block/uuid") - [:block/uuid (:block/uuid class-m')]))))) + (dissoc :block/created-at :block/updated-at) + (merge (add-missing-timestamps + (select-keys tag-block [:block/created-at :block/updated-at]))) + (replace-namespace-with-parent page-names-to-uuids))] + (when (:new-class? (meta class-m)) (swap! classes-tx conj class-m')) + (assert (:block/uuid class-m') "Class must have a :block/uuid") + [:block/uuid (:block/uuid class-m')])))))) (defn- logseq-class-ident? [k] @@ -1285,7 +1293,13 @@ tx' (common-util/fast-remove-nils tx) ;; _ (prn :tx-counts (map count (vector whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx))) ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :tx tx'})) - main-tx-report (d/transact! conn tx' {::new-graph? true}) + main-tx-report (try + (d/transact! conn tx' {::new-graph? true}) + (catch :default e + (js/console.error e) + (prn :db (ldb/write-transit-str @conn)) + (prn :tx (ldb/write-transit-str tx')) + (throw e))) upstream-properties-tx (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn) From e737a69583e62abe5df37c96b1e97103d2d5030d Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 19:47:50 +0800 Subject: [PATCH 031/105] fix: importing tags that have the same names as internal tags --- .../src/logseq/graph_parser/exporter.cljs | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 992065ffd1f..cdbd7666368 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -70,20 +70,21 @@ ([db class-name all-idents] (find-or-create-class db class-name all-idents {})) ([db class-name all-idents class-block] - (if-let [db-ident (get @all-idents (keyword class-name))] - {:db/ident db-ident} - (let [m - (if (:block/namespace class-block) + (let [ident (keyword "user.class" class-name)] + (if-let [db-ident (get @all-idents ident)] + {:db/ident db-ident} + (let [m + (if (:block/namespace class-block) ;; Give namespaced tags a unique ident so they don't conflict with other tags - (-> (db-class/build-new-class db {:block/title (build-class-ident-name class-name)}) - (merge {:block/title class-name - :block/name (common-util/page-name-sanity-lc class-name)}) - (build-new-namespace-page)) - (db-class/build-new-class db - {:block/title class-name - :block/name (common-util/page-name-sanity-lc class-name)}))] - (swap! all-idents assoc (keyword class-name) (:db/ident m)) - (with-meta m {:new-class? true}))))) + (-> (db-class/build-new-class db {:block/title (build-class-ident-name class-name)}) + (merge {:block/title class-name + :block/name (common-util/page-name-sanity-lc class-name)}) + (build-new-namespace-page)) + (db-class/build-new-class db + {:block/title class-name + :block/name (common-util/page-name-sanity-lc class-name)}))] + (swap! all-idents assoc ident (:db/ident m)) + (with-meta m {:new-class? true})))))) (defn- find-or-gen-class-uuid [page-names-to-uuids page-name db-ident & {:keys [temp-new-class?]}] (or (if temp-new-class? @@ -142,22 +143,23 @@ (assert (:block/uuid class-m') "Class must have a :block/uuid") [:block/uuid (:block/uuid class-m')]) (when (convert-tag? (:block/name tag-block) user-options) - (let [existing-tag-uuid (find-existing-class db tag-block)] + (let [existing-tag-uuid (find-existing-class db tag-block) + internal-tag-conflict? (contains? #{"tag" "property" "page" "journal" "asset"} (:block/name tag-block))] (cond ;; Don't overwrite internal tags - (contains? #{"tag" "property" "page" "journal" "asset"} (:block/name tag-block)) - [:block/uuid (common-uuid/gen-uuid)] - - existing-tag-uuid + (and existing-tag-uuid (not internal-tag-conflict?)) [:block/uuid existing-tag-uuid] :else ;; Creates or updates page within same tx (let [class-m (find-or-create-class db (:block/title tag-block) all-idents tag-block) class-m' (-> (merge tag-block class-m - (when-not (:block/uuid tag-block) - {:block/uuid (find-or-gen-class-uuid page-names-to-uuids (:block/name tag-block) (:db/ident class-m))})) - ;; override with imported timestamps + (if internal-tag-conflict? + {:block/uuid (common-uuid/gen-uuid :db-ident-block-uuid (:db/ident class-m))} + (when-not (:block/uuid tag-block) + (let [id (find-or-gen-class-uuid page-names-to-uuids (:block/name tag-block) (:db/ident class-m))] + {:block/uuid id})))) + ;; override with imported timestamps (dissoc :block/created-at :block/updated-at) (merge (add-missing-timestamps (select-keys tag-block [:block/created-at :block/updated-at]))) From a025b4b0963e275c955fcd5422504795b76cd852 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 21:17:15 +0800 Subject: [PATCH 032/105] wip: fix exporter tests --- .../src/logseq/graph_parser/exporter.cljs | 14 +++++---- .../logseq/graph_parser/exporter_test.cljs | 30 +++++++++---------- .../handler/page/db_based/page_test.cljs | 2 +- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index cdbd7666368..c1963444e68 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -70,7 +70,7 @@ ([db class-name all-idents] (find-or-create-class db class-name all-idents {})) ([db class-name all-idents class-block] - (let [ident (keyword "user.class" class-name)] + (let [ident (keyword class-name)] (if-let [db-ident (get @all-idents ident)] {:db/ident db-ident} (let [m @@ -306,7 +306,7 @@ (assoc page-m :block/uuid (common-uuid/gen-uuid :journal-page-uuid date-int) :block/journal-day date-int))) - (assoc :block/tags :logseq.class/Journal))] + (assoc :block/tags #{:logseq.class/Journal}))] {:block (-> block (assoc :logseq.task/deadline [:block/uuid (:block/uuid deadline-page)]) @@ -1299,8 +1299,8 @@ (d/transact! conn tx' {::new-graph? true}) (catch :default e (js/console.error e) - (prn :db (ldb/write-transit-str @conn)) - (prn :tx (ldb/write-transit-str tx')) + ;; (prn :db (ldb/write-transit-str @conn)) + ;; (prn :tx (ldb/write-transit-str tx')) (throw e))) upstream-properties-tx @@ -1318,7 +1318,11 @@ {:keys [notify-user set-ui-state export-file] :or {set-ui-state (constantly nil) export-file (fn export-file [conn m opts] - (add-file-to-db-graph conn (:file/path m) (:file/content m) opts))} + (try + (add-file-to-db-graph conn (:file/path m) (:file/content m) opts) + (catch :default e + (js/console.error e) + (prn :debug "failed to parse " (:file/path m)))))} :as options}] ;; (prn :export-doc-file path idx) (-> (p/let [_ (set-ui-state [:graph/importing-state :current-idx] (inc idx)) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 73cdc855d7b..402473f4546 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -32,15 +32,18 @@ (->> content (d/q '[:find [(pull ?b [*]) ...] :in $ ?pattern - :where [?b :block/title ?content] - [(missing? $ ?b :block/tags)] + :where + [?b :block/title ?content] + [?b :block/page] [(re-find ?pattern ?content)]] db) first) (->> content (d/q '[:find [(pull ?b [*]) ...] :in $ ?content - :where [?b :block/title ?content] [(missing? $ ?b :block/tags)]] + :where + [?b :block/title ?content] + [?b :block/page]] db) first))) @@ -64,12 +67,7 @@ first)) (defn- find-page-by-name [db name] - (->> name - (d/q '[:find [(pull ?b [*]) ...] - :in $ ?name - :where [?b :block/title ?name]] - db) - first)) + (ldb/get-page db name)) (defn- build-graph-files "Given a file graph directory, return all files including assets and adds relative paths @@ -157,7 +155,8 @@ [k (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] (if (= :block/tags k) - (mapv #(:db/ident (d/entity db (:db/id %))) v) + (->> (mapv #(:db/ident (d/entity db (:db/id %))) v) + (remove #{:logseq.class/Tag :logseq.class/Property})) (if (db-property-type/all-ref-property-types built-in-type) (db-property/ref->property-value-contents db v) v)) @@ -272,12 +271,14 @@ set)) "Block with properties has correct refs") - (is (= {:user.property/prop-num2 10} + (is (= {:user.property/prop-num2 10 + :block/tags [:logseq.class/Page]} (readable-properties @conn (find-page-by-name @conn "new page"))) "New page has correct properties") (is (= {:user.property/prop-bool true :user.property/prop-num 5 - :user.property/prop-string "yeehaw"} + :user.property/prop-string "yeehaw" + :block/tags [:logseq.class/Page]} (readable-properties @conn (find-page-by-name @conn "some page"))) "Existing page has correct properties") @@ -361,7 +362,7 @@ (:db/ident (find-page-by-name @conn "life"))) "Namespaced tag's ident has hierarchy to make it unique") - (is (= ["Tag"] + (is (= ["Tag" "Page"] (d/q '[:find [?t-title ...] :where [?b :block/name "life"] @@ -445,7 +446,7 @@ (is (= #{:logseq.property/description :user.property/description} (set (d/q '[:find [?ident ...] :where [?b :db/ident ?ident] [?b :block/name "description"]] @conn))) "user description property is separate from built-in one") - (is (= #{"Page" "Class"} + (is (= #{"Page" "Tag"} (set (d/q '[:find [?t-title ...] :where [?b :block/tags ?t] [?b :block/name "task"] @@ -549,7 +550,6 @@ files (mapv #(node-path/join file-graph-dir %) ["journals/2024_02_07.md" "pages/Interstellar.md"]) conn (db-test/create-conn) _ (import-files-to-db files conn {:tag-classes ["movie"]})] - (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") diff --git a/src/test/frontend/worker/handler/page/db_based/page_test.cljs b/src/test/frontend/worker/handler/page/db_based/page_test.cljs index 13ff732dd63..fd3e4584e94 100644 --- a/src/test/frontend/worker/handler/page/db_based/page_test.cljs +++ b/src/test/frontend/worker/handler/page/db_based/page_test.cljs @@ -44,7 +44,7 @@ "Child class with new parent has correct parents") (worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true}) - (is (= #{"Class" "Page"} + (is (= #{"tag" "Page"} (set (d/q '[:find [?tag-title ...] :where [?b :block/title "class1"] From 916a3e5bb1db2c84e7fd15f6bcaf072909349105 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 21:24:40 +0800 Subject: [PATCH 033/105] fix: lint --- .../outliner/src/logseq/outliner/validate.cljs | 1 - .../test/logseq/outliner/pipeline_test.cljs | 18 +++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index bd88048004e..be4349b31e1 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -6,7 +6,6 @@ [logseq.db :as ldb] [logseq.common.date :as common-date] [logseq.common.util.namespace :as ns-util] - [datascript.impl.entity :as de] [clojure.set :as set])) (defn ^:api validate-page-title-characters diff --git a/deps/outliner/test/logseq/outliner/pipeline_test.cljs b/deps/outliner/test/logseq/outliner/pipeline_test.cljs index d096dda27f1..be7dc7fbc00 100644 --- a/deps/outliner/test/logseq/outliner/pipeline_test.cljs +++ b/deps/outliner/test/logseq/outliner/pipeline_test.cljs @@ -50,15 +50,15 @@ updated-blocks (->> (get-blocks @conn) ;; Only keep enough of content to uniquely identify block (map #(hash-map :block/title (re-find #"\w+" (:block/title %)) - :path-ref-names (set (map :block/name (:block/path-refs %))))))] - (let [page-tag-refs #{"tags" "page"}] - (is (= [{:block/title "parent" - :path-ref-names (set/union page-tag-refs #{"page1" "bar"})} - {:block/title "child" - :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz"})} - {:block/title "grandchild" - :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz" "bing"})}] - updated-blocks)))))) + :path-ref-names (set (map :block/name (:block/path-refs %)))))) + page-tag-refs #{"tags" "page"}] + (is (= [{:block/title "parent" + :path-ref-names (set/union page-tag-refs #{"page1" "bar"})} + {:block/title "child" + :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz"})} + {:block/title "grandchild" + :path-ref-names (set/union page-tag-refs #{"page1" "bar" "baz" "bing"})}] + updated-blocks))))) (deftest block-content-refs (let [conn (db-test/create-conn-with-blocks From c6c925d40e95dd5b87aa3f6733feb257e5d80d57 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 22:08:51 +0800 Subject: [PATCH 034/105] fix: exporter whiteboards tests --- deps/graph-parser/src/logseq/graph_parser/exporter.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index c1963444e68..c7e49735bb1 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -1221,8 +1221,9 @@ (->> pages ;; migrate previous attribute for :block/title (map #(-> % - (assoc :block/title (:block/original-name %)) - (dissoc :block/original-name)))))) + (assoc :block/title (or (:block/original-name %) (:block/title %)) + :block/tags #{:logseq.class/Whiteboard}) + (dissoc :block/type :block/original-name)))))) (update :blocks update-whiteboard-blocks format)) :else From 1269d1c2778b9e990c241f85ba009f33e67da2b6 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 22:19:26 +0800 Subject: [PATCH 035/105] fix: more tests --- .../test/logseq/graph_parser/exporter_test.cljs | 12 ++++++++---- .../test/logseq/graph_parser/extract_test.cljs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 402473f4546..86a3798af27 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -201,10 +201,14 @@ (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn)))) ;; Don't count pages like url.md that have properties but no content - (is (= 10 - (count (->> (d/q '[:find [(pull ?b [:block/title :block/tags]) ...] - :where [?b :block/title] [_ :block/page ?b] (not [?b :logseq.property/built-in?])] @conn) - (filter ldb/internal-page?)))) + (is (= 11 + (count (->> (d/q '[:find [?b ...] + :where + [?b :block/title] + [_ :block/page ?b] + (not [?b :logseq.property/built-in?])] @conn) + (filter (fn [id] + (ldb/internal-page? (d/entity @conn id))))))) "Correct number of pages with block content") (is (= 11 (->> @conn (d/q '[:find [?ident ...] diff --git a/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs b/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs index c7936b3a1c3..7936e924814 100644 --- a/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs @@ -145,6 +145,6 @@ page (first pages)] (is (= (get-in page [:block/file :file/path]) "/whiteboards/foo.edn")) (is (= (:block/name page) "foo")) - (is (ldb/whiteboard? page)) + (is (= (:block/type page) "whiteboard")) (is (= (:block/title page) "Foo")) (is (every? #(= (:block/parent %) [:block/uuid #uuid "a846e3b4-c41d-4251-80e1-be6978c36d8c"]) blocks)))) From 380628650e398df3e4e484c077de889de24dd9de Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 9 Dec 2024 23:22:36 +0800 Subject: [PATCH 036/105] fix: more tests --- deps/db/src/logseq/db/sqlite/util.cljs | 2 +- .../src/logseq/graph_parser/exporter.cljs | 15 +++++++++------ .../test/logseq/graph_parser/exporter_test.cljs | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/util.cljs b/deps/db/src/logseq/db/sqlite/util.cljs index 071f1dbbc2a..e5f14a10257 100644 --- a/deps/db/src/logseq/db/sqlite/util.cljs +++ b/deps/db/src/logseq/db/sqlite/util.cljs @@ -118,7 +118,7 @@ (cond-> {:block/format :markdown} (not= (:db/ident block) :logseq.class/Tag) - (assoc :block/tags #{:logseq.class/Tag}))) + (assoc :block/tags (set (conj (:block/tags block) :logseq.class/Tag))))) (and (not= (:db/ident block) :logseq.class/Root) (nil? (:logseq.property/parent block))) (assoc :logseq.property/parent :logseq.class/Root)))) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index c7e49735bb1..b059655b0d7 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -75,14 +75,16 @@ {:db/ident db-ident} (let [m (if (:block/namespace class-block) - ;; Give namespaced tags a unique ident so they don't conflict with other tags - (-> (db-class/build-new-class db {:block/title (build-class-ident-name class-name)}) + ;; Give namespaced tags a unique ident so they don't conflict with other tags + (-> (db-class/build-new-class db {:block/title (build-class-ident-name class-name) + :block/tags (:block/tags class-block)}) (merge {:block/title class-name :block/name (common-util/page-name-sanity-lc class-name)}) (build-new-namespace-page)) (db-class/build-new-class db - {:block/title class-name - :block/name (common-util/page-name-sanity-lc class-name)}))] + (assoc {:block/title class-name + :block/name (common-util/page-name-sanity-lc class-name)} + :block/tags (:block/tags class-block))))] (swap! all-idents assoc ident (:db/ident m)) (with-meta m {:new-class? true})))))) @@ -704,8 +706,9 @@ (seq classes-from-properties) ;; Add a map of {:block.temp/new-class TAG} to be processed later (update :block/tags - (fnil into []) - (map #(hash-map :block.temp/new-class %) classes-from-properties))) + (fn [tags] + (let [tags' (if (sequential? tags) tags (set tags))] + (into tags' (map #(hash-map :block.temp/new-class %) classes-from-properties)))))) :properties-tx pvalues-tx}) {:block block :properties-tx []}) (update :block dissoc :block/properties :block/properties-text-values :block/properties-order :block/invalid-properties))) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 86a3798af27..4e4279e8f81 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -617,8 +617,8 @@ (is (nil? (find-page-by-name @conn "type")) "No page exists for configured property") - (is (= [:user.class/Property] - (:block/tags (readable-properties @conn (find-page-by-name @conn "url")))) + (is (= #{:user.class/Property :logseq.class/Page} + (set (:block/tags (readable-properties @conn (find-page-by-name @conn "url"))))) "tagged page has configured tag imported as a class")))) (deftest-async export-files-with-remove-inline-tags From 87caca8c680ba982fb6dba3dcd467fd6491a7881 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 9 Dec 2024 22:57:08 -0500 Subject: [PATCH 037/105] fix: some imported pages were missing Page class Also fix error reporting for users wasn't working. Also fix lint and add another attribute to linter --- .../src/logseq/graph_parser/exporter.cljs | 20 ++++++-------- .../logseq/graph_parser/exporter_test.cljs | 26 +++++++++++-------- .../logseq/graph_parser/extract_test.cljs | 3 +-- .../logseq/tasks/dev/db_and_file_graphs.clj | 3 ++- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index b059655b0d7..66d226140fe 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -202,6 +202,12 @@ (cond-> block true (update :block/tags convert-tags-to-classes db per-file-state user-options all-idents) + ;; ensure pages are a Page + true + (update :block/tags (fn [tags] + (if (seq (set/intersection (set tags) #{:logseq.class/Page :logseq.class/Journal :logseq.class/Whiteboard :logseq.class/Property})) + tags + (conj (vec tags) :logseq.class/Page)))) (seq page-tags) (merge {:logseq.property/page-tags page-tags}))) block)) @@ -1299,13 +1305,7 @@ tx' (common-util/fast-remove-nils tx) ;; _ (prn :tx-counts (map count (vector whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx))) ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :tx tx'})) - main-tx-report (try - (d/transact! conn tx' {::new-graph? true}) - (catch :default e - (js/console.error e) - ;; (prn :db (ldb/write-transit-str @conn)) - ;; (prn :tx (ldb/write-transit-str tx')) - (throw e))) + main-tx-report (d/transact! conn tx' {::new-graph? true}) upstream-properties-tx (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn) @@ -1322,11 +1322,7 @@ {:keys [notify-user set-ui-state export-file] :or {set-ui-state (constantly nil) export-file (fn export-file [conn m opts] - (try - (add-file-to-db-graph conn (:file/path m) (:file/content m) opts) - (catch :default e - (js/console.error e) - (prn :debug "failed to parse " (:file/path m)))))} + (add-file-to-db-graph conn (:file/path m) (:file/content m) opts))} :as options}] ;; (prn :export-doc-file path idx) (-> (p/let [_ (set-ui-state [:graph/importing-state :current-idx] (inc idx)) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 4e4279e8f81..6286d6afa2d 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -156,7 +156,8 @@ (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] (if (= :block/tags k) (->> (mapv #(:db/ident (d/entity db (:db/id %))) v) - (remove #{:logseq.class/Tag :logseq.class/Property})) + (remove #{:logseq.class/Tag :logseq.class/Property}) + vec) (if (db-property-type/all-ref-property-types built-in-type) (db-property/ref->property-value-contents db v) v)) @@ -201,14 +202,17 @@ (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn)))) ;; Don't count pages like url.md that have properties but no content + ;; TODO: Fix behavior leading to different count (is (= 11 - (count (->> (d/q '[:find [?b ...] - :where - [?b :block/title] - [_ :block/page ?b] - (not [?b :logseq.property/built-in?])] @conn) - (filter (fn [id] - (ldb/internal-page? (d/entity @conn id))))))) + (->> (d/q '[:find [?b ...] + :where + [?b :block/title] + [_ :block/page ?b] + (not [?b :logseq.property/built-in?])] @conn) + (map #(d/entity @conn %)) + (filter ldb/internal-page?) + #_(map #(select-keys % [:block/title :block/tags])) + count)) "Correct number of pages with block content") (is (= 11 (->> @conn (d/q '[:find [?ident ...] @@ -542,8 +546,8 @@ (is (and tag-page (not (ldb/class? tag-page))) "tag page is not a class") - (is (= {:logseq.property/page-tags #{"Movie"}} - (readable-properties @conn tagged-page)) + (is (= #{"Movie"} + (:logseq.property/page-tags (readable-properties @conn tagged-page))) "tagged page has existing page imported as a tag to page-tags") (is (= #{"LargeLanguageModel" "fun" "ai"} (:logseq.property/page-tags (readable-properties @conn (find-page-by-name @conn "chat-gpt")))) @@ -571,7 +575,7 @@ (is (and another-tag-page (not (ldb/class? another-tag-page))) "unconfigured tag page is not a class") - (is (= {:block/tags [:user.class/Movie]} + (is (= {:block/tags [:logseq.class/Page :user.class/Movie]} (readable-properties @conn (find-page-by-name @conn "Interstellar"))) "tagged page has configured tag imported as a class")))) diff --git a/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs b/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs index 7936e924814..0eb2f2e3a93 100644 --- a/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/extract_test.cljs @@ -2,8 +2,7 @@ (:require [cljs.test :refer [deftest is are]] [logseq.graph-parser.extract :as extract] [datascript.core :as d] - [logseq.db.frontend.schema :as db-schema] - [logseq.db :as ldb])) + [logseq.db.frontend.schema :as db-schema])) ;; This is a copy of frontend.util.fs/multiplatform-reserved-chars for reserved chars testing (def multiplatform-reserved-chars ":\\*\\?\"<>|\\#\\\\") diff --git a/scripts/src/logseq/tasks/dev/db_and_file_graphs.clj b/scripts/src/logseq/tasks/dev/db_and_file_graphs.clj index 0c3509c4eb4..b16a18dcc19 100644 --- a/scripts/src/logseq/tasks/dev/db_and_file_graphs.clj +++ b/scripts/src/logseq/tasks/dev/db_and_file_graphs.clj @@ -91,7 +91,8 @@ (let [file-concepts (->> ;; from logseq.db.frontend.schema [:block/namespace :block/properties-text-values :block/pre-block :recent/pages :block/file :block/properties-order - :block/repeated :block/deadline :block/scheduled :block/priority :block/marker :block/macros] + :block/repeated :block/deadline :block/scheduled :block/priority :block/marker :block/macros + :block/type] (map str) (into [;; e.g. block/properties :title "block/properties :" From a2a08ef64713dffd80faa68c04e07f3b517a54cd Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 15:53:56 +0800 Subject: [PATCH 038/105] fix: graph parser tests --- .../src/logseq/graph_parser/exporter.cljs | 24 +++++++++++++++++-- .../logseq/graph_parser/exporter_test.cljs | 5 ++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 66d226140fe..507c3a21656 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -1246,6 +1246,25 @@ [(:block/name %) (date-time-util/journal-day->ms journal-day)])) (into {}))) +(defn- remove-alias-if-equals-to-page-name + [pages] + (let [[page alias] (some (fn [page] (when (and (:block/namespace page) (:block/alias page)) + [page (:block/alias page)])) pages) + page-last-part (when page (common-util/page-name-sanity-lc (ns-util/get-last-part (:block/name page)))) + self-alias? (and (seq alias) (some #(= page-last-part (:block/name %)) alias))] + (if self-alias? + (->> pages + (remove (fn [p] + (= (:block/name p) page-last-part))) + (map (fn [p] + (if (= (:block/uuid p) (:block/uuid page)) + (-> page + (dissoc :block/alias) + (update :block/properties dissoc :alias) + (update :block/properties-text-values dissoc :alias)) + p)))) + pages))) + (defn add-file-to-db-graph "Parse file and save parsed data to the given db graph. Options available: @@ -1263,11 +1282,12 @@ :as *options}] (let [options (assoc *options :notify-user notify-user :log-fn log-fn) {:keys [pages blocks]} (extract-pages-and-blocks @conn file content options) + pages' (remove-alias-if-equals-to-page-name pages) tx-options (merge (build-tx-options options) - {:journal-created-ats (build-journal-created-ats pages)}) + {:journal-created-ats (build-journal-created-ats pages')}) old-properties (keys @(get-in options [:import-state :property-schemas])) ;; Build page and block txs - {:keys [pages-tx page-properties-tx per-file-state existing-pages]} (build-pages-tx conn pages blocks tx-options) + {:keys [pages-tx page-properties-tx per-file-state existing-pages]} (build-pages-tx conn pages' blocks tx-options) whiteboard-pages (->> pages-tx ;; support old and new whiteboards (filter ldb/whiteboard?) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 6286d6afa2d..22484bd0842 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -201,9 +201,8 @@ (is (= 3 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn)))) (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn)))) - ;; Don't count pages like url.md that have properties but no content - ;; TODO: Fix behavior leading to different count - (is (= 11 + ;; `y`, `url` and `tool` have text or url blocks + (is (= 12 (->> (d/q '[:find [?b ...] :where [?b :block/title] From 2a373113075f723694dc726fcdb0f085f32fb980 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 16:08:55 +0800 Subject: [PATCH 039/105] fix: disallow removing #journal --- deps/db/src/logseq/db.cljs | 1 + deps/db/src/logseq/db/frontend/entity_util.cljs | 7 ++++++- src/main/frontend/components/block.cljs | 4 ++-- src/main/frontend/components/property/value.cljs | 2 +- src/main/frontend/db/model.cljs | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index af2d652e55b..6406d7867bb 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -87,6 +87,7 @@ (def public-built-in-property? db-property/public-built-in-property?) (def get-entity-types entity-util/get-entity-types) (def internal-tags entity-util/internal-tags) +(def type-tags entity-util/type-tags) (defn sort-by-order [blocks] diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 6d077788617..8248d1d3825 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -2,7 +2,8 @@ "Lower level entity util fns used across db namespaces" (:require [datascript.core :as d] [clojure.string :as string] - [datascript.impl.entity :as de]) + [datascript.impl.entity :as de] + [clojure.set :as set]) (:refer-clojure :exclude [object?])) (defn db-based-graph? @@ -84,3 +85,7 @@ (def internal-tags #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root :logseq.class/Asset}) + +(def type-tags + (set/union internal-tags + #{:logseq.class/Journal :logseq.class/Whiteboard})) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index f20f59d048d..678cd5ff064 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2618,7 +2618,7 @@ :tag? true :disable-preview? true) tag) - (when-not (ldb/internal-tags (:db/ident tag)) + (when-not (ldb/type-tags (:db/ident tag)) [:a.close.flex.transition-opacity.duration-300.ease-in {:class (if @*hover? "!opacity-100" "!opacity-0") :title "Remove this tag" @@ -2655,7 +2655,7 @@ (fn [] (for [tag block-tags] [:div.flex.flex-row.items-center.gap-1 - (when-not (ldb/internal-tags (:db/ident tag)) + (when-not (ldb/type-tags (:db/ident tag)) (shui/button {:title "Remove tag" :variant :ghost diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 47719bd70e0..0ff669a1481 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -975,7 +975,7 @@ *el (rum/use-ref nil) items (cond->> (if (de/entity? v) #{v} v) (= (:db/ident property) :block/tags) - (remove (fn [v] (contains? ldb/internal-tags (:db/ident v)))))] + (remove (fn [v] (contains? ldb/type-tags (:db/ident v)))))] (rum/use-effect! (fn [] (when editing? diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index ddf8bd1438b..4dee8c0f9df 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -806,7 +806,7 @@ independent of format as format specific heading characters are stripped" (map (fn [d] (db-utils/entity db (:e d)))) (remove (fn [d] - (contains? entity-util/internal-tags (:db/ident d)))))] + (contains? entity-util/type-tags (:db/ident d)))))] (if except-root-class? (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes) classes))) From d4b6a3af88cf2462240b8279e965a47ce55d4120 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 16:15:11 +0800 Subject: [PATCH 040/105] fix: remove hidden pages from views --- src/main/frontend/db/model.cljs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index 4dee8c0f9df..8d93b560a37 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -826,13 +826,15 @@ independent of format as format specific heading characters are stripped" (defn get-class-objects [repo class-id] (when-let [class (db-utils/entity repo class-id)] - (if (first (:logseq.property/_parent class)) ; has children classes - (let [all-classes (conj (->> (get-structured-children repo class-id) - (map #(db-utils/entity repo %))) - class)] - (->> (mapcat :block/_tags all-classes) - distinct)) - (:block/_tags class)))) + (->> + (if (first (:logseq.property/_parent class)) ; has children classes + (let [all-classes (conj (->> (get-structured-children repo class-id) + (map #(db-utils/entity repo %))) + class)] + (->> (mapcat :block/_tags all-classes) + distinct)) + (:block/_tags class)) + (remove ldb/hidden?)))) (defn sub-class-objects [repo class-id] @@ -853,7 +855,8 @@ independent of format as format specific heading characters are stripped" (rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value] {:deps rules/rules-dependencies}) (:db/ident property)) - (map #(db-utils/entity repo %))))) + (map #(db-utils/entity repo %)) + (remove ldb/hidden?)))) (defn get-all-namespace-relation [repo] From 7d961ca915d480127cd20d57074d070c4547818c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 17:08:19 +0800 Subject: [PATCH 041/105] fix: disallow setting internal type tags to any block --- deps/outliner/src/logseq/outliner/property.cljs | 15 +++++++++++++-- deps/outliner/src/logseq/outliner/validate.cljs | 1 - src/main/frontend/handler/db_based/page.cljs | 17 ++++++----------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 461c2bb6d14..e5dff3cc1a0 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -38,7 +38,7 @@ multiple-values-empty? (and (sequential? old-value) (contains? (set (map :db/ident old-value)) :logseq.property/empty-placeholder)) block' (assoc (outliner-core/block-with-updated-at {:db/id (:db/id block)}) - property-id value) + property-id value) block-tx-data (cond-> block' (and status? (not (ldb/class-instance? (d/entity @conn :logseq.class/Task) block))) (assoc :block/tags :logseq.class/Task))] @@ -184,6 +184,13 @@ [id] (if (uuid? id) [:block/uuid id] id)) +(defn- check-internal-tag-usage + [conn property-id v] + (when (and (= property-id :block/tags) (ldb/type-tags (:db/ident (d/entity @conn v)))) + (throw (ex-info (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) + {:property-id property-id + :v v})))) + (defn- raw-set-block-property! "Adds the raw property pair (value not modified) to the given block if the property value is valid" [conn block property property-type new-value] @@ -281,10 +288,13 @@ _ (assert (qualified-keyword? property-id) "property-id should be a keyword") block (d/entity @conn block-eid) db-attribute? (some? (db-schema/schema-for-db-based-graph property-id))] - (if db-attribute? + (check-internal-tag-usage conn property-id v) + (cond + db-attribute? (when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself (ldb/transact! conn [{:db/id (:db/id block) property-id v}] {:outliner-op :save-block})) + :else (let [property (d/entity @conn property-id) _ (assert (some? property) (str "Property " property-id " doesn't exist yet")) property-type (get-in property [:block/schema :type] :default) @@ -301,6 +311,7 @@ [conn block-ids property-id v] (assert property-id "property-id is nil") (throw-error-if-read-only-property property-id) + (check-internal-tag-usage conn property-id v) (let [block-eids (map ->eid block-ids) property (d/entity @conn property-id) _ (when (= (:db/ident property) :logseq.property/parent) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index be4349b31e1..13403f24075 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -99,7 +99,6 @@ - Page names of other types are unique for their type e.g. their can be #Journal ('class') and Journal ('page') - Property names are unique and don't consider built-in property names" [db new-title entity] - (assert (ldb/page? entity) "`entity` is not a page") (validate-unique-for-page db new-title entity)) (defn ^:api validate-disallow-page-with-journal-name diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index cf8c36b7437..461ad75b70b 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -4,9 +4,9 @@ [frontend.db :as db] [frontend.handler.editor :as editor-handler] [frontend.handler.common.page :as page-common-handler] + [frontend.handler.db-based.property :as db-property-handler] [frontend.handler.notification :as notification] [frontend.state :as state] - [frontend.modules.outliner.ui :as ui-outliner-tx] [logseq.outliner.validate :as outliner-validate] [logseq.db.frontend.class :as db-class] [logseq.common.util :as common-util] @@ -33,16 +33,11 @@ (throw e))))) (defn add-tag [repo block-id tag-entity] - (ui-outliner-tx/transact! - {:outliner-op :save-block} - (p/do! - (editor-handler/save-current-block!) - ;; Check after save-current-block to get most up to date block content - (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) - (let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)] - ;; TODO: Move this to outliner.core to consistently add refs for tags - [:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]] - (db/transact! repo tx-data {:outliner-op :save-block})))))) + (p/do! + (editor-handler/save-current-block!) + ;; Check after save-current-block to get most up to date block content + (when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity) + (db-property-handler/set-block-property! block-id :block/tags (:db/id tag-entity))))) (defn convert-to-tag! [page-entity] From 7f80b5c3659b4e46a6283fe8bcb1499bbe0b24cb Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 17:08:19 +0800 Subject: [PATCH 042/105] chore: move `block-unique-title` to handler --- src/main/frontend/components/block.cljs | 3 +- src/main/frontend/components/cmdk/core.cljs | 6 ++-- src/main/frontend/components/container.cljs | 6 ++-- src/main/frontend/components/editor.cljs | 4 +-- .../frontend/components/property/value.cljs | 3 +- src/main/frontend/components/title.cljs | 30 ------------------- src/main/frontend/handler/block.cljs | 28 ++++++++++++++++- 7 files changed, 37 insertions(+), 43 deletions(-) delete mode 100644 src/main/frontend/components/title.cljs diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 678cd5ff064..8245f6b83cb 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -20,7 +20,6 @@ [frontend.components.query :as query] [frontend.components.query.builder :as query-builder-component] [frontend.components.svg :as svg] - [frontend.components.title :as title] [frontend.components.select :as select] [frontend.config :as config] [frontend.context.i18n :refer [t]] @@ -722,7 +721,7 @@ (->elem :span (map-inline config label)) show-unique-title? - (title/block-unique-title page-entity) + (block-handler/block-unique-title page-entity) :else (let [title (:block/title page-entity) diff --git a/src/main/frontend/components/cmdk/core.cljs b/src/main/frontend/components/cmdk/core.cljs index 1955fc6ff0f..89005c2c0dc 100644 --- a/src/main/frontend/components/cmdk/core.cljs +++ b/src/main/frontend/components/cmdk/core.cljs @@ -4,7 +4,6 @@ [electron.ipc :as ipc] [frontend.components.block :as block] [frontend.components.cmdk.list-item :as list-item] - [frontend.components.title :as title] [frontend.config :as config] [frontend.context.i18n :refer [t]] [frontend.db :as db] @@ -18,6 +17,7 @@ [frontend.handler.page :as page-handler] [frontend.handler.route :as route-handler] [frontend.handler.whiteboard :as whiteboard-handler] + [frontend.handler.block :as block-handler] [frontend.mixins :as mixins] [frontend.modules.shortcut.core :as shortcut] [frontend.modules.shortcut.utils :as shortcut-utils] @@ -235,7 +235,7 @@ "whiteboard" :else "page") - title (title/block-unique-title page) + title (block-handler/block-unique-title page) title' (if source-page (str title " -> alias: " (:block/title source-page)) title)] (hash-map :icon icon :icon-theme :gray @@ -245,7 +245,7 @@ (defn- block-item [repo block current-page !input] (let [id (:block/uuid block) - text (title/block-unique-title block) + text (block-handler/block-unique-title block) icon "letter-n"] {:icon icon :icon-theme :gray diff --git a/src/main/frontend/components/container.cljs b/src/main/frontend/components/container.cljs index b4a8cecd6de..9380b903879 100644 --- a/src/main/frontend/components/container.cljs +++ b/src/main/frontend/components/container.cljs @@ -15,7 +15,6 @@ [frontend.components.block :as block] [dommy.core :as d] [frontend.components.content :as cp-content] - [frontend.components.title :as title] [frontend.config :as config] [frontend.context.i18n :refer [t]] [frontend.db :as db] @@ -32,6 +31,7 @@ [frontend.handler.user :as user-handler] [frontend.handler.whiteboard :as whiteboard-handler] [frontend.handler.recent :as recent-handler] + [frontend.handler.block :as block-handler] [frontend.mixins :as mixins] [frontend.mobile.action-bar :as action-bar] [frontend.mobile.footer :as footer] @@ -133,7 +133,7 @@ :class "w-60"}}) (util/stop e))} (ldb/object? page) - (assoc :title (title/block-unique-title page))) + (assoc :title (block-handler/block-unique-title page))) [:span.page-icon.ml-3.justify-center icon] [:span.page-title {:class (when untitled? "opacity-50") :style {:display "ruby"}} @@ -206,7 +206,7 @@ (for [page pages] [:li.recent-item.select-none {:key (str "recent-" (:db/id page)) - :title (title/block-unique-title page) + :title (block-handler/block-unique-title page) :draggable true :on-drag-start (fn [event] (editor-handler/block->data-transfer! (:block/name page) event true)) :data-ref name} diff --git a/src/main/frontend/components/editor.cljs b/src/main/frontend/components/editor.cljs index 8fe43764cd0..7bbd6453b10 100644 --- a/src/main/frontend/components/editor.cljs +++ b/src/main/frontend/components/editor.cljs @@ -6,7 +6,6 @@ [frontend.components.file-based.datetime :as datetime-comp] [frontend.components.search :as search] [frontend.components.svg :as svg] - [frontend.components.title :as title] [frontend.config :as config] [frontend.context.i18n :refer [t]] [frontend.date :as date] @@ -19,6 +18,7 @@ [frontend.handler.paste :as paste-handler] [frontend.handler.property.util :as pu] [frontend.handler.search :as search-handler] + [frontend.handler.block :as block-handler] [frontend.mixins :as mixins] [frontend.search :refer [fuzzy-search]] [frontend.state :as state] @@ -215,7 +215,7 @@ (if (ldb/class? target) (str (:block/title block) " -> alias: " (:block/title target)) (:block/title block))) - (title/block-unique-title block))] + (block-handler/block-unique-title block))] (search-handler/highlight-exact-query title q))]])) :empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 (if db-tag? "Search for a tag" diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 0ff669a1481..8b6c35c0c97 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -5,7 +5,6 @@ [dommy.core :as d] [frontend.components.icon :as icon-component] [frontend.components.select :as select] - [frontend.components.title :as title] [frontend.config :as config] [frontend.date :as date] [frontend.db :as db] @@ -515,7 +514,7 @@ [header label] (if (integer? id) (let [node-title (if (seq (:property/schema.classes property)) (:block/title node) - (title/block-unique-title node)) + (block-handler/block-unique-title node)) title (subs node-title 0 256) node (or (db/entity id) node) icon (get-node-icon node) diff --git a/src/main/frontend/components/title.cljs b/src/main/frontend/components/title.cljs deleted file mode 100644 index b3c16591c6c..00000000000 --- a/src/main/frontend/components/title.cljs +++ /dev/null @@ -1,30 +0,0 @@ -(ns frontend.components.title - (:require [clojure.string :as string] - [frontend.db :as db] - [logseq.db :as ldb] - [datascript.impl.entity :as de])) - -(defn block-unique-title - "Multiple pages/objects may have the same `:block/title`. - Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients." - [block] - (let [block-e (cond - (de/entity? block) - block - (uuid? (:block/uuid block)) - (db/entity [:block/uuid (:block/uuid block)]) - :else - block) - tags (remove (fn [t] (some-> (:block/raw-title block-e) (ldb/inline-tag? t))) - (map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))] - (if (and (seq tags) - (not (ldb/journal? block))) - (str (:block/title block) - " " - (string/join - ", " - (keep (fn [tag] - (when-let [title (:block/title tag)] - (str "#" title))) - tags))) - (:block/title block)))) diff --git a/src/main/frontend/handler/block.cljs b/src/main/frontend/handler/block.cljs index 7e8a88f87b6..0e914f1204c 100644 --- a/src/main/frontend/handler/block.cljs +++ b/src/main/frontend/handler/block.cljs @@ -22,7 +22,8 @@ [frontend.handler.property.util :as pu] [dommy.core :as dom] [goog.object :as gobj] - [promesa.core :as p])) + [promesa.core :as p] + [datascript.impl.entity :as de])) ;; Fns @@ -188,6 +189,31 @@ (-> (property-util/remove-built-in-properties format content) (drawer/remove-logbook)))) +(defn block-unique-title + "Multiple pages/objects may have the same `:block/title`. + Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients." + [block] + (let [block-e (cond + (de/entity? block) + block + (uuid? (:block/uuid block)) + (db/entity [:block/uuid (:block/uuid block)]) + :else + block) + tags (remove (fn [t] (some-> (:block/raw-title block-e) (ldb/inline-tag? t))) + (map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))] + (if (and (seq tags) + (not (ldb/journal? block))) + (str (:block/title block) + " " + (string/join + ", " + (keep (fn [tag] + (when-let [title (:block/title tag)] + (str "#" title))) + tags))) + (:block/title block)))) + (defn edit-block! [block pos & {:keys [_container-id custom-content tail-len save-code-editor?] :or {tail-len 0 From 953f846b8cb14fdd2e92d4ee80c8dadd30bddbe6 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 17:23:50 +0800 Subject: [PATCH 043/105] enhance: don't show type tags in unique title --- src/main/frontend/handler/block.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/handler/block.cljs b/src/main/frontend/handler/block.cljs index 0e914f1204c..e2edea7e2b2 100644 --- a/src/main/frontend/handler/block.cljs +++ b/src/main/frontend/handler/block.cljs @@ -200,10 +200,11 @@ (db/entity [:block/uuid (:block/uuid block)]) :else block) - tags (remove (fn [t] (some-> (:block/raw-title block-e) (ldb/inline-tag? t))) + tags (remove (fn [t] + (or (some-> (:block/raw-title block-e) (ldb/inline-tag? t)) + (ldb/type-tags (:db/ident t)))) (map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))] - (if (and (seq tags) - (not (ldb/journal? block))) + (if (seq tags) (str (:block/title block) " " (string/join From aa8226aab5f48d8b5214f61e55cbf347886b7781 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 20:39:39 +0800 Subject: [PATCH 044/105] fix: root tag page crash --- src/main/frontend/components/block.cljs | 6 ++++-- src/main/frontend/components/icon.cljs | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 8245f6b83cb..eefe5adef58 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -700,9 +700,11 @@ (when (and show-icon? (not tag?)) (let [own-icon (get page-entity (pu/get-pid :logseq.property/icon)) emoji? (and (map? own-icon) (= (:type own-icon) :emoji))] - (when own-icon + (when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true + :not-text-or-page? true + :own-icon? true})] [:span {:class (str "icon-emoji-wrap " (when emoji? "as-emoji"))} - own-icon]))) + icon]))) [:span (if (and (coll? children) (seq children)) (for [child children] diff --git a/src/main/frontend/components/icon.cljs b/src/main/frontend/components/icon.cljs index 699d5e61ade..2284d2ae08b 100644 --- a/src/main/frontend/components/icon.cljs +++ b/src/main/frontend/components/icon.cljs @@ -61,7 +61,9 @@ (defn get-node-icon-cp [node-entity opts] (let [opts' (merge {:size 14} opts) - node-icon (get-node-icon node-entity)] + node-icon (if (:own-icon? opts) + (get node-entity (pu/get-pid :logseq.property/icon)) + (get-node-icon node-entity))] (when-not (or (string/blank? node-icon) (and (contains? #{"letter-n" "page"} node-icon) (:not-text-or-page? opts))) [:div.icon-cp-container.flex.items-center (merge {:style {:color (or (:color node-icon) "inherit")}} From 8c19526e44a4c324067fb0795003afd900301462 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 20:57:20 +0800 Subject: [PATCH 045/105] fix: transact class db idents first in db migration --- src/main/frontend/worker/db/migrate.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index 6f79d8786b4..7b43702ce7e 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -520,7 +520,7 @@ [47 {:fix replace-hidden-type-with-schema}] [48 {:properties [:logseq.property/default-value :logseq.property/scalar-default-value]}] [49 {:fix replace-special-id-ref-with-id-ref}] - [50 {:classes [:logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard :logseq.class/Property]}] + [50 {:classes [:logseq.class/Property :logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard]}] [51 {:fix replace-block-type-with-tags}]]) (let [max-schema-version (apply max (map first schema-version->updates))] @@ -547,9 +547,12 @@ (into {}) (#(sqlite-create-graph/build-initial-classes* % (zipmap properties properties))) (map (fn [b] (assoc b :logseq.property/built-in? true)))) + new-class-idents (keep (fn [class] + (when-let [db-ident (:db/ident class)] + {:db/ident db-ident})) new-classes) fixes (when (fn? fix) (fix conn search-db)) - tx-data (if db-based? (concat new-properties new-classes fixes) fixes) + tx-data (if db-based? (concat new-class-idents new-properties new-classes fixes) fixes) tx-data' (concat [(sqlite-util/kv :logseq.kv/schema-version version)] tx-data)] From 066b50589d500cdb333961d1c48e598ca183179c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 10 Dec 2024 10:15:10 -0500 Subject: [PATCH 046/105] fix: notify user when rejecting internal tag In some contexts silent failure will seem like a bug as new users may try to accidentally use or create some of these --- deps/outliner/src/logseq/outliner/property.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index e5dff3cc1a0..75adfb643e7 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -188,8 +188,11 @@ [conn property-id v] (when (and (= property-id :block/tags) (ldb/type-tags (:db/ident (d/entity @conn v)))) (throw (ex-info (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) - {:property-id property-id - :v v})))) + {:type :notification + :payload {:message (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) + :type :error} + :property-id property-id + :property-value v})))) (defn- raw-set-block-property! "Adds the raw property pair (value not modified) to the given block if the property value is valid" From 78aef6136015c151f5221d64b3d83ca9aa3ffdb8 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Dec 2024 23:51:06 +0800 Subject: [PATCH 047/105] Add electron-devtools-installer --- resources/package.json | 3 +- static/yarn.lock | 78 +++++++++++++++++++++++++++++------------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/resources/package.json b/resources/package.json index b3d04503a02..ea5e003bbaf 100644 --- a/resources/package.json +++ b/resources/package.json @@ -57,7 +57,8 @@ "@electron/rebuild": "3.2.10", "electron": "31.7.5", "electron-builder": "^22.11.7", - "electron-forge-maker-appimage": "https://github.com/logseq/electron-forge-maker-appimage.git" + "electron-forge-maker-appimage": "https://github.com/logseq/electron-forge-maker-appimage.git", + "electron-devtools-installer": "^3.2.0" }, "resolutions": { "**/electron": "31.7.5", diff --git a/static/yarn.lock b/static/yarn.lock index 9acde9de9e2..e18fcd83bcf 100644 --- a/static/yarn.lock +++ b/static/yarn.lock @@ -860,14 +860,14 @@ integrity sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ== "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.1": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76" - integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" @@ -1898,9 +1898,9 @@ duplexer3@^0.1.4: integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== ejs@^3.1.6: - version "3.1.8" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" - integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" @@ -1939,7 +1939,7 @@ electron-deeplink@1.0.10: electron-log "^4.2.3" node-addon-api "^2.0.0" -electron-devtools-installer@3.2.0: +electron-devtools-installer@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz#acc48d24eb7033fe5af284a19667e73b78d406d0" integrity sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ== @@ -2708,9 +2708,9 @@ global-agent@^3.0.0: serialize-error "^7.0.1" global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== dependencies: ini "2.0.0" @@ -4193,7 +4193,7 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -plist@^3.0.0, plist@^3.0.1, plist@^3.0.4, plist@^3.0.5: +plist@^3.0.0, plist@^3.0.4, plist@^3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== @@ -4201,7 +4201,7 @@ plist@^3.0.0, plist@^3.0.1, plist@^3.0.4, plist@^3.0.5: base64-js "^1.5.1" xmlbuilder "^15.1.1" -plist@^3.1.0: +plist@^3.0.1, plist@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== @@ -4649,18 +4649,28 @@ semver@7.5.2: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" +semver@^7.3.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@^7.3.8: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" @@ -4881,8 +4891,7 @@ stream-buffers@~2.2.0: resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3, string-width@^5.0.0, string-width@^5.1.2: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4891,6 +4900,15 @@ stream-buffers@~2.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3, string-width@^5.0.0, string-width@^5.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -4905,8 +4923,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: - name strip-ansi-cjs +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5315,8 +5339,16 @@ word-wrap@^1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^6.2.0, wrap-ansi@^7.0.0, wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: - name wrap-ansi-cjs +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^6.2.0, wrap-ansi@^7.0.0, wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 9cac93c4527d7e2c95f92fa8827987a44e876b3c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 00:13:17 +0800 Subject: [PATCH 048/105] fix: page? and whiteboard? should work for file graphs --- .../src/logseq/db/frontend/entity_util.cljs | 21 ++++++--- src/main/frontend/worker/db/migrate.cljs | 45 ++++++++++--------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 8248d1d3825..4344e02c7c7 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -35,7 +35,11 @@ (defn whiteboard? "Given a page entity or map, check if it is a whiteboard page" [entity] - (has-tag? entity :logseq.class/Whiteboard)) + (or + ;; db based graph + (has-tag? entity :logseq.class/Whiteboard) + ;; file based graph + (= "whiteboard" (:block/type entity)))) (defn closed-value? [entity] @@ -48,11 +52,16 @@ (defn page? [entity] - (or (internal-page? entity) - (class? entity) - (property? entity) - (whiteboard? entity) - (journal? entity))) + (or + ;; db based graph + (internal-page? entity) + (class? entity) + (property? entity) + (whiteboard? entity) + (journal? entity) + + ;; file based graph + (contains? #{"page" "journal" "whiteboard"} (:block/type entity)))) (defn asset? "Given an entity or map, check if it is an asset block" diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index 7b43702ce7e..21b6bbc06dd 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -563,30 +563,31 @@ "Migrate 'frontend' datascript schema and data. To add a new migration, add an entry to schema-version->updates and bump db-schema/version" [conn search-db] - (let [db @conn - version-in-db (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0)] - (cond - (= version-in-db db-schema/version) - nil + (when (ldb/db-based-graph? @conn) + (let [db @conn + version-in-db (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0)] + (cond + (= version-in-db db-schema/version) + nil - (< db-schema/version version-in-db) ; outdated client, db version could be synced from server + (< db-schema/version version-in-db) ; outdated client, db version could be synced from server ;; FIXME: notify users to upgrade to the latest version asap - nil - - (> db-schema/version version-in-db) - (try - (let [db-based? (ldb/db-based-graph? @conn) - updates (keep (fn [[v updates]] - (when (and (< version-in-db v) (<= v db-schema/version)) - [v updates])) - schema-version->updates)] - (println "DB schema migrated from" version-in-db) - (doseq [[v m] updates] - (upgrade-version! conn search-db db-based? v m))) - (catch :default e - (prn :error (str "DB migration failed to migrate to " db-schema/version " from " version-in-db ":")) - (js/console.error e) - (throw e)))))) + nil + + (> db-schema/version version-in-db) + (try + (let [db-based? (ldb/db-based-graph? @conn) + updates (keep (fn [[v updates]] + (when (and (< version-in-db v) (<= v db-schema/version)) + [v updates])) + schema-version->updates)] + (println "DB schema migrated from" version-in-db) + (doseq [[v m] updates] + (upgrade-version! conn search-db db-based? v m))) + (catch :default e + (prn :error (str "DB migration failed to migrate to " db-schema/version " from " version-in-db ":")) + (js/console.error e) + (throw e))))))) ;; Backend migrations ;; ================== From d1052601d8923a2825572929036b0d87e1195f08 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 00:46:41 +0800 Subject: [PATCH 049/105] fix: keep electron-devtools-installer in devDependencies --- resources/package.json | 3 +-- static/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/package.json b/resources/package.json index ea5e003bbaf..e4f42b73e38 100644 --- a/resources/package.json +++ b/resources/package.json @@ -43,8 +43,7 @@ "posthog-js": "1.10.2", "semver": "7.5.2", "socks-proxy-agent": "8.0.2", - "update-electron-app": "2.0.1", - "electron-devtools-installer": "3.2.0" + "update-electron-app": "2.0.1" }, "devDependencies": { "@electron-forge/cli": "^7.3.1", diff --git a/static/yarn.lock b/static/yarn.lock index e18fcd83bcf..4729bcb00b3 100644 --- a/static/yarn.lock +++ b/static/yarn.lock @@ -1939,7 +1939,7 @@ electron-deeplink@1.0.10: electron-log "^4.2.3" node-addon-api "^2.0.0" -electron-devtools-installer@^3.2.0: +electron-devtools-installer@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz#acc48d24eb7033fe5af284a19667e73b78d406d0" integrity sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ== From 71d4e02703e1f804fad980f66fa4384603b2bdfa Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 10 Dec 2024 12:01:07 -0500 Subject: [PATCH 050/105] chore: yarn.lock update after last commit --- static/yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/yarn.lock b/static/yarn.lock index 4729bcb00b3..e18fcd83bcf 100644 --- a/static/yarn.lock +++ b/static/yarn.lock @@ -1939,7 +1939,7 @@ electron-deeplink@1.0.10: electron-log "^4.2.3" node-addon-api "^2.0.0" -electron-devtools-installer@3.2.0: +electron-devtools-installer@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz#acc48d24eb7033fe5af284a19667e73b78d406d0" integrity sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ== From ee6fd77c4ab96e1777b7c797b27d89e682e793d4 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 10 Dec 2024 12:02:32 -0500 Subject: [PATCH 051/105] enhance(dev): reuse test helper from graph-parser with other deps Also bring back page test helper which doesn't have file graph assumptions --- deps/db/src/logseq/db/test/helper.cljs | 37 ++++- .../logseq/graph_parser/exporter_test.cljs | 129 +++++++----------- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/deps/db/src/logseq/db/test/helper.cljs b/deps/db/src/logseq/db/test/helper.cljs index c8ef1990381..7b8e984ec11 100644 --- a/deps/db/src/logseq/db/test/helper.cljs +++ b/deps/db/src/logseq/db/test/helper.cljs @@ -5,13 +5,38 @@ [logseq.db.sqlite.create-graph :as sqlite-create-graph] [logseq.db.frontend.schema :as db-schema])) -(defn find-block-by-content [db content] - (->> content - (d/q '[:find [(pull ?b [*]) ...] - :in $ ?content - :where [?b :block/title ?content]] +(defn find-block-by-content + "Find first block by exact block string or by fuzzier regex" + [db content] + (if (instance? js/RegExp content) + (->> content + (d/q '[:find [(pull ?b [*]) ...] + :in $ ?pattern + :where + [?b :block/title ?content] + [?b :block/page] + [(re-find ?pattern ?content)]] + db) + first) + (->> content + (d/q '[:find [(pull ?b [*]) ...] + :in $ ?content + :where + [?b :block/title ?content] + [?b :block/page]] + db) + first))) + +(defn find-page-by-title + "Find first page by its title" + [db title] + (->> title + (d/q '[:find [?b ...] + :in $ ?title + :where [?b :block/title ?title]] db) - first)) + first + (d/entity db))) (defn create-conn "Create a conn for a DB graph seeded with initial data" diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 22484bd0842..70fddf2fa90 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -27,26 +27,6 @@ ;; ======= ;; some have been copied from db-import script -(defn- find-block-by-content [db content] - (if (instance? js/RegExp content) - (->> content - (d/q '[:find [(pull ?b [*]) ...] - :in $ ?pattern - :where - [?b :block/title ?content] - [?b :block/page] - [(re-find ?pattern ?content)]] - db) - first) - (->> content - (d/q '[:find [(pull ?b [*]) ...] - :in $ ?content - :where - [?b :block/title ?content] - [?b :block/page]] - db) - first))) - (defn- extract-rules [rules] (rules/extract-rules rules/db-query-dsl-rules @@ -66,9 +46,6 @@ db property property-value (extract-rules [:property])) first)) -(defn- find-page-by-name [db name] - (ldb/get-page db name)) - (defn- build-graph-files "Given a file graph directory, return all files including assets and adds relative paths on ::rpath since paths are absolute by default and exporter needs relative paths for @@ -268,11 +245,11 @@ (is (= {:user.property/prop-bool true :user.property/prop-num 5 :user.property/prop-string "woot"} - (update-vals (db-property/properties (find-block-by-content @conn "b1")) + (update-vals (db-property/properties (db-test/find-block-by-content @conn "b1")) (fn [v] (if (map? v) (db-property/ref->property-value-content @conn v) v)))) "Basic block has correct properties") (is (= #{"prop-num" "prop-string" "prop-bool"} - (->> (d/entity @conn (:db/id (find-block-by-content @conn "b1"))) + (->> (d/entity @conn (:db/id (db-test/find-block-by-content @conn "b1"))) :block/refs (map :block/title) set)) @@ -280,60 +257,60 @@ (is (= {:user.property/prop-num2 10 :block/tags [:logseq.class/Page]} - (readable-properties @conn (find-page-by-name @conn "new page"))) + (readable-properties @conn (db-test/find-page-by-title @conn "new page"))) "New page has correct properties") (is (= {:user.property/prop-bool true :user.property/prop-num 5 :user.property/prop-string "yeehaw" :block/tags [:logseq.class/Page]} - (readable-properties @conn (find-page-by-name @conn "some page"))) + (readable-properties @conn (db-test/find-page-by-title @conn "some page"))) "Existing page has correct properties") (is (= {:user.property/rating 5.5} - (readable-properties @conn (find-block-by-content @conn ":rating float"))) + (readable-properties @conn (db-test/find-block-by-content @conn ":rating float"))) "Block with float property imports as a float")) (testing "built-in properties" - (is (= [(:db/id (find-block-by-content @conn "original block"))] - (mapv :db/id (:block/refs (find-block-by-content @conn #"ref to")))) + (is (= [(:db/id (db-test/find-block-by-content @conn "original block"))] + (mapv :db/id (:block/refs (db-test/find-block-by-content @conn #"ref to")))) "block with a block-ref has correct :block/refs") - (let [b (find-block-by-content @conn #"MEETING TITLE")] + (let [b (db-test/find-block-by-content @conn #"MEETING TITLE")] (is (= {} (and b (readable-properties @conn b))) ":template properties are ignored to not invalidate its property types")) (is (= {:logseq.task/deadline "Nov 26th, 2022"} - (readable-properties @conn (find-block-by-content @conn "only deadline"))) + (readable-properties @conn (db-test/find-block-by-content @conn "only deadline"))) "deadline block has correct journal as property value") (is (= {:logseq.task/deadline "Nov 25th, 2022"} - (readable-properties @conn (find-block-by-content @conn "only scheduled"))) + (readable-properties @conn (db-test/find-block-by-content @conn "only scheduled"))) "scheduled block converted to correct deadline") (is (= {:logseq.task/priority "High"} - (readable-properties @conn (find-block-by-content @conn "high priority"))) + (readable-properties @conn (db-test/find-block-by-content @conn "high priority"))) "priority block has correct property") (is (= {:logseq.task/status "Doing" :logseq.task/priority "Medium" :block/tags [:logseq.class/Task]} - (readable-properties @conn (find-block-by-content @conn "status test"))) + (readable-properties @conn (db-test/find-block-by-content @conn "status test"))) "status block has correct task properties and class") (is (= #{:logseq.task/status :block/tags} - (set (keys (readable-properties @conn (find-block-by-content @conn "old todo block"))))) + (set (keys (readable-properties @conn (db-test/find-block-by-content @conn "old todo block"))))) "old task properties like 'todo' are ignored") (is (= {:logseq.property/order-list-type "number"} - (readable-properties @conn (find-block-by-content @conn "list one"))) + (readable-properties @conn (db-test/find-block-by-content @conn "list one"))) "numered block has correct property") (is (= #{"gpt"} - (:block/alias (readable-properties @conn (find-page-by-name @conn "chat-gpt")))) + (:block/alias (readable-properties @conn (db-test/find-page-by-title @conn "chat-gpt")))) "alias set correctly") (is (= {:logseq.property.linked-references/includes #{"Oct 9th, 2024"} :logseq.property.linked-references/excludes #{"ref2"}} - (select-keys (readable-properties @conn (find-page-by-name @conn "chat-gpt")) + (select-keys (readable-properties @conn (db-test/find-page-by-title @conn "chat-gpt")) [:logseq.property.linked-references/excludes :logseq.property.linked-references/includes])) "linked ref filters set correctly")) @@ -347,26 +324,26 @@ (readable-properties @conn (find-block-by-property-value @conn :logseq.property/query "(property :prop-string)"))) "simple query block has correct query properties") (is (= "For example, here's a query with title text:" - (:block/title (find-block-by-content @conn #"query with title text"))) + (:block/title (db-test/find-block-by-content @conn #"query with title text"))) "Text around a simple query block is set as a query's title") (is (= {:logseq.property.view/type "List View" :logseq.property/query "{:query (task todo doing)}" :block/tags [:logseq.class/Query] :logseq.property.table/ordered-columns [:block/title]} - (readable-properties @conn (find-block-by-content @conn #"tasks with"))) + (readable-properties @conn (db-test/find-block-by-content @conn #"tasks with"))) "Advanced query has correct query properties") (is (= "tasks with todo and doing" - (:block/title (find-block-by-content @conn #"tasks with"))) + (:block/title (db-test/find-block-by-content @conn #"tasks with"))) "Advanced query has custom title migrated") ;; Cards (is (= {:block/tags [:logseq.class/Card]} - (readable-properties @conn (find-block-by-content @conn "card 1"))) + (readable-properties @conn (db-test/find-block-by-content @conn "card 1"))) "None of the card properties are imported since they are deprecated")) (testing "tags convert to classes" (is (= :user.class/Quotes___life - (:db/ident (find-page-by-name @conn "life"))) + (:db/ident (db-test/find-page-by-title @conn "life"))) "Namespaced tag's ident has hierarchy to make it unique") (is (= ["Tag" "Page"] @@ -378,7 +355,7 @@ "When a class is used and referenced on the same page, there should only be one instance of it") (is (= ["life"] - (->> (:block/tags (find-block-by-content @conn #"with namespace tag")) + (->> (:block/tags (db-test/find-block-by-content @conn #"with namespace tag")) (mapv #(db-property/ref->property-value-contents @conn %)))) "Block tagged with namespace tag is only associated with leaf child tag")) @@ -391,25 +368,25 @@ (is (= [{:parent "n1" :child "x"} {:parent "x" :child "z"} {:parent "x" :child "y"}] - (rest (expand-children (d/entity @conn (:db/id (find-page-by-name @conn "n1"))) nil))) + (rest (expand-children (d/entity @conn (:db/id (db-test/find-page-by-title @conn "n1"))) nil))) "First namespace tests duplicate parent page name") (is (= [{:parent "n2" :child "x"} {:parent "x" :child "z"} {:parent "n2" :child "alias"}] - (rest (expand-children (d/entity @conn (:db/id (find-page-by-name @conn "n2"))) nil))) + (rest (expand-children (d/entity @conn (:db/id (db-test/find-page-by-title @conn "n2"))) nil))) "First namespace tests duplicate child page name and built-in page name"))) (testing "journal timestamps" (is (= (date-time-util/journal-day->ms 20240207) - (:block/created-at (find-page-by-name @conn "Feb 7th, 2024"))) + (:block/created-at (db-test/find-page-by-title @conn "Feb 7th, 2024"))) "journal pages are created on their journal day") (is (= (date-time-util/journal-day->ms 20240207) - (:block/created-at (find-block-by-content @conn #"Inception"))) + (:block/created-at (db-test/find-block-by-content @conn #"Inception"))) "journal blocks are created on their page's journal day")) (testing "db attributes" (is (= true - (:block/collapsed? (find-block-by-content @conn "collapsed block"))) + (:block/collapsed? (db-test/find-block-by-content @conn "collapsed block"))) "Collapsed blocks are imported")) (testing "property :type changes" @@ -424,7 +401,7 @@ (get-in (d/entity @conn :user.property/description) [:block/schema :type])) ":default property to :node (or any non :default value) remains :default") (is (= "[[Jakob]]" - (:user.property/description (readable-properties @conn (find-block-by-content @conn #":default to :node")))) + (:user.property/description (readable-properties @conn (db-test/find-block-by-content @conn #":default to :node")))) ":default to :node property saves :default property value default with full text") (testing "with changes to upstream/existing property value" @@ -432,19 +409,19 @@ (get-in (d/entity @conn :user.property/duration) [:block/schema :type])) ":number property to :default value changes to :default") (is (= "20" - (:user.property/duration (readable-properties @conn (find-block-by-content @conn "existing :number to :default")))) + (:user.property/duration (readable-properties @conn (db-test/find-block-by-content @conn "existing :number to :default")))) "existing :number property value correctly saved as :default") (is (= {:block/schema {:type :default} :db/cardinality :db.cardinality/many} (select-keys (d/entity @conn :user.property/people) [:block/schema :db/cardinality])) ":node property to :default value changes to :default and keeps existing cardinality") (is (= #{"[[Jakob]] [[Gabriel]]"} - (:user.property/people (readable-properties @conn (find-block-by-content @conn ":node people")))) + (:user.property/people (readable-properties @conn (db-test/find-block-by-content @conn ":node people")))) "existing :node property value correctly saved as :default with full text") (is (= #{"[[Gabriel]] [[Jakob]]"} - (:user.property/people (readable-properties @conn (find-block-by-content @conn #"pending block for :node")))) + (:user.property/people (readable-properties @conn (db-test/find-block-by-content @conn #"pending block for :node")))) "pending :node property value correctly saved as :default with full text") - (is (some? (find-page-by-name @conn "Jakob")) + (is (some? (db-test/find-page-by-title @conn "Jakob")) "Previous :node property value still exists") (is (= 3 (count (find-block-by-property @conn :user.property/people))) "Converted property has correct number of property values"))) @@ -461,12 +438,12 @@ "user page is separate from built-in class")) (testing "multiline blocks" - (is (= "|markdown| table|\n|some|thing|" (:block/title (find-block-by-content @conn #"markdown.*table")))) - (is (= "multiline block\na 2nd\nand a 3rd" (:block/title (find-block-by-content @conn #"multiline block")))) - (is (= "logbook block" (:block/title (find-block-by-content @conn #"logbook block"))))) + (is (= "|markdown| table|\n|some|thing|" (:block/title (db-test/find-block-by-content @conn #"markdown.*table")))) + (is (= "multiline block\na 2nd\nand a 3rd" (:block/title (db-test/find-block-by-content @conn #"multiline block")))) + (is (= "logbook block" (:block/title (db-test/find-block-by-content @conn #"logbook block"))))) (testing ":block/refs and :block/path-refs" - (let [page (find-page-by-name @conn "chat-gpt")] + (let [page (db-test/find-page-by-title @conn "chat-gpt")] (is (set/subset? #{"type" "LargeLanguageModel"} (->> page :block/refs (map #(:block/title (d/entity @conn (:db/id %)))) set)) @@ -476,7 +453,7 @@ (->> page :block/path-refs (map #(:block/title (d/entity @conn (:db/id %)))) set)) "Page has correct property and property value :block/path-refs")) - (let [block (find-block-by-content @conn "old todo block")] + (let [block (db-test/find-block-by-content @conn "old todo block")] (is (set/subset? #{:logseq.task/status :logseq.class/Task} (->> block @@ -493,7 +470,7 @@ "Block has correct task tag and property :block/path-refs"))) (testing "whiteboards" - (let [block-with-props (find-block-by-content @conn #"block with props")] + (let [block-with-props (db-test/find-block-by-content @conn #"block with props")] (is (= {:user.property/prop-num 10} (readable-properties @conn block-with-props))) (is (= "block with props" (:block/title block-with-props))))))) @@ -519,7 +496,7 @@ (testing "replacing refs in :block/title when :remove-inline-tags? set" (is (= 2 - (->> (find-block-by-content @conn #"replace with same start string") + (->> (db-test/find-block-by-content @conn #"replace with same start string") :block/title (re-seq db-content/id-ref-pattern) distinct @@ -527,7 +504,7 @@ "A block with ref names that start with same string has 2 distinct refs") (is (= 1 - (->> (find-block-by-content @conn #"replace case insensitive") + (->> (db-test/find-block-by-content @conn #"replace case insensitive") :block/title (re-seq db-content/id-ref-pattern) distinct @@ -535,9 +512,9 @@ "A block with different case of same ref names has 1 distinct ref")) (testing "tags convert to page, refs and page-tags" - (let [block (find-block-by-content @conn #"Inception") - tag-page (find-page-by-name @conn "Movie") - tagged-page (find-page-by-name @conn "Interstellar")] + (let [block (db-test/find-block-by-content @conn #"Inception") + tag-page (db-test/find-page-by-title @conn "Movie") + tagged-page (db-test/find-page-by-title @conn "Interstellar")] (is (string/starts-with? (str (:block/title block)) "Inception [[") "tagged block tag converts tag to page ref") (is (= [(:db/id tag-page)] (map :db/id (:block/refs block))) @@ -549,7 +526,7 @@ (:logseq.property/page-tags (readable-properties @conn tagged-page))) "tagged page has existing page imported as a tag to page-tags") (is (= #{"LargeLanguageModel" "fun" "ai"} - (:logseq.property/page-tags (readable-properties @conn (find-page-by-name @conn "chat-gpt")))) + (:logseq.property/page-tags (readable-properties @conn (db-test/find-page-by-title @conn "chat-gpt")))) "tagged page has new page and other pages marked with '#' and '[[]]` imported as tags to page-tags"))))) (deftest-async export-files-with-tag-classes-option @@ -560,9 +537,9 @@ (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") - (let [block (find-block-by-content @conn #"Inception") - tag-page (find-page-by-name @conn "Movie") - another-tag-page (find-page-by-name @conn "p0")] + (let [block (db-test/find-block-by-content @conn #"Inception") + tag-page (db-test/find-page-by-title @conn "Movie") + another-tag-page (db-test/find-page-by-title @conn "p0")] (is (= (:block/title block) "Inception") "tagged block with configured tag strips tag from content") (is (= [:user.class/Movie] @@ -575,7 +552,7 @@ "unconfigured tag page is not a class") (is (= {:block/tags [:logseq.class/Page :user.class/Movie]} - (readable-properties @conn (find-page-by-name @conn "Interstellar"))) + (readable-properties @conn (db-test/find-page-by-title @conn "Interstellar"))) "tagged page has configured tag imported as a class")))) (deftest-async export-files-with-property-classes-option @@ -604,8 +581,8 @@ set)) "Properties are correctly inferred for a class") - (let [block (find-block-by-content @conn #"The Creator") - tag-page (find-page-by-name @conn "Movie")] + (let [block (db-test/find-block-by-content @conn #"The Creator") + tag-page (db-test/find-page-by-title @conn "Movie")] (is (= (:block/title block) "The Creator") "tagged block with configured tag strips tag from content") (is (= [:user.class/Movie] @@ -617,11 +594,11 @@ (is (= :logseq.class/Tag (:db/ident (first (:block/tags tag-page)))) "configured tag page derived from :property-classes is a class") - (is (nil? (find-page-by-name @conn "type")) + (is (nil? (db-test/find-page-by-title @conn "type")) "No page exists for configured property") (is (= #{:user.class/Property :logseq.class/Page} - (set (:block/tags (readable-properties @conn (find-page-by-name @conn "url"))))) + (set (:block/tags (readable-properties @conn (db-test/find-page-by-title @conn "url"))))) "tagged page has configured tag imported as a class")))) (deftest-async export-files-with-remove-inline-tags @@ -632,7 +609,7 @@ (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") - (is (string/starts-with? (:block/title (find-block-by-content @conn #"Inception")) + (is (string/starts-with? (:block/title (db-test/find-block-by-content @conn #"Inception")) "Inception #Movie") "block with tag preserves inline tag"))) From afab95f8fcc99df14432e54fee3e7e3493e9beef Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 10 Dec 2024 16:20:39 -0500 Subject: [PATCH 052/105] fix: imported properties aren't both a Page and Property imported property should behave like app --- deps/graph-parser/src/logseq/graph_parser/exporter.cljs | 3 ++- .../test/logseq/graph_parser/exporter_test.cljs | 8 +++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 507c3a21656..7fb4ba8f73b 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -1156,7 +1156,8 @@ property-page-properties-tx (keep (fn [b] (when-let [page-properties (not-empty (db-property/properties b))] (merge page-properties {:block/uuid (:block/uuid b) - :block/tags (conj (:block/tags page-properties) :logseq.class/Property)}))) + :block/tags (-> (remove #(= :logseq.class/Page %) (:block/tags page-properties)) + (conj :logseq.class/Property))}))) properties-tx)] {:pages-tx pages-tx' :property-pages-tx (concat property-pages-tx converted-property-pages-tx) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 70fddf2fa90..e81defc3b4c 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -132,9 +132,7 @@ [k (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] (if (= :block/tags k) - (->> (mapv #(:db/ident (d/entity db (:db/id %))) v) - (remove #{:logseq.class/Tag :logseq.class/Property}) - vec) + (mapv #(:db/ident (d/entity db (:db/id %))) v) (if (db-property-type/all-ref-property-types built-in-type) (db-property/ref->property-value-contents db v) v)) @@ -597,9 +595,9 @@ (is (nil? (db-test/find-page-by-title @conn "type")) "No page exists for configured property") - (is (= #{:user.class/Property :logseq.class/Page} + (is (= #{:user.class/Property :logseq.class/Property} (set (:block/tags (readable-properties @conn (db-test/find-page-by-title @conn "url"))))) - "tagged page has configured tag imported as a class")))) + "tagged page has correct tags including one from option")))) (deftest-async export-files-with-remove-inline-tags (p/let [file-graph-dir "test/resources/exporter-test-graph" From 596700c6a3653a0aa06e1725314f2ee7cfb1099b Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 10 Dec 2024 17:13:03 -0500 Subject: [PATCH 053/105] fix: imported tags aren't both a Page and a Tag imported tags should behave like app --- .../src/logseq/graph_parser/exporter.cljs | 19 +++++++++++++++++-- .../logseq/graph_parser/exporter_test.cljs | 4 ++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 7fb4ba8f73b..770ee690fb6 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -1266,6 +1266,20 @@ p)))) pages))) +(defn- merge-pages-and-classes + "If a new tag is also in pages-tx, ensure that it only has one tag by removing + :logseq.class/Page from pages-tx" + [classes-tx pages-tx'] + (if (seq classes-tx) + (let [class-map (into {} (map (juxt :block/uuid :block/title) classes-tx))] + (mapv (fn [page] + (if (some-> (get class-map (:block/uuid page)) + (= (:block/title page))) + (update page :block/tags (fn [tags] (vec (remove #(= % :logseq.class/Page) tags)))) + page)) + pages-tx')) + pages-tx')) + (defn add-file-to-db-graph "Parse file and save parsed data to the given db graph. Options available: @@ -1308,8 +1322,9 @@ main-props-tx-report (d/transact! conn property-pages-tx {::new-graph? true}) classes-tx @(:classes-tx tx-options) + pages-tx'' (merge-pages-and-classes classes-tx pages-tx') ;; Build indices - pages-index (->> (map #(select-keys % [:block/uuid]) pages-tx') + pages-index (->> (map #(select-keys % [:block/uuid]) pages-tx'') (concat (map #(select-keys % [:block/uuid]) classes-tx)) distinct) block-ids (map (fn [block] {:block/uuid (:block/uuid block)}) blocks-tx) @@ -1322,7 +1337,7 @@ blocks-index (set/union (set block-ids) (set block-refs-ids)) ;; Order matters. pages-index and blocks-index needs to come before their corresponding tx for ;; uuids to be valid. Also upstream-properties-tx comes after blocks-tx to possibly override blocks - tx (concat whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx) + tx (concat whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx'' classes-tx blocks-index blocks-tx) tx' (common-util/fast-remove-nils tx) ;; _ (prn :tx-counts (map count (vector whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx))) ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :tx tx'})) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index e81defc3b4c..53a2bbec5b7 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -544,7 +544,7 @@ (:block/tags (readable-properties @conn block))) "tagged block has configured tag imported as a class") - (is (= :logseq.class/Tag (:db/ident (first (:block/tags tag-page)))) + (is (= [:logseq.class/Tag] (mapv :db/ident (:block/tags tag-page))) "configured tag page in :tag-classes is a class") (is (and another-tag-page (not (ldb/class? another-tag-page))) "unconfigured tag page is not a class") @@ -590,7 +590,7 @@ "tagged block can have another property that references the same class it is tagged with, without creating a duplicate class") - (is (= :logseq.class/Tag (:db/ident (first (:block/tags tag-page)))) + (is (= [:logseq.class/Tag] (map :db/ident (:block/tags tag-page))) "configured tag page derived from :property-classes is a class") (is (nil? (db-test/find-page-by-title @conn "type")) "No page exists for configured property") From 3fe71cc023281a04a82b285bc871f2afbe19169b Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 15:23:37 +0800 Subject: [PATCH 054/105] fix: tests --- deps/db/src/logseq/db/frontend/entity_util.cljs | 6 +++++- src/test/frontend/db/db_based_model_test.cljs | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 4344e02c7c7..6fd70c803e9 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -48,7 +48,11 @@ (defn journal? "Given a page entity or map, check if it is a journal page" [entity] - (has-tag? entity :logseq.class/Journal)) + (or + ;; db based graph + (has-tag? entity :logseq.class/Journal) + ;; file based graph + (= "journal" (:block/type entity)))) (defn page? [entity] diff --git a/src/test/frontend/db/db_based_model_test.cljs b/src/test/frontend/db/db_based_model_test.cljs index c6f7c87a02c..8df0d726753 100644 --- a/src/test/frontend/db/db_based_model_test.cljs +++ b/src/test/frontend/db/db_based_model_test.cljs @@ -5,7 +5,8 @@ [frontend.test.helper :as test-helper] [datascript.core :as d] [logseq.outliner.property :as outliner-property] - [logseq.db.frontend.class :as db-class])) + [logseq.db.frontend.class :as db-class] + [logseq.db :as ldb])) (def repo test-helper/test-db-name-db-version) @@ -27,7 +28,9 @@ _ (test-helper/create-page! "class2" opts)] (is (= (set (concat - (map :title (vals db-class/built-in-classes)) + (map :title (vals (remove (fn [[ident _]] + (contains? ldb/type-tags ident)) + db-class/built-in-classes))) ["class1" "class2"])) (set (map :block/title (model/get-all-classes repo))))))) From 06b9f6107d6b4bed5bbb99827c9173c55f9c6e0a Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 16:08:07 +0800 Subject: [PATCH 055/105] fix: tests --- deps/graph-parser/src/logseq/graph_parser/block.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/block.cljs b/deps/graph-parser/src/logseq/graph_parser/block.cljs index 84ee71356f4..f7a5de0cdc8 100644 --- a/deps/graph-parser/src/logseq/graph_parser/block.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/block.cljs @@ -394,8 +394,7 @@ (or (:block/tags page) [:logseq.class/Page]))] (assoc page :block/tags tags)) - (let [type (if class? "class" (or (:block/type page) "page"))] - (assoc page :block/type type))))))) + (assoc page :block/type (or (:block/type page) "page"))))))) (defn- db-namespace-page? "Namespace page that're not journal pages" From 02d6bcf0a4b5fc80ff1eb82678ada2e2d5b38422 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 16:53:38 +0800 Subject: [PATCH 056/105] fix: rtc db listener tests --- .../frontend/worker/rtc/db_listener_test.cljs | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/test/frontend/worker/rtc/db_listener_test.cljs b/src/test/frontend/worker/rtc/db_listener_test.cljs index b972fea07f0..c235d8feb35 100644 --- a/src/test/frontend/worker/rtc/db_listener_test.cljs +++ b/src/test/frontend/worker/rtc/db_listener_test.cljs @@ -11,7 +11,8 @@ [frontend.worker.state :as worker-state] [logseq.db.frontend.schema :as db-schema] [logseq.outliner.batch-tx :as batch-tx] - [logseq.outliner.core :as outliner-core])) + [logseq.outliner.core :as outliner-core] + [logseq.db.test.helper :as db-test])) (t/use-fixtures :each test-helper/db-based-start-and-destroy-db-map-fixture @@ -27,11 +28,11 @@ (deftest entity-datoms=>ops-test (testing "remove whiteboard page-block" - (let [conn (d/conn-from-db empty-db) + (let [conn (db-test/create-conn) block-uuid (random-uuid) _create-whiteboard-page-block (d/transact! conn [{:block/uuid block-uuid - :block/type "whiteboard" + :block/tags :logseq.class/Whiteboard :block/name "block-name" :block/title "BLOCK-NAME"}]) remove-whiteboard-page-block @@ -44,20 +45,20 @@ (map (fn [[op-type _t op-value]] [op-type op-value]) r))))) (testing "update-schema op" - (let [conn (d/conn-from-db empty-db) - tx-data [[:db/add 69 :db/index true] - [:db/add 69 :block/uuid #uuid "66558abf-6512-469d-9e83-8f1ba0be9305"] - [:db/add 69 :db/valueType :db.type/ref] - [:db/add 69 :block/updated-at 1716882111476] - [:db/add 69 :block/created-at 1716882111476] - [:db/add 69 :block/schema {:type :number}] - [:db/add 69 :block/format :markdown] - [:db/add 69 :db/cardinality :db.cardinality/one] - [:db/add 69 :db/ident :user.property/qqq] - [:db/add 69 :block/type "property"] - [:db/add 69 :block/order "b0T"] - [:db/add 69 :block/name "qqq"] - [:db/add 69 :block/title "qqq"]] + (let [conn (db-test/create-conn) + tx-data [[:db/add 1000000 :db/index true] + [:db/add 1000000 :block/uuid #uuid "66558abf-6512-469d-9e83-8f1ba0be9305"] + [:db/add 1000000 :db/valueType :db.type/ref] + [:db/add 1000000 :block/updated-at 1716882111476] + [:db/add 1000000 :block/created-at 1716882111476] + [:db/add 1000000 :block/schema {:type :number}] + [:db/add 1000000 :block/format :markdown] + [:db/add 1000000 :db/cardinality :db.cardinality/one] + [:db/add 1000000 :db/ident :user.property/qqq] + [:db/add 1000000 :block/tags :logseq.class/Property] + [:db/add 1000000 :block/order "b0T"] + [:db/add 1000000 :block/name "qqq"] + [:db/add 1000000 :block/title "qqq"]] {:keys [db-before db-after tx-data]} (d/transact! conn tx-data) ops (#'subject/entity-datoms=>ops db-before db-after (tx-data=>e->a->add?->v->t tx-data) @@ -72,10 +73,11 @@ [:block/updated-at "[\"~#'\",1716882111476]"] [:block/created-at "[\"~#'\",1716882111476]"] [:block/schema "[\"^ \",\"~:type\",\"~:number\"]"] + [:block/tags #uuid "00000002-1038-7670-4800-000000000000"] [:block/title "[\"~#'\",\"qqq\"]"] [:db/cardinality "[\"~#'\",\"~:db.cardinality/one\"]"] ;; [:db/ident "[\"~#'\",\"~:user.property/qqq\"]"] - [:block/type "[\"~#'\",\"property\"]"]]}]] + ]}]] (map (fn [[op-type _t op-value]] [op-type (cond-> op-value (:av-coll op-value) @@ -83,16 +85,16 @@ ops))))) (testing "create user-class" - (let [conn (d/conn-from-db empty-db) - tx-data [[:db/add 62 :block/uuid #uuid "66856a29-6eb3-4122-af97-8580a853c6a6" 536870954] - [:db/add 62 :block/updated-at 1720019497643 536870954] - [:db/add 62 :logseq.property/parent 4 536870954] - [:db/add 62 :block/created-at 1720019497643 536870954] - [:db/add 62 :block/format :markdown 536870954] - [:db/add 62 :db/ident :user.class/zzz 536870954] - [:db/add 62 :block/type "class" 536870954] - [:db/add 62 :block/name "zzz" 536870954] - [:db/add 62 :block/title "zzz" 536870954]] + (let [conn (db-test/create-conn) + tx-data [[:db/add 1000000 :block/uuid #uuid "66856a29-6eb3-4122-af97-8580a853c6a6" 536870954] + [:db/add 1000000 :block/updated-at 1720019497643 536870954] + [:db/add 1000000 :logseq.property/parent :logseq.class/Root 536870954] + [:db/add 1000000 :block/created-at 1720019497643 536870954] + [:db/add 1000000 :block/format :markdown 536870954] + [:db/add 1000000 :db/ident :user.class/zzz 536870954] + [:db/add 1000000 :block/tags :logseq.class/Tag 536870954] + [:db/add 1000000 :block/name "zzz" 536870954] + [:db/add 1000000 :block/title "zzz" 536870954]] {:keys [db-before db-after tx-data]} (d/transact! conn tx-data) ops (#'subject/entity-datoms=>ops db-before db-after (tx-data=>e->a->add?->v->t tx-data) @@ -103,9 +105,9 @@ :av-coll [[:block/updated-at "[\"~#'\",1720019497643]"] [:block/created-at "[\"~#'\",1720019497643]"] + [:block/tags #uuid "00000002-5389-0208-3000-000000000000"] [:block/title "[\"~#'\",\"zzz\"]"] - [:block/type "[\"~#'\",\"class\"]"] - [:logseq.property/parent "[\"~#'\",4]"] + [:logseq.property/parent #uuid "00000002-2737-8382-7000-000000000000"] ;;1. shouldn't have :db/ident, :db/ident is special, will be handled later ]}]] (map (fn [[op-type _t op-value]] From 0798f0b7f68c095b38182d3d977dd6a162183ee7 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 18:37:48 +0800 Subject: [PATCH 057/105] fix: page test --- .../frontend/worker/handler/page/db_based/page_test.cljs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/test/frontend/worker/handler/page/db_based/page_test.cljs b/src/test/frontend/worker/handler/page/db_based/page_test.cljs index fd3e4584e94..e9942873ae6 100644 --- a/src/test/frontend/worker/handler/page/db_based/page_test.cljs +++ b/src/test/frontend/worker/handler/page/db_based/page_test.cljs @@ -9,12 +9,8 @@ (let [conn (db-test/create-conn) _ (worker-db-page/create! conn "movie" {:class? true}) _ (worker-db-page/create! conn "Movie" {:class? true}) - movie-class (->> (d/q '[:find [(pull ?b [*]) ...] :in $ ?title :where [?b :block/title ?title]] - @conn "movie") - first) - Movie-class (->> (d/q '[:find [(pull ?b [*]) ...] :in $ ?title :where [?b :block/title ?title]] - @conn "Movie") - first)] + movie-class (ldb/get-case-page @conn "movie") + Movie-class (ldb/get-case-page @conn "Movie")] (is (ldb/class? movie-class) "Creates a class") (is (ldb/class? Movie-class) "Creates another class with a different case sensitive name") From 600372a17158ed5fa7109d6a3e66a2bf627b30bf Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 19:01:01 +0800 Subject: [PATCH 058/105] fix: tests d/transact doesn't accept entities. --- src/main/frontend/worker/handler/page/db_based/page.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index cc05465cffd..3f434fd36f7 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -215,7 +215,8 @@ page-txs) (build-first-block-tx (:block/uuid (first page-txs)) format)) txs (concat - parents + ;; transact doesn't support entities + (remove de/entity? parents) page-txs first-block-tx)] (when (seq txs) From 7bacc1fc5dc44dd4aa1c7cad0ad4e7f6122a43ee Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 19:17:14 +0800 Subject: [PATCH 059/105] enhance: assert not transacting entities --- deps/db/src/logseq/db.cljs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 6406d7867bb..a714e50024e 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -17,7 +17,8 @@ [logseq.db.sqlite.util :as sqlite-util] [logseq.db.frontend.property :as db-property] [logseq.common.util.namespace :as ns-util] - [logseq.common.util.page-ref :as page-ref]) + [logseq.common.util.page-ref :as page-ref] + [clojure.walk :as walk]) (:refer-clojure :exclude [object?])) (defonce *transact-fn (atom nil)) @@ -40,11 +41,24 @@ m)) tx-data))) +(defn assert-no-entities + [tx-data] + (walk/prewalk + (fn [f] + (if (de/entity? f) + (throw (ex-info "ldb/transact! doesn't support Entity" + {:entity f + :tx-data tx-data})) + f)) + tx-data)) + (defn transact! "`repo-or-conn`: repo for UI thread and conn for worker/node" ([repo-or-conn tx-data] (transact! repo-or-conn tx-data nil)) ([repo-or-conn tx-data tx-meta] + (when goog.DEBUG + (assert-no-entities tx-data)) (let [tx-data (map (fn [m] (if (map? m) (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor From 9b6d09bcfe897e1d1ca5b2100d918382c45d974c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 19:19:41 +0800 Subject: [PATCH 060/105] fix: tests --- src/test/frontend/worker/handler/page/db_based/page_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/frontend/worker/handler/page/db_based/page_test.cljs b/src/test/frontend/worker/handler/page/db_based/page_test.cljs index e9942873ae6..27691480492 100644 --- a/src/test/frontend/worker/handler/page/db_based/page_test.cljs +++ b/src/test/frontend/worker/handler/page/db_based/page_test.cljs @@ -40,7 +40,7 @@ "Child class with new parent has correct parents") (worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true}) - (is (= #{"tag" "Page"} + (is (= #{"Tag" "Page"} (set (d/q '[:find [?tag-title ...] :where [?b :block/title "class1"] From 2eb86ba253e9a557322562237b2bbd75efae3c93 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 19:25:16 +0800 Subject: [PATCH 061/105] fix: tests --- deps/db/src/logseq/db.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index a714e50024e..7452b686b01 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -57,7 +57,8 @@ ([repo-or-conn tx-data] (transact! repo-or-conn tx-data nil)) ([repo-or-conn tx-data tx-meta] - (when goog.DEBUG + (when (and (exists? js/goog) + (aget js/goog "DEBUG")) (assert-no-entities tx-data)) (let [tx-data (map (fn [m] (if (map? m) From f67d47a9db45bcd927358f1c56ba5c7269457061 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 21:20:28 +0800 Subject: [PATCH 062/105] fix: tests --- src/test/frontend/worker/rtc/remote_update_test.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/frontend/worker/rtc/remote_update_test.cljs b/src/test/frontend/worker/rtc/remote_update_test.cljs index ec9babf2ccb..cca2a10e21f 100644 --- a/src/test/frontend/worker/rtc/remote_update_test.cljs +++ b/src/test/frontend/worker/rtc/remote_update_test.cljs @@ -9,7 +9,7 @@ (deftest remote-op-value->tx-data-test (let [[block-uuid ref-uuid1 ref-uuid2] (repeatedly random-uuid) db (d/db-with (d/empty-db db-schema/schema-for-db-based-graph) - (sqlite-create-graph/build-db-initial-data ""))] + (sqlite-create-graph/build-db-initial-data "{}" {}))] (testing ":block/title" (let [db (d/db-with db [{:block/uuid block-uuid :block/title "local-content"}]) From 3a748c99751a17420be4a5b496f91efcad4f9ed0 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 21:26:01 +0800 Subject: [PATCH 063/105] fix: lint --- src/test/frontend/worker/rtc/db_listener_test.cljs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/frontend/worker/rtc/db_listener_test.cljs b/src/test/frontend/worker/rtc/db_listener_test.cljs index c235d8feb35..2fc84532503 100644 --- a/src/test/frontend/worker/rtc/db_listener_test.cljs +++ b/src/test/frontend/worker/rtc/db_listener_test.cljs @@ -9,7 +9,6 @@ [frontend.worker.rtc.db-listener :as subject] [frontend.worker.rtc.fixture :as r.fixture] [frontend.worker.state :as worker-state] - [logseq.db.frontend.schema :as db-schema] [logseq.outliner.batch-tx :as batch-tx] [logseq.outliner.core :as outliner-core] [logseq.db.test.helper :as db-test])) @@ -18,8 +17,6 @@ test-helper/db-based-start-and-destroy-db-map-fixture r.fixture/listen-test-db-to-gen-rtc-ops-fixture) -(def empty-db (d/empty-db db-schema/schema-for-db-based-graph)) - (defn- tx-data=>e->a->add?->v->t [tx-data] (let [datom-vec-coll (map vec tx-data) From 0ec09b6f0a1905efb3b048dcb1081d078cf87bf0 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Dec 2024 21:34:21 +0800 Subject: [PATCH 064/105] fix: allow #Journal, #Tag, #Property to be shown for tags property --- deps/db/src/logseq/db.cljs | 1 + deps/db/src/logseq/db/frontend/entity_util.cljs | 3 +++ src/main/frontend/components/property/value.cljs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 7452b686b01..df17bd3a775 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -103,6 +103,7 @@ (def get-entity-types entity-util/get-entity-types) (def internal-tags entity-util/internal-tags) (def type-tags entity-util/type-tags) +(def hidden-tags entity-util/hidden-tags) (defn sort-by-order [blocks] diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 6fd70c803e9..3a4df9c2c64 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -99,6 +99,9 @@ #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root :logseq.class/Asset}) +(def hidden-tags + #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset}) + (def type-tags (set/union internal-tags #{:logseq.class/Journal :logseq.class/Whiteboard})) diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 8b6c35c0c97..8b39a173646 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -974,7 +974,7 @@ *el (rum/use-ref nil) items (cond->> (if (de/entity? v) #{v} v) (= (:db/ident property) :block/tags) - (remove (fn [v] (contains? ldb/type-tags (:db/ident v)))))] + (remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))] (rum/use-effect! (fn [] (when editing? From f58f83eb38a33f050847d512af273108309e9e0b Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 11 Dec 2024 12:47:15 -0500 Subject: [PATCH 065/105] fix: more imported properties and tags that had extra :logseq.class/Page. Explains the page count change in this branch --- .../src/logseq/graph_parser/exporter.cljs | 50 +++++++++++++------ .../logseq/graph_parser/exporter_test.cljs | 41 +++++++++++---- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 770ee690fb6..e40302d1855 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -1151,6 +1151,9 @@ (merge (select-keys new-prop [:block/tags :block/schema :db/ident :db/index :db/cardinality :db/valueType]) {:block/uuid existing-page-uuid}))) (set/intersection new-properties (set (map keyword (keys existing-pages))))) + ;; Could do this only for existing pages but the added complexity isn't worth reducing the tx noise + retract-page-tag-from-properties-tx (map #(vector :db/retract [:block/uuid (:block/uuid %)] :block/tags :logseq.class/Page) + (concat property-pages-tx converted-property-pages-tx)) ;; Save properties on new property pages separately as they can contain new properties and thus need to be ;; transacted separately the property pages property-page-properties-tx (keep (fn [b] @@ -1160,7 +1163,7 @@ (conj :logseq.class/Property))}))) properties-tx)] {:pages-tx pages-tx' - :property-pages-tx (concat property-pages-tx converted-property-pages-tx) + :property-pages-tx (concat property-pages-tx converted-property-pages-tx retract-page-tag-from-properties-tx) :property-page-properties-tx property-page-properties-tx})) (defn- update-whiteboard-blocks [blocks format] @@ -1266,19 +1269,31 @@ p)))) pages))) -(defn- merge-pages-and-classes - "If a new tag is also in pages-tx, ensure that it only has one tag by removing - :logseq.class/Page from pages-tx" - [classes-tx pages-tx'] - (if (seq classes-tx) - (let [class-map (into {} (map (juxt :block/uuid :block/title) classes-tx))] - (mapv (fn [page] - (if (some-> (get class-map (:block/uuid page)) - (= (:block/title page))) - (update page :block/tags (fn [tags] (vec (remove #(= % :logseq.class/Page) tags)))) - page)) - pages-tx')) - pages-tx')) +(defn- clean-extra-invalid-tags + "If a page/class tx is an existing property or a new or existing class, ensure that + it only has one tag by removing :logseq.class/Page from its tx" + [db pages-tx' classes-tx] + ;; TODO: Improve perf if we tracked all created classes in atom + (let [existing-classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag) + (map #(d/entity db (:e %))) + (map :block/uuid) + set) + classes (set/union existing-classes + (set (map :block/uuid classes-tx))) + existing-properties (->> (d/datoms db :avet :block/tags :logseq.class/Property) + (map #(d/entity db (:e %))) + (map :block/uuid) + set)] + {:pages-tx + (mapv (fn [page] + (if (or (contains? classes (:block/uuid page)) + (contains? existing-properties (:block/uuid page))) + (update page :block/tags (fn [tags] (vec (remove #(= % :logseq.class/Page) tags)))) + page)) + pages-tx') + :retract-page-tag-from-classes-tx + (mapv #(vector :db/retract [:block/uuid (:block/uuid %)] :block/tags :logseq.class/Page) + classes-tx)})) (defn add-file-to-db-graph "Parse file and save parsed data to the given db graph. Options available: @@ -1318,11 +1333,13 @@ vec) {:keys [property-pages-tx property-page-properties-tx] pages-tx' :pages-tx} (split-pages-and-properties-tx pages-tx old-properties existing-pages (:import-state options)) + ;; _ (when (seq property-pages-tx) (cljs.pprint/pprint {:property-pages-tx property-pages-tx})) ;; Necessary to transact new property entities first so that block+page properties can be transacted next main-props-tx-report (d/transact! conn property-pages-tx {::new-graph? true}) classes-tx @(:classes-tx tx-options) - pages-tx'' (merge-pages-and-classes classes-tx pages-tx') + {:keys [retract-page-tag-from-classes-tx] pages-tx'' :pages-tx} (clean-extra-invalid-tags @conn pages-tx' classes-tx) + classes-tx' (concat classes-tx retract-page-tag-from-classes-tx) ;; Build indices pages-index (->> (map #(select-keys % [:block/uuid]) pages-tx'') (concat (map #(select-keys % [:block/uuid]) classes-tx)) @@ -1337,7 +1354,7 @@ blocks-index (set/union (set block-ids) (set block-refs-ids)) ;; Order matters. pages-index and blocks-index needs to come before their corresponding tx for ;; uuids to be valid. Also upstream-properties-tx comes after blocks-tx to possibly override blocks - tx (concat whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx'' classes-tx blocks-index blocks-tx) + tx (concat whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx'' classes-tx' blocks-index blocks-tx) tx' (common-util/fast-remove-nils tx) ;; _ (prn :tx-counts (map count (vector whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx))) ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :tx tx'})) @@ -1345,6 +1362,7 @@ upstream-properties-tx (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn) + ;; _ (when (seq upstream-properties-tx) (cljs.pprint/pprint {:upstream-properties-tx upstream-properties-tx})) upstream-tx-report (when (seq upstream-properties-tx) (d/transact! conn upstream-properties-tx {::new-graph? true}))] ;; Return all tx-reports that occurred in this fn as UI needs to know what changed diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 53a2bbec5b7..a578d08a46a 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -152,7 +152,14 @@ (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") - (is (= 0 (count @(:ignored-properties import-state))) "No ignored properties"))) + (is (= 0 (count @(:ignored-properties import-state))) "No ignored properties") + (is (= [] + (->> (d/q '[:find (pull ?b [:block/title {:block/tags [:db/ident]}]) + :where [?b :block/tags :logseq.class/Tag]] + @conn) + (map first) + (remove #(= [{:db/ident :logseq.class/Tag}] (:block/tags %))))) + "All classes only have :logseq.class/Tag as their tag (and don't have Page)"))) (deftest-async export-basic-graph-with-convert-all-tags ;; This graph will contain basic examples of different features to import @@ -176,8 +183,8 @@ (is (= 3 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn)))) (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn)))) - ;; `y`, `url` and `tool` have text or url blocks - (is (= 12 + ;; Properties and tags aren't included in this count as they aren't a Page + (is (= 10 (->> (d/q '[:find [?b ...] :where [?b :block/title] @@ -266,7 +273,15 @@ (is (= {:user.property/rating 5.5} (readable-properties @conn (db-test/find-block-by-content @conn ":rating float"))) - "Block with float property imports as a float")) + "Block with float property imports as a float") + + (is (= [] + (->> (d/q '[:find (pull ?b [:block/title {:block/tags [:db/ident]}]) + :where [?b :block/tags :logseq.class/Property]] + @conn) + (map first) + (remove #(= [{:db/ident :logseq.class/Property}] (:block/tags %))))) + "All properties only have :logseq.class/Property as their tag (and don't have Page)")) (testing "built-in properties" (is (= [(:db/id (db-test/find-block-by-content @conn "original block"))] @@ -344,18 +359,22 @@ (:db/ident (db-test/find-page-by-title @conn "life"))) "Namespaced tag's ident has hierarchy to make it unique") - (is (= ["Tag" "Page"] - (d/q '[:find [?t-title ...] - :where - [?b :block/name "life"] - [?b :block/tags ?t] - [?t :block/title ?t-title]] @conn)) + (is (= [:logseq.class/Tag] + (map :db/ident (:block/tags (db-test/find-page-by-title @conn "life")))) "When a class is used and referenced on the same page, there should only be one instance of it") (is (= ["life"] (->> (:block/tags (db-test/find-block-by-content @conn #"with namespace tag")) (mapv #(db-property/ref->property-value-contents @conn %)))) - "Block tagged with namespace tag is only associated with leaf child tag")) + "Block tagged with namespace tag is only associated with leaf child tag") + + (is (= [] + (->> (d/q '[:find (pull ?b [:block/title {:block/tags [:db/ident]}]) + :where [?b :block/tags :logseq.class/Tag]] + @conn) + (map first) + (remove #(= [{:db/ident :logseq.class/Tag}] (:block/tags %))))) + "All classes only have :logseq.class/Tag as their tag (and don't have Page)")) (testing "namespaces" (let [expand-children (fn expand-children [ent parent] From 3dff9e26c0f80b497d8a4f879a7612d6a11a2e5c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 11 Dec 2024 14:33:01 -0500 Subject: [PATCH 066/105] fix: aliasing of namespaced pages broken Also fixes docs graph warnings about ignored attributes --- .../src/logseq/graph_parser/exporter.cljs | 28 +++---------------- .../logseq/graph_parser/exporter_test.cljs | 8 ++++++ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index e40302d1855..9138576a848 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -76,8 +76,8 @@ (let [m (if (:block/namespace class-block) ;; Give namespaced tags a unique ident so they don't conflict with other tags - (-> (db-class/build-new-class db {:block/title (build-class-ident-name class-name) - :block/tags (:block/tags class-block)}) + (-> (db-class/build-new-class db (merge {:block/title (build-class-ident-name class-name)} + (select-keys class-block [:block/tags]))) (merge {:block/title class-name :block/name (common-util/page-name-sanity-lc class-name)}) (build-new-namespace-page)) @@ -1250,25 +1250,6 @@ [(:block/name %) (date-time-util/journal-day->ms journal-day)])) (into {}))) -(defn- remove-alias-if-equals-to-page-name - [pages] - (let [[page alias] (some (fn [page] (when (and (:block/namespace page) (:block/alias page)) - [page (:block/alias page)])) pages) - page-last-part (when page (common-util/page-name-sanity-lc (ns-util/get-last-part (:block/name page)))) - self-alias? (and (seq alias) (some #(= page-last-part (:block/name %)) alias))] - (if self-alias? - (->> pages - (remove (fn [p] - (= (:block/name p) page-last-part))) - (map (fn [p] - (if (= (:block/uuid p) (:block/uuid page)) - (-> page - (dissoc :block/alias) - (update :block/properties dissoc :alias) - (update :block/properties-text-values dissoc :alias)) - p)))) - pages))) - (defn- clean-extra-invalid-tags "If a page/class tx is an existing property or a new or existing class, ensure that it only has one tag by removing :logseq.class/Page from its tx" @@ -1312,12 +1293,11 @@ :as *options}] (let [options (assoc *options :notify-user notify-user :log-fn log-fn) {:keys [pages blocks]} (extract-pages-and-blocks @conn file content options) - pages' (remove-alias-if-equals-to-page-name pages) tx-options (merge (build-tx-options options) - {:journal-created-ats (build-journal-created-ats pages')}) + {:journal-created-ats (build-journal-created-ats pages)}) old-properties (keys @(get-in options [:import-state :property-schemas])) ;; Build page and block txs - {:keys [pages-tx page-properties-tx per-file-state existing-pages]} (build-pages-tx conn pages' blocks tx-options) + {:keys [pages-tx page-properties-tx per-file-state existing-pages]} (build-pages-tx conn pages blocks tx-options) whiteboard-pages (->> pages-tx ;; support old and new whiteboards (filter ldb/whiteboard?) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index a578d08a46a..01e99f6829a 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -320,6 +320,14 @@ (is (= #{"gpt"} (:block/alias (readable-properties @conn (db-test/find-page-by-title @conn "chat-gpt")))) "alias set correctly") + (is (= ["y"] + (->> (d/q '[:find [?b ...] :where [?b :block/title "y"] [?b :logseq.property/parent]] + @conn) + first + (d/entity @conn) + :block/alias + (map :block/title))) + "alias set correctly on namespaced page") (is (= {:logseq.property.linked-references/includes #{"Oct 9th, 2024"} :logseq.property.linked-references/excludes #{"ref2"}} From a42d4a8f7d07b1e62d652b70fbb4c0a82a5892a4 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 16:13:10 +0800 Subject: [PATCH 067/105] fix: can't create an Asset from the Asset page --- deps/outliner/src/logseq/outliner/property.cljs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 75adfb643e7..e42bd8f58d3 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -185,8 +185,12 @@ (if (uuid? id) [:block/uuid id] id)) (defn- check-internal-tag-usage - [conn property-id v] - (when (and (= property-id :block/tags) (ldb/type-tags (:db/ident (d/entity @conn v)))) + [conn block-eids property-id v] + (when (and (= property-id :block/tags) (ldb/type-tags (:db/ident (d/entity @conn v))) + ;; Allow assets to be tagged + (not (and + (every? (fn [id] (ldb/asset? (d/entity @conn id))) block-eids) + (= :logseq.class/Asset (:db/ident (d/entity @conn v)))))) (throw (ex-info (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) {:type :notification :payload {:message (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) @@ -291,7 +295,7 @@ _ (assert (qualified-keyword? property-id) "property-id should be a keyword") block (d/entity @conn block-eid) db-attribute? (some? (db-schema/schema-for-db-based-graph property-id))] - (check-internal-tag-usage conn property-id v) + (check-internal-tag-usage conn [block-eid] property-id v) (cond db-attribute? (when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself @@ -314,8 +318,8 @@ [conn block-ids property-id v] (assert property-id "property-id is nil") (throw-error-if-read-only-property property-id) - (check-internal-tag-usage conn property-id v) (let [block-eids (map ->eid block-ids) + _ (check-internal-tag-usage conn block-eids property-id v) property (d/entity @conn property-id) _ (when (= (:db/ident property) :logseq.property/parent) (outliner-validate/validate-parent-property From 06f31d2b00e09adeb0b4c85292b5feb2be7051a8 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 16:27:41 +0800 Subject: [PATCH 068/105] fix: disable setting parent for built-in pages --- src/main/frontend/components/property.cljs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index add8e09469f..b15e48ee03b 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -375,7 +375,8 @@ (or (contains? #{:logseq.property/query} (:db/ident m)) (and (not page?) (contains? #{:block/alias} (:db/ident m))) ;; Filters out properties from being in wrong :view-context and :never view-contexts - (and (not= view-context :all) (not (contains? block-types view-context)))))) + (and (not= view-context :all) (not (contains? block-types view-context))) + (and (ldb/built-in? block) (contains? #{:logseq.property/parent} (:db/ident m)))))) property (rum/react *property) property-key (rum/react *property-key)] [:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1 @@ -697,7 +698,10 @@ (state/set-selection-blocks! [block]) (some-> js/document.activeElement (.blur))) (d/remove-class! target "ls-popup-closed")))} - (let [properties' (remove (fn [[k _v]] (contains? #{:logseq.property/icon :logseq.property/query} k)) full-properties)] + (let [remove-properties (cond-> #{:logseq.property/icon :logseq.property/query} + (ldb/built-in? block) + (conj :logseq.property/parent)) + properties' (remove (fn [[k _v]] (contains? remove-properties k)) full-properties)] (properties-section block (if class-schema? properties properties') opts)) (rum/with-key (new-property block opts) (str id "-add-property"))]))) From f1430e2791c8eb908bc8d810ff049330df980079 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 16:49:43 +0800 Subject: [PATCH 069/105] fix(regression): Tagged nodes can't be collapsed --- src/main/frontend/components/page.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 5e7edd9c50f..3fb316ac9ea 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -575,7 +575,7 @@ (shui/tabs {:defaultValue default-tab :class (str "w-full")} - (when (or both? property?) + (when (or class? property?) [:div.flex.flex-row.gap-1.items-center.-ml-4 {:on-mouse-over #(set-control-display! true) :on-mouse-out #(set-control-display! false)} From 02888b9e6ff8331a806cb7a43e60593945630515 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 17:14:32 +0800 Subject: [PATCH 070/105] enhance(ux): allow view title to be collapsed --- src/main/frontend/components/page.cljs | 34 ++++------- src/main/frontend/components/views.cljs | 79 +++++++++++++------------ 2 files changed, 52 insertions(+), 61 deletions(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 3fb316ac9ea..ff2cb8e8d94 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -559,9 +559,7 @@ (rum/defc tabs [page opts] - (let [[collapsed? set-collapsed!] (rum/use-state false) - [control-display? set-control-display!] (rum/use-state false) - class? (ldb/class? page) + (let [class? (ldb/class? page) property? (ldb/property? page) both? (and class? property?) default-tab (cond @@ -575,16 +573,8 @@ (shui/tabs {:defaultValue default-tab :class (str "w-full")} - (when (or class? property?) - [:div.flex.flex-row.gap-1.items-center.-ml-4 - {:on-mouse-over #(set-control-display! true) - :on-mouse-out #(set-control-display! false)} - [:a - {:class (if (or control-display? collapsed?) - "opacity-50 hover:opacity-100" - "opacity-0") - :on-click #(set-collapsed! (not collapsed?))} - (rotating-arrow collapsed?)] + (when (or both? property?) + [:div.flex.flex-row.gap-1.items-center (shui/tabs-list {:class "h-8"} (when class? @@ -600,16 +590,14 @@ (when property? (db-page/configure-property page)))]) - (when-not collapsed? - [:<> - (when class? - (shui/tabs-content - {:value "tag"} - (objects/class-objects page opts))) - (when property? - (shui/tabs-content - {:value "property"} - (objects/property-related-objects page (:current-page? opts))))]))])) + (when class? + (shui/tabs-content + {:value "tag"} + (objects/class-objects page opts))) + (when property? + (shui/tabs-content + {:value "property"} + (objects/property-related-objects page (:current-page? opts)))))])) ;; A page is just a logical block (rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query mixins/container-id diff --git a/src/main/frontend/components/views.cljs b/src/main/frontend/components/views.cljs index f7ed468f133..eba8bd10e0d 100644 --- a/src/main/frontend/components/views.cljs +++ b/src/main/frontend/components/views.cljs @@ -80,11 +80,11 @@ {:variant "text" :class "h-8 !pl-4 !px-2 !py-0 hover:text-foreground w-full justify-start" :on-click #(column-toggle-sorting! column)} - (let [title (str (:name column))] - [:span {:title title - :class "max-w-full overflow-hidden text-ellipsis"} - title]) - (case asc? + (let [title (str (:name column))] + [:span {:title title + :class "max-w-full overflow-hidden text-ellipsis"} + title]) + (case asc? true (ui/icon "arrow-up") false @@ -1237,39 +1237,42 @@ [:div.flex.flex-col.gap-2.grid {:ref *view-ref} - [:div.flex.flex-wrap.items-center.justify-between.gap-1 - (when-not render-empty-title? - [:div.flex.flex-row.items-center.gap-2 - (or - views-title - [:div.font-medium.opacity-50.text-sm - (t (or title-key :views.table/default-title) - (count (:rows table)))])]) - [:div.view-actions.flex.items-center.gap-1 - - (filter-properties columns table) - - (search input {:on-change set-input! - :set-input! set-input!}) - - [:div.text-muted-foreground.text-sm - (pv/property-value view-entity (db/entity :logseq.property.view/type) - (db/entity display-type) {})] - - (more-actions columns table) - - (when add-new-object! (new-record-button table view-entity))]] - - (filters-row table) - - (case display-type - :logseq.property.view/type.list - (list-view (:config option) view-entity (:rows table)) - - :logseq.property.view/type.gallery - (gallery-view (:config option) table view-entity (:rows table) *scroller-ref) - - (table-view table option row-selection add-new-object! *scroller-ref))])) + (ui/foldable + [:div.flex.flex-1.flex-wrap.items-center.justify-between.gap-1 + (when-not render-empty-title? + [:div.flex.flex-row.items-center.gap-2 + (or + views-title + [:div.font-medium.opacity-50.text-sm + (t (or title-key :views.table/default-title) + (count (:rows table)))])]) + [:div.view-actions.flex.items-center.gap-1 + + (filter-properties columns table) + + (search input {:on-change set-input! + :set-input! set-input!}) + + [:div.text-muted-foreground.text-sm + (pv/property-value view-entity (db/entity :logseq.property.view/type) + (db/entity display-type) {})] + + (more-actions columns table) + + (when add-new-object! (new-record-button table view-entity))]] + (fn [] + [:div.ls-view-body.flex.flex-col.gap-2.grid + (filters-row table) + + (case display-type + :logseq.property.view/type.list + (list-view (:config option) view-entity (:rows table)) + + :logseq.property.view/type.gallery + (gallery-view (:config option) table view-entity (:rows table) *scroller-ref) + + (table-view table option row-selection add-new-object! *scroller-ref))]) + {:title-trigger? false})])) (rum/defcs view "Provides a view for data like query results and tagged objects, multiple From 63191e40af4e3f7a26c272c5c4375a2f4c150d5b Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 17:55:50 +0800 Subject: [PATCH 071/105] fix: style --- src/main/frontend/components/right_sidebar.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/components/right_sidebar.cljs b/src/main/frontend/components/right_sidebar.cljs index cfead54879e..ba98b4ea9b2 100644 --- a/src/main/frontend/components/right_sidebar.cljs +++ b/src/main/frontend/components/right_sidebar.cljs @@ -97,8 +97,8 @@ (let [lookup (if (integer? db-id) db-id [:block/uuid db-id]) page (db/entity repo lookup)] (if (ldb/page? page) - [[:.flex.items-center.page-title - (icon/get-node-icon-cp page {:class "text-md mr-2"}) + [[:.flex.items-center.page-title.gap-1 + (icon/get-node-icon-cp page {:class "text-md"}) [:span.overflow-hidden.text-ellipsis (:block/title page)]] (page-cp repo (str (:block/uuid page)))] (block-with-breadcrumb repo page idx [repo db-id block-type] false))) From 8e0afa80455777ecca95b798f33a84889def5e9d Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Dec 2024 21:50:43 +0800 Subject: [PATCH 072/105] fix: lint --- src/main/frontend/components/page.cljs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index ff2cb8e8d94..18d69cc20ad 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -551,12 +551,6 @@ (plugins/hook-ui-slot :page-head-actions-slotted nil) (plugins/hook-ui-items :pagebar)]))) -(rum/defc rotating-arrow - [collapsed?] - [:span - {:class (if collapsed? "rotating-arrow collapsed" "rotating-arrow not-collapsed")} - (svg/caret-right)]) - (rum/defc tabs [page opts] (let [class? (ldb/class? page) From 953d9e39cf6b1f3851a282ea4d70b36856c3d579 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 12 Dec 2024 11:03:41 -0500 Subject: [PATCH 073/105] fix: users can't change parent property of built in classes This was still possible from a couple of places so hiding parent wasn't helping --- .../outliner/src/logseq/outliner/property.cljs | 2 ++ .../outliner/src/logseq/outliner/validate.cljs | 18 ++++++++++++++++-- .../test/logseq/outliner/validate_test.cljs | 2 +- src/main/frontend/components/property.cljs | 4 +--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index e42bd8f58d3..118f3220ffd 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -296,6 +296,8 @@ block (d/entity @conn block-eid) db-attribute? (some? (db-schema/schema-for-db-based-graph property-id))] (check-internal-tag-usage conn [block-eid] property-id v) + (when (= property-id :logseq.property/parent) + (outliner-validate/validate-parent-property v [block])) (cond db-attribute? (when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index 13403f24075..f1b6c8fb11e 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -6,7 +6,8 @@ [logseq.db :as ldb] [logseq.common.date :as common-date] [logseq.common.util.namespace :as ns-util] - [clojure.set :as set])) + [clojure.set :as set] + [logseq.db.frontend.class :as db-class])) (defn ^:api validate-page-title-characters "Validates characters that must not be in a page title" @@ -118,7 +119,7 @@ (validate-unique-by-name-tag-and-block-type db new-title existing-block-entity) (validate-disallow-page-with-journal-name new-title existing-block-entity)) -(defn validate-parent-property +(defn- validate-parent-property-have-same-type "Validates whether given parent and children are valid. Allows 'class' and 'page' types to have a relationship with their own type. May consider allowing more page types if they don't cause systemic bugs" @@ -131,3 +132,16 @@ :payload {:message "Can't set this page as a parent because the child page is a different type" :type :warning} :blocks (map #(select-keys % [:db/id :block/title]) (remove ldb/class? child-ents))})))) + +(defn- validate-parent-property-disallows-built-in-class-changes + [_parent-ent child-ents] + (when (some #(get db-class/built-in-classes (:db/ident %)) child-ents) + (throw (ex-info "Can't change the parent of a built-in tag" + {:type :notification + :payload {:message "Can't change the parent of a built-in tag" + :type :warning} })))) + +(defn validate-parent-property + [parent-ent child-ents] + (validate-parent-property-disallows-built-in-class-changes parent-ent child-ents) + (validate-parent-property-have-same-type parent-ent child-ents)) \ No newline at end of file diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index 09e398ffdce..9a3bac4312b 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -136,5 +136,5 @@ (doseq [[parent-id child-id] parent-child-pairs] (let [parent (d/entity @conn parent-id) child (d/entity @conn child-id)] - (is (nil? (outliner-validate/validate-parent-property parent [child])) + (is (nil? (#'outliner-validate/validate-parent-property-have-same-type parent [child])) (str "Parent and child page is valid: " (pr-str (:block/title parent)) " " (pr-str (:block/title child)))))))))) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index b15e48ee03b..5ef5238f140 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -698,9 +698,7 @@ (state/set-selection-blocks! [block]) (some-> js/document.activeElement (.blur))) (d/remove-class! target "ls-popup-closed")))} - (let [remove-properties (cond-> #{:logseq.property/icon :logseq.property/query} - (ldb/built-in? block) - (conj :logseq.property/parent)) + (let [remove-properties #{:logseq.property/icon :logseq.property/query} properties' (remove (fn [[k _v]] (contains? remove-properties k)) full-properties)] (properties-section block (if class-schema? properties properties') opts)) From d624d5e7db26b91b9298398407a93c56791ded78 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 12 Dec 2024 16:08:52 -0500 Subject: [PATCH 074/105] fix: users cant change tags of built-in classes like Tag Could cause all kinds of bugs. Also moved tags vars to db-class to have a central ns like we do with properties and l.db.frontend.property --- deps/db/src/logseq/db.cljs | 6 +-- deps/db/src/logseq/db/frontend/class.cljs | 16 ++++++++ .../src/logseq/db/frontend/entity_util.cljs | 16 +------- .../src/logseq/outliner/property.cljs | 20 ++------- .../src/logseq/outliner/validate.cljs | 41 ++++++++++++++++--- .../test/logseq/outliner/validate_test.cljs | 28 ++++++++++++- src/main/frontend/components/block.cljs | 4 +- src/main/frontend/db/model.cljs | 3 +- src/main/frontend/handler/block.cljs | 2 +- src/test/frontend/db/db_based_model_test.cljs | 2 +- 10 files changed, 92 insertions(+), 46 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index df17bd3a775..5cedbd0ffd1 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -101,9 +101,9 @@ (def asset? entity-util/asset?) (def public-built-in-property? db-property/public-built-in-property?) (def get-entity-types entity-util/get-entity-types) -(def internal-tags entity-util/internal-tags) -(def type-tags entity-util/type-tags) -(def hidden-tags entity-util/hidden-tags) +(def internal-tags db-class/internal-tags) +(def private-tags db-class/private-tags) +(def hidden-tags db-class/hidden-tags) (defn sort-by-order [blocks] diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 00c4ec7bc7e..6f8be000d82 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -2,6 +2,7 @@ "Class related fns for DB graphs and frontend/datascript usage" (:require [logseq.db.sqlite.util :as sqlite-util] [logseq.db.frontend.db-ident :as db-ident] + [clojure.set :as set] [flatland.ordered.map :refer [ordered-map]])) (def ^:large-vars/data-var built-in-classes @@ -80,6 +81,21 @@ ;; TODO: Add more classes such as :book, :paper, :movie, :music, :project) )) +(def internal-tags + "Built-in classes that are hidden on a node and all pages view" + #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root + :logseq.class/Asset}) + +(def private-tags + "Built-in classes that are private and should not be used by a user directly. + These used to be in :block/type" + (set/union internal-tags + #{:logseq.class/Journal :logseq.class/Whiteboard})) + +(def hidden-tags + "Built-in classes that are hidden in a few contexts like property values" + #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset}) + (defn create-user-class-ident-from-name "Creates a class :db/ident for a default user namespace. NOTE: Only use this when creating a db-ident for a new class." diff --git a/deps/db/src/logseq/db/frontend/entity_util.cljs b/deps/db/src/logseq/db/frontend/entity_util.cljs index 3a4df9c2c64..b7fa28e2ef0 100644 --- a/deps/db/src/logseq/db/frontend/entity_util.cljs +++ b/deps/db/src/logseq/db/frontend/entity_util.cljs @@ -2,8 +2,7 @@ "Lower level entity util fns used across db namespaces" (:require [datascript.core :as d] [clojure.string :as string] - [datascript.impl.entity :as de] - [clojure.set :as set]) + [datascript.impl.entity :as de]) (:refer-clojure :exclude [object?])) (defn db-based-graph? @@ -93,15 +92,4 @@ :logseq.class/Journal :journal :logseq.class/Whiteboard :whiteboard :logseq.class/Page :page}] - (set (map #(ident->type (:db/ident %)) (:block/tags entity))))) - -(def internal-tags - #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root - :logseq.class/Asset}) - -(def hidden-tags - #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset}) - -(def type-tags - (set/union internal-tags - #{:logseq.class/Journal :logseq.class/Whiteboard})) + (set (map #(ident->type (:db/ident %)) (:block/tags entity))))) \ No newline at end of file diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 118f3220ffd..2a6832a4094 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -184,20 +184,6 @@ [id] (if (uuid? id) [:block/uuid id] id)) -(defn- check-internal-tag-usage - [conn block-eids property-id v] - (when (and (= property-id :block/tags) (ldb/type-tags (:db/ident (d/entity @conn v))) - ;; Allow assets to be tagged - (not (and - (every? (fn [id] (ldb/asset? (d/entity @conn id))) block-eids) - (= :logseq.class/Asset (:db/ident (d/entity @conn v)))))) - (throw (ex-info (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) - {:type :notification - :payload {:message (str "Can't set tag with internal #" (:block/title (d/entity @conn v))) - :type :error} - :property-id property-id - :property-value v})))) - (defn- raw-set-block-property! "Adds the raw property pair (value not modified) to the given block if the property value is valid" [conn block property property-type new-value] @@ -295,7 +281,8 @@ _ (assert (qualified-keyword? property-id) "property-id should be a keyword") block (d/entity @conn block-eid) db-attribute? (some? (db-schema/schema-for-db-based-graph property-id))] - (check-internal-tag-usage conn [block-eid] property-id v) + (when (= property-id :block/tags) + (outliner-validate/validate-tags-property @conn [block-eid] v)) (when (= property-id :logseq.property/parent) (outliner-validate/validate-parent-property v [block])) (cond @@ -321,7 +308,8 @@ (assert property-id "property-id is nil") (throw-error-if-read-only-property property-id) (let [block-eids (map ->eid block-ids) - _ (check-internal-tag-usage conn block-eids property-id v) + _ (when (= property-id :block/tags) + (outliner-validate/validate-tags-property @conn block-eids v)) property (d/entity @conn property-id) _ (when (= (:db/ident property) :logseq.property/parent) (outliner-validate/validate-parent-property diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index f1b6c8fb11e..082054e874c 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -1,6 +1,6 @@ (ns logseq.outliner.validate - "Reusable DB graph validations for outliner level and above. Most validations throw - errors so the user action stops immediately to display a notification" + "Reusable DB graph validations for outliner level and above. Most validations + throw errors so the user action stops immediately to display a notification" (:require [clojure.string :as string] [datascript.core :as d] [logseq.db :as ldb] @@ -133,15 +133,44 @@ :type :warning} :blocks (map #(select-keys % [:db/id :block/title]) (remove ldb/class? child-ents))})))) -(defn- validate-parent-property-disallows-built-in-class-changes +(defn- disallow-built-in-class-parent-change [_parent-ent child-ents] (when (some #(get db-class/built-in-classes (:db/ident %)) child-ents) (throw (ex-info "Can't change the parent of a built-in tag" {:type :notification :payload {:message "Can't change the parent of a built-in tag" - :type :warning} })))) + :type :warning}})))) (defn validate-parent-property [parent-ent child-ents] - (validate-parent-property-disallows-built-in-class-changes parent-ent child-ents) - (validate-parent-property-have-same-type parent-ent child-ents)) \ No newline at end of file + (disallow-built-in-class-parent-change parent-ent child-ents) + (validate-parent-property-have-same-type parent-ent child-ents)) + +(defn- disallow-node-cant-tag-with-private-tags + [db block-eids v] + (when (and (ldb/private-tags (:db/ident (d/entity db v))) + ;; Allow assets to be tagged + (not (and + (every? (fn [id] (ldb/asset? (d/entity db id))) block-eids) + (= :logseq.class/Asset (:db/ident (d/entity db v)))))) + (throw (ex-info (str "Can't set tag with built-in #" (:block/title (d/entity db v))) + {:type :notification + :payload {:message (str "Can't set tag with built-in #" (:block/title (d/entity db v))) + :type :error} + :property-id :block/tags + :property-value v})))) + +(defn- disallow-tagging-a-built-in-class + [db block-eids] + (when-let [built-in-tag + (some #(when (get db-class/built-in-classes (:db/ident %)) %) + (map #(d/entity db %) block-eids))] + (throw (ex-info (str "Can't add tag to built-in #" (:block/title built-in-tag)) + {:type :notification + :payload {:message (str "Can't add tag on built-in #" (:block/title built-in-tag)) + :type :error}})))) + +(defn validate-tags-property + [db block-eids v] + (disallow-tagging-a-built-in-class db block-eids) + (disallow-node-cant-tag-with-private-tags db block-eids v)) \ No newline at end of file diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index 9a3bac4312b..c3c3a64165e 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -99,7 +99,33 @@ class1 page1 page1 class1 property page1 - property class1)))) + property class1)) + + (testing "built-in tag can't have parent changed" + (is (thrown-with-msg? + js/Error + #"Can't change.*built-in" + (outliner-validate/validate-parent-property (d/entity @conn :logseq.class/Task) + [(d/entity @conn :logseq.class/Cards)])))))) + +(deftest validate-tags-property + (let [conn (db-test/create-conn-with-blocks + {:pages-and-blocks + [{:page {:block/title "page1"} + :blocks [{:block/title "block"}]}]}) + block (find-block-by-content conn "block")] + + (is (thrown-with-msg? + js/Error + #"Can't add tag.*Tag" + (outliner-validate/validate-tags-property @conn [:logseq.class/Tag] :logseq.class/Asset)) + ":logseq.class/Tag must not be tagged by the user") + + (is (thrown-with-msg? + js/Error + #"Can't set tag.*Page" + (outliner-validate/validate-tags-property @conn [(:db/id block)] :logseq.class/Page)) + "Nodes can't be tagged with type tags"))) ;; Try as many of the validations against a new graph to confirm ;; that validations make sense and are valid for a new graph diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index eefe5adef58..f1cb23e5142 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2619,7 +2619,7 @@ :tag? true :disable-preview? true) tag) - (when-not (ldb/type-tags (:db/ident tag)) + (when-not (ldb/private-tags (:db/ident tag)) [:a.close.flex.transition-opacity.duration-300.ease-in {:class (if @*hover? "!opacity-100" "!opacity-0") :title "Remove this tag" @@ -2656,7 +2656,7 @@ (fn [] (for [tag block-tags] [:div.flex.flex-row.items-center.gap-1 - (when-not (ldb/type-tags (:db/ident tag)) + (when-not (ldb/private-tags (:db/ident tag)) (shui/button {:title "Remove tag" :variant :ghost diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index 8d93b560a37..105dbd30a66 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -14,7 +14,6 @@ [frontend.util :as util :refer [react]] [logseq.db.frontend.rules :as rules] [logseq.db.frontend.content :as db-content] - [logseq.db.frontend.entity-util :as entity-util] [logseq.graph-parser.db :as gp-db] [logseq.common.util :as common-util] [logseq.common.util.date-time :as date-time-util] @@ -806,7 +805,7 @@ independent of format as format specific heading characters are stripped" (map (fn [d] (db-utils/entity db (:e d)))) (remove (fn [d] - (contains? entity-util/type-tags (:db/ident d)))))] + (contains? ldb/private-tags (:db/ident d)))))] (if except-root-class? (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes) classes))) diff --git a/src/main/frontend/handler/block.cljs b/src/main/frontend/handler/block.cljs index e2edea7e2b2..be8e893d415 100644 --- a/src/main/frontend/handler/block.cljs +++ b/src/main/frontend/handler/block.cljs @@ -202,7 +202,7 @@ block) tags (remove (fn [t] (or (some-> (:block/raw-title block-e) (ldb/inline-tag? t)) - (ldb/type-tags (:db/ident t)))) + (ldb/private-tags (:db/ident t)))) (map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))] (if (seq tags) (str (:block/title block) diff --git a/src/test/frontend/db/db_based_model_test.cljs b/src/test/frontend/db/db_based_model_test.cljs index 8df0d726753..bea44bb7043 100644 --- a/src/test/frontend/db/db_based_model_test.cljs +++ b/src/test/frontend/db/db_based_model_test.cljs @@ -29,7 +29,7 @@ (is (= (set (concat (map :title (vals (remove (fn [[ident _]] - (contains? ldb/type-tags ident)) + (contains? ldb/private-tags ident)) db-class/built-in-classes))) ["class1" "class2"])) (set (map :block/title (model/get-all-classes repo))))))) From 4dc07f275b11aaaeca9426020d5cd67ea15cfed8 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 12 Dec 2024 16:51:45 -0500 Subject: [PATCH 075/105] fix: creating views page for file graphs --- src/main/frontend/worker/db_worker.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/frontend/worker/db_worker.cljs b/src/main/frontend/worker/db_worker.cljs index 576d9c52a89..b1045ee2abe 100644 --- a/src/main/frontend/worker/db_worker.cljs +++ b/src/main/frontend/worker/db_worker.cljs @@ -318,7 +318,7 @@ (when-not db-based? (try - (when-not (ldb/page-exists? @conn common-config/views-page-name #{:logseq.class/property}) + (when-not (ldb/page-exists? @conn common-config/views-page-name #{:logseq.class/Page}) (ldb/transact! conn (sqlite-create-graph/build-initial-views))) (catch :default _e))) From 8cda4758a2df987535930ac004f06fe63c9efc7e Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 12 Dec 2024 18:19:45 -0500 Subject: [PATCH 076/105] fix: built-in tags modified when tagging node ref When typing `[[foo]]#Property`, :logseq.class/Property was modified to have a different parent. This affected most built-in classes --- src/main/frontend/worker/handler/page/db_based/page.cljs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index 3f434fd36f7..f948a8527f0 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -14,7 +14,8 @@ [logseq.graph-parser.block :as gp-block] [logseq.graph-parser.text :as text] [logseq.outliner.validate :as outliner-validate] - [logseq.db.frontend.entity-util :as entity-util])) + [logseq.db.frontend.entity-util :as entity-util] + [logseq.db.frontend.malli-schema :as db-malli-schema])) (defn- build-page-tx [conn properties page {:keys [whiteboard? class? tags]}] (when (:block/uuid page) @@ -184,7 +185,7 @@ (when (and class? (not (ldb/class? existing-page)) (or (ldb/property? existing-page) (ldb/internal-page? existing-page))) - ;; convert existing property or page to class + ;; Convert existing user property or page to class (let [tx-data (db-class/build-new-class db (select-keys existing-page [:block/title :block/uuid :db/ident :block/created-at]))] (ldb/transact! conn tx-data tx-meta)))) (let [format :markdown @@ -199,7 +200,9 @@ (let [pages (split-namespace-pages db page date-formatter)] [(last pages) (butlast pages)]) [page nil])] - (when page + (when (and page (or (nil? (:db/ident page)) + ;; New page creation must not override built-in entities + (not (db-malli-schema/internal-ident? (:db/ident page))))) ;; Don't validate journal names because they can have '/' (when (not= :logseq.class/Journal type) (outliner-validate/validate-page-title-characters (str (:block/title page)) {:node page}) From 26430eb2ca2d3708360b86efa277365d3ccbf60c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 12 Dec 2024 22:10:44 -0500 Subject: [PATCH 077/105] fix: convert page to class --- src/main/frontend/handler/db_based/page.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index 461ad75b70b..7e390de2ba5 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -41,7 +41,7 @@ (defn convert-to-tag! [page-entity] - (if (db/page-exists? (:block/title page-entity) "class") + (if (db/page-exists? (:block/title page-entity) #{:logseq.class/Tag}) (notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false) (let [class (db-class/build-new-class (db/get-db) {:db/id (:db/id page-entity) From f092acb798567839ba5f684cc8dc0304d7850a55 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Fri, 13 Dec 2024 09:25:34 -0500 Subject: [PATCH 078/105] fix: tag conversion removes extra tag A page converted to tag should have the same :block/tags as an original tag --- src/main/frontend/handler/db_based/page.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/frontend/handler/db_based/page.cljs b/src/main/frontend/handler/db_based/page.cljs index 7e390de2ba5..d7710362560 100644 --- a/src/main/frontend/handler/db_based/page.cljs +++ b/src/main/frontend/handler/db_based/page.cljs @@ -43,12 +43,13 @@ [page-entity] (if (db/page-exists? (:block/title page-entity) #{:logseq.class/Tag}) (notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false) - (let [class (db-class/build-new-class (db/get-db) - {:db/id (:db/id page-entity) - :block/title (:block/title page-entity) - :block/created-at (:block/created-at page-entity)})] + (let [txs [(db-class/build-new-class (db/get-db) + {:db/id (:db/id page-entity) + :block/title (:block/title page-entity) + :block/created-at (:block/created-at page-entity)}) + [:db/retract (:db/id page-entity) :block/tags :logseq.class/Page]]] - (db/transact! (state/get-current-repo) [class] {:outliner-op :save-block})))) + (db/transact! (state/get-current-repo) txs {:outliner-op :save-block})))) (defn Date: Fri, 13 Dec 2024 12:23:31 -0500 Subject: [PATCH 079/105] fix: Tag and Property not storing their parents correctly They weren't visible on their Root Page but bigger bug is that their :logseq.property/parent wasn't storing or querying correctly. Also changed their parent to Root as they aren't dependent on Page and no need to make bootstrapping more complex --- deps/db/src/logseq/db/frontend/class.cljs | 9 +++------ deps/db/src/logseq/db/sqlite/create_graph.cljs | 17 ++++++++++------- .../logseq/db/sqlite/create_graph_test.cljs | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 6f8be000d82..9fd707595d9 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -10,14 +10,11 @@ (ordered-map :logseq.class/Root {:title "Root Tag"} - :logseq.class/Page {:title "Page"} + :logseq.class/Tag {:title "Tag"} - :logseq.class/Tag {:title "Tag" - :properties {:logseq.property/parent :logseq.class/Page}} + :logseq.class/Property {:title "Property"} - :logseq.class/Property - {:title "Property" - :properties {:logseq.property/parent :logseq.class/Page}} + :logseq.class/Page {:title "Page"} :logseq.class/Journal {:title "Journal" diff --git a/deps/db/src/logseq/db/sqlite/create_graph.cljs b/deps/db/src/logseq/db/sqlite/create_graph.cljs index e648d9a5a30..1f19ab6d444 100644 --- a/deps/db/src/logseq/db/sqlite/create_graph.cljs +++ b/deps/db/src/logseq/db/sqlite/create_graph.cljs @@ -71,7 +71,6 @@ (->> (keep :db/ident tx) frequencies (keep (fn [[k v]] (when (> v 1) k))) - (remove #{:logseq.class/Root}) seq)] (throw (ex-info (str "The following :db/idents are not unique and clobbered each other: " (vec conflicting-idents)) @@ -149,8 +148,7 @@ (sqlite-util/kv :logseq.kv/graph-initial-schema-version db-schema/version) (sqlite-util/kv :logseq.kv/graph-created-at (common-util/time-ms)) ;; Empty property value used by db.type/ref properties - {:db/ident :logseq.property/empty-placeholder} - {:db/ident :logseq.class/Root}] + {:db/ident :logseq.property/empty-placeholder}] import-type (into (sqlite-util/import-tx import-type))) initial-files [{:block/uuid (d/squuid) @@ -174,10 +172,15 @@ default-pages (->> (map sqlite-util/build-new-page built-in-pages-names) (map mark-block-as-built-in)) hidden-pages (concat (build-initial-views) (build-favorites-page)) - depend-class? (fn [c] (when (contains? #{:logseq.class/Property :logseq.class/Tag} (:db/ident c)) c)) - depend-classes (filter depend-class? default-classes) - other-classes (remove depend-class? default-classes) - tx (vec (concat initial-data depend-classes properties-tx other-classes + ;; These classes bootstrap our tags and properties as they depend on each other e.g. + ;; Root <-> Tag, classes-tx depends on logseq.property/parent, properties-tx depends on Property + bootstrap-class? (fn [c] (contains? #{:logseq.class/Root :logseq.class/Property :logseq.class/Tag} (:db/ident c))) + bootstrap-classes (filter bootstrap-class? default-classes) + bootstrap-class-ids (map #(select-keys % [:db/ident :block/uuid]) bootstrap-classes) + classes-tx (concat (map #(dissoc % :db/ident) bootstrap-classes) + (remove bootstrap-class? default-classes)) + ;; Order of tx is critical. bootstrap-class-ids bootstraps properties-tx and classes-tx + tx (vec (concat bootstrap-class-ids initial-data properties-tx classes-tx initial-files default-pages hidden-pages))] (validate-tx-for-duplicate-idents tx) tx)) diff --git a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs index 2a67f5c6e66..c1f7b017d14 100644 --- a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs +++ b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs @@ -73,6 +73,21 @@ (is (every? ldb/property? (:logseq.property.class/properties task)) "Each task property has correct type"))) +(deftest new-graph-initializes-default-classes + (let [conn (db-test/create-conn) + child-classes (->> (d/q '[:find (pull ?b [:db/ident :logseq.property/parent]) :where [?b :logseq.property/parent]] + @conn) + (map first))] + (assert (seq child-classes) "Classes must be present") + (is (every? integer? (map #(get-in % [:logseq.property/parent :db/id]) child-classes)) + "All parents of child classes must have valid :db/id") + (is (= (count child-classes) + (count (->> (conj child-classes {:db/ident :logseq.class/Root}) + (map #(d/entity @conn (:db/ident %))) + (mapcat :logseq.property/_parent) + set))) + "Reverse lookup of :logseq.property/parent correctly fetches number of child classes"))) + (deftest new-graph-is-valid (let [conn (db-test/create-conn) validation (db-validate/validate-db! @conn)] From 05a31b7d76103e7293aee7c86b72e17e7e064f4e Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Fri, 13 Dec 2024 17:06:27 -0500 Subject: [PATCH 080/105] fix: importer had incorrect set of Page based classes Just derive from built-in-classes so this doesn't keep happening --- deps/db/src/logseq/db/frontend/class.cljs | 13 +++++++++++++ .../src/logseq/graph_parser/exporter.cljs | 5 +++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/class.cljs b/deps/db/src/logseq/db/frontend/class.cljs index 9fd707595d9..201ffef9c59 100644 --- a/deps/db/src/logseq/db/frontend/class.cljs +++ b/deps/db/src/logseq/db/frontend/class.cljs @@ -5,6 +5,9 @@ [clojure.set :as set] [flatland.ordered.map :refer [ordered-map]])) +;; Main class vars +;; =============== + (def ^:large-vars/data-var built-in-classes "Map of built-in classes for db graphs with their :db/ident as keys" (ordered-map @@ -78,6 +81,13 @@ ;; TODO: Add more classes such as :book, :paper, :movie, :music, :project) )) +(def page-children-classes + "Children of :logseq.class/Page" + (set + (keep (fn [[class-ident m]] + (when (= (get-in m [:properties :logseq.property/parent]) :logseq.class/Page) class-ident)) + built-in-classes))) + (def internal-tags "Built-in classes that are hidden on a node and all pages view" #{:logseq.class/Page :logseq.class/Property :logseq.class/Tag :logseq.class/Root @@ -93,6 +103,9 @@ "Built-in classes that are hidden in a few contexts like property values" #{:logseq.class/Page :logseq.class/Root :logseq.class/Asset}) +;; Helper fns +;; ========== + (defn create-user-class-ident-from-name "Creates a class :db/ident for a default user namespace. NOTE: Only use this when creating a db-ident for a new class." diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 9138576a848..be63fd00d77 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -198,14 +198,15 @@ ;; Ignore new class tags from extract e.g. :logseq.class/Journal (logseq-class-ident? %))) (map #(vector :block/uuid (get-page-uuid (:page-names-to-uuids per-file-state) (:block/name %)))) - set)] + set) + page-classes (into #{:logseq.class/Page} db-class/page-children-classes)] (cond-> block true (update :block/tags convert-tags-to-classes db per-file-state user-options all-idents) ;; ensure pages are a Page true (update :block/tags (fn [tags] - (if (seq (set/intersection (set tags) #{:logseq.class/Page :logseq.class/Journal :logseq.class/Whiteboard :logseq.class/Property})) + (if (seq (set/intersection (set tags) page-classes)) tags (conj (vec tags) :logseq.class/Page)))) (seq page-tags) From f72370d21eb6529ff64e0f5c2289b5b68d44bb1e Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Fri, 13 Dec 2024 22:10:46 -0500 Subject: [PATCH 081/105] fix(regression): blocks can't have duplicate content This wouldn't allow tasks with the same name or even any two blocks have the same content. See ddabf902631242c9d469a5f20dbf932ff6dcd8c8 for the original fix for more. Also fixed lint and tweaked test --- deps/db/.carve/ignore | 2 ++ deps/outliner/src/logseq/outliner/validate.cljs | 6 +++--- deps/outliner/test/logseq/outliner/validate_test.cljs | 6 +----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/deps/db/.carve/ignore b/deps/db/.carve/ignore index 0aea51509a5..0abf81c40de 100644 --- a/deps/db/.carve/ignore +++ b/deps/db/.carve/ignore @@ -15,6 +15,8 @@ logseq.db.frontend.inputs/resolve-input ;; API logseq.db.frontend.class/build-new-class ;; API +logseq.db.frontend.class/page-children-classes +;; API logseq.db.frontend.db-ident/ensure-unique-db-ident ;; API logseq.db.sqlite.build/create-blocks diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index 082054e874c..4d704fffccd 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -97,10 +97,10 @@ (defn ^:api validate-unique-by-name-tag-and-block-type "Validates uniqueness of nodes for the following cases: - Page names of type 'page' are unique by tag e.g. their can be Apple #Company and Apple #Fruit - - Page names of other types are unique for their type e.g. their can be #Journal ('class') and Journal ('page') - - Property names are unique and don't consider built-in property names" + - Page names of other types are unique for their type e.g. their can be #Journal ('class') and Journal ('page')" [db new-title entity] - (validate-unique-for-page db new-title entity)) + (when (ldb/page? entity) + (validate-unique-for-page db new-title entity))) (defn ^:api validate-disallow-page-with-journal-name "Validates a non-journal page renamed to journal format" diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index c3c3a64165e..e532d24a6da 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -135,11 +135,7 @@ (testing "Validate pages" (let [pages (->> (d/q '[:find [?b ...] :where [?b :block/title] - (or [?b :block/tags :logseq.class/Tag] - [?b :block/tags :logseq.class/Property] - [?b :block/tags :logseq.class/Page] - [?b :block/tags :logseq.class/Journal] - [?b :block/tags :logseq.class/Whiteboard])] @conn) + [?b :block/tags]] @conn) (map (fn [id] (d/entity @conn id)))) page-errors (atom {})] From e51959da0e00acdfb4f664f9ee1a7db5e033088a Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 15 Dec 2024 14:39:41 +0800 Subject: [PATCH 082/105] enhance: move tag remove button to the left --- src/main/frontend/components/block.cljs | 29 +++++++++++++--------- src/main/frontend/components/block.css | 10 +++++--- src/main/frontend/components/container.css | 2 +- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index f1cb23e5142..316f7dec226 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -756,7 +756,7 @@ (db-content/content-id-ref->page s (:block/refs page-entity)) :else s) - s (if tag? (str "#" s) s)] + s (if (and tag? (not (:hide-tag-symbol? config))) (str "#" s) s)] (if (ldb/page? page-entity) s (block-title config page-entity))))] @@ -2589,8 +2589,9 @@ (rum/defcs block-tag < (rum/local false ::hover?) [state block tag config popup-opts] - (let [*hover? (::hover? state)] - [:div.block-tag + (let [*hover? (::hover? state) + hover? @*hover?] + [:div.block-tag.items-center {:key (str "tag-" (:db/id tag)) :on-mouse-over #(reset! *hover? true) :on-mouse-out #(reset! *hover? false) @@ -2615,19 +2616,23 @@ :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))} "Remove tag")]) popup-opts))} - (page-cp (assoc config - :tag? true - :disable-preview? true) - tag) - (when-not (ldb/private-tags (:db/ident tag)) - [:a.close.flex.transition-opacity.duration-300.ease-in + (if (and hover? (not (ldb/private-tags (:db/ident tag)))) + [:a.inline.close.flex.transition-opacity.duration-300.ease-in {:class (if @*hover? "!opacity-100" "!opacity-0") :title "Remove this tag" :on-pointer-down (fn [e] (util/stop e) (db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag)))} - (ui/icon "x" {:size 15})])])) + (ui/icon "x" {:size 14 + :style {:margin-top 1}})] + [:a.hash-symbol {:style {:margin-left 5}} + "#"]) + (page-cp (assoc config + :disable-preview? true + :tag? true + :hide-tag-symbol? true) + tag)])) (rum/defc tags-cp "Tags without inline or hidden tags" @@ -2645,7 +2650,7 @@ tags-count (count block-tags)] (when (seq block-tags) (if (< tags-count 3) - [:div.block-tags + [:div.block-tags.gap-1 (for [tag block-tags] (rum/with-key (block-tag block tag config popup-opts) @@ -2676,7 +2681,7 @@ :tag? true :disable-preview? true :disable-click? true) tag)]) - [:div.text-sm.opacity-50 + [:div.text-sm.opacity-50.ml-1 (str "+" (- tags-count 2))]]))))) (rum/defc block-positioned-properties diff --git a/src/main/frontend/components/block.css b/src/main/frontend/components/block.css index e3647a6dddf..1d2203985aa 100644 --- a/src/main/frontend/components/block.css +++ b/src/main/frontend/components/block.css @@ -970,14 +970,18 @@ html.is-mac { } .block-tag { - @apply pr-1 flex flex-row items-center gap-1; + @apply flex flex-row items-center; } .block-tag a.tag { @apply flex text-sm font-normal items-center opacity-70; } -.block-tag a.tag:hover { +.block-tag a.hash-symbol { + @apply text-sm text-sm font-normal opacity-70; +} + +.block-tag a.tag:hover, .block-tag a.hash-symbol:hover { @apply opacity-100; } @@ -1013,7 +1017,7 @@ html.is-mac { } .ls-page-title .block-tags { - @apply relative -right-1 min-h-full; + @apply relative min-h-full; } .ls-code-editor-wrap { diff --git a/src/main/frontend/components/container.css b/src/main/frontend/components/container.css index 884654b79d5..2c4cca38b8a 100644 --- a/src/main/frontend/components/container.css +++ b/src/main/frontend/components/container.css @@ -541,7 +541,7 @@ flex: 1; .page { - @apply px-6; + @apply px-4; } } From 57d1c60acd98543bb0cf62020fca5b2e9cbfa955 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 15 Dec 2024 22:07:07 +0800 Subject: [PATCH 083/105] style tweaks --- src/main/frontend/components/block.css | 2 +- src/main/frontend/components/property/value.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/components/block.css b/src/main/frontend/components/block.css index 1d2203985aa..d2ca9c024c7 100644 --- a/src/main/frontend/components/block.css +++ b/src/main/frontend/components/block.css @@ -949,7 +949,7 @@ html.is-mac { .positioned-properties.block-right { button { - @apply whitespace-nowrap mr-0.5; + @apply whitespace-nowrap; } .block-title-wrap { diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 8b39a173646..38b698a9596 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -1010,7 +1010,7 @@ (do (some-> (rum/deref *el) (.click)) (util/stop e)) :dune)) - :class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2 pr-4"} + :class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2"} (let [not-empty-value? (not= (map :db/ident items) [:logseq.property/empty-placeholder])] (if (and (seq items) not-empty-value?) (concat From 7506936481ea35345a0fff02370ed722a1789f3a Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 16 Dec 2024 15:51:15 +0800 Subject: [PATCH 084/105] fix: db migration Ensures internal tags exist before transacting other tx-data. --- src/main/frontend/worker/db/migrate.cljs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index 21b6bbc06dd..d3b1e9d8431 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -421,7 +421,10 @@ [conn _search-db] (let [db @conn block-type-entity (d/entity db :block/type) - datoms (d/datoms db :avet :block/type) + ;; Not using (d/datoms db :avet :block/type) here because some old graphs + ;; don't have :block/type indexed + datoms (->> (d/datoms db :eavt) + (filter (fn [d] (= :block/type (:a d))))) journal-entity (d/entity db :logseq.class/Journal) tx-data (mapcat (fn [{:keys [e _a v]}] (let [tag (case v @@ -539,11 +542,11 @@ (into {}) sqlite-create-graph/build-initial-properties* (map (fn [b] (assoc b :logseq.property/built-in? true)))) - new-classes (->> (select-keys db-class/built-in-classes classes) - ;; class already exists, this should never happen - (remove (fn [[k _]] - (when (d/entity db k) - (assert (str "DB migration: class already exists " k))))) + classes' (->> (concat [:logseq.class/Property :logseq.class/Tag :logseq.class/Page :logseq.class/Journal :logseq.class/Whiteboard] classes) + distinct) + new-classes (->> (select-keys db-class/built-in-classes classes') + ;; class already exists, this should never happen + (remove (fn [[k _]] (d/entity db k))) (into {}) (#(sqlite-create-graph/build-initial-classes* % (zipmap properties properties))) (map (fn [b] (assoc b :logseq.property/built-in? true)))) From ac156c403fbb65cf8c462475dbe8625b80596d66 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 16 Dec 2024 22:27:55 +0800 Subject: [PATCH 085/105] enhance(ux): open properties in sidebar fixes https://github.com/logseq/db-test/issues/167 --- src/main/frontend/components/page.cljs | 26 ++++++++- src/main/frontend/components/property.cljs | 57 ++++++++++++------- src/main/frontend/components/property.css | 4 +- .../frontend/components/right_sidebar.css | 4 -- src/main/frontend/ui.cljs | 21 ++++--- src/main/frontend/ui.css | 6 +- 6 files changed, 76 insertions(+), 42 deletions(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 18d69cc20ad..ac622148be2 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -593,6 +593,23 @@ {:value "property"} (objects/property-related-objects page (:current-page? opts)))))])) +(rum/defc sidebar-page-properties + [config page] + (let [[collapsed? set-collapsed!] (rum/use-state true)] + [:div.ls-sidebar-page-properties.flex.flex-col.gap-2.mt-2 + [:div + (shui/button + {:variant :ghost + :size :sm + :class "px-1 text-muted-foreground" + :on-click #(set-collapsed! (not collapsed?))} + [:span.text-xs (str (if collapsed? "Open" "Hide")) " properties"])] + + (when-not collapsed? + [:<> + (component-block/db-properties-cp config page {:sidebar-properties? true}) + [:hr.my-4]])])) + ;; A page is just a logical block (rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query mixins/container-id (rum/local false ::all-collapsed?) @@ -639,9 +656,8 @@ (if (and whiteboard-page? (not sidebar?)) [:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive - [:div.relative.grid.gap-4.page-inner - (when (or (and db-based? (not block?)) - (and (not db-based?) (not sidebar?) (not block?))) + [:div.relative.grid.gap-8.page-inner + (when-not (or block? sidebar?) [:div.flex.flex-row.space-between (when (and (or (mobile-util/native-platform?) (util/mobile?)) (not db-based?)) [:div.flex.flex-row.pr-2 @@ -659,6 +675,10 @@ :preview? preview?}))) (lsp-pagebar-slot)]) + (when (and db-based? sidebar?) + [:div.-mb-8 + (sidebar-page-properties config page)]) + (when (and block? (not sidebar?) (not whiteboard?)) (let [config (merge config {:id "block-parent" :block? true})] diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index 5ef5238f140..d2087e745d8 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -425,7 +425,7 @@ (rum/defcs new-property < rum/reactive [state block opts] - (when (and (not config/publishing?) (:class-schema? opts)) + (when-not config/publishing? [:div.ls-new-property {:style {:margin-left 6 :margin-top 1}} [:a.fade-link.flex.jtrigger {:tab-index 0 @@ -586,7 +586,7 @@ :will-remount (fn [state] (let [block (db/entity (:db/id (::block state)))] (assoc state ::classes (async-load-classes! block))))} - [state _target-block {:keys [class-schema?] :as opts}] + [state _target-block {:keys [class-schema? sidebar-properties?] :as opts}] (let [id (::id state) db-id (:db/id (::block state)) block (db/sub-block db-id) @@ -685,21 +685,38 @@ (when (and class? (nil? (:logseq.property.class/properties block))) [[:logseq.property.class/properties nil]])) remove-built-in-or-other-position-properties)] - (when-not (and (empty? full-properties) (not (:class-schema? opts))) - [:div.ls-properties-area - {:id id - :class (util/classnames [{:class-properties class-schema? - :ls-page-properties (and page? (not class-schema?))}]) - :tab-index 0 - :on-key-up #(when-let [block (and (= "Escape" (.-key %)) - (.closest (.-target %) "[blockid]"))] - (let [target (.-target %)] - (when-not (d/has-class? target "ls-popup-closed") - (state/set-selection-blocks! [block]) - (some-> js/document.activeElement (.blur))) - (d/remove-class! target "ls-popup-closed")))} - (let [remove-properties #{:logseq.property/icon :logseq.property/query} - properties' (remove (fn [[k _v]] (contains? remove-properties k)) full-properties)] - (properties-section block (if class-schema? properties properties') opts)) - - (rum/with-key (new-property block opts) (str id "-add-property"))]))) + (cond + (and (empty? full-properties) (not (:class-schema? opts))) + (when sidebar-properties? + (rum/with-key (new-property block opts) (str id "-add-property"))) + + :else + (let [remove-properties #{:logseq.property/icon :logseq.property/query} + properties' (remove (fn [[k _v]] (contains? remove-properties k)) full-properties) + properties'' (cond->> properties' + (not class-schema?) + (remove (fn [[k _v]] (= k :logseq.property.class/properties)))) + page? (ldb/page? block)] + [:div.ls-properties-area + {:id id + :class (util/classnames [{:class-properties class-schema? + :ls-page-properties (and page? (not class-schema?))}]) + :tab-index 0 + :on-key-up #(when-let [block (and (= "Escape" (.-key %)) + (.closest (.-target %) "[blockid]"))] + (let [target (.-target %)] + (when-not (d/has-class? target "ls-popup-closed") + (state/set-selection-blocks! [block]) + (some-> js/document.activeElement (.blur))) + (d/remove-class! target "ls-popup-closed")))} + (properties-section block (if class-schema? properties properties'') opts) + + (when page? + (rum/with-key (new-property block opts) (str id "-add-property"))) + + (when page? + (let [properties'' (filter (fn [[k _v]] (= k :logseq.property.class/properties)) properties')] + (when (seq properties'') + [:<> + (when-not class-schema? [:hr.my-4]) + (properties-section block (if class-schema? properties properties'') opts)])))])))) diff --git a/src/main/frontend/components/property.css b/src/main/frontend/components/property.css index 102d69bd61e..c390c13b263 100644 --- a/src/main/frontend/components/property.css +++ b/src/main/frontend/components/property.css @@ -29,9 +29,7 @@ margin-left: 0; } -.ls-block .property-pair, -.property-block .property-value, -.block-property-value { +.ls-block .property-pair, .ls-sidebar-page-properties .property-pair, .property-block .property-value, .block-property-value { margin-left: 7px; } diff --git a/src/main/frontend/components/right_sidebar.css b/src/main/frontend/components/right_sidebar.css index 5d068900241..483a25708f6 100644 --- a/src/main/frontend/components/right_sidebar.css +++ b/src/main/frontend/components/right_sidebar.css @@ -42,7 +42,3 @@ html[data-theme=light] a.toggle:hover { @apply opacity-100; } } - -.sidebar-panel-content { - @apply pt-3; -} \ No newline at end of file diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index 23827b716f0..2a4a2344a41 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -604,28 +604,27 @@ (rum/local false ::control?) [state {:keys [on-pointer-down header title-trigger? collapsed?]}] (let [control? (get state ::control?)] - [:div.content + [:div.ls-foldable-title.content [:div.flex-1.flex-row.foldable-title (cond-> {:on-mouse-over #(reset! control? true) :on-mouse-out #(reset! control? false)} title-trigger? (assoc :on-pointer-down on-pointer-down :class "cursor")) - [:div.flex.flex-row.items-center.ls-foldable-header + [:div.flex.flex-row.items-center.ls-foldable-header.gap-1 {:on-click (fn [^js e] (let [^js target (.-target e)] (when (some-> target (.closest ".as-toggle")) (reset! collapsed? (not @collapsed?)))))} (when-not (mobile-util/native-platform?) - [:a.block-control.opacity-50.hover:opacity-100.mr-2 - (cond-> - {:style {:width 14 - :height 16 - :margin-left -30}} - (not title-trigger?) - (assoc :on-pointer-down on-pointer-down)) - [:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")} - (rotating-arrow @collapsed?)]]) + (let [style {:width 14 :height 16}] + [:a.ls-foldable-title-control.block-control.opacity-50.hover:opacity-100 + (cond-> + {:style style} + (not title-trigger?) + (assoc :on-pointer-down on-pointer-down)) + [:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")} + (rotating-arrow @collapsed?)]])) (if (fn? header) (header @collapsed?) header)]]])) diff --git a/src/main/frontend/ui.css b/src/main/frontend/ui.css index 68d308ddecf..2f98e6124b5 100644 --- a/src/main/frontend/ui.css +++ b/src/main/frontend/ui.css @@ -329,4 +329,8 @@ input[type='range'] { .as-toggle { @apply opacity-60 cursor-pointer select-none active:opacity-50; } -} \ No newline at end of file +} + +.ls-foldable-title-control { + margin-left: -27px; +} From a23b821cb563dd0b367c15958ad901f849627170 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 16 Dec 2024 22:30:24 +0800 Subject: [PATCH 086/105] fix: lint --- src/main/frontend/components/property.cljs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index d2087e745d8..da8b434957b 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -596,7 +596,6 @@ (and (set? ids) (contains? ids (:block/uuid block)))))) _ (doseq [class (::classes state)] (db/sub-block (:db/id class))) - page? (db/page? block) class? (ldb/class? block) block-properties (:block/properties block) properties (cond From f391f19e7b3699aa312701bedefe44d9b16f24b3 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 10:47:40 -0500 Subject: [PATCH 087/105] fix: logseq.class/Tag did not have a Tag leading to buggy exceptions for :logseq.class/Tag anywhere :block/tags queries :logseq.class/Tag is used e.g. validation, page existence and querying. Perhaps before 89cdf5f9f6940dba48cacd82056ad62253ce46c7, this wasn't possible because of the way the graph was initialized. Also removed one parent assertion for previous fix since we didn't need both assertions to prevent a regression --- deps/db/src/logseq/db/sqlite/util.cljs | 6 ++--- .../logseq/db/sqlite/create_graph_test.cljs | 22 +++++++++---------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/util.cljs b/deps/db/src/logseq/db/sqlite/util.cljs index e5f14a10257..4c401931be6 100644 --- a/deps/db/src/logseq/db/sqlite/util.cljs +++ b/deps/db/src/logseq/db/sqlite/util.cljs @@ -115,10 +115,8 @@ {:pre [(qualified-keyword? (:db/ident block))]} (block-with-timestamps (cond-> (merge block - (cond-> - {:block/format :markdown} - (not= (:db/ident block) :logseq.class/Tag) - (assoc :block/tags (set (conj (:block/tags block) :logseq.class/Tag))))) + {:block/format :markdown + :block/tags (set (conj (:block/tags block) :logseq.class/Tag))}) (and (not= (:db/ident block) :logseq.class/Root) (nil? (:logseq.property/parent block))) (assoc :logseq.property/parent :logseq.class/Root)))) diff --git a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs index c1f7b017d14..312479f0ac4 100644 --- a/deps/db/test/logseq/db/sqlite/create_graph_test.cljs +++ b/deps/db/test/logseq/db/sqlite/create_graph_test.cljs @@ -9,7 +9,8 @@ [logseq.db.frontend.property :as db-property] [logseq.db.sqlite.build :as sqlite-build] [logseq.db :as ldb] - [logseq.db.test.helper :as db-test])) + [logseq.db.test.helper :as db-test] + [logseq.db.frontend.class :as db-class])) (deftest new-graph-db-idents (testing "a new graph follows :db/ident conventions for" @@ -73,17 +74,14 @@ (is (every? ldb/property? (:logseq.property.class/properties task)) "Each task property has correct type"))) -(deftest new-graph-initializes-default-classes - (let [conn (db-test/create-conn) - child-classes (->> (d/q '[:find (pull ?b [:db/ident :logseq.property/parent]) :where [?b :logseq.property/parent]] - @conn) - (map first))] - (assert (seq child-classes) "Classes must be present") - (is (every? integer? (map #(get-in % [:logseq.property/parent :db/id]) child-classes)) - "All parents of child classes must have valid :db/id") - (is (= (count child-classes) - (count (->> (conj child-classes {:db/ident :logseq.class/Root}) - (map #(d/entity @conn (:db/ident %))) +(deftest new-graph-initializes-default-classes-correctly + (let [conn (db-test/create-conn)] + (is (= (count db-class/built-in-classes) (count (d/datoms @conn :avet :block/tags :logseq.class/Tag))) + "All built-in classes have a :logseq.class/Tag") + + (is (= (count (dissoc db-class/built-in-classes :logseq.class/Root)) + (count (->> (d/datoms @conn :avet :block/tags :logseq.class/Tag) + (map #(d/entity @conn (:e %))) (mapcat :logseq.property/_parent) set))) "Reverse lookup of :logseq.property/parent correctly fetches number of child classes"))) From 5399278bb6313f59219491b18eea6f4ad77f6797 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 14:07:14 -0500 Subject: [PATCH 088/105] fix: page-exists? not returning anything for db graphs Caused some buggy page creation. While [?b :block/tags ?db-ident] for an individual db-ident, it doesn't work when db-ident is a destructure :in arg. For that case only integers worked --- deps/db/src/logseq/db.cljs | 18 +++++++++++------- deps/db/test/logseq/db_test.cljs | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 5cedbd0ffd1..0673d4ff708 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -195,31 +195,35 @@ (def db-based-graph? entity-util/db-based-graph?) (defn page-exists? - "Whether a page exists with the `tags`." + "Returns truthy value if page exists. + For db graphs, returns all page db ids that given title and one of the given `tags`. + For file graphs, returns page entity if it exists" [db page-name tags] (when page-name (if (db-based-graph? db) ;; Classes and properties are case sensitive (let [tags (if (coll? tags) (set tags) #{tags})] - (if (set/intersection #{:logseq.class/Tag :logseq.class/Property} tags) + (if (seq (set/intersection #{:logseq.class/Tag :logseq.class/Property} tags)) (seq (d/q '[:find [?p ...] - :in $ ?name [?tag ...] + :in $ ?name [?tag-ident ...] :where [?p :block/title ?name] - [?p :block/tags ?tag]] + [?p :block/tags ?tag] + [?tag :db/ident ?tag-ident]] db page-name tags)) - ;; TODO: Decouple db graphs from file specific :block/name + ;; TODO: Decouple db graphs from file specific :block/name (seq (d/q '[:find [?p ...] - :in $ ?name [?tag ...] + :in $ ?name [?tag-ident ...] :where [?p :block/name ?name] - [?p :block/tags ?tag]] + [?p :block/tags ?tag] + [?tag :db/ident ?tag-ident]] db (common-util/page-name-sanity-lc page-name) tags)))) diff --git a/deps/db/test/logseq/db_test.cljs b/deps/db/test/logseq/db_test.cljs index a3e8ddfd9aa..13cbfef4554 100644 --- a/deps/db/test/logseq/db_test.cljs +++ b/deps/db/test/logseq/db_test.cljs @@ -64,3 +64,22 @@ ;; Case sensitive classes (is (= "movie" (:block/title (ldb/get-case-page @conn "movie")))) (is (= "Movie" (:block/title (ldb/get-case-page @conn "Movie")))))) + +(deftest page-exists + (let [conn (db-test/create-conn-with-blocks + {:properties + {:foo {:block/schema {:type :default}} + :Foo {:block/schema {:type :default}}} + :classes {:movie {} :Movie {}}})] + (is (= ["foo"] + (map #(:block/title (d/entity @conn %)) (ldb/page-exists? @conn "foo" #{:logseq.class/Property}))) + "Property pages correctly found for given class") + (is (= nil + (ldb/page-exists? @conn "foo" #{:logseq.class/Tag})) + "Property pages correctly not found for given class") + (is (= ["movie"] + (map #(:block/title (d/entity @conn %)) (ldb/page-exists? @conn "movie" #{:logseq.class/Tag}))) + "Class pages correctly found for given class") + (is (= nil + (ldb/page-exists? @conn "movie" #{:logseq.class/Property})) + "Class pages correctly not found for given class"))) From f5e7b2ce837aa35ff6e1e0fc3b255451c302ec6c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 15:11:04 -0500 Subject: [PATCH 089/105] fix(regression): skipping validation on journal creation --- src/main/frontend/worker/handler/page/db_based/page.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index f948a8527f0..353e0da16c6 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -204,7 +204,8 @@ ;; New page creation must not override built-in entities (not (db-malli-schema/internal-ident? (:db/ident page))))) ;; Don't validate journal names because they can have '/' - (when (not= :logseq.class/Journal type) + (when-not (or (contains? types :logseq.class/Journal) + (contains? (set (:block/tags page)) :logseq.class/Journal)) (outliner-validate/validate-page-title-characters (str (:block/title page)) {:node page}) (doseq [parent parents] (outliner-validate/validate-page-title-characters (str (:block/title parent)) {:node parent}))) From fb2340c1a1d7552433793dd1cc50558fcd071343 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 15:34:03 -0500 Subject: [PATCH 090/105] fix: new journals should only have #Journal Redundant and confusing also be adding #Page, which is a parent of #Journal, to all journals. Also remove unused option create! --- src/main/frontend/handler/page.cljs | 1 - .../worker/handler/page/db_based/page.cljs | 2 +- .../worker/handler/page/db_based/page_test.cljs | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/handler/page.cljs b/src/main/frontend/handler/page.cljs index fd20ed0f171..a371be5b7ab 100644 --- a/src/main/frontend/handler/page.cljs +++ b/src/main/frontend/handler/page.cljs @@ -425,7 +425,6 @@ (> (d/entity @conn [:block/uuid page-uuid]) + :block/tags + (map #(:db/ident (d/entity @conn (:db/id %)))))) + "New journal only has Journal tag"))) \ No newline at end of file From 7b885a55ded43025ca9ab8431948ad54e6bad22e Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 16:41:37 -0500 Subject: [PATCH 091/105] fix(regression): tagging a block with a page or property After page-exists? was fixed in 8cede822f393cb0dbd3e32727f627f0370f3986d, using a page or property as a tag for the first tag with `#` failed hard --- src/main/frontend/worker/handler/page/db_based/page.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/frontend/worker/handler/page/db_based/page.cljs b/src/main/frontend/worker/handler/page/db_based/page.cljs index a05ea24c5da..b29b014f895 100644 --- a/src/main/frontend/worker/handler/page/db_based/page.cljs +++ b/src/main/frontend/worker/handler/page/db_based/page.cljs @@ -171,7 +171,7 @@ date-formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal)) title (sanitize-title title*) types (cond class? - #{:logseq.class/Tag :logseq.class/Property :logseq.class/Page} + #{:logseq.class/Tag} whiteboard? #{:logseq.class/Whiteboard} today-journal? From bc0f63694a75bf02addd020cfd765013c2bbe5fd Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 16 Dec 2024 17:08:54 -0500 Subject: [PATCH 092/105] fix: Don't show 'New page' in page autocompletion for an existing tag or property. This made sense when pages were split by type but it doesn't make sense now that a Tag or Property can be a Page. The 'New page' that showed up was also buggy as it didn't actually create a new page. Also fixed page-exists? doing a case sensitive check when a Page and Tag were in the same tags --- deps/db/src/logseq/db.cljs | 11 ++++++----- src/main/frontend/components/editor.cljs | 10 ++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 0673d4ff708..722ffb79752 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -201,9 +201,10 @@ [db page-name tags] (when page-name (if (db-based-graph? db) - ;; Classes and properties are case sensitive - (let [tags (if (coll? tags) (set tags) #{tags})] - (if (seq (set/intersection #{:logseq.class/Tag :logseq.class/Property} tags)) + (let [tags' (if (coll? tags) (set tags) #{tags})] + ;; Classes and properties are case sensitive and can be looked up + ;; as such in case-sensitive contexts e.g. no Page + (if (and (seq tags') (every? #{:logseq.class/Tag :logseq.class/Property} tags')) (seq (d/q '[:find [?p ...] @@ -214,7 +215,7 @@ [?tag :db/ident ?tag-ident]] db page-name - tags)) + tags')) ;; TODO: Decouple db graphs from file specific :block/name (seq (d/q @@ -226,7 +227,7 @@ [?tag :db/ident ?tag-ident]] db (common-util/page-name-sanity-lc page-name) - tags)))) + tags')))) (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)])))) (defn get-page diff --git a/src/main/frontend/components/editor.cljs b/src/main/frontend/components/editor.cljs index 7bbd6453b10..73947e5143c 100644 --- a/src/main/frontend/components/editor.cljs +++ b/src/main/frontend/components/editor.cljs @@ -35,7 +35,8 @@ [logseq.shui.ui :as shui] [promesa.core :as p] [react-draggable] - [rum.core :as rum])) + [rum.core :as rum] + [logseq.db.frontend.class :as db-class])) (defn filter-commands [page? commands] @@ -156,7 +157,12 @@ ;; reorder, shortest and starts-with first. (let [matched-pages-with-new-page (fn [partial-matched-pages] - (if (or (db/page-exists? q (if db-tag? #{:logseq.class/Tag} #{:logseq.class/Page})) + (if (or (db/page-exists? q (if db-tag? + #{:logseq.class/Tag} + ;; Page existence here should be the same as entity-util/page?. + ;; Don't show 'New page' if a page has any of these tags + (into #{:logseq.class/Page :logseq.class/Tag :logseq.class/Property} + db-class/page-children-classes))) (and db-tag? (some ldb/class? (:block/_alias (db/get-page q))))) partial-matched-pages (if db-tag? From ad485875fec695db509a0dd55f76a7a9b5bbc07a Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 10:01:21 -0500 Subject: [PATCH 093/105] fix(dev): update debug edn of schema.org graph --- .../logseq/tasks/db_graph/create_graph_with_schema_org.cljs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs b/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs index 973bea7e670..98ba9e4a7cd 100644 --- a/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs +++ b/scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs @@ -366,6 +366,7 @@ {:logseq.property.class/properties [:block/title]} {:property/schema.classes [:block/title]} {:logseq.property/parent [:block/title]} + {:block/tags [:block/title]} {:block/refs [:block/title]}]) ...] :in $ :where [?b :db/ident ?ident]] @@ -381,6 +382,7 @@ :db/cardinality :property/schema.classes :block/refs]) (seq props) (assoc :block/properties (-> (update-keys props name) + (dissoc "tags") (update-vals (fn [v] (if (:db/id v) (db-property/property-value-content (d/entity db (:db/id v))) @@ -391,6 +393,8 @@ (update :logseq.property/parent :block/title) (seq (:property/schema.classes m)) (update :property/schema.classes #(set (map :block/title %))) + (seq (:block/tags m)) + (update :block/tags #(set (map :block/title %))) (seq (:block/refs m)) (update :block/refs #(set (map :block/title %))))))) set))))) From cafa70f4092fa2c9481a6797d3d6d78942a84908 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 11:58:02 -0500 Subject: [PATCH 094/105] enhance: properties, tags and other core tags can be queried from simple queries and used as property value selection criteria --- src/main/frontend/components/property/config.cljs | 2 +- src/main/frontend/components/property/value.cljs | 5 ++++- src/main/frontend/components/query/builder.cljs | 2 +- src/main/frontend/db/model.cljs | 15 ++++++++++++--- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/frontend/components/property/config.cljs b/src/main/frontend/components/property/config.cljs index 47e0562a5bd..298e84957a1 100644 --- a/src/main/frontend/components/property/config.cljs +++ b/src/main/frontend/components/property/config.cljs @@ -96,7 +96,7 @@ (let [toggle-fn #(do (when (fn? on-hide) (on-hide)) (shui/popup-hide! id)) - classes (model/get-all-classes (state/get-current-repo) {:except-root-class? true}) + classes (model/get-all-readable-classes (state/get-current-repo) {:except-root-class? true}) options (map (fn [class] {:label (:block/title class) :value (:block/uuid class)}) diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 38b698a9596..d21f5db062a 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -553,7 +553,10 @@ "Choose nodes" :else "Choose node") - :show-new-when-not-exact-match? (if (and parent-property? (contains? (set children-pages) (:db/id block))) + :show-new-when-not-exact-match? (if (or (and parent-property? (contains? (set children-pages) (:db/id block))) + ;; Don't allow creating private tags + (seq (set/intersection (set (map :db/ident classes)) + ldb/private-tags))) false true) :extract-chosen-fn :value diff --git a/src/main/frontend/components/query/builder.cljs b/src/main/frontend/components/query/builder.cljs index dc795c4d8eb..bfe46a9f936 100644 --- a/src/main/frontend/components/query/builder.cljs +++ b/src/main/frontend/components/query/builder.cljs @@ -227,7 +227,7 @@ db-based? (config/db-based-graph? repo)] (rum/use-effect! (fn [] - (let [result (db-model/get-all-classes repo {:except-root-class? true})] + (let [result (db-model/get-all-readable-classes repo {:except-root-class? true})] (set-values! result))) []) (let [items (->> values diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index 105dbd30a66..0e5d7143dc2 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -798,18 +798,27 @@ independent of format as format specific heading characters are stripped" :nonce (:nonce shape)})))))) (defn get-all-classes - [repo & {:keys [except-root-class?] - :or {except-root-class? false}}] + [repo & {:keys [except-root-class? except-private-tags?] + :or {except-root-class? false + except-private-tags? true}}] (let [db (conn/get-db repo) classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag) (map (fn [d] (db-utils/entity db (:e d)))) (remove (fn [d] - (contains? ldb/private-tags (:db/ident d)))))] + (and except-private-tags? + (contains? ldb/private-tags (:db/ident d))))))] (if except-root-class? (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes) classes))) +(defn get-all-readable-classes + "Gets all classes that are used in a read only context e.g. querying or used + for property value selection. This should _not_ be used in a write context e.g. + adding a tag to a node or creating a new node with a tag" + [repo opts] + (get-all-classes repo (merge opts {:except-private-tags? false}))) + (defn get-structured-children [repo eid] (->> From 48975198157e57f8ea33f89d4ea0c429af240d61 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 14:21:10 -0500 Subject: [PATCH 095/105] fix: remove unused core schema attrs :block/alias and :block/tags are validated as properties and thus no longer need attribute definitions. This should've been updated with attribute properties refactor. :block/namespace has not been in db graphs for awhile --- .../src/logseq/db/frontend/malli_schema.cljs | 28 ++++++++----------- deps/db/src/logseq/db/frontend/property.cljs | 10 +++---- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 4330525d614..f0a4fc8c7a5 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -70,6 +70,15 @@ (= :logseq.property/empty-placeholder (:db/ident (d/entity db property-val)))) (= :logseq.property/empty-placeholder property-val))) +(defn internal-ident? + "Determines if given ident is created by Logseq. All Logseq internal idents + must start with 'block' or 'logseq' to keep Logseq internals from leaking + across namespaces and to allow for users and 3rd party plugins to choose + any other namespace" + [ident] + (or (contains? db-property/db-attribute-properties ident) + (contains? logseq-ident-namespaces (namespace ident)))) + (defn validate-property-value "Validates the property value in a property tuple. The property value is expected to be a coll if the property has a :many cardinality. validate-fn is @@ -77,7 +86,7 @@ validate-fn varies by property type" [db validate-fn [{:block/keys [schema] :as property} property-val] & {:keys [new-closed-value?]}] ;; For debugging - ;; (when (not (string/starts-with? (namespace (:db/ident property)) "logseq.")) (prn :validate-val (dissoc property :property/closed-values) property-val)) + ;; (when (not (internal-ident? (:db/ident property))) (prn :validate-val (dissoc property :property/closed-values) property-val)) (let [validate-fn' (if (db-property-type/property-types-with-db (:type schema)) (fn [value] (validate-fn db value {:new-closed-value? new-closed-value?})) @@ -170,15 +179,6 @@ (mapv (fn [[db-id m]] (with-meta m {:db/id db-id})) (datoms->entity-maps datoms))) -(defn internal-ident? - "Determines if given ident is created by Logseq. All Logseq internal idents - must start with 'block' or 'logseq' to keep Logseq internals from leaking - across namespaces and to allow for users and 3rd party plugins to choose - any other namespace" - [ident] - (or (contains? db-property/db-attribute-properties ident) - (contains? logseq-ident-namespaces (namespace ident)))) - (assert (every? #(re-find #"^(block|logseq\.)" (namespace %)) db-property/db-attribute-properties) "All db-attribute idents start with an internal namespace") (assert (every? #(re-find #"^logseq\." %) logseq-ident-namespaces) @@ -219,7 +219,6 @@ ;; Injected by update-properties-in-ents [:block/properties {:optional true} block-properties] [:block/refs {:optional true} [:set :int]] - [:block/tags {:optional true} [:set :int]] [:block/tx-id {:optional true} :int] [:block/collapsed? {:optional true} :boolean]]) @@ -227,11 +226,7 @@ "Common attributes for pages" [[:block/name :string] [:block/title :string] - [:block/alias {:optional true} [:set :int]] - ;; TODO: Should this be here or in common? - [:block/path-refs {:optional true} [:set :int]] - ;; file-based - [:block/namespace {:optional true} :int]]) + [:block/path-refs {:optional true} [:set :int]]]) (def property-attrs "Common attributes for properties" @@ -479,6 +474,7 @@ (let [malli-many-ref-attrs (->> (concat property-attrs page-attrs block-attrs page-or-block-attrs (rest closed-value-block*)) (filter #(= (last %) [:set :int])) (map first) + (into db-property/public-db-attribute-properties) set)] (when-let [undeclared-ref-attrs (seq (remove malli-many-ref-attrs db-schema/card-many-ref-type-attributes))] (throw (ex-info (str "The malli DB schema is missing the following cardinality-many ref attributes from datascript's schema: " diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index 2428bcfa77e..4ffba699a32 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -437,6 +437,11 @@ :block/created-at :block/updated-at :logseq.property.attribute/kv-value :logseq.property.attribute/property-schema-classes :logseq.property.attribute/property-value-content}) +(assert (= db-attribute-properties + (set (keep (fn [[k {:keys [attribute]}]] (when attribute k)) + built-in-properties))) + "All db attribute properties are configured in built-in-properties") + (def private-db-attribute-properties "db-attribute properties that are not visible to user" (->> db-attribute-properties @@ -451,11 +456,6 @@ "Property values that shouldn't be updated" #{:logseq.property/built-in?}) -(assert (= db-attribute-properties - (set (keep (fn [[k {:keys [attribute]}]] (when attribute k)) - built-in-properties))) - "All db attribute properties are configured in built-in-properties") - (def logseq-property-namespaces #{"logseq.property" "logseq.property.tldraw" "logseq.property.pdf" "logseq.property.fsrs" "logseq.task" "logseq.property.linked-references" "logseq.property.asset" "logseq.property.table" "logseq.property.node" From de339cb6f7f5576339462d91ceac4030944d75b3 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 14:58:44 -0500 Subject: [PATCH 096/105] fix(regression): file graphs can see and filter page type on all pages --- src/main/frontend/components/all_pages.cljs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/frontend/components/all_pages.cljs b/src/main/frontend/components/all_pages.cljs index 2971c953938..82c7c064f7a 100644 --- a/src/main/frontend/components/all_pages.cljs +++ b/src/main/frontend/components/all_pages.cljs @@ -21,6 +21,14 @@ :cell (fn [_table row _column] (component-block/page-cp {} row)) :type :string} + (when (not (config/db-based-graph? (state/get-current-repo))) + {:id :block/type + :name "Page type" + :cell (fn [_table row _column] + (let [type (get row :block/type)] + [:div.capitalize type])) + :get-value (fn [row] (get row :block/type)) + :type :string}) {:id :block.temp/refs-count :name (t :page/backlinks) :cell (fn [_table row _column] (:block.temp/refs-count row)) From 7fc777de96900ba8701a77b149cb9c4cde529567 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 16:54:44 -0500 Subject: [PATCH 097/105] fix: new property name allowed invalid characters --- deps/outliner/src/logseq/outliner/property.cljs | 2 ++ deps/outliner/src/logseq/outliner/validate.cljs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 2a6832a4094..2c180d027ab 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -160,6 +160,8 @@ db-ident' (db-ident/ensure-unique-db-ident @conn db-ident)] (assert (some? k-name) (prn "property-id: " property-id ", property-name: " property-name)) + (outliner-validate/validate-page-title property-name {:node {:db/ident db-ident'}}) + (outliner-validate/validate-page-title-characters property-name {:node {:db/ident db-ident'}}) (ldb/transact! conn [(sqlite-util/build-new-property db-ident' schema {:title k-name})] {:outliner-op :new-property}) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index 4d704fffccd..5e75ee0c019 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -96,8 +96,8 @@ (defn ^:api validate-unique-by-name-tag-and-block-type "Validates uniqueness of nodes for the following cases: - - Page names of type 'page' are unique by tag e.g. their can be Apple #Company and Apple #Fruit - - Page names of other types are unique for their type e.g. their can be #Journal ('class') and Journal ('page')" + - Page names are unique for a tag e.g. their can be Apple #Company and Apple #Fruit + - Page names are unique for a :logseq.property/parent" [db new-title entity] (when (ldb/page? entity) (validate-unique-for-page db new-title entity))) From 9d3b5a42bb08a897dc2e960aec391b8cef763035 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 17 Dec 2024 21:29:11 -0500 Subject: [PATCH 098/105] fix: new property validation and test from previous commit --- .../src/logseq/outliner/property.cljs | 4 +-- src/test/frontend/db/db_based_model_test.cljs | 28 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index 2c180d027ab..d594f066828 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -160,8 +160,8 @@ db-ident' (db-ident/ensure-unique-db-ident @conn db-ident)] (assert (some? k-name) (prn "property-id: " property-id ", property-name: " property-name)) - (outliner-validate/validate-page-title property-name {:node {:db/ident db-ident'}}) - (outliner-validate/validate-page-title-characters property-name {:node {:db/ident db-ident'}}) + (outliner-validate/validate-page-title k-name {:node {:db/ident db-ident'}}) + (outliner-validate/validate-page-title-characters k-name {:node {:db/ident db-ident'}}) (ldb/transact! conn [(sqlite-util/build-new-property db-ident' schema {:title k-name})] {:outliner-op :new-property}) diff --git a/src/test/frontend/db/db_based_model_test.cljs b/src/test/frontend/db/db_based_model_test.cljs index bea44bb7043..e66a7301f0c 100644 --- a/src/test/frontend/db/db_based_model_test.cljs +++ b/src/test/frontend/db/db_based_model_test.cljs @@ -4,9 +4,10 @@ [frontend.db :as db] [frontend.test.helper :as test-helper] [datascript.core :as d] - [logseq.outliner.property :as outliner-property] [logseq.db.frontend.class :as db-class] - [logseq.db :as ldb])) + [logseq.db :as ldb] + [logseq.db.test.helper :as db-test] + [frontend.db.conn :as conn])) (def repo test-helper/test-db-name-db-version) @@ -54,19 +55,16 @@ (:db/id (db/entity [:block/uuid sbid]))]))))) (deftest get-classes-with-property-test - (let [opts {:redirect? false :create-first-block? false :class? true} - _ (test-helper/create-page! "class1" opts) - _ (test-helper/create-page! "class2" opts) - class1 (db/get-case-page "class1") - class2 (db/get-case-page "class2") - conn (db/get-db false)] - (outliner-property/upsert-property! conn :user.property/property-1 {:type :node} {}) - (outliner-property/class-add-property! conn (:db/id class1) :user.property/property-1) - (outliner-property/class-add-property! conn (:db/id class2) :user.property/property-1) - (let [property (db/entity :user.property/property-1) - classes (model/get-classes-with-property (:db/ident property))] - (is (= (set (map :db/id classes)) - #{(:db/id class1) (:db/id class2)}))))) + (let [conn (db-test/create-conn-with-blocks + {:properties {:prop1 {:block/schema {:type :default}}} + :classes + {:Class1 {:build/schema-properties [:prop1]} + :Class2 {:build/schema-properties [:prop1]}}}) + property (d/entity @conn :user.property/prop1) + classes (with-redefs [conn/get-db (constantly @conn)] + (model/get-classes-with-property (:db/ident property)))] + (is (= ["Class1" "Class2"] + (map :block/title classes))))) (deftest hidden-page-test (let [opts {:redirect? false :create-first-block? false} From cc53500815a6585d982cdc3bec0c00353f1f3089 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 18 Dec 2024 11:16:41 -0500 Subject: [PATCH 099/105] fix(regression): Properties can't be renamed to a built-in name Also removed yet another duplicated find-by test helper --- .../src/logseq/outliner/validate.cljs | 37 ++++++++++------ .../test/logseq/outliner/validate_test.cljs | 42 ++++++++----------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index 5e75ee0c019..18fa8af8d3f 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -67,16 +67,27 @@ [db new-title {:block/keys [tags] :as entity}] (cond (seq tags) - (when-let [another-id (first (d/q '[:find [?b ...] - :in $ ?eid ?title [?tag-id ...] - :where - [?b :block/title ?title] - [?b :block/tags ?tag-id] - [(not= ?b ?eid)]] - db - (:db/id entity) - new-title - (map :db/id tags)))] + (when-let [another-id (first + (d/q (if (ldb/property? entity) + ;; Property names are unique in that they can + ;; have the same names as built-in property names + '[:find [?b ...] + :in $ ?eid ?title [?tag-id ...] + :where + [?b :block/title ?title] + [?b :block/tags ?tag-id] + [(missing? $ ?b :logseq.property/built-in?)] + [(not= ?b ?eid)]] + '[:find [?b ...] + :in $ ?eid ?title [?tag-id ...] + :where + [?b :block/title ?title] + [?b :block/tags ?tag-id] + [(not= ?b ?eid)]]) + db + (:db/id entity) + new-title + (map :db/id tags)))] (let [another (d/entity db another-id) this-tags (set (map :db/ident tags)) another-tags (set (map :db/ident (:block/tags another))) @@ -86,9 +97,9 @@ (> (count another-tags) 1)) (throw (ex-info "Duplicate page" {:type :notification - :payload {:message (str "Another page named " (pr-str new-title) " already exists for tags " - (pr-str - (map (fn [id] (:block/title (d/entity db id))) common-tag-ids))) + :payload {:message (str "Another page named " (pr-str new-title) " already exists for tags: " + (string/join ", " + (map (fn [id] (str "#" (:block/title (d/entity db id)))) common-tag-ids))) :type :warning}}))))) (:logseq.property/parent entity) diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index e532d24a6da..fd6cbede056 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -4,24 +4,16 @@ [logseq.outliner.validate :as outliner-validate] [logseq.db.test.helper :as db-test])) -(defn- find-block-by-content [conn content] - (->> content - (d/q '[:find [(pull ?b [* {:block/tags [:db/id :block/title :db/ident]}]) ...] - :in $ ?content - :where [?b :block/title ?content] [(missing? $ ?b :logseq.property/built-in?)]] - @conn) - first)) - (deftest validate-block-title-unique-for-properties (let [conn (db-test/create-conn-with-blocks - ;; use a property name that's same as built-in - {:properties {:background-image {:block/schema {:type :default}}}})] + {:properties {:color {:block/schema {:type :default}} + :color2 {:block/schema {:type :default}}}})] (is (nil? (outliner-validate/validate-unique-by-name-tag-and-block-type @conn - "background-color" - (assoc (find-block-by-content conn "background-image") :db/id 10000))) + (:block/title (d/entity @conn :logseq.property/background-color)) + (d/entity @conn :user.property/color))) "Allow user property to have same name as built-in property") (is (thrown-with-msg? @@ -29,14 +21,16 @@ #"Duplicate page" (outliner-validate/validate-unique-by-name-tag-and-block-type @conn - "background-image" - (assoc (find-block-by-content conn "background-image") :db/id 10000))) + "color" + (d/entity @conn :user.property/color2))) "Disallow duplicate user property"))) (deftest validate-block-title-unique-for-pages (let [conn (db-test/create-conn-with-blocks [{:page {:block/title "page1"}} + {:page {:block/title "another page"}} {:page {:block/title "Apple" :build/tags [:Company]}} + {:page {:block/title "Another Company" :build/tags [:Company]}} {:page {:block/title "Banana" :build/tags [:Fruit]}}])] (is (thrown-with-msg? @@ -45,13 +39,13 @@ (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "Apple" - (assoc (find-block-by-content conn "Apple") :db/id 10000))) + (db-test/find-page-by-title @conn "Another Company"))) "Disallow duplicate page with tag") (is (nil? (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "Apple" - (find-block-by-content conn "Banana"))) + (db-test/find-page-by-title @conn "Banana"))) "Allow page with same name for different tag") (is (thrown-with-msg? @@ -60,14 +54,14 @@ (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "page1" - (assoc (find-block-by-content conn "page1") :db/id 10000))) + (db-test/find-page-by-title @conn "another page"))) "Disallow duplicate page without tag") (is (nil? (outliner-validate/validate-unique-by-name-tag-and-block-type @conn "Apple" - (find-block-by-content conn "Fruit"))) + (db-test/find-page-by-title @conn "Fruit"))) "Allow class to have same name as a page"))) (deftest validate-parent-property @@ -77,11 +71,11 @@ :pages-and-blocks [{:page {:block/title "page1"}} {:page {:block/title "page2"}}]}) - page1 (find-block-by-content conn "page1") - page2 (find-block-by-content conn "page2") - class1 (find-block-by-content conn "Class1") - class2 (find-block-by-content conn "Class2") - property (find-block-by-content conn "prop1")] + page1 (db-test/find-page-by-title @conn "page1") + page2 (db-test/find-page-by-title @conn "page2") + class1 (db-test/find-page-by-title @conn "Class1") + class2 (db-test/find-page-by-title @conn "Class2") + property (db-test/find-page-by-title @conn "prop1")] (testing "valid parent and child combinations" (is (nil? (outliner-validate/validate-parent-property page1 [page2])) @@ -113,7 +107,7 @@ {:pages-and-blocks [{:page {:block/title "page1"} :blocks [{:block/title "block"}]}]}) - block (find-block-by-content conn "block")] + block (db-test/find-block-by-content @conn "block")] (is (thrown-with-msg? js/Error From 3638644dab0cc84719866b8f822a9280b691d4b0 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 18 Dec 2024 15:09:09 -0500 Subject: [PATCH 100/105] enhance: improve :block/tags validation The move from :block/type to :block/tags allows for a couple types of bugs that weren't possible before. Add validations to ensure built-in entities aren't being accidentally modified by the UI and that :logseq.class/Page remains simple and a useful tag for features later. The Page validation is turned off until an importer bug is fixed --- .../src/logseq/db/frontend/malli_schema.cljs | 80 +++++++++++++------ 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index f0a4fc8c7a5..5f29df33a57 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -113,30 +113,47 @@ (set (get-in db-class/built-in-classes [:logseq.class/Asset :schema :required-properties])) #{:logseq.property/created-from-property})) +(defn- property-entity->map + "Provide the minimal number of property attributes to validate the property + and to reduce noise in error messages. The resulting map should be the same as + what the frontend property since they both call validate-property-value" + [property] + ;; use explicit call to be nbb compatible + (let [closed-values (entity-plus/lookup-kv-then-entity property :property/closed-values)] + (cond-> (assoc (select-keys property [:db/ident :db/valueType :db/cardinality]) + :block/schema + (select-keys (:block/schema property) [:type])) + (seq closed-values) + (assoc :property/closed-values closed-values)))) + (defn update-properties-in-ents "Prepares properties in entities to be validated by DB schema" [db ents] - (mapv - (fn [ent] - (reduce (fn [m [k v]] - (if-let [property (and (db-property/property? k) - ;; This allows schemas like property-value-block to require properties in - ;; their schema that they depend on - (not (contains? required-properties k)) - (d/entity db k))] - (update m :block/properties (fnil conj []) - ;; use explicit call to be nbb compatible - [(let [closed-values (entity-plus/lookup-kv-then-entity property :property/closed-values)] - (cond-> (assoc (select-keys property [:db/ident :db/valueType :db/cardinality]) - :block/schema - (select-keys (:block/schema property) [:type])) - (seq closed-values) - (assoc :property/closed-values closed-values))) - v]) - (assoc m k v))) - {} - ent)) - ents)) + ;; required-properties allows schemas like property-value-block to require + ;; properties in their schema that they depend on + (let [exceptions-to-block-properties (conj required-properties :block/tags) + page-class-id (:db/id (d/entity db :logseq.class/Page)) + private-tag-ids (set (map #(:db/id (d/entity db %)) db-class/private-tags))] + (mapv + (fn [ent] + (reduce (fn [m [k v]] + (if-let [property (and (db-property/property? k) + (not (contains? exceptions-to-block-properties k)) + (d/entity db k))] + (update m :block/properties (fnil conj []) + [(property-entity->map property) v]) + (if (= :block/tags k) + ;; Provides additional options map to validation for data about current entity being tagged + (let [property (d/entity db :block/tags)] + (assoc m k [(property-entity->map property) + v + (merge (select-keys ent [:logseq.property/built-in?]) + {:page-class-id page-class-id + :private-tag-ids private-tag-ids})])) + (assoc m k v)))) + {} + ent)) + ents))) (defn datoms->entity-maps "Returns entity maps for given :eavt datoms indexed by db/id. Optional keys: @@ -193,8 +210,7 @@ nil) (def property-tuple - "A tuple of a property map and a property value. This schema - has 1 metadata hook which is used to inject a datascript db later" + "A tuple of a property map and a property value" (into [:multi {:dispatch #(-> % first :block/schema :type)}] (map (fn [[prop-type value-schema]] @@ -210,6 +226,23 @@ property with its property value that is valid for its type" [:sequential property-tuple]) +(def block-tags + [:and + property-tuple + ;; Important to keep data integrity of built-in entities. Ensure UI doesn't accidentally modify them + [:fn {:error/message "should only have one tag for a built-in entity"} + (fn [[_k v opts]] + (if (:logseq.property/built-in? opts) + (= 1 (count v)) + true))] + ;; Ensure use of :logseq.class/Page is consistent and simple. Doing so reduces complexity elsewhere + ;; and allows for Page to exist as its own public concept later + #_[:fn {:error/message "should not have other built-in private tags when tagged with #Page"} + (fn [[_k v {:keys [page-class-id private-tag-ids]}]] + (if (contains? v page-class-id) + (empty? (set/intersection (disj v page-class-id) private-tag-ids)) + true))]]) + (def page-or-block-attrs "Common attributes for page and normal blocks" [[:block/uuid :uuid] @@ -218,6 +251,7 @@ [:block/format [:enum :markdown]] ;; Injected by update-properties-in-ents [:block/properties {:optional true} block-properties] + [:block/tags {:optional true} block-tags] [:block/refs {:optional true} [:set :int]] [:block/tx-id {:optional true} :int] [:block/collapsed? {:optional true} :boolean]]) From e5b32b16f5020cbe4f7cc5e2efe4b1a9676b6e31 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 18 Dec 2024 15:55:42 -0500 Subject: [PATCH 101/105] fix: invalid entity when user class is used as a property --- deps/db/src/logseq/db/frontend/malli_schema.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 5f29df33a57..a92fa31539d 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -329,7 +329,8 @@ (vec (concat [:map - [:db/ident user-property-ident] + ;; class-ident allows for a class to be used as a property + [:db/ident [:or user-property-ident class-ident]] [:block/schema user-property-schema]] property-attrs page-attrs From c7343b259f838ca421acfa0ace2c7366b1112ec9 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 18 Dec 2024 16:20:10 -0500 Subject: [PATCH 102/105] fix: tagging built-in properties and pages Mustn't be possible to modify built-ins as this could wreak havok on lower level properties or cause built-in features to behave buggily --- .../src/logseq/outliner/validate.cljs | 9 ++++----- .../test/logseq/outliner/validate_test.cljs | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index 18fa8af8d3f..b68489511d2 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -173,12 +173,11 @@ (defn- disallow-tagging-a-built-in-class [db block-eids] - (when-let [built-in-tag - (some #(when (get db-class/built-in-classes (:db/ident %)) %) - (map #(d/entity db %) block-eids))] - (throw (ex-info (str "Can't add tag to built-in #" (:block/title built-in-tag)) + (when-let [built-in-ent (some #(when (:logseq.property/built-in? %) %) + (map #(d/entity db %) block-eids))] + (throw (ex-info (str "Can't add tag on built-in " (pr-str (:block/title built-in-ent))) {:type :notification - :payload {:message (str "Can't add tag on built-in #" (:block/title built-in-tag)) + :payload {:message (str "Can't add tag on built-in " (pr-str (:block/title built-in-ent))) :type :error}})))) (defn validate-tags-property diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index fd6cbede056..c34dc4a04d1 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -104,7 +104,8 @@ (deftest validate-tags-property (let [conn (db-test/create-conn-with-blocks - {:pages-and-blocks + {:classes {:SomeTag {}} + :pages-and-blocks [{:page {:block/title "page1"} :blocks [{:block/title "block"}]}]}) block (db-test/find-block-by-content @conn "block")] @@ -112,8 +113,20 @@ (is (thrown-with-msg? js/Error #"Can't add tag.*Tag" - (outliner-validate/validate-tags-property @conn [:logseq.class/Tag] :logseq.class/Asset)) - ":logseq.class/Tag must not be tagged by the user") + (outliner-validate/validate-tags-property @conn [:logseq.class/Tag] :user.class/SomeTag)) + "built-in tag must not be tagged by the user") + + (is (thrown-with-msg? + js/Error + #"Can't add tag.*Heading" + (outliner-validate/validate-tags-property @conn [:logseq.property/heading] :user.class/SomeTag)) + "built-in property must not be tagged by the user") + + (is (thrown-with-msg? + js/Error + #"Can't add tag.*Contents" + (outliner-validate/validate-tags-property @conn [(:db/id (db-test/find-page-by-title @conn "Contents"))] :user.class/SomeTag)) + "built-in page must not be tagged by the user") (is (thrown-with-msg? js/Error From 9ba95a0beb07fc6b51fa728dc712b4ee933b618c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 18 Dec 2024 17:11:11 -0500 Subject: [PATCH 103/105] fix: tagging with a built-in property causes invalid blocks Display error to user rather than cause invalid state --- .../src/logseq/db/frontend/malli_schema.cljs | 1 + .../outliner/src/logseq/outliner/validate.cljs | 18 +++++++++++++++--- .../test/logseq/outliner/validate_test.cljs | 8 +++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index a92fa31539d..1a4abdcb37e 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -228,6 +228,7 @@ (def block-tags [:and + ;; FIXME: Display error message instead of 'unknown error' property-tuple ;; Important to keep data integrity of built-in entities. Ensure UI doesn't accidentally modify them [:fn {:error/message "should only have one tag for a built-in entity"} diff --git a/deps/outliner/src/logseq/outliner/validate.cljs b/deps/outliner/src/logseq/outliner/validate.cljs index b68489511d2..4e0c648ff73 100644 --- a/deps/outliner/src/logseq/outliner/validate.cljs +++ b/deps/outliner/src/logseq/outliner/validate.cljs @@ -157,6 +157,17 @@ (disallow-built-in-class-parent-change parent-ent child-ents) (validate-parent-property-have-same-type parent-ent child-ents)) +(defn- disallow-node-cant-tag-with-built-in-non-tags + [db _block-eids v] + (let [tag-ent (d/entity db v)] + (when (and (:logseq.property/built-in? tag-ent) + (not (ldb/class? tag-ent))) + (throw (ex-info (str "Can't set tag with built-in page that isn't a tag " (pr-str (:block/title tag-ent))) + {:type :notification + :payload {:message (str "Can't set tag with built-in page that isn't a tag " (pr-str (:block/title tag-ent))) + :type :error} + :property-value v}))))) + (defn- disallow-node-cant-tag-with-private-tags [db block-eids v] (when (and (ldb/private-tags (:db/ident (d/entity db v))) @@ -171,7 +182,7 @@ :property-id :block/tags :property-value v})))) -(defn- disallow-tagging-a-built-in-class +(defn- disallow-tagging-a-built-in-entity [db block-eids] (when-let [built-in-ent (some #(when (:logseq.property/built-in? %) %) (map #(d/entity db %) block-eids))] @@ -182,5 +193,6 @@ (defn validate-tags-property [db block-eids v] - (disallow-tagging-a-built-in-class db block-eids) - (disallow-node-cant-tag-with-private-tags db block-eids v)) \ No newline at end of file + (disallow-tagging-a-built-in-entity db block-eids) + (disallow-node-cant-tag-with-private-tags db block-eids v) + (disallow-node-cant-tag-with-built-in-non-tags db block-eids v)) \ No newline at end of file diff --git a/deps/outliner/test/logseq/outliner/validate_test.cljs b/deps/outliner/test/logseq/outliner/validate_test.cljs index c34dc4a04d1..994bc9b6ed5 100644 --- a/deps/outliner/test/logseq/outliner/validate_test.cljs +++ b/deps/outliner/test/logseq/outliner/validate_test.cljs @@ -132,7 +132,13 @@ js/Error #"Can't set tag.*Page" (outliner-validate/validate-tags-property @conn [(:db/id block)] :logseq.class/Page)) - "Nodes can't be tagged with type tags"))) + "Nodes can't be tagged with built-in private tags") + + (is (thrown-with-msg? + js/Error + #"Can't set tag.*Priority" + (outliner-validate/validate-tags-property @conn [(:db/id block)] :logseq.task/priority)) + "Nodes can't be tagged with built-in non tags"))) ;; Try as many of the validations against a new graph to confirm ;; that validations make sense and are valid for a new graph From b2d6aa495673f28bbc5d635344be03852274d19c Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 19 Dec 2024 15:17:51 -0500 Subject: [PATCH 104/105] fix: accidental temp file from last merge --- .clj-kondo/funcool/promesa/config.edn | 9 --------- .gitignore | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 .clj-kondo/funcool/promesa/config.edn diff --git a/.clj-kondo/funcool/promesa/config.edn b/.clj-kondo/funcool/promesa/config.edn deleted file mode 100644 index 1844f77c5a6..00000000000 --- a/.clj-kondo/funcool/promesa/config.edn +++ /dev/null @@ -1,9 +0,0 @@ -{:lint-as {promesa.core/-> clojure.core/-> - promesa.core/->> clojure.core/->> - promesa.core/as-> clojure.core/as-> - promesa.core/let clojure.core/let - promesa.core/plet clojure.core/let - promesa.core/loop clojure.core/loop - promesa.core/recur clojure.core/recur - promesa.core/with-redefs clojure.core/with-redefs - promesa.core/doseq clojure.core/doseq}} diff --git a/.gitignore b/.gitignore index 2e0a2f54ffa..ca297e90df1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ resources/electron.js .clj-kondo/metosin/malli .clj-kondo/rewrite-clj .clj-kondo/taoensso +.clj-kondo/funcool /libs/dist/ charlie/ .vscode From 48cb4d2f9cd389f4dcc3498f307f0b727609864f Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 24 Dec 2024 17:44:37 +0800 Subject: [PATCH 105/105] fix: unresolved conflict --- src/main/frontend/worker/db/migrate.cljs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/frontend/worker/db/migrate.cljs b/src/main/frontend/worker/db/migrate.cljs index c10ced6dc02..13c82de276a 100644 --- a/src/main/frontend/worker/db/migrate.cljs +++ b/src/main/frontend/worker/db/migrate.cljs @@ -417,7 +417,6 @@ :block/title title'}))))) datoms))) -<<<<<<< HEAD (defn- replace-block-type-with-tags [conn _search-db] (let [db @conn @@ -445,7 +444,7 @@ [[:db/add (:db/id journal-entity) :block/tags :logseq.class/Page]] tx-data [[:db/retractEntity (:db/id block-type-entity)]]))) -======= + (defn- deprecate-logseq-user-ns [conn _search-db] (let [db @conn] @@ -464,7 +463,6 @@ [:db/retract e :logseq.user/email] [:db/retract e :logseq.user/avatar]]) db-ids)))))) ->>>>>>> feat/db (def schema-version->updates "A vec of tuples defining datascript migrations. Each tuple consists of the