Skip to content

Commit 1fca2a9

Browse files
committed
enhance(ux): store image width/height to avoid scrolling junk
related to logseq/db-test#562 related to logseq/db-test#488
1 parent ba8442e commit 1fca2a9

File tree

6 files changed

+176
-125
lines changed

6 files changed

+176
-125
lines changed

deps/db/src/logseq/db/frontend/malli_schema.cljs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,9 @@
483483
;; TODO: Derive required property types from existing schema in frontend.property
484484
[[:logseq.property.asset/type :string]
485485
[:logseq.property.asset/checksum :string]
486-
[:logseq.property.asset/size :int]]
486+
[:logseq.property.asset/size :int]
487+
[:logseq.property.asset/width {:optional true} :int]
488+
[:logseq.property.asset/height {:optional true} :int]]
487489
block-attrs
488490
page-or-block-attrs)))
489491

deps/db/src/logseq/db/frontend/property.cljs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,16 @@
492492
:hide? true
493493
:public? false}
494494
:queryable? true}
495+
:logseq.property.asset/width {:title "Image width"
496+
:schema {:type :raw-number
497+
:hide? true
498+
:public? false}
499+
:queryable? true}
500+
:logseq.property.asset/height {:title "Image height"
501+
:schema {:type :raw-number
502+
:hide? true
503+
:public? false}
504+
:queryable? true}
495505
:logseq.property.asset/checksum {:title "File checksum"
496506
:schema {:type :string
497507
:hide? true

deps/db/src/logseq/db/frontend/schema.cljs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
(map (juxt :major :minor)
3838
[(parse-schema-version x) (parse-schema-version y)])))
3939

40-
(def version (parse-schema-version "65.12"))
40+
(def version (parse-schema-version "65.13"))
4141

4242
(defn major-version
4343
"Return a number.

src/main/frontend/components/block.cljs

Lines changed: 153 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
[frontend.handler.file-sync :as file-sync]
5252
[frontend.handler.notification :as notification]
5353
[frontend.handler.plugin :as plugin-handler]
54+
[frontend.handler.property :as property-handler]
5455
[frontend.handler.property.file :as property-file]
5556
[frontend.handler.property.util :as pu]
5657
[frontend.handler.route :as route-handler]
@@ -301,106 +302,127 @@
301302
[:span.handle-left.image-resize (assoc handle-props :ref *handle-left)]
302303
[:span.handle-right.image-resize (assoc handle-props :ref *handle-right)]]))
303304

