Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2024-01-15 10:10:35 +01:00
commit 8a81bc11e0
19 changed files with 201 additions and 118 deletions

View file

@ -14,6 +14,7 @@
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.db.sql :as sql] [app.db.sql :as sql]
[app.features.components-v2 :as feat.compv2]
[app.features.fdata :as fdata] [app.features.fdata :as fdata]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
@ -46,22 +47,26 @@
[cfg {:keys [::rpc/profile-id project-id] :as params}] [cfg {:keys [::rpc/profile-id project-id] :as params}]
(db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
(projects/check-edition-permissions! conn profile-id project-id) (projects/check-edition-permissions! conn profile-id project-id)
(let [team (teams/get-team conn (let [team (teams/get-team conn :profile-id profile-id :project-id project-id)
:profile-id profile-id
:project-id project-id)
;; When we create files, we only need to respect the team ;; When we create files, we only need to respect the team
;; features, because some features can be enabled ;; features, because some features can be enabled
;; globally, but the team is still not migrated properly. ;; globally, but the team is still not migrated properly.
features (-> (cfeat/get-team-enabled-features cf/flags team) input-features (:features params #{})
(cfeat/check-client-features! (:features params)))
;; If the imported project doesn't contain v2 we need to remove it
team-features
(cond-> (cfeat/get-team-enabled-features cf/flags team)
(not (contains? input-features "components/v2"))
(disj "components/v2"))
;; We also include all no migration features declared by ;; We also include all no migration features declared by
;; client; that enables the ability to enable a runtime ;; client; that enables the ability to enable a runtime
;; feature on frontend and make it permanent on file ;; feature on frontend and make it permanent on file
features (-> (:features params #{}) features (-> input-features
(set/intersection cfeat/no-migration-features) (set/intersection cfeat/no-migration-features)
(set/union features)) (set/union team-features))
params (-> params params (-> params
(assoc :profile-id profile-id) (assoc :profile-id profile-id)
@ -100,7 +105,7 @@
;; --- MUTATION COMMAND: persist-temp-file ;; --- MUTATION COMMAND: persist-temp-file
(defn persist-temp-file (defn persist-temp-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as params}] [{:keys [::db/conn] :as cfg} {:keys [id ::rpc/profile-id] :as params}]
(let [file (files/get-file cfg id (let [file (files/get-file cfg id
:migrate? false :migrate? false
:lock-for-update? true)] :lock-for-update? true)]
@ -109,6 +114,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :cant-persist-already-persisted-file)) :code :cant-persist-already-persisted-file))
(let [changes (->> (db/cursor conn (let [changes (->> (db/cursor conn
(sql/select :file-change {:file-id id} (sql/select :file-change {:file-id id}
{:order-by [[:revn :asc]]}) {:order-by [[:revn :asc]]})
@ -133,6 +139,19 @@
:revn 1 :revn 1
:data (blob/encode (:data file))} :data (blob/encode (:data file))}
{:id id}) {:id id})
(let [team (teams/get-team conn :profile-id profile-id :project-id (:project-id file))
file-features (:features file)
team-features (cfeat/get-team-enabled-features cf/flags team)]
(when (and (contains? team-features "components/v2")
(not (contains? file-features "components/v2")))
;; Migrate components v2
(feat.compv2/migrate-file! cfg
(:id file)
:max-procs 2
:validate? true
:throw-on-validate? true)))
nil))) nil)))
(def ^:private schema:persist-temp-file (def ^:private schema:persist-temp-file

View file

@ -524,7 +524,10 @@
(dissoc :main-instance-y)) (dissoc :main-instance-y))
obj (-> (cts/setup-shape attrs) obj (-> (cts/setup-shape attrs)
(check-name file root-type))] (check-name file root-type)
;; Components need to have nil values for frame and parent
(assoc :frame-id nil)
(assoc :parent-id nil))]
(-> file (-> file
(commit-change (commit-change
@ -537,19 +540,24 @@
:shapes [obj]}) :shapes [obj]})
(assoc :last-id (:id obj)) (assoc :last-id (:id obj))
(update :parent-stack conjv (:id obj)) (assoc :parent-stack [(:id obj)])
(assoc :current-component-id (:id obj)) (assoc :current-component-id (:id obj))
(assoc :current-frame-id (when (= (:type obj) :frame) (assoc :current-frame-id (if (= (:type obj) :frame) (:id obj) uuid/zero))))))
(:id obj)))))))
(defn finish-component (defn finish-component
[file] [file]
(let [component-id (:current-component-id file) (let [component-id (:current-component-id file)
component-data (ctkl/get-component (:data file) component-id)
component (lookup-shape file component-id) component (lookup-shape file component-id)
children (->> component :shapes (mapv #(lookup-shape file %))) children (->> component :shapes (mapv #(lookup-shape file %)))
file file
(cond (cond
;; Components-v2 component we skip this step
(and component-data (:main-instance-id component-data))
file
(empty? children) (empty? children)
(commit-change (commit-change
file file

View file

@ -406,10 +406,9 @@
height: $s-16; height: $s-16;
min-width: $s-16; min-width: $s-16;
min-height: $s-16; min-height: $s-16;
border-radius: $br-6; background-color: var(--input-checkbox-background-color-rest);
background-color: var(--input-checkbox-inactive-background-color); border: $s-1 solid var(--input-checkbox-border-color-rest);
border-radius: $br-4; border-radius: $br-4;
box-shadow: inset 0 0 0px 2px rgb(255 255 255 / 20%);
svg { svg {
width: $s-16; width: $s-16;
height: $s-16; height: $s-16;
@ -422,22 +421,25 @@
&:focus { &:focus {
border-color: var(--input-checkbox-border-color-focus); border-color: var(--input-checkbox-border-color-focus);
} }
&:global(.checked) { &:global(.checked) {
border-color: var(--input-checkbox-border-color-active); border-color: var(--input-checkbox-border-color-active);
background-color: var(--input-checkbox-active-background-color); background-color: var(--input-checkbox-background-color-active);
svg { svg {
@extend .button-icon-small; @extend .button-icon-small;
stroke: var(--input-checkbox-active-foreground-color); stroke: var(--input-checkbox-foreground-color-active);
} }
} }
&:global(.intermediate) { &:global(.intermediate) {
background-color: var(--input-checkbox-background-color-intermediate); background-color: var(--input-checkbox-background-color-intermediate);
border-color: var(--input-checkbox-border-color-active); border-color: var(--input-checkbox-border-color-intermediate);
svg { svg {
@extend .button-icon-small; @extend .button-icon-small;
stroke: var(--input-details-color); stroke: var(--input-checkbox-foreground-color-intermediate);
} }
} }
&:global(.unchecked) { &:global(.unchecked) {
background-color: var(--input-checkbox-background-color-rest); background-color: var(--input-checkbox-background-color-rest);
border: $s-1 solid var(--input-checkbox-background-color-rest); border: $s-1 solid var(--input-checkbox-background-color-rest);
@ -468,6 +470,13 @@
border-color: var(--input-checkbox-border-color-hover); border-color: var(--input-checkbox-border-color-hover);
} }
} }
&:focus,
&:focus-within {
span {
border-color: var(--input-checkbox-border-color-focus);
}
}
} }
} }

View file

@ -159,18 +159,22 @@
--input-border-color-error: var(--error-color); --input-border-color-error: var(--error-color);
--input-border-color-success: var(--color-accent-primary); --input-border-color-success: var(--color-accent-primary);
--input-details-color: var(--color-background-primary); --input-details-color: var(--color-background-primary);
--input-checkbox-background-color-rest: var(--color-background-quaternary); --input-checkbox-background-color-rest: var(--color-background-quaternary);
--input-checkbox-border-color-rest: var(--color-foreground-secondary);
--input-checkbox-border-color-active: var(--color-background-quaternary); --input-checkbox-border-color-active: var(--color-background-quaternary);
--input-checkbox-border-color-focus: var(--color-accent-primary); --input-checkbox-border-color-focus: var(--color-accent-primary);
--input-checkbox-border-color: var(--color-background-secondary); --input-checkbox-border-color: var(--color-background-secondary);
--input-checkbox-border-color-hover: var(--color-accent-primary-muted); --input-checkbox-border-color-hover: var(--color-accent-primary-muted);
--input-checkbox-background-color-intermediate: var(--color-foreground-secondary); --input-checkbox-background-color-intermediate: var(--color-foreground-secondary);
--input-checkbox-border-color-intermediate: var(--color-foreground-secondary);
--input-checkbox-foreground-color-intermediate: var(--color-background-secondary);
// Checkbox color // Checkbox color
--input-checkbox-inactive-background-color: var(--color-foreground-secondary); --input-checkbox-inactive-background-color: var(--color-foreground-secondary);
--input-checkbox-inactive-foreground-color: var(--color-background-primary); --input-checkbox-inactive-foreground-color: var(--color-background-primary);
--input-checkbox-active-background-color: var(--color-accent-primary); --input-checkbox-background-color-active: var(--color-accent-primary);
--input-checkbox-active-foreground-color: var(--color-background-primary); --input-checkbox-foreground-color-active: var(--color-background-primary);
--input-checkbox-text-foreground-color: var(--color-foreground-secondary); --input-checkbox-text-foreground-color: var(--color-foreground-secondary);
--menu-background-color: var(--color-background-tertiary); --menu-background-color: var(--color-background-tertiary);

View file

@ -336,7 +336,7 @@
;; used to render thumbnails on assets panel. ;; used to render thumbnails on assets panel.
(mf/defc component-svg (mf/defc component-svg
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} {::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
[{:keys [objects root-shape show-grids? zoom] :or {zoom 1} :as props}] [{:keys [objects root-shape show-grids? zoom class] :or {zoom 1} :as props}]
(when root-shape (when root-shape
(let [root-shape-id (:id root-shape) (let [root-shape-id (:id root-shape)
include-metadata (mf/use-ctx export/include-metadata-ctx) include-metadata (mf/use-ctx export/include-metadata-ctx)
@ -373,6 +373,7 @@
:width (ust/format-precision width viewbox-decimal-precision) :width (ust/format-precision width viewbox-decimal-precision)
:height (ust/format-precision height viewbox-decimal-precision) :height (ust/format-precision height viewbox-decimal-precision)
:version "1.1" :version "1.1"
:class class
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
@ -388,7 +389,7 @@
(mf/defc component-svg-thumbnail (mf/defc component-svg-thumbnail
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} {::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
[{:keys [thumbnail-uri on-error show-grids? [{:keys [thumbnail-uri on-error show-grids? class
objects root-shape zoom] :or {zoom 1} :as props}] objects root-shape zoom] :or {zoom 1} :as props}]
(when root-shape (when root-shape
@ -421,6 +422,7 @@
:width (ust/format-precision width-zoom viewbox-decimal-precision) :width (ust/format-precision width-zoom viewbox-decimal-precision)
:height (ust/format-precision height-zoom viewbox-decimal-precision) :height (ust/format-precision height-zoom viewbox-decimal-precision)
:version "1.1" :version "1.1"
:class class
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:fill "none"} :fill "none"}
@ -506,7 +508,7 @@
(mf/deps objects) (mf/deps objects)
(fn [] (frame-wrapper-factory objects)))] (fn [] (frame-wrapper-factory objects)))]
[:> "symbol" #js {:id (str root-id) [:> "symbol" #js {:id (str (:id component))
:viewBox vbox :viewBox vbox
"penpot:path" path "penpot:path" path
"penpot:main-instance-id" main-instance-id "penpot:main-instance-id" main-instance-id

View file

@ -80,12 +80,13 @@
(assoc :deleted? true)))))) (assoc :deleted? true))))))
(defn set-analyze-error (defn set-analyze-error
[files uri] [files uri error]
(->> files (->> files
(mapv (fn [file] (mapv (fn [file]
(cond-> file (cond-> file
(= uri (:uri file)) (= uri (:uri file))
(assoc :status :analyze-error)))))) (-> (assoc :status :analyze-error)
(assoc :error error)))))))
(defn set-analyze-result [files uri type data] (defn set-analyze-result [files uri type data]
(let [existing-files? (into #{} (->> files (map :file-id) (filter some?))) (let [existing-files? (into #{} (->> files (map :file-id) (filter some?)))
@ -150,7 +151,6 @@
(mf/defc import-entry (mf/defc import-entry
[{:keys [state file editing? can-be-deleted?]}] [{:keys [state file editing? can-be-deleted?]}]
(let [loading? (or (= :analyzing (:status file)) (let [loading? (or (= :analyzing (:status file))
(= :importing (:status file))) (= :importing (:status file)))
analyze-error? (= :analyze-error (:status file)) analyze-error? (= :analyze-error (:status file))
@ -226,7 +226,9 @@
(cond (cond
analyze-error? analyze-error?
[:div {:class (stl/css :error-message)} [:div {:class (stl/css :error-message)}
(tr "dashboard.import.analyze-error")] (if (some? (:error file))
(tr (:error file))
(tr "dashboard.import.analyze-error"))]
import-error? import-error?
[:div {:class (stl/css :error-message)} [:div {:class (stl/css :error-message)}
@ -260,13 +262,14 @@
(fn [files] (fn [files]
(->> (uw/ask-many! (->> (uw/ask-many!
{:cmd :analyze-import {:cmd :analyze-import
:files files}) :files files
:features @features/features-ref})
(rx/mapcat #(rx/delay emit-delay (rx/of %))) (rx/mapcat #(rx/delay emit-delay (rx/of %)))
(rx/filter some?) (rx/filter some?)
(rx/subs! (rx/subs!
(fn [{:keys [uri data error type] :as msg}] (fn [{:keys [uri data error type] :as msg}]
(if (some? error) (if (some? error)
(swap! state update :files set-analyze-error uri) (swap! state update :files set-analyze-error uri error)
(swap! state update :files set-analyze-result uri type data))))))) (swap! state update :files set-analyze-result uri type data)))))))
import-files import-files

View file

@ -240,17 +240,21 @@
(tr "common.share-link.permissions-pages")] (tr "common.share-link.permissions-pages")]
[:div {:class (stl/css :items)} [:div {:class (stl/css :items)}
(if (= 1 (count pages)) (if (= 1 (count pages))
[:div {:class (stl/css :checkbox-wrapper)} [:div {:class (stl/css :checkbox-wrapper)}
[:label {:for (str "page-" current-page-id)
:class (stl/css-case :global/checked true)}
[:span {:class (stl/css :checked)}
i/status-tick-refactor]
(:name current-page)]
[:input {:type "checkbox" [:input {:type "checkbox"
:id (dm/str "page-" current-page-id) :id (dm/str "page-" current-page-id)
:data-page-id (dm/str current-page-id) :data-page-id (dm/str current-page-id)
:on-change on-mark-checked-page :on-change on-mark-checked-page
:checked true}] :checked true}]
[:label {:for (str "page-" current-page-id)} (:name current-page)]
[:span {:class (stl/css-case :checkobox-tick true
:global/checked true)}
i/status-tick-refactor]
[:span (str " " (tr "common.share-link.current-tag"))]] [:span (str " " (tr "common.share-link.current-tag"))]]
[:* [:*

View file

@ -187,4 +187,12 @@
@extend .input-checkbox; @extend .input-checkbox;
height: $s-32; height: $s-32;
padding: 0; padding: 0;
span.checked {
background-color: var(--input-checkbox-background-color-active);
border: $s-1 solid var(--input-checkbox-background-color-active);
svg {
@extend .button-icon-small;
stroke: var(--input-checkbox-foreground-color-active);
}
}
} }

View file

@ -281,7 +281,7 @@
(mf/defc component-item-thumbnail (mf/defc component-item-thumbnail
"Component that renders the thumbnail image or the original SVG." "Component that renders the thumbnail image or the original SVG."
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [file-id root-shape component container]}] [{:keys [file-id root-shape component container class]}]
(let [retry (mf/use-state 0) (let [retry (mf/use-state 0)
thumbnail-uri (get-component-thumbnail-uri file-id component) thumbnail-uri (get-component-thumbnail-uri file-id component)
handle-error handle-error
@ -294,6 +294,7 @@
(if (some? thumbnail-uri) (if (some? thumbnail-uri)
[:& component-svg-thumbnail [:& component-svg-thumbnail
{:thumbnail-uri thumbnail-uri {:thumbnail-uri thumbnail-uri
:class class
:on-error handle-error :on-error handle-error
:root-shape root-shape :root-shape root-shape
:objects (:objects container) :objects (:objects container)
@ -301,6 +302,7 @@
[:& component-svg [:& component-svg
{:root-shape root-shape {:root-shape root-shape
:class class
:objects (:objects container) :objects (:objects container)
:show-grids? true}]))) :show-grids? true}])))

View file

@ -179,7 +179,8 @@
(when visible? (when visible?
[:& cmm/component-item-thumbnail {:file-id file-id [:& cmm/component-item-thumbnail {:file-id file-id
:class (stl/css :thumbnail) :class (stl/css-case :thumbnail true
:asset-list-thumbnail (not listing-thumbs?))
:root-shape root-shape :root-shape root-shape
:component component :component component
:container container}])])])) :container container}])])]))

View file

@ -26,14 +26,6 @@
background-color: var(--assets-component-background-color); background-color: var(--assets-component-background-color);
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
img {
height: auto;
width: auto;
max-height: 100%;
max-width: 100%;
pointer-events: none;
border: 0;
}
.cell-name { .cell-name {
@include titleTipography; @include titleTipography;
@ -84,6 +76,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
pointer-events: none;
border: 0;
} }
.grid-placeholder { .grid-placeholder {
@ -96,10 +90,13 @@
.asset-enum { .asset-enum {
margin: 0 $s-12; margin: 0 $s-12;
} }
.enum-item { .enum-item {
position: relative; position: relative;
display: flex; display: grid;
grid-template-columns: auto 1fr;
align-items: center; align-items: center;
column-gap: $s-8;
height: $s-36; height: $s-36;
margin-bottom: $s-4; margin-bottom: $s-4;
padding: $s-2; padding: $s-2;
@ -107,55 +104,6 @@
background-color: var(--assets-item-background-color); background-color: var(--assets-item-background-color);
cursor: pointer; cursor: pointer;
svg,
img {
@include flexCenter;
flex-shrink: 0;
padding: $s-2;
height: $s-32;
width: $s-32;
border-radius: $br-6;
background-color: var(--color-foreground-secondary);
}
.item-name {
@include titleTipography;
@include textEllipsis;
padding-left: $s-8;
color: var(--assets-item-name-foreground-color);
&.editing {
display: flex;
align-items: center;
height: $s-32;
border: $s-1 solid var(--input-border-color-focus);
border-radius: $br-8;
background-color: var(--input-background-color);
input {
@include textEllipsis;
@include titleTipography;
@include removeInputStyle;
flex-grow: 1;
height: $s-28;
max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size)));
padding-left: $s-6;
margin: 0;
border-radius: $br-8;
color: var(--input-foreground-color);
}
span {
@include flexCenter;
height: $s-28;
background-color: transparent;
border-radius: $br-8;
svg {
@extend .button-icon-small;
stroke: var(--input-foreground-color);
transform: rotate(90deg);
}
}
}
}
&:hover { &:hover {
background-color: var(--assets-item-background-color-hover); background-color: var(--assets-item-background-color-hover);
.item-name { .item-name {
@ -176,6 +124,45 @@
} }
} }
.item-name {
@include titleTipography;
@include textEllipsis;
order: 2;
color: var(--assets-item-name-foreground-color);
input {
@include textEllipsis;
@include titleTipography;
@include removeInputStyle;
height: $s-32;
padding: $s-4;
}
span {
display: flex;
place-items: center;
padding-inline-end: $s-4;
}
&.editing {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
column-gap: $s-8;
border: $s-1 solid var(--input-border-color-focus);
border-radius: $br-8;
background-color: var(--input-background-color);
}
}
.asset-list-thumbnail {
@include flexCenter;
flex-shrink: 0;
padding: $s-2;
height: $s-32;
width: $s-32;
border-radius: $br-6;
background-color: var(--assets-component-background-color);
}
.grid-placeholder { .grid-placeholder {
height: $s-2; height: $s-2;
width: 100%; width: 100%;

View file

@ -17,6 +17,8 @@
} }
.path { .path {
@include textEllipsis;
max-width: 90%;
margin-left: $s-2; margin-left: $s-2;
text-transform: initial; text-transform: initial;
color: var(--title-foreground-color-hover); color: var(--title-foreground-color-hover);

View file

@ -47,6 +47,8 @@
.component-name { .component-name {
@include titleTipography; @include titleTipography;
@include textEllipsis; @include textEllipsis;
direction: rtl;
text-align: left;
width: 70%; width: 70%;
flex-grow: 2; flex-grow: 2;
margin-left: $s-8; margin-left: $s-8;
@ -55,6 +57,9 @@
.component-parent-name { .component-parent-name {
@include titleTipography; @include titleTipography;
@include textEllipsis; @include textEllipsis;
direction: rtl;
text-align: left;
max-width: 95%;
padding-left: $s-36; padding-left: $s-36;
color: var(--title-foreground-color); color: var(--title-foreground-color);
} }
@ -224,6 +229,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: $s-4; margin-bottom: $s-4;
padding-right: $s-8;
font-size: $s-12; font-size: $s-12;
cursor: pointer; cursor: pointer;
width: 100%; width: 100%;

View file

@ -147,7 +147,7 @@
border-radius: $br-6; border-radius: $br-6;
background-color: var(--input-checkbox-inactive-background-color); background-color: var(--input-checkbox-inactive-background-color);
&.checked { &.checked {
background-color: var(--input-checkbox-active-background-color); background-color: var(--input-checkbox-background-color-active);
svg { svg {
@extend .button-icon-small; @extend .button-icon-small;
stroke: var(--input-details-color); stroke: var(--input-details-color);

View file

@ -654,7 +654,8 @@
[:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")] [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")]
[:div {:class (stl/css :input-element-wrapper) [:div {:class (stl/css :input-element-wrapper)
:title (tr "workspace.options.interaction-ms")} :title (tr "workspace.options.interaction-ms")}
[:span.after (tr "workspace.options.interaction-ms")] [:span {:class (stl/css :after)}
(tr "workspace.options.interaction-ms")]
[:> numeric-input* {:ref ext-duration-ref [:> numeric-input* {:ref ext-duration-ref
:on-change change-duration :on-change change-duration
:value (-> interaction :animation :duration) :value (-> interaction :animation :duration)
@ -719,9 +720,9 @@
[:button {:class (stl/css :add-interaction-btn) [:button {:class (stl/css :add-interaction-btn)
:on-click add-interaction} :on-click add-interaction}
i/add-refactor]]]) i/add-refactor]]])
[:div {:class (stl/css :help-content)}
(when (= (count interactions) 0) (when (= (count interactions) 0)
[:* [:div {:class (stl/css :help-content)}
(when (and shape (not (cfh/unframed-shape? shape))) (when (and shape (not (cfh/unframed-shape? shape)))
[:div {:class (stl/css :help-group)} [:div {:class (stl/css :help-group)}
[:div {:class (stl/css :interactions-help-icon)} i/add-refactor] [:div {:class (stl/css :interactions-help-icon)} i/add-refactor]
@ -734,7 +735,7 @@
[:div {:class (stl/css :help-group)} [:div {:class (stl/css :help-group)}
[:div {:class (stl/css :interactions-help-icon)} i/play-refactor] [:div {:class (stl/css :interactions-help-icon)} i/play-refactor]
[:div {:class (stl/css :interactions-help)} [:div {:class (stl/css :interactions-help)}
(tr "workspace.options.use-play-button")]]])] (tr "workspace.options.use-play-button")]]])
[:div {:class (stl/css :groups)} [:div {:class (stl/css :groups)}
(for [[index interaction] (d/enumerate interactions)] (for [[index interaction] (d/enumerate interactions)]
[:& interaction-entry {:key (dm/str (:id shape) "-" index) [:& interaction-entry {:key (dm/str (:id shape) "-" index)

View file

@ -54,6 +54,11 @@
} }
} }
.after {
@include titleTipography;
margin-top: $s-1;
}
.interactions-help { .interactions-help {
@include titleTipography; @include titleTipography;
text-align: center; text-align: center;

View file

@ -137,7 +137,11 @@
:is-shared (:shared context) :is-shared (:shared context)
:project-id (:project-id context) :project-id (:project-id context)
:create-page false :create-page false
:features features})))
;; If the features object exists send that. Otherwise we remove the components/v2 because
;; if the features attribute doesn't exist is a version < 2.0. The other features will
;; be kept so the shapes are created full featured
:features (d/nilv (:features context) (disj features "components/v2"))})))
(defn link-file-libraries (defn link-file-libraries
"Create a new file on the back-end" "Create a new file on the back-end"
@ -154,7 +158,8 @@
[context file] [context file]
(let [file-id (:id file) (let [file-id (:id file)
session-id (uuid/next) session-id (uuid/next)
batches (->> (fb/generate-changes file) changes (fb/generate-changes file)
batches (->> changes
(partition change-batch-size change-batch-size nil) (partition change-batch-size change-batch-size nil)
(mapv vec)) (mapv vec))
@ -538,7 +543,9 @@
(rx/merge-map (comp d/kebab-keys parser/string->uuid)) (rx/merge-map (comp d/kebab-keys parser/string->uuid))
(rx/mapcat (rx/mapcat
(fn [[id media]] (fn [[id media]]
(let [media (assoc media :id (resolve id))] (let [media (-> media
(assoc :id (resolve id))
(update :name str))]
(->> (get-file context :media id media) (->> (get-file context :media id media)
(rx/map (fn [blob] (rx/map (fn [blob]
(let [content (.slice blob 0 (.-size blob) (:mtype media))] (let [content (.slice blob 0 (.-size blob) (:mtype media))]
@ -625,7 +632,7 @@
"other"))) "other")))
(defmethod impl/handler :analyze-import (defmethod impl/handler :analyze-import
[{:keys [files]}] [{:keys [files features]}]
(->> (rx/from files) (->> (rx/from files)
(rx/merge-map (rx/merge-map
@ -646,7 +653,16 @@
(rx/merge-map #(zip/loadAsync (:body %))) (rx/merge-map #(zip/loadAsync (:body %)))
(rx/merge-map #(get-file {:zip %} :manifest)) (rx/merge-map #(get-file {:zip %} :manifest))
(rx/map (comp d/kebab-keys parser/string->uuid)) (rx/map (comp d/kebab-keys parser/string->uuid))
(rx/map #(hash-map :uri (:uri file) :data % :type "application/zip"))) (rx/map
(fn [data]
;; Checks if the file is exported with components v2 and the current team only
;; supports components v1
(let [has-file-v2?
(->> (:files data)
(d/seek (fn [[_ file]] (contains? (set (:features file)) "components/v2"))))]
(if (and has-file-v2? (not (contains? features "components/v2")))
{:uri (:uri file) :error "dashboard.import.analyze-error.components-v2"}
(hash-map :uri (:uri file) :data data :type "application/zip"))))))
(->> st (->> st
(rx/filter (fn [data] (= "application/octet-stream" (:type data)))) (rx/filter (fn [data] (= "application/octet-stream" (:type data))))
(rx/map (fn [_] (rx/map (fn [_]

View file

@ -534,6 +534,9 @@ msgstr "Import Penpot files"
msgid "dashboard.import.analyze-error" msgid "dashboard.import.analyze-error"
msgstr "Oops! We couldn't import this file" msgstr "Oops! We couldn't import this file"
msgid "dashboard.import.analyze-error.components-v2"
msgstr "File with components v2 activated but this team doesn't support it yet."
msgid "dashboard.import.import-error" msgid "dashboard.import.import-error"
msgstr "There was a problem importing the file. The file wasn't imported." msgstr "There was a problem importing the file. The file wasn't imported."

View file

@ -542,6 +542,9 @@ msgstr "Importar archivos Penpot"
msgid "dashboard.import.analyze-error" msgid "dashboard.import.analyze-error"
msgstr "¡Vaya! No hemos podido importar el fichero" msgstr "¡Vaya! No hemos podido importar el fichero"
msgid "dashboard.import.analyze-error.components-v2"
msgstr "Fichero exportado con componentes-v2 pero el equipo actual no lo soporta aún."
msgid "dashboard.import.import-error" msgid "dashboard.import.import-error"
msgstr "Hubo un problema importando el fichero. No ha podido ser importado." msgstr "Hubo un problema importando el fichero. No ha podido ser importado."