mirror of
https://github.com/penpot/penpot.git
synced 2025-03-29 08:01:41 -05:00
✨ Import/Export framework first version
This commit is contained in:
parent
d7eec3b92b
commit
bf5f845789
12 changed files with 413 additions and 7 deletions
70
common/src/app/common/file_builder.cljc
Normal file
70
common/src/app/common/file_builder.cljc
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
;; 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.common.file-builder
|
||||||
|
"A version parsing helper."
|
||||||
|
(:require
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.common.pages.init :as init]
|
||||||
|
[app.common.pages.changes :as ch]
|
||||||
|
))
|
||||||
|
|
||||||
|
(def root-frame uuid/zero)
|
||||||
|
|
||||||
|
(defn create-file
|
||||||
|
([name]
|
||||||
|
(let [id (uuid/next)]
|
||||||
|
{:id id
|
||||||
|
:name name
|
||||||
|
:data (-> init/empty-file-data
|
||||||
|
(assoc :id id))
|
||||||
|
|
||||||
|
;; We keep the changes so we can send them to the backend
|
||||||
|
:changes []})))
|
||||||
|
|
||||||
|
;; TODO: Change to `false`
|
||||||
|
(def verify-on-commit? true)
|
||||||
|
|
||||||
|
(defn commit-change [file change]
|
||||||
|
(-> file
|
||||||
|
(update :changes conj change)
|
||||||
|
(update :data ch/process-changes [change] verify-on-commit?)))
|
||||||
|
|
||||||
|
(defn add-page
|
||||||
|
[file name]
|
||||||
|
|
||||||
|
(let [page-id (uuid/next)]
|
||||||
|
(-> file
|
||||||
|
(commit-change
|
||||||
|
{:type :add-page
|
||||||
|
:id page-id
|
||||||
|
:name name
|
||||||
|
:page (-> init/empty-page-data
|
||||||
|
(assoc :name name))})
|
||||||
|
|
||||||
|
;; Current page being edited
|
||||||
|
(assoc :current-page-id page-id)
|
||||||
|
|
||||||
|
;; Current parent stack we'll be nesting
|
||||||
|
(assoc :parent-stack [root-frame]))))
|
||||||
|
|
||||||
|
(defn add-artboard [file data])
|
||||||
|
|
||||||
|
(defn close-artboard [file])
|
||||||
|
|
||||||
|
(defn add-group [file data])
|
||||||
|
(defn close-group [file data])
|
||||||
|
|
||||||
|
(defn create-rect [file data])
|
||||||
|
(defn create-circle [file data])
|
||||||
|
(defn create-path [file data])
|
||||||
|
(defn create-text [file data])
|
||||||
|
(defn create-image [file data])
|
||||||
|
|
||||||
|
(defn close-page [file])
|
||||||
|
|
||||||
|
(defn generate-changes [file])
|
|
@ -126,10 +126,11 @@
|
||||||
:height (:height selection-rect)})
|
:height (:height selection-rect)})
|
||||||
|
|
||||||
(defn make-file-data
|
(defn make-file-data
|
||||||
([file-id] (make-file-data file-id(uuid/next)))
|
([file-id]
|
||||||
|
(make-file-data file-id (uuid/next)))
|
||||||
|
|
||||||
([file-id page-id]
|
([file-id page-id]
|
||||||
(let [
|
(let [pd (assoc empty-page-data
|
||||||
pd (assoc empty-page-data
|
|
||||||
:id page-id
|
:id page-id
|
||||||
:name "Page-1")]
|
:name "Page-1")]
|
||||||
(-> empty-file-data
|
(-> empty-file-data
|
||||||
|
|
|
@ -16,7 +16,8 @@ goog.scope(function() {
|
||||||
const self = app.common.uuid_impl;
|
const self = app.common.uuid_impl;
|
||||||
|
|
||||||
const fill = (() => {
|
const fill = (() => {
|
||||||
if (typeof global.crypto !== "undefined") {
|
if (typeof global.crypto !== "undefined" &&
|
||||||
|
typeof global.crypto.getRandomValues !== "undefined") {
|
||||||
return (buf) => {
|
return (buf) => {
|
||||||
global.crypto.getRandomValues(buf);
|
global.crypto.getRandomValues(buf);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
:jvm-opts ["-Xmx700m" "-Xms100m" "-XX:+UseSerialGC" "-XX:-OmitStackTraceInFastThrow"]
|
:jvm-opts ["-Xmx700m" "-Xms100m" "-XX:+UseSerialGC" "-XX:-OmitStackTraceInFastThrow"]
|
||||||
:dev-http {8888 "classpath:public"}
|
:dev-http {8888 "classpath:public"}
|
||||||
|
|
||||||
|
|
||||||
:builds
|
:builds
|
||||||
{:main
|
{:main
|
||||||
{:target :browser
|
{:target :browser
|
||||||
|
@ -35,6 +36,27 @@
|
||||||
:anon-fn-naming-policy :off
|
:anon-fn-naming-policy :off
|
||||||
:source-map-detail-level :all}}}
|
:source-map-detail-level :all}}}
|
||||||
|
|
||||||
|
:lib-penpot
|
||||||
|
{:target :esm
|
||||||
|
:output-dir "resources/public/libs"
|
||||||
|
|
||||||
|
:modules
|
||||||
|
{:penpot {:exports {:renderPage app.libs.render/render-page-export
|
||||||
|
:createFile app.libs.file-builder/create-file-export}}}
|
||||||
|
|
||||||
|
:compiler-options
|
||||||
|
{:output-feature-set :es8
|
||||||
|
:output-wrapper false
|
||||||
|
:warnings {:fn-deprecated false}}
|
||||||
|
|
||||||
|
:release
|
||||||
|
{:compiler-options
|
||||||
|
{:fn-invoke-direct true
|
||||||
|
:source-map true
|
||||||
|
:elide-asserts true
|
||||||
|
:anon-fn-naming-policy :off
|
||||||
|
:source-map-detail-level :all}}}
|
||||||
|
|
||||||
:test
|
:test
|
||||||
{:target :node-test
|
{:target :node-test
|
||||||
:output-to "target/tests.js"
|
:output-to "target/tests.js"
|
||||||
|
@ -45,4 +67,3 @@
|
||||||
{:output-feature-set :es8
|
{:output-feature-set :es8
|
||||||
:output-wrapper false
|
:output-wrapper false
|
||||||
:warnings {:fn-deprecated false}}}}}
|
:warnings {:fn-deprecated false}}}}}
|
||||||
|
|
||||||
|
|
23
frontend/src/app/libs/file_builder.cljs
Normal file
23
frontend/src/app/libs/file_builder.cljs
Normal 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 })
|
28
frontend/src/app/libs/render.cljs
Normal file
28
frontend/src/app/libs/render.cljs
Normal 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})
|
74
frontend/src/app/main/render.cljs
Normal file
74
frontend/src/app/main/render.cljs
Normal 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)))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
50
frontend/src/app/main/ui/dashboard/import.cljs
Normal file
50
frontend/src/app/main/ui/dashboard/import.cljs
Normal 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}]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
;; Copyright (c) UXBOX Labs SL
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.icons
|
(ns app.main.ui.icons
|
||||||
|
(:refer-clojure :exclude [import])
|
||||||
(:require-macros [app.main.ui.icons :refer [icon-xref]])
|
(:require-macros [app.main.ui.icons :refer [icon-xref]])
|
||||||
(:require [rumext.alpha :as mf]))
|
(:require [rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
(def icon-set (icon-xref :icon-set))
|
(def icon-set (icon-xref :icon-set))
|
||||||
(def icon-verify (icon-xref :icon-verify))
|
(def icon-verify (icon-xref :icon-verify))
|
||||||
(def image (icon-xref :image))
|
(def image (icon-xref :image))
|
||||||
|
(def import (icon-xref :import))
|
||||||
(def infocard (icon-xref :infocard))
|
(def infocard (icon-xref :infocard))
|
||||||
(def interaction (icon-xref :interaction))
|
(def interaction (icon-xref :interaction))
|
||||||
(def layers (icon-xref :layers))
|
(def layers (icon-xref :layers))
|
||||||
|
@ -60,9 +62,9 @@
|
||||||
(def libraries (icon-xref :libraries))
|
(def libraries (icon-xref :libraries))
|
||||||
(def library (icon-xref :library))
|
(def library (icon-xref :library))
|
||||||
(def line (icon-xref :line))
|
(def line (icon-xref :line))
|
||||||
|
(def line-height (icon-xref :line-height))
|
||||||
(def listing-enum (icon-xref :listing-enum))
|
(def listing-enum (icon-xref :listing-enum))
|
||||||
(def listing-thumbs (icon-xref :listing-thumbs))
|
(def listing-thumbs (icon-xref :listing-thumbs))
|
||||||
(def line-height (icon-xref :line-height))
|
|
||||||
(def loader (icon-xref :loader))
|
(def loader (icon-xref :loader))
|
||||||
(def lock (icon-xref :lock))
|
(def lock (icon-xref :lock))
|
||||||
(def logo (icon-xref :uxbox-logo))
|
(def logo (icon-xref :uxbox-logo))
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.worker
|
(ns app.worker
|
||||||
(:require
|
(:require
|
||||||
|
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
|
@ -14,6 +15,9 @@
|
||||||
[app.util.worker :as w]
|
[app.util.worker :as w]
|
||||||
[app.worker.impl :as impl]
|
[app.worker.impl :as impl]
|
||||||
[app.worker.selection]
|
[app.worker.selection]
|
||||||
|
|
||||||
|
[app.worker.import]
|
||||||
|
[app.worker.export]
|
||||||
[app.worker.snaps]
|
[app.worker.snaps]
|
||||||
[app.worker.thumbnails]
|
[app.worker.thumbnails]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
|
@ -159,4 +163,3 @@
|
||||||
(set! process-message-sub (subscribe-buffer-messages))
|
(set! process-message-sub (subscribe-buffer-messages))
|
||||||
(.addEventListener js/self "message" on-message))
|
(.addEventListener js/self "message" on-message))
|
||||||
|
|
||||||
|
|
||||||
|
|
77
frontend/src/app/worker/export.cljs
Normal file
77
frontend/src/app/worker/export.cljs
Normal 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 %)))))))
|
56
frontend/src/app/worker/import.cljs
Normal file
56
frontend/src/app/worker/import.cljs
Normal 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])))))
|
Loading…
Add table
Reference in a new issue