diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index 39abab9d4..8ad5ec59a 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -13,6 +13,7 @@ [app.common.math :as mth] [cljs.analyzer.api :as aapi] [clojure.set :as set] + [cuerdas.core :as str] #?(:cljs [cljs.reader :as r] :clj [clojure.edn :as r]) #?(:cljs [cljs.core :as core] @@ -541,12 +542,13 @@ when it's a vector or a map" [mfn m] (let [do-map - (fn [[k v]] - (cond - (or (vector? v) (map? v)) - [k (deep-mapm mfn v)] - :else - (mfn [k v])))] + (fn [entry] + (let [[k v] (mfn entry)] + (cond + (or (vector? v) (map? v)) + [k (deep-mapm mfn v)] + :else + (mfn [k v]))))] (cond (map? m) (into {} (map do-map) m) @@ -560,3 +562,9 @@ (defn not-empty? [coll] (boolean (seq coll))) + +(defn kebab-keys [m] + (->> m + (deep-mapm + (fn [[k v]] + [(keyword (str/kebab (name k))) v])))) diff --git a/common/src/app/common/file_builder.cljc b/common/src/app/common/file_builder.cljc index 056e67804..7f7927578 100644 --- a/common/src/app/common/file_builder.cljc +++ b/common/src/app/common/file_builder.cljc @@ -130,7 +130,7 @@ (let [container-id (or (:current-component-id file) (:current-page-id file)) unames (get-in file [:unames container-id])] - (d/unique-name (or unames #{}) name))) + (d/unique-name name (or unames #{})))) (defn clear-names [file] (dissoc file :unames)) @@ -355,36 +355,32 @@ (defn add-library-color [file color] - (let [id (uuid/next)] - (commit-change - file - {:type :add-color - :id id - :color color}) - - id)) + (let [id (or (:id color) (uuid/next))] + (-> file + (commit-change + {:type :add-color + :id id + :color (assoc color :id id)}) + (assoc :last-id id)))) (defn add-library-typography [file typography] - (let [id (uuid/next)] - (commit-change - file - {:type :add-typography - :id id - :typography typography}) - - id)) + (let [id (or (:id typography) (uuid/next))] + (-> file + (commit-change + {:type :add-typography + :id id + :typography (assoc typography :id id)}) + (assoc :last-id id)))) (defn add-library-media [file media] - (let [id (uuid/next)] - (commit-change - file - {:type :add-media - :id id - :media media}) - - id)) + (let [id (or (:id media) (uuid/next))] + (-> file + (commit-change + {:type :add-media + :object (assoc media :id id)}) + (assoc :last-id id)))) (defn start-component [file data] diff --git a/frontend/src/app/util/zip.cljs b/frontend/src/app/util/zip.cljs index 47f3544f6..a3b6dc3f3 100644 --- a/frontend/src/app/util/zip.cljs +++ b/frontend/src/app/util/zip.cljs @@ -31,24 +31,28 @@ (rx/map :body) (rx/flat-map zip/loadAsync))) -(defn- process-file [entry path] +(defn- process-file + [entry path type] (cond (nil? entry) - (p/rejected "No file found") + (p/rejected (str "File not found: " path)) (.-dir entry) (p/resolved {:dir path}) :else - (-> (.async entry "text") + (-> (.async entry type) (p/then #(hash-map :path path :content %))))) (defn get-file "Gets a single file from the zip archive" - [zip path] - (-> (.file zip path) - (process-file path) - (rx/from))) + ([zip path] + (get-file zip path "text")) + + ([zip path type] + (-> (.file zip path) + (process-file path type) + (rx/from)))) (defn extract-files "Creates a stream that will emit values for every file in the zip" @@ -56,7 +60,7 @@ (let [promises (atom []) get-file (fn [path entry] - (let [current (process-file entry path)] + (let [current (process-file entry path "text")] (swap! promises conj current)))] (.forEach zip get-file) diff --git a/frontend/src/app/worker/export.cljs b/frontend/src/app/worker/export.cljs index 3e9ee110d..6a9c71ba5 100644 --- a/frontend/src/app/worker/export.cljs +++ b/frontend/src/app/worker/export.cljs @@ -41,7 +41,7 @@ :pages pages :pagesIndex index :hasComponents (d/not-empty? (get-in file [:data :components])) - :hasImages (d/not-empty? (get-in file [:data :media])) + :hasMedia (d/not-empty? (get-in file [:data :media])) :hasColors (d/not-empty? (get-in file [:data :colors])) :hasTypographies (d/not-empty? (get-in file [:data :typographies]))}))))] (let [manifest {:teamId (str team-id) @@ -123,13 +123,13 @@ (->> (vals media) (reduce collect-media {}) (json/encode))] - (rx/of (vector (str file-id "/images.json") markup))) + (rx/of (vector (str file-id "/media.json") markup))) (->> (rx/from (vals media)) (rx/map #(assoc % :file-id file-id)) (rx/flat-map (fn [media] - (let [file-path (str file-id "/images/" (:id media) "." (dom/mtype->extension (:mtype media)))] + (let [file-path (str file-id "/media/" (:id media) "." (dom/mtype->extension (:mtype media)))] (->> (http/send! {:uri (cfg/resolve-file-media media) :response-type :blob diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index 9bbf1a4c6..b05a05a50 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -11,12 +11,14 @@ [app.common.pages :as cp] [app.common.uuid :as uuid] [app.main.repo :as rp] + [app.util.dom :as dom] [app.util.http :as http] [app.util.import.parser :as cip] [app.util.json :as json] [app.util.zip :as uz] [app.worker.impl :as impl] [beicon.core :as rx] + [cuerdas.core :as str] [tubax.core :as tubax])) ;; Upload changes batches size @@ -144,7 +146,7 @@ (rx/merge-scan f seed ob)) (rx/last))) -(defn resolve-images +(defn resolve-media [file-id node] (if (and (not (cip/close? node)) (cip/has-image? node)) @@ -172,7 +174,7 @@ (assoc :name page-name))] (->> (rx/from nodes) (rx/filter cip/shape?) - (rx/mapcat (partial resolve-images file-id)) + (rx/mapcat (partial resolve-media file-id)) (rx/reduce add-shape-file (fb/add-page file page-data)) (rx/map post-process-file) (rx/map fb/close-page))) @@ -182,20 +184,98 @@ (str dir-id "/" id ".svg")) (defn process-page [file-id zip [page-id page-name]] - (->> (uz/get-file zip (get-page-path (d/name file-id) page-id)) - (rx/map (comp tubax/xml->clj :content)) - (rx/map #(vector page-name %)))) + (let [path (get-page-path (d/name file-id) page-id)] + (->> (uz/get-file zip path) + (rx/map (comp tubax/xml->clj :content)) + (rx/map #(vector page-name %))))) -(defn process-file +(defn process-file-pages [file file-id file-desc zip] - (let [index (:pagesIndex file-desc) + (let [index (:pages-index file-desc) pages (->> (:pages file-desc) (mapv #(vector % (get-in index [(keyword %) :name]))))] (->> (rx/from pages) (rx/mapcat #(process-page file-id zip %)) - (merge-reduce import-page file) - (rx/flat-map send-changes) - (rx/ignore)))) + (merge-reduce import-page file)))) + +(defn process-library-colors + [file file-id file-desc zip] + (if (:has-colors file-desc) + (let [add-color + (fn [file [id color]] + (let [color (-> (d/kebab-keys color) + (d/update-in-when [:gradient :type] keyword)) + file (fb/add-library-color file color)] + (assoc file [:library-mapping id] (:last-id file)))) + + path (str (d/name file-id) "/colors.json")] + (->> (uz/get-file zip path) + (rx/mapcat (comp json/decode :content)) + (rx/reduce add-color file))) + + (rx/of file))) + +(defn process-library-typographies + [file file-id file-desc zip] + (if (:has-typographies file-desc) + (let [add-typography + (fn [file [id typography]] + (let [typography (d/kebab-keys typography) + file (fb/add-library-typography file typography)] + (assoc file [:library-mapping id] (:last-id file)))) + + path (str (d/name file-id) "/typographies.json")] + (->> (uz/get-file zip path) + (rx/mapcat (comp json/decode :content)) + (rx/reduce add-typography file))) + + (rx/of file))) + +(defn process-library-media + [file file-id file-desc zip] + (rx/of file) + (if (:has-media file-desc) + (let [add-media + (fn [file media] + (let [file (fb/add-library-media file (dissoc media :old-id))] + (assoc file [:library-mapping (:old-id media)] (:last-id file)))) + + path (str (d/name file-id) "/media.json")] + + (->> (uz/get-file zip path) + (rx/mapcat (comp json/decode :content)) + (rx/flat-map + (fn [[id media]] + (let [file-path (str (d/name file-id) "/media/" (d/name id) "." (dom/mtype->extension (:mtype media)))] + (->> (uz/get-file zip file-path "blob") + (rx/map (fn [{blob :content}] + (let [content (.slice blob 0 (.-size blob) (:mtype media))] + {:name (:name media) + :file-id (:id file) + :content content + :is-local false}))) + (rx/flat-map #(rp/mutation! :upload-file-media-object %)) + (rx/map (fn [response] + (-> media + (assoc :old-id id) + (assoc :id (:id response))))))))) + (rx/reduce add-media file))) + + (rx/of file))) + +(defn process-library-components + [file file-id file-desc zip] + (rx/of file)) + +(defn process-file + [file file-id file-desc zip] + (->> (process-file-pages file file-id file-desc zip) + (rx/flat-map #(process-library-colors % file-id file-desc zip)) + (rx/flat-map #(process-library-typographies % file-id file-desc zip)) + (rx/flat-map #(process-library-media % file-id file-desc zip)) + (rx/flat-map #(process-library-components % file-id file-desc zip)) + (rx/flat-map send-changes) + (rx/ignore))) (defn process-package [project-id zip-file] @@ -203,8 +283,9 @@ (rx/flat-map (comp :files json/decode :content)) (rx/flat-map (fn [[file-id file-desc]] - (->> (create-file project-id file-desc) - (rx/flat-map #(process-file % file-id file-desc zip-file))))))) + (let [file-desc (d/kebab-keys file-desc)] + (->> (create-file project-id file-desc) + (rx/flat-map #(process-file % file-id file-desc zip-file)))))))) (defmethod impl/handler :import-file [{:keys [project-id files]}]