Tienson Qin 2 лет назад
Родитель
Сommit
0c0bad073a

+ 47 - 23
src/main/frontend/components/property.cljs

@@ -3,42 +3,66 @@
   (:require [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.handler.property :as property-handler]
+            [frontend.handler.ui :as ui-handler]
             [frontend.db :as db]
             [rum.core :as rum]
             [frontend.state :as state]
             [frontend.mixins :as mixins]
-            [clojure.edn :as edn]))
+            [clojure.edn :as edn]
+            [clojure.string :as string]))
 
-(rum/defcs property-class-config <
+(rum/defcs property-config <
   rum/static
   (rum/local nil ::property-name)
   (rum/local nil ::property-schema)
   {:will-mount (fn [state]
-                 (let [[repo property-uuid] (:rum/args state)]
-                   (reset! (::property-name state) (:block/name (db/pull repo '[*] [:block/uuid property-uuid])))
-                   (reset! (::property-schema state) (:block/schema (db/pull repo '[*] [:block/uuid property-uuid])))
+                 (let [[repo property-uuid] (:rum/args state)
+                       property (db/pull repo '[*] [:block/uuid property-uuid])]
+                   (reset! (::property-name state) (:block/name property))
+                   (reset! (::property-schema state) (:block/schema property))
                    state))}
   [state repo property-uuid]
   (let [*property-name (::property-name state)
         *property-schema (::property-schema state)]
-    [:div
-     [:div
-      [:span.mr-8 "property name:"]
-      [:input
+    [:div.property-configure
+     [:h1.title "Configure property"]
+
+     [:div.grid.gap-2.p-1
+      [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+      [:label.cols-1 "Name:"]
+      [:input.form-input
        {:on-change #(reset! *property-name (util/evalue %))
         :value @*property-name}]]
+
+     [:div.grid.grid-cols-4.gap-1.leading-8
+      [:label.cols-1 "Schema type:"]
+      (let [schema-types (->> (keys property-handler/builtin-schema-types)
+                              (map (comp string/capitalize name))
+                              (map (fn [type]
+                                     {:label type
+                                      :value type
+                                      :selected (= (keyword (string/lower-case type))
+                                                   (:type @*property-schema))})))]
+        (ui/select schema-types
+         (fn [_e v]
+           (let [type (keyword (string/lower-case v))]
+             (swap! *property-schema assoc :type type)))))]
+
+     [:div.grid.grid-cols-4.gap-1.items-center.leading-8
+      [:label.cols-1 "Multiple values:"]
+      (ui/checkbox {:checked (= :many (:cardinality @*property-schema))
+                    :on-change (fn [v]
+                                 (swap! *property-schema assoc :cardinality (if (= "on" (util/evalue v)) :many :one)))})]
+
      [:div
-      [:span.mr-8 "property schema:"]
-      [:input
-       {:on-change #(reset! *property-schema (util/evalue %))
-        :value (str @*property-schema)}]]
-     [:a
-      {:on-click (fn []
-                   (property-handler/update-property-class!
-                    repo property-uuid
-                    {:property-name @*property-name
-                     :property-schema (edn/read-string @*property-schema)}))}
-      "Save"]]))
+      (ui/button
+        "Save"
+        :on-click (fn []
+                    (property-handler/update-property!
+                     repo property-uuid
+                     {:property-name @*property-name
+                      :property-schema @*property-schema})
+                    (state/close-modal!)))]]]))
 
 (rum/defcs new-property < rum/reactive
   (rum/local nil ::property-key)
@@ -83,11 +107,11 @@
        [:div
         (for [[prop-uuid-or-built-in-prop v] properties]
           (if (uuid? prop-uuid-or-built-in-prop)
-            (when-let [property-class (db/pull [:block/uuid prop-uuid-or-built-in-prop])]
+            (when-let [property (db/pull [:block/uuid prop-uuid-or-built-in-prop])]
               [:div
                [:a.mr-2
-                {:on-click (fn [] (state/set-modal! #(property-class-config repo prop-uuid-or-built-in-prop)))}
-                (:block/name property-class)]
+                {:on-click (fn [] (state/set-modal! #(property-config repo prop-uuid-or-built-in-prop)))}
+                (:block/name property)]
                [:span (or (get properties-text-values prop-uuid-or-built-in-prop) (str v))]
                [:a.ml-8 {:on-click
                          (fn []

+ 103 - 140
src/main/frontend/handler/property.cljs

@@ -2,6 +2,7 @@
   "Block properties handler."
   (:require [clojure.edn :as edn]
             [clojure.string :as string]
+            [clojure.set :as set]
             [frontend.db :as db]
             [frontend.format.block :as block]
             [frontend.handler.notification :as notification]
@@ -14,11 +15,27 @@
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util.page-ref :as page-ref]
             [malli.util :as mu]
-            [malli.core :as m]))
+            [malli.core :as m]
+            [malli.error :as me]))
+
+(defn- date-str?
+  [value]
+  (when-let [d (js/Date. value)]
+    (not= (str d) "Invalid Date")))
 
 (def builtin-schema-types
-  {:string-contains-refs :string        ;default
-   :refs [:sequential :string]})
+  {:default string?                     ; default, might be mixed with refs, tags
+   :number  number?
+   :date    inst?
+   :boolean boolean?
+   :url     uri?
+   :object  uuid?})                     ; TODO: make sure block exists
+
+;; schema -> type, cardinality, object's class
+;;           min, max -> string length, number range, cardinality size limit
+
+(def builtin-schema->type
+  (set/map-invert builtin-schema-types))
 
 (def ^:private gp-mldoc-config (gp-mldoc/default-config :markdown))
 
@@ -31,79 +48,77 @@
                    distinct)]
     refs'))
 
-(defn- is-type-x?
-  [schema-ast x]
-  (or (= x (:type schema-ast))
-      (and (= :and (:type schema-ast))
-           (some #(= x (:type %)) (:children schema-ast)))))
-
-(defn- schema-base-type
-  [schema]
-  (when-let [ast (try (m/ast schema) (catch :default _))]
-    (cond
-      (is-type-x? ast :int)
-      :int
-
-      (is-type-x? ast :float)
-      :float
-
-      (is-type-x? ast :string)
-      :string
-
-      :else
-      nil)))
-
 (defn- infer-schema-from-input-string
   [v-str]
   (cond
-    (parse-long v-str) :int
-    (parse-double v-str) :float
-    :else nil))
+    (parse-long v-str) :number
+    (parse-double v-str) :number
+    (util/uuid-string? v-str) :object
+    (gp-util/url? v-str) :url
+    (date-str? v-str) :date
+    (contains? #{"true" "false"} (string/lower-case v-str)) :boolean
+    :else :default))
 
 (defn convert-property-input-string
-  [schema v-str]
-  (case (schema-base-type schema)
-    :string
+  [schema-type v-str]
+  (case schema-type
+    :default
     v-str
 
-    (:int :float nil)
-    (edn/read-string v-str)))
+    :number
+    (edn/read-string v-str)
+
+    :boolean
+    (edn/read-string (string/lower-case v-str))
+
+    :object
+    (uuid v-str)
+
+    :date
+    (js/Date. v-str)
+
+    :url
+    (goog.Uri. v-str)))
 
 (defn add-property!
   [repo block k-name v]
-  (let [property-class      (db/pull repo '[*] [:block/name k-name])
-        property-class-uuid (or (:block/uuid property-class) (random-uuid))
-        property-schema (:block/schema property-class)
+  (let [property      (db/pull repo '[*] [:block/name k-name])
+        property-uuid (or (:block/uuid property) (random-uuid))
+        existing-schema (:block/schema property)
+        property-type (:type existing-schema)
         infer-schema (infer-schema-from-input-string v)
-        property-schema (or property-schema infer-schema :string-contains-refs)
-        schema (get builtin-schema-types property-schema property-schema)]
+        property-type (or property-type infer-schema :default)
+        schema (get builtin-schema-types property-type)]
     (when-let [v* (try
-                    (convert-property-input-string schema v)
+                    (convert-property-input-string property-type v)
                     (catch :default e
                       (notification/show! (str e) :error false)
                       nil))]
-      (if-let [msg (malli.util/explain-data schema v*)]
-        (notification/show! (str msg) :error false)
-        (do (when (nil? property-class) ;if property-class not exists yet
-              (db/transact! repo [{:block/schema property-schema
-                                   :block/name k-name
-                                   :block/uuid property-class-uuid
-                                   :block/type "property"}]))
+      (if-let [msg (me/humanize (mu/explain-data schema v*))]
+        (notification/show! msg :error false)
+        (do (when (nil? property) ;if property not exists yet
+              (db/transact! repo [(outliner-core/block-with-timestamps
+                                   {:block/schema {:type property-type}
+                                    :block/original-name k-name
+                                    :block/name (util/page-name-sanity-lc k-name)
+                                    :block/uuid property-uuid
+                                    :block/type "property"})]))
             (let [block-properties (assoc (:block/properties block)
-                                          property-class-uuid
-                                          (if (= property-schema :string-contains-refs)
-                                            (set (extract-page-refs-from-prop-str-value v*))
+                                          property-uuid
+                                          (if (= property-type :default)
+                                            (let [refs (extract-page-refs-from-prop-str-value v*)]
+                                              (if (seq refs) (set refs) v*))
                                             v*))
                   block-properties-text-values
-                  (if (= property-schema :string-contains-refs)
-                    (assoc (:block/properties-text-values block) property-class-uuid v*)
-                    (dissoc (:block/properties-text-values block) property-class-uuid))]
+                  (if (= property-type :default)
+                    (assoc (:block/properties-text-values block) property-uuid v*)
+                    (dissoc (:block/properties-text-values block) property-uuid))]
               (outliner-tx/transact!
-               {:outliner-op :save-block}
-               (outliner-core/save-block!
-                {:block/uuid (:block/uuid block)
-                 :block/properties block-properties
-                 :block/properties-text-values block-properties-text-values}))))))))
+                {:outliner-op :save-block}
+                (outliner-core/save-block!
+                 {:block/uuid (:block/uuid block)
+                  :block/properties block-properties
+                  :block/properties-text-values block-properties-text-values}))))))))
 
 (defn remove-property!
   [repo block k-uuid-or-builtin-k-name]
@@ -111,29 +126,27 @@
   (let [origin-properties (:block/properties block)]
     (assert (contains? (set (keys origin-properties)) k-uuid-or-builtin-k-name))
     (db/transact!
-     repo
-     [{:block/uuid (:block/uuid block)
-       :block/properties (dissoc origin-properties k-uuid-or-builtin-k-name)
-       :block/properties-text-values (dissoc (:block/properties-text-values block) k-uuid-or-builtin-k-name)}])))
+      repo
+      [{:block/uuid (:block/uuid block)
+        :block/properties (dissoc origin-properties k-uuid-or-builtin-k-name)
+        :block/properties-text-values (dissoc (:block/properties-text-values block) k-uuid-or-builtin-k-name)}])))
 
-
-(defn update-property-class!
+(defn update-property!
   [repo property-uuid {:keys [property-name property-schema]}]
   {:pre [(uuid? property-uuid)]}
   (let [tx-data (cond-> {:block/uuid property-uuid}
                   property-name (assoc :block/name property-name)
-                  property-schema (assoc :block/schema property-schema))]
+                  property-schema (assoc :block/schema property-schema)
+                  true outliner-core/block-with-updated-at)]
     (db/transact! repo [tx-data])))
 
-
-
 (defn- extract-refs
   [entity properties]
   (let [property-values (->>
                          properties
                          (map (fn [[k v]]
-                                (let [schema (:block/property-schema (db/pull [:block/uuid k]))
-                                      object? (= (:type schema) "object")
+                                (let [schema (:block/schema (db/pull [:block/uuid k]))
+                                      object? (= (:type schema) :object)
                                       f (if object? page-ref/->page-ref identity)]
                                   (->> (if (coll? v)
                                          v
@@ -155,78 +168,28 @@
             [:block/uuid (uuid %)]
             (block/page-name->map % true)) refs')))
 
-(defn validate
-  "Check whether the `value` validate against the `schema`."
-  [schema value]
-  (if (string/blank? value)
-    [true value]
-    (case (:type schema)
-      "any" [true value]
-      "number" (if-let [n (parse-double value)]
-                 (let [[min-n max-n] [(:min schema) (:max schema)]
-                       min-result (if min-n (>= n min-n) true)
-                       max-result (if max-n (<= n max-n) true)]
-                   (cond
-                     (and min-result max-result)
-                     [true n]
-
-                     (false? min-result)
-                     [false (str "the min value is " min-n)]
-
-                     (false? max-result)
-                     [false (str "the max value is " max-n)]
-
-                     :else
-                     n))
-                 [false "invalid number"])
-      "date" (if-let [result (js/Date. value)]
-               (if (not= (str result) "Invalid Date")
-                 [true value]
-                 [false "invalid date"])
-               [false "invalid date"])
-      "url" (if (gp-util/url? value)
-              [true value]
-              [false "invalid URL"])
-      "object" (let [page-name (or
-                                (try
-                                  (page-ref/get-page-name value)
-                                  (catch :default _))
-                                value)]
-                 [true page-name]))))
-
-(defn delete-property!
-  [entity property-id]
-  (when (and entity (uuid? property-id))
-    (let [properties' (dissoc (:block/properties entity) property-id)
-          refs (extract-refs entity properties')]
-      (outliner-tx/transact!
-        {:outliner-op :save-block}
-        (outliner-core/save-block!
-         {:block/uuid (:block/uuid entity)
-          :block/properties properties'
-          :block/refs refs})))))
-
-(defn delete-property-value!
-  "Delete value if a property has multiple values"
-  [entity property-id property-value]
-  (when (and entity (uuid? property-id))
-    (when (not= property-id (:block/uuid entity))
-      (when-let [property (db/pull [:block/uuid property-id])]
-        (let [schema (:block/property-schema property)
-              [success? property-value-or-error] (validate schema property-value)
-              multiple-values? (:multiple-values? schema)]
-          (when (and multiple-values? success?)
-            (let [properties (:block/properties entity)
-                  properties' (update properties property-id disj property-value-or-error)
-                  refs (extract-refs entity properties')]
-              (outliner-tx/transact!
-               {:outliner-op :save-block}
-               (outliner-core/save-block!
-                {:block/uuid (:block/uuid entity)
-                 :block/properties properties'
-                 :block/refs refs}))))
-          (state/clear-editor-action!)
-          (state/clear-edit!))))))
+(comment
+  (defn delete-property-value!
+    "Delete value if a property has multiple values"
+    [entity property-id property-value]
+    (when (and entity (uuid? property-id))
+      (when (not= property-id (:block/uuid entity))
+        (when-let [property (db/pull [:block/uuid property-id])]
+          (let [schema (:block/schema property)
+                [success? property-value-or-error] (validate schema property-value)
+                multiple-values? (:multiple-values? schema)]
+            (when (and multiple-values? success?)
+              (let [properties (:block/properties entity)
+                    properties' (update properties property-id disj property-value-or-error)
+                    refs (extract-refs entity properties')]
+                (outliner-tx/transact!
+                  {:outliner-op :save-block}
+                  (outliner-core/save-block!
+                   {:block/uuid (:block/uuid entity)
+                    :block/properties properties'
+                    :block/refs refs}))))
+            (state/clear-editor-action!)
+            (state/clear-edit!)))))))
 
 (defn set-editing-new-property!
   [value]

+ 5 - 0
src/main/frontend/modules/outliner/core.cljs

@@ -63,6 +63,11 @@
                 (assoc :block/created-at updated-at))]
     block))
 
+(defn block-with-updated-at
+  [block]
+  (let [updated-at (util/time-ms)]
+    (assoc block :block/updated-at updated-at)))
+
 (defn- remove-orphaned-page-refs!
   [db-id txs-state old-refs new-refs]
   (when (not= old-refs new-refs)

+ 4 - 2
src/main/frontend/util/property.cljs

@@ -31,7 +31,9 @@
 (defn properties-hidden?
   [properties]
   (and (seq properties)
-       (let [ks (map (comp keyword string/lower-case name) (keys properties))
+       (let [ks (if (config/db-based-graph? (state/get-current-repo))
+                  (map #(:block/name (db/entity [:block/uuid %])) (keys properties))
+                  (map (comp keyword string/lower-case name) (keys properties)))
              hidden-properties-set (hidden-properties)]
          (every? hidden-properties-set ks))))
 
@@ -462,4 +464,4 @@
     (if (seq properties-order)
       (keep (fn [k] (when (contains? properties k) [k (get properties k)]))
             (distinct properties-order))
-      properties*)))
+      properties*)))