0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-19 11:11:21 -05:00

🎉 Make components-v2 an optional feature

This commit is contained in:
Andrés Moya 2022-07-12 13:52:48 +02:00
parent 1ef37281e6
commit a5bf1c03e7
40 changed files with 495 additions and 296 deletions

View file

@ -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)

View file

@ -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)))

View 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))]

View file

@ -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))

View file

@ -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)

View file

@ -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}))))

View file

@ -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

View file

@ -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)))

View file

@ -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)

View file

@ -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.

View file

@ -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]

View file

@ -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))

View file

@ -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

View file

@ -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"

View file

@ -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]))

View file

@ -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

View file

@ -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))

View file

@ -179,7 +179,8 @@
}
}
.confirm-dialog {
.confirm-dialog,
.alert-dialog {
background-color: $color-white;
p {

View file

@ -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]

View file

@ -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)

View file

@ -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}]

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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))

View 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}]]]]]))

View file

@ -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))

View file

@ -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]}

View file

@ -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])]]]]]]))

View file

@ -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

View file

@ -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

View file

@ -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]]

View file

@ -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)]

View file

@ -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)

View file

@ -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))

View file

@ -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))

View file

@ -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)."

View file

@ -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)."