Răsfoiți Sursa

refactor: make all user-facing property types refs

Tienson Qin 1 an în urmă
părinte
comite
11c902eb1e

+ 2 - 1
deps/db/src/logseq/db/frontend/entity_plus.cljc

@@ -48,7 +48,8 @@
 
 
      :block/_parent
      :block/_parent
      (->> (lookup-entity e k default-value)
      (->> (lookup-entity e k default-value)
-          (remove (fn [e] (:logseq.property/created-from-property e)))
+          (remove (fn [e] (or (:logseq.property/created-from-property e)
+                              (:block/closed-value-property e))))
           seq)
           seq)
 
 
      :block/_raw-parent
      :block/_raw-parent

+ 13 - 21
deps/db/src/logseq/db/frontend/property/build.cljs

@@ -7,21 +7,22 @@
 (defonce hidden-page-name-prefix "$$$")
 (defonce hidden-page-name-prefix "$$$")
 
 
 (defn- closed-value-new-block
 (defn- closed-value-new-block
-  [page-id block-id value property]
-  {:block/type #{"closed value"}
-   :block/format :markdown
-   :block/uuid block-id
-   :block/page page-id
-   :block/content value
-   :block/closed-value-property (:db/ident property)
-   :block/parent page-id})
+  [block-id value property]
+  (let [property-id (:db/ident property)]
+    {:block/type #{"closed value"}
+     :block/format :markdown
+     :block/uuid block-id
+     :block/page property-id
+     :block/content value
+     :block/closed-value-property property-id
+     :block/parent property-id}))
 
 
 (defn build-closed-value-block
 (defn build-closed-value-block
   "Builds a closed value block to be transacted"
   "Builds a closed value block to be transacted"
-  [block-uuid block-value page-id property {:keys [db-ident icon description]}]
+  [block-uuid block-value property {:keys [db-ident icon description]}]
   (assert block-uuid (uuid? block-uuid))
   (assert block-uuid (uuid? block-uuid))
   (cond->
   (cond->
-   (closed-value-new-block page-id block-uuid block-value property)
+   (closed-value-new-block block-uuid block-value property)
     (and db-ident (keyword? db-ident))
     (and db-ident (keyword? db-ident))
     (assoc :db/ident db-ident)
     (assoc :db/ident db-ident)
 
 
@@ -38,13 +39,6 @@
     true
     true
     sqlite-util/block-with-timestamps))
     sqlite-util/block-with-timestamps))
 
 
-(defn build-property-hidden-page
-  "Builds a hidden property page for closed values to be transacted"
-  [property]
-  (let [page-name (str hidden-page-name-prefix (:block/uuid property))]
-    (-> (sqlite-util/build-new-page page-name)
-        (assoc :block/type #{"hidden"}))))
-
 (defn build-closed-values
 (defn build-closed-values
   "Builds all the tx needed for property with closed values including
   "Builds all the tx needed for property with closed values including
    the hidden page and closed value blocks as needed"
    the hidden page and closed value blocks as needed"
@@ -59,18 +53,16 @@
         ;; closed ref types don't have hidden tx
         ;; closed ref types don't have hidden tx
         (if ref-type?
         (if ref-type?
           []
           []
-          (let [page-tx (build-property-hidden-page property-tx)
-                closed-value-blocks-tx
+          (let [closed-value-blocks-tx
                 (map (fn [{:keys [db-ident value icon description uuid]}]
                 (map (fn [{:keys [db-ident value icon description uuid]}]
                        (cond->
                        (cond->
                         (build-closed-value-block
                         (build-closed-value-block
                          uuid
                          uuid
                          value
                          value
-                         [:block/uuid (:block/uuid page-tx)]
                          property
                          property
                          {:db-ident db-ident :icon icon :description description})
                          {:db-ident db-ident :icon icon :description description})
                          (not from-ui-thread?)
                          (not from-ui-thread?)
                          (assoc :block/order (db-order/gen-key))))
                          (assoc :block/order (db-order/gen-key))))
                      (:closed-values property))]
                      (:closed-values property))]
-            (into [page-tx] closed-value-blocks-tx)))]
+            closed-value-blocks-tx))]
     (into [property-tx] hidden-tx)))
     (into [property-tx] hidden-tx)))

