mirror of
https://github.com/penpot/penpot.git
synced 2025-03-19 03:01:27 -05:00
🎉 Make components-v2 an optional feature
This commit is contained in:
parent
1ef37281e6
commit
a5bf1c03e7
40 changed files with 495 additions and 296 deletions
|
@ -46,7 +46,7 @@
|
|||
(s/def ::is-shared ::us/boolean)
|
||||
(s/def ::create-file
|
||||
(s/keys :req-un [::profile-id ::name ::project-id]
|
||||
:opt-un [::id ::is-shared]))
|
||||
:opt-un [::id ::is-shared ::components-v2]))
|
||||
|
||||
(sv/defmethod ::create-file
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
||||
|
@ -66,11 +66,12 @@
|
|||
|
||||
(defn create-file
|
||||
[conn {:keys [id name project-id is-shared data revn
|
||||
modified-at deleted-at ignore-sync-until]
|
||||
modified-at deleted-at ignore-sync-until
|
||||
components-v2]
|
||||
:or {is-shared false revn 0}
|
||||
:as params}]
|
||||
(let [id (or id (:id data) (uuid/next))
|
||||
data (or data (ctf/make-file-data id))
|
||||
data (or data (ctf/make-file-data id components-v2))
|
||||
file (db/insert! conn :file
|
||||
(d/without-nils
|
||||
{:id id
|
||||
|
@ -317,10 +318,11 @@
|
|||
|
||||
(s/def ::session-id ::us/uuid)
|
||||
(s/def ::revn ::us/integer)
|
||||
(s/def ::components-v2 ::us/boolean)
|
||||
(s/def ::update-file
|
||||
(s/and
|
||||
(s/keys :req-un [::id ::session-id ::profile-id ::revn]
|
||||
:opt-un [::changes ::changes-with-metadata])
|
||||
:opt-un [::changes ::changes-with-metadata ::components-v2])
|
||||
(fn [o]
|
||||
(or (contains? o :changes)
|
||||
(contains? o :changes-with-metadata)))))
|
||||
|
@ -357,7 +359,8 @@
|
|||
(simpl/del-object backend file))))
|
||||
|
||||
(defn- update-file
|
||||
[{:keys [conn metrics] :as cfg} {:keys [file changes changes-with-metadata session-id profile-id] :as params}]
|
||||
[{:keys [conn metrics] :as cfg}
|
||||
{:keys [file changes changes-with-metadata session-id profile-id components-v2] :as params}]
|
||||
(when (> (:revn params)
|
||||
(:revn file))
|
||||
|
||||
|
@ -382,12 +385,18 @@
|
|||
(update :data (fn [data]
|
||||
;; Trace the length of bytes of processed data
|
||||
(mtx/run! metrics {:id :update-file-bytes-processed :inc (alength data)})
|
||||
(-> data
|
||||
(blob/decode)
|
||||
(assoc :id (:id file))
|
||||
(pmg/migrate-data)
|
||||
(cp/process-changes changes)
|
||||
(blob/encode)))))]
|
||||
(cond-> data
|
||||
:always
|
||||
(-> (blob/decode)
|
||||
(assoc :id (:id file))
|
||||
(pmg/migrate-data))
|
||||
|
||||
components-v2
|
||||
(ctf/migrate-to-components-v2)
|
||||
|
||||
:always
|
||||
(-> (cp/process-changes changes)
|
||||
(blob/encode))))))]
|
||||
;; Insert change to the xlog
|
||||
(db/insert! conn :file-change
|
||||
{:id (uuid/next)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.common.pages.helpers :as cph]
|
||||
[app.common.pages.migrations :as pmg]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.db :as db]
|
||||
[app.db.sql :as sql]
|
||||
|
@ -27,7 +28,6 @@
|
|||
[cuerdas.core :as str]))
|
||||
|
||||
(declare decode-row)
|
||||
(declare decode-row-xf)
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
|||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::search-term ::us/string)
|
||||
(s/def ::components-v2 ::us/boolean)
|
||||
|
||||
;; --- Query: File Permissions
|
||||
|
||||
|
@ -123,8 +124,7 @@
|
|||
(defn check-comment-permissions!
|
||||
[conn profile-id file-id share-id]
|
||||
(let [can-read (has-read-permissions? conn profile-id file-id)
|
||||
can-comment (has-comment-permissions? conn profile-id file-id share-id)
|
||||
]
|
||||
can-comment (has-comment-permissions? conn profile-id file-id share-id)]
|
||||
(when-not (or can-read can-comment)
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found
|
||||
|
@ -227,20 +227,29 @@
|
|||
(d/index-by :object-id :data))))))
|
||||
|
||||
(defn retrieve-file
|
||||
[{:keys [pool] :as cfg} id]
|
||||
(->> (db/get-by-id pool :file id)
|
||||
(decode-row)
|
||||
(pmg/migrate-file)))
|
||||
[{:keys [pool] :as cfg} id components-v2]
|
||||
(let [file (->> (db/get-by-id pool :file id)
|
||||
(decode-row)
|
||||
(pmg/migrate-file))]
|
||||
|
||||
(if components-v2
|
||||
(update file :data ctf/migrate-to-components-v2)
|
||||
(if (get-in file [:data :options :components-v2])
|
||||
(ex/raise :type :restriction
|
||||
:code :feature-disabled
|
||||
:hint "tried to open a components-v2 file with feature disabled")
|
||||
file))))
|
||||
|
||||
(s/def ::file
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
(s/keys :req-un [::profile-id ::id]
|
||||
:opt-un [::components-v2]))
|
||||
|
||||
(sv/defmethod ::file
|
||||
"Retrieve a file by its ID. Only authenticated users."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id components-v2] :as params}]
|
||||
(let [perms (get-permissions pool profile-id id)]
|
||||
(check-read-permissions! perms)
|
||||
(let [file (retrieve-file cfg id)
|
||||
(let [file (retrieve-file cfg id components-v2)
|
||||
thumbs (retrieve-object-thumbnails cfg id)]
|
||||
(-> file
|
||||
(assoc :thumbnails thumbs)
|
||||
|
@ -269,7 +278,7 @@
|
|||
(s/def ::page
|
||||
(s/and
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
:opt-un [::page-id ::object-id])
|
||||
:opt-un [::page-id ::object-id ::components-v2])
|
||||
(fn [obj]
|
||||
(if (contains? obj :object-id)
|
||||
(contains? obj :page-id)
|
||||
|
@ -285,9 +294,9 @@
|
|||
mandatory.
|
||||
|
||||
Mainly used for rendering purposes."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id] :as props}]
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id components-v2] :as props}]
|
||||
(check-read-permissions! pool profile-id file-id)
|
||||
(let [file (retrieve-file cfg file-id)
|
||||
(let [file (retrieve-file cfg file-id components-v2)
|
||||
page-id (or page-id (-> file :data :pages first))
|
||||
page (get-in file [:data :pages-index page-id])]
|
||||
|
||||
|
@ -374,14 +383,15 @@
|
|||
(update :objects assoc-thumbnails page-id thumbs)))))
|
||||
|
||||
(s/def ::file-data-for-thumbnail
|
||||
(s/keys :req-un [::profile-id ::file-id]))
|
||||
(s/keys :req-un [::profile-id ::file-id]
|
||||
:opt-in [::components-v2]))
|
||||
|
||||
(sv/defmethod ::file-data-for-thumbnail
|
||||
"Retrieves the data for generate the thumbnail of the file. Used
|
||||
mainly for render thumbnails on dashboard."
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as props}]
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id components-v2] :as props}]
|
||||
(check-read-permissions! pool profile-id file-id)
|
||||
(let [file (retrieve-file cfg file-id)]
|
||||
(let [file (retrieve-file cfg file-id components-v2)]
|
||||
{:file-id file-id
|
||||
:revn (:revn file)
|
||||
:page (get-file-thumbnail-data cfg file)}))
|
||||
|
@ -523,7 +533,3 @@
|
|||
(cond-> row
|
||||
changes (assoc :changes (blob/decode changes))
|
||||
data (assoc :data (blob/decode data)))))
|
||||
|
||||
(def decode-row-xf
|
||||
(comp (map decode-row)
|
||||
(map pmg/migrate-file)))
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
(db/get-by-id pool :project id {:columns [:id :name :team-id]}))
|
||||
|
||||
(defn- retrieve-bundle
|
||||
[{:keys [pool] :as cfg} file-id profile-id]
|
||||
(p/let [file (files/retrieve-file cfg file-id)
|
||||
[{:keys [pool] :as cfg} file-id profile-id components-v2]
|
||||
(p/let [file (files/retrieve-file cfg file-id components-v2)
|
||||
project (retrieve-project pool (:project-id file))
|
||||
libs (files/retrieve-file-libraries cfg false file-id)
|
||||
users (comments/retrieve-file-comments-users pool file-id profile-id)
|
||||
|
@ -47,14 +47,14 @@
|
|||
(s/def ::share-id ::us/uuid)
|
||||
|
||||
(s/def ::view-only-bundle
|
||||
(s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id]))
|
||||
(s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id ::components-v2]))
|
||||
|
||||
(sv/defmethod ::view-only-bundle {:auth false}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id components-v2] :as params}]
|
||||
(p/let [slink (slnk/retrieve-share-link pool file-id share-id)
|
||||
perms (files/get-permissions pool profile-id file-id share-id)
|
||||
thumbs (files/retrieve-object-thumbnails cfg file-id)
|
||||
bundle (p/-> (retrieve-bundle cfg file-id profile-id)
|
||||
bundle (p/-> (retrieve-bundle cfg file-id profile-id components-v2)
|
||||
(assoc :permissions perms)
|
||||
(assoc-in [:file :thumbnails] thumbs))]
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
:project-id proj-id
|
||||
:id file-id
|
||||
:name "foobar"
|
||||
:is-shared false}
|
||||
:is-shared false
|
||||
:components-v2 true}
|
||||
out (th/mutation! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -71,7 +72,8 @@
|
|||
(t/testing "query single file without users"
|
||||
(let [data {::th/type :file
|
||||
:profile-id (:id prof)
|
||||
:id file-id}
|
||||
:id file-id
|
||||
:components-v2 true}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -95,7 +97,8 @@
|
|||
(t/testing "query single file after delete"
|
||||
(let [data {::th/type :file
|
||||
:profile-id (:id prof)
|
||||
:id file-id}
|
||||
:id file-id
|
||||
:components-v2 true}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -143,6 +146,7 @@
|
|||
:session-id (uuid/random)
|
||||
:profile-id profile-id
|
||||
:revn revn
|
||||
:components-v2 true
|
||||
:changes changes}
|
||||
out (th/mutation! params)]
|
||||
(t/is (nil? (:error out)))
|
||||
|
@ -171,6 +175,7 @@
|
|||
:id shid
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:components-v2 true
|
||||
:obj {:id shid
|
||||
:name "image"
|
||||
:frame-id uuid/zero
|
||||
|
@ -246,7 +251,8 @@
|
|||
:profile-id (:id profile2)
|
||||
:project-id (:default-project-id profile1)
|
||||
:name "foobar"
|
||||
:is-shared false}
|
||||
:is-shared false
|
||||
:components-v2 true}
|
||||
out (th/mutation! data)
|
||||
error (:error out)]
|
||||
|
||||
|
@ -462,6 +468,7 @@
|
|||
(th/update-file* {:file-id (:id file)
|
||||
:profile-id (:id prof)
|
||||
:revn 0
|
||||
:components-v2 true
|
||||
:changes changes})
|
||||
|
||||
(t/testing "RPC page query (rendering purposes)"
|
||||
|
@ -469,7 +476,8 @@
|
|||
;; Query :page RPC method without passing page-id
|
||||
(let [data {::th/type :page
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)}
|
||||
:file-id (:id file)
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -485,7 +493,8 @@
|
|||
(let [data {::th/type :page
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:page-id page-id}
|
||||
:page-id page-id
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (map? result))
|
||||
|
@ -501,7 +510,8 @@
|
|||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:page-id page-id
|
||||
:object-id frame1-id}
|
||||
:object-id frame1-id
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (map? result))
|
||||
|
@ -516,7 +526,8 @@
|
|||
(let [data {::th/type :page
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:object-id frame1-id}
|
||||
:object-id frame1-id
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (= :validation (th/ex-type error)))
|
||||
|
@ -537,7 +548,8 @@
|
|||
;; Check the result
|
||||
(let [data {::th/type :file-data-for-thumbnail
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)}
|
||||
:file-id (:id file)
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (map? result))
|
||||
|
@ -562,7 +574,8 @@
|
|||
;; Check the result
|
||||
(let [data {::th/type :file-data-for-thumbnail
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)}
|
||||
:file-id (:id file)
|
||||
:components-v2 true}
|
||||
{:keys [error result] :as out} (th/query! data)]
|
||||
;; (th/print-result! out)
|
||||
(t/is (map? result))
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
(let [data {::th/type :view-only-bundle
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
:page-id (get-in file [:data :pages 0])
|
||||
:components-v2 true}
|
||||
|
||||
out (th/query! data)]
|
||||
|
||||
|
@ -63,7 +64,8 @@
|
|||
(let [data {::th/type :view-only-bundle
|
||||
:profile-id (:id prof2)
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
:page-id (get-in file [:data :pages 0])
|
||||
:components-v2 true}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -78,7 +80,8 @@
|
|||
:profile-id (:id prof2)
|
||||
:share-id @share-id
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
:page-id (get-in file [:data :pages 0])
|
||||
:components-v2 true}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
@ -93,7 +96,8 @@
|
|||
(let [data {::th/type :view-only-bundle
|
||||
:share-id @share-id
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
:page-id (get-in file [:data :pages 0])
|
||||
:components-v2 true}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
|
|
@ -164,7 +164,8 @@
|
|||
(us/assert uuid? project-id)
|
||||
(#'files/create-file conn
|
||||
(merge {:id (mk-uuid "file" i)
|
||||
:name (str "file" i)}
|
||||
:name (str "file" i)
|
||||
:components-v2 true}
|
||||
params))))
|
||||
|
||||
(defn mark-file-deleted*
|
||||
|
@ -249,6 +250,7 @@
|
|||
:metrics metrics}
|
||||
{:file file
|
||||
:revn revn
|
||||
:components-v2 true
|
||||
:changes changes
|
||||
:session-id session-id
|
||||
:profile-id profile-id}))))
|
||||
|
|
|
@ -465,15 +465,20 @@
|
|||
(defmulti components-changed (fn [_ change] (:type change)))
|
||||
|
||||
(defmethod components-changed :mod-obj
|
||||
[file-data {:keys [id page-id component-id operations]}]
|
||||
[file-data {:keys [id page-id _component-id operations]}]
|
||||
(when page-id
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
shape-and-parents (map #(ctn/get-shape page %)
|
||||
(into [id] (cph/get-parent-ids (:objects page) id)))
|
||||
any-set? (some #(= (:type %) :set) operations)]
|
||||
(when any-set?
|
||||
need-sync? (fn [operation]
|
||||
; We need to trigger a sync if the shape has changed any
|
||||
; attribute that participates in components syncronization.
|
||||
(and (= (:type operation) :set)
|
||||
(component-sync-attrs (:attr operation))))
|
||||
any-sync? (some need-sync? operations)]
|
||||
(when any-sync?
|
||||
(into #{} (->> shape-and-parents
|
||||
(filter #(:main-instance? %))
|
||||
(filter #(:main-instance? %)) ; Select shapes that are main component instances
|
||||
(map :id)))))))
|
||||
|
||||
(defmethod components-changed :default
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
(defn with-objects
|
||||
[changes objects]
|
||||
(let [file-data (-> (ctf/make-file-data (uuid/next) uuid/zero)
|
||||
(let [file-data (-> (ctf/make-file-data (uuid/next) uuid/zero true)
|
||||
(assoc-in [:pages-index uuid/zero :objects] objects))]
|
||||
(vary-meta changes assoc ::file-data file-data
|
||||
::applied-changes-count 0)))
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[app.common.colors :as clr]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(def file-version 20)
|
||||
(def file-version 19)
|
||||
(def default-color clr/gray-20)
|
||||
(def root uuid/zero)
|
||||
|
||||
|
|
|
@ -440,68 +440,5 @@
|
|||
(update :pages-index d/update-vals update-container)
|
||||
(update :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate 20
|
||||
[data]
|
||||
(let [components (ctkl/components-seq data)]
|
||||
(if (empty? components)
|
||||
data
|
||||
(let [grid-gap 50
|
||||
|
||||
[data page-id start-pos]
|
||||
(ctf/get-or-add-library-page data grid-gap)
|
||||
|
||||
add-main-instance
|
||||
(fn [data component position]
|
||||
(let [page (ctpl/get-page data page-id)
|
||||
|
||||
[new-shape new-shapes]
|
||||
(ctn/make-component-instance page
|
||||
component
|
||||
(:id data)
|
||||
position
|
||||
true)
|
||||
|
||||
add-shapes
|
||||
(fn [page]
|
||||
(reduce (fn [page shape]
|
||||
(ctst/add-shape (:id shape)
|
||||
shape
|
||||
page
|
||||
(:frame-id shape)
|
||||
(:parent-id shape)
|
||||
nil ; <- As shapes are ordered, we can safely add each
|
||||
true)) ; one at the end of the parent's children list.
|
||||
page
|
||||
new-shapes))
|
||||
|
||||
update-component
|
||||
(fn [component]
|
||||
(assoc component
|
||||
:main-instance-id (:id new-shape)
|
||||
:main-instance-page page-id))]
|
||||
|
||||
(-> data
|
||||
(ctpl/update-page page-id add-shapes)
|
||||
(ctkl/update-component (:id component) update-component))))
|
||||
|
||||
add-instance-grid
|
||||
(fn [data components]
|
||||
(let [position-seq (ctst/generate-shape-grid
|
||||
(map ctk/get-component-root components)
|
||||
start-pos
|
||||
grid-gap)]
|
||||
(loop [data data
|
||||
components-seq (seq components)
|
||||
position-seq position-seq]
|
||||
(let [component (first components-seq)
|
||||
position (first position-seq)]
|
||||
(if (nil? component)
|
||||
data
|
||||
(recur (add-main-instance data component position)
|
||||
(rest components-seq)
|
||||
(rest position-seq)))))))]
|
||||
|
||||
(add-instance-grid data (sort-by :name components))))))
|
||||
|
||||
;; TODO: pending to do a migration for delete already not used fill
|
||||
;; and stroke props. This should be done for >1.14.x version.
|
||||
|
|
|
@ -14,13 +14,19 @@
|
|||
|
||||
(defn add-component
|
||||
[file-data id name path main-instance-id main-instance-page shapes]
|
||||
(assoc-in file-data [:components id]
|
||||
{:id id
|
||||
:name name
|
||||
:path path
|
||||
:main-instance-id main-instance-id
|
||||
:main-instance-page main-instance-page
|
||||
:objects (d/index-by :id shapes)}))
|
||||
(let [components-v2 (get-in file-data [:options :components-v2])]
|
||||
(cond-> file-data
|
||||
:always
|
||||
(assoc-in [:components id]
|
||||
{:id id
|
||||
:name name
|
||||
:path path
|
||||
:objects (d/index-by :id shapes)})
|
||||
|
||||
components-v2
|
||||
(update-in [:components id] #(assoc %
|
||||
:main-instance-id main-instance-id
|
||||
:main-instance-page main-instance-page)))))
|
||||
|
||||
(defn get-component
|
||||
[file-data component-id]
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
"Clone the shape and all children. Generate new ids and detach
|
||||
from parent and frame. Update the original shapes to have links
|
||||
to the new ones."
|
||||
[shape objects file-id]
|
||||
[shape objects file-id components-v2]
|
||||
(assert (nil? (:component-id shape)))
|
||||
(assert (nil? (:component-file shape)))
|
||||
(assert (nil? (:shape-ref shape)))
|
||||
|
@ -94,8 +94,10 @@
|
|||
(nil? (:parent-id new-shape))
|
||||
(assoc :component-id (:id new-shape)
|
||||
:component-file file-id
|
||||
:component-root? true
|
||||
:main-instance? true)
|
||||
:component-root? true)
|
||||
|
||||
components-v2
|
||||
(assoc :main-instance? true)
|
||||
|
||||
(some? (:parent-id new-shape))
|
||||
(dissoc :component-root?)))]
|
||||
|
@ -103,6 +105,9 @@
|
|||
(ctst/clone-object shape nil objects update-new-shape update-original-shape)))
|
||||
|
||||
(defn make-component-instance
|
||||
"Clone the shapes of the component, generating new names and ids, and linking
|
||||
each new shape to the corresponding one of the component. Place the new instance
|
||||
coordinates in the given position."
|
||||
[container component component-file-id position main-instance?]
|
||||
(let [component-shape (get-shape component (:id component))
|
||||
|
||||
|
|
|
@ -83,14 +83,17 @@
|
|||
:pages-index {}})
|
||||
|
||||
(defn make-file-data
|
||||
([file-id]
|
||||
(make-file-data file-id (uuid/next)))
|
||||
([file-id components-v2]
|
||||
(make-file-data file-id (uuid/next) components-v2))
|
||||
|
||||
([file-id page-id]
|
||||
([file-id page-id components-v2]
|
||||
(let [page (ctp/make-empty-page page-id "Page-1")]
|
||||
(-> empty-file-data
|
||||
(assoc :id file-id)
|
||||
(ctpl/add-page page)))))
|
||||
(cond-> (-> empty-file-data
|
||||
(assoc :id file-id)
|
||||
(ctpl/add-page page))
|
||||
|
||||
components-v2
|
||||
(assoc-in [:options :components-v2] true)))))
|
||||
|
||||
;; Helpers
|
||||
|
||||
|
@ -162,9 +165,9 @@
|
|||
assets-seq)))
|
||||
|
||||
(defn get-or-add-library-page
|
||||
[file-data grid-gap]
|
||||
"If exists a page named 'Library page', get the id and calculate the position to start
|
||||
adding new components. If not, create it and start at (0, 0)."
|
||||
[file-data grid-gap]
|
||||
(let [library-page (d/seek #(= (:name %) "Library page") (ctpl/pages-seq file-data))]
|
||||
(if (some? library-page)
|
||||
(let [compare-pos (fn [pos shape]
|
||||
|
@ -180,6 +183,74 @@
|
|||
(let [library-page (ctp/make-empty-page (uuid/next) "Library page")]
|
||||
[(ctpl/add-page file-data library-page) (:id library-page) (gpt/point 0 0)]))))
|
||||
|
||||
(defn migrate-to-components-v2
|
||||
"If there is any component in the file library, add a new 'Library Page' and generate
|
||||
main instances for all components there. Mark the file with the :comonents-v2 option."
|
||||
[file-data]
|
||||
(let [components (ctkl/components-seq file-data)]
|
||||
(if (or (empty? components)
|
||||
(get-in file-data [:options :components-v2]))
|
||||
(assoc-in file-data [:options :components-v2] true)
|
||||
(let [grid-gap 50
|
||||
|
||||
[file-data page-id start-pos]
|
||||
(get-or-add-library-page file-data grid-gap)
|
||||
|
||||
add-main-instance
|
||||
(fn [file-data component position]
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
|
||||
[new-shape new-shapes]
|
||||
(ctn/make-component-instance page
|
||||
component
|
||||
(:id file-data)
|
||||
position
|
||||
true)
|
||||
|
||||
add-shapes
|
||||
(fn [page]
|
||||
(reduce (fn [page shape]
|
||||
(ctst/add-shape (:id shape)
|
||||
shape
|
||||
page
|
||||
(:frame-id shape)
|
||||
(:parent-id shape)
|
||||
nil ; <- As shapes are ordered, we can safely add each
|
||||
true)) ; one at the end of the parent's children list.
|
||||
page
|
||||
new-shapes))
|
||||
|
||||
update-component
|
||||
(fn [component]
|
||||
(assoc component
|
||||
:main-instance-id (:id new-shape)
|
||||
:main-instance-page page-id))]
|
||||
|
||||
(-> file-data
|
||||
(ctpl/update-page page-id add-shapes)
|
||||
(ctkl/update-component (:id component) update-component))))
|
||||
|
||||
add-instance-grid
|
||||
(fn [file-data components]
|
||||
(let [position-seq (ctst/generate-shape-grid
|
||||
(map ctk/get-component-root components)
|
||||
start-pos
|
||||
grid-gap)]
|
||||
(loop [file-data file-data
|
||||
components-seq (seq components)
|
||||
position-seq position-seq]
|
||||
(let [component (first components-seq)
|
||||
position (first position-seq)]
|
||||
(if (nil? component)
|
||||
file-data
|
||||
(recur (add-main-instance file-data component position)
|
||||
(rest components-seq)
|
||||
(rest position-seq)))))))]
|
||||
|
||||
(-> file-data
|
||||
(add-instance-grid (sort-by :name components))
|
||||
(assoc-in [:options :components-v2] true))))))
|
||||
|
||||
(defn- absorb-components
|
||||
[file-data library-data used-components]
|
||||
(let [grid-gap 50
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
(t/deftest process-change-set-option
|
||||
(let [file-id (uuid/custom 2 2)
|
||||
page-id (uuid/custom 1 1)
|
||||
data (ctf/make-file-data file-id page-id)]
|
||||
data (ctf/make-file-data file-id page-id true)]
|
||||
(t/testing "Sets option single"
|
||||
(let [chg {:type :set-option
|
||||
:page-id page-id
|
||||
|
@ -81,7 +81,7 @@
|
|||
(t/deftest process-change-add-obj
|
||||
(let [file-id (uuid/custom 2 2)
|
||||
page-id (uuid/custom 1 1)
|
||||
data (ctf/make-file-data file-id page-id)
|
||||
data (ctf/make-file-data file-id page-id true)
|
||||
id-a (uuid/custom 2 1)
|
||||
id-b (uuid/custom 2 2)
|
||||
id-c (uuid/custom 2 3)]
|
||||
|
@ -135,7 +135,7 @@
|
|||
(t/deftest process-change-mod-obj
|
||||
(let [file-id (uuid/custom 2 2)
|
||||
page-id (uuid/custom 1 1)
|
||||
data (ctf/make-file-data file-id page-id)]
|
||||
data (ctf/make-file-data file-id page-id true)]
|
||||
(t/testing "simple mod-obj"
|
||||
(let [chg {:type :mod-obj
|
||||
:page-id page-id
|
||||
|
@ -162,7 +162,7 @@
|
|||
(let [file-id (uuid/custom 2 2)
|
||||
page-id (uuid/custom 1 1)
|
||||
id (uuid/custom 2 1)
|
||||
data (ctf/make-file-data file-id page-id)
|
||||
data (ctf/make-file-data file-id page-id true)
|
||||
data (-> data
|
||||
(assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id])
|
||||
(assoc-in [:pages-index page-id :objects id]
|
||||
|
@ -206,7 +206,7 @@
|
|||
|
||||
file-id (uuid/custom 2 2)
|
||||
page-id (uuid/custom 1 1)
|
||||
data (ctf/make-file-data file-id page-id)
|
||||
data (ctf/make-file-data file-id page-id true)
|
||||
|
||||
data (update-in data [:pages-index page-id :objects]
|
||||
#(-> %
|
||||
|
@ -450,7 +450,7 @@
|
|||
:obj {:type :rect
|
||||
:name "Shape 3"}}
|
||||
]
|
||||
data (ctf/make-file-data file-id page-id)
|
||||
data (ctf/make-file-data file-id page-id true)
|
||||
data (cp/process-changes data changes)]
|
||||
|
||||
(t/testing "preserve order on multiple shape mov 1"
|
||||
|
@ -557,7 +557,7 @@
|
|||
:parent-id group-1-id
|
||||
:shapes [shape-1-id shape-2-id]}]
|
||||
|
||||
data (ctf/make-file-data file-id page-id)
|
||||
data (ctf/make-file-data file-id page-id true)
|
||||
data (cp/process-changes data changes)]
|
||||
|
||||
(t/testing "case 1"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
(ns app.common.test-helpers.components
|
||||
(:require
|
||||
[cljs.test :as t :include-macros true]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[clojure.test :as t]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]))
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
([file-id page-id props]
|
||||
(merge {:id file-id
|
||||
:name (get props :name "File1")
|
||||
:data (ctf/make-file-data file-id page-id)}
|
||||
:data (ctf/make-file-data file-id page-id true)}
|
||||
props)))
|
||||
|
||||
(defn sample-shape
|
||||
|
@ -68,9 +68,10 @@
|
|||
(let [page (ctpl/get-page file-data page-id)
|
||||
|
||||
[component-shape component-shapes updated-shapes]
|
||||
(ctn/make-component-shape (ctn/get-shape page shape-id)
|
||||
(ctn/make-component-shape (ctn/get-shape page shape-id true)
|
||||
(:objects page)
|
||||
(:id file))]
|
||||
(:id file)
|
||||
true)]
|
||||
|
||||
(swap! idmap assoc label (:id component-shape))
|
||||
(-> file-data
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
|
||||
(t/is (= (:name p-group) "Group1"))
|
||||
(t/is (ctk/instance-of? p-group file-id (:id component1)))
|
||||
(t/is (not (:main-instance? p-group)))
|
||||
(t/is (not (ctk/is-main-instance? (:id p-group) file-page-id component1)))
|
||||
(t/is (ctk/is-main-of? c-group1 p-group))
|
||||
|
||||
|
|
|
@ -179,7 +179,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.confirm-dialog {
|
||||
.confirm-dialog,
|
||||
.alert-dialog {
|
||||
background-color: $color-white;
|
||||
|
||||
p {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.sentry :as sentry]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui :as ui]
|
||||
[app.main.ui.alert]
|
||||
[app.main.ui.confirm]
|
||||
[app.main.ui.modal :refer [modal]]
|
||||
[app.main.ui.routes :as rt]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.main.data.fonts :as df]
|
||||
[app.main.data.media :as di]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
|
@ -718,12 +719,13 @@
|
|||
(-deref [_] {:project-id project-id})
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(watch [it state _]
|
||||
(let [{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)
|
||||
name (name (gensym (str (tr "dashboard.new-file-prefix") " ")))
|
||||
params (assoc params :name name)]
|
||||
components-v2 (features/active-feature? state :components-v2)
|
||||
params (assoc params :name name :components-v2 components-v2)]
|
||||
|
||||
(->> (rp/mutation! :create-file params)
|
||||
(rx/tap on-success)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.globals :as ug]
|
||||
[app.util.router :as rt]
|
||||
|
@ -100,9 +101,15 @@
|
|||
(us/assert ::fetch-bundle-params params)
|
||||
(ptk/reify ::fetch-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [params' (cond-> {:file-id file-id}
|
||||
(uuid? share-id) (assoc :share-id share-id))]
|
||||
(watch [_ state _]
|
||||
(let [components-v2 (features/active-feature? state :components-v2)
|
||||
params' (cond-> {:file-id file-id}
|
||||
(uuid? share-id)
|
||||
(assoc :share-id share-id)
|
||||
|
||||
:always
|
||||
(assoc :components-v2 components-v2))]
|
||||
|
||||
(->> (rp/query :view-only-bundle params')
|
||||
(rx/mapcat
|
||||
(fn [{:keys [fonts] :as bundle}]
|
||||
|
|
|
@ -192,6 +192,7 @@
|
|||
process-page-changes
|
||||
(fn [[page-id _changes]]
|
||||
(update-indices page-id redo-changes))]
|
||||
|
||||
(rx/concat
|
||||
(rx/from (map process-page-changes changes-by-pages))
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
|
@ -283,7 +284,7 @@
|
|||
|
||||
(defn- add-component2
|
||||
"This is the second step of the component creation."
|
||||
[selected]
|
||||
[selected components-v2]
|
||||
(ptk/reify ::add-component2
|
||||
IDeref
|
||||
(-deref [_] {:num-shapes (count selected)})
|
||||
|
@ -296,7 +297,7 @@
|
|||
shapes (dwg/shapes-for-grouping objects selected)]
|
||||
(when-not (empty? shapes)
|
||||
(let [[group _ changes]
|
||||
(dwlh/generate-add-component it shapes objects page-id file-id)]
|
||||
(dwlh/generate-add-component it shapes objects page-id file-id components-v2)]
|
||||
(when-not (empty? (:redo-changes changes))
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dws/select-shapes (d/ordered-set (:id group)))))))))))
|
||||
|
@ -310,10 +311,11 @@
|
|||
(ptk/reify ::add-component
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (->> (wsh/lookup-selected state)
|
||||
(cph/clean-loops objects))]
|
||||
(rx/of (add-component2 selected))))))
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (->> (wsh/lookup-selected state)
|
||||
(cph/clean-loops objects))
|
||||
components-v2 (features/active-feature? state :components-v2)]
|
||||
(rx/of (add-component2 selected components-v2))))))
|
||||
|
||||
(defn rename-component
|
||||
"Rename the component with the given id, in the current file library."
|
||||
|
@ -357,8 +359,12 @@
|
|||
unames (into #{} (map :name) all-components)
|
||||
new-name (ctst/generate-unique-name unames (:name component))
|
||||
|
||||
main-instance-page (wsh/lookup-page state (:main-instance-page component))
|
||||
main-instance-shape (ctn/get-shape main-instance-page (:main-instance-id component))
|
||||
components-v2 (features/active-feature? state :components-v2)
|
||||
|
||||
main-instance-page (when components-v2
|
||||
(wsh/lookup-page state (:main-instance-page component)))
|
||||
main-instance-shape (when components-v2
|
||||
(ctn/get-shape main-instance-page (:main-instance-id component)))
|
||||
|
||||
[new-component-shape new-component-shapes
|
||||
new-main-instance-shape new-main-instance-shapes]
|
||||
|
@ -606,7 +612,11 @@
|
|||
"Synchronize the given file from the given library. Walk through all
|
||||
shapes in all pages in the file that use some color, typography or
|
||||
component of the library, and copy the new values to the shapes. Do
|
||||
it also for shapes inside components of the local file library."
|
||||
it also for shapes inside components of the local file library.
|
||||
|
||||
If it's known that only one asset has changed, you can give its
|
||||
type and id, and only shapes that use it will be synced, thus avoiding
|
||||
a lot of unneeded checks."
|
||||
([file-id library-id]
|
||||
(sync-file file-id library-id nil nil))
|
||||
([file-id library-id asset-type asset-id]
|
||||
|
@ -752,8 +762,10 @@
|
|||
[]
|
||||
(ptk/reify ::watch-component-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(let [stopper
|
||||
(watch [_ state stream]
|
||||
(let [components-v2 (features/active-feature? state :components-v2)
|
||||
|
||||
stopper
|
||||
(->> stream
|
||||
(rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %))
|
||||
(= ::watch-component-changes (ptk/type %)))))
|
||||
|
@ -775,16 +787,16 @@
|
|||
components-changed (reduce #(into %1 (ch/components-changed data %2))
|
||||
#{}
|
||||
changes)]
|
||||
(js/console.log "components-changed" (clj->js components-changed))
|
||||
(when (d/not-empty? components-changed)
|
||||
(apply st/emit!
|
||||
(map #(update-component-sync % (:id data))
|
||||
components-changed)))))]
|
||||
|
||||
(->> change-str
|
||||
(rx/with-latest-from workspace-data-str)
|
||||
(rx/map check-changes)
|
||||
(rx/take-until stopper))))))
|
||||
(when components-v2
|
||||
(->> change-str
|
||||
(rx/with-latest-from workspace-data-str)
|
||||
(rx/map check-changes)
|
||||
(rx/take-until stopper)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Backend interactions
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
"If there is exactly one id, and it's a group, use it as root. Otherwise,
|
||||
create a group that contains all ids. Then, make a component with it,
|
||||
and link all shapes to their corresponding one in the component."
|
||||
[it shapes objects page-id file-id]
|
||||
[it shapes objects page-id file-id components-v2]
|
||||
(if (and (= (count shapes) 1)
|
||||
(:component-id (first shapes)))
|
||||
[(first shapes) (pcb/empty-changes it)]
|
||||
|
@ -77,7 +77,7 @@
|
|||
(dwg/prepare-create-group it objects page-id shapes name true))
|
||||
|
||||
[new-shape new-shapes updated-shapes]
|
||||
(ctn/make-component-shape group objects file-id)
|
||||
(ctn/make-component-shape group objects file-id components-v2)
|
||||
|
||||
changes (-> changes
|
||||
(pcb/add-component (:id new-shape)
|
||||
|
@ -106,13 +106,14 @@
|
|||
|
||||
|
||||
[new-instance-shape new-instance-shapes]
|
||||
(ctn/make-component-instance main-instance-page
|
||||
{:id (:id new-component-shape)
|
||||
:name (:name new-component-shape)
|
||||
:objects (d/index-by :id new-component-shapes)}
|
||||
(:component-file main-instance-shape)
|
||||
position
|
||||
false)]
|
||||
(when (and (some? main-instance-page) (some? main-instance-shape))
|
||||
(ctn/make-component-instance main-instance-page
|
||||
{:id (:id new-component-shape)
|
||||
:name (:name new-component-shape)
|
||||
:objects (d/index-by :id new-component-shapes)}
|
||||
(:component-file main-instance-shape)
|
||||
position
|
||||
false))]
|
||||
|
||||
[new-component-shape new-component-shapes
|
||||
new-instance-shape new-instance-shapes]))
|
||||
|
@ -254,7 +255,6 @@
|
|||
(and (if (nil? component-id)
|
||||
(ctk/uses-library-components? shape library-id)
|
||||
(ctk/instance-of? shape library-id component-id))
|
||||
(not (:main-instance? shape)) ; not need to sync the main instance (avoid infinite loop)
|
||||
(or (:component-root? shape) (not page?)))) ; avoid nested components inside pages
|
||||
|
||||
(defmethod uses-assets? :colors
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
[app.config :as cf]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.thumbnails :as dwt]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.util.http :as http]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[beicon.core :as rx]
|
||||
|
@ -124,8 +127,7 @@
|
|||
(rx/map persist-synchronous-changes)
|
||||
(rx/take-until (rx/delay 100 stoper))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: synchronous save loop"))))
|
||||
)))))
|
||||
(log/debug :hint "finalize persistence: synchronous save loop")))))))))
|
||||
|
||||
(defn persist-changes
|
||||
[file-id changes]
|
||||
|
@ -134,12 +136,14 @@
|
|||
(ptk/reify ::persist-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [sid (:session-id state)
|
||||
file (get state :workspace-file)
|
||||
params {:id (:id file)
|
||||
:revn (:revn file)
|
||||
:session-id sid
|
||||
:changes-with-metadata (into [] changes)}]
|
||||
(let [components-v2 (features/active-feature? state :components-v2)
|
||||
sid (:session-id state)
|
||||
file (get state :workspace-file)
|
||||
params {:id (:id file)
|
||||
:revn (:revn file)
|
||||
:session-id sid
|
||||
:changes-with-metadata (into [] changes)
|
||||
:components-v2 components-v2}]
|
||||
|
||||
(when (= file-id (:id params))
|
||||
(->> (rp/mutation :update-file params)
|
||||
|
@ -175,13 +179,15 @@
|
|||
(ptk/reify ::persist-synchronous-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [sid (:session-id state)
|
||||
(let [components-v2 (features/active-feature? state :components-v2)
|
||||
sid (:session-id state)
|
||||
file (get-in state [:workspace-libraries file-id])
|
||||
|
||||
params {:id (:id file)
|
||||
:revn (:revn file)
|
||||
:session-id sid
|
||||
:changes changes}]
|
||||
:changes changes
|
||||
:components-v2 components-v2}]
|
||||
|
||||
(when (:id params)
|
||||
(->> (rp/mutation :update-file params)
|
||||
|
@ -261,8 +267,9 @@
|
|||
(ptk/reify ::fetch-bundle
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [share-id (-> state :viewer-local :share-id)]
|
||||
(->> (rx/zip (rp/query :file-raw {:id file-id})
|
||||
(let [share-id (-> state :viewer-local :share-id)
|
||||
components-v2 (features/active-feature? state :components-v2)]
|
||||
(->> (rx/zip (rp/query :file-raw {:id file-id :components-v2 components-v2})
|
||||
(rp/query :team-users {:file-id file-id})
|
||||
(rp/query :project {:id project-id})
|
||||
(rp/query :file-libraries {:file-id file-id})
|
||||
|
@ -276,8 +283,16 @@
|
|||
:file-comments-users file-comments-users}))
|
||||
(rx/mapcat (fn [{:keys [project] :as bundle}]
|
||||
(rx/of (ptk/data-event ::bundle-fetched bundle)
|
||||
(df/load-team-fonts (:team-id project))))))))))
|
||||
|
||||
(df/load-team-fonts (:team-id project)))))
|
||||
(rx/catch (fn [err]
|
||||
(if (and (= (:type err) :restriction)
|
||||
(= (:code err) :feature-disabled))
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/of (modal/show
|
||||
{:type :alert
|
||||
:message (tr "errors.components-v2")
|
||||
:on-accept #(st/emit! (rt/nav :dashboard-projects {:team-id team-id}))})))
|
||||
(rx/throw err)))))))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
|
@ -24,6 +23,7 @@
|
|||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.features :as features]
|
||||
[app.main.streams :as ms]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
|
@ -148,7 +148,7 @@
|
|||
ids (cph/clean-loops objects ids)
|
||||
lookup (d/getf objects)
|
||||
|
||||
local-library {file-id {:data file}}
|
||||
components-v2 (features/active-feature? state :components-v2)
|
||||
|
||||
groups-to-unmask
|
||||
(reduce (fn [group-ids id]
|
||||
|
@ -221,23 +221,16 @@
|
|||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||
|
||||
components-to-delete
|
||||
(reduce (fn [components id]
|
||||
(let [shape (get objects id)
|
||||
|
||||
component
|
||||
(when (and (:component-id shape) (:component-file shape))
|
||||
;; Only local components may have main instances
|
||||
(cph/get-component local-library (:component-file shape) (:component-id shape)))
|
||||
|
||||
main-instance?
|
||||
(when component
|
||||
(ctk/is-main-instance? (:id shape) (:id page) component))]
|
||||
|
||||
(if main-instance?
|
||||
(conj components (:component-id shape))
|
||||
components)))
|
||||
[]
|
||||
(into ids all-children))
|
||||
(if components-v2
|
||||
(reduce (fn [components id]
|
||||
(let [shape (get objects id)]
|
||||
(if (and (= (:component-file shape) file-id) ;; Main instances should exist only in local file
|
||||
(:main-instance? shape)) ;; but check anyway
|
||||
(conj components (:component-id shape))
|
||||
components)))
|
||||
[]
|
||||
(into ids all-children))
|
||||
[])
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-page page)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.features
|
||||
(ns app.main.features
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as log]
|
||||
|
@ -15,9 +15,9 @@
|
|||
|
||||
(log/set-level! :debug)
|
||||
|
||||
(def features-list #{:auto-layout})
|
||||
(def features-list #{:auto-layout :components-v2})
|
||||
|
||||
(defn toggle-feature
|
||||
(defn- toggle-feature
|
||||
[feature]
|
||||
(ptk/reify ::toggle-feature
|
||||
ptk/UpdateEvent
|
||||
|
@ -41,6 +41,13 @@
|
|||
(assert (contains? features-list feature) "Not supported feature")
|
||||
(st/emit! (toggle-feature feature)))
|
||||
|
||||
(defn active-feature?
|
||||
([feature]
|
||||
(active-feature? @st/state feature))
|
||||
([state feature]
|
||||
(assert (contains? features-list feature) "Not supported feature")
|
||||
(contains? (get state :features) feature)))
|
||||
|
||||
(def features
|
||||
(l/derived :features st/state))
|
||||
|
78
frontend/src/app/main/ui/alert.cljs
Normal file
78
frontend/src/app/main/ui/alert.cljs
Normal file
|
@ -0,0 +1,78 @@
|
|||
;; 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.alert
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr t]]
|
||||
[app.util.keyboard :as k]
|
||||
[goog.events :as events]
|
||||
[rumext.alpha :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(mf/defc alert-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :alert}
|
||||
[{:keys [message
|
||||
scd-message
|
||||
title
|
||||
on-accept
|
||||
hint
|
||||
accept-label
|
||||
accept-style] :as props}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
|
||||
on-accept (or on-accept identity)
|
||||
message (or message (t locale "ds.alert-title"))
|
||||
accept-label (or accept-label (tr "ds.alert-ok"))
|
||||
accept-style (or accept-style :danger)
|
||||
title (or title (t locale "ds.alert-title"))
|
||||
|
||||
accept-fn
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (modal/hide))
|
||||
(on-accept props)))]
|
||||
|
||||
(mf/with-effect
|
||||
(letfn [(on-keydown [event]
|
||||
(when (k/enter? event)
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (modal/hide))
|
||||
(on-accept props)))]
|
||||
(->> (events/listen js/document EventType.KEYDOWN on-keydown)
|
||||
(partial events/unlistenByKey))))
|
||||
|
||||
[:div.modal-overlay
|
||||
[:div.modal-container.alert-dialog
|
||||
[:div.modal-header
|
||||
[:div.modal-header-title
|
||||
[:h2 title]]
|
||||
[:div.modal-close-button
|
||||
{:on-click accept-fn} i/close]]
|
||||
|
||||
[:div.modal-content
|
||||
(when (and (string? message) (not= message ""))
|
||||
[:h3 message])
|
||||
(when (and (string? scd-message) (not= scd-message ""))
|
||||
[:h3 scd-message])
|
||||
(when (string? hint)
|
||||
[:p hint])]
|
||||
|
||||
[:div.modal-footer
|
||||
[:div.action-buttons
|
||||
[:input.accept-button
|
||||
{:class (dom/classnames
|
||||
:danger (= accept-style :danger)
|
||||
:primary (= accept-style :primary))
|
||||
:type "button"
|
||||
:value accept-label
|
||||
:on-click accept-fn}]]]]]))
|
|
@ -25,3 +25,4 @@
|
|||
(def scroll-ctx (mf/create-context nil))
|
||||
(def active-frames-ctx (mf/create-context nil))
|
||||
(def render-thumbnails (mf/create-context nil))
|
||||
(def components-v2 (mf/create-context nil))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.math :as mth]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.features :as features]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -36,9 +37,11 @@
|
|||
(defn ask-for-thumbnail
|
||||
"Creates some hooks to handle the files thumbnails cache"
|
||||
[file]
|
||||
(wrk/ask! {:cmd :thumbnails/generate
|
||||
:revn (:revn file)
|
||||
:file-id (:id file)}))
|
||||
(let [components-v2 (features/active-feature? :components-v2)]
|
||||
(wrk/ask! {:cmd :thumbnails/generate
|
||||
:revn (:revn file)
|
||||
:file-id (:id file)
|
||||
:components-v2 components-v2})))
|
||||
|
||||
(mf/defc grid-item-thumbnail
|
||||
{::mf/wrap [mf/memo]}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.main.data.messages :as msg]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
|
@ -114,13 +115,12 @@
|
|||
(mf/defc workspace
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [project-id file-id page-id layout-name] :as props}]
|
||||
(let [file (mf/deref refs/workspace-file)
|
||||
project (mf/deref refs/workspace-project)
|
||||
layout (mf/deref refs/workspace-layout)
|
||||
wglobal (mf/deref refs/workspace-global)
|
||||
wglobal (mf/deref refs/workspace-global)
|
||||
libraries (mf/deref refs/workspace-libraries)
|
||||
local-library (mf/deref refs/workspace-local-library)
|
||||
(let [file (mf/deref refs/workspace-file)
|
||||
project (mf/deref refs/workspace-project)
|
||||
layout (mf/deref refs/workspace-layout)
|
||||
wglobal (mf/deref refs/workspace-global)
|
||||
|
||||
components-v2 (features/use-feature :components-v2)
|
||||
|
||||
background-color (:background-color wglobal)]
|
||||
|
||||
|
@ -148,9 +148,7 @@
|
|||
[:& (mf/provider ctx/current-team-id) {:value (:team-id project)}
|
||||
[:& (mf/provider ctx/current-project-id) {:value (:id project)}
|
||||
[:& (mf/provider ctx/current-page-id) {:value page-id}
|
||||
[:& (mf/provider ctx/libraries) {:value (assoc libraries
|
||||
(:id local-library)
|
||||
{:data local-library})}
|
||||
[:& (mf/provider ctx/components-v2) {:value components-v2}
|
||||
[:section#workspace {:style {:background-color background-color}}
|
||||
(when (not (:hide-ui layout))
|
||||
[:& header {:file file
|
||||
|
@ -169,5 +167,3 @@
|
|||
:layout layout}]
|
||||
[:& workspace-loader])]]]]]]))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.collapse :as dwc]
|
||||
|
@ -87,7 +86,7 @@
|
|||
(when (seq (:touched shape)) " *")])))
|
||||
|
||||
(mf/defc layer-item
|
||||
[{:keys [index page item selected objects] :as props}]
|
||||
[{:keys [index item selected objects] :as props}]
|
||||
(let [id (:id item)
|
||||
blocked? (:blocked item)
|
||||
hidden? (:hidden item)
|
||||
|
@ -103,11 +102,10 @@
|
|||
container? (or (cph/frame-shape? item)
|
||||
(cph/group-shape? item))
|
||||
|
||||
libraries (mf/use-ctx ctx/libraries)
|
||||
component (when (and (:component-id item) (:component-file item))
|
||||
(cph/get-component libraries (:component-file item) (:component-id item)))
|
||||
main-instance? (when component
|
||||
(ctk/is-main-instance? (:id item) (:id page) component))
|
||||
components-v2 (mf/use-ctx ctx/components-v2)
|
||||
main-instance? (if components-v2
|
||||
(:main-instance? item)
|
||||
true)
|
||||
|
||||
toggle-collapse
|
||||
(mf/use-fn
|
||||
|
@ -277,8 +275,7 @@
|
|||
(for [[index id] (reverse (d/enumerate (:shapes item)))]
|
||||
(when-let [item (get objects id)]
|
||||
[:& layer-item
|
||||
{:page page
|
||||
:item item
|
||||
{:item item
|
||||
:selected selected
|
||||
:index index
|
||||
:objects objects
|
||||
|
@ -299,9 +296,8 @@
|
|||
{::mf/wrap [#(mf/memo % =)
|
||||
#(mf/throttle % 200)]}
|
||||
[{:keys [objects] :as props}]
|
||||
(let [page (mf/deref refs/workspace-page)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
selected (hooks/use-equal-memo selected)
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
selected (hooks/use-equal-memo selected)
|
||||
root (get objects uuid/zero)]
|
||||
[:ul.element-list
|
||||
[:& hooks/sortable-container {}
|
||||
|
@ -315,8 +311,7 @@
|
|||
:objects objects
|
||||
:key id}]
|
||||
[:& layer-item
|
||||
{:page page
|
||||
:item obj
|
||||
{:item obj
|
||||
:selected selected
|
||||
:index index
|
||||
:objects objects
|
||||
|
@ -326,16 +321,14 @@
|
|||
{::mf/wrap [#(mf/memo % =)
|
||||
#(mf/throttle % 200)]}
|
||||
[{:keys [objects] :as props}]
|
||||
(let [page (mf/deref refs/workspace-page)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
(let [selected (mf/deref refs/selected-shapes)
|
||||
selected (hooks/use-equal-memo selected)
|
||||
root (get objects uuid/zero)]
|
||||
[:ul.element-list
|
||||
(for [[index id] (d/enumerate (:shapes root))]
|
||||
(when-let [obj (get objects id)]
|
||||
[:& layer-item
|
||||
{:page page
|
||||
:item obj
|
||||
{:item obj
|
||||
:selected selected
|
||||
:index index
|
||||
:objects objects
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.component
|
||||
(:require
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
|
@ -19,13 +17,12 @@
|
|||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(def component-attrs [:component-id :component-file :shape-ref])
|
||||
(def component-attrs [:component-id :component-file :shape-ref :main-instance?])
|
||||
|
||||
(mf/defc component-menu
|
||||
[{:keys [ids values shape-name] :as props}]
|
||||
(let [current-file-id (mf/use-ctx ctx/current-file-id)
|
||||
current-page-id (mf/use-ctx ctx/current-page-id)
|
||||
libraries (mf/use-ctx ctx/libraries)
|
||||
components-v2 (mf/use-ctx ctx/components-v2)
|
||||
|
||||
id (first ids)
|
||||
local (mf/use-state {:menu-open false})
|
||||
|
@ -33,10 +30,9 @@
|
|||
component-id (:component-id values)
|
||||
library-id (:component-file values)
|
||||
show? (some? component-id)
|
||||
|
||||
component (when (and component-id library-id)
|
||||
(cph/get-component libraries library-id component-id))
|
||||
main-instance? (ctk/is-main-instance? id current-page-id component)
|
||||
main-instance? (if components-v2
|
||||
(:main-instance? values)
|
||||
true)
|
||||
|
||||
on-menu-click
|
||||
(mf/use-callback
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
(ns app.main.ui.workspace.sidebar.options.shapes.frame
|
||||
(:require
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.features :as features]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.common.uri :as u]
|
||||
[app.config :as cf]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.features :as features]
|
||||
[app.main.render :as render]
|
||||
[app.main.repo :as repo]
|
||||
[app.main.store :as st]
|
||||
|
@ -99,22 +100,24 @@
|
|||
|
||||
(mf/defc object-svg
|
||||
[{:keys [page-id file-id object-id render-embed?]}]
|
||||
(let [fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id object-id)
|
||||
(fn []
|
||||
(->> (rx/zip
|
||||
(repo/query! :font-variants {:file-id file-id})
|
||||
(repo/query! :page {:file-id file-id
|
||||
:page-id page-id
|
||||
:object-id object-id}))
|
||||
(rx/tap (fn [[fonts]]
|
||||
(when (seq fonts)
|
||||
(st/emit! (df/fonts-fetched fonts)))))
|
||||
(rx/map (comp :objects second))
|
||||
(rx/map (fn [objects]
|
||||
(let [objects (render/adapt-objects-for-shape objects object-id)]
|
||||
{:objects objects
|
||||
:object (get objects object-id)}))))))
|
||||
(let [components-v2 (features/use-feature :components-v2)
|
||||
fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id object-id)
|
||||
(fn []
|
||||
(->> (rx/zip
|
||||
(repo/query! :font-variants {:file-id file-id})
|
||||
(repo/query! :page {:file-id file-id
|
||||
:page-id page-id
|
||||
:object-id object-id
|
||||
:components-v2 components-v2}))
|
||||
(rx/tap (fn [[fonts]]
|
||||
(when (seq fonts)
|
||||
(st/emit! (df/fonts-fetched fonts)))))
|
||||
(rx/map (comp :objects second))
|
||||
(rx/map (fn [objects]
|
||||
(let [objects (render/adapt-objects-for-shape objects object-id)]
|
||||
{:objects objects
|
||||
:object (get objects object-id)}))))))
|
||||
|
||||
{:keys [objects object]} (use-resource fetch-state)]
|
||||
|
||||
|
@ -124,8 +127,8 @@
|
|||
(when object
|
||||
(dom/set-page-style!
|
||||
{:size (str/concat
|
||||
(mth/ceil (:width object)) "px "
|
||||
(mth/ceil (:height object)) "px")})))
|
||||
(mth/ceil (:width object)) "px "
|
||||
(mth/ceil (:height object)) "px")})))
|
||||
|
||||
(when objects
|
||||
[:& render/object-svg
|
||||
|
@ -135,17 +138,19 @@
|
|||
|
||||
(mf/defc objects-svg
|
||||
[{:keys [page-id file-id object-ids render-embed?]}]
|
||||
(let [fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id)
|
||||
(fn []
|
||||
(->> (rx/zip
|
||||
(repo/query! :font-variants {:file-id file-id})
|
||||
(repo/query! :page {:file-id file-id
|
||||
:page-id page-id}))
|
||||
(rx/tap (fn [[fonts]]
|
||||
(when (seq fonts)
|
||||
(st/emit! (df/fonts-fetched fonts)))))
|
||||
(rx/map (comp :objects second)))))
|
||||
(let [components-v2 (features/use-feature :components-v2)
|
||||
fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id)
|
||||
(fn []
|
||||
(->> (rx/zip
|
||||
(repo/query! :font-variants {:file-id file-id})
|
||||
(repo/query! :page {:file-id file-id
|
||||
:page-id page-id
|
||||
:components-v2 components-v2}))
|
||||
(rx/tap (fn [[fonts]]
|
||||
(when (seq fonts)
|
||||
(st/emit! (df/fonts-fetched fonts)))))
|
||||
(rx/map (comp :objects second)))))
|
||||
|
||||
objects (use-resource fetch-state)]
|
||||
|
||||
|
|
|
@ -48,11 +48,12 @@
|
|||
(= :request-body-too-large code)))
|
||||
|
||||
(defn- request-data-for-thumbnail
|
||||
[file-id revn]
|
||||
[file-id revn components-v2]
|
||||
(let [path "api/rpc/query/file-data-for-thumbnail"
|
||||
params {:file-id file-id
|
||||
:revn revn
|
||||
:strip-frames-with-thumbnails true}
|
||||
:strip-frames-with-thumbnails true
|
||||
:components-v2 components-v2}
|
||||
request {:method :get
|
||||
:uri (u/join (cfg/get-public-uri) path)
|
||||
:credentials "include"
|
||||
|
@ -107,18 +108,18 @@
|
|||
(rx/map (constantly params)))))
|
||||
|
||||
(defmethod impl/handler :thumbnails/generate
|
||||
[{:keys [file-id revn] :as message}]
|
||||
[{:keys [file-id revn components-v2] :as message}]
|
||||
(letfn [(on-result [{:keys [data props]}]
|
||||
{:data data
|
||||
:fonts (:fonts props)})
|
||||
|
||||
(on-cache-miss [_]
|
||||
(->> (request-data-for-thumbnail file-id revn)
|
||||
(->> (request-data-for-thumbnail file-id revn components-v2)
|
||||
(rx/map render-thumbnail)
|
||||
(rx/mapcat persist-thumbnail)))]
|
||||
|
||||
(if (debug? :disable-thumbnail-cache)
|
||||
(->> (request-data-for-thumbnail file-id revn)
|
||||
(->> (request-data-for-thumbnail file-id revn components-v2)
|
||||
(rx/map render-thumbnail))
|
||||
(->> (request-thumbnail file-id revn)
|
||||
(rx/catch not-found? on-cache-miss)
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
;; This namespace is only to export the functions for toggle features
|
||||
(ns features
|
||||
(:require
|
||||
[app.main.ui.features :as features]))
|
||||
[app.main.features :as features]))
|
||||
|
||||
(defn ^:export autolayout []
|
||||
(features/toggle-feature! :auto-layout))
|
||||
|
||||
(defn ^:export components-v2 []
|
||||
(features/toggle-feature! :components-v2))
|
||||
|
||||
|
|
|
@ -105,7 +105,8 @@
|
|||
shapes
|
||||
(:objects page)
|
||||
(:id page)
|
||||
current-file-id)]
|
||||
current-file-id
|
||||
true)]
|
||||
|
||||
(swap! idmap assoc instance-label (:id group)
|
||||
component-label (:id component-root))
|
||||
|
|
|
@ -639,6 +639,14 @@ msgstr "Your name"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Your Penpot"
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-title"
|
||||
msgstr "Attention"
|
||||
|
||||
#: src/app/main/ui/confirm.cljs
|
||||
msgid "ds.component-subtitle"
|
||||
msgstr "Components to update:"
|
||||
|
@ -718,6 +726,10 @@ msgstr "LDAP authentication is disabled."
|
|||
msgid "errors.media-format-unsupported"
|
||||
msgstr "The image format is not supported (must be svg, jpg or png)."
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs
|
||||
msgid "errors.components-v2"
|
||||
msgstr "This file has already used with Components V2 enabled."
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs
|
||||
msgid "errors.media-too-large"
|
||||
msgstr "The image is too large to be inserted (must be under 5mb)."
|
||||
|
|
|
@ -656,6 +656,14 @@ msgstr "Tu nombre"
|
|||
msgid "dashboard.your-penpot"
|
||||
msgstr "Tu Penpot"
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-ok"
|
||||
msgstr "Ok"
|
||||
|
||||
#: src/app/main/ui/alert.cljs
|
||||
msgid "ds.alert-title"
|
||||
msgstr "Atención"
|
||||
|
||||
#: src/app/main/ui/confirm.cljs
|
||||
msgid "ds.component-subtitle"
|
||||
msgstr "Componentes a actualizar:"
|
||||
|
@ -736,6 +744,10 @@ msgstr "La autheticacion via LDAP esta deshabilitada."
|
|||
msgid "errors.media-format-unsupported"
|
||||
msgstr "No se reconoce el formato de imagen (debe ser svg, jpg o png)."
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs
|
||||
msgid "errors.components-v2"
|
||||
msgstr "Este fichero ya se ha usado con los Componentes V2 activados."
|
||||
|
||||
#: src/app/main/data/workspace/persistence.cljs
|
||||
msgid "errors.media-too-large"
|
||||
msgstr "La imagen es demasiado grande (debe tener menos de 5mb)."
|
||||
|
|
Loading…
Add table
Reference in a new issue