0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-02 04:19:08 -05:00

Exporting big files flow

This commit is contained in:
Alejandro Alonso 2022-03-18 14:00:10 +01:00 committed by Andrey Antukh
parent 0d4de50f13
commit 3ae7c42afa
9 changed files with 118 additions and 87 deletions

View file

@ -5,7 +5,8 @@
### :boom: Breaking changes ### :boom: Breaking changes
### :sparkles: New features ### :sparkles: New features
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/2854) - Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
- Multiexport from main menu [Taiga #520](https://tree.taiga.io/project/penpot/us/28541)
- Multipexport assets (aka bulk export) [Taiga #520](https://tree.taiga.io/project/penpot/us/520) - Multipexport assets (aka bulk export) [Taiga #520](https://tree.taiga.io/project/penpot/us/520)
- Set the artboard layer fixed at the top side of the layers [Taiga #2636](https://tree.taiga.io/project/penpot/us/2636) - Set the artboard layer fixed at the top side of the layers [Taiga #2636](https://tree.taiga.io/project/penpot/us/2636)
- Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526) - Set an artboard as the file thumbnail [Taiga #1526](https://tree.taiga.io/project/penpot/us/1526)

View file

@ -55,6 +55,7 @@
on-progress (fn [progress] on-progress (fn [progress]
(let [data {:type :export-update (let [data {:type :export-update
:resource-id (:id resource) :resource-id (:id resource)
:name (:name resource)
:status "running" :status "running"
:progress progress}] :progress progress}]
(redis/pub! topic data))) (redis/pub! topic data)))
@ -62,6 +63,7 @@
on-complete (fn [resource] on-complete (fn [resource]
(let [data {:type :export-update (let [data {:type :export-update
:resource-id (:id resource) :resource-id (:id resource)
:name (:name resource)
:size (:size resource) :size (:size resource)
:status "ended"}] :status "ended"}]
(redis/pub! topic data))) (redis/pub! topic data)))
@ -69,6 +71,7 @@
on-error (fn [cause] on-error (fn [cause]
(let [data {:type :export-update (let [data {:type :export-update
:resource-id (:id resource) :resource-id (:id resource)
:name (:name resource)
:status "error" :status "error"
:cause (ex-message cause)}] :cause (ex-message cause)}]
(redis/pub! topic data))) (redis/pub! topic data)))

View file

@ -235,13 +235,13 @@
.import-dialog, .import-dialog,
.export-dialog, .export-dialog,
.export-shapes-dialog { .export-multiple-dialog {
background-color: $color-white; background-color: $color-white;
border: 1px solid $color-gray-20; border: 1px solid $color-gray-20;
width: 30rem; width: 30rem;
min-height: 14rem; min-height: 14rem;
&.no-shapes { &.empty {
width: 39rem; width: 39rem;
} }
@ -1338,7 +1338,7 @@
} }
} }
// Export shapes // Export multiple
.export-progress-modal-overlay { .export-progress-modal-overlay {
display: flex; display: flex;
@ -1413,7 +1413,7 @@
} }
} }
.export-shapes-dialog { .export-multiple-dialog {
.modal-content { .modal-content {
padding: 0; padding: 0;
} }

View file

@ -6,6 +6,7 @@
(ns app.main.data.exports (ns app.main.data.exports
(:require (:require
[app.common.data.macros :as dm]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.persistence :as dwp]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
@ -92,8 +93,31 @@
(rx/of (modal/show :export-shapes {:exports (vec exports) (rx/of (modal/show :export-shapes {:exports (vec exports)
:filename filename})))))) :filename filename}))))))
(defn show-workspace-export-frames-dialog
([frames]
(ptk/reify ::show-workspace-export-frames-dialog
ptk/WatchEvent
(watch [_ state _]
(let [file-id (:current-file-id state)
page-id (:current-page-id state)
filename (-> (wsh/lookup-page state page-id)
:name
(dm/str ".pdf"))
exports (for [frame frames]
{:enabled true
:page-id page-id
:file-id file-id
:frame-id (:id frame)
:shape frame
:name (:name frame)})]
(rx/of (modal/show :export-frames
{:exports (vec exports)
:filename filename})))))))
(defn- initialize-export-status (defn- initialize-export-status
[exports filename resource-id] [exports filename resource-id query]
(ptk/reify ::initialize-export-status (ptk/reify ::initialize-export-status
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -106,7 +130,8 @@
:detail-visible true :detail-visible true
:exports exports :exports exports
:filename filename :filename filename
:last-update (dt/now)})))) :last-update (dt/now)
:query query}))))
(defn- update-export-status (defn- update-export-status
[{:keys [progress status resource-id name] :as data}] [{:keys [progress status resource-id name] :as data}]
@ -154,7 +179,7 @@
(dissoc state :export)))))))))) (dissoc state :export))))))))))
(defn request-multiple-export (defn request-multiple-export
[{:keys [filename exports] :as params}] [{:keys [filename exports query] :as params}]
(ptk/reify ::request-multiple-export (ptk/reify ::request-multiple-export
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
@ -187,11 +212,11 @@
;; Launch the exportation process and stores the resource id ;; Launch the exportation process and stores the resource id
;; locally. ;; locally.
(->> (rp/query! :export-shapes-multiple params) (->> (rp/query! query params)
(rx/tap (fn [{:keys [id]}] (rx/tap (fn [{:keys [id]}]
(vreset! resource-id id))) (vreset! resource-id id)))
(rx/map (fn [{:keys [id]}] (rx/map (fn [{:keys [id]}]
(initialize-export-status exports filename id)))) (initialize-export-status exports filename id query))))
;; We proceed to update the export state with incoming ;; We proceed to update the export state with incoming
;; progress updates. We delay the stoper for give some time ;; progress updates. We delay the stoper for give some time
@ -220,6 +245,6 @@
(ptk/reify ::retry-last-export (ptk/reify ::retry-last-export
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [{:keys [exports filename]} (:export state)] (let [{:keys [exports filename query]} (:export state)]
(rx/of (request-multiple-export {:exports exports :filename filename})))))) (rx/of (request-multiple-export {:exports exports :filename filename :query query}))))))