+ 18 - 16
deps/db/src/logseq/db/frontend/property/type.cljs

@@ -2,8 +2,7 @@
   "Provides property types and related helper fns e.g. property value validation
   "Provides property types and related helper fns e.g. property value validation
   fns and their allowed schema attributes"
   fns and their allowed schema attributes"
   (:require [datascript.core :as d]
   (:require [datascript.core :as d]
-            [clojure.set :as set]
-            [logseq.common.util.macro :as macro-util]))
+            [clojure.set :as set]))
 
 
 ;; Config vars
 ;; Config vars
 ;; ===========
 ;; ===========
@@ -27,7 +26,7 @@
 
 
 (def ref-property-types
 (def ref-property-types
   "User facing ref types"
   "User facing ref types"
-  #{:default :page :date})
+  #{:default :page :date :number :url})
 
 
 (assert (set/subset? ref-property-types
 (assert (set/subset? ref-property-types
                      (set user-built-in-property-types))
                      (set user-built-in-property-types))
@@ -65,21 +64,16 @@
          (catch :default _e
          (catch :default _e
            false))))
            false))))
 
 
-(defn macro-url?
-  [s]
-  ;; TODO: Confirm that macro expanded value is url when it's easier to pass data into validations
-  (macro-util/macro? s))
-
 (defn- entity?
 (defn- entity?
   [db id]
   [db id]
   (some? (d/entity db id)))
   (some? (d/entity db id)))
 
 
-(defn- url-or-closed-url?
+(defn- url-entity?
   [db val]
   [db val]
-  (or (url? val)
-      (macro-url? val)
-      (when-let [ent (d/entity db val)]
-        (url? (:block/content ent)))))
+  (or
+   (= val :logseq.property/empty-placeholder)
+   (when-let [ent (d/entity db val)]
+     (url? (:block/content ent)))))
 
 
 (defn- property-value-block?
 (defn- property-value-block?
   [db s]
   [db s]
@@ -104,6 +98,14 @@
       (when-let [entity (d/entity db s)]
       (when-let [entity (d/entity db s)]
         (string? (:block/content entity)))))
         (string? (:block/content entity)))))
 
 
+(defn- number-entity?
+  [db id]
+  (or
+   (= id :logseq.property/empty-placeholder)
+   (when-let [entity (d/entity db id)]
+     (number? (some-> (:block/content entity)
+                      parse-double)))))
+
 (def built-in-validation-schemas
 (def built-in-validation-schemas
   "Map of types to malli validation schemas that validate a property value for that type"
   "Map of types to malli validation schemas that validate a property value for that type"
   {:default  [:fn
   {:default  [:fn
@@ -115,14 +117,14 @@
    :number   [:fn
    :number   [:fn
               {:error/message "should be a number"}
               {:error/message "should be a number"}
               ;; Also handles entity? so no need to use it
               ;; Also handles entity? so no need to use it
-              number?]
+              number-entity?]
    :date     [:fn
    :date     [:fn
               {:error/message "should be a journal date"}
               {:error/message "should be a journal date"}
               date?]
               date?]
    :checkbox boolean?
    :checkbox boolean?
    :url      [:fn
    :url      [:fn
               {:error/message "should be a URL"}
               {:error/message "should be a URL"}
-              url-or-closed-url?]
+              url-entity?]
    :page     [:fn
    :page     [:fn
               {:error/message "should be a page"}
               {:error/message "should be a page"}
               page?]
               page?]
@@ -148,7 +150,7 @@
 
 
 (def property-types-with-db
 (def property-types-with-db
   "Property types whose validation fn requires a datascript db"
   "Property types whose validation fn requires a datascript db"
-  #{:default :string :url :date :page :template :entity})
+  #{:default :string :url :number :date :page :template :entity})
 
 
 ;; Helper fns
 ;; Helper fns
 ;; ==========
 ;; ==========

+ 60 - 44
src/main/frontend/components/property/value.cljs

@@ -25,7 +25,8 @@
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [datascript.impl.entity :as de]
             [datascript.impl.entity :as de]
-            [frontend.handler.property.util :as pu]))
+            [frontend.handler.property.util :as pu]
+            [logseq.db.frontend.property.type :as db-property-type]))
 
 
 (rum/defc property-empty-value
 (rum/defc property-empty-value
   [& {:as opts}]
   [& {:as opts}]
@@ -67,6 +68,19 @@
   (state/set-state! :editor/editing-property-value-id {})
   (state/set-state! :editor/editing-property-value-id {})
   (state/clear-edit!))
   (state/clear-edit!))
 
 
+(defn <create-new-block!
+  [block property value & {:keys [edit-block?]
+                           :or {edit-block? true}}]
+  (p/let [{:keys [block-id result]} (db-property-handler/create-property-text-block!
+                                     block property value editor-handler/wrap-parse-block {})]
+    (p/do!
+     result
+     (exit-edit-property)
+     (let [block (db/entity [:block/uuid block-id])]
+       (when edit-block?
+         (editor-handler/edit-block! block :max {:container-id :unknown-container}))
+       block))))
+
 (defn <add-property!
 (defn <add-property!
   "If a class and in a class schema context, add the property to its schema.
   "If a class and in a class schema context, add the property to its schema.
   Otherwise, add a block's property and its value. Creates a new property as needed"
   Otherwise, add a block's property and its value. Creates a new property as needed"
@@ -90,7 +104,13 @@
                       (db-property/create-user-property-ident-from-name property-key))
                       (db-property/create-user-property-ident-from-name property-key))
                      :logseq.property/empty-placeholder])
                      :logseq.property/empty-placeholder])
                   [property-key property-value])]
                   [property-key property-value])]
