Преглед на файлове

Merge branch 'feat/db' into feat/text-template

Tienson Qin преди 8 месеца
родител
ревизия
7d1dda44e1

+ 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 itself as own property value"
+                    {:type :notification
+                     :payload {:message "Can't set this block itself 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]

+ 50 - 42
src/main/frontend/components/block.cljs

@@ -2605,51 +2605,59 @@
 
 (rum/defcs block-tag <
   (rum/local false ::hover?)
+  (rum/local false ::hover-container?)
   [state block tag config popup-opts]
   (let [*hover? (::hover? state)
-        hover? @*hover?]
-    [:div.block-tag.items-center
+        *hover-container? (::hover-container? state)]
+    [:div.block-tag.items-center.relative
      {:key (str "tag-" (:db/id tag))
-      :on-mouse-over #(reset! *hover? true)
-      :on-mouse-out #(reset! *hover? false)
-      :on-context-menu
-      (fn [e]
-        (util/stop e)
-        (shui/popup-show! e
-                          (fn []
-                            [:<>
-                             (shui/dropdown-menu-item
-                              {:key "Go to tag"
-                               :on-click #(route-handler/redirect-to-page! (:block/uuid tag))}
-                              (str "Go to #" (:block/title tag))
-                              (shui/dropdown-menu-shortcut (shortcut-utils/decorate-binding "mod+click")))
-                             (shui/dropdown-menu-item
-                              {:key "Open tag in sidebar"
-                               :on-click #(state/sidebar-add-block! (state/get-current-repo) (:db/id tag) :page)}
-                              "Open tag in sidebar"
-                              (shui/dropdown-menu-shortcut (shortcut-utils/decorate-binding "shift+click")))
-                             (shui/dropdown-menu-item
-                              {:key "Remove tag"
-                               :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
-                              "Remove tag")])
-                          popup-opts))}
-     (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 14
-                      :style {:margin-top 1}})]
-       [:a.hash-symbol.select-none {:style {:margin-left 5}}
-        "#"])
-     (page-cp (assoc config
-                     :disable-preview? true
-                     :tag? true
-                     :hide-tag-symbol? true)
-              tag)]))
+      :on-mouse-over #(reset! *hover-container? true)
+      :on-mouse-out #(reset! *hover-container? false)}
+     (when (not (ldb/private-tags (:db/ident tag)))
+       [:div.absolute.-left-5.bg-gray-01.transition-opacity.duration-300.ease-in-out
+        {:class (if @*hover-container? "!opacity-100" "!opacity-0")
+         :style {:top -2}}
+        (shui/button
+         {:size :sm
+          :variant :ghost
+          :class "px-1 py-1 h-6 text-muted-foreground"
+          :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 14}))])
+     [:div.flex.items-center
+      {:on-mouse-over #(reset! *hover? true)
+       :on-mouse-out #(reset! *hover? false)
+       :on-context-menu
+       (fn [e]
+         (util/stop e)
+         (shui/popup-show! e
+                           (fn []
+                             [:<>
+                              (shui/dropdown-menu-item
+                               {:key "Go to tag"
+                                :on-click #(route-handler/redirect-to-page! (:block/uuid tag))}
+                               (str "Go to #" (:block/title tag))
+                               (shui/dropdown-menu-shortcut (shortcut-utils/decorate-binding "mod+click")))
+                              (shui/dropdown-menu-item
+                               {:key "Open tag in sidebar"
+                                :on-click #(state/sidebar-add-block! (state/get-current-repo) (:db/id tag) :page)}
+                               "Open tag in sidebar"
+                               (shui/dropdown-menu-shortcut (shortcut-utils/decorate-binding "shift+click")))
+                              (shui/dropdown-menu-item
+                               {:key "Remove tag"
+                                :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
+                               "Remove tag")])
+                           popup-opts))}
+      [:a.hash-symbol.select-none.flex
+       "#"]
+      (page-cp (assoc config
+                      :disable-preview? true
+                      :tag? true
+                      :hide-tag-symbol? true)
+               tag)]]))
 
 (rum/defc tags-cp
   "Tags without inline or hidden tags"

+ 4 - 4
src/main/frontend/components/page.cljs

@@ -428,13 +428,13 @@
 
 (rum/defc db-page-title-actions
   [page]
-  [:div.absolute.-top-3.left-0.opacity-0.db-page-title-actions
+  [:div.absolute.-top-4.left-0.opacity-0.db-page-title-actions
    [:div.flex.flex-row.items-center.gap-2
     (when-not (:logseq.property/icon (db/entity (:db/id page)))
       (shui/button
        {:variant :outline
         :size :sm
-        :class "px-2 py-0 h-4 text-xs text-muted-foreground"
+        :class "px-2 py-0 h-6 text-xs text-muted-foreground"
         :on-click (fn [e]
                     (state/pub-event! [:editor/new-property {:property-key "Icon"
                                                              :block page
@@ -444,11 +444,11 @@
     (shui/button
      {:variant :outline
       :size :sm
-      :class "px-2 py-0 h-4 text-xs text-muted-foreground"
+      :class "px-2 py-0 h-6 text-xs text-muted-foreground"
       :on-click (fn [e]
                   (state/pub-event! [:editor/new-property {:block page
                                                            :target (.-target e)}]))}
-     "Set page property")]])
+     "Set property")]])
 
 (rum/defc db-page-title
   [page whiteboard-page? sidebar? container-id]

+ 76 - 55
src/main/frontend/components/property/value.cljs

@@ -926,11 +926,14 @@
                      :property-block? true
                      :on-block-content-pointer-down (when default-value?
                                                       (fn [_e]
-                                                        (<create-new-block! block property (or (:block/title default-value) ""))))}]
+                                                        (<create-new-block! block property (or (:block/title default-value) ""))))
+                     :p-block (:db/id block)
+                     :p-property (:db/id property)}]
          (if (set? value-block)
            (blocks-container config (ldb/sort-by-order value-block))
            (rum/with-key
-             (block-container (assoc config :property-default-value? default-value?) value-block)
+             (block-container (assoc config
+                                     :property-default-value? default-value?) value-block)
              (str (:db/id property) "-" (:block/uuid value-block)))))]
       [:div
        {:tabIndex 0
@@ -1233,7 +1236,7 @@
     (multiple-values-inner block property value' opts)))
 
 (rum/defcs property-value < rum/reactive db-mixins/query
-  [state block property {:keys [show-tooltip?]
+  [state block property {:keys [show-tooltip? p-block p-property]
                          :as opts}]
   (ui/catch-error
    (ui/block-error "Something wrong" {})
@@ -1260,55 +1263,73 @@
              (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))
+             (and (= p-block (:db/id block)) (= p-property (:db/id property))))
+       [: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))))))

+ 32 - 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]
@@ -189,32 +190,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
   []