Import/Export framework first version

This commit is contained in:
alonso.torres 2021-05-28 12:59:43 +02:00 committed by Andrés Moya
parent d7eec3b92b
commit bf5f845789
12 changed files with 413 additions and 7 deletions

View file

@ -0,0 +1,23 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.libs.file-builder
(:require
[app.common.data :as d]
[app.common.file-builder :as fb]))
(deftype File [^:mutable file]
Object
(addPage [self name]
(set! file (fb/add-page file name))
(str (:current-page-id file))))
(defn create-file-export [^string name]
(File. (fb/create-file name)))
(defn exports []
#js { :createFile create-file-export })

View file

@ -0,0 +1,28 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.libs.render
(:require
[app.common.uuid :as uuid]
[app.main.render :as r]
[beicon.core :as rx]
[promesa.core :as p]))
(defn render-page-export
[file ^string page-id]
;; Better to expose the api as a promise to be consumed from JS
(let [page-id (uuid/uuid page-id)
file-data (.-file file)
data (get-in file-data [:data :pages-index page-id])]
(p/create
(fn [resolve reject]
(->> (r/render-page data)
(rx/take 1)
(rx/subs resolve reject))) )))
(defn exports []
#js {:renderPage render-page-export})

View file

@ -0,0 +1,74 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.render
(:require
["react-dom/server" :as rds]
[app.config :as cfg]
[app.main.exports :as exports]
[app.main.exports :as svg]
[app.main.fonts :as fonts]
[app.util.http :as http]
[beicon.core :as rx]
[clojure.set :as set]
[rumext.alpha :as mf]))
(defn- text? [{type :type}]
(= type :text))
(defn- get-image-data [shape]
(cond
(= :image (:type shape))
[(:metadata shape)]
(some? (:fill-image shape))
[(:fill-image shape)]
:else
[]))
(defn populate-images-cache
([data]
(populate-images-cache data nil))
([data {:keys [resolve-media?] :or {resolve-media? false}}]
(let [images (->> (:objects data)
(vals)
(mapcat get-image-data))]
(->> (rx/from images)
(rx/map #(cfg/resolve-file-media %))
(rx/flat-map http/fetch-data-uri)))))
(defn populate-fonts-cache [data]
(let [texts (->> (:objects data)
(vals)
(filterv text?)
(mapv :content)) ]
(->> (rx/from texts)
(rx/map fonts/get-content-fonts)
(rx/reduce set/union #{})
(rx/flat-map identity)
(rx/flat-map fonts/fetch-font-css)
(rx/flat-map fonts/extract-fontface-urls)
(rx/flat-map http/fetch-data-uri))))
(defn render-page
[data]
(rx/concat
(->> (rx/merge
(populate-images-cache data)
(populate-fonts-cache data))
(rx/ignore))
(->> (rx/of data)
(rx/map
(fn [data]
(let [elem (mf/element exports/page-svg #js {:data data :embed? true})]
(rds/renderToStaticMarkup elem)))))))

View file

@ -0,0 +1,50 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.dashboard.import
(:require
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.icons :as i]
[app.main.worker :as uw]
[app.util.dom :as dom]
[app.util.logging :as log]
[beicon.core :as rx]
[rumext.alpha :as mf]))
(log/set-level! :warn)
(defn use-import-file
[project-id]
(mf/use-callback
(mf/deps project-id)
(fn [files]
(when files
(let [files (->> files (mapv dom/create-uri))]
(->> (uw/ask-many!
{:cmd :import-file
:project-id project-id
:files files})
(rx/subs
(fn [result]
(log/debug :action "import-result" :result result)))))))))
(mf/defc import-button
[{:keys [project-id]}]
(let [file-input (mf/use-ref nil)
on-file-selected (use-import-file project-id)]
[:form.import-file
[:button.import-file-icon {:type "button"
:on-click #(dom/click (mf/ref-val file-input))} i/import]
[:& file-uploader {:accept "application/zip"
:multi true
:input-ref file-input
:on-selected on-file-selected}]]))

View file

@ -5,6 +5,7 @@
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.icons
(:refer-clojure :exclude [import])
(:require-macros [app.main.ui.icons :refer [icon-xref]])
(:require [rumext.alpha :as mf]))
@ -53,6 +54,7 @@
(def icon-set (icon-xref :icon-set))
(def icon-verify (icon-xref :icon-verify))
(def image (icon-xref :image))
(def import (icon-xref :import))
(def infocard (icon-xref :infocard))
(def interaction (icon-xref :interaction))
(def layers (icon-xref :layers))
@ -60,9 +62,9 @@
(def libraries (icon-xref :libraries))
(def library (icon-xref :library))
(def line (icon-xref :line))
(def line-height (icon-xref :line-height))
(def listing-enum (icon-xref :listing-enum))
(def listing-thumbs (icon-xref :listing-thumbs))
(def line-height (icon-xref :line-height))
(def loader (icon-xref :loader))
(def lock (icon-xref :lock))
(def logo (icon-xref :uxbox-logo))

View file

@ -6,6 +6,7 @@
(ns app.worker
(:require
[app.common.exceptions :as ex]
[app.common.spec :as us]
[app.common.transit :as t]
@ -14,6 +15,9 @@
[app.util.worker :as w]
[app.worker.impl :as impl]
[app.worker.selection]
[app.worker.import]
[app.worker.export]
[app.worker.snaps]
[app.worker.thumbnails]
[beicon.core :as rx]
@ -159,4 +163,3 @@
(set! process-message-sub (subscribe-buffer-messages))
(.addEventListener js/self "message" on-message))

View file

@ -0,0 +1,77 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.worker.export
(:require
[app.main.render :as r]
[app.util.dom :as dom]
[app.util.http :as http]
[app.util.zip :as uz]
[app.worker.impl :as impl]
[beicon.core :as rx]))
(defn- handle-response
[response]
(cond
(http/success? response)
(rx/of (:body response))
(http/client-error? response)
(rx/throw (:body response))
:else
(rx/throw {:type :unexpected
:code (:error response)})))
(defn get-page-data
[{file-name :file-name {:keys [id name] :as data} :data}]
(->> (r/render-page data)
(rx/map (fn [markup]
{:id id
:name name
:file-name file-name
:markup markup}))))
(defn query-file [file-id]
(->> (http/send! {:uri "/api/rpc/query/file"
:query {:id file-id}
:method :get})
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response)))
(defn process-pages [file]
(let [pages (get-in file [:data :pages])
pages-index (get-in file [:data :pages-index])]
(->> pages
(map #(hash-map
:file-name (:name file)
:data (get pages-index %))))))
(defn collect-page
[coll {:keys [id file-name name markup] :as page}]
(conj coll [(str file-name "/" name ".svg") markup]))
(defmethod impl/handler :export-file
[{:keys [team-id files] :as message}]
(let [render-stream
(->> (rx/from (->> files (mapv :id)))
(rx/merge-map query-file)
(rx/flat-map process-pages)
(rx/observe-on :async)
(rx/flat-map get-page-data)
(rx/share))]
(rx/merge
(->> render-stream
(rx/map #(hash-map :type :progress
:data (str "Render " (:file-name %) " - " (:name %)))))
(->> render-stream
(rx/reduce collect-page [])
(rx/tap #(prn %))
(rx/flat-map uz/compress-files)
(rx/map #(hash-map :type :finish
:data (dom/create-uri %)))))))