-            (property-handler/set-block-property! repo (:block/uuid block) property-id property-value'))))
+            (p/let [property (db/entity property-key)
+                    value (if (and (db-property-type/ref-property-types (get-in property [:block/schema :type]))
+                                   (not (int? property-value')))
+                            (p/let [result (<create-new-block! block (db/entity property-id) property-value' {:edit-block? false})]
+                              (:db/id result))
+                            property-value')]
+              (property-handler/set-block-property! repo (:block/uuid block) property-id value)))))
       (when exit-edit?
       (when exit-edit?
         (shui/popup-hide!)
         (shui/popup-hide!)
         (exit-edit-property))))))
         (exit-edit-property))))))
@@ -354,16 +374,6 @@
                      :input-opts input-opts)]
                      :input-opts input-opts)]
     (select-page property opts')))
     (select-page property opts')))
 
 
-(defn <create-new-block!
-  [block property value]
-  (p/let [{:keys [block-id result]} (db-property-handler/create-property-text-block!
-                                     block property value editor-handler/wrap-parse-block {})]
-    (p/do!
-     result
-     (exit-edit-property)
-     (let [block (db/entity [:block/uuid block-id])]
-       (editor-handler/edit-block! block :max {:container-id :unknown-container})))))
-
 (defn <create-new-block-from-template!
 (defn <create-new-block-from-template!
   "`template`: tag block"
   "`template`: tag block"
   [block property template]
   [block property template]
@@ -389,6 +399,7 @@
             property (db/sub-block (:db/id property))
             property (db/sub-block (:db/id property))
             type (:type schema)
             type (:type schema)
             closed-values? (seq (:property/closed-values property))
             closed-values? (seq (:property/closed-values property))
+            ref-type? (db-property-type/ref-property-types type)
             items (if closed-values?
             items (if closed-values?
                     (keep (fn [block]
                     (keep (fn [block]
                             (let [icon (pu/get-block-property-value block :logseq.property/icon)
                             (let [icon (pu/get-block-property-value block :logseq.property/icon)
@@ -404,6 +415,14 @@
                                    (if (coll? value)
                                    (if (coll? value)
                                      (map (fn [v] {:value v}) value)
                                      (map (fn [v] {:value v}) value)
                                      [{:value value}])))
                                      [{:value value}])))
+                         (map (fn [{:keys [value]}]
+                                (if (and ref-type? (number? value))
+                                  (when-let [e (db/entity value)]
+                                    {:label (or (:block/content e)
+                                                (:block/original-name e))
+                                     :value value})
+                                  {:label value
+                                   :vaue value})))
                          (distinct)))
                          (distinct)))
             items (->> (if (= :date type)
             items (->> (if (= :date type)
                          (map (fn [m] (let [label (:block/original-name (db/entity (:value m)))]
                          (map (fn [m] (let [label (:block/original-name (db/entity (:value m)))]
@@ -421,33 +440,31 @@
                                     (remove nil?))
                                     (remove nil?))
                                [selected-choices'])]
                                [selected-choices'])]
         (select-aux block property
         (select-aux block property
-                    (cond->
-                     {:multiple-choices? multiple-choices?
-                      :items items
-                      :selected-choices selected-choices
-                      :dropdown? dropdown?
-                      :show-new-when-not-exact-match? (not (or closed-values? (= :date type)))
-                      :input-default-placeholder "Select"
-                      :extract-chosen-fn :value
-                      :content-props content-props
-                      :on-chosen on-chosen
-                      :input-opts (fn [_]
-                                    {:on-blur (fn []
-                                                (exit-edit-property)
-                                                (when-let [f (:on-chosen select-opts)] (f)))
-                                     :on-click (fn []
-                                                 (when *show-new-property-config?
-                                                   (reset! *show-new-property-config? false)))
-                                     :on-key-down
-                                     (fn [e]
-                                       (case (util/ekey e)
-                                         "Escape"
-                                         (do
-                                           (exit-edit-property)
-                                           (when-let [f (:on-chosen select-opts)] (f)))
-                                         nil))})}
-                      closed-values?
-                      (assoc :extract-fn :label)))))))
+                    {:multiple-choices? multiple-choices?
+                     :items items
+                     :selected-choices selected-choices
+                     :dropdown? dropdown?
+                     :show-new-when-not-exact-match? (not (or closed-values? (= :date type)))
+                     :input-default-placeholder "Select"
+                     :extract-chosen-fn :value
+                     :extract-fn :label
+                     :content-props content-props
+                     :on-chosen on-chosen
+                     :input-opts (fn [_]
+                                   {:on-blur (fn []
+                                               (exit-edit-property)
+                                               (when-let [f (:on-chosen select-opts)] (f)))
+                                    :on-click (fn []
+                                                (when *show-new-property-config?
+                                                  (reset! *show-new-property-config? false)))
+                                    :on-key-down
+                                    (fn [e]
+                                      (case (util/ekey e)
+                                        "Escape"
+                                        (do
+                                          (exit-edit-property)
+                                          (when-let [f (:on-chosen select-opts)] (f)))
+                                        nil))})})))))
 
 
 (rum/defc property-normal-block-value < rum/reactive db-mixins/query
 (rum/defc property-normal-block-value < rum/reactive db-mixins/query
   {:init (fn [state]
   {:init (fn [state]
@@ -578,8 +595,9 @@
        closed-values?
        closed-values?
        (closed-value-item value opts)
        (closed-value-item value opts)
 
 
-       (= type :number)
-       [:span.number (str value)]
+       (de/entity? value)
+       (when-let [content (:block/content value)]
+         (inline-text {} :markdown (macro-util/expand-value-if-macro content (state/get-macros))))
 
 
        :else
        :else
        (inline-text {} :markdown (macro-util/expand-value-if-macro (str value) (state/get-macros))))]))
        (inline-text {} :markdown (macro-util/expand-value-if-macro (str value) (state/get-macros))))]))
@@ -701,9 +719,7 @@
       :style {:min-height 24}
       :style {:min-height 24}
       :on-click (fn []
       :on-click (fn []
                   (when (and (= type :default) (nil? value))
                   (when (and (= type :default) (nil? value))
-                    (<create-new-block! block property ""))
-                  (when (= type :string)
-                    (set-editing! block property editor-id dom-id value opts)))}
+                    (<create-new-block! block property "")))}
      (if (and (string/blank? value) template?)
      (if (and (string/blank? value) template?)
        (when-let [template (first (:property/schema.classes schema))]
        (when-let [template (first (:property/schema.classes schema))]
          [:a.fade-link.pointer.text-sm.jtrigger
          [:a.fade-link.pointer.text-sm.jtrigger

+ 37 - 104
src/main/frontend/handler/db_based/property.cljs

@@ -29,9 +29,9 @@
 ;;           min, max -> string length, number range, cardinality size limit
 ;;           min, max -> string length, number range, cardinality size limit
 
 
 (defn- build-property-value-tx-data
 (defn- build-property-value-tx-data
-  ([block property-id value]
-   (build-property-value-tx-data block property-id value (= property-id :logseq.task/status)))
-  ([block property-id value status?]
+  ([db block property-id value]
+   (build-property-value-tx-data db block property-id value (= property-id :logseq.task/status)))
+  ([db block property-id value status?]
    (when (some? value)
    (when (some? value)
      (let [block (assoc (outliner-core/block-with-updated-at {:db/id (:db/id block)})
      (let [block (assoc (outliner-core/block-with-updated-at {:db/id (:db/id block)})
                         property-id value)
                         property-id value)
@@ -86,16 +86,12 @@
     v-str))
     v-str))
 
 
 (defn- update-datascript-schema
 (defn- update-datascript-schema
-  [property {:keys [type cardinality values]}]
+  [property {:keys [cardinality]}]
   (let [ident (:db/ident property)
   (let [ident (:db/ident property)
-        cardinality (if (= cardinality :many) :db.cardinality/many :db.cardinality/one)
-        type-data (when (and type (or (db-property-type/ref-property-types type) (seq values))) ; type changes or closed values
-                    {:db/ident ident
-                     :db/valueType :db.type/ref
-                     :db/cardinality cardinality})]
-    (or type-data
-        {:db/ident ident
-         :db/cardinality cardinality})))
+        cardinality (if (= cardinality :many) :db.cardinality/many :db.cardinality/one)]
+    {:db/ident ident
+     :db/valueType :db.type/ref
+     :db/cardinality cardinality}))
 
 
 (defn ensure-unique-db-ident
 (defn ensure-unique-db-ident
   "Ensures the given db-ident is unique. If a db-ident conflicts, it is made
   "Ensures the given db-ident is unique. If a db-ident conflicts, it is made
@@ -124,7 +120,8 @@
    with the given property-id or :property-name option. When a property is created
    with the given property-id or :property-name option. When a property is created
    it is ensured to have a unique :db/ident"
    it is ensured to have a unique :db/ident"
   [repo property-id schema {:keys [property-name properties]}]
   [repo property-id schema {:keys [property-name properties]}]
-  (let [db-ident (or property-id (db-property/create-user-property-ident-from-name property-name))]
+  (let [db-ident (or property-id (db-property/create-user-property-ident-from-name property-name))
+        db (db/get-db repo)]
     (assert (qualified-keyword? db-ident))
     (assert (qualified-keyword? db-ident))
     (if-let [property (and (qualified-keyword? property-id) (db/entity db-ident))]
     (if-let [property (and (qualified-keyword? property-id) (db/entity db-ident))]
       (let [changed-property-attrs
       (let [changed-property-attrs
@@ -149,7 +146,7 @@
                             (when (seq properties)
                             (when (seq properties)
                               (mapcat
                               (mapcat
                                (fn [[property-id v]]
                                (fn [[property-id v]]
-                                 (build-property-value-tx-data property property-id v)) properties)))
+                                 (build-property-value-tx-data db property property-id v)) properties)))
             many->one? (and (db-property/many? property) (= :one (:cardinality schema)))]
             many->one? (and (db-property/many? property) (= :one (:cardinality schema)))]
         (when (seq tx-data)
         (when (seq tx-data)
           (db/transact! repo tx-data {:outliner-op :update-property
           (db/transact! repo tx-data {:outliner-op :update-property
@@ -206,7 +203,8 @@
         property-schema (:block/schema property)
         property-schema (:block/schema property)
         {:keys [type]} property-schema
         {:keys [type]} property-schema
         v' (or (resolve-tag v) v)
         v' (or (resolve-tag v) v)
-        db-attribute? (contains? db-property/db-attribute-properties property-id)]
+        db-attribute? (contains? db-property/db-attribute-properties property-id)
+        db (db/get-db repo)]
     (cond
     (cond
       db-attribute?
       db-attribute?
       (db/transact! repo [{:db/id (:db/id block) property-id v'}]
       (db/transact! repo [{:db/id (:db/id block) property-id v'}]
@@ -245,7 +243,7 @@
                                     (vec (remove string/blank? new-value*))
                                     (vec (remove string/blank? new-value*))
                                     (set (remove string/blank? new-value*)))
                                     (set (remove string/blank? new-value*)))
                                   new-value*)
                                   new-value*)
-                      tx-data (build-property-value-tx-data block property-id new-value status?)]
+                      tx-data (build-property-value-tx-data db block property-id new-value status?)]
                   (db/transact! repo tx-data {:outliner-op :save-block}))))))))))
                   (db/transact! repo tx-data {:outliner-op :save-block}))))))))))
 
 
 (defn class-add-property!
 (defn class-add-property!
@@ -297,6 +295,7 @@
             property-type (or type infer-schema :default)
             property-type (or type infer-schema :default)
             many? (db-property/many? property)
             many? (db-property/many? property)
             status? (= :logseq.task/status (:db/ident property))
             status? (= :logseq.task/status (:db/ident property))
+            db (db/get-db repo)
             txs (->>
             txs (->>
                  (mapcat
                  (mapcat
                   (fn [eid]
                   (fn [eid]
@@ -307,7 +306,7 @@
                                         (catch :default e
                                         (catch :default e
                                           (notification/show! (str e) :error false)
                                           (notification/show! (str e) :error false)
                                           nil))]
                                           nil))]
-                          (build-property-value-tx-data block property-id v* status?)))))
+                          (build-property-value-tx-data db block property-id v* status?)))))
                   block-eids)
                   block-eids)
                  (remove nil?))]
                  (remove nil?))]
         (when (seq txs)
         (when (seq txs)
@@ -481,12 +480,6 @@
     {:page page-tx
     {:page page-tx
      :blocks [new-block]}))
      :blocks [new-block]}))
 
 
