mirror of
https://github.com/penpot/penpot.git
synced 2025-08-04 08:08:23 +02:00
259 lines
9.2 KiB
Clojure
259 lines
9.2 KiB
Clojure
;; 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) KALEIDOS INC
|
|
|
|
(ns app.main.data.exports
|
|
(:require
|
|
[app.common.uuid :as uuid]
|
|
[app.main.data.modal :as modal]
|
|
[app.main.data.persistence :as dwp]
|
|
[app.main.data.workspace.state-helpers :as wsh]
|
|
[app.main.refs :as refs]
|
|
[app.main.repo :as rp]
|
|
[app.main.store :as st]
|
|
[app.util.dom :as dom]
|
|
[app.util.time :as dt]
|
|
[app.util.websocket :as ws]
|
|
[beicon.v2.core :as rx]
|
|
[potok.v2.core :as ptk]))
|
|
|
|
(def default-timeout 5000)
|
|
|
|
(defn toggle-detail-visibililty
|
|
[]
|
|
(ptk/reify ::toggle-detail-visibililty
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(update-in state [:export :detail-visible] not))))
|
|
|
|
(defn toggle-widget-visibililty
|
|
[]
|
|
(ptk/reify ::toggle-widget-visibility
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(update-in state [:export :widget-visible] not))))
|
|
|
|
(defn clear-export-state
|
|
[id]
|
|
(ptk/reify ::clear-export-state
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
;; only clear if the existing export is the same
|
|
(let [existing-id (-> state :export :id)]
|
|
(if (and (some? existing-id)
|
|
(not= id existing-id))
|
|
state
|
|
(dissoc state :export))))))
|
|
|
|
|
|
(defn show-workspace-export-dialog
|
|
([] (show-workspace-export-dialog nil))
|
|
([{:keys [selected]}]
|
|
(ptk/reify ::show-workspace-export-dialog
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [file-id (:current-file-id state)
|
|
page-id (:current-page-id state)
|
|
selected (or selected (wsh/lookup-selected state page-id {}))
|
|
|
|
shapes (if (seq selected)
|
|
(wsh/lookup-shapes state selected)
|
|
(reverse (wsh/filter-shapes state #(pos? (count (:exports %))))))
|
|
|
|
exports (for [shape shapes
|
|
export (:exports shape)]
|
|
(-> export
|
|
(assoc :enabled true)
|
|
(assoc :page-id page-id)
|
|
(assoc :file-id file-id)
|
|
(assoc :object-id (:id shape))
|
|
(assoc :shape (dissoc shape :exports))
|
|
(assoc :name (:name shape))))]
|
|
|
|
(rx/of (modal/show :export-shapes
|
|
{:exports (vec exports)})))))))
|
|
|
|
(defn show-viewer-export-dialog
|
|
[{:keys [shapes page-id file-id share-id exports]}]
|
|
(ptk/reify ::show-viewer-export-dialog
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [exports (for [shape shapes
|
|
export exports]
|
|
(-> export
|
|
(assoc :enabled true)
|
|
(assoc :page-id page-id)
|
|
(assoc :file-id file-id)
|
|
(assoc :object-id (:id shape))
|
|
(assoc :shape (dissoc shape :exports))
|
|
(assoc :name (:name shape))
|
|
(cond-> share-id (assoc :share-id share-id))))]
|
|
(rx/of (modal/show :export-shapes {:exports (vec exports)})))))) #_TODO
|
|
|
|
(defn show-workspace-export-frames-dialog
|
|
[frames]
|
|
(ptk/reify ::show-workspace-export-frames-dialog
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [file-id (:current-file-id state)
|
|
page-id (:current-page-id state)
|
|
exports (for [frame frames]
|
|
{:enabled true
|
|
:page-id page-id
|
|
:file-id file-id
|
|
:object-id (:id frame)
|
|
:shape frame
|
|
:name (:name frame)})]
|
|
|
|
(rx/of (modal/show :export-frames
|
|
{:exports (vec exports)}))))))
|
|
|
|
(defn- initialize-export-status
|
|
[exports cmd resource]
|
|
(ptk/reify ::initialize-export-status
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(assoc state :export {:in-progress true
|
|
:resource-id (:id resource)
|
|
:healthy? true
|
|
:error false
|
|
:progress 0
|
|
:widget-visible true
|
|
:detail-visible true
|
|
:exports exports
|
|
:last-update (dt/now)
|
|
:cmd cmd}))))
|
|
|
|
(defn- update-export-status
|
|
[{:keys [done status resource-id filename] :as data}]
|
|
(ptk/reify ::update-export-status
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(let [time-diff (dt/diff (dt/now)
|
|
(get-in state [:export :last-update]))
|
|
healthy? (< time-diff (dt/duration {:seconds 6}))]
|
|
(cond-> state
|
|
(= status "running")
|
|
(update :export assoc :progress done :last-update (dt/now) :healthy? healthy?)
|
|
|
|
(= status "error")
|
|
(update :export assoc :in-progress false :error (:cause data) :last-update (dt/now) :healthy? healthy?)
|
|
|
|
(= status "ended")
|
|
(update :export assoc :in-progress false :last-update (dt/now) :healthy? healthy?))))
|
|
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(when (= status "ended")
|
|
(->> (rp/cmd! :export {:cmd :get-resource :blob? true :id resource-id})
|
|
(rx/delay 500)
|
|
(rx/map #(dom/trigger-download filename %)))))))
|
|
|
|
(defn request-simple-export
|
|
[{:keys [export]}]
|
|
(ptk/reify ::request-simple-export
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(update state :export assoc :in-progress true :id uuid/zero))
|
|
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [profile-id (:profile-id state)
|
|
params {:exports [export]
|
|
:profile-id profile-id
|
|
:cmd :export-shapes
|
|
:wait true}]
|
|
(rx/concat
|
|
(rx/of ::dwp/force-persist)
|
|
|
|
;; Wait the persist to be succesfull
|
|
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
|
|
(rx/filter #(or (nil? %) (= :saved %)))
|
|
(rx/first)
|
|
(rx/timeout 400 (rx/empty)))
|
|
|
|
(->> (rp/cmd! :export params)
|
|
(rx/mapcat (fn [{:keys [id filename]}]
|
|
(->> (rp/cmd! :export {:cmd :get-resource :blob? true :id id})
|
|
(rx/map (fn [data]
|
|
(dom/trigger-download filename data)
|
|
(clear-export-state uuid/zero))))))
|
|
(rx/catch (fn [cause]
|
|
(rx/concat
|
|
(rx/of (clear-export-state uuid/zero))
|
|
(rx/throw cause))))))))))
|
|
|
|
|
|
(defn request-multiple-export
|
|
[{:keys [exports cmd]
|
|
:or {cmd :export-shapes}
|
|
:as params}]
|
|
(ptk/reify ::request-multiple-export
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [resource-id (volatile! nil)
|
|
profile-id (:profile-id state)
|
|
ws-conn (:ws-conn state)
|
|
params {:exports exports
|
|
:cmd cmd
|
|
:profile-id profile-id
|
|
:wait false}
|
|
|
|
progress-stream
|
|
(->> (ws/get-rcv-stream ws-conn)
|
|
(rx/filter ws/message-event?)
|
|
(rx/map :payload)
|
|
(rx/filter #(= :export-update (:type %)))
|
|
(rx/filter #(= @resource-id (:resource-id %)))
|
|
(rx/share))
|
|
|
|
stopper
|
|
(rx/filter #(or (= "ended" (:status %))
|
|
(= "error" (:status %)))
|
|
progress-stream)]
|
|
|
|
(swap! st/ongoing-tasks conj :export)
|
|
|
|
(rx/merge
|
|
;; Force that all data is persisted; best effort.
|
|
(rx/of ::dwp/force-persist)
|
|
|
|
;; Launch the exportation process and stores the resource id
|
|
;; locally.
|
|
(->> (rp/cmd! :export params)
|
|
(rx/map (fn [{:keys [id] :as resource}]
|
|
(vreset! resource-id id)
|
|
(initialize-export-status exports cmd resource))))
|
|
|
|
;; We proceed to update the export state with incoming
|
|
;; progress updates. We delay the stopper for give some time
|
|
;; to update the status with ended or errored status before
|
|
;; close the stream.
|
|
(->> progress-stream
|
|
(rx/map update-export-status)
|
|
(rx/take-until (rx/delay 500 stopper))
|
|
(rx/finalize (fn []
|
|
(swap! st/ongoing-tasks disj :export))))
|
|
|
|
;; We hide need to hide the ui elements of the export after
|
|
;; some interval. We also delay a little bit more the stopper
|
|
;; for ensure that after some security time, the stream is
|
|
;; completely closed.
|
|
(->> progress-stream
|
|
(rx/filter #(= "ended" (:status %)))
|
|
(rx/take 1)
|
|
(rx/delay default-timeout)
|
|
(rx/map #(clear-export-state @resource-id))
|
|
(rx/take-until (rx/delay 6000 stopper))))))))
|
|
|
|
(defn retry-last-export
|
|
[]
|
|
(ptk/reify ::retry-last-export
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [params (select-keys (:export state) [:exports :cmd])]
|
|
(when (seq params)
|
|
(rx/of (request-multiple-export params)))))))
|
|
|