diff --git a/CHANGES.md b/CHANGES.md index a823d53817..3c4e56be46 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## :rocket: Next +### :boom: Breaking changes + ### :sparkles: New features - Add select layer option to context menu [Taiga #2474](https://tree.taiga.io/project/penpot/us/2474). @@ -11,6 +13,11 @@ - Create e2e tests for drawing basic shapes [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608). - Create firsts e2e test [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608). +### :bug: Bugs fixed +### :arrow_up: Deps updates +### :heart: Community contributions by (Thank you!) + + ## 1.11.0-beta ### :boom: Breaking changes @@ -85,6 +92,7 @@ - Fix auto hide header in viewer full screen [Taiga #2632](https://tree.taiga.io/project/penpot/issue/2632) - Fix zoom in/out after fit or fill [Taiga #2630](https://tree.taiga.io/project/penpot/issue/2630) - Normalize zoom levels in workspace and viewer [Taiga #2631](https://tree.taiga.io/project/penpot/issue/2631) +- Avoid empty names in projects, files and pages [Taiga #2594](https://tree.taiga.io/project/penpot/issue/2594) ### :arrow_up: Deps updates @@ -95,8 +103,8 @@ - Spelling fixes (by @jsoref) [#1340](https://github.com/penpot/penpot/pull/1340). - Explain folders in components (by @candideu) [Penpot-docs #42](https://github.com/penpot/penpot-docs/pull/42). - Readability improvements of user guide (by @PaulSchulz) [Penpot-docs #50](https://github.com/penpot/penpot-docs/pull/50). - -# 1.10.4-beta + +## 1.10.4-beta ### :sparkles: Enhacements @@ -108,7 +116,7 @@ - Minor fix on how file changes log is persisted. - Fix many issues on error reporting. -# 1.10.3-beta +## 1.10.3-beta ### :sparkles: Enhacements @@ -130,7 +138,7 @@ - Update log4j2 dependency. -# 1.10.2-beta +## 1.10.2-beta ### :bug: Bugs fixed @@ -142,7 +150,7 @@ - Update log4j2 dependency. -# 1.10.1-beta +## 1.10.1-beta ### :bug: Bugs fixed diff --git a/common/src/app/common/pages.cljc b/common/src/app/common/pages.cljc index 3df51409d3..4c6248b86a 100644 --- a/common/src/app/common/pages.cljc +++ b/common/src/app/common/pages.cljc @@ -95,7 +95,6 @@ (s/def ::color ::spec/color) (s/def ::data ::spec/data) (s/def ::media-object ::spec/media-object) -(s/def ::minimal-shape ::spec/minimal-shape) (s/def ::page ::spec/page) (s/def ::recent-color ::spec/recent-color) (s/def ::shape-attrs ::spec/shape-attrs) diff --git a/common/src/app/common/pages/spec.cljc b/common/src/app/common/pages/spec.cljc index cc74339ed1..cb642c7fcd 100644 --- a/common/src/app/common/pages/spec.cljc +++ b/common/src/app/common/pages/spec.cljc @@ -173,7 +173,6 @@ ;; Page Data related (s/def :internal.shape/blocked boolean?) (s/def :internal.shape/collapsed boolean?) -(s/def :internal.shape/content any?) (s/def :internal.shape/fill-color string?) (s/def :internal.shape/fill-opacity ::us/safe-number) @@ -277,17 +276,21 @@ :luminosity}) (s/def ::shape-attrs - (s/keys :opt-un [:internal.shape/selrect + (s/keys :req-un [::type ::name] + :opt-un [::id + ::component-id + ::component-file + ::component-root? + ::shape-ref + :internal.shape/selrect :internal.shape/points :internal.shape/blocked :internal.shape/collapsed - :internal.shape/content :internal.shape/fill-color :internal.shape/fill-opacity :internal.shape/fill-color-gradient :internal.shape/fill-color-ref-file :internal.shape/fill-color-ref-id - :internal.shape/hide-fill-on-export ;; only for frames :internal.shape/font-family :internal.shape/font-size :internal.shape/font-style @@ -332,22 +335,50 @@ :internal.shape/opacity :internal.shape/blend-mode])) +(s/def :internal.shape.text/type #{"root" "paragraph-set" "paragraph"}) +(s/def :internal.shape.text/children + (s/coll-of :internal.shape.text/content + :kind vector? + :min-count 1)) -;; shapes-group is handled differently +(s/def :internal.shape.text/text string?) +(s/def :internal.shape.text/key string?) -(s/def ::minimal-shape - (s/keys :req-un [::type ::name] - :opt-un [::id])) +(s/def :internal.shape.text/content + (s/or :container + (s/keys :req-un [:internal.shape.text/type + :internal.shape.text/children] + :opt-un [:internal.shape.text/key]) + :content + (s/keys :req-un [:internal.shape.text/text]))) -(s/def ::shape - (s/and ::minimal-shape - ::shape-attrs - (s/keys :opt-un [::id - ::component-id - ::component-file - ::component-root? - ::shape-ref]))) +(s/def :internal.shape.path/command keyword?) +(s/def :internal.shape.path/params (s/map-of keyword? any?)) +(s/def :internal.shape.path/command-item + (s/keys :req-un [:internal.shape.path/command + :internal.shape.path/params])) +(s/def :internal.shape.path/content + (s/coll-of :internal.shape.path/command-item :kind vector?)) + +(defmulti shape-spec :type) + +(defmethod shape-spec :default [_] + (s/spec ::shape-attrs)) + +(defmethod shape-spec :text [_] + (s/and ::shape-attrs + (s/keys :opt-un [:internal.shape.text/content]))) + +(defmethod shape-spec :path [_] + (s/and ::shape-attrs + (s/keys :opt-un [:internal.shape.path/content]))) + +(defmethod shape-spec :frame [_] + (s/and ::shape-attrs + (s/keys :opt-un [:internal.shape/hide-fill-on-export]))) + +(s/def ::shape (s/multi-spec shape-spec :type)) (s/def :internal.page/objects (s/map-of uuid? ::shape)) (s/def ::page @@ -356,7 +387,6 @@ ::cto/options :internal.page/objects])) - (s/def ::recent-color (s/keys :opt-un [:internal.color/value :internal.color/color diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 6af0751cbd..fbe80847b5 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1247,7 +1247,7 @@ (defn update-shape-flags [ids {:keys [blocked hidden] :as flags}] (us/verify (s/coll-of ::us/uuid) ids) - (s/assert ::shape-attrs flags) + (us/assert ::shape-attrs flags) (ptk/reify ::update-shape-flags ptk/WatchEvent (watch [_ state _] @@ -1449,7 +1449,6 @@ (defn show-shape-context-menu [{:keys [shape] :as params}] - (us/verify (s/nilable ::cp/minimal-shape) shape) (ptk/reify ::show-shape-context-menu ptk/WatchEvent (watch [_ state _] @@ -1858,7 +1857,7 @@ (defn paste-text [text] - (s/assert string? text) + (us/assert string? text) (ptk/reify ::paste-text ptk/WatchEvent (watch [_ state _] @@ -1887,7 +1886,7 @@ (defn- paste-svg [text] - (s/assert string? text) + (us/assert string? text) (ptk/reify ::paste-svg ptk/WatchEvent (watch [_ state _] diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index ef413c78ce..9d050206ab 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -16,6 +16,7 @@ [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [cuerdas.core :as str] [rumext.alpha :as mf])) (mf/defc header @@ -63,9 +64,11 @@ (if (:edition @local) [:& inline-edition {:content (:name project) :on-end (fn [name] - (st/emit! (-> (dd/rename-project (assoc project :name name)) - (with-meta {::ev/origin "project"}))) - (swap! local assoc :edition false))}] + (let [name (str/trim name)] + (when-not (str/empty? name) + (st/emit! (-> (dd/rename-project (assoc project :name name)) + (with-meta {::ev/origin "project"})))) + (swap! local assoc :edition false)))}] [:div.dashboard-title [:h1 {:on-double-click on-edit} (:name project)]])) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index d25620b048..9a3ddc966b 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -18,6 +18,7 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [app.util.time :as dt] + [cuerdas.core :as str] [okulary.core :as l] [rumext.alpha :as mf])) @@ -73,9 +74,11 @@ (mf/use-callback (mf/deps project) (fn [name] - (st/emit! (-> (dd/rename-project (assoc project :name name)) - (with-meta {::ev/origin "dashboard"}))) - (swap! local assoc :edition? false))) + (let [name (str/trim name)] + (when-not (str/empty? name) + (st/emit! (-> (dd/rename-project (assoc project :name name)) + (with-meta {::ev/origin "dashboard"})))) + (swap! local assoc :edition? false)))) on-file-created (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index e23e839772..6d3fff53da 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -46,8 +46,8 @@ (on-stop-edit) (swap! local assoc :edition false) (st/emit! (dw/end-rename-shape) - (when-not (str/empty? name) - (dw/update-shape (:id shape) {:name name}))))) + (when-not (str/empty? (str/trim name)) + (dw/update-shape (:id shape) {:name (str/trim name)}))))) cancel-edit (fn [] (on-stop-edit) diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 5e77a90040..d2a65a52b5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -18,6 +18,7 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] + [cuerdas.core :as str] [okulary.core :as l] [rumext.alpha :as mf])) @@ -66,8 +67,9 @@ (mf/use-callback (fn [event] (let [target (dom/event->target event) - name (dom/get-value target)] - (st/emit! (dw/rename-page id name)) + name (str/trim (dom/get-value target))] + (when-not (str/empty? name) + (st/emit! (dw/rename-page id name))) (swap! local assoc :edition false)))) on-key-down diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index e6bdb5d494..1dc38cc520 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -316,6 +316,11 @@ (->> (rx/of node) (rx/observe-on :async)))) +(defn media-node? [node] + (and (cip/shape? node) + (cip/has-image? node) + (not (cip/close? node)))) + (defn import-page [context file [page-id page-name content]] (let [nodes (->> content cip/node-seq) @@ -326,18 +331,29 @@ (assoc :id (resolve page-id))) flows (->> (get-in page-data [:options :flows]) (mapv #(update % :starting-frame resolve))) - guides (->> (get-in page-data [:options :guides]) - (d/mapm #(update %2 :frame-id resolve))) + page-data (d/assoc-in-when page-data [:options :flows] flows) + file (-> file (fb/add-page page-data)) - page-data (-> page-data - (d/assoc-in-when [:options :flows] flows) - (d/assoc-in-when [:options :guides] guides)) - file (-> file (fb/add-page page-data))] - (->> (rx/from nodes) - (rx/filter cip/shape?) - (rx/merge-map (partial resolve-media context file-id)) - (rx/reduce (partial process-import-node context) file) - (rx/map (comp fb/close-page setup-interactions))))) + ;; Preprocess nodes to parallel upload the images. Store the result in a table + ;; old-node => node with image + ;; that will be used in the second pass immediately + pre-process-images + (->> (rx/from nodes) + (rx/filter media-node?) + (rx/merge-map + (fn [node] + (->> (resolve-media context file-id node) + (rx/map (fn [result] [node result]))))) + (rx/reduce conj {}))] + + (->> pre-process-images + (rx/flat-map + (fn [pre-proc] + (->> (rx/from nodes) + (rx/filter cip/shape?) + (rx/map (fn [node] (or (get pre-proc node) node))) + (rx/reduce (partial process-import-node context) file) + (rx/map (comp fb/close-page setup-interactions)))))))) (defn import-component [context file node] (let [resolve (:resolve context) @@ -357,7 +373,7 @@ (rx/filter cip/shape?) (rx/skip 1) (rx/skip-last 1) - (rx/merge-map (partial resolve-media context file-id)) + (rx/mapcat (partial resolve-media context file-id)) (rx/reduce (partial process-import-node context) file) (rx/map fb/finish-component)))) @@ -377,7 +393,6 @@ (fn [[page-id page-name]] (->> (get-file context :page page-id) (rx/map (fn [page-data] [page-id page-name page-data]))))) - (rx/concat-reduce (partial import-page context) file)))) (defn process-library-colors @@ -416,7 +431,7 @@ (let [resolve (:resolve context)] (->> (get-file context :media-list) (rx/flat-map (comp d/kebab-keys cip/string->uuid)) - (rx/merge-map + (rx/mapcat (fn [[id media]] (let [media (assoc media :id (resolve id))] (->> (get-file context :media id media)