-(defn- get-property-hidden-page
-  [property]
-  (let [page-name (str db-property-build/hidden-page-name-prefix (:block/uuid property))]
-    (or (db/get-case-page page-name)
-        (db-property-build/build-property-hidden-page property))))
-
 (defn re-init-commands!
 (defn re-init-commands!
   "Update commands after task status and priority's closed values has been changed"
   "Update commands after task status and priority's closed values has been changed"
   [property]
   [property]
@@ -524,7 +517,6 @@
                                  (notification/show! (str e) :error false)
                                  (notification/show! (str e) :error false)
                                  nil))
                                  nil))
               block (when id (db/entity [:block/uuid id]))
               block (when id (db/entity [:block/uuid id]))
-              value-block (when (uuid? value) (db/entity [:block/uuid value]))
               validate-message (validate-property-value
               validate-message (validate-property-value
                                 (get-property-value-schema property-type property {:new-closed-value? true})
                                 (get-property-value-schema property-type property {:new-closed-value? true})
                                 resolved-value)]
                                 resolved-value)]
@@ -545,10 +537,6 @@
           (nil? resolved-value)
           (nil? resolved-value)
           nil
           nil
 
 
-          (db/page? value-block)             ; page
-          {:block-id value
-           :tx-data [[:db/add [:block/uuid value] :property/value-property (:db/id property)]]}
-
           :else
           :else
           (let [block-id (or id (db/new-block-id))
           (let [block-id (or id (db/new-block-id))
                 icon (when-not (and (string? icon) (string/blank? icon)) icon)
                 icon (when-not (and (string? icon) (string/blank? icon)) icon)
@@ -560,29 +548,20 @@
                               (outliner-core/block-with-updated-at
                               (outliner-core/block-with-updated-at
                                {:block/uuid id
                                {:block/uuid id
                                 :block/content resolved-value
                                 :block/content resolved-value
+                                :block/closed-value-property (:db/id property)
                                 :block/schema (if description
                                 :block/schema (if description
                                                 (assoc schema :description description)
                                                 (assoc schema :description description)
                                                 (dissoc schema :description))})
                                                 (dissoc schema :description))})
                                icon
                                icon
                                (assoc :logseq.property/icon icon)))]
                                (assoc :logseq.property/icon icon)))]