305+
(defn measure-image! [url on-dimensions]
306+
(let [img (js/Image.)]
307+
(set! (.-onload img)
308+
(fn []
309+
(on-dimensions (.-naturalWidth img) (.-naturalHeight img))))
310+
(set! (.-src img) url)))
311+
304312
(defonce *resizing-image? (atom false))
305-
(rum/defc asset-container
313+
(rum/defc ^:large-vars/cleanup-todo asset-container
306314
[asset-block src title metadata {:keys [breadcrumb? positioned? local? full-text]}]
307-
(let [*el-ref (rum/use-ref nil)
308-
image-src (fs/asset-path-normalize src)
309-
src' (if (or (string/starts-with? src "/")
310-
(string/starts-with? src "~"))
311-
(str "file://" src)
312-
src)
313-
get-blockid #(some-> (rum/deref *el-ref) (.closest "[blockid]") (.getAttribute "blockid") (uuid))]
314-
[:div.asset-container
315-
{:key "resize-asset-container"
316-
:on-pointer-down util/stop
317-
:on-click (fn [e]
318-
(util/stop e)
319-
(when (= "IMG" (some-> (.-target e) (.-nodeName)))
320-
(open-lightbox! e)))
321-
:ref *el-ref}
322-
[:img.rounded-sm.relative
323-
(merge
324-
{:loading "lazy"
325-
:referrerPolicy "no-referrer"
326-
:src src'
327-
:title title}
328-
metadata)]
329-
(when (and (not breadcrumb?)
330-
(not positioned?))
331-
[:<>
332-
(let [handle-copy!
333-
(fn [_e]
334-
(-> (util/copy-image-to-clipboard image-src)
335-
(p/then #(notification/show! "Copied!" :success))))
336-
handle-delete!
337-
(fn [_e]
338-
(when-let [block-id (get-blockid)]
339-
(let [*local-selected? (atom local?)]
340-
(-> (shui/dialog-confirm!
341-
[:div.text-xs.opacity-60.-my-2
342-
(when (and local? (not= (:block/uuid asset-block) block-id))
343-
[:label.flex.gap-1.items-center
344-
(shui/checkbox
345-
{:default-checked @*local-selected?
346-
:on-checked-change #(reset! *local-selected? %)})
347-
(t :asset/physical-delete)])]
348-
{:title (t :asset/confirm-delete (.toLocaleLowerCase (t :text/image)))
349-
:outside-cancel? true})
350-
(p/then (fn []
351-
(shui/dialog-close!)
352-
(editor-handler/delete-asset-of-block!
353-
{:block-id block-id
354-
:asset-block asset-block
355-
:local? local?
356-
:delete-local? @*local-selected?
357-
:repo (state/get-current-repo)
358-
:href src
359-
:title title
360-
:full-text full-text})))))))]
361-
[:.asset-action-bar {:aria-hidden "true"}
362-
(shui/button-group
363-
(shui/button
364-
{:variant :outline
365-
:size :icon
366-
:class "h-7 w-7"
367-
:on-pointer-down util/stop
368-
:on-click (fn [e]
369-
(shui/popup-show! (.closest (.-target e) ".asset-action-bar")
370-
(fn []
371-
[:div
372-
{:on-click #(shui/popup-hide!)}
373-
(shui/dropdown-menu-item
374-
{:on-click #(some-> (db/entity [:block/uuid (get-blockid)])
375-
(editor-handler/edit-block! :max {:container-id :unknown-container}))}
376-
[:span.flex.items-center.gap-1
377-
(ui/icon "edit") (t :asset/edit-block)])
378-
(shui/dropdown-menu-item
379-
{:on-click handle-copy!}
380-
[:span.flex.items-center.gap-1
381-
(ui/icon "copy") (t :asset/copy)])
382-
(when (util/electron?)
315+
(let [asset-width (:logseq.property.asset/width asset-block)
316+
asset-height (:logseq.property.asset/height asset-block)]
317+
(hooks/use-effect!
318+
(fn []
319+
(when-not (or asset-width asset-height)
320+
(measure-image!
321+
src
322+
(fn [width height]
323+
(when (nil? (:logseq.property.asset/width asset-block))
324+
(property-handler/set-block-properties! (state/get-current-repo)
325+
(:block/uuid asset-block)
326+
{:logseq.property.asset/width width
327+
:logseq.property.asset/height height})))))
328+
(fn []))
329+
[])
330+
(let [*el-ref (rum/use-ref nil)
331+
image-src (fs/asset-path-normalize src)
332+
src' (if (or (string/starts-with? src "/")
333+
(string/starts-with? src "~"))
334+
(str "file://" src)
335+
src)
336+
get-blockid #(some-> (rum/deref *el-ref) (.closest "[blockid]") (.getAttribute "blockid") (uuid))]
337+
[:div.asset-container
338+
{:key "resize-asset-container"
339+
:on-pointer-down util/stop
340+
:on-click (fn [e]
341+
(util/stop e)
342+
(when (= "IMG" (some-> (.-target e) (.-nodeName)))
343+
(open-lightbox! e)))
344+
:ref *el-ref}
345+
[:img.rounded-sm.relative.fade-in.fade-in-faster
346+
(merge
347+
{:loading "lazy"
348+
:referrerPolicy "no-referrer"
349+
:src src'
350+
:title title}
351+
metadata)]
352+
(when (and (not breadcrumb?)
353+
(not positioned?))
354+
[:<>
355+
(let [handle-copy!
356+
(fn [_e]
357+
(-> (util/copy-image-to-clipboard image-src)
358+
(p/then #(notification/show! "Copied!" :success))))
359+
handle-delete!
360+
(fn [_e]
361+
(when-let [block-id (get-blockid)]
362+
(let [*local-selected? (atom local?)]
363+
(-> (shui/dialog-confirm!
364+
[:div.text-xs.opacity-60.-my-2
365+
(when (and local? (not= (:block/uuid asset-block) block-id))
366+
[:label.flex.gap-1.items-center
367+
(shui/checkbox
368+
{:default-checked @*local-selected?
369+
:on-checked-change #(reset! *local-selected? %)})
370+
(t :asset/physical-delete)])]
371+
{:title (t :asset/confirm-delete (.toLocaleLowerCase (t :text/image)))
372+
:outside-cancel? true})
373+
(p/then (fn []
374+
(shui/dialog-close!)
375+
(editor-handler/delete-asset-of-block!
376+
{:block-id block-id
377+
:asset-block asset-block
378+
:local? local?
379+
:delete-local? @*local-selected?
380+
:repo (state/get-current-repo)
381+
:href src
382+
:title title
383+
:full-text full-text})))))))]
384+
[:.asset-action-bar {:aria-hidden "true"}
385+
(shui/button-group
386+
(shui/button
387+
{:variant :outline
388+
:size :icon
389+
:class "h-7 w-7"
390+
:on-pointer-down util/stop
391+
:on-click (fn [e]
392+
(shui/popup-show! (.closest (.-target e) ".asset-action-bar")
393+
(fn []
394+
[:div
395+
{:on-click #(shui/popup-hide!)}
383396
(shui/dropdown-menu-item
384-
{:on-click (fn [e]
385-
(util/stop e)
386-
(if local?
387-
(ipc/ipc "openFileInFolder" image-src)
388-
(js/window.apis.openExternal image-src)))}
397+
{:on-click #(some-> (db/entity [:block/uuid (get-blockid)])
398+
(editor-handler/edit-block! :max {:container-id :unknown-container}))}
389399
[:span.flex.items-center.gap-1
390-
(ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
391-
392-
(when-not config/publishing?
393-
[:<>
394-
(shui/dropdown-menu-separator)
395-
(shui/dropdown-menu-item
396-
{:on-click handle-delete!}
397-
[:span.flex.items-center.gap-1.text-red-700
398-
(ui/icon "trash") (t :asset/delete)])])])
399-
{:align :start
400-
:dropdown-menu? true}))}
401-
(shui/tabler-icon "dots-vertical")))])])]))
402-
403-
;; TODO: store image height and width for better ux
400+
(ui/icon "edit") (t :asset/edit-block)])
401+
(shui/dropdown-menu-item
402+
{:on-click handle-copy!}
403+
[:span.flex.items-center.gap-1
404+
(ui/icon "copy") (t :asset/copy)])
405+
(when (util/electron?)
406+
(shui/dropdown-menu-item
407+
{:on-click (fn [e]
408+
(util/stop e)
409+
(if local?
410+
(ipc/ipc "openFileInFolder" image-src)
411+
(js/window.apis.openExternal image-src)))}
412+
[:span.flex.items-center.gap-1
413+
(ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
414+
415+
(when-not config/publishing?
416+
[:<>
417+
(shui/dropdown-menu-separator)
418+
(shui/dropdown-menu-item
419+
{:on-click handle-delete!}
420+
[:span.flex.items-center.gap-1.text-red-700
421+
(ui/icon "trash") (t :asset/delete)])])])
422+
{:align :start
423+
:dropdown-menu? true}))}
424+
(shui/tabler-icon "dots-vertical")))])])])))
425+
404426
(rum/defcs ^:large-vars/cleanup-todo resizable-image <
405427
(rum/local nil ::size)
406428
{:will-unmount (fn [state]
@@ -410,16 +432,10 @@
410432
(let [breadcrumb? (:breadcrumb? config)
411433
positioned? (:property-position config)
412434
asset-block (:asset-block config)
413-
width (or (get-in asset-block [:logseq.property.asset/resize-metadata :width])
414-
(:width metadata))
435+
width (:width metadata)
415436
*width (get state ::size)
416-
width (or @*width width 250)
417-
metadata' (merge
418-
(cond->
419-
{:height 125}
420-
width
421-
(assoc :width width))
422-
metadata)
437+
width (or @*width width)
438+
metadata' (assoc metadata :width width)
423439
resizable? (and (not (mobile-util/native-platform?))
424440
(not breadcrumb?)
425441
(not positioned?))
@@ -503,14 +519,13 @@
503519
repo (state/get-current-repo)
504520
href (config/get-local-asset-absolute-path href)
505521
db-based? (config/db-based-graph? repo)]
506-
(when (and (or db-based?
507-
(util/electron?)
508-
(mobile-util/native-platform?))
509-
(nil? @src))
522+
523+
(when (nil? @src)
510524
(p/then (assets-handler/<make-asset-url href)
511525
#(reset! src (common-util/safe-decode-uri-component %))))
512-
513-
(when @src
526+
(:image-placeholder config)
527+
(if-not @src
528+
(:image-placeholder config)
514529
(let [ext (keyword (or (util/get-file-ext @src)
515530
(util/get-file-ext href)))
516531
repo (state/get-current-repo)
@@ -1074,13 +1089,35 @@
10741089
file-exists? @(::file-exists? state)
10751090
repo (state/get-current-repo)
10761091
asset-file-write-finished? (state/sub :assets/asset-file-write-finish
1077-
{:path-in-sub-atom [repo (str (:block/uuid block))]})]
1078-
(when (or file-exists? asset-file-write-finished?)
1079-
(asset-link (assoc config :asset-block block)
1092+
{:path-in-sub-atom [repo (str (:block/uuid block))]})
1093+
asset-type (:logseq.property.asset/type block)
1094+
image? (contains? (common-config/img-formats) (keyword asset-type))
1095+
width (get-in block [:logseq.property.asset/resize-metadata :width])
1096+
asset-width (:logseq.property.asset/width block)
1097+
asset-height (:logseq.property.asset/height block)
1098+
img-metadata (when image?
1099+
(let [width (or width 250 asset-width)
1100+
aspect-ratio (when (and asset-width asset-height)
1101+
(/ asset-width asset-height))]
1102+
(merge
1103+
(when width
1104+
{:width width})
1105+
(when (and width aspect-ratio)
1106+
{:height (/ width aspect-ratio)}))))
1107+
img-placeholder (when image?
1108+
[:div.img-placeholder.asset-container
1109+
{:style img-metadata}])]
1110+
(cond
1111+
(or file-exists? asset-file-write-finished?)
1112+
(asset-link (assoc config
1113+
:asset-block block
1114+
:image-placeholder img-placeholder)
10801115
(:block/title block)
10811116
(path/path-join (str "../" common-config/local-assets-dir) file)
1082-
nil
1083-
nil))))
1117+
img-metadata
1118+
nil)
1119+
image?
1120+
img-placeholder)))
10841121

10851122
(defn- img-audio-video?
10861123
[block]

src/main/frontend/worker/db/migrate.cljs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,9 @@
430430
["65.9" {:properties [:logseq.property.embedding/hnsw-label-updated-at]}]
431431
["65.10" {:properties [:block/journal-day :logseq.property.view/sort-groups-by-property :logseq.property.view/sort-groups-desc?]}]
432432
["65.11" {:fix remove-block-path-refs}]
433-
["65.12" {:fix remove-position-property-from-url-properties}]])
433+
["65.12" {:fix remove-position-property-from-url-properties}]
434+
["65.13" {:properties [:logseq.property.asset/width
435+
:logseq.property.asset/height]}]])
434436

435437
(let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
436438
schema-version->updates)))]

src/main/logseq/api/db_based.cljs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,25 +219,25 @@
219219
(sdk-utils/result->js tag)))))
220220

221221
(defn tag-add-property [tag-id property-id-or-name]
222-
(p/let [tag (db/get-page tag-id)
223-
property (db/get-page property-id-or-name)]
222+
(p/let [tag (db/get-case-page tag-id)
223+
property (db/get-case-page property-id-or-name)]
224224
(when-not (ldb/class? tag) (throw (ex-info "Not a valid tag" {:tag tag-id})))
225225
(when-not (ldb/property? property) (throw (ex-info "Not a valid property" {:property property-id-or-name})))
226226
(when (and (not (ldb/public-built-in-property? property))
227227
(ldb/built-in? property))
228228
(throw (ex-info "This is a private built-in property that can't be used." {:value property})))
229229
(p/do!
230230
(db-property-handler/class-add-property! (:db/id tag) (:db/ident property))
231-
(sdk-utils/result->js (db/get-page tag-id)))))
231+
(sdk-utils/result->js (db/get-case-page tag-id)))))
232232

233233
(defn tag-remove-property [tag-id property-id-or-name]
234-
(p/let [tag (db/get-page tag-id)
235-
property (db/get-page property-id-or-name)]
234+
(p/let [tag (db/get-case-page tag-id)
235+
property (db/get-case-page property-id-or-name)]
236236
(when-not (ldb/class? tag) (throw (ex-info "Not a valid tag" {:tag tag-id})))
237237
(when-not (ldb/property? property) (throw (ex-info "Not a valid property" {:property property-id-or-name})))
238238
(p/do!
239239
(db-property-handler/class-remove-property! (:db/id tag) (:db/ident property))
240-
(sdk-utils/result->js (db/get-page tag-id)))))
240+
(sdk-utils/result->js (db/get-case-page tag-id)))))
241241

242242
(defn add-block-tag [id-or-name tag-id]
243243
(p/let [repo (state/get-current-repo)

0 commit comments

Comments
 (0)