1
0
Эх сурвалжийг харах

fix: embed self in property value

Tienson Qin 8 сар өмнө
parent
commit
0a29d32c65

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

@@ -334,7 +334,7 @@
      (sort-by-order (:block/_parent parent)))))
 
 (defn get-block-parents
-  [db block-id {:keys [depth] :or {depth 100}}]
+  [db block-id & {:keys [depth] :or {depth 100}}]
   (loop [block-id block-id
          parents' (list)
          d 1]

+ 25 - 11
deps/outliner/src/logseq/outliner/property.cljs

@@ -280,6 +280,14 @@
     ;; only value-ref-property types should call this
     (find-or-create-property-value conn property-id v)))
 
+(defn throw-error-if-self-value
+  [block value ref?]
+  (when (and ref? (= value (:db/id block)))
+    (throw (ex-info "Can't set this block ifself as own property value"
+                    {:type :notification
+                     :payload {:message "Can't set this block ifself as own property value"
+                               :type :error}}))))
+
 (defn set-block-property!
   "Updates a block property's value for an existing property-id and block.  If
   property is a ref type, automatically handles a raw property value i.e. you
@@ -304,10 +312,12 @@
       (let [property (d/entity @conn property-id)
             _ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
             property-type (get property :logseq.property/type :default)
-            new-value (if (db-property-type/all-ref-property-types property-type)
+            ref? (db-property-type/all-ref-property-types property-type)
+            new-value (if ref?
                         (convert-ref-property-value conn property-id v property-type)
                         v)
             existing-value (get block property-id)]
+        (throw-error-if-self-value block new-value ref?)
         (when-not (= existing-value new-value)
           (raw-set-block-property! conn block property property-type new-value))))))
 
@@ -328,17 +338,21 @@
         _ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
         property-type (get property :logseq.property/type :default)
         _ (assert (some? v) "Can't set a nil property value must be not nil")
-        v' (if (db-property-type/value-ref-property-types property-type)
+        ref? (db-property-type/value-ref-property-types property-type)
+        v' (if ref?
              (convert-ref-property-value conn property-id v property-type)