-                          (let [hidden-tx
-                                (if (contains? db-property-type/ref-property-types (:type property-schema))
-                                  []
-                                  (let [page (get-property-hidden-page property)
-                                        max-order (:block/order (last (:property/closed-values property)))
-                                        new-block (-> (db-property-build/build-closed-value-block block-id resolved-value [:block/uuid (:block/uuid page)]
-                                                                                                  property {:icon icon
-                                                                                                            :description description})
-                                                      (assoc :block/order (db-order/gen-key max-order nil)))]
-                                    (cond-> []
-                                      (not (e/entity? page))
-                                      (conj page)
-                                      true
-                                      (conj new-block))))]
-                            (conj hidden-tx
-                                  (outliner-core/block-with-updated-at
-                                   {:db/id (:db/id property)
-                                    :block/schema (merge {:type property-type} property-schema)}))))]
+                          (let [max-order (:block/order (last (:property/closed-values property)))
+                                new-block (-> (db-property-build/build-closed-value-block block-id resolved-value
+                                                                                                property {:icon icon
+                                                                                                          :description description})
+                                              (assoc :block/order (db-order/gen-key max-order nil)))]
+                            [new-block
+                             (outliner-core/block-with-updated-at
+                              {:db/id (:db/id property)})]))]
             {:block-id block-id
             {:block-id block-id
              :tx-data tx-data}))))))
              :tx-data tx-data}))))))
 
 
