diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 1dead8bf4..8da556dbc 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -161,15 +161,15 @@ (->> (uw/ask-many! {:cmd :export-file :team-id current-team-id - :files files}) + :files (->> files (mapv :id))}) (rx/subs - (fn [{:keys [type data] :as msg}] - (case type + (fn [msg] + (case (:type msg) :progress - (prn "[Progress]" data) + (prn "[Progress]" (:data msg)) :finish - (dom/save-as data "export" "application/zip" "Export package (*.zip)"))))))] + (dom/trigger-download-uri (:filename msg) (:mtype msg) (:uri msg)))))))] (mf/use-effect (fn [] diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index dcec18c73..c540a744a 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -45,7 +45,7 @@ (let [on-file-selected (use-import-file project-id on-finish-import)] [:form.import-file - [:& file-uploader {:accept "application/zip" + [:& file-uploader {:accept ".penpot" :multi true :ref external-ref :on-selected on-file-selected}]])) diff --git a/frontend/src/app/main/ui/shapes/svg_raw.cljs b/frontend/src/app/main/ui/shapes/svg_raw.cljs index 7038c9128..01c984104 100644 --- a/frontend/src/app/main/ui/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/shapes/svg_raw.cljs @@ -27,6 +27,8 @@ (defn set-styles [attrs shape] (let [custom-attrs (-> (usa/extract-style-attrs shape) (obj/without ["transform"])) + + attrs (or attrs {}) attrs (cond-> attrs (string? (:style attrs)) usvg/clean-attrs) style (obj/merge! (clj->js (:style attrs {})) diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 13de17231..66a5f53f2 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -284,14 +284,15 @@ (defn mtype->extension [mtype] ;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types (case mtype - "image/apng" "apng" - "image/avif" "avif" - "image/gif" "gif" - "image/jpeg" "jpg" - "image/png" "png" - "image/svg+xml" "svg" - "image/webp" "webp" - "application/zip" "zip" + "image/apng" "apng" + "image/avif" "avif" + "image/gif" "gif" + "image/jpeg" "jpg" + "image/png" "png" + "image/svg+xml" "svg" + "image/webp" "webp" + "application/zip" "zip" + "application/penpot" "penpot" nil)) (defn set-attribute [^js node ^string attr value] @@ -346,11 +347,15 @@ :types [{:description description :accept { mtype [(str "." extension)]}}]}] - (p/let [file-system (.showSaveFilePicker globals/window (clj->js opts)) - writable (.createWritable file-system) - response (js/fetch uri) - blob (.blob response) - _ (.write writable blob)] - (.close writable))) + (-> (p/let [file-system (.showSaveFilePicker globals/window (clj->js opts)) + writable (.createWritable file-system) + response (js/fetch uri) + blob (.blob response) + _ (.write writable blob)] + (.close writable)) + (p/catch + #(when-not (and (= (type %) js/DOMException) + (= (.-name %) "AbortError")) + (trigger-download-uri filename mtype uri))))) (trigger-download-uri filename mtype uri))) diff --git a/frontend/src/app/util/import/parser.cljc b/frontend/src/app/util/import/parser.cljc index ab5fc82db..83ace5ca2 100644 --- a/frontend/src/app/util/import/parser.cljc +++ b/frontend/src/app/util/import/parser.cljc @@ -592,8 +592,9 @@ (assoc :grids grids)))) (defn has-image? - [type node] - (let [pattern-image + [node] + (let [type (get-type node) + pattern-image (-> node (find-node :defs) (find-node :pattern) diff --git a/frontend/src/app/worker/export.cljs b/frontend/src/app/worker/export.cljs index 26ae4a89f..5c2d485a0 100644 --- a/frontend/src/app/worker/export.cljs +++ b/frontend/src/app/worker/export.cljs @@ -57,14 +57,12 @@ [{:keys [id file-id markup] :as page}] [(str file-id "/" id ".svg") markup]) -(defmethod impl/handler :export-file - [{:keys [team-id project-id files] :as message}] - (let [files-ids (->> files (mapv :id)) +(defn export-file + [team-id file-id] - files-stream - (->> (rx/from files-ids) - (rx/merge-map #(rp/query :file {:id %})) + (let [files-stream + (->> (rp/query :file {:id file-id}) (rx/reduce #(assoc %1 (:id %2) %2) {}) (rx/share)) @@ -87,11 +85,30 @@ (rx/merge (->> render-stream - (rx/map #(hash-map :type :progress - :data (str "Render " (:file-name %) " - " (:name %))))) - (->> (rx/merge pages-stream - manifest-stream) + (rx/map #(hash-map + :type :progress + :file file-id + :data (str "Render " (:file-name %) " - " (:name %))))) + + (->> (rx/merge manifest-stream pages-stream) (rx/reduce conj []) - (rx/flat-map uz/compress-files) - (rx/map #(hash-map :type :finish - :data (dom/create-uri %))))))) + (rx/with-latest-from files-stream) + (rx/flat-map (fn [[data files]] + (->> (uz/compress-files data) + (rx/map #(vector (get files file-id) %))))))))) + +(defmethod impl/handler :export-file + [{:keys [team-id project-id files] :as message}] + + (->> (rx/from files) + (rx/mapcat #(export-file team-id %)) + (rx/map + (fn [value] + (if (contains? value :type) + value + (let [[file export-blob] value] + {:type :finish + :filename (:name file) + :mtype "application/penpot" + :description "Penpot export (*.penpot)" + :uri (dom/create-uri export-blob)})))))) diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index 48fe14a15..f9ee680fa 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -118,7 +118,7 @@ (defn resolve-images [file-id node] (if (and (not (cip/close? node)) - (cip/has-image? type node)) + (cip/has-image? node)) (let [name (cip/get-image-name node) data-uri (cip/get-image-data node)] (->> (upload-media-files file-id name data-uri) @@ -162,25 +162,26 @@ pages (->> (:pages file-desc) (mapv #(vector % (get-in index [(keyword %) :name]))))] (->> (rx/from pages) - (rx/flat-map #(process-page file-id zip %)) + (rx/mapcat #(process-page file-id zip %)) (merge-reduce import-page file) (rx/flat-map send-changes) (rx/ignore)))) +(defn process-package + [project-id zip-file] + (->> (uz/get-file zip-file "manifest.json") + (rx/flat-map (comp :files json/decode :content)) + (rx/flat-map + (fn [[file-id file-desc]] + (->> (create-file project-id (:name file-desc)) + (rx/flat-map #(process-file % file-id file-desc zip-file))))))) + (defmethod impl/handler :import-file [{:keys [project-id files]}] - (let [zip-str (->> (rx/from files) - (rx/flat-map uz/load-from-url) - (rx/share))] - - (->> zip-str - (rx/flat-map #(uz/get-file % "manifest.json")) - (rx/flat-map (comp :files json/decode :content)) - (rx/with-latest-from zip-str) - (rx/flat-map - (fn [[[file-id file-desc] zip]] - (->> (create-file project-id (:name file-desc)) - (rx/flat-map #(process-file % file-id file-desc zip)) - (rx/catch (fn [err] - (.error js/console "ERROR" err (clj->js (.-data err))))))))))) + (->> (rx/from files) + (rx/flat-map uz/load-from-url) + (rx/flat-map (partial process-package project-id)) + (rx/catch + (fn [err] + (.error js/console "ERROR" err (clj->js (.-data err)))))))