mirror of
https://github.com/penpot/penpot.git
synced 2025-02-09 00:28:20 -05:00
Merge pull request #1672 from penpot/set-artboard-as-thumbnail
✨ Set an artboard as the file thumbnail
This commit is contained in:
commit
636dbd4e57
11 changed files with 150 additions and 26 deletions
|
@ -5,6 +5,7 @@
|
|||
### :boom: Breaking changes
|
||||
### :sparkles: New features
|
||||
|
||||
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)
|
||||
- Add border radius to our artboars [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
|
||||
- Allow send multiple team invitations at once [Taiga #2798](https://tree.taiga.io/project/penpot/us/2798)
|
||||
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
|
||||
|
|
|
@ -249,6 +249,8 @@
|
|||
(update :data assoc :pages [page-id]))))
|
||||
|
||||
(declare strip-frames-with-thumbnails)
|
||||
(declare extract-file-thumbnail)
|
||||
(declare get-first-page-data)
|
||||
|
||||
(s/def ::strip-frames-with-thumbnails ::us/boolean)
|
||||
|
||||
|
@ -256,16 +258,37 @@
|
|||
(s/keys :req-un [::profile-id ::file-id]
|
||||
:opt-un [::strip-frames-with-thumbnails]))
|
||||
|
||||
(s/def ::file-data-for-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
:opt-un [::strip-frames-with-thumbnails]))
|
||||
|
||||
(sv/defmethod ::file-data-for-thumbnail
|
||||
"Retrieves the data for generate the thumbnail of the file. Used mainly for render
|
||||
thumbnails on dashboard."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as props}]
|
||||
(check-read-permissions! pool profile-id file-id)
|
||||
(p/let [file (retrieve-file cfg file-id)
|
||||
data (get-first-page-data file props)
|
||||
file-thumbnail (extract-file-thumbnail (get-in file [:data :pages-index]))]
|
||||
|
||||
(assoc data :file-thumbnail file-thumbnail)))
|
||||
|
||||
(sv/defmethod ::page
|
||||
"Retrieves the first page of the file. Used mainly for render
|
||||
thumbnails on dashboard."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as props}]
|
||||
(check-read-permissions! pool profile-id file-id)
|
||||
(p/let [file (retrieve-file cfg file-id)
|
||||
page-id (get-in file [:data :pages 0])]
|
||||
(cond-> (get-in file [:data :pages-index page-id])
|
||||
(true? (:strip-frames-with-thumbnails props))
|
||||
(strip-frames-with-thumbnails))))
|
||||
data (get-first-page-data file props)]
|
||||
data))
|
||||
|
||||
(defn get-first-page-data
|
||||
[file props]
|
||||
(let [page-id (get-in file [:data :pages 0])
|
||||
data (cond-> (get-in file [:data :pages-index page-id])
|
||||
(true? (:strip-frames-with-thumbnails props))
|
||||
(strip-frames-with-thumbnails))]
|
||||
data))
|
||||
|
||||
(defn strip-frames-with-thumbnails
|
||||
"Remove unnecesary shapes from frames that have thumbnail."
|
||||
|
@ -295,6 +318,16 @@
|
|||
(update data :objects update-objects)))
|
||||
|
||||
|
||||
(defn extract-file-thumbnail
|
||||
"Extract the frame marked as file-thumbnail"
|
||||
[pages]
|
||||
(->> pages
|
||||
vals
|
||||
(mapcat :objects)
|
||||
vals
|
||||
(filter :file-thumbnail)
|
||||
first))
|
||||
|
||||
;; --- Query: Shared Library Files
|
||||
|
||||
(def ^:private sql:team-shared-files
|
||||
|
|
|
@ -959,6 +959,36 @@
|
|||
(let [selected (wsh/lookup-selected state)]
|
||||
(rx/of (dch/update-shapes selected #(update % :blocked not)))))))
|
||||
|
||||
(defn extract-file-thumbnails-from-page
|
||||
[state selected page]
|
||||
(let [extract-frames (fn [page-id]
|
||||
(let [objects (wsh/lookup-page-objects state page-id)]
|
||||
(cph/get-frames objects)))
|
||||
page-id (key page)
|
||||
frames-with-thumbnail (->> (extract-frames page-id)
|
||||
(filter (comp true? :file-thumbnail))
|
||||
(map :id)
|
||||
(remove #(some #{%} selected))
|
||||
(map #(into {} {:id % :page-id page-id})))]
|
||||
(when frames-with-thumbnail frames-with-thumbnail)))
|
||||
|
||||
|
||||
(defn toggle-file-thumbnail-selected
|
||||
[]
|
||||
(ptk/reify ::toggle-file-thumbnail-selected
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [selected (wsh/lookup-selected state)
|
||||
pages (get-in state [:workspace-data
|
||||
:pages-index])
|
||||
file-thumbnails (->> pages
|
||||
(mapcat #(extract-file-thumbnails-from-page state selected %)))]
|
||||
(rx/concat
|
||||
(rx/from
|
||||
(for [ft file-thumbnails]
|
||||
(dch/update-shapes [(:id ft)] #(update % :file-thumbnail not) (:page-id ft) nil)))
|
||||
(rx/of (dch/update-shapes selected #(update % :file-thumbnail not))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Navigation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -71,9 +71,10 @@
|
|||
(update :undo-changes conj (assoc change :operations uops)))))
|
||||
|
||||
(defn update-shapes
|
||||
([ids f] (update-shapes ids f nil))
|
||||
([ids f {:keys [reg-objects? save-undo? attrs ignore-tree]
|
||||
:or {reg-objects? false save-undo? true attrs nil}}]
|
||||
([ids f] (update-shapes ids f nil nil))
|
||||
([ids f keys] (update-shapes ids f nil keys))
|
||||
([ids f page-id {:keys [reg-objects? save-undo? attrs ignore-tree]
|
||||
:or {reg-objects? false save-undo? true attrs nil}}]
|
||||
|
||||
(us/assert ::coll-of-uuid ids)
|
||||
(us/assert fn? f)
|
||||
|
@ -81,8 +82,8 @@
|
|||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
(let [page-id (or page-id (:current-page-id state))
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
changes {:redo-changes []
|
||||
:undo-changes []
|
||||
:origin it
|
||||
|
@ -91,8 +92,8 @@
|
|||
ids (into [] (filter some?) ids)
|
||||
|
||||
changes (reduce
|
||||
#(update-shape-changes %1 page-id objects f attrs %2 (get ignore-tree %2))
|
||||
changes ids)]
|
||||
#(update-shape-changes %1 page-id objects f attrs %2 (get ignore-tree %2))
|
||||
changes ids)]
|
||||
|
||||
(when-not (empty? (:redo-changes changes))
|
||||
(let [reg-objs {:type :reg-objects
|
||||
|
|
|
@ -364,7 +364,12 @@
|
|||
|
||||
:toggle-focus-mode {:command "f"
|
||||
:tooltip "F"
|
||||
:fn #(st/emit! (dw/toggle-focus-mode))}})
|
||||
:fn #(st/emit! (dw/toggle-focus-mode))}
|
||||
|
||||
:thumbnail-set {:tooltip (ds/shift "T")
|
||||
:command "shift+t"
|
||||
:fn #(st/emit! (dw/toggle-file-thumbnail-selected))}})
|
||||
|
||||
|
||||
(def opacity-shortcuts
|
||||
(into {} (->>
|
||||
|
|
|
@ -215,6 +215,31 @@
|
|||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}])))]]]))
|
||||
|
||||
(mf/defc file-thumbnail-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [data embed? include-metadata?] :as props
|
||||
:or {embed? false include-metadata? false}}]
|
||||
(let [data (assoc data :x 0 :y 0)
|
||||
vbox (format-viewbox {:width (:width data 0) :height (:height data 0)})
|
||||
background-color (get-in data [:options :background] default-color)]
|
||||
|
||||
[:& (mf/provider embed/context) {:value embed?}
|
||||
[:& (mf/provider export/include-metadata-ctx) {:value include-metadata?}
|
||||
[:svg {:view-box vbox
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")
|
||||
:style {:width "100%"
|
||||
:height "100%"
|
||||
:background background-color}}
|
||||
|
||||
(when include-metadata?
|
||||
[:& export/export-page {:options (:options data)}])
|
||||
|
||||
[:> shape-container {:shape data}
|
||||
[:& frame/frame-thumbnail {:shape data}]]]]]))
|
||||
|
||||
(mf/defc frame-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [objects frame zoom show-thumbnails?] :or {zoom 1} :as props}]
|
||||
|
|
|
@ -38,12 +38,12 @@
|
|||
(def ^:const CACHE-NAME "penpot")
|
||||
(def ^:const CACHE-URL "https://penpot.app/cache/")
|
||||
|
||||
|
||||
(defn use-thumbnail-cache
|
||||
"Creates some hooks to handle the files thumbnails cache"
|
||||
[file]
|
||||
|
||||
(let [cache-url (str CACHE-URL (:id file) "/" (:revn file) ".svg")
|
||||
|
||||
get-thumbnail
|
||||
(mf/use-callback
|
||||
(mf/deps cache-url)
|
||||
|
@ -83,14 +83,12 @@
|
|||
(if (some? thumb-data)
|
||||
(rx/of thumb-data)
|
||||
(->> (wrk/ask! {:cmd :thumbnails/generate
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])})
|
||||
:file-id (:id file)})
|
||||
(rx/tap cache-thumbnail)))))
|
||||
|
||||
;; If we have a problem we delegate to the thumbnail generation
|
||||
(rx/catch #(wrk/ask! {:cmd :thumbnails/generate
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])})))))))
|
||||
:file-id (:id file)})))))))
|
||||
|
||||
(mf/defc grid-item-thumbnail
|
||||
{::mf/wrap [mf/memo]}
|
||||
|
|
|
@ -165,6 +165,22 @@
|
|||
:on-click do-flip-horizontal}]
|
||||
[:& menu-separator]]))
|
||||
|
||||
(mf/defc context-menu-thumbnail
|
||||
[{:keys [shapes]}]
|
||||
(let [single? (= (count shapes) 1)
|
||||
has-frame? (->> shapes (d/seek #(= :frame (:type %))))
|
||||
is-frame? (and single? has-frame?)
|
||||
do-toggle-thumbnail (st/emitf (dw/toggle-file-thumbnail-selected))]
|
||||
(when is-frame?
|
||||
[:*
|
||||
(if (every? :file-thumbnail shapes)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.thumbnail-remove")
|
||||
:on-click do-toggle-thumbnail}]
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.thumbnail-set")
|
||||
:shortcut (sc/get-tooltip :thumbnail-set)
|
||||
:on-click do-toggle-thumbnail}])
|
||||
[:& menu-separator]])))
|
||||
|
||||
(mf/defc context-menu-group
|
||||
[{:keys [shapes]}]
|
||||
|
||||
|
@ -437,6 +453,7 @@
|
|||
[:> context-menu-edit props]
|
||||
[:> context-menu-layer-position props]
|
||||
[:> context-menu-flip props]
|
||||
[:> context-menu-thumbnail props]
|
||||
[:> context-menu-group props]
|
||||
[:> context-focus-mode-menu props]
|
||||
[:> context-menu-path props]
|
||||
|
|
|
@ -29,11 +29,10 @@
|
|||
(rx/throw {:type :unexpected
|
||||
:code (:error response)})))
|
||||
|
||||
(defn- request-page
|
||||
[file-id page-id]
|
||||
(let [uri (u/join (cfg/get-public-uri) "api/rpc/query/page")
|
||||
(defn- request-thumbnail
|
||||
[file-id]
|
||||
(let [uri (u/join (cfg/get-public-uri) "api/rpc/query/file-data-for-thumbnail")
|
||||
params {:file-id file-id
|
||||
:id page-id
|
||||
:strip-frames-with-thumbnails true}]
|
||||
(->> (http/send!
|
||||
{:method :get
|
||||
|
@ -45,20 +44,23 @@
|
|||
|
||||
(defonce cache (atom {}))
|
||||
|
||||
(defn render-page
|
||||
(defn render-frame
|
||||
[data ckey]
|
||||
(let [prev (get @cache ckey)]
|
||||
(if (= (:data prev) data)
|
||||
(:result prev)
|
||||
(let [elem (mf/element render/page-svg #js {:data data :width "290" :height "150" :thumbnails? true})
|
||||
(let [file-thumbnail (:file-thumbnail data)
|
||||
elem (if file-thumbnail
|
||||
(mf/element render/file-thumbnail-svg #js {:data file-thumbnail :width "290" :height "150"})
|
||||
(mf/element render/page-svg #js {:data data :width "290" :height "150" :thumbnails? true}))
|
||||
result (rds/renderToStaticMarkup elem)]
|
||||
(swap! cache assoc ckey {:data data :result result})
|
||||
result))))
|
||||
|
||||
(defmethod impl/handler :thumbnails/generate
|
||||
[{:keys [file-id page-id] :as message}]
|
||||
(->> (request-page file-id page-id)
|
||||
[{:keys [file-id] :as message}]
|
||||
(->> (request-thumbnail file-id)
|
||||
(rx/map
|
||||
(fn [data]
|
||||
{:svg (render-page data #{file-id page-id})
|
||||
{:svg (render-frame data #{file-id})
|
||||
:fonts @fonts/loaded}))))
|
||||
|
|
|
@ -3265,6 +3265,12 @@ msgstr "Flip horizontal"
|
|||
msgid "workspace.shape.menu.flip-vertical"
|
||||
msgstr "Flip vertical"
|
||||
|
||||
msgid "workspace.shape.menu.thumbnail-set"
|
||||
msgstr "Set as thumbnail"
|
||||
|
||||
msgid "workspace.shape.menu.thumbnail-remove"
|
||||
msgstr "Remove thumbnail"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.flow-start"
|
||||
msgstr "Flow start"
|
||||
|
|
|
@ -3279,6 +3279,12 @@ msgstr "Voltear horizontal"
|
|||
msgid "workspace.shape.menu.flip-vertical"
|
||||
msgstr "Voltear vertical"
|
||||
|
||||
msgid "workspace.shape.menu.thumbnail-set"
|
||||
msgstr "Poner como miniatura"
|
||||
|
||||
msgid "workspace.shape.menu.thumbnail-remove"
|
||||
msgstr "Quitar miniatura"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.flow-start"
|
||||
msgstr "Inicio de flujo"
|
||||
|
|
Loading…
Add table
Reference in a new issue