@@ -591,64 +570,18 @@
   [property values]
   [property values]
   (assert (e/entity? property))
   (assert (e/entity? property))
   (when (seq values)
   (when (seq values)
-    (let [values' (remove string/blank? values)
-          property-schema (:block/schema property)]
-      (if (every? uuid? values')
-        (p/let [values (keep #(db/entity [:block/uuid %]) values')]
-          (when (seq values)
-            (let [property-tx {:db/ident (:db/ident property)
-                               :db/valueType :db.type/ref
-                               :db/cardinality (:db/cardinality property)
-                               :block/schema property-schema}
-                  value-property-tx (map (fn [id]
-                                           {:db/id id
-                                            :block/type "closed value"
-                                            :block/closed-value-property (:db/id property)})
-                                         (map :db/id values))]
-              (db/transact! (state/get-current-repo) (cons property-tx value-property-tx)
-                            {:outliner-op :insert-blocks}))))
-        (p/let [property-id (:db/ident property)
-                page (get-property-hidden-page property)
-                page-tx (when-not (e/entity? page) page)
-                page-id (:block/uuid page)
-                *max-key (atom nil)
-                closed-value-blocks (doall
-                                     (map (fn [value]
-                                            (let [b (db-property-build/build-closed-value-block
-                                                     (db/new-block-id)
-                                                     value
-                                                     [:block/uuid page-id]
-                                                     property
-                                                     {})]
-                                              (assoc b :block/order (db-order/gen-key @*max-key nil))))
-                                          values'))
-                value->block-id (zipmap
-                                 (map :block/content closed-value-blocks)
-                                 (map :block/uuid closed-value-blocks))
-                property-tx {:db/ident (:db/ident property)
-                             :db/valueType :db.type/ref
-                             :db/cardinality (:db/cardinality property)
-                             :block/schema property-schema}
-                property-values (db-async/<get-block-property-values (state/get-current-repo) (:db/ident property))
-                block-values (->> property-values
-                                  (remove #(uuid? (first %))))
-                id-values (filter second block-values)
-                ;; Order matters here, retract old property values first and then update the property schema
-                ;; Otherwise, the UI Datascript will throw tempid errors
-                tx-data (concat
-                         ;; retract old property values
-                         (map (fn [[id _value]] [:db/retract id property-id]) id-values)
-                         (when page-tx [page-tx])
-                         closed-value-blocks
-                         [property-tx]
-                         (map (fn [[id value]]
-                                (let [value' (if (set? value)
-                                               (set (map #(vector :block/uuid (value->block-id %)) value))
-                                               [:block/uuid (value->block-id value)])]
-                                  {:db/id id property-id value'}))
-                              id-values))]
-          (db/transact! (state/get-current-repo) tx-data
-                        {:outliner-op :insert-blocks}))))))
+    (let [values' (remove string/blank? values)]
+      (assert (every? uuid? values') "existing values should all be UUIDs")
+      (p/let [values (keep #(db/entity [:block/uuid %]) values')]
+        (when (seq values)
+          (let [value-property-tx (map (fn [id]
+                                         {:db/id id
+                                          :block/type "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)})]
+            (db/transact! (state/get-current-repo) (cons property-tx value-property-tx)
+                          {:outliner-op :save-blocks})))))))
 
 
 (defn delete-closed-value!
 (defn delete-closed-value!
   "Returns true when deleted or if not deleted displays warning and returns false"
   "Returns true when deleted or if not deleted displays warning and returns false"