-             v)
-        txs (mapcat
-             (fn [eid]
-               (if-let [block (d/entity @conn eid)]
-                 (build-property-value-tx-data conn block property-id v')
-                 (js/console.error "Skipping setting a block's property because the block id could not be found:" eid)))
-             block-eids)]
-    (when (seq txs)
-      (ldb/transact! conn txs {:outliner-op :save-block}))))
+             v)]
+    (doseq [eid block-eids]
+      (let [block (d/entity @conn eid)]
+        (throw-error-if-self-value block v' ref?)))
+    (let [txs (mapcat
+               (fn [eid]
+                 (if-let [block (d/entity @conn eid)]
+                   (build-property-value-tx-data conn block property-id v')
+                   (js/console.error "Skipping setting a block's property because the block id could not be found:" eid)))
+               block-eids)]
+      (when (seq txs)
+        (ldb/transact! conn txs {:outliner-op :save-block})))))
 
 (defn batch-remove-property!
   [conn block-ids property-id]

+ 69 - 52
src/main/frontend/components/property/value.cljs

@@ -1260,55 +1260,72 @@
              (first v)
              :else
              v)
-         empty-value? (when (coll? v) (= :logseq.property/empty-placeholder (:db/ident (first v))))
-         closed-values? (seq (:property/closed-values property))
-         property-ident (:db/ident property)
-         value-cp [:div.property-value-inner
-                   {:data-type type
-                    :class (str (when empty-value? "empty-value")
-                                (when-not (:other-position? opts) " w-full"))}
-                   (cond
-                     (= property-ident :logseq.property.class/properties)
-                     (properties-cp {} block {:selected? false
-                                              :class-schema? true})
-
-                     (and multiple-values? (contains? #{:default :url} type) (not closed-values?))
-                     (property-normal-block-value block property v)
-
-                     multiple-values?
-                     (multiple-values block property opts)
-
-                     :else
-                     (let [parent? (= property-ident :logseq.property/parent)
-                           value-cp (property-scalar-value block property v
-                                                           (merge
-                                                            opts
-                                                            {:editor-id editor-id
-                                                             :dom-id dom-id}))
-                           page-ancestors (when parent?
-                                            (let [ancestor-pages (loop [parents [block]]
-                                                                   (if-let [parent (:logseq.property/parent (last parents))]
-                                                                     (when-not (contains? (set parents) parent)
-                                                                       (recur (conj parents parent)))
-                                                                     parents))]
-                                              (->> (reverse ancestor-pages)
-                                                   (remove (fn [e] (= (:db/id block) (:db/id e))))
-                                                   butlast)))]
-                       (if (seq page-ancestors)
-                         [:div.flex.flex-1.items-center.gap-1
-                          (interpose [:span.opacity-50.text-sm " > "]
-                                     (concat
-                                      (map (fn [{title :block/title :as ancestor}]
-                                             [:a.whitespace-nowrap {:on-click #(route-handler/redirect-to-page! (:block/uuid ancestor))} title])
-                                           page-ancestors)
-                                      [value-cp]))]
-                         value-cp)))]]
-     (if show-tooltip?
-       (shui/tooltip-provider
-        (shui/tooltip
-         {:delayDuration 1200}
-         (shui/tooltip-trigger
-          {:onFocusCapture #(util/stop-propagation %)} value-cp)
-         (shui/tooltip-content
-          (str "Change " (:block/title property)))))
-       value-cp))))
+         self-value-or-embedded? (fn [v]
+                                   (or (= (:db/id v) (:db/id block))
+                                       ;; property value self embedded
+                                       (= (:db/id (:block/link v)) (:db/id block))))]
+     (if (or (and (de/entity? v) (self-value-or-embedded? v))
+             (and (coll? v) (every? de/entity? v)
+                  (some self-value-or-embedded? v)))
+       [:div.flex.flex-row.items-center.gap-1
+        [:div.warning "Self reference"]
+        (shui/button {:variant :outline
+                      :size :sm
+                      :class "h-5"
+                      :on-click (fn []
+                                  (db-property-handler/remove-block-property!
+                                   (:db/id block)
+                                   (:db/ident property)))}
+                     "Fix it!")]
+       (let [empty-value? (when (coll? v) (= :logseq.property/empty-placeholder (:db/ident (first v))))
+             closed-values? (seq (:property/closed-values property))
+             property-ident (:db/ident property)
+             value-cp [:div.property-value-inner
+                       {:data-type type
+                        :class (str (when empty-value? "empty-value")
+                                    (when-not (:other-position? opts) " w-full"))}
+                       (cond
+                         (= property-ident :logseq.property.class/properties)
+                         (properties-cp {} block {:selected? false
+                                                  :class-schema? true})
+
+                         (and multiple-values? (contains? #{:default :url} type) (not closed-values?))
+                         (property-normal-block-value block property v)
+
+                         multiple-values?
+                         (multiple-values block property opts)
+
+                         :else
+                         (let [parent? (= property-ident :logseq.property/parent)
+                               value-cp (property-scalar-value block property v
+                                                               (merge
+                                                                opts
+                                                                {:editor-id editor-id
+                                                                 :dom-id dom-id}))
+                               page-ancestors (when parent?
+                                                (let [ancestor-pages (loop [parents [block]]
+                                                                       (if-let [parent (:logseq.property/parent (last parents))]
+                                                                         (when-not (contains? (set parents) parent)
+                                                                           (recur (conj parents parent)))
+                                                                         parents))]
+                                                  (->> (reverse ancestor-pages)
+                                                       (remove (fn [e] (= (:db/id block) (:db/id e))))
+                                                       butlast)))]
+                           (if (seq page-ancestors)
+                             [:div.flex.flex-1.items-center.gap-1
+                              (interpose [:span.opacity-50.text-sm " > "]
+                                         (concat
+                                          (map (fn [{title :block/title :as ancestor}]
+                                                 [:a.whitespace-nowrap {:on-click #(route-handler/redirect-to-page! (:block/uuid ancestor))} title])
+                                               page-ancestors)
+                                          [value-cp]))]
+                             value-cp)))]]
+         (if show-tooltip?
+           (shui/tooltip-provider
+            (shui/tooltip
+             {:delayDuration 1200}
+             (shui/tooltip-trigger
+              {:onFocusCapture #(util/stop-propagation %)} value-cp)
+             (shui/tooltip-content
+              (str "Change " (:block/title property)))))
+           value-cp))))))

+ 33 - 25
src/main/frontend/handler/paste.cljs

@@ -8,6 +8,7 @@
             [frontend.format.block :as block]
             [frontend.format.mldoc :as mldoc]
             [frontend.handler.editor :as editor-handler]
+            [frontend.handler.notification :as notification]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -17,6 +18,7 @@
             [lambdaisland.glogi :as log]
             [logseq.common.util :as common-util]
             [logseq.common.util.block-ref :as block-ref]
+            [logseq.db :as ldb]
             [logseq.db.frontend.content :as db-content]
             [logseq.graph-parser.block :as gp-block]
             [promesa.core :as p]))
@@ -189,32 +191,38 @@
   ;; todo: logseq/whiteboard-shapes is now text/html
   [input text e html]
   (util/stop e)
-  (->
-   (p/let [{:keys [graph blocks embed-block?]} (get-copied-blocks)]
-     (if (and (seq blocks) (= graph (state/get-current-repo)))
+  (let [repo (state/get-current-repo)]
+    (->
+     (p/let [{:keys [graph blocks embed-block?]} (get-copied-blocks)]
+       (if (and (seq blocks) (= graph repo))
        ;; Handle internal paste
-       (let [revert-cut-txs (get-revert-cut-txs blocks)
-             keep-uuid? (= (state/get-block-op-type) :cut)
-             blocks (if (config/db-based-graph? (state/get-current-repo))
-                      (map (fn [b] (dissoc b :block/properties)) blocks)
-                      blocks)]
-         (if embed-block?
-           (when-let [block-id (:block/uuid (first blocks))]
-             (when-let [current-block (state/get-edit-block)]
-               (p/do!
-                (editor-handler/api-insert-new-block! ""
-                                                      {:block-uuid (:block/uuid current-block)
-                                                       :sibling? true
-                                                       :replace-empty-target? true
-                                                       :other-attrs {:block/link (:db/id (db/entity [:block/uuid block-id]))}})
-                (state/clear-edit!))))
-           (editor-handler/paste-blocks blocks {:revert-cut-txs revert-cut-txs
-                                                :keep-uuid? keep-uuid?})))
-       (paste-copied-text input text html)))
-   (p/catch (fn [error]
-              (log/error :msg "Paste failed" :exception error)
-              (state/pub-event! [:capture-error {:error error
-                                                 :payload {:type ::paste-copied-blocks-or-text}}])))))
+         (let [revert-cut-txs (get-revert-cut-txs blocks)
+               keep-uuid? (= (state/get-block-op-type) :cut)
+               blocks (if (config/db-based-graph? (state/get-current-repo))
+                        (map (fn [b] (dissoc b :block/properties)) blocks)
+                        blocks)]
+           (if embed-block?
+             (when-let [block-id (:block/uuid (first blocks))]
+               (when-let [current-block (state/get-edit-block)]
+                 (cond
+                   (some #(= block-id (:block/uuid %)) (db/get-block-parents repo (:block/uuid current-block) {}))
+                   (notification/show! "Can't embed parent block as its own property" :error)
+
+                   :else
+                   (p/do!
+                    (editor-handler/api-insert-new-block! ""
+                                                          {:block-uuid (:block/uuid current-block)
+                                                           :sibling? true
+                                                           :replace-empty-target? true
+                                                           :other-attrs {:block/link (:db/id (db/entity [:block/uuid block-id]))}})
+                    (state/clear-edit!)))))
+             (editor-handler/paste-blocks blocks {:revert-cut-txs revert-cut-txs
+                                                  :keep-uuid? keep-uuid?})))
+         (paste-copied-text input text html)))
+     (p/catch (fn [error]
+                (log/error :msg "Paste failed" :exception error)
+                (state/pub-event! [:capture-error {:error error
+                                                   :payload {:type ::paste-copied-blocks-or-text}}]))))))
 
 (defn paste-text-in-one-block-at-point
   []