View file

@ -126,24 +126,14 @@
[_ params] [_ params]
(send-export-command :cmd :export-shapes :params params :blob? false)) (send-export-command :cmd :export-shapes :params params :blob? false))
(defmethod query :export-frames
[_ params]
(send-export-command :cmd :export-frames :params (assoc params :uri (str base-uri)) :blob? false))
(defmethod query :download-export-resource (defmethod query :download-export-resource
[_ id] [_ id]
(send-export-command :cmd :get-resource :params {:id id} :blob? true)) (send-export-command :cmd :get-resource :params {:id id} :blob? true))
(defmethod query :export-frames
[_ exports]
(let [params {:uri (str base-uri)
:cmd :export-frames
:wait false
:exports exports}]
(->> (http/send! {:method :post
:uri (u/join base-uri "api/export")
:body (http/transit-data params)
:credentials "include"
:response-type :blob})
(rx/mapcat handle-response)
(rx/ignore))))
(derive :upload-file-media-object ::multipart-upload) (derive :upload-file-media-object ::multipart-upload)
(derive :update-profile-photo ::multipart-upload) (derive :update-profile-photo ::multipart-upload)
(derive :update-team-photo ::multipart-upload) (derive :update-team-photo ::multipart-upload)

View file

@ -21,10 +21,8 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(mf/defc export-shapes-dialog (mf/defc export-multiple-dialog
{::mf/register modal/components [{:keys [exports filename title query no-selection]}]
::mf/register-as :export-shapes}
[{:keys [exports filename]}]
(let [lstate (mf/deref refs/export) (let [lstate (mf/deref refs/export)
in-progress? (:in-progress lstate) in-progress? (:in-progress lstate)
@ -45,7 +43,8 @@
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(st/emit! (modal/hide) (st/emit! (modal/hide)
(de/request-multiple-export {:filename filename :exports enabled-exports}))) (de/request-multiple-export {:filename filename :exports enabled-exports :query query})))
on-toggle-enabled on-toggle-enabled
(fn [index] (fn [index]
(swap! exports update-in [index :enabled] not)) (swap! exports update-in [index :enabled] not))
@ -53,15 +52,14 @@
change-all change-all
(fn [_] (fn [_]
(swap! exports (fn [exports] (swap! exports (fn [exports]
(mapv #(assoc % :enabled (not all-checked?)) exports)))) (mapv #(assoc % :enabled (not all-checked?)) exports))))]
]
[:div.modal-overlay [:div.modal-overlay
[:div.modal-container.export-shapes-dialog [:div.modal-container.export-multiple-dialog
{:class (when (empty? all-exports) "no-shapes")} {:class (when (empty? all-exports) "empty")}
[:div.modal-header [:div.modal-header
[:div.modal-header-title [:div.modal-header-title
[:h2 (tr "dashboard.export-shapes.title")]] [:h2 title]]
[:div.modal-close-button [:div.modal-close-button
{:on-click cancel-fn} i/close]] {:on-click cancel-fn} i/close]]
@ -76,7 +74,7 @@
all-checked? [:span i/checkbox-checked] all-checked? [:span i/checkbox-checked]
all-unchecked? [:span i/checkbox-unchecked] all-unchecked? [:span i/checkbox-unchecked]
:else [:span i/checkbox-intermediate])] :else [:span i/checkbox-intermediate])]
[:div.field.title (tr "dashboard.export-shapes.selected" [:div.field.title (tr "dashboard.export-multiple.selected"
(c (count enabled-exports)) (c (count enabled-exports))
(c (count all-exports)))]] (c (count all-exports)))]]
@ -90,22 +88,27 @@
[:span.unchecked i/checkbox-unchecked])] [:span.unchecked i/checkbox-unchecked])]
[:div.field.image [:div.field.image
[:svg {:view-box (dm/str x " " y " " width " " height) (if (some? (:thumbnail shape))
:width 24 [:img {:src (:thumbnail shape)}]
:height 20 [:svg {:view-box (dm/str x " " y " " width " " height)
:version "1.1" :width 24
:xmlns "http://www.w3.org/2000/svg" :height 20
:xmlnsXlink "http://www.w3.org/1999/xlink" :version "1.1"
;; Fix Chromium bug about color of html texts :xmlns "http://www.w3.org/2000/svg"
;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 :xmlnsXlink "http://www.w3.org/1999/xlink"
:style {:-webkit-print-color-adjust :exact}} ;; Fix Chromium bug about color of html texts
;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5
:style {:-webkit-print-color-adjust :exact}}
[:& shape-wrapper {:shape shape}]]] [:& shape-wrapper {:shape shape}]])]
[:div.field.name (cond-> (:name shape) suffix (str suffix))] [:div.field.name (cond-> (:name shape) suffix (str suffix))]
[:div.field.scale (dm/str (* width (:scale export)) "x" (when (:scale export)
(* height (:scale export)) "px ")] [:div.field.scale (dm/str (* width (:scale export)) "x"
[:div.field.extension (-> export :type d/name str/upper)]]))] (* height (:scale export)) "px ")])
(when (:type export)
[:div.field.extension (-> export :type d/name str/upper)])]))]
[:div.modal-footer [:div.modal-footer
[:div.action-buttons [:div.action-buttons
@ -124,13 +127,30 @@
(tr "labels.export")) (tr "labels.export"))
:on-click (when-not in-progress? accept-fn)}]]]] :on-click (when-not in-progress? accept-fn)}]]]]
[:div.no-selection [:& no-selection])]]]]))
[:img {:src "images/export-no-shapes.png" :border "0"}]
[:p (tr "dashboard.export-shapes.no-elements")] (mf/defc shapes-no-selection []
[:p (tr "dashboard.export-shapes.how-to")] [:div.no-selection
[:p [:a {:target "_blank" [:img {:src "images/export-no-shapes.png" :border "0"}]
:href "https://help.penpot.app/user-guide/exporting/ "} [:p (tr "dashboard.export-shapes.no-elements")]
(tr "dashboard.export-shapes.how-to-link")]]])]]]])) [:p (tr "dashboard.export-shapes.how-to")]
[:p [:a {:target "_blank"
:href "https://help.penpot.app/user-guide/exporting/ "}
(tr "dashboard.export-shapes.how-to-link")]]])
(mf/defc export-shapes-dialog
{::mf/register modal/components
::mf/register-as :export-shapes}
[{:keys [exports filename]}]
(let [title (tr "dashboard.export-shapes.title")]
(export-multiple-dialog {:exports exports :filename filename :title title :query :export-shapes-multiple :no-selection shapes-no-selection})))
(mf/defc export-frames
{::mf/register modal/components
::mf/register-as :export-frames}
[{:keys [exports filename]}]
(let [title (tr "dashboard.export-frames.title")]
(export-multiple-dialog {:exports exports :filename filename :title title :query :export-frames})))
(mf/defc export-progress-widget (mf/defc export-progress-widget
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}

View file

@ -7,11 +7,9 @@
(ns app.main.ui.workspace.header (ns app.main.ui.workspace.header
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.config :as cf] [app.config :as cf]
[app.main.data.events :as ev] [app.main.data.events :as ev]
[app.main.data.exports :as de] [app.main.data.exports :as de]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.shortcuts :as sc] [app.main.data.workspace.shortcuts :as sc]
@ -105,7 +103,7 @@
;; --- Header Users ;; --- Header Users
(mf/defc menu (mf/defc menu
[{:keys [layout project file team-id page-id] :as props}] [{:keys [layout project file team-id] :as props}]
(let [show-menu? (mf/use-state false) (let [show-menu? (mf/use-state false)
show-sub-menu? (mf/use-state false) show-sub-menu? (mf/use-state false)
editing? (mf/use-state false) editing? (mf/use-state false)
@ -188,21 +186,7 @@
(mf/use-callback (mf/use-callback
(mf/deps file frames) (mf/deps file frames)
(fn [_] (fn [_]
(when (seq frames) (st/emit! (de/show-workspace-export-frames-dialog frames))))
(let [filename (dm/str (:name file) ".pdf")
xform (comp (map :id)
(map (fn [id]
{:file-id (:id file)
:page-id page-id
:frame-id id})))]
(st/emit! (msg/info (tr "workspace.options.exporting-object") {:timeout nil}))
(->> (rp/query! :export-frames (into [] xform frames))
(rx/subs
(fn [body]
(dom/trigger-download filename body))
(fn [_error]
(st/emit! (msg/error (tr "errors.unexpected-error"))))
(st/emitf msg/hide)))))))
on-item-hover on-item-hover
(mf/use-callback (mf/use-callback

View file

@ -304,23 +304,27 @@ msgstr "Include shared library assets in file libraries"
msgid "dashboard.export.title" msgid "dashboard.export.title"
msgstr "Export files" msgstr "Export files"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-frames.title"
msgstr "Export to PDF"
#:src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.title" msgid "dashboard.export-shapes.title"
msgstr "Export selection" msgstr "Export selection"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.selected" msgid "dashboard.export-multiple.selected"
msgstr "%s de %s elements selected" msgstr "%s de %s elements selected"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.no-elements" msgid "dashboard.export-shapes.no-elements"
msgstr "There are no elements with export settings." msgstr "There are no elements with export settings."
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.how-to" msgid "dashboard.export-shapes.how-to"
msgstr "You can add export settings to elements from the design properties (at the bottom of the right sidebar)." msgstr "You can add export settings to elements from the design properties (at the bottom of the right sidebar)."
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.how-to-link" msgid "dashboard.export-shapes.how-to-link"
msgstr "Info how to set exports at Penpot." msgstr "Info how to set exports at Penpot."

View file

@ -308,23 +308,27 @@ msgstr "Incluir librerias compartidas dentro de las librerias del fichero"
msgid "dashboard.export.title" msgid "dashboard.export.title"
msgstr "Exportar ficheros" msgstr "Exportar ficheros"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #:src/app/main/ui/export.cljs
msgid "dashboard.export-frames.title"
msgstr "Exportar a PDF"
#: src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.title" msgid "dashboard.export-shapes.title"
msgstr "Exportar selección" msgstr "Exportar selección"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #: src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.selected" msgid "dashboard.export-multiple.selected"
msgstr "%s de %s elementos seleccionados" msgstr "%s de %s elementos seleccionados"
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #: src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.no-elements" msgid "dashboard.export-shapes.no-elements"
msgstr "No hay elementos con configuraciones de exportación." msgstr "No hay elementos con configuraciones de exportación."
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #: src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.how-to" msgid "dashboard.export-shapes.how-to"
msgstr " Puedes añadir configuraciones de exportación a elementos desde las propiedades de diseño (al final del lateral derecho)." msgstr " Puedes añadir configuraciones de exportación a elementos desde las propiedades de diseño (al final del lateral derecho)."
#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs #: src/app/main/ui/export.cljs
msgid "dashboard.export-shapes.how-to-link" msgid "dashboard.export-shapes.how-to-link"
msgstr "Información sobre cómo configurar exportaciones en Penpot." msgstr "Información sobre cómo configurar exportaciones en Penpot."