View file

@ -0,0 +1,56 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.worker.import
(:require
[app.common.data :as d]
[app.common.file-builder :as fb]
[app.util.zip :as uz]
[app.worker.impl :as impl]
[beicon.core :as rx]
[cuerdas.core :as str]
[tubax.core :as tubax]))
(defn parse-file-name
[dir]
(if (str/ends-with? dir "/")
(subs dir 0 (dec (count dir)))
dir))
(defn parse-page-name [path]
(let [[file page] (str/split path "/")]
(str/replace page ".svg" "")))
(defn import-page [file {:keys [path data]}]
(let [page-name (parse-page-name path)]
(-> file
(fb/add-page page-name))))
(defmethod impl/handler :import-file
[{:keys [project-id files]}]
(let [extract-stream
(->> (rx/from files)
(rx/merge-map uz/extract-files))
dir-str
(->> extract-stream
(rx/filter #(contains? % :dir))
(rx/map :dir))
file-str
(->> extract-stream
(rx/filter #(not (contains? % :dir)))
(rx/map #(d/update-when % :content tubax/xml->clj)))]
(->> dir-str
(rx/merge-map
(fn [dir]
(->> file-str
(rx/filter #(str/starts-with? (:path %) dir))
(rx/reduce import-page (fb/create-file (parse-file-name dir))))))
(rx/map #(select-keys % [:id :name])))))