From 7f6bffdbfca1163a79a87ebd01d7f33f87806021 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 15 Oct 2025 10:50:33 +0200 Subject: [PATCH 1/4] :zap: Add minor comment threads queries optimization --- CHANGES.md | 1 + backend/src/app/rpc/commands/comments.clj | 104 +++++++++++----------- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3c45e3e8be..c1a74b6828 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -41,6 +41,7 @@ - Alternative ways of creating variants - Button Viewport [Taiga #11931](https://tree.taiga.io/project/penpot/us/11931) - Reorder properties for a component [Taiga #10225](https://tree.taiga.io/project/penpot/us/10225) - File Data storage layout refactor [Github #7345](https://github.com/penpot/penpot/pull/7345) +- Make several queries optimization on comment threads [Github #7506](https://github.com/penpot/penpot/pull/7506) ### :bug: Bugs fixed diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj index 19d4633bf3..7043064cfc 100644 --- a/backend/src/app/rpc/commands/comments.clj +++ b/backend/src/app/rpc/commands/comments.clj @@ -234,36 +234,39 @@ (files/check-comment-permissions! conn profile-id file-id share-id) (get-comment-threads conn profile-id file-id)))) -(def ^:private sql:comment-threads - "SELECT DISTINCT ON (ct.id) - ct.*, - pf.fullname AS owner_fullname, - pf.email AS owner_email, - pf.photo_id AS owner_photo_id, - p.team_id AS team_id, - f.name AS file_name, - f.project_id AS project_id, - first_value(c.content) OVER w AS content, - (SELECT count(1) - FROM comment AS c - WHERE c.thread_id = ct.id) AS count_comments, - (SELECT count(1) - FROM comment AS c - WHERE c.thread_id = ct.id - AND c.created_at >= coalesce(cts.modified_at, ct.created_at)) AS count_unread_comments - FROM comment_thread AS ct - INNER JOIN comment AS c ON (c.thread_id = ct.id) - INNER JOIN file AS f ON (f.id = ct.file_id) - INNER JOIN project AS p ON (p.id = f.project_id) - LEFT JOIN comment_thread_status AS cts ON (cts.thread_id = ct.id AND cts.profile_id = ?) - LEFT JOIN profile AS pf ON (ct.owner_id = pf.id) - WHERE f.deleted_at IS NULL - AND p.deleted_at IS NULL - WINDOW w AS (PARTITION BY c.thread_id ORDER BY c.created_at ASC)") +(defn- get-comment-threads-sql + [where] + (str/ffmt + "SELECT DISTINCT ON (ct.id) + ct.*, + pf.fullname AS owner_fullname, + pf.email AS owner_email, + pf.photo_id AS owner_photo_id, + p.team_id AS team_id, + f.name AS file_name, + f.project_id AS project_id, + first_value(c.content) OVER w AS content, + (SELECT count(1) + FROM comment AS c + WHERE c.thread_id = ct.id) AS count_comments, + (SELECT count(1) + FROM comment AS c + WHERE c.thread_id = ct.id + AND c.created_at >= coalesce(cts.modified_at, ct.created_at)) AS count_unread_comments + FROM comment_thread AS ct + INNER JOIN comment AS c ON (c.thread_id = ct.id) + INNER JOIN file AS f ON (f.id = ct.file_id) + INNER JOIN project AS p ON (p.id = f.project_id) + LEFT JOIN comment_thread_status AS cts ON (cts.thread_id = ct.id AND cts.profile_id = ?) + LEFT JOIN profile AS pf ON (ct.owner_id = pf.id) + WHERE f.deleted_at IS NULL + AND p.deleted_at IS NULL + %1 + WINDOW w AS (PARTITION BY c.thread_id ORDER BY c.created_at ASC)" + where)) (def ^:private sql:comment-threads-by-file-id - (str "WITH threads AS (" sql:comment-threads ")" - "SELECT * FROM threads WHERE file_id = ?")) + (get-comment-threads-sql "AND ct.file_id = ?")) (defn- get-comment-threads [conn profile-id file-id] @@ -273,34 +276,28 @@ ;; --- COMMAND: Get Unread Comment Threads (def ^:private sql:unread-all-comment-threads-by-team - (str "WITH threads AS (" sql:comment-threads ")" - "SELECT * FROM threads WHERE count_unread_comments > 0 AND team_id = ?")) + (str "WITH threads AS (" + (get-comment-threads-sql "AND p.team_id = ?") + ")" + "SELECT t.* FROM threads AS t + WHERE t.count_unread_comments > 0")) -;; The partial configuration will retrieve only comments created by the user and -;; threads that have a mention to the user. (def ^:private sql:unread-partial-comment-threads-by-team - (str "WITH threads AS (" sql:comment-threads ")" - "SELECT * FROM threads - WHERE count_unread_comments > 0 - AND team_id = ? - AND (owner_id = ? OR ? = ANY(mentions))")) + (str "WITH threads AS (" + (get-comment-threads-sql "AND p.team_id = ? AND (ct.owner_id = ? OR ? = ANY(ct.mentions))") + ")" + "SELECT t.* FROM threads AS t + WHERE t.count_unread_comments > 0")) (defn- get-unread-comment-threads [cfg profile-id team-id] (let [profile (-> (db/get cfg :profile {:id profile-id}) (profile/decode-row)) - notify (or (-> profile :props :notifications :dashboard-comments) :all)] - - (case notify - :all - (->> (db/exec! cfg [sql:unread-all-comment-threads-by-team profile-id team-id]) - (into [] xf-decode-row)) - - :partial - (->> (db/exec! cfg [sql:unread-partial-comment-threads-by-team profile-id team-id profile-id profile-id]) - (into [] xf-decode-row)) - - []))) + notify (or (-> profile :props :notifications :dashboard-comments) :all) + result (case notify + :all (db/exec! cfg [sql:unread-all-comment-threads-by-team profile-id team-id]) + :partial (db/exec! cfg [sql:unread-partial-comment-threads-by-team profile-id team-id profile-id profile-id]))] + (into [] xf-decode-row result))) (def ^:private schema:get-unread-comment-threads @@ -323,16 +320,17 @@ [:id ::sm/uuid] [:share-id {:optional true} [:maybe ::sm/uuid]]]) +(def ^:private sql:get-comment-thread + (get-comment-threads-sql "AND ct.file_id = ? AND ct.id = ?")) + (sv/defmethod ::get-comment-thread {::doc/added "1.15" ::sm/params schema:get-comment-thread} [cfg {:keys [::rpc/profile-id file-id id share-id] :as params}] (db/run! cfg (fn [{:keys [::db/conn]}] (files/check-comment-permissions! conn profile-id file-id share-id) - (let [sql (str "WITH threads AS (" sql:comment-threads ")" - "SELECT * FROM threads WHERE id = ? AND file_id = ?")] - (-> (db/exec-one! conn [sql profile-id id file-id]) - (decode-row)))))) + (some-> (db/exec-one! conn [sql:get-comment-thread profile-id file-id id]) + (decode-row))))) ;; --- COMMAND: Retrieve Comments From b057ed1b9a14169a01b48de5d579e2d463b07ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 15 Oct 2025 15:20:36 +0200 Subject: [PATCH 2/4] :bug: Fix scroll on inspect tab --- CHANGES.md | 1 + frontend/src/app/main/ui/inspect/right_sidebar.scss | 5 ++++- frontend/src/app/main/ui/workspace/sidebar/options.scss | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 83708db5bb..0688933f4c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -56,6 +56,7 @@ - Fix auto-width changes to fixed when switching variants [Taiga #12172](https://tree.taiga.io/project/penpot/issue/12172) - Fix component number has no singular translation string [Taiga #12106](https://tree.taiga.io/project/penpot/issue/12106) - Fix adding/removing identical text fills [Taiga #12287](https://tree.taiga.io/project/penpot/issue/12287) +- Fix scroll on the inspect tab [Taiga #12293](https://tree.taiga.io/project/penpot/issue/12293) ## 2.10.1 diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.scss b/frontend/src/app/main/ui/inspect/right_sidebar.scss index ffcc1f11c7..a4c3e6cf45 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/inspect/right_sidebar.scss @@ -146,9 +146,12 @@ .viewer-tab-switcher { --tabs-nav-padding-inline-start: 0; --tabs-nav-padding-inline-end: var(--sp-m); - --max-inspect-tab-height: calc(100vh - 12rem); + /* same height as .element-options in workspace/sidebar/options.scss */ + /* which is one of the parents of this component */ + --max-inspect-tab-height: var(--sidebar-element-options-height); max-block-size: var(--max-inspect-tab-height); + overflow: auto; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.scss b/frontend/src/app/main/ui/workspace/sidebar/options.scss index 0967561672..93e63cdbd3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options.scss @@ -30,7 +30,10 @@ flex-direction: column; gap: deprecated.$s-8; width: 100%; - height: calc(100vh - $sz-88); + /* FIXME: This is hacky and prone to break, we should tackle the whole layout + of the sidebar differently */ + --sidebar-element-options-height: calc(100vh - $sz-88); + height: var(--sidebar-element-options-height); padding-top: deprecated.$s-8; } From e964f9820e5f42a03485cba18941e3d18008b196 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Thu, 16 Oct 2025 11:40:19 +0200 Subject: [PATCH 3/4] :bug: Fix tooltip position of proportion lock button (#7519) --- CHANGES.md | 1 + frontend/src/app/main/ui/ds/buttons/icon_button.cljs | 5 ++++- .../main/ui/workspace/sidebar/options/menus/measures.cljs | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0688933f4c..92c5cf4224 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -57,6 +57,7 @@ - Fix component number has no singular translation string [Taiga #12106](https://tree.taiga.io/project/penpot/issue/12106) - Fix adding/removing identical text fills [Taiga #12287](https://tree.taiga.io/project/penpot/issue/12287) - Fix scroll on the inspect tab [Taiga #12293](https://tree.taiga.io/project/penpot/issue/12293) +- Fix lock proportion tooltip [Taiga #12326](https://tree.taiga.io/project/penpot/issue/12326) ## 2.10.1 diff --git a/frontend/src/app/main/ui/ds/buttons/icon_button.cljs b/frontend/src/app/main/ui/ds/buttons/icon_button.cljs index 1d2995ea8e..551142e757 100644 --- a/frontend/src/app/main/ui/ds/buttons/icon_button.cljs +++ b/frontend/src/app/main/ui/ds/buttons/icon_button.cljs @@ -20,13 +20,15 @@ [:icon [:and :string [:fn #(contains? icon-list %)]]] [:aria-label :string] + [:tooltip-placement {:optional true} + [:maybe [:enum "top" "bottom" "left" "right" "top-right" "bottom-right" "bottom-left" "top-left"]]] [:variant {:optional true} [:maybe [:enum "primary" "secondary" "ghost" "destructive" "action"]]]]) (mf/defc icon-button* {::mf/schema schema:icon-button ::mf/memo true} - [{:keys [class icon icon-class variant aria-label children] :rest props}] + [{:keys [class icon icon-class variant aria-label children tooltip-placement] :rest props}] (let [variant (d/nilv variant "primary") @@ -47,6 +49,7 @@ :aria-labelledby tooltip-id})] [:> tooltip* {:content aria-label + :placement tooltip-placement :id tooltip-id} [:> :button props [:> icon* {:icon-id icon :aria-hidden true :class icon-class}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 94f2982ea5..040bb3eb29 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -528,6 +528,7 @@ :value (:height values)}]]]) [:> icon-button* {:variant "ghost" + :tooltip-placement "top-left" :icon (if proportion-lock "lock" "unlock") :class (stl/css-case :selected (true? proportion-lock)) :disabled (= proportion-lock :multiple) From 5ad04e0f4c8a5adf808147923d0228fc27db0ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 16 Oct 2025 14:29:30 +0200 Subject: [PATCH 4/4] :bug: Fix error when selecting set in theme --- CHANGES.md | 1 + frontend/playwright/ui/specs/tokens.spec.js | 6 ++++++ .../app/main/ui/workspace/tokens/themes/create_modal.cljs | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 92c5cf4224..9433a7af38 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -58,6 +58,7 @@ - Fix adding/removing identical text fills [Taiga #12287](https://tree.taiga.io/project/penpot/issue/12287) - Fix scroll on the inspect tab [Taiga #12293](https://tree.taiga.io/project/penpot/issue/12293) - Fix lock proportion tooltip [Taiga #12326](https://tree.taiga.io/project/penpot/issue/12326) +- Fix internal Error when selecting a set by name in the token theme editor [Taiga #12310](https://tree.taiga.io/project/penpot/issue/12310) ## 2.10.1 diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index f371b0cb7a..077f46b9f0 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -864,6 +864,12 @@ test.describe("Tokens: Themes modal", () => { } } + const firstButton = await tokenThemeUpdateCreateModal + .getByTestId('tokens-set-item') + .first(); + + await firstButton.click(); + await tokenThemeUpdateCreateModal .getByRole("button", { name: "Save theme", diff --git a/frontend/src/app/main/ui/workspace/tokens/themes/create_modal.cljs b/frontend/src/app/main/ui/workspace/tokens/themes/create_modal.cljs index 1224efcfbc..dfbacfa370 100644 --- a/frontend/src/app/main/ui/workspace/tokens/themes/create_modal.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/themes/create_modal.cljs @@ -319,7 +319,8 @@ (mf/use-fn (mf/deps on-toggle-token-set) (fn [set-id] - (on-toggle-token-set set-id)))] + (let [set (ctob/get-set lib set-id)] + (on-toggle-token-set (ctob/get-name set)))))] [:div {:class (stl/css :themes-modal-wrapper)} [:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}