diff --git a/CHANGES.md b/CHANGES.md index b3d96067f..2488cbb2d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -40,6 +40,8 @@ - Fix several issues with internal srepl helpers - Fix unexpected exception on template import from libraries - Fix incorrect uuid parsing from different parts of code +- Fix update layout on component restore [Taiga #10637](https://tree.taiga.io/project/penpot/issue/10637) +- Fix horizontal scroll in viewer [Github #6290](https://github.com/penpot/penpot/issues/6290) ## 2.6.1 diff --git a/backend/src/app/binfile/cleaner.clj b/backend/src/app/binfile/cleaner.clj new file mode 100644 index 000000000..558cfa129 --- /dev/null +++ b/backend/src/app/binfile/cleaner.clj @@ -0,0 +1,63 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.binfile.cleaner + "A collection of helpers for perform cleaning of artifacts; mainly + for recently imported shapes." + (:require + [app.common.data :as d] + [app.common.uuid :as uuid])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PRE DECODE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn clean-shape-pre-decode + "Applies a pre-decode phase migration to the shape" + [shape] + (if (= "bool" (:type shape)) + (if-let [content (get shape :bool-content)] + (-> shape + (assoc :content content) + (dissoc :bool-content)) + shape) + shape)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; POST DECODE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- fix-shape-shadow-color + "Some shapes can come with invalid `id` property on shadow colors + caused by incorrect uuid parsing bug that should be already fixed; + this function removes the invalid id from the data structure." + [shape] + (let [fix-color + (fn [{:keys [id] :as color}] + (if (uuid? id) + color + (if (and (string? id) + (re-matches uuid/regex id)) + (assoc color :id (uuid/uuid id)) + (dissoc color :id)))) + + fix-shadow + (fn [shadow] + (d/update-when shadow :color fix-color)) + + xform + (map fix-shadow)] + + (d/update-when shape :shadow + (fn [shadows] + (into [] xform shadows))))) + +(defn clean-shape-post-decode + "A shape procesor that expected to be executed after schema decoding + process but before validation." + [shape] + (-> shape + (fix-shape-shadow-color))) diff --git a/backend/src/app/binfile/v3.clj b/backend/src/app/binfile/v3.clj index d7ec8acdd..f945b3d81 100644 --- a/backend/src/app/binfile/v3.clj +++ b/backend/src/app/binfile/v3.clj @@ -8,6 +8,7 @@ "A ZIP based binary file exportation" (:refer-clojure :exclude [read]) (:require + [app.binfile.cleaner :as bfl] [app.binfile.common :as bfc] [app.binfile.migrations :as bfm] [app.common.data :as d] @@ -592,6 +593,38 @@ {}) (not-empty))) +(defn- read-file-components + [{:keys [::bfc/input ::file-id ::entries]}] + (let [clean-component-post-decode + (fn [component] + (d/update-when component :objects + (fn [objects] + (reduce-kv (fn [objects id shape] + (assoc objects id (bfl/clean-shape-post-decode shape))) + objects + objects)))) + clean-component-pre-decode + (fn [component] + (d/update-when component :objects + (fn [objects] + (reduce-kv (fn [objects id shape] + (assoc objects id (bfl/clean-shape-pre-decode shape))) + objects + objects))))] + + (->> (keep (match-component-entry-fn file-id) entries) + (reduce (fn [result {:keys [id entry]}] + (let [object (->> (read-entry input entry) + (clean-component-pre-decode) + (decode-component) + (clean-component-post-decode) + (validate-component))] + (if (= id (:id object)) + (assoc result id object) + result))) + {}) + (not-empty)))) + (defn- read-file-typographies [{:keys [::bfc/input ::file-id ::entries]}] (->> (keep (match-typography-entry-fn file-id) entries) @@ -612,49 +645,15 @@ (decode-tokens-lib) (validate-tokens-lib)))) -(defn- pre-decode-migrate-shape - "Applies a pre-decode phase migration to the shape" - [shape] - (if (= "bool" (:type shape)) - (if-let [content (get shape :bool-content)] - (-> shape - (assoc :content content) - (dissoc :bool-content)) - shape) - shape)) - -(defn- pre-decode-migrate-component - "Applies a pre-decode phase migration to component" - [component] - (d/update-when component :objects - (fn [objects] - (reduce-kv (fn [objects id shape] - (assoc objects id (pre-decode-migrate-shape shape))) - objects - objects)))) - -(defn- read-file-components - [{:keys [::bfc/input ::file-id ::entries]}] - (->> (keep (match-component-entry-fn file-id) entries) - (reduce (fn [result {:keys [id entry]}] - (let [object (->> (read-entry input entry) - (pre-decode-migrate-component) - (decode-component) - (validate-component))] - (if (= id (:id object)) - (assoc result id object) - result))) - {}) - (not-empty))) - (defn- read-file-shapes [{:keys [::bfc/input ::file-id ::page-id ::entries] :as cfg}] (->> (keep (match-shape-entry-fn file-id page-id) entries) (reduce (fn [result {:keys [id entry]}] - (let [object (-> (read-entry input entry) - (pre-decode-migrate-shape) - (decode-shape) - (validate-shape))] + (let [object (->> (read-entry input entry) + (bfl/clean-shape-pre-decode) + (decode-shape) + (bfl/clean-shape-post-decode) + (validate-shape))] (if (= id (:id object)) (assoc result id object) result))) diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 36d0c1c3d..f26897258 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -238,6 +238,7 @@ (db/update! conn :file {:data (blob/encode (:data file)) :version (:version file) + :modified-at (dt/now) :features (db/create-array conn "text" (:features file))} {:id id}) diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj index f4c5e3d14..2455807dd 100644 --- a/backend/src/app/rpc/commands/files_thumbnails.clj +++ b/backend/src/app/rpc/commands/files_thumbnails.clj @@ -180,8 +180,7 @@ (def ^:private schema:get-file-data-for-thumbnail [:map {:title "get-file-data-for-thumbnail"} - [:file-id ::sm/uuid] - [:features {:optional true} ::cfeat/features]]) + [:file-id ::sm/uuid]]) (def ^:private schema:partial-file @@ -211,7 +210,6 @@ (fmg/migrate-file)))] (-> (cfeat/get-team-enabled-features cf/flags team) - (cfeat/check-client-features! (:features params)) (cfeat/check-file-features! (:features file))) {:file-id file-id diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc index 4e1f58518..7d440882a 100644 --- a/common/src/app/common/files/migrations.cljc +++ b/common/src/app/common/files/migrations.cljc @@ -28,6 +28,7 @@ [app.common.types.container :as ctn] [app.common.types.file :as ctf] [app.common.types.shape :as cts] + [app.common.types.shape.interactions :as ctsi] [app.common.types.shape.shadow :as ctss] [app.common.uuid :as uuid] [clojure.set :as set] @@ -847,9 +848,6 @@ (update :pages-index update-vals update-container) (update :components update-vals update-container)))) -(def ^:private valid-shadow? - (sm/lazy-validator ::ctss/shadow)) - (defmethod migrate-data "legacy-44" [data _] (letfn [(fix-shadow [shadow] @@ -861,7 +859,7 @@ (update-object [object] (let [xform (comp (map fix-shadow) - (filter valid-shadow?))] + (filter ctss/valid-shadow?))] (d/update-when object :shadow #(into [] xform %)))) (update-container [container] @@ -1036,7 +1034,7 @@ (update-shape [shape] (let [xform (comp (map fix-shadow) - (filter valid-shadow?))] + (filter ctss/valid-shadow?))] (d/update-when shape :shadow #(into [] xform %)))) (update-container [container] @@ -1207,11 +1205,11 @@ object)) (update-container [container] - (d/update-when container :objects update-vals update-object))] + (d/update-when container :objects d/update-vals update-object))] (-> data - (update :pages-index update-vals update-container) - (update :components update-vals update-container)))) + (update :pages-index d/update-vals update-container) + (update :components d/update-vals update-container)))) (defmethod migrate-data "legacy-67" [data _] @@ -1222,8 +1220,8 @@ (d/update-when container :objects update-vals update-object))] (-> data - (update :pages-index update-vals update-container) - (update :components update-vals update-container)))) + (update :pages-index d/update-vals update-container) + (update :components d/update-vals update-container)))) (defmethod migrate-data "0001-remove-tokens-from-groups" [data _] @@ -1237,8 +1235,33 @@ (dissoc :applied-tokens))) (update-page [page] - (d/update-when page :objects update-vals update-object))] - (update data :pages-index update-vals update-page))) + (d/update-when page :objects d/update-vals update-object))] + + (update data :pages-index d/update-vals update-page))) + +(defmethod migrate-data "0002-clean-shape-interactions" + [data _] + (let [decode-fn (sm/decoder ctsi/schema:interaction sm/json-transformer) + validate-fn (sm/validator ctsi/schema:interaction) + + xform + (comp + (map decode-fn) + (filter validate-fn)) + + update-object + (fn [object] + (d/update-when object :interactions + (fn [interactions] + (into [] xform interactions)))) + + update-container + (fn [container] + (d/update-when container :objects d/update-vals update-object))] + + (-> data + (update :pages-index d/update-vals update-container) + (update :components d/update-vals update-container)))) (defmethod migrate-data "0002-normalize-bool-content" [data _] @@ -1315,4 +1338,5 @@ "legacy-66" "legacy-67" "0001-remove-tokens-from-groups" - "0002-normalize-bool-content"])) + "0002-normalize-bool-content" + "0002-clean-shape-interactions"])) diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 23d32e3e7..64eaa1f9b 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -864,7 +864,7 @@ choices))] {:pred pred :type-properties - {:title "contains" + {:title "contains any" :description "contains predicate"}}))}) (register! diff --git a/common/src/app/common/types/shape.cljc b/common/src/app/common/types/shape.cljc index d6172db8b..b7d50a1c1 100644 --- a/common/src/app/common/types/shape.cljc +++ b/common/src/app/common/types/shape.cljc @@ -212,7 +212,7 @@ [:interactions {:optional true} [:vector {:gen/max 2} ::ctsi/interaction]] [:shadow {:optional true} - [:vector {:gen/max 1} ::ctss/shadow]] + [:vector {:gen/max 1} ctss/schema:shadow]] [:blur {:optional true} ::ctsb/blur] [:grow-type {:optional true} [::sm/one-of grow-types]] diff --git a/common/src/app/common/types/shape/shadow.cljc b/common/src/app/common/types/shape/shadow.cljc index 1b37dd3e9..c00a1ce82 100644 --- a/common/src/app/common/types/shape/shadow.cljc +++ b/common/src/app/common/types/shape/shadow.cljc @@ -26,7 +26,9 @@ [:hidden :boolean] [:color ::ctc/color]]) -(sm/register! ::shadow schema:shadow) - (def check-shadow (sm/check-fn schema:shadow)) + +(def valid-shadow? + (sm/validator schema:shadow)) + diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index d5ad664a6..53a70f4bf 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -579,8 +579,13 @@ ldata (dsh/lookup-file-data state library-id) changes (-> (pcb/empty-changes it) - (cll/generate-restore-component ldata component-id library-id page objects))] - (rx/of (dch/commit-changes changes)))))) + (cll/generate-restore-component ldata component-id library-id page objects)) + + frames + (->> changes :redo-changes (keep :frame-id))] + + (rx/of (dch/commit-changes changes) + (ptk/data-event :layout/update {:ids frames})))))) (defn restore-components diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 3fd373103..df094bca8 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -89,14 +89,16 @@ (mf/with-effect [file-id revn visible? thumbnail-id] (when (and visible? (not thumbnail-id)) - (->> (ask-for-thumbnail file-id revn) - (rx/subs! (fn [thumbnail-id] - (st/emit! (dd/set-file-thumbnail file-id thumbnail-id))) - (fn [cause] - (log/error :hint "unable to render thumbnail" - :file-if file-id - :revn revn - :message (ex-message cause))))))) + (let [subscription + (->> (ask-for-thumbnail file-id revn) + (rx/subs! (fn [thumbnail-id] + (st/emit! (dd/set-file-thumbnail file-id thumbnail-id))) + (fn [cause] + (log/error :hint "unable to render thumbnail" + :file-if file-id + :revn revn + :message (ex-message cause)))))] + (partial rx/dispose! subscription)))) [:div {:class (stl/css :grid-item-th) :style {:background-color bg-color} diff --git a/frontend/src/app/main/ui/viewer.scss b/frontend/src/app/main/ui/viewer.scss index 7e475a227..31194da31 100644 --- a/frontend/src/app/main/ui/viewer.scss +++ b/frontend/src/app/main/ui/viewer.scss @@ -117,6 +117,7 @@ padding-right: 0 $s-8 $s-40 $s-8; transition: transform 400ms ease 300ms; z-index: $z-index-2; + pointer-events: none; } .reset-button { diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 5b1fe0467..f05981d1d 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -432,7 +432,7 @@ (let [id (obj/get self "$id") value (mapv #(shadow-defaults (parser/parse-shadow %)) value)] (cond - (not (sm/validate [:vector ::ctss/shadow] value)) + (not (sm/validate [:vector ctss/schema:shadow] value)) (u/display-not-valid :shadows value) (not (r/check-permission plugin-id "content:write")) diff --git a/frontend/src/app/util/http.cljs b/frontend/src/app/util/http.cljs index 47f382366..98177688c 100644 --- a/frontend/src/app/util/http.cljs +++ b/frontend/src/app/util/http.cljs @@ -50,7 +50,8 @@ [headers] (into {} (map vec) (seq (.entries ^js headers)))) -(def default-headers +(defn default-headers + [] {"x-frontend-version" (:full cfg/version)}) (defn fetch @@ -74,7 +75,7 @@ headers (cond-> headers (not omit-default-headers) - (d/merge default-headers)) + (merge (default-headers))) headers (-update-headers body headers) diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index 658429c19..cca294cb0 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -42,12 +42,11 @@ :http-body body}))) (defn- request-data-for-thumbnail - [file-id revn features] + [file-id revn] (let [path "api/rpc/command/get-file-data-for-thumbnail" params {:file-id file-id :revn revn - :strip-frames-with-thumbnails true - :features features} + :strip-frames-with-thumbnails true} request {:method :get :uri (u/join cf/public-uri path) :credentials "include" @@ -86,6 +85,6 @@ nil))) (defmethod impl/handler :thumbnails/generate-for-file - [{:keys [file-id revn features] :as message} _] - (->> (request-data-for-thumbnail file-id revn features) + [{:keys [file-id revn] :as message} _] + (->> (request-data-for-thumbnail file-id revn) (rx/map render-thumbnail)))