mirror of
https://github.com/penpot/penpot.git
synced 2025-05-14 16:06:38 +02:00
✨ Import/export UI and final touches
This commit is contained in:
parent
1b1c0ff9e4
commit
d0ab813520
12 changed files with 856 additions and 117 deletions
|
@ -202,12 +202,338 @@
|
||||||
background: $color-primary;
|
background: $color-primary;
|
||||||
border: 1px solid $color-primary;
|
border: 1px solid $color-primary;
|
||||||
color: $color-black;
|
color: $color-black;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $color-primary-dark;
|
background: $color-primary-dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog,
|
||||||
|
.export-dialog {
|
||||||
|
background-color: $color-white;
|
||||||
|
border: 1px solid $color-gray-20;
|
||||||
|
width: 30rem;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: $fs14;
|
||||||
|
color: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
font-size: $fs12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail, .explain {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
border: 1px solid $color-gray-20;
|
||||||
|
background: $color-white;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0.3rem 1.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $color-gray-20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-button {
|
||||||
|
background: $color-primary;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid $color-primary;
|
||||||
|
color: $color-black;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.3rem 1.25rem;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
border: 1px solid #E3E3E3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $color-primary-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(65vh);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header-title {
|
||||||
|
padding-left: 2rem;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: $fs14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-dialog {
|
||||||
|
min-height: 215px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
max-width: 18px;
|
||||||
|
max-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-entry {
|
||||||
|
margin: 0.75rem 1rem;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&.editable:hover {
|
||||||
|
.file-name-label {
|
||||||
|
background-color: $color-primary-lighter;
|
||||||
|
}
|
||||||
|
.edit-entry-buttons {
|
||||||
|
display: flex;
|
||||||
|
background-color: $color-primary-lighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
width: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader-pencil {
|
||||||
|
fill: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tick {
|
||||||
|
fill: $color-success;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-close {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
fill: $color-danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: $color-black;
|
||||||
|
|
||||||
|
.file-name-label {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 2rem;
|
||||||
|
margin-left: -0.25rem;
|
||||||
|
padding-left: 0.25rem;
|
||||||
|
|
||||||
|
.icon-library {
|
||||||
|
width: 14px;
|
||||||
|
fill: $color-gray-20;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
padding-top: 1px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name-edit {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid $color-gray-20;
|
||||||
|
height: 2rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-banner {
|
||||||
|
color: $color-black;
|
||||||
|
background: $color-success-lighter;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 1rem;
|
||||||
|
|
||||||
|
.message {
|
||||||
|
padding: 0 1rem;
|
||||||
|
font-size: $fs12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
background: $color-success;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
margin: 0 2rem;
|
||||||
|
color: $color-danger;
|
||||||
|
font-size: $fs12;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linked-libraries {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-left: 2rem;
|
||||||
|
|
||||||
|
.icon-chain, .icon-unchain {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linked-library-tag {
|
||||||
|
font-size: $fs10;
|
||||||
|
color: $color-black;
|
||||||
|
background: #d8f7fe;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0.25rem;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background-color: $color-danger-lighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-entry-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
font-size: $fs14;
|
||||||
|
height: 2rem;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-dialog {
|
||||||
|
min-height: 24rem;
|
||||||
|
|
||||||
|
.export-option {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid $color-gray-10;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, p {
|
||||||
|
font-size: $fs12;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
color: $color-black;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border: 1px solid $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-container {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 40px;
|
||||||
|
padding-right: 1rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
// margin-bottom: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-radio-check {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
left: 12px;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
background-color: $color-white;
|
||||||
|
border: 1px solid $color-gray-10;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover input ~ .option-radio-check {
|
||||||
|
border-color: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked ~ .option-radio-check {
|
||||||
|
border-color: $color-primary;
|
||||||
|
background-color: $color-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-radio-check:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked ~ .option-radio-check:after {
|
||||||
|
display: block;
|
||||||
|
background-color: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-radio-check:after {
|
||||||
|
top: 3px;
|
||||||
|
left: 3px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraries-dialog {
|
.libraries-dialog {
|
||||||
|
@ -564,4 +890,3 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,10 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
|
[app.main.ui.dashboard.export]
|
||||||
[app.main.ui.dashboard.files :refer [files-section]]
|
[app.main.ui.dashboard.files :refer [files-section]]
|
||||||
[app.main.ui.dashboard.fonts :refer [fonts-page font-providers-page]]
|
[app.main.ui.dashboard.fonts :refer [fonts-page font-providers-page]]
|
||||||
|
[app.main.ui.dashboard.import]
|
||||||
[app.main.ui.dashboard.libraries :refer [libraries-page]]
|
[app.main.ui.dashboard.libraries :refer [libraries-page]]
|
||||||
[app.main.ui.dashboard.projects :refer [projects-section]]
|
[app.main.ui.dashboard.projects :refer [projects-section]]
|
||||||
[app.main.ui.dashboard.search :refer [search-page]]
|
[app.main.ui.dashboard.search :refer [search-page]]
|
||||||
|
@ -131,4 +133,3 @@
|
||||||
:section section
|
:section section
|
||||||
:search-term search-term
|
:search-term search-term
|
||||||
:team team}])])]]))
|
:team team}])])]]))
|
||||||
|
|
||||||
|
|
92
frontend/src/app/main/ui/dashboard/export.cljs
Normal file
92
frontend/src/app/main/ui/dashboard/export.cljs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
;; 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.export
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.main.worker :as uw]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(def ^:const options [:all :merge :detach])
|
||||||
|
|
||||||
|
(mf/defc export-dialog
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :export}
|
||||||
|
[{:keys [team-id files]}]
|
||||||
|
(let [selected-option (mf/use-state :all)
|
||||||
|
|
||||||
|
cancel-fn
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (modal/hide))))
|
||||||
|
|
||||||
|
accept-fn
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps @selected-option)
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
|
||||||
|
(->> (uw/ask-many!
|
||||||
|
{:cmd :export-file
|
||||||
|
:team-id team-id
|
||||||
|
:export-type @selected-option
|
||||||
|
:files files})
|
||||||
|
(rx/subs
|
||||||
|
(fn [msg]
|
||||||
|
(when (= :finish (:type msg))
|
||||||
|
(dom/trigger-download-uri (:filename msg) (:mtype msg) (:uri msg))))))
|
||||||
|
|
||||||
|
(st/emit! (modal/hide))))
|
||||||
|
|
||||||
|
on-change-handler
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [_ type]
|
||||||
|
(reset! selected-option type)))]
|
||||||
|
|
||||||
|
[:div.modal-overlay
|
||||||
|
[:div.modal-container.export-dialog
|
||||||
|
[:div.modal-header
|
||||||
|
[:div.modal-header-title
|
||||||
|
[:h2 (tr "dashboard.export.title")]]
|
||||||
|
|
||||||
|
[:div.modal-close-button
|
||||||
|
{:on-click cancel-fn} i/close]]
|
||||||
|
|
||||||
|
[:div.modal-content
|
||||||
|
[:p.explain (tr "dashboard.export.explain")]
|
||||||
|
[:p.detail (tr "dashboard.export.detail")]
|
||||||
|
|
||||||
|
(for [type [:all :merge :detach]]
|
||||||
|
(let [selected? (= @selected-option type)]
|
||||||
|
[:div.export-option {:class (when selected? "selected")}
|
||||||
|
[:label.option-container
|
||||||
|
[:h3 (tr (str "dashboard.export.options." (d/name type) ".title"))]
|
||||||
|
[:p (tr (str "dashboard.export.options." (d/name type) ".message"))]
|
||||||
|
[:input {:type "radio"
|
||||||
|
:checked selected?
|
||||||
|
:on-change #(on-change-handler % type)
|
||||||
|
:name "export-option"}]
|
||||||
|
[:span {:class "option-radio-check"}]]]))]
|
||||||
|
|
||||||
|
[:div.modal-footer
|
||||||
|
[:div.action-buttons
|
||||||
|
[:input.cancel-button
|
||||||
|
{:type "button"
|
||||||
|
:value (tr "labels.cancel")
|
||||||
|
:on-click cancel-fn}]
|
||||||
|
|
||||||
|
[:input.accept-button
|
||||||
|
{:class "primary"
|
||||||
|
:type "button"
|
||||||
|
:value (tr "labels.export")
|
||||||
|
:on-click accept-fn}]]]]]))
|
|
@ -13,8 +13,6 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.context-menu :refer [context-menu]]
|
[app.main.ui.components.context-menu :refer [context-menu]]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.worker :as uw]
|
|
||||||
[app.util.debug :as d]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
|
@ -158,18 +156,11 @@
|
||||||
|
|
||||||
on-export-files
|
on-export-files
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(->> (uw/ask-many!
|
(st/emit!
|
||||||
{:cmd :export-file
|
(modal/show
|
||||||
:team-id current-team-id
|
{:type :export
|
||||||
:files (->> files (mapv :id))})
|
:team-id current-team-id
|
||||||
(rx/subs
|
:files (->> files (mapv :id))})))]
|
||||||
(fn [msg]
|
|
||||||
(case (:type msg)
|
|
||||||
:progress
|
|
||||||
(prn "[Progress]" (:data msg))
|
|
||||||
|
|
||||||
:finish
|
|
||||||
(dom/trigger-download-uri (:filename msg) (:mtype msg) (:uri msg)))))))]
|
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -195,8 +186,7 @@
|
||||||
[[(tr "dashboard.duplicate-multi" file-count) on-duplicate]
|
[[(tr "dashboard.duplicate-multi" file-count) on-duplicate]
|
||||||
(when (or (seq current-projects) (seq other-teams))
|
(when (or (seq current-projects) (seq other-teams))
|
||||||
[(tr "dashboard.move-to-multi" file-count) nil sub-options])
|
[(tr "dashboard.move-to-multi" file-count) nil sub-options])
|
||||||
(when (d/debug? :export)
|
[(tr "dashboard.export-multi" file-count) on-export-files]
|
||||||
[(tr "dashboard.export-multi" file-count) on-export-files])
|
|
||||||
[:separator]
|
[:separator]
|
||||||
[(tr "labels.delete-multi-files" file-count) on-delete]]
|
[(tr "labels.delete-multi-files" file-count) on-delete]]
|
||||||
|
|
||||||
|
@ -208,8 +198,7 @@
|
||||||
(if (:is-shared file)
|
(if (:is-shared file)
|
||||||
[(tr "dashboard.remove-shared") on-del-shared]
|
[(tr "dashboard.remove-shared") on-del-shared]
|
||||||
[(tr "dashboard.add-shared") on-add-shared])
|
[(tr "dashboard.add-shared") on-add-shared])
|
||||||
(when (d/debug? :export)
|
[(tr "dashboard.export-single") on-export-files]
|
||||||
[(tr "dashboard.export-single") on-export-files])
|
|
||||||
[:separator]
|
[:separator]
|
||||||
[(tr "labels.delete") on-delete]])]
|
[(tr "labels.delete") on-delete]])]
|
||||||
|
|
||||||
|
|
|
@ -6,37 +6,41 @@
|
||||||
|
|
||||||
(ns app.main.ui.dashboard.import
|
(ns app.main.ui.dashboard.import
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
[app.main.worker :as uw]
|
[app.main.worker :as uw]
|
||||||
|
[app.util.data :refer [classnames]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.logging :as log]
|
[app.util.logging :as log]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(log/set-level! :debug)
|
(log/set-level! :debug)
|
||||||
|
|
||||||
|
(defn rx-delay-emit [ms ob]
|
||||||
|
(->> ob (rx/mapcat #(rx/delay ms (rx/of %)))))
|
||||||
|
|
||||||
(defn use-import-file
|
(defn use-import-file
|
||||||
[project-id on-finish-import]
|
[project-id on-finish-import]
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps project-id on-finish-import)
|
(mf/deps project-id on-finish-import)
|
||||||
(fn [files]
|
(fn [files]
|
||||||
(when files
|
(when files
|
||||||
(let [files (->> files (mapv dom/create-uri))]
|
(let [files (->> files
|
||||||
(->> (uw/ask-many!
|
(mapv
|
||||||
{:cmd :import-file
|
(fn [file]
|
||||||
:project-id project-id
|
{:name (.-name file)
|
||||||
:files files})
|
:uri (dom/create-uri file)})))]
|
||||||
|
(st/emit! (modal/show
|
||||||
(rx/subs
|
{:type :import
|
||||||
(fn [result]
|
:project-id project-id
|
||||||
(log/debug :action "import-result" :result result))
|
:files files
|
||||||
|
:on-finish-import on-finish-import})))))))
|
||||||
(fn [err]
|
|
||||||
(log/debug :action "import-error" :result err))
|
|
||||||
|
|
||||||
(fn []
|
|
||||||
(log/debug :action "import-end")
|
|
||||||
(when on-finish-import (on-finish-import))))))))))
|
|
||||||
|
|
||||||
(mf/defc import-form
|
(mf/defc import-form
|
||||||
{::mf/forward-ref true}
|
{::mf/forward-ref true}
|
||||||
|
@ -49,6 +53,264 @@
|
||||||
:ref external-ref
|
:ref external-ref
|
||||||
:on-selected on-file-selected}]]))
|
:on-selected on-file-selected}]]))
|
||||||
|
|
||||||
|
(defn update-file [files file-id new-name]
|
||||||
|
(->> files
|
||||||
|
(mapv
|
||||||
|
(fn [file]
|
||||||
|
(cond-> file
|
||||||
|
(= (:file-id file) file-id)
|
||||||
|
(assoc :name new-name))))))
|
||||||
|
|
||||||
|
(defn remove-file [files file-id]
|
||||||
|
(->> files
|
||||||
|
(mapv
|
||||||
|
(fn [file]
|
||||||
|
(cond-> file
|
||||||
|
(= (:file-id file) file-id)
|
||||||
|
(assoc :deleted? true))))))
|
||||||
|
|
||||||
|
(defn set-analyze-error
|
||||||
|
[files uri]
|
||||||
|
(->> files
|
||||||
|
(mapv (fn [file]
|
||||||
|
(cond-> file
|
||||||
|
(= uri (:uri file))
|
||||||
|
(assoc :status :analyze-error))))))
|
||||||
|
|
||||||
|
(defn set-analyze-result [files uri data]
|
||||||
|
(let [exiting-files? (into #{} (->> files (map :file-id) (filter some?)))
|
||||||
|
replace-file
|
||||||
|
(fn [file]
|
||||||
|
(if (and (= uri (:uri file) )
|
||||||
|
(= (:status file) :analyzing))
|
||||||
|
(->> (:files data)
|
||||||
|
(remove (comp exiting-files? first) )
|
||||||
|
(mapv (fn [[file-id file-data]]
|
||||||
|
(-> file-data
|
||||||
|
(assoc :file-id file-id
|
||||||
|
:status :ready
|
||||||
|
:uri uri)))))
|
||||||
|
[file]))]
|
||||||
|
(into [] (mapcat replace-file) files)))
|
||||||
|
|
||||||
|
(defn mark-files-importing [files]
|
||||||
|
(->> files
|
||||||
|
(filter #(= :ready (:status %)))
|
||||||
|
(mapv #(assoc % :status :importing))))
|
||||||
|
|
||||||
|
(defn update-status [files file-id status]
|
||||||
|
(->> files
|
||||||
|
(mapv (fn [file]
|
||||||
|
(cond-> file
|
||||||
|
(= file-id (:file-id file))
|
||||||
|
(assoc :status status))))))
|
||||||
|
|
||||||
|
(mf/defc import-entry
|
||||||
|
[{:keys [state file editing?]}]
|
||||||
|
|
||||||
|
(let [loading? (or (= :analyzing (:status file))
|
||||||
|
(= :importing (:status file)))
|
||||||
|
load-success? (= :import-success (:status file))
|
||||||
|
analyze-error? (= :analyze-error (:status file))
|
||||||
|
import-error? (= :import-error (:status file))
|
||||||
|
ready? (= :ready (:status file))
|
||||||
|
is-shared? (:shared file)
|
||||||
|
|
||||||
|
handle-edit-key-press
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [e]
|
||||||
|
(when (or (kbd/enter? e) (kbd/esc? e))
|
||||||
|
(dom/prevent-default e)
|
||||||
|
(dom/stop-propagation e)
|
||||||
|
(dom/blur! (dom/get-target e)))))
|
||||||
|
|
||||||
|
handle-edit-blur
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps file)
|
||||||
|
(fn [e]
|
||||||
|
(let [value (dom/get-target-val e)]
|
||||||
|
(swap! state #(-> (assoc % :editing nil)
|
||||||
|
(update :files update-file (:file-id file) value))))))
|
||||||
|
|
||||||
|
handle-edit-entry
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps file)
|
||||||
|
(fn []
|
||||||
|
(swap! state assoc :editing (:file-id file))))
|
||||||
|
|
||||||
|
handle-remove-entry
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps file)
|
||||||
|
(fn []
|
||||||
|
(swap! state update :files remove-file (:file-id file))))]
|
||||||
|
|
||||||
|
[:div.file-entry
|
||||||
|
{:class (classnames :loading loading?
|
||||||
|
:success load-success?
|
||||||
|
:error (or import-error? analyze-error?)
|
||||||
|
:editable (and ready? (not editing?)))}
|
||||||
|
|
||||||
|
[:div.file-name
|
||||||
|
[:div.file-icon
|
||||||
|
(cond loading? i/loader-pencil
|
||||||
|
ready? i/logo-icon
|
||||||
|
load-success? i/tick
|
||||||
|
import-error? i/close
|
||||||
|
analyze-error? i/close)]
|
||||||
|
|
||||||
|
(if editing?
|
||||||
|
[:div.file-name-edit
|
||||||
|
[:input {:type "text"
|
||||||
|
:auto-focus true
|
||||||
|
:default-value (:name file)
|
||||||
|
:on-key-press handle-edit-key-press
|
||||||
|
:on-blur handle-edit-blur}]]
|
||||||
|
|
||||||
|
[:div.file-name-label (:name file) (when is-shared? i/library)])
|
||||||
|
|
||||||
|
[:div.edit-entry-buttons
|
||||||
|
[:button {:on-click handle-edit-entry} i/pencil]
|
||||||
|
[:button {:on-click handle-remove-entry} i/trash]]]
|
||||||
|
|
||||||
|
(when analyze-error?
|
||||||
|
[:div.error-message
|
||||||
|
(tr "dashboard.import.analyze-error")])
|
||||||
|
|
||||||
|
(when import-error?
|
||||||
|
[:div.error-message
|
||||||
|
(tr "dashboard.import.import-error")])
|
||||||
|
|
||||||
|
[:div.linked-libraries
|
||||||
|
(for [library-id (:libraries file)]
|
||||||
|
(let [library-data (->> @state :files (d/seek #(= library-id (:file-id %))))
|
||||||
|
error? (or (:deleted? library-data) (:import-error library-data))]
|
||||||
|
(when (some? library-data)
|
||||||
|
[:div.linked-library-tag {:class (when error? "error")}
|
||||||
|
(if error? i/unchain i/chain) (:name library-data)])))]]))
|
||||||
|
|
||||||
|
(mf/defc import-dialog
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :import}
|
||||||
|
[{:keys [project-id files on-finish-import]}]
|
||||||
|
(let [state (mf/use-state
|
||||||
|
{:status :analyzing
|
||||||
|
:editing nil
|
||||||
|
:files (->> files
|
||||||
|
(mapv #(assoc % :status :analyzing)))})
|
||||||
|
|
||||||
|
analyze-import
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [files]
|
||||||
|
(->> (uw/ask-many!
|
||||||
|
{:cmd :analyze-import
|
||||||
|
:files (->> files (mapv :uri))})
|
||||||
|
(rx-delay-emit 1000)
|
||||||
|
(rx/subs
|
||||||
|
(fn [{:keys [uri data error] :as msg}]
|
||||||
|
(log/debug :msg msg)
|
||||||
|
(if (some? error)
|
||||||
|
(swap! state update :files set-analyze-error uri)
|
||||||
|
(swap! state update :files set-analyze-result uri data)))))))
|
||||||
|
|
||||||
|
import-files
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [project-id files]
|
||||||
|
(->> (uw/ask-many!
|
||||||
|
{:cmd :import-files
|
||||||
|
:project-id project-id
|
||||||
|
:files files})
|
||||||
|
(rx-delay-emit 1000)
|
||||||
|
(rx/subs
|
||||||
|
(fn [{:keys [file-id status] :as msg}]
|
||||||
|
(log/debug :msg msg)
|
||||||
|
(swap! state update :files update-status file-id status))))))
|
||||||
|
|
||||||
|
handle-cancel
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:editing @state))
|
||||||
|
(fn [event]
|
||||||
|
(when (nil? (:editing @state))
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (modal/hide)))))
|
||||||
|
|
||||||
|
handle-continue
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps project-id (:files @state))
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(let [files (->> @state :files (filterv #(= :ready (:status %))))]
|
||||||
|
(import-files project-id files))
|
||||||
|
|
||||||
|
(swap! state
|
||||||
|
(fn [state]
|
||||||
|
(-> state
|
||||||
|
(assoc :status :importing)
|
||||||
|
(update :files mark-files-importing))))))
|
||||||
|
|
||||||
|
handle-accept
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(st/emit! (modal/hide))
|
||||||
|
(when on-finish-import (on-finish-import))))
|
||||||
|
|
||||||
|
success-files (->> @state :files (filter #(= (:status %) :import-success)) count)
|
||||||
|
pending-analysis? (> (->> @state :files (filter #(= (:status %) :analyzing)) count) 0)
|
||||||
|
pending-import? (> (->> @state :files (filter #(= (:status %) :importing)) count) 0)]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(let [sub (analyze-import files)]
|
||||||
|
#(rx/dispose! sub))))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
;; dispose uris when the component is umount
|
||||||
|
#(doseq [file files]
|
||||||
|
(dom/revoke-uri (:uri file)))))
|
||||||
|
|
||||||
|
[:div.modal-overlay
|
||||||
|
[:div.modal-container.import-dialog
|
||||||
|
[:div.modal-header
|
||||||
|
[:div.modal-header-title
|
||||||
|
[:h2 (tr "dashboard.import")]]
|
||||||
|
|
||||||
|
[:div.modal-close-button
|
||||||
|
{:on-click handle-cancel} i/close]]
|
||||||
|
|
||||||
|
[:div.modal-content
|
||||||
|
(when (and (= :importing (:status @state))
|
||||||
|
(not pending-import?))
|
||||||
|
[:div.feedback-banner
|
||||||
|
[:div.icon i/checkbox-checked]
|
||||||
|
[:div.message (tr "dashboard.import.import-message" success-files)]])
|
||||||
|
|
||||||
|
(for [file (->> (:files @state) (filterv (comp not :deleted?)))]
|
||||||
|
(let [editing? (and (some? (:file-id file))
|
||||||
|
(= (:file-id file) (:editing @state)))]
|
||||||
|
[:& import-entry {:state state
|
||||||
|
:file file
|
||||||
|
:editing? editing?}]))]
|
||||||
|
|
||||||
|
[:div.modal-footer
|
||||||
|
[:div.action-buttons
|
||||||
|
[:input.cancel-button
|
||||||
|
{:type "button"
|
||||||
|
:value (tr "labels.cancel")
|
||||||
|
:on-click handle-cancel}]
|
||||||
|
|
||||||
|
(when (= :analyzing (:status @state))
|
||||||
|
[:input.accept-button
|
||||||
|
{:class "primary"
|
||||||
|
:type "button"
|
||||||
|
:value (tr "labels.continue")
|
||||||
|
:disabled pending-analysis?
|
||||||
|
:on-click handle-continue}])
|
||||||
|
|
||||||
|
(when (= :importing (:status @state))
|
||||||
|
[:input.accept-button
|
||||||
|
{:class "primary"
|
||||||
|
:type "button"
|
||||||
|
:value (tr "labels.accept")
|
||||||
|
:disabled pending-import?
|
||||||
|
:on-click handle-accept}])]]]]))
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
[app.main.ui.components.context-menu :refer [context-menu]]
|
[app.main.ui.components.context-menu :refer [context-menu]]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.dashboard.import :as udi]
|
[app.main.ui.dashboard.import :as udi]
|
||||||
[app.util.debug :as d]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
|
@ -107,8 +106,7 @@
|
||||||
[(tr "dashboard.move-to") nil
|
[(tr "dashboard.move-to") nil
|
||||||
(for [team teams]
|
(for [team teams]
|
||||||
[(:name team) (on-move (:id team))])])
|
[(:name team) (on-move (:id team))])])
|
||||||
(when (d/debug? :import)
|
[(tr "dashboard.import") on-import-files]
|
||||||
[(tr "dashboard.import") on-import-files])
|
|
||||||
[:separator]
|
[:separator]
|
||||||
[(tr "labels.delete") on-delete]]}]]))
|
[(tr "labels.delete") on-delete]]}]]))
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
|
|
||||||
(defmacro icon-xref
|
(defmacro icon-xref
|
||||||
[id]
|
[id]
|
||||||
(let [href (str "#icon-" (name id))]
|
(let [href (str "#icon-" (name id))
|
||||||
|
class (str "icon-" (name id))]
|
||||||
`(rumext.alpha/html
|
`(rumext.alpha/html
|
||||||
[:svg {:width 500 :height 500}
|
[:svg {:width 500 :height 500 :class ~class}
|
||||||
[:use {:xlinkHref ~href}]])))
|
[:use {:xlinkHref ~href}]])))
|
||||||
|
|
||||||
|
|
|
@ -15,4 +15,4 @@
|
||||||
(mf/defc loader
|
(mf/defc loader
|
||||||
[]
|
[]
|
||||||
(when (mf/deref st/loader)
|
(when (mf/deref st/loader)
|
||||||
[:div.loader-content i/loader]))
|
[:div.loader-content i/loader-pencil]))
|
||||||
|
|
|
@ -213,6 +213,10 @@
|
||||||
[node]
|
[node]
|
||||||
(.focus node))
|
(.focus node))
|
||||||
|
|
||||||
|
(defn blur!
|
||||||
|
[node]
|
||||||
|
(.blur node))
|
||||||
|
|
||||||
(defn fullscreen?
|
(defn fullscreen?
|
||||||
[]
|
[]
|
||||||
(cond
|
(cond
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
(defn create-manifest
|
(defn create-manifest
|
||||||
"Creates a manifest entry for the given files"
|
"Creates a manifest entry for the given files"
|
||||||
[team-id file-id files]
|
[team-id file-id export-type files]
|
||||||
(letfn [(format-page [manifest page]
|
(letfn [(format-page [manifest page]
|
||||||
(-> manifest
|
(-> manifest
|
||||||
(assoc (str (:id page))
|
(assoc (str (:id page))
|
||||||
|
@ -47,6 +47,7 @@
|
||||||
:pages pages
|
:pages pages
|
||||||
:pagesIndex index
|
:pagesIndex index
|
||||||
:libraries (->> (:libraries file) (into #{}) (mapv str))
|
:libraries (->> (:libraries file) (into #{}) (mapv str))
|
||||||
|
:exportType (d/name export-type)
|
||||||
:hasComponents (d/not-empty? (get-in file [:data :components]))
|
:hasComponents (d/not-empty? (get-in file [:data :components]))
|
||||||
:hasMedia (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]))
|
:hasColors (d/not-empty? (get-in file [:data :colors]))
|
||||||
|
@ -158,8 +159,36 @@
|
||||||
(-> file
|
(-> file
|
||||||
(assoc :libraries libraries-ids)))))))
|
(assoc :libraries libraries-ids)))))))
|
||||||
|
|
||||||
|
(defn merge-assets [target-file assets-files]
|
||||||
|
(let [merge-file-assets
|
||||||
|
(fn [target file]
|
||||||
|
(-> target
|
||||||
|
(update-in [:data :colors] merge (get-in file [:data :colors]))
|
||||||
|
(update-in [:data :typographies] merge (get-in file [:data :typographies]))
|
||||||
|
(update-in [:data :media] merge (get-in file [:data :media]))
|
||||||
|
(update-in [:data :components] merge (get-in file [:data :components]))))]
|
||||||
|
|
||||||
|
(->> assets-files
|
||||||
|
(reduce merge-file-assets target-file))))
|
||||||
|
|
||||||
|
(defn detach-libraries
|
||||||
|
[files file-id]
|
||||||
|
files)
|
||||||
|
|
||||||
|
(defn process-export
|
||||||
|
[file-id export-type files]
|
||||||
|
|
||||||
|
(case export-type
|
||||||
|
:all files
|
||||||
|
:merge (let [file-list (-> files (d/without-keys [file-id]) vals)]
|
||||||
|
(-> (select-keys files [file-id])
|
||||||
|
(update file-id merge-assets file-list)
|
||||||
|
(update file-id dissoc :libraries)))
|
||||||
|
:detach (-> (select-keys files [file-id])
|
||||||
|
(update file-id detach-libraries file-id))))
|
||||||
|
|
||||||
(defn collect-files
|
(defn collect-files
|
||||||
[file-id]
|
[file-id export-type]
|
||||||
|
|
||||||
(letfn [(fetch-dependencies [[files pending]]
|
(letfn [(fetch-dependencies [[files pending]]
|
||||||
(if (empty? pending)
|
(if (empty? pending)
|
||||||
|
@ -185,17 +214,18 @@
|
||||||
(->> (rx/of [files pending])
|
(->> (rx/of [files pending])
|
||||||
(rx-expand fetch-dependencies)
|
(rx-expand fetch-dependencies)
|
||||||
(rx/last)
|
(rx/last)
|
||||||
(rx/map first)))))
|
(rx/map first)
|
||||||
|
(rx/map #(process-export file-id export-type %))))))
|
||||||
|
|
||||||
(defn export-file
|
(defn export-file
|
||||||
[team-id file-id]
|
[team-id file-id export-type]
|
||||||
|
|
||||||
(let [files-stream (->> (collect-files file-id)
|
(let [files-stream (->> (collect-files file-id export-type)
|
||||||
(rx/share))
|
(rx/share))
|
||||||
|
|
||||||
manifest-stream
|
manifest-stream
|
||||||
(->> files-stream
|
(->> files-stream
|
||||||
(rx/map #(create-manifest team-id file-id %))
|
(rx/map #(create-manifest team-id file-id export-type %))
|
||||||
(rx/map #(vector "manifest.json" %)))
|
(rx/map #(vector "manifest.json" %)))
|
||||||
|
|
||||||
render-stream
|
render-stream
|
||||||
|
@ -258,10 +288,10 @@
|
||||||
(rx/map #(vector (get files file-id) %)))))))))
|
(rx/map #(vector (get files file-id) %)))))))))
|
||||||
|
|
||||||
(defmethod impl/handler :export-file
|
(defmethod impl/handler :export-file
|
||||||
[{:keys [team-id files] :as message}]
|
[{:keys [team-id files export-type] :as message}]
|
||||||
|
|
||||||
(->> (rx/from files)
|
(->> (rx/from files)
|
||||||
(rx/mapcat #(export-file team-id %))
|
(rx/mapcat #(export-file team-id % export-type))
|
||||||
(rx/map
|
(rx/map
|
||||||
(fn [value]
|
(fn [value]
|
||||||
(if (contains? value :type)
|
(if (contains? value :type)
|
||||||
|
|
|
@ -84,25 +84,26 @@
|
||||||
(let [id-mapping-atom (atom {})
|
(let [id-mapping-atom (atom {})
|
||||||
resolve
|
resolve
|
||||||
(fn [id-mapping id]
|
(fn [id-mapping id]
|
||||||
(assert (uuid? id))
|
(assert (uuid? id) (str id))
|
||||||
(get id-mapping id))
|
(get id-mapping id))
|
||||||
|
|
||||||
set-id
|
set-id
|
||||||
(fn [id-mapping id]
|
(fn [id-mapping id]
|
||||||
(assert (uuid? id))
|
(assert (uuid? id) (str id))
|
||||||
(cond-> id-mapping
|
(cond-> id-mapping
|
||||||
(nil? (resolve id-mapping id))
|
(nil? (resolve id-mapping id))
|
||||||
(assoc id (uuid/next))))]
|
(assoc id (uuid/next))))]
|
||||||
|
|
||||||
(fn [id]
|
(fn [id]
|
||||||
(swap! id-mapping-atom set-id id)
|
(when (some? id)
|
||||||
(resolve @id-mapping-atom id))))
|
(swap! id-mapping-atom set-id id)
|
||||||
|
(resolve @id-mapping-atom id)))))
|
||||||
|
|
||||||
(defn create-file
|
(defn create-file
|
||||||
"Create a new file on the back-end"
|
"Create a new file on the back-end"
|
||||||
[context file-id]
|
[context]
|
||||||
(let [resolve (:resolve context)
|
(let [resolve (:resolve context)
|
||||||
file-id (resolve file-id)]
|
file-id (resolve (:file-id context))]
|
||||||
(rp/mutation
|
(rp/mutation
|
||||||
:create-temp-file
|
:create-temp-file
|
||||||
{:id file-id
|
{:id file-id
|
||||||
|
@ -111,19 +112,19 @@
|
||||||
:project-id (:project-id context)
|
:project-id (:project-id context)
|
||||||
:data (-> cp/empty-file-data (assoc :id file-id))})))
|
:data (-> cp/empty-file-data (assoc :id file-id))})))
|
||||||
|
|
||||||
(defn persist-file [file]
|
|
||||||
(rp/mutation :persist-temp-file {:id (:id file)}))
|
|
||||||
|
|
||||||
(defn link-file-libraries
|
(defn link-file-libraries
|
||||||
"Create a new file on the back-end"
|
"Create a new file on the back-end"
|
||||||
[context file-id]
|
[context]
|
||||||
(let [resolve (:resolve context)
|
(let [resolve (:resolve context)
|
||||||
file-id (resolve file-id)
|
file-id (resolve (:file-id context))
|
||||||
libraries (->> context :libraries (mapv resolve))]
|
libraries (->> context :libraries (mapv resolve))]
|
||||||
(->> (rx/from libraries)
|
(->> (rx/from libraries)
|
||||||
(rx/map #(hash-map :file-id file-id :library-id %))
|
(rx/map #(hash-map :file-id file-id :library-id %))
|
||||||
(rx/flat-map (partial rp/mutation :link-file-to-library)))))
|
(rx/flat-map (partial rp/mutation :link-file-to-library)))))
|
||||||
|
|
||||||
|
(defn persist-file [file]
|
||||||
|
(rp/mutation :persist-temp-file {:id (:id file)}))
|
||||||
|
|
||||||
(defn send-changes
|
(defn send-changes
|
||||||
"Creates batches of changes to be sent to the backend"
|
"Creates batches of changes to be sent to the backend"
|
||||||
[file]
|
[file]
|
||||||
|
@ -391,65 +392,59 @@
|
||||||
(rx/flat-map (partial process-library-typographies context))
|
(rx/flat-map (partial process-library-typographies context))
|
||||||
(rx/flat-map (partial process-library-media context))
|
(rx/flat-map (partial process-library-media context))
|
||||||
(rx/flat-map (partial process-library-components context))
|
(rx/flat-map (partial process-library-components context))
|
||||||
(rx/flat-map send-changes)
|
(rx/flat-map send-changes)))
|
||||||
(rx/ignore)))
|
|
||||||
|
|
||||||
(defn create-files [context manifest]
|
(defn create-files
|
||||||
(->> manifest :files rx/from
|
[context files]
|
||||||
|
|
||||||
|
(let [data (group-by :file-id files)]
|
||||||
|
(rx/concat
|
||||||
|
(->> (rx/from files)
|
||||||
|
(rx/map #(merge context %))
|
||||||
|
(rx/flat-map
|
||||||
|
(fn [context]
|
||||||
|
(->> (create-file context)
|
||||||
|
(rx/map #(vector % (first (get data (:file-id context)))))))))
|
||||||
|
|
||||||
|
(->> (rx/from files)
|
||||||
|
(rx/map #(merge context %))
|
||||||
|
(rx/flat-map link-file-libraries)
|
||||||
|
(rx/ignore)))))
|
||||||
|
|
||||||
|
(defmethod impl/handler :analyze-import
|
||||||
|
[{:keys [files]}]
|
||||||
|
|
||||||
|
(->> (rx/from files)
|
||||||
(rx/flat-map
|
(rx/flat-map
|
||||||
(fn [[file-id file-desc]]
|
(fn [uri]
|
||||||
(create-file (merge context file-desc) file-id)))
|
(->> (rx/of uri)
|
||||||
(rx/reduce #(assoc %1 (:id %2) %2) {})))
|
(rx/flat-map uz/load-from-url)
|
||||||
|
(rx/flat-map #(get-file {:zip %} :manifest))
|
||||||
|
(rx/map (comp d/kebab-keys cip/string->uuid))
|
||||||
|
(rx/map #(hash-map :uri uri :data %))
|
||||||
|
(rx/catch #(rx/of {:uri uri :error (.-message %)})))))))
|
||||||
|
|
||||||
(defn link-libraries [context manifest]
|
(defmethod impl/handler :import-files
|
||||||
(->> manifest :files rx/from
|
|
||||||
(rx/flat-map
|
|
||||||
(fn [[file-id file-desc]]
|
|
||||||
(link-file-libraries (merge context file-desc) file-id)))))
|
|
||||||
|
|
||||||
(defn process-files [context manifest files]
|
|
||||||
(->> manifest :files rx/from
|
|
||||||
(rx/flat-map
|
|
||||||
(fn [[file-id file-desc]]
|
|
||||||
(let [resolve (:resolve context)
|
|
||||||
context (-> context
|
|
||||||
(merge file-desc)
|
|
||||||
(assoc :file-id file-id))
|
|
||||||
file (get files (resolve file-id))]
|
|
||||||
(process-file context file))))))
|
|
||||||
|
|
||||||
(defn process-package
|
|
||||||
[context]
|
|
||||||
(->> (get-file context :manifest)
|
|
||||||
(rx/map (comp d/kebab-keys cip/string->uuid))
|
|
||||||
|
|
||||||
;; Create the temporary files
|
|
||||||
(rx/mapcat (fn [manifest]
|
|
||||||
(->> (create-files context manifest)
|
|
||||||
(rx/map #(vector manifest %)))))
|
|
||||||
|
|
||||||
;; Set-up the files dependencies
|
|
||||||
(rx/mapcat (fn [[manifest files]]
|
|
||||||
(rx/concat
|
|
||||||
(link-libraries context manifest)
|
|
||||||
(rx/of [manifest files]))))
|
|
||||||
|
|
||||||
;; Creates files data
|
|
||||||
(rx/mapcat (fn [[manifest files]]
|
|
||||||
(process-files context manifest files)))
|
|
||||||
|
|
||||||
;; Mark temporary files as persisted
|
|
||||||
(rx/mapcat persist-file)))
|
|
||||||
|
|
||||||
(defmethod impl/handler :import-file
|
|
||||||
[{:keys [project-id files]}]
|
[{:keys [project-id files]}]
|
||||||
|
|
||||||
(let [context {:project-id project-id
|
(let [context {:project-id project-id
|
||||||
:resolve (resolve-factory)}]
|
:resolve (resolve-factory)}]
|
||||||
(->> (rx/from files)
|
(->> (create-files context files)
|
||||||
(rx/flat-map uz/load-from-url)
|
(rx/catch #(.error js/console "IMPORT ERROR" %))
|
||||||
(rx/map #(assoc context :zip %))
|
(rx/flat-map
|
||||||
(rx/flat-map process-package)
|
(fn [[file data]]
|
||||||
(rx/catch
|
(->> (uz/load-from-url (:uri data))
|
||||||
(fn [err]
|
(rx/map #(-> context (assoc :zip %) (merge data)))
|
||||||
(.error js/console "ERROR" err (clj->js (.-data err))))))))
|
(rx/flat-map #(process-file % file))
|
||||||
|
(rx/map
|
||||||
|
(fn [_]
|
||||||
|
{:status :import-success
|
||||||
|
:file-id (:file-id data)}))
|
||||||
|
|
||||||
|
(rx/catch
|
||||||
|
(fn [err]
|
||||||
|
(.error js/console "ERROR" (:file-id data) err)
|
||||||
|
(rx/of {:status :import-error
|
||||||
|
:file-id (:file-id data)
|
||||||
|
:error (.-message err)
|
||||||
|
:error-data (clj->js (.-data err))})))))))))
|
||||||
|
|
|
@ -822,6 +822,9 @@ msgstr "Admin"
|
||||||
msgid "labels.all"
|
msgid "labels.all"
|
||||||
msgstr "All"
|
msgstr "All"
|
||||||
|
|
||||||
|
msgid "labels.continue"
|
||||||
|
msgstr "Continue"
|
||||||
|
|
||||||
#: src/app/main/ui/static.cljs
|
#: src/app/main/ui/static.cljs
|
||||||
msgid "labels.bad-gateway.desc-message"
|
msgid "labels.bad-gateway.desc-message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2696,4 +2699,43 @@ msgid "workspace.updates.update"
|
||||||
msgstr "Update"
|
msgstr "Update"
|
||||||
|
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Click to close the path"
|
msgstr "Click to close the path"
|
||||||
|
|
||||||
|
msgid "dashboard.import.import-message"
|
||||||
|
msgstr "%s files have been imported succesfully."
|
||||||
|
|
||||||
|
msgid "dashboard.import.import-error"
|
||||||
|
msgstr "There was a problem importing the file. The file wasn't imported."
|
||||||
|
|
||||||
|
msgid "dashboard.import.analyze-error"
|
||||||
|
msgstr "Oops! We couldn't import this file"
|
||||||
|
|
||||||
|
msgid "dashboard.export.title"
|
||||||
|
msgstr "Export files"
|
||||||
|
|
||||||
|
msgid "dashboard.export.explain"
|
||||||
|
msgstr "One or more files that you want to export are using shared libraries. What do you want to do with their assets*?"
|
||||||
|
|
||||||
|
msgid "dashboard.export.detail"
|
||||||
|
msgstr "* Might include components, graphics, colors and/or typographies."
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.all.title"
|
||||||
|
msgstr "Export shared libraries"
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.all.message"
|
||||||
|
msgstr "files with shared libraries will be included in the export, maintaining their linkage."
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.merge.title"
|
||||||
|
msgstr "Include shared library assets in file libraries"
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.merge.message"
|
||||||
|
msgstr "Your file will be exported with all external assets merged into the file library."
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.detach.title"
|
||||||
|
msgstr "Treat shared library assets as basic objects"
|
||||||
|
|
||||||
|
msgid "dashboard.export.options.detach.message"
|
||||||
|
msgstr "Shared libraries will not be included in the export and no assets will be added to the library. "
|
||||||
|
|
||||||
|
msgid "labels.export"
|
||||||
|
msgstr "Export"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue