mirror of
https://github.com/penpot/penpot.git
synced 2025-05-30 13:26:10 +02:00
♻️ Move page setup out of the data.workspace ns (#6502)
* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename file to target-name
* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename source-file to git-split-temp
* ♻️ Split history workspace.cljs to workspace/pages.cljs - restore name of source-file
* ♻️ Cleanup after adding new ns, add exports
* ♻️ Move set-plugin-data to main.data.plugins
* 🐛 Possible bugfix, cherry-picked from commit 8f7c63d6e2
(conflict during refactor)
---------
Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
This commit is contained in:
parent
f36aa30525
commit
71bb2556f9
7 changed files with 317 additions and 273 deletions
|
@ -7,6 +7,8 @@
|
||||||
(ns app.main.data.plugins
|
(ns app.main.data.plugins
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.changes-builder :as pcb]
|
||||||
|
[app.main.data.changes :as dch]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.notifications :as ntf]
|
[app.main.data.notifications :as ntf]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -178,3 +180,29 @@
|
||||||
(assoc-in [:plugins-permissions-peek :updated-at] now)))
|
(assoc-in [:plugins-permissions-peek :updated-at] now)))
|
||||||
|
|
||||||
state)))))
|
state)))))
|
||||||
|
|
||||||
|
(defn set-plugin-data
|
||||||
|
([file-id type namespace key value]
|
||||||
|
(set-plugin-data file-id type nil nil namespace key value))
|
||||||
|
|
||||||
|
([file-id type id namespace key value]
|
||||||
|
(set-plugin-data file-id type id nil namespace key value))
|
||||||
|
|
||||||
|
([file-id type id page-id namespace key value]
|
||||||
|
(dm/assert! (contains? #{:file :page :shape :color :typography :component} type))
|
||||||
|
(dm/assert! (or (nil? id) (uuid? id)))
|
||||||
|
(dm/assert! (or (nil? page-id) (uuid? page-id)))
|
||||||
|
(dm/assert! (uuid? file-id))
|
||||||
|
(dm/assert! (keyword? namespace))
|
||||||
|
(dm/assert! (string? key))
|
||||||
|
(dm/assert! (or (nil? value) (string? value)))
|
||||||
|
|
||||||
|
(ptk/reify ::set-file-plugin-data
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [file-data (dm/get-in state [:files file-id :data])
|
||||||
|
changes (-> (pcb/empty-changes it)
|
||||||
|
(pcb/with-file-data file-data)
|
||||||
|
(assoc :file-id file-id)
|
||||||
|
(pcb/set-plugin-data type id page-id namespace key value))]
|
||||||
|
(rx/of (dch/commit-changes changes)))))))
|
||||||
|
|
|
@ -20,13 +20,9 @@
|
||||||
[app.common.logic.shapes :as cls]
|
[app.common.logic.shapes :as cls]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.common.types.component :as ctc]
|
[app.common.types.component :as ctc]
|
||||||
[app.common.types.components-list :as ctkl]
|
|
||||||
[app.common.types.container :as ctn]
|
|
||||||
[app.common.types.page :as ctp]
|
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
|
||||||
[app.main.data.changes :as dch]
|
[app.main.data.changes :as dch]
|
||||||
[app.main.data.comments :as dcmt]
|
[app.main.data.comments :as dcmt]
|
||||||
[app.main.data.common :as dcm]
|
[app.main.data.common :as dcm]
|
||||||
|
@ -57,6 +53,7 @@
|
||||||
[app.main.data.workspace.layout :as layout]
|
[app.main.data.workspace.layout :as layout]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.notifications :as dwn]
|
[app.main.data.workspace.notifications :as dwn]
|
||||||
|
[app.main.data.workspace.pages :as dwpg]
|
||||||
[app.main.data.workspace.path :as dwdp]
|
[app.main.data.workspace.path :as dwdp]
|
||||||
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
@ -79,16 +76,13 @@
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.globals :as ug]
|
[app.util.globals :as ug]
|
||||||
[app.util.http :as http]
|
[app.util.http :as http]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
|
||||||
[app.util.storage :as storage]
|
[app.util.storage :as storage]
|
||||||
[app.util.timers :as tm]
|
[app.util.timers :as tm]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.v2.core :as ptk]))
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
(def default-workspace-local {:zoom 1})
|
|
||||||
(log/set-level! :debug)
|
(log/set-level! :debug)
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -259,15 +253,6 @@
|
||||||
(rx/of (dws/select-shapes frames-id)
|
(rx/of (dws/select-shapes frames-id)
|
||||||
dwz/zoom-to-selected-shape)))))
|
dwz/zoom-to-selected-shape)))))
|
||||||
|
|
||||||
(defn- select-frame-tool
|
|
||||||
[file-id page-id]
|
|
||||||
(ptk/reify ::select-frame-tool
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [page (dsh/lookup-page state file-id page-id)]
|
|
||||||
(when (ctp/is-empty? page)
|
|
||||||
(rx/of (dwd/select-for-drawing :frame)))))))
|
|
||||||
|
|
||||||
(defn- fetch-bundle
|
(defn- fetch-bundle
|
||||||
"Multi-stage file bundle fetch coordinator"
|
"Multi-stage file bundle fetch coordinator"
|
||||||
[file-id features]
|
[file-id features]
|
||||||
|
@ -443,247 +428,6 @@
|
||||||
;; Make this event callable through dynamic resolution
|
;; Make this event callable through dynamic resolution
|
||||||
(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file))
|
(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file))
|
||||||
|
|
||||||
(def ^:private xf:collect-file-media
|
|
||||||
"Resolve and collect all file media on page objects"
|
|
||||||
(comp (map second)
|
|
||||||
(keep (fn [{:keys [metadata fill-image]}]
|
|
||||||
(cond
|
|
||||||
(some? metadata) (cf/resolve-file-media metadata)
|
|
||||||
(some? fill-image) (cf/resolve-file-media fill-image))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- initialize-page*
|
|
||||||
"Second phase of page initialization, once we know the page is
|
|
||||||
available on the sate"
|
|
||||||
[file-id page-id page]
|
|
||||||
(ptk/reify ::initialize-page*
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
;; selection; when user abandon the current page, the selection is lost
|
|
||||||
(let [local (dm/get-in state [:workspace-cache [file-id page-id]] default-workspace-local)]
|
|
||||||
(-> state
|
|
||||||
(assoc :current-page-id page-id)
|
|
||||||
(assoc :workspace-local (assoc local :selected (d/ordered-set)))
|
|
||||||
(assoc :workspace-trimmed-page (dm/select-keys page [:id :name]))
|
|
||||||
|
|
||||||
;; FIXME: this should be done on `initialize-layout` (?)
|
|
||||||
(update :workspace-layout layout/load-layout-flags)
|
|
||||||
(update :workspace-global layout/load-layout-state))))
|
|
||||||
|
|
||||||
ptk/EffectEvent
|
|
||||||
(effect [_ _ _]
|
|
||||||
(let [uris (into #{} xf:collect-file-media (:objects page))]
|
|
||||||
(->> (rx/from uris)
|
|
||||||
(rx/subs! #(http/fetch-data-uri % false)))))))
|
|
||||||
|
|
||||||
(defn initialize-page
|
|
||||||
[file-id page-id]
|
|
||||||
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
|
||||||
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
|
||||||
|
|
||||||
(ptk/reify ::initialize-page
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(if-let [page (dsh/lookup-page state file-id page-id)]
|
|
||||||
(rx/concat
|
|
||||||
(rx/of (initialize-page* file-id page-id page)
|
|
||||||
(dwth/watch-state-changes file-id page-id)
|
|
||||||
(dwl/watch-component-changes))
|
|
||||||
(let [profile (:profile state)
|
|
||||||
props (get profile :props)]
|
|
||||||
(when (not (:workspace-visited props))
|
|
||||||
(rx/of (select-frame-tool file-id page-id)))))
|
|
||||||
|
|
||||||
;; NOTE: this redirect is necessary for cases where user
|
|
||||||
;; explicitly passes an non-existing page-id on the url
|
|
||||||
;; params, so on check it we can detect that there are no data
|
|
||||||
;; for the page and redirect user to an existing page
|
|
||||||
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
|
|
||||||
|
|
||||||
(defn finalize-page
|
|
||||||
[file-id page-id]
|
|
||||||
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
|
||||||
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
|
||||||
|
|
||||||
(ptk/reify ::finalize-page
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [local (-> (:workspace-local state)
|
|
||||||
(dissoc :edition :edit-path :selected))
|
|
||||||
exit? (not= :workspace (rt/lookup-name state))
|
|
||||||
state (-> state
|
|
||||||
(update :workspace-cache assoc [file-id page-id] local)
|
|
||||||
(dissoc :current-page-id
|
|
||||||
:workspace-local
|
|
||||||
:workspace-trimmed-page
|
|
||||||
:workspace-focus-selected))]
|
|
||||||
|
|
||||||
(cond-> state
|
|
||||||
exit? (dissoc :workspace-drawing))))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Workspace Page CRUD
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn create-page
|
|
||||||
[{:keys [page-id file-id]}]
|
|
||||||
(let [id (or page-id (uuid/next))]
|
|
||||||
(ptk/reify ::create-page
|
|
||||||
ev/Event
|
|
||||||
(-data [_]
|
|
||||||
{:id id
|
|
||||||
:file-id file-id})
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [pages (-> (dsh/lookup-file-data state)
|
|
||||||
(get :pages-index))
|
|
||||||
unames (cfh/get-used-names pages)
|
|
||||||
name (cfh/generate-unique-name "Page" unames :immediate-suffix? true)
|
|
||||||
changes (-> (pcb/empty-changes it)
|
|
||||||
(pcb/add-empty-page id name))]
|
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)))))))
|
|
||||||
|
|
||||||
(defn duplicate-page
|
|
||||||
[page-id]
|
|
||||||
(ptk/reify ::duplicate-page
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [id (uuid/next)
|
|
||||||
fdata (dsh/lookup-file-data state)
|
|
||||||
pages (get fdata :pages-index)
|
|
||||||
page (get pages page-id)
|
|
||||||
|
|
||||||
unames (cfh/get-used-names pages)
|
|
||||||
suffix-fn (fn [copy-count]
|
|
||||||
(str/concat " "
|
|
||||||
(tr "dashboard.copy-suffix")
|
|
||||||
(when (> copy-count 1)
|
|
||||||
(str " " copy-count))))
|
|
||||||
base-name (:name page)
|
|
||||||
name (cfh/generate-unique-name base-name unames :suffix-fn suffix-fn)
|
|
||||||
objects (update-vals (:objects page) #(dissoc % :use-for-thumbnail))
|
|
||||||
|
|
||||||
main-instances-ids (set (keep #(when (ctc/main-instance? (val %)) (key %)) objects))
|
|
||||||
ids-to-remove (set (apply concat (map #(cfh/get-children-ids objects %) main-instances-ids)))
|
|
||||||
|
|
||||||
add-component-copy
|
|
||||||
(fn [objs id shape]
|
|
||||||
(let [component (ctkl/get-component fdata (:component-id shape))
|
|
||||||
[new-shape new-shapes]
|
|
||||||
(ctn/make-component-instance page
|
|
||||||
component
|
|
||||||
fdata
|
|
||||||
(gpt/point (:x shape) (:y shape))
|
|
||||||
{:keep-ids? true :force-frame-id (:frame-id shape)})
|
|
||||||
children (into {} (map (fn [shape] [(:id shape) shape]) new-shapes))
|
|
||||||
objs (assoc objs id new-shape)]
|
|
||||||
(merge objs children)))
|
|
||||||
|
|
||||||
objects
|
|
||||||
(reduce
|
|
||||||
(fn [objs [id shape]]
|
|
||||||
(cond (contains? main-instances-ids id)
|
|
||||||
(add-component-copy objs id shape)
|
|
||||||
(contains? ids-to-remove id)
|
|
||||||
objs
|
|
||||||
:else
|
|
||||||
(assoc objs id shape)))
|
|
||||||
{}
|
|
||||||
objects)
|
|
||||||
|
|
||||||
page (-> page
|
|
||||||
(assoc :name name)
|
|
||||||
(assoc :id id)
|
|
||||||
(assoc :objects
|
|
||||||
objects))
|
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it)
|
|
||||||
(pcb/add-page id page))]
|
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes))))))
|
|
||||||
|
|
||||||
(s/def ::rename-page
|
|
||||||
(s/keys :req-un [::id ::name]))
|
|
||||||
|
|
||||||
(defn rename-page
|
|
||||||
[id name]
|
|
||||||
(dm/assert! (uuid? id))
|
|
||||||
(dm/assert! (string? name))
|
|
||||||
(ptk/reify ::rename-page
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [page (dsh/lookup-page state id)
|
|
||||||
changes (-> (pcb/empty-changes it)
|
|
||||||
(pcb/with-page page)
|
|
||||||
(pcb/mod-page page {:name name}))]
|
|
||||||
(rx/of (dch/commit-changes changes))))))
|
|
||||||
|
|
||||||
(defn set-plugin-data
|
|
||||||
([file-id type namespace key value]
|
|
||||||
(set-plugin-data file-id type nil nil namespace key value))
|
|
||||||
([file-id type id namespace key value]
|
|
||||||
(set-plugin-data file-id type id nil namespace key value))
|
|
||||||
([file-id type id page-id namespace key value]
|
|
||||||
(dm/assert! (contains? #{:file :page :shape :color :typography :component} type))
|
|
||||||
(dm/assert! (or (nil? id) (uuid? id)))
|
|
||||||
(dm/assert! (or (nil? page-id) (uuid? page-id)))
|
|
||||||
(dm/assert! (uuid? file-id))
|
|
||||||
(dm/assert! (keyword? namespace))
|
|
||||||
(dm/assert! (string? key))
|
|
||||||
(dm/assert! (or (nil? value) (string? value)))
|
|
||||||
|
|
||||||
(ptk/reify ::set-file-plugin-data
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [file-data (dm/get-in state [:files file-id :data])
|
|
||||||
changes (-> (pcb/empty-changes it)
|
|
||||||
(pcb/with-file-data file-data)
|
|
||||||
(assoc :file-id file-id)
|
|
||||||
(pcb/set-plugin-data type id page-id namespace key value))]
|
|
||||||
(rx/of (dch/commit-changes changes)))))))
|
|
||||||
|
|
||||||
(declare purge-page)
|
|
||||||
|
|
||||||
(defn- delete-page-components
|
|
||||||
[changes page]
|
|
||||||
(let [components-to-delete (->> page
|
|
||||||
:objects
|
|
||||||
vals
|
|
||||||
(filter #(true? (:main-instance %)))
|
|
||||||
(map :component-id))
|
|
||||||
|
|
||||||
changes (reduce (fn [changes component-id]
|
|
||||||
(pcb/delete-component changes component-id (:id page)))
|
|
||||||
changes
|
|
||||||
components-to-delete)]
|
|
||||||
changes))
|
|
||||||
|
|
||||||
(defn delete-page
|
|
||||||
[id]
|
|
||||||
(ptk/reify ::delete-page
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [file-id (:current-file-id state)
|
|
||||||
fdata (dsh/lookup-file-data state file-id)
|
|
||||||
pindex (:pages-index fdata)
|
|
||||||
pages (:pages fdata)
|
|
||||||
|
|
||||||
index (d/index-of pages id)
|
|
||||||
page (get pindex id)
|
|
||||||
page (assoc page :index index)
|
|
||||||
pages (filter #(not= % id) pages)
|
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it)
|
|
||||||
(pcb/with-library-data fdata)
|
|
||||||
(delete-page-components page)
|
|
||||||
(pcb/del-page page))]
|
|
||||||
|
|
||||||
(rx/of (dch/commit-changes changes)
|
|
||||||
(when (= id (:current-page-id state))
|
|
||||||
(dcm/go-to-workspace {:page-id (first pages)})))))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; WORKSPACE File Actions
|
;; WORKSPACE File Actions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -1519,7 +1263,6 @@
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
;; Transform
|
;; Transform
|
||||||
|
|
||||||
(dm/export dwt/trigger-bounding-box-cloaking)
|
(dm/export dwt/trigger-bounding-box-cloaking)
|
||||||
(dm/export dwt/start-resize)
|
(dm/export dwt/start-resize)
|
||||||
(dm/export dwt/update-dimensions)
|
(dm/export dwt/update-dimensions)
|
||||||
|
@ -1621,3 +1364,11 @@
|
||||||
|
|
||||||
;; Undo
|
;; Undo
|
||||||
(dm/export dwu/reinitialize-undo)
|
(dm/export dwu/reinitialize-undo)
|
||||||
|
|
||||||
|
;; Pages
|
||||||
|
(dm/export dwpg/initialize-page)
|
||||||
|
(dm/export dwpg/finalize-page)
|
||||||
|
(dm/export dwpg/create-page)
|
||||||
|
(dm/export dwpg/duplicate-page)
|
||||||
|
(dm/export dwpg/rename-page)
|
||||||
|
(dm/export dwpg/delete-page)
|
||||||
|
|
262
frontend/src/app/main/data/workspace/pages.cljs
Normal file
262
frontend/src/app/main/data/workspace/pages.cljs
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
;; 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.workspace.pages
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.changes-builder :as pcb]
|
||||||
|
[app.common.files.helpers :as cfh]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.types.component :as ctc]
|
||||||
|
[app.common.types.components-list :as ctkl]
|
||||||
|
[app.common.types.container :as ctn]
|
||||||
|
[app.common.types.page :as ctp]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.main.data.changes :as dch]
|
||||||
|
[app.main.data.common :as dcm]
|
||||||
|
[app.main.data.event :as ev]
|
||||||
|
[app.main.data.helpers :as dsh]
|
||||||
|
[app.main.data.persistence :as-alias dps]
|
||||||
|
[app.main.data.workspace.drawing :as dwd]
|
||||||
|
[app.main.data.workspace.layout :as layout]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.workspace.thumbnails :as dwth]
|
||||||
|
[app.main.errors]
|
||||||
|
[app.main.router :as rt]
|
||||||
|
[app.util.http :as http]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[beicon.v2.core :as rx]
|
||||||
|
[cljs.spec.alpha :as s]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
|
(def default-workspace-local {:zoom 1})
|
||||||
|
|
||||||
|
(defn- select-frame-tool
|
||||||
|
[file-id page-id]
|
||||||
|
(ptk/reify ::select-frame-tool
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [page (dsh/lookup-page state file-id page-id)]
|
||||||
|
(when (ctp/is-empty? page)
|
||||||
|
(rx/of (dwd/select-for-drawing :frame)))))))
|
||||||
|
|
||||||
|
(def ^:private xf:collect-file-media
|
||||||
|
"Resolve and collect all file media on page objects"
|
||||||
|
(comp (map second)
|
||||||
|
(keep (fn [{:keys [metadata fill-image]}]
|
||||||
|
(cond
|
||||||
|
(some? metadata) (cf/resolve-file-media metadata)
|
||||||
|
(some? fill-image) (cf/resolve-file-media fill-image))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- initialize-page*
|
||||||
|
"Second phase of page initialization, once we know the page is
|
||||||
|
available in the state"
|
||||||
|
[file-id page-id page]
|
||||||
|
(ptk/reify ::initialize-page*
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
;; selection; when user abandon the current page, the selection is lost
|
||||||
|
(let [local (dm/get-in state [:workspace-cache [file-id page-id]] default-workspace-local)]
|
||||||
|
(-> state
|
||||||
|
(assoc :current-page-id page-id)
|
||||||
|
(assoc :workspace-local (assoc local :selected (d/ordered-set)))
|
||||||
|
(assoc :workspace-trimmed-page (dm/select-keys page [:id :name]))
|
||||||
|
|
||||||
|
;; FIXME: this should be done on `initialize-layout` (?)
|
||||||
|
(update :workspace-layout layout/load-layout-flags)
|
||||||
|
(update :workspace-global layout/load-layout-state))))
|
||||||
|
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ _ _]
|
||||||
|
(let [uris (into #{} xf:collect-file-media (:objects page))]
|
||||||
|
(->> (rx/from uris)
|
||||||
|
(rx/subs! #(http/fetch-data-uri % false)))))))
|
||||||
|
|
||||||
|
(defn initialize-page
|
||||||
|
[file-id page-id]
|
||||||
|
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
||||||
|
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
||||||
|
|
||||||
|
(ptk/reify ::initialize-page
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(if-let [page (dsh/lookup-page state file-id page-id)]
|
||||||
|
(rx/concat
|
||||||
|
(rx/of (initialize-page* file-id page-id page)
|
||||||
|
(dwth/watch-state-changes file-id page-id)
|
||||||
|
(dwl/watch-component-changes))
|
||||||
|
(let [profile (:profile state)
|
||||||
|
props (get profile :props)]
|
||||||
|
(when (not (:workspace-visited props))
|
||||||
|
(rx/of (select-frame-tool file-id page-id)))))
|
||||||
|
|
||||||
|
;; NOTE: this redirect is necessary for cases where user
|
||||||
|
;; explicitly passes an non-existing page-id on the url
|
||||||
|
;; params, so on check it we can detect that there are no data
|
||||||
|
;; for the page and redirect user to an existing page
|
||||||
|
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
|
||||||
|
|
||||||
|
(defn finalize-page
|
||||||
|
[file-id page-id]
|
||||||
|
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
||||||
|
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
||||||
|
|
||||||
|
(ptk/reify ::finalize-page
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [local (-> (:workspace-local state)
|
||||||
|
(dissoc :edition :edit-path :selected))
|
||||||
|
exit? (not= :workspace (rt/lookup-name state))
|
||||||
|
state (-> state
|
||||||
|
(update :workspace-cache assoc [file-id page-id] local)
|
||||||
|
(dissoc :current-page-id
|
||||||
|
:workspace-local
|
||||||
|
:workspace-trimmed-page
|
||||||
|
:workspace-focus-selected))]
|
||||||
|
|
||||||
|
(cond-> state
|
||||||
|
exit? (dissoc :workspace-drawing))))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Workspace Page CRUD
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn create-page
|
||||||
|
[{:keys [page-id file-id]}]
|
||||||
|
(let [id (or page-id (uuid/next))]
|
||||||
|
(ptk/reify ::create-page
|
||||||
|
ev/Event
|
||||||
|
(-data [_]
|
||||||
|
{:id id
|
||||||
|
:file-id file-id})
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [pages (-> (dsh/lookup-file-data state)
|
||||||
|
(get :pages-index))
|
||||||
|
unames (cfh/get-used-names pages)
|
||||||
|
name (cfh/generate-unique-name "Page" unames :immediate-suffix? true)
|
||||||
|
changes (-> (pcb/empty-changes it)
|
||||||
|
(pcb/add-empty-page id name))]
|
||||||
|
|
||||||
|
(rx/of (dch/commit-changes changes)))))))
|
||||||
|
|
||||||
|
(defn duplicate-page
|
||||||
|
[page-id]
|
||||||
|
(ptk/reify ::duplicate-page
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [id (uuid/next)
|
||||||
|
fdata (dsh/lookup-file-data state)
|
||||||
|
pages (get fdata :pages-index)
|
||||||
|
page (get pages page-id)
|
||||||
|
|
||||||
|
unames (cfh/get-used-names pages)
|
||||||
|
suffix-fn (fn [copy-count]
|
||||||
|
(str/concat " "
|
||||||
|
(tr "dashboard.copy-suffix")
|
||||||
|
(when (> copy-count 1)
|
||||||
|
(str " " copy-count))))
|
||||||
|
base-name (:name page)
|
||||||
|
name (cfh/generate-unique-name base-name unames :suffix-fn suffix-fn)
|
||||||
|
objects (update-vals (:objects page) #(dissoc % :use-for-thumbnail))
|
||||||
|
|
||||||
|
main-instances-ids (set (keep #(when (ctc/main-instance? (val %)) (key %)) objects))
|
||||||
|
ids-to-remove (set (apply concat (map #(cfh/get-children-ids objects %) main-instances-ids)))
|
||||||
|
|
||||||
|
add-component-copy
|
||||||
|
(fn [objs id shape]
|
||||||
|
(let [component (ctkl/get-component fdata (:component-id shape))
|
||||||
|
[new-shape new-shapes]
|
||||||
|
(ctn/make-component-instance page
|
||||||
|
component
|
||||||
|
fdata
|
||||||
|
(gpt/point (:x shape) (:y shape))
|
||||||
|
{:keep-ids? true :force-frame-id (:frame-id shape)})
|
||||||
|
children (into {} (map (fn [shape] [(:id shape) shape]) new-shapes))
|
||||||
|
objs (assoc objs id new-shape)]
|
||||||
|
(merge objs children)))
|
||||||
|
|
||||||
|
objects
|
||||||
|
(reduce
|
||||||
|
(fn [objs [id shape]]
|
||||||
|
(cond (contains? main-instances-ids id)
|
||||||
|
(add-component-copy objs id shape)
|
||||||
|
(contains? ids-to-remove id)
|
||||||
|
objs
|
||||||
|
:else
|
||||||
|
(assoc objs id shape)))
|
||||||
|
{}
|
||||||
|
objects)
|
||||||
|
|
||||||
|
page (-> page
|
||||||
|
(assoc :name name)
|
||||||
|
(assoc :id id)
|
||||||
|
(assoc :objects
|
||||||
|
objects))
|
||||||
|
|
||||||
|
changes (-> (pcb/empty-changes it)
|
||||||
|
(pcb/add-page id page))]
|
||||||
|
|
||||||
|
(rx/of (dch/commit-changes changes))))))
|
||||||
|
|
||||||
|
(s/def ::rename-page
|
||||||
|
(s/keys :req-un [::id ::name]))
|
||||||
|
|
||||||
|
(defn rename-page
|
||||||
|
[id name]
|
||||||
|
(dm/assert! (uuid? id))
|
||||||
|
(dm/assert! (string? name))
|
||||||
|
(ptk/reify ::rename-page
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page (dsh/lookup-page state id)
|
||||||
|
changes (-> (pcb/empty-changes it)
|
||||||
|
(pcb/with-page page)
|
||||||
|
(pcb/mod-page page {:name name}))]
|
||||||
|
(rx/of (dch/commit-changes changes))))))
|
||||||
|
|
||||||
|
(defn- delete-page-components
|
||||||
|
[changes page]
|
||||||
|
(let [components-to-delete (->> page
|
||||||
|
:objects
|
||||||
|
vals
|
||||||
|
(filter #(true? (:main-instance %)))
|
||||||
|
(map :component-id))
|
||||||
|
|
||||||
|
changes (reduce (fn [changes component-id]
|
||||||
|
(pcb/delete-component changes component-id (:id page)))
|
||||||
|
changes
|
||||||
|
components-to-delete)]
|
||||||
|
changes))
|
||||||
|
|
||||||
|
(defn delete-page
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::delete-page
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [file-id (:current-file-id state)
|
||||||
|
fdata (dsh/lookup-file-data state file-id)
|
||||||
|
pindex (:pages-index fdata)
|
||||||
|
pages (:pages fdata)
|
||||||
|
|
||||||
|
index (d/index-of pages id)
|
||||||
|
page (get pindex id)
|
||||||
|
page (assoc page :index index)
|
||||||
|
pages (filter #(not= % id) pages)
|
||||||
|
|
||||||
|
changes (-> (pcb/empty-changes it)
|
||||||
|
(pcb/with-library-data fdata)
|
||||||
|
(delete-page-components page)
|
||||||
|
(pcb/del-page page))]
|
||||||
|
|
||||||
|
(rx/of (dch/commit-changes changes)
|
||||||
|
(when (= id (:current-page-id state))
|
||||||
|
(dcm/go-to-workspace {:page-id (first pages)})))))))
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.exports.files :as exports.files]
|
[app.main.data.exports.files :as exports.files]
|
||||||
|
[app.main.data.plugins :as dp]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.versions :as dwv]
|
[app.main.data.workspace.versions :as dwv]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
|
@ -163,7 +164,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -199,7 +200,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data id :file (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data id :file (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.types.typography :as ctt]
|
[app.common.types.typography :as ctt]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.plugins :as dp]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
[app.main.data.workspace.variants :as dwv]
|
[app.main.data.workspace.variants :as dwv]
|
||||||
|
@ -227,7 +227,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :color id (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :color id (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :color id (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :color id (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
@ -562,7 +562,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :typography id (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :typography id (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -601,7 +601,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :typography id (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :typography id (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
@ -707,7 +707,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :component id (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :component id (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -746,7 +746,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :component id (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :component id (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
@ -872,7 +872,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :file (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :file (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -908,7 +908,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :file (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :file (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.comments :as dc]
|
[app.main.data.comments :as dc]
|
||||||
[app.main.data.common :as dcm]
|
[app.main.data.common :as dcm]
|
||||||
|
[app.main.data.plugins :as dp]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.guides :as dwgu]
|
[app.main.data.workspace.guides :as dwgu]
|
||||||
[app.main.data.workspace.interactions :as dwi]
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
|
@ -213,7 +214,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -249,7 +250,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [self namespace]
|
(fn [self namespace]
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
[app.common.types.shape.radius :as ctsr]
|
[app.common.types.shape.radius :as ctsr]
|
||||||
[app.common.types.shape.shadow :as ctss]
|
[app.common.types.shape.shadow :as ctss]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.plugins :as dp]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.guides :as dwgu]
|
[app.main.data.workspace.guides :as dwgu]
|
||||||
|
@ -848,7 +849,7 @@
|
||||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :shape id page-id (keyword "plugin" (str plugin-id)) key value))))
|
(st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "plugin" (str plugin-id)) key value))))
|
||||||
|
|
||||||
:getPluginDataKeys
|
:getPluginDataKeys
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -884,7 +885,7 @@
|
||||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dw/set-plugin-data file-id :shape id page-id (keyword "shared" namespace) key value))))
|
(st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "shared" namespace) key value))))
|
||||||
|
|
||||||
:getSharedPluginDataKeys
|
:getSharedPluginDataKeys
|
||||||
(fn [namespace]
|
(fn [namespace]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue