From e3b3fa3342bdbab21d513c2d0a1322965177a5c6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 13 Mar 2025 09:32:19 +0100 Subject: [PATCH 1/3] :paperclip: Update changelog --- CHANGES.md | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a8a0feace..fd01e4c3b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,27 +2,14 @@ ## 2.5.3 -### :rocket: Epics and highlights - -### :boom: Breaking changes & Deprecations - -### :heart: Community contributions (Thank you!) - -### :sparkles: New features - ### :bug: Bugs fixed - Component sync issues with multiple tabs [Taiga #10471](https://tree.taiga.io/project/penpot/issue/10471) ## 2.5.2 -### :rocket: Epics and highlights - -### :boom: Breaking changes & Deprecations - -### :heart: Community contributions (Thank you!) - ### :sparkles: New features + - When the workspace is empty, set default the board creation tool [Taiga #9425](https://tree.taiga.io/project/penpot/us/9425) ### :bug: Bugs fixed @@ -34,22 +21,12 @@ ## 2.5.1 -### :rocket: Epics and highlights - -### :boom: Breaking changes & Deprecations - -### :heart: Community contributions (Thank you!) - ### :sparkles: New features - Improve Nginx entryponit to get the resolvers dinamically by default -### :bug: Bugs fixed - ## 2.5.0 -### :rocket: Epics and highlights - ### :boom: Breaking changes & Deprecations Although this is not a breaking change, we believe it’s important to highlight it in this @@ -78,9 +55,6 @@ If you have a big database and many cores available, you can reduce the time of all files by increasing paralelizacion changing the `max-jobs` value from 1 to N (where N is a number of cores) - -### :heart: Community contributions (Thank you!) - ### :sparkles: New features - [GRADIENTS] New gradients UI with multi-stop support. [Taiga #3418](https://tree.taiga.io/project/penpot/epic/3418) From f450c9dbe337a357691e23163f7f72e41b60e89b Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 13 Mar 2025 15:15:30 +0000 Subject: [PATCH 2/3] :tada: Add support for WEBP format on shape export It is very convenient to be able to export WEBP right from penpot. Otherwise users have to first download to PNG then convert it locally. --- Playwright only supports JPEG and PNG. So in order to support WEBP I had to first generate a PNG and then convert it afterwards. Signed-off-by: Dalai Felinto --- CHANGES.md | 6 ++++++ common/src/app/common/types/shape/export.cljc | 2 +- docs/user-guide/exporting/index.njk | 2 +- exporter/src/app/renderer.cljs | 3 ++- exporter/src/app/renderer/bitmap.cljs | 6 +++++- exporter/src/app/util/mime.cljs | 4 +++- frontend/src/app/main/data/exports/assets.cljs | 4 ++-- frontend/src/app/main/ui/inspect/exports.cljs | 3 ++- frontend/src/app/main/ui/inspect/exports.scss | 6 +++--- .../main/ui/workspace/sidebar/options/menus/exports.cljs | 3 ++- .../main/ui/workspace/sidebar/options/menus/exports.scss | 6 +++--- frontend/src/app/plugins/format.cljs | 2 +- frontend/src/app/plugins/parser.cljs | 2 +- 13 files changed, 32 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fd01e4c3b..09ea41efc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.5.4 (Unreleased) + +### :sparkles: New features + +- Add support for WEBP format on shape export [Github #6053](https://github.com/penpot/penpot/pull/6053) and [Github #6074](https://github.com/penpot/penpot/pull/6074) + ## 2.5.3 ### :bug: Bugs fixed diff --git a/common/src/app/common/types/shape/export.cljc b/common/src/app/common/types/shape/export.cljc index bd2bee0a5..feb7f8dec 100644 --- a/common/src/app/common/types/shape/export.cljc +++ b/common/src/app/common/types/shape/export.cljc @@ -8,7 +8,7 @@ (:require [app.common.schema :as sm])) -(def types #{:png :jpeg :svg :pdf}) +(def types #{:png :jpeg :webp :svg :pdf}) (def schema:export [:map {:title "ShapeExport"} diff --git a/docs/user-guide/exporting/index.njk b/docs/user-guide/exporting/index.njk index 63f9d9fb2..d685a6683 100644 --- a/docs/user-guide/exporting/index.njk +++ b/docs/user-guide/exporting/index.njk @@ -27,7 +27,7 @@ title: 07· Exporting objects
  • Size - Options for the most common sizing scales.
  • Suffix - Especially useful if you are exporting at different scales.
  • -
  • File format - PNG, SVG, JPEG, PDF.
  • +
  • File format - PNG, JPEG, WEBP, SVG, PDF.

