From f2f352d06c925d319cdced19ae5cd3f5f6da43ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marina=20L=C3=B3pez?= Date: Fri, 4 Sep 2026 10:13:25 +0200 Subject: [PATCH 1/4] :sparkles: Add props to invitations and organizations events (#11508) --- .../src/app/rpc/commands/teams_invitations.clj | 5 ++--- backend/src/app/rpc/commands/verify_token.clj | 6 +++++- backend/test/backend_tests/rpc_team_test.clj | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/backend/src/app/rpc/commands/teams_invitations.clj b/backend/src/app/rpc/commands/teams_invitations.clj index 729205c4c9..7051c2b594 100644 --- a/backend/src/app/rpc/commands/teams_invitations.clj +++ b/backend/src/app/rpc/commands/teams_invitations.clj @@ -235,9 +235,8 @@ :organization-name (:name organization) :member-email (:email-to invitation) :member-id (:id member) - :role role} - organization - (assoc :user-who-send-invitation (str profile-id)) + :role role + :user-who-send-invitation (str profile-id)} (not organization) (assoc :team-belongs-to-organization (boolean team-organization-id) diff --git a/backend/src/app/rpc/commands/verify_token.clj b/backend/src/app/rpc/commands/verify_token.clj index 8db7afcd8b..d46c7a8057 100644 --- a/backend/src/app/rpc/commands/verify_token.clj +++ b/backend/src/app/rpc/commands/verify_token.clj @@ -308,7 +308,9 @@ (assoc :name "accept-organization-invitation") (assoc :props (-> props - (assoc :organization-id organization-id-on-add) + (assoc :organization-id organization-id-on-add + :user-id (:id profile) + :user-who-send-invitation (:created-by invitation)) (audit/clean-props)))))) (cond-> (assoc claims :state :created) @@ -325,6 +327,8 @@ (assoc :organization-id organization-id-on-add :organization-member-add-source organization-add-source :belongs-to-team-on-add (boolean team-id) + :user-id (:id profile) + :user-who-send-invitation (:created-by invitation) :organization-member-count-before organization-member-count-before) (audit/clean-props))})))))) diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index 3d755d8231..b087eb6819 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -154,10 +154,14 @@ (get-in % [:props :member-email]))) events))] (doseq [event [create-organization update-organization]] + (t/is (= (str (:id owner)) + (get-in event [:props :user-who-send-invitation]))) (t/is (true? (get-in event [:props :team-belongs-to-organization]))) (t/is (true? (get-in event [:props :adds-invitee-to-organization]))) (t/is (true? (get-in event [:props :invitee-already-organization-member])))) + (t/is (= (str (:id owner)) + (get-in create-plain [:props :user-who-send-invitation]))) (t/is (false? (get-in create-plain [:props :team-belongs-to-organization]))) (t/is (false? (get-in create-plain [:props :adds-invitee-to-organization]))) (t/is (false? (get-in create-plain [:props :invitee-already-organization-member]))))))) @@ -521,6 +525,9 @@ (let [event (organization-event)] (t/is (= organization-id (get-in event [:props :organization-id]))) + (t/is (= (:id invitee) (get-in event [:props :user-id]))) + (t/is (= (:id inviter) + (get-in event [:props :user-who-send-invitation]))) (t/is (not (contains? (:props event) :organization-member-add-source))) (t/is (not (contains? (:props event) :belongs-to-team-on-add))) (t/is (not (contains? (:props event) :organization-member-count-before))) @@ -530,6 +537,10 @@ (:origin @frontend-event))) (t/is (= organization-id (get-in @frontend-event [:props :organization-id]))) + (t/is (= (:id invitee) + (get-in @frontend-event [:props :user-id]))) + (t/is (= (:id inviter) + (get-in @frontend-event [:props :user-who-send-invitation]))) (t/is (= "direct-organization-invitation" (get-in @frontend-event [:props :organization-member-add-source]))) (t/is (false? (get-in @frontend-event [:props :belongs-to-team-on-add]))) @@ -570,6 +581,9 @@ (t/is (some #(= "accept-team-invitation-from" (:name %)) events)) (t/is (= (:id team) (get-in event [:props :team-id]))) (t/is (= organization-id (get-in event [:props :organization-id]))) + (t/is (= (:id invitee) (get-in event [:props :user-id]))) + (t/is (= (:id inviter) + (get-in event [:props :user-who-send-invitation]))) (t/is (not (contains? (:props event) :organization-member-add-source))) (t/is (not (contains? (:props event) :belongs-to-team-on-add))) (t/is (not (contains? (:props event) :organization-member-count-before))) @@ -578,6 +592,10 @@ (t/is (= (:id team) (get-in @frontend-event [:props :team-id]))) (t/is (= organization-id (get-in @frontend-event [:props :organization-id]))) + (t/is (= (:id invitee) + (get-in @frontend-event [:props :user-id]))) + (t/is (= (:id inviter) + (get-in @frontend-event [:props :user-who-send-invitation]))) (t/is (= "team-invitation" (get-in @frontend-event [:props :organization-member-add-source]))) (t/is (true? (get-in @frontend-event [:props :belongs-to-team-on-add]))) From 214dbd0c8bca19c7ba6a0e51c7972f61d671cc73 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 4 Sep 2026 10:15:38 +0200 Subject: [PATCH 2/4] :bug: Stop opening Comments section from persisting display-comments (#11492) AI-assisted-by: claude-sonnet-5 --- frontend/playwright/ui/pages/WorkspacePage.js | 6 ++ .../ui/specs/workspace-comments.spec.js | 61 +++++++++++++++++++ .../src/app/main/data/workspace/drawing.cljs | 6 -- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index f6b6a5a11a..d2953a2c71 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -600,6 +600,12 @@ export class WorkspacePage extends BaseWebSocketPage { .getByRole("button", { name: "Comments (C)" }) .click(clickOptions); } + + async toggleCommentsVisibilityFromMenu(clickOptions = {}) { + await this.page.getByRole("button", { name: "Main menu" }).click(); + await this.page.getByText("view").last().click(); + await this.page.locator("#file-menu-comments").click(clickOptions); + } } export default WorkspacePage; diff --git a/frontend/playwright/ui/specs/workspace-comments.spec.js b/frontend/playwright/ui/specs/workspace-comments.spec.js index 8cc3cbe203..735b10b044 100644 --- a/frontend/playwright/ui/specs/workspace-comments.spec.js +++ b/frontend/playwright/ui/specs/workspace-comments.spec.js @@ -35,3 +35,64 @@ test("Group bubbles when zooming out if they overlap", async ({ page }) => { /unread/, ); }); + +test("Opening the Comments section only temporarily overrides a disabled global comments setting", async ({ + page, +}) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(); + await workspacePage.setupFileWithComments(); + await workspacePage.goToWorkspace(); + + const bubble = page.getByTestId("floating-thread-bubble-1"); + + // "Display comments" is enabled by default, so the bubble is already visible. + await expect(bubble).toBeVisible(); + + // Turn the global "Display comments" setting off from the main menu. + await workspacePage.toggleCommentsVisibilityFromMenu(); + await expect(bubble).toBeHidden(); + + // Opening the Comments section shows comments regardless of the global setting. + await workspacePage.showComments(); + await expect(bubble).toBeVisible(); + + // Closing the Comments section falls back to the (still disabled) global setting. + await workspacePage.showComments(); + await expect(bubble).toBeHidden(); + + // The global setting itself must be untouched by opening/closing the section. + await page.getByRole("button", { name: "Main menu" }).click(); + await page.getByText("view").last().click(); + await expect(page.locator("#file-menu-comments")).toContainText( + "Show comments", + ); + await page.keyboard.press("Escape"); +}); + +test("Comments stay visible through opening and closing the Comments section when the global setting is enabled", async ({ + page, +}) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(); + await workspacePage.setupFileWithComments(); + await workspacePage.goToWorkspace(); + + const bubble = page.getByTestId("floating-thread-bubble-1"); + + // "Display comments" is enabled by default. + await expect(bubble).toBeVisible(); + + await workspacePage.showComments(); + await expect(bubble).toBeVisible(); + + await workspacePage.showComments(); + await expect(bubble).toBeVisible(); + + await page.getByRole("button", { name: "Main menu" }).click(); + await page.getByText("view").last().click(); + await expect(page.locator("#file-menu-comments")).toContainText( + "Hide comments", + ); + await page.keyboard.press("Escape"); +}); diff --git a/frontend/src/app/main/data/workspace/drawing.cljs b/frontend/src/app/main/data/workspace/drawing.cljs index 4812cf32a0..193b5747af 100644 --- a/frontend/src/app/main/data/workspace/drawing.cljs +++ b/frontend/src/app/main/data/workspace/drawing.cljs @@ -15,7 +15,6 @@ [app.main.data.workspace.drawing.common :as common] [app.main.data.workspace.drawing.curve :as curve] [app.main.data.workspace.drawing.line :as line] - [app.main.data.workspace.layout :as dwlo] [app.main.data.workspace.path :as path] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -47,11 +46,6 @@ (when (= tool :path) (rx/of (start-drawing :path))) - ;; NOTE: comments are a special case and they manage they - ;; own interrupt cycle. - (when (= tool :comments) - (rx/of (dwlo/toggle-layout-flag :display-comments :force? true))) - (when (and (not= tool :comments) (not= tool :path)) (let [stopper (rx/filter (ptk/type? ::clear-drawing) stream)] From 064f200774de80e307bd1c2c3191ab5b596157a6 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 4 Sep 2026 10:21:31 +0200 Subject: [PATCH 3/4] :bug: Fix crash pressing Ctrl+D with no shape selected (#11491) AI-assisted-by: claude-sonnet-5 --- frontend/src/app/main/data/workspace/variants.cljs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index 2d224facc2..a8d013012b 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -539,9 +539,10 @@ (let [objects (dsh/lookup-page-objects state) selected-ids (dsh/lookup-selected state) selected-shapes (map (d/getf objects) selected-ids) - add-new-variant? (every? ctc/is-variant? selected-shapes) + add-new-variant? (and (seq selected-shapes) (every? ctc/is-variant? selected-shapes)) undo-id (js/Symbol)] - (if add-new-variant? + (cond + add-new-variant? (rx/concat (rx/of (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-duplicate"}) @@ -549,7 +550,12 @@ (add-new-variant (first selected-ids) false)) (rx/from (map #(add-new-variant % true) (rest selected-ids))) (rx/of (dwu/commit-undo-transaction undo-id))) - (rx/of (dws/duplicate-selected true))))))) + + (seq selected-ids) + (rx/of (dws/duplicate-selected true)) + + :else + (rx/empty)))))) (defn rename-variant "Rename the variant container and all components belonging to this variant" From fdcc4666e31e65a3d14f7d31a2850cd6c9a0b405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Torr=C3=B3?= Date: Fri, 4 Sep 2026 11:00:35 +0200 Subject: [PATCH 4/4] :bug: Fix multiple shapes export on files rendered with webgl (#11506) --- exporter/src/app/renderer/bitmap.cljs | 8 +++----- frontend/src/app/render.cljs | 8 +++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs index e04c60076b..814bce13f2 100644 --- a/exporter/src/app/renderer/bitmap.cljs +++ b/exporter/src/app/renderer/bitmap.cljs @@ -17,7 +17,7 @@ [promesa.core :as p])) (defn render - [{:keys [file-id page-id share-id token scale type objects skip-children is-wasm] :as params} on-object] + [{:keys [file-id page-id share-id token scale type objects skip-children] :as params} on-object] (letfn [(prepare-options [uri] #js {:screen #js {:width bw/default-viewport-width :height bw/default-viewport-height} @@ -25,7 +25,7 @@ :height bw/default-viewport-height} :locale "en-US" :storageState #js {:cookies (bw/create-cookies uri {:token token})} - :deviceScaleFactor (if is-wasm 1 scale) ;; wasm won't use deviceScaleFactor + :deviceScaleFactor scale :userAgent bw/default-user-agent}) (render-object [page {:keys [id] :as object}] @@ -59,9 +59,7 @@ :share-id share-id :object-id (mapv :id objects) :route "objects" - :skip-children skip-children - :wasm (when is-wasm "true") - :scale scale} + :skip-children skip-children} uri (-> (cf/get-internal-uri) (u/ensure-path-slash) (u/join "render.html") diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index 99dfed871a..3d1931beaf 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -136,7 +136,9 @@ (repo/cmd! :get-page {:file-id file-id :page-id page-id :share-id share-id - :object-id object-id + :object-id (if (uuid? object-id) + object-id + (set object-id)) :features features})) (rx/tap (fn [[fonts]] (when (seq fonts) @@ -155,7 +157,7 @@ [:embed {:optional true} :boolean] [:skip-children {:optional true} :boolean] [:object-id - [:or [::sm/set ::sm/uuid] ::sm/uuid]]]) + [:or [:vector ::sm/uuid] ::sm/uuid]]]) (def ^:private coerce-render-objects-params (sm/coercer schema:render-objects)) @@ -188,7 +190,7 @@ {:file-id file-id :page-id page-id :share-id share-id - :object-ids (into #{} object-id) + :object-ids (into [] (distinct) object-id) :embed embed :skip-children skip-children :wasm wasm