|
|
@@ -51,6 +51,7 @@
|
|
|
[frontend.handler.file-sync :as file-sync]
|
|
|
[frontend.handler.notification :as notification]
|
|
|
[frontend.handler.plugin :as plugin-handler]
|
|
|
+ [frontend.handler.property :as property-handler]
|
|
|
[frontend.handler.property.file :as property-file]
|
|
|
[frontend.handler.property.util :as pu]
|
|
|
[frontend.handler.route :as route-handler]
|
|
|
@@ -301,106 +302,127 @@
|
|
|
[:span.handle-left.image-resize (assoc handle-props :ref *handle-left)]
|
|
|
[:span.handle-right.image-resize (assoc handle-props :ref *handle-right)]]))
|
|
|
|
|
|
+(defn measure-image! [url on-dimensions]
|
|
|
+ (let [img (js/Image.)]
|
|
|
+ (set! (.-onload img)
|
|
|
+ (fn []
|
|
|
+ (on-dimensions (.-naturalWidth img) (.-naturalHeight img))))
|
|
|
+ (set! (.-src img) url)))
|
|
|
+
|
|
|
(defonce *resizing-image? (atom false))
|
|
|
-(rum/defc asset-container
|
|
|
+(rum/defc ^:large-vars/cleanup-todo asset-container
|
|
|
[asset-block src title metadata {:keys [breadcrumb? positioned? local? full-text]}]
|
|
|
- (let [*el-ref (rum/use-ref nil)
|
|
|
- image-src (fs/asset-path-normalize src)
|
|
|
- src' (if (or (string/starts-with? src "/")
|
|
|
- (string/starts-with? src "~"))
|
|
|
- (str "file://" src)
|
|
|
- src)
|
|
|
- get-blockid #(some-> (rum/deref *el-ref) (.closest "[blockid]") (.getAttribute "blockid") (uuid))]
|
|
|
- [:div.asset-container
|
|
|
- {:key "resize-asset-container"
|
|
|
- :on-pointer-down util/stop
|
|
|
- :on-click (fn [e]
|
|
|
- (util/stop e)
|
|
|
- (when (= "IMG" (some-> (.-target e) (.-nodeName)))
|
|
|
- (open-lightbox! e)))
|
|
|
- :ref *el-ref}
|
|
|
- [:img.rounded-sm.relative
|
|
|
- (merge
|
|
|
- {:loading "lazy"
|
|
|
- :referrerPolicy "no-referrer"
|
|
|
- :src src'
|
|
|
- :title title}
|
|
|
- metadata)]
|
|
|
- (when (and (not breadcrumb?)
|
|
|
- (not positioned?))
|
|
|
- [:<>
|
|
|
- (let [handle-copy!
|
|
|
- (fn [_e]
|
|
|
- (-> (util/copy-image-to-clipboard image-src)
|
|
|
- (p/then #(notification/show! "Copied!" :success))))
|
|
|
- handle-delete!
|
|
|
- (fn [_e]
|
|
|
- (when-let [block-id (get-blockid)]
|
|
|
- (let [*local-selected? (atom local?)]
|
|
|
- (-> (shui/dialog-confirm!
|
|
|
- [:div.text-xs.opacity-60.-my-2
|
|
|
- (when (and local? (not= (:block/uuid asset-block) block-id))
|
|
|
- [:label.flex.gap-1.items-center
|
|
|
- (shui/checkbox
|
|
|
- {:default-checked @*local-selected?
|
|
|
- :on-checked-change #(reset! *local-selected? %)})
|
|
|
- (t :asset/physical-delete)])]
|
|
|
- {:title (t :asset/confirm-delete (.toLocaleLowerCase (t :text/image)))
|
|
|
- :outside-cancel? true})
|
|
|
- (p/then (fn []
|
|
|
- (shui/dialog-close!)
|
|
|
- (editor-handler/delete-asset-of-block!
|
|
|
- {:block-id block-id
|
|
|
- :asset-block asset-block
|
|
|
- :local? local?
|
|
|
- :delete-local? @*local-selected?
|
|
|
- :repo (state/get-current-repo)
|
|
|
- :href src
|
|
|
- :title title
|
|
|
- :full-text full-text})))))))]
|
|
|
- [:.asset-action-bar {:aria-hidden "true"}
|
|
|
- (shui/button-group
|
|
|
- (shui/button
|
|
|
- {:variant :outline
|
|
|
- :size :icon
|
|
|
- :class "h-7 w-7"
|
|
|
- :on-pointer-down util/stop
|
|
|
- :on-click (fn [e]
|
|
|
- (shui/popup-show! (.closest (.-target e) ".asset-action-bar")
|
|
|
- (fn []
|
|
|
- [:div
|
|
|
- {:on-click #(shui/popup-hide!)}
|
|
|
- (shui/dropdown-menu-item
|
|
|
- {:on-click #(some-> (db/entity [:block/uuid (get-blockid)])
|
|
|
- (editor-handler/edit-block! :max {:container-id :unknown-container}))}
|
|
|
- [:span.flex.items-center.gap-1
|
|
|
- (ui/icon "edit") (t :asset/edit-block)])
|
|
|
- (shui/dropdown-menu-item
|
|
|
- {:on-click handle-copy!}
|
|
|
- [:span.flex.items-center.gap-1
|
|
|
- (ui/icon "copy") (t :asset/copy)])
|
|
|
- (when (util/electron?)
|
|
|
+ (let [asset-width (:logseq.property.asset/width asset-block)
|
|
|
+ asset-height (:logseq.property.asset/height asset-block)]
|
|
|
+ (hooks/use-effect!
|
|
|
+ (fn []
|
|
|
+ (when-not (or asset-width asset-height)
|
|
|
+ (measure-image!
|
|
|
+ src
|
|
|
+ (fn [width height]
|
|
|
+ (when (nil? (:logseq.property.asset/width asset-block))
|
|
|
+ (property-handler/set-block-properties! (state/get-current-repo)
|
|
|
+ (:block/uuid asset-block)
|
|
|
+ {:logseq.property.asset/width width
|
|
|
+ :logseq.property.asset/height height})))))
|
|
|
+ (fn []))
|
|
|
+ [])
|
|
|
+ (let [*el-ref (rum/use-ref nil)
|
|
|
+ image-src (fs/asset-path-normalize src)
|
|
|
+ src' (if (or (string/starts-with? src "/")
|
|
|
+ (string/starts-with? src "~"))
|
|
|
+ (str "file://" src)
|
|
|
+ src)
|
|
|
+ get-blockid #(some-> (rum/deref *el-ref) (.closest "[blockid]") (.getAttribute "blockid") (uuid))]
|
|
|
+ [:div.asset-container
|
|
|
+ {:key "resize-asset-container"
|
|
|
+ :on-pointer-down util/stop
|
|
|
+ :on-click (fn [e]
|
|
|
+ (util/stop e)
|
|
|
+ (when (= "IMG" (some-> (.-target e) (.-nodeName)))
|
|
|
+ (open-lightbox! e)))
|
|
|
+ :ref *el-ref}
|
|
|
+ [:img.rounded-sm.relative.fade-in.fade-in-faster
|
|
|
+ (merge
|
|
|
+ {:loading "lazy"
|
|
|
+ :referrerPolicy "no-referrer"
|
|
|
+ :src src'
|
|
|
+ :title title}
|
|
|
+ metadata)]
|
|
|
+ (when (and (not breadcrumb?)
|
|
|
+ (not positioned?))
|
|
|
+ [:<>
|
|
|
+ (let [handle-copy!
|
|
|
+ (fn [_e]
|
|
|
+ (-> (util/copy-image-to-clipboard image-src)
|
|
|
+ (p/then #(notification/show! "Copied!" :success))))
|
|
|
+ handle-delete!
|
|
|
+ (fn [_e]
|
|
|
+ (when-let [block-id (get-blockid)]
|
|
|
+ (let [*local-selected? (atom local?)]
|
|
|
+ (-> (shui/dialog-confirm!
|
|
|
+ [:div.text-xs.opacity-60.-my-2
|
|
|
+ (when (and local? (not= (:block/uuid asset-block) block-id))
|
|
|
+ [:label.flex.gap-1.items-center
|
|
|
+ (shui/checkbox
|
|
|
+ {:default-checked @*local-selected?
|
|
|
+ :on-checked-change #(reset! *local-selected? %)})
|
|
|
+ (t :asset/physical-delete)])]
|
|
|
+ {:title (t :asset/confirm-delete (.toLocaleLowerCase (t :text/image)))
|
|
|
+ :outside-cancel? true})
|
|
|
+ (p/then (fn []
|
|
|
+ (shui/dialog-close!)
|
|
|
+ (editor-handler/delete-asset-of-block!
|
|
|
+ {:block-id block-id
|
|
|
+ :asset-block asset-block
|
|
|
+ :local? local?
|
|
|
+ :delete-local? @*local-selected?
|
|
|
+ :repo (state/get-current-repo)
|
|
|
+ :href src
|
|
|
+ :title title
|
|
|
+ :full-text full-text})))))))]
|
|
|
+ [:.asset-action-bar {:aria-hidden "true"}
|
|
|
+ (shui/button-group
|
|
|
+ (shui/button
|
|
|
+ {:variant :outline
|
|
|
+ :size :icon
|
|
|
+ :class "h-7 w-7"
|
|
|
+ :on-pointer-down util/stop
|
|
|
+ :on-click (fn [e]
|
|
|
+ (shui/popup-show! (.closest (.-target e) ".asset-action-bar")
|
|
|
+ (fn []
|
|
|
+ [:div
|
|
|
+ {:on-click #(shui/popup-hide!)}
|
|
|
(shui/dropdown-menu-item
|
|
|
- {:on-click (fn [e]
|
|
|
- (util/stop e)
|
|
|
- (if local?
|
|
|
- (ipc/ipc "openFileInFolder" image-src)
|
|
|
- (js/window.apis.openExternal image-src)))}
|
|
|
+ {:on-click #(some-> (db/entity [:block/uuid (get-blockid)])
|
|
|
+ (editor-handler/edit-block! :max {:container-id :unknown-container}))}
|
|
|
[:span.flex.items-center.gap-1
|
|
|
- (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
|
|
|
-
|
|
|
- (when-not config/publishing?
|
|
|
- [:<>
|
|
|
- (shui/dropdown-menu-separator)
|
|
|
- (shui/dropdown-menu-item
|
|
|
- {:on-click handle-delete!}
|
|
|
- [:span.flex.items-center.gap-1.text-red-700
|
|
|
- (ui/icon "trash") (t :asset/delete)])])])
|
|
|
- {:align :start
|
|
|
- :dropdown-menu? true}))}
|
|
|
- (shui/tabler-icon "dots-vertical")))])])]))
|
|
|
-
|
|
|
-;; TODO: store image height and width for better ux
|
|
|
+ (ui/icon "edit") (t :asset/edit-block)])
|
|
|
+ (shui/dropdown-menu-item
|
|
|
+ {:on-click handle-copy!}
|
|
|
+ [:span.flex.items-center.gap-1
|
|
|
+ (ui/icon "copy") (t :asset/copy)])
|
|
|
+ (when (util/electron?)
|
|
|
+ (shui/dropdown-menu-item
|
|
|
+ {:on-click (fn [e]
|
|
|
+ (util/stop e)
|
|
|
+ (if local?
|
|
|
+ (ipc/ipc "openFileInFolder" image-src)
|
|
|
+ (js/window.apis.openExternal image-src)))}
|
|
|
+ [:span.flex.items-center.gap-1
|
|
|
+ (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
|
|
|
+
|
|
|
+ (when-not config/publishing?
|
|
|
+ [:<>
|
|
|
+ (shui/dropdown-menu-separator)
|
|
|
+ (shui/dropdown-menu-item
|
|
|
+ {:on-click handle-delete!}
|
|
|
+ [:span.flex.items-center.gap-1.text-red-700
|
|
|
+ (ui/icon "trash") (t :asset/delete)])])])
|
|
|
+ {:align :start
|
|
|
+ :dropdown-menu? true}))}
|
|
|
+ (shui/tabler-icon "dots-vertical")))])])])))
|
|
|
+
|
|
|
(rum/defcs ^:large-vars/cleanup-todo resizable-image <
|
|
|
(rum/local nil ::size)
|
|
|
{:will-unmount (fn [state]
|
|
|
@@ -410,16 +432,10 @@
|
|
|
(let [breadcrumb? (:breadcrumb? config)
|
|
|
positioned? (:property-position config)
|
|
|
asset-block (:asset-block config)
|
|
|
- width (or (get-in asset-block [:logseq.property.asset/resize-metadata :width])
|
|
|
- (:width metadata))
|
|
|
+ width (:width metadata)
|
|
|
*width (get state ::size)
|
|
|
- width (or @*width width 250)
|
|
|
- metadata' (merge
|
|
|
- (cond->
|
|
|
- {:height 125}
|
|
|
- width
|
|
|
- (assoc :width width))
|
|
|
- metadata)
|
|
|
+ width (or @*width width)
|
|
|
+ metadata' (assoc metadata :width width)
|
|
|
resizable? (and (not (mobile-util/native-platform?))
|
|
|
(not breadcrumb?)
|
|
|
(not positioned?))
|
|
|
@@ -503,14 +519,13 @@
|
|
|
repo (state/get-current-repo)
|
|
|
href (config/get-local-asset-absolute-path href)
|
|
|
db-based? (config/db-based-graph? repo)]
|
|
|
- (when (and (or db-based?
|
|
|
- (util/electron?)
|
|
|
- (mobile-util/native-platform?))
|
|
|
- (nil? @src))
|
|
|
+
|
|
|
+ (when (nil? @src)
|
|
|
(p/then (assets-handler/<make-asset-url href)
|
|
|
#(reset! src (common-util/safe-decode-uri-component %))))
|
|
|
-
|
|
|
- (when @src
|
|
|
+ (:image-placeholder config)
|
|
|
+ (if-not @src
|
|
|
+ (:image-placeholder config)
|
|
|
(let [ext (keyword (or (util/get-file-ext @src)
|
|
|
(util/get-file-ext href)))
|
|
|
repo (state/get-current-repo)
|
|
|
@@ -1074,13 +1089,35 @@
|
|
|
file-exists? @(::file-exists? state)
|
|
|
repo (state/get-current-repo)
|
|
|
asset-file-write-finished? (state/sub :assets/asset-file-write-finish
|
|
|
- {:path-in-sub-atom [repo (str (:block/uuid block))]})]
|
|
|
- (when (or file-exists? asset-file-write-finished?)
|
|
|
- (asset-link (assoc config :asset-block block)
|
|
|
+ {:path-in-sub-atom [repo (str (:block/uuid block))]})
|
|
|
+ asset-type (:logseq.property.asset/type block)
|
|
|
+ image? (contains? (common-config/img-formats) (keyword asset-type))
|
|
|
+ width (get-in block [:logseq.property.asset/resize-metadata :width])
|
|
|
+ asset-width (:logseq.property.asset/width block)
|
|
|
+ asset-height (:logseq.property.asset/height block)
|
|
|
+ img-metadata (when image?
|
|
|
+ (let [width (or width 250 asset-width)
|
|
|
+ aspect-ratio (when (and asset-width asset-height)
|
|
|
+ (/ asset-width asset-height))]
|
|
|
+ (merge
|
|
|
+ (when width
|
|
|
+ {:width width})
|
|
|
+ (when (and width aspect-ratio)
|
|
|
+ {:height (/ width aspect-ratio)}))))
|
|
|
+ img-placeholder (when image?
|
|
|
+ [:div.img-placeholder.asset-container
|
|
|
+ {:style img-metadata}])]
|
|
|
+ (cond
|
|
|
+ (or file-exists? asset-file-write-finished?)
|
|
|
+ (asset-link (assoc config
|
|
|
+ :asset-block block
|
|
|
+ :image-placeholder img-placeholder)
|
|
|
(:block/title block)
|
|
|
(path/path-join (str "../" common-config/local-assets-dir) file)
|
|
|
- nil
|
|
|
- nil))))
|
|
|
+ img-metadata
|
|
|
+ nil)
|
|
|
+ image?
|
|
|
+ img-placeholder)))
|
|
|
|
|
|
(defn- img-audio-video?
|
|
|
[block]
|