Exporting multiple elements

diff --git a/exporter/src/app/renderer.cljs b/exporter/src/app/renderer.cljs index b4abaca4b..4a7cf8af7 100644 --- a/exporter/src/app/renderer.cljs +++ b/exporter/src/app/renderer.cljs @@ -15,7 +15,7 @@ (s/def ::name ::us/string) (s/def ::suffix ::us/string) -(s/def ::type #{:jpeg :png :pdf :svg}) +(s/def ::type #{:png :jpeg :webp :pdf :svg}) (s/def ::page-id ::us/uuid) (s/def ::file-id ::us/uuid) (s/def ::share-id ::us/uuid) @@ -40,6 +40,7 @@ (case type :png (rb/render params on-object) :jpeg (rb/render params on-object) + :webp (rb/render params on-object) :pdf (rp/render params on-object) :svg (rs/render params on-object))) diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs index 38022db73..750102af7 100644 --- a/exporter/src/app/renderer/bitmap.cljs +++ b/exporter/src/app/renderer/bitmap.cljs @@ -34,7 +34,11 @@ (bw/wait-for node) (case type :png (bw/screenshot node {:omit-background? true :type type :path path}) - :jpeg (bw/screenshot node {:omit-background? false :type type :path path})) + :jpeg (bw/screenshot node {:omit-background? false :type type :path path}) + :webp (p/let [png-path (sh/tempfile :prefix "penpot.tmp.render.bitmap." :suffix ".png")] + ;; playwright only supports jpg and png, we need to convert it afterwards + (bw/screenshot node {:omit-background? true :type :png :path png-path}) + (sh/run-cmd! (str "convert " png-path " -quality 100 WEBP:" path)))) (on-object (assoc object :path path)))) (render [uri page] diff --git a/exporter/src/app/util/mime.cljs b/exporter/src/app/util/mime.cljs index 18601cbd0..7bb0ce77c 100644 --- a/exporter/src/app/util/mime.cljs +++ b/exporter/src/app/util/mime.cljs @@ -15,6 +15,7 @@ (case type :png ".png" :jpeg ".jpg" + :webp ".webp" :svg ".svg" :pdf ".pdf" :zip ".zip")) @@ -26,6 +27,7 @@ :pdf "application/pdf" :svg "image/svg+xml" :jpeg "image/jpeg" - :png "image/png")) + :png "image/png" + :webp "image/webp")) diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index 1e800aee6..b1f029399 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -266,10 +266,10 @@ (defn export-shapes-event [exports origin] (let [types (reduce (fn [counts {:keys [type]}] - (if (#{:png :pdf :svg :jpeg} type) + (if (#{:png :jpeg :webp :svg :pdf} type) (update counts type inc) counts)) - {:png 0, :pdf 0, :svg 0, :jpeg 0} + {:png 0, :jpeg 0, :webp 0, :pdf 0, :svg 0} exports)] (ptk/event ::ev/event (merge types diff --git a/frontend/src/app/main/ui/inspect/exports.cljs b/frontend/src/app/main/ui/inspect/exports.cljs index 8c5495ad1..5f1aa2118 100644 --- a/frontend/src/app/main/ui/inspect/exports.cljs +++ b/frontend/src/app/main/ui/inspect/exports.cljs @@ -37,7 +37,7 @@ scale-enabled? (mf/use-callback (fn [export] - (#{:png :jpeg} (:type export)))) + (#{:png :jpeg :webp} (:type export)))) in-progress? (:in-progress xstate) @@ -123,6 +123,7 @@ format-options [{:value "png" :label "PNG"} {:value "jpeg" :label "JPG"} + {:value "webp" :label "WEBP"} {:value "svg" :label "SVG"} {:value "pdf" :label "PDF"}]] diff --git a/frontend/src/app/main/ui/inspect/exports.scss b/frontend/src/app/main/ui/inspect/exports.scss index 8244d9e23..f7365067c 100644 --- a/frontend/src/app/main/ui/inspect/exports.scss +++ b/frontend/src/app/main/ui/inspect/exports.scss @@ -51,7 +51,7 @@ .element-group { display: grid; - grid-template-columns: repeat(8, 1fr); + grid-template-columns: repeat(9, 1fr); column-gap: $s-4; .action-btn { @extend .button-tertiary; @@ -64,13 +64,13 @@ } .input-wrapper { - grid-column: span 7; + grid-column: span 8; display: grid; grid-template-columns: subgrid; } .format-select { - grid-column: span 2; + grid-column: span 3; padding: 0; .dropdown-upwards { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs index dd8908d76..415d0295a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs @@ -53,7 +53,7 @@ scale-enabled? (mf/use-fn (fn [export] - (#{:png :jpeg} (:type export)))) + (#{:png :jpeg :webp} (:type export)))) on-download (mf/use-fn @@ -173,6 +173,7 @@ format-options [{:value "png" :label "PNG"} {:value "jpeg" :label "JPG"} + {:value "webp" :label "WEBP"} {:value "svg" :label "SVG"} {:value "pdf" :label "PDF"}]] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss index 6bf3152dd..0f9aae670 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss @@ -32,18 +32,18 @@ .element-group { display: grid; - grid-template-columns: repeat(8, 1fr); + grid-template-columns: repeat(9, 1fr); column-gap: $s-4; } .input-wrapper { - grid-column: span 7; + grid-column: span 8; display: grid; grid-template-columns: subgrid; } .format-select { - grid-column: span 2; + grid-column: span 3; padding: 0; .dropdown-upwards { diff --git a/frontend/src/app/plugins/format.cljs b/frontend/src/app/plugins/format.cljs index 829476da0..6555f6a36 100644 --- a/frontend/src/app/plugins/format.cljs +++ b/frontend/src/app/plugins/format.cljs @@ -261,7 +261,7 @@ :hidden hidden}))) ;; export interface Export { -;; type: 'png' | 'jpeg' | 'svg' | 'pdf'; +;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf'; ;; scale: number; ;; suffix: string; ;; } diff --git a/frontend/src/app/plugins/parser.cljs b/frontend/src/app/plugins/parser.cljs index 8d884350d..0ce6aad41 100644 --- a/frontend/src/app/plugins/parser.cljs +++ b/frontend/src/app/plugins/parser.cljs @@ -243,7 +243,7 @@ ;; export interface Export { -;; type: 'png' | 'jpeg' | 'svg' | 'pdf'; +;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf'; ;; scale: number; ;; suffix: string; ;; } From 05d6d2fcd4ee5439f39449287eade641f22cecec Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 14 Mar 2025 09:55:41 +0100 Subject: [PATCH 3/3] :bug: Fix several corner cases that causes race conditions on workspace and dashboard loading * :bug: Fix several race conditions on workspace and dashboard code It also fixes a corner case that happens when penpot workspace is loaded in a background tab on firefox. * :bug: Add missing team-id prop to several file returning endpoints --- CHANGES.md | 8 ++ backend/src/app/rpc/commands/files.clj | 3 + frontend/src/app/main/data/dashboard.cljs | 40 ++++----- frontend/src/app/main/data/fonts.cljs | 11 ++- frontend/src/app/main/data/team.cljs | 59 +++++--------- frontend/src/app/main/data/workspace.cljs | 7 +- frontend/src/app/main/ui/dashboard.cljs | 81 ++++++++++--------- .../src/app/main/ui/dashboard/file_menu.cljs | 2 +- frontend/src/app/main/ui/dashboard/grid.cljs | 6 +- .../src/app/main/ui/dashboard/import.cljs | 6 +- .../src/app/main/ui/dashboard/projects.cljs | 17 ++-- .../src/app/main/ui/dashboard/templates.cljs | 1 + .../src/app/main/ui/workspace/libraries.cljs | 5 +- frontend/src/app/render.cljs | 17 +++- 14 files changed, 138 insertions(+), 125 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 09ea41efc..83dac4172 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,14 @@ - Add support for WEBP format on shape export [Github #6053](https://github.com/penpot/penpot/pull/6053) and [Github #6074](https://github.com/penpot/penpot/pull/6074) +### :bug: Bugs fixed + +- Fix feature loading on workspace when opening a file in a background + tab [Taiga #10377](https://tree.taiga.io/project/penpot/issue/10377) +- Fix minor inconsistencies on RPC `get-file-libraries` and `get-file` + methods (add missing team-id prop) + + ## 2.5.3 ### :bug: Bugs fixed diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index bc512c0b7..e3e29ddf0 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -323,6 +323,7 @@ file (-> (get-file cfg id :project-id project-id) (assoc :permissions perms) + (assoc :team-id (:id team)) (check-version!))] (-> (cfeat/get-team-enabled-features cf/flags team) @@ -613,6 +614,7 @@ SELECT l.id, l.features, l.project_id, + p.team_id, l.created_at, l.modified_at, l.deleted_at, @@ -622,6 +624,7 @@ l.synced_at, l.is_shared FROM libs AS l + INNER JOIN project AS p ON (p.id = l.project_id) WHERE l.deleted_at IS NULL OR l.deleted_at > now();") (defn get-file-libraries diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index b3ebe6ace..564e97dd9 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -38,7 +38,9 @@ (declare process-message) (defn initialize - [] + [team-id] + (assert (uuid? team-id) "expected uuid instance for `team-id`") + (ptk/reify ::initialize ptk/WatchEvent (watch [_ state stream] @@ -46,8 +48,8 @@ profile-id (:profile-id state)] (->> (rx/merge - (rx/of (fetch-projects) - (df/fetch-fonts)) + (rx/of (fetch-projects team-id) + (df/fetch-fonts team-id)) (->> stream (rx/filter (ptk/type? ::dws/message)) (rx/map deref) @@ -60,8 +62,8 @@ (rx/take-until stopper)))))) (defn finalize - [] - (ptk/data-event ::finalize {})) + [team-id] + (ptk/data-event ::finalize {:team-id team-id})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Fetching (context aware: current team) @@ -69,7 +71,7 @@ ;; --- EVENT: fetch-projects -(defn projects-fetched +(defn- projects-fetched [projects] (ptk/reify ::projects-fetched ptk/UpdateEvent @@ -80,13 +82,12 @@ projects)))) (defn fetch-projects - [] + [team-id] (ptk/reify ::fetch-projects ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-projects {:team-id team-id}) - (rx/map projects-fetched)))))) + (watch [_ _ _] + (->> (rp/cmd! :get-projects {:team-id team-id}) + (rx/map projects-fetched))))) ;; --- EVENT: search @@ -115,7 +116,7 @@ ;; --- EVENT: recent-files -(defn recent-files-fetched +(defn- recent-files-fetched [files] (ptk/reify ::recent-files-fetched ptk/UpdateEvent @@ -126,13 +127,14 @@ (update :files d/merge files)))))) (defn fetch-recent-files - [] - (ptk/reify ::fetch-recent-files - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-team-recent-files {:team-id team-id}) - (rx/map recent-files-fetched)))))) + ([] (fetch-recent-files nil)) + ([team-id] + (ptk/reify ::fetch-recent-files + ptk/WatchEvent + (watch [_ state _] + (when-let [team-id (or team-id (:current-team-id state))] + (->> (rp/cmd! :get-team-recent-files {:team-id team-id}) + (rx/map recent-files-fetched))))))) ;; --- EVENT: fetch-template-files diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index a1aba3d67..4706e7c5c 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -73,13 +73,12 @@ (fonts/register! :custom fonts)))))) (defn fetch-fonts - [] - (ptk/reify ::load-team-fonts + [team-id] + (ptk/reify ::fetch-fonts ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-font-variants {:team-id team-id}) - (rx/map fonts-fetched)))))) + (watch [_ _ _] + (->> (rp/cmd! :get-font-variants {:team-id team-id}) + (rx/map fonts-fetched))))) (defn process-upload "Given a seq of blobs and the team id, creates a ready-to-use fonts diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index 50af94a44..43e169025 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -64,13 +64,14 @@ (update :profiles merge (d/index-by :id members)))))) (defn fetch-members - [] - (ptk/reify ::fetch-members - ptk/WatchEvent - (watch [_ state _] - (let [team-id (:current-team-id state)] - (->> (rp/cmd! :get-team-members {:team-id team-id}) - (rx/map (partial members-fetched team-id))))))) + ([] (fetch-members nil)) + ([team-id] + (ptk/reify ::fetch-members + ptk/WatchEvent + (watch [_ state _] + (when-let [team-id (or team-id (:current-team-id state))] + (->> (rp/cmd! :get-team-members {:team-id team-id}) + (rx/map (partial members-fetched team-id)))))))) (defn- invitations-fetched [team-id invitations] @@ -88,41 +89,20 @@ (->> (rp/cmd! :get-team-invitations {:team-id team-id}) (rx/map (partial invitations-fetched team-id))))))) -(defn set-current-team - [{:keys [id permissions features] :as team}] - (ptk/reify ::set-current-team - ptk/UpdateEvent - (update [_ state] - (-> state - ;; FIXME: redundant operation, only necessary on workspace - ;; until workspace initialization is refactored - (update-in [:teams id] merge team) - (assoc :permissions permissions) - ;; FIXME: this is a redundant operation that only needed by - ;; workspace; ti will not be needed after workspace - ;; bootstrap & urls refactor - (assoc :current-team-id id))) - - ptk/WatchEvent - (watch [_ _ _] - (rx/of (features/initialize (or features #{})))) - - ptk/EffectEvent - (effect [_ _ _] - (swap! storage/global assoc ::current-team-id id)))) - (defn- team-initialized - [] + [team-id] (ptk/reify ::team-initialized ptk/WatchEvent (watch [_ state _] - (let [team-id (:current-team-id state) - teams (get state :teams) - team (get teams team-id)] + (let [teams (get state :teams) + team (get teams team-id)] (if (not team) (rx/throw (ex/error :type :authentication)) - (rx/of (set-current-team team) - (fetch-members))))))) + (let [permissions (get team :permissions) + features (get team :features)] + (rx/of #(assoc % :permissions permissions) + (features/initialize (or features #{})) + (fetch-members team-id)))))))) (defn initialize-team [team-id] @@ -138,8 +118,7 @@ (rx/of (fetch-teams)) (->> stream (rx/filter (ptk/type? ::teams-fetched)) - (rx/observe-on :async) - (rx/map team-initialized))) + (rx/map (partial team-initialized team-id)))) (rx/take-until stopper)))))) (defn finalize-team @@ -169,7 +148,7 @@ params (assoc params :team-id team-id)] (->> (rp/cmd! :update-team-member-role params) (rx/mapcat (fn [_] - (rx/of (fetch-members) + (rx/of (fetch-members team-id) (fetch-teams) (ptk/data-event ::ev/event {::ev/name "update-team-member-role" @@ -187,7 +166,7 @@ params (assoc params :team-id team-id)] (->> (rp/cmd! :delete-team-member params) (rx/mapcat (fn [_] - (rx/of (fetch-members) + (rx/of (fetch-members team-id) (fetch-teams) (ptk/data-event ::ev/event {::ev/name "delete-team-member" diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 168e305c1..fc8295433 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -291,7 +291,8 @@ (watch [_ state stream] (let [features (features/get-team-enabled-features state) render-wasm? (contains? features "render-wasm/v1") - stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)] + stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream) + team-id (:current-team-id state)] (->> (rx/concat ;; Firstly load wasm module if it is enabled and fonts @@ -305,7 +306,7 @@ (rx/filter (ptk/type? ::df/fonts-loaded)) (rx/take 1) (rx/ignore)) - (rx/of (df/fetch-fonts))) + (rx/of (df/fetch-fonts team-id))) ;; Then fetch file and thumbnails (->> (rx/zip (rp/cmd! :get-file {:id file-id :features features}) @@ -335,7 +336,7 @@ ptk/WatchEvent (watch [_ state stream] - (log/debug :hint "initialize-workspace" :file-id file-id) + (log/debug :hint "initialize-workspace" :file-id (dm/str file-id)) (let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream) rparams (rt/get-params state)] diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index d0d7bafde..9dbe96ae0 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -210,42 +210,45 @@ (swap! storage/session dissoc :plugin-url)))))) (defn use-templates-import - [can-edit? template-url default-project-id] - (mf/with-layout-effect - [can-edit? template-url default-project-id] - (when (and (some? template-url) (some? default-project-id)) - (if can-edit? - (let [valid-url? (and (str/ends-with? template-url ".penpot") - (str/starts-with? template-url cf/templates-uri)) - template-name (when valid-url? (subs template-url (count cf/templates-uri))) - on-import #(st/emit! (dpj/fetch-files default-project-id) - (dd/fetch-recent-files) - (dd/fetch-projects) - (dd/clear-selected-files) - (ptk/event ::ev/event {::ev/name "install-template-from-link-finished" - :name template-name - :url template-url}))] - (if valid-url? - (do - (st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url})) - (->> (http/send! {:method :get - :uri template-url - :response-type :blob - :omit-default-headers true}) - (rx/subs! - (fn [result] - (if (or (< (:status result) 200) (>= (:status result) 300)) - (st/emit! (notif/error (tr "dashboard.import.error"))) - (st/emit! (modal/show - {:type :import - :project-id default-project-id - :entries [{:name template-name :uri (wapi/create-uri (:body result))}] - :on-finish-import on-import}))))))) - (st/emit! (notif/error (tr "dashboard.import.bad-url"))))) - (st/emit! (notif/error (tr "dashboard.import.no-perms")))) + [can-edit? template-url project] + (let [project-id (get project :id) + team-id (get project :team-id)] + (mf/with-layout-effect [can-edit? template-url project-id team-id] + (when (and (some? template-url) + (some? project-id) + (some? team-id)) + (if can-edit? + (let [valid-url? (and (str/ends-with? template-url ".penpot") + (str/starts-with? template-url cf/templates-uri)) + template-name (when valid-url? (subs template-url (count cf/templates-uri))) + on-import #(st/emit! (dpj/fetch-files project-id) + (dd/fetch-recent-files team-id) + (dd/fetch-projects team-id) + (dd/clear-selected-files) + (ptk/event ::ev/event {::ev/name "install-template-from-link-finished" + :name template-name + :url template-url}))] + (if valid-url? + (do + (st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url})) + (->> (http/send! {:method :get + :uri template-url + :response-type :blob + :omit-default-headers true}) + (rx/subs! + (fn [result] + (if (or (< (:status result) 200) (>= (:status result) 300)) + (st/emit! (notif/error (tr "dashboard.import.error"))) + (st/emit! (modal/show + {:type :import + :project-id project-id + :entries [{:name template-name :uri (wapi/create-uri (:body result))}] + :on-finish-import on-import}))))))) + (st/emit! (notif/error (tr "dashboard.import.bad-url"))))) + (st/emit! (notif/error (tr "dashboard.import.no-perms")))) - (binding [storage/*sync* true] - (swap! storage/session dissoc :template-url))))) + (binding [storage/*sync* true] + (swap! storage/session dissoc :template-url)))))) (mf/defc dashboard* {::mf/props :obj} @@ -270,10 +273,10 @@ (hooks/use-shortcuts ::dashboard sc/shortcuts) - (mf/with-effect [] - (st/emit! (dd/initialize)) + (mf/with-effect [team-id] + (st/emit! (dd/initialize team-id)) (fn [] - (st/emit! (dd/finalize)))) + (st/emit! (dd/finalize team-id)))) (mf/with-effect [] (let [key (events/listen goog/global "keydown" @@ -285,7 +288,7 @@ (events/unlistenByKey key)))) (use-plugin-register plugin-url team-id (:id default-project)) - (use-templates-import can-edit? template-url (:id default-project)) + (use-templates-import can-edit? template-url default-project) [:& (mf/provider ctx/current-project-id) {:value project-id} [:> modal-container*] diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index f882c2179..6e10ede27 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -133,7 +133,7 @@ (st/emit! (dcm/go-to-dashboard-files {:project-id project-id :team-id team-id})) - (st/emit! (dd/fetch-recent-files) + (st/emit! (dd/fetch-recent-files team-id) (dd/clear-selected-files)))) on-move-accept diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 4a3112ca2..2a2bf20f7 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -566,8 +566,9 @@ on-finish-import (mf/use-fn + (mf/deps team-id) (fn [] - (st/emit! (dd/fetch-recent-files) + (st/emit! (dd/fetch-recent-files team-id) (dd/clear-selected-files)))) import-files (use-import-file project-id on-finish-import) @@ -608,9 +609,10 @@ on-drop-success (mf/use-fn + (mf/deps team-id) (fn [] (st/emit! (ntf/success (tr "dashboard.success-move-file")) - (dd/fetch-recent-files) + (dd/fetch-recent-files team-id) (dd/clear-selected-files)))) on-drop diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index c9fb7df2b..ce6b83afb 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -344,11 +344,13 @@ continue-template (mf/use-fn + (mf/deps on-finish-import) (fn [template] (let [on-success (fn [_event] (reset! status* :import-success) - (st/emit! (dd/fetch-recent-files))) + (when (fn? on-finish-import) + (on-finish-import))) on-error (fn [cause] @@ -479,8 +481,6 @@ [:> import-entry* {:entry (assoc template :status status) :can-be-deleted false}])] - ;; (prn "import-dialog" status) - [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} (when (= :analyze status) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 12f22309b..3aaa590f8 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -105,7 +105,8 @@ [{:keys [project is-first team files can-edit]}] (let [locale (mf/deref i18n/locale) - project-id (:id project) + project-id (get project :id) + team-id (get team :id) file-count (or (:count project) 0) is-draft? (:is-default project) @@ -191,11 +192,11 @@ on-import (mf/use-fn - (mf/deps project-id) + (mf/deps project-id team-id) (fn [] (st/emit! (dpj/fetch-files project-id) - (dd/fetch-recent-files) - (dd/fetch-projects) + (dd/fetch-recent-files team-id) + (dd/fetch-projects team-id) (dd/clear-selected-files)))) handle-create-click @@ -317,6 +318,8 @@ (sort-by :modified-at) (reverse))) + team-id (get team :id) + recent-map (mf/deref ref:recent-files) permisions (:permissions team) @@ -327,7 +330,7 @@ show-team-hero* (mf/use-state #(get storage/global ::show-team-hero true)) show-team-hero? (deref show-team-hero*) - is-my-penpot (= (:default-team-id profile) (:id team)) + is-my-penpot (= (:default-team-id profile) team-id) is-defalt-team? (:is-default team) on-close @@ -346,8 +349,8 @@ (:name team))] (dom/set-html-title (tr "title.dashboard.projects" tname)))) - (mf/with-effect [] - (st/emit! (dd/fetch-recent-files) + (mf/with-effect [team-id] + (st/emit! (dd/fetch-recent-files team-id) (dd/clear-selected-files))) (when (seq projects) diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index b33694236..cef2fcc91 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -37,6 +37,7 @@ [template team-id project-id default-project-id section] (letfn [(on-finish [] (st/emit! + (dd/fetch-recent-files team-id) (ptk/event ::ev/event {::ev/name "import-template-finish" ::ev/origin "dashboard" :template (:name template) diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index 90d9111b2..0c6a8b8fe 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -568,7 +568,6 @@ [{:keys [starting-tab file-id] :as props :or {starting-tab :libraries}}] (let [files (mf/deref refs/files) file (get files file-id) - team-id (:team-id file) shared? (:is-shared file) linked-libraries @@ -616,8 +615,8 @@ :id "updates" :content updates-tab}]] - (mf/with-effect [team-id] - (st/emit! (dtm/fetch-shared-files team-id))) + (mf/with-effect [] + (st/emit! (dtm/fetch-shared-files))) [:div {:class (stl/css :modal-overlay) :on-click close-dialog-outside diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index d8a3d8650..258c98812 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -14,7 +14,6 @@ [app.common.types.components-list :as ctkl] [app.common.uri :as u] [app.main.data.fonts :as df] - [app.main.data.team :as dtm] [app.main.features :as features] [app.main.render :as render] [app.main.repo :as repo] @@ -30,6 +29,20 @@ (log/setup! {:app :info}) +(defn set-current-team + [{:keys [id permissions features] :as team}] + (ptk/reify ::set-current-team + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc :permissions permissions) + (update :teams assoc id team) + (assoc :current-team-id id))) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (features/initialize (or features #{})))))) + (defn- fetch-team [& {:keys [file-id]}] (ptk/reify ::fetch-team @@ -37,7 +50,7 @@ (watch [_ _ _] (->> (repo/cmd! :get-team {:file-id file-id}) (rx/mapcat (fn [team] - (rx/of (dtm/set-current-team team) + (rx/of (set-current-team team) (ptk/data-event ::team-fetched team)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;