From 44b6db6ff1156d4cb1a93c228fb87416380bb519 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 24 Apr 2026 11:47:24 +0100 Subject: [PATCH 01/11] Replace LayoutToggle with compound-web Switch component --- package.json | 2 +- pnpm-lock.yaml | 11 +++-- src/components/CallFooter.tsx | 19 ++++++-- src/room/LayoutToggle.module.css | 79 ------------------------------- src/room/LayoutToggle.stories.tsx | 25 ---------- src/room/LayoutToggle.tsx | 59 ----------------------- 6 files changed, 22 insertions(+), 173 deletions(-) delete mode 100644 src/room/LayoutToggle.module.css delete mode 100644 src/room/LayoutToggle.stories.tsx delete mode 100644 src/room/LayoutToggle.tsx diff --git a/package.json b/package.json index c0f4d505..5fa4fed2 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@typescript-eslint/parser": "^8.31.0", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^10.0.0", - "@vector-im/compound-web": "^9.0.0", + "@vector-im/compound-web": "element-hq/compound-web#e7c91ef18e20f2fc70069696f4414018361ac512", "@vitejs/plugin-react": "^4.0.1", "@vitest/coverage-v8": "^4.0.18", "babel-plugin-transform-vite-meta-env": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c800b0c2..07bc5fbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,8 +150,8 @@ importers: specifier: ^10.0.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.5) '@vector-im/compound-web': - specifier: ^9.0.0 - version: 9.2.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: element-hq/compound-web#e7c91ef18e20f2fc70069696f4414018361ac512 + version: https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vitejs/plugin-react': specifier: ^4.0.1 version: 4.7.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)) @@ -3037,8 +3037,9 @@ packages: react: optional: true - '@vector-im/compound-web@9.2.0': - resolution: {integrity: sha512-jHbABGEQ2yqNtm5xRIkklQs198VEfSk9AJQolI+e4WSJ0xg8Ozyv9t9KIuKQAmjdSV9aow5G6hDE861XB6DQgw==} + '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512': + resolution: {tarball: https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512} + version: 9.2.1 peerDependencies: '@fontsource/inconsolata': ^5 '@fontsource/inter': ^5 @@ -9473,7 +9474,7 @@ snapshots: '@types/react': 19.2.14 react: 19.2.5 - '@vector-im/compound-web@9.2.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@fontsource/inconsolata': 5.2.8 diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index 4e728d3b..21ff52c6 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -8,6 +8,12 @@ Please see LICENSE in the repository root for full details. import { type FC, type JSX, type Ref, useMemo } from "react"; import classNames from "classnames"; import { BehaviorSubject } from "rxjs"; +import { Switch } from "@vector-im/compound-web"; +import { t } from "i18next"; +import { + SpotlightIcon, + GridIcon, +} from "@vector-im/compound-design-tokens/assets/web/icons"; import LogoMark from "../icons/LogoMark.svg?react"; import LogoType from "../icons/LogoType.svg?react"; @@ -23,7 +29,6 @@ import { type ReactionData, } from "../button"; import styles from "./CallFooter.module.css"; -import { LayoutToggle } from "../room/LayoutToggle"; import { type GridMode } from "../state/CallViewModel/CallViewModel"; export interface AudioOutputSwitcher { @@ -232,10 +237,16 @@ export const CallFooter: FC = ({ {!hideControls &&
{buttons}
} {setLayoutMode && layoutMode && showLayoutSwitcher && ( - )} diff --git a/src/room/LayoutToggle.module.css b/src/room/LayoutToggle.module.css deleted file mode 100644 index d9ae5813..00000000 --- a/src/room/LayoutToggle.module.css +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2023, 2024 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE in the repository root for full details. -*/ - -.toggle { - padding: 2px; - border: 1px solid var(--cpd-color-border-interactive-secondary); - border-radius: var(--cpd-radius-pill-effect); - background: var(--cpd-color-bg-canvas-default); - display: flex; - position: relative; -} - -.toggle input { - appearance: none; - /* Safari puts a margin on these, which is not removed via appearance: none */ - margin: 0; - block-size: var(--cpd-space-11x); - inline-size: var(--cpd-space-11x); - cursor: pointer; - border-radius: var(--cpd-radius-pill-effect); - background: var(--cpd-color-bg-action-secondary-rest); - box-shadow: var(--small-drop-shadow); - transition: background-color 0.1s; -} - -.toggle svg { - display: block; - position: absolute; - padding: calc(2.5 * var(--cpd-space-1x)); - pointer-events: none; - color: var(--cpd-color-icon-primary); - transition: color 0.1s; -} - -.toggle svg:nth-child(2) { - inset-inline-start: 2px; -} - -.toggle svg:nth-child(4) { - inset-inline-end: 2px; -} - -@media (hover: hover) { - .toggle input:hover { - background: var(--cpd-color-bg-action-secondary-hovered); - box-shadow: none; - } -} - -.toggle input:active { - background: var(--cpd-color-bg-action-secondary-pressed); - box-shadow: none; -} - -.toggle input:checked { - background: var(--cpd-color-bg-action-primary-rest); -} - -.toggle input:checked + svg { - color: var(--cpd-color-icon-on-solid-primary); -} - -@media (hover: hover) { - .toggle input:checked:hover { - background: var(--cpd-color-bg-action-primary-hovered); - } -} - -.toggle input:checked:active { - background: var(--cpd-color-bg-action-primary-pressed); -} - -.toggle input:first-child { - margin-inline-end: 5px; -} diff --git a/src/room/LayoutToggle.stories.tsx b/src/room/LayoutToggle.stories.tsx deleted file mode 100644 index 72a2ffad..00000000 --- a/src/room/LayoutToggle.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2026 Element Creations Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE in the repository root for full details. -*/ - -import { fn } from "storybook/test"; - -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { LayoutToggle } from "./LayoutToggle"; - -const meta = { - component: LayoutToggle, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - layout: "grid", - setLayout: fn(), - }, -}; diff --git a/src/room/LayoutToggle.tsx b/src/room/LayoutToggle.tsx deleted file mode 100644 index 98ed91d3..00000000 --- a/src/room/LayoutToggle.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2023, 2024 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE in the repository root for full details. -*/ - -import { type ChangeEvent, type FC, useCallback } from "react"; -import { useTranslation } from "react-i18next"; -import { Tooltip } from "@vector-im/compound-web"; -import { - SpotlightIcon, - GridIcon, -} from "@vector-im/compound-design-tokens/assets/web/icons"; -import classNames from "classnames"; - -import styles from "./LayoutToggle.module.css"; - -export type Layout = "spotlight" | "grid"; - -type Props = { - layout: Layout; - setLayout: (layout: Layout) => void; - className?: string; -}; - -export const LayoutToggle: FC = ({ layout, setLayout, className }) => { - const { t } = useTranslation(); - - const onChange = useCallback( - (e: ChangeEvent) => setLayout(e.target.value as Layout), - [setLayout], - ); - - return ( -
- - - - - - - - - - ); -}; From d8be06974767e36598f71003121b80b5c7673a00 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 24 Apr 2026 11:54:24 +0100 Subject: [PATCH 02/11] Fix type --- locales/en/app.json | 1 + package.json | 2 +- src/components/CallFooter.tsx | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 5398930f..2545bb4b 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -143,6 +143,7 @@ "text": "This call already exists, would you like to join?", "title": "Join existing call?" }, + "layout_switch_label": "Layout", "layout_grid_label": "Grid", "layout_spotlight_label": "Spotlight", "lobby": { diff --git a/package.json b/package.json index 5fa4fed2..f27cf8f4 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@typescript-eslint/parser": "^8.31.0", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^10.0.0", - "@vector-im/compound-web": "element-hq/compound-web#e7c91ef18e20f2fc70069696f4414018361ac512", + "@vector-im/compound-web": "element-hq/compound-web#fc2e677326aaefec61ef74fb1d9de3c01eecfa7e", "@vitejs/plugin-react": "^4.0.1", "@vitest/coverage-v8": "^4.0.18", "babel-plugin-transform-vite-meta-env": "^1.0.3", diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index 21ff52c6..d10e4ecf 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -237,7 +237,8 @@ export const CallFooter: FC = ({ {!hideControls &&
{buttons}
} {setLayoutMode && layoutMode && showLayoutSwitcher && ( - + aria-label={t("layout_switch_label")} leftLabel={t("layout_spotlight_label")} leftValue="spotlight" leftIcon={SpotlightIcon} From 9b71070ef8e312bd17d1b0c71eda775c36cf4e30 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 24 Apr 2026 11:57:13 +0100 Subject: [PATCH 03/11] publish lock changes --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07bc5fbc..06269963 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,8 +150,8 @@ importers: specifier: ^10.0.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.5) '@vector-im/compound-web': - specifier: element-hq/compound-web#e7c91ef18e20f2fc70069696f4414018361ac512 - version: https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: element-hq/compound-web#fc2e677326aaefec61ef74fb1d9de3c01eecfa7e + version: https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vitejs/plugin-react': specifier: ^4.0.1 version: 4.7.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)) @@ -3037,8 +3037,8 @@ packages: react: optional: true - '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512': - resolution: {tarball: https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512} + '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e': + resolution: {tarball: https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e} version: 9.2.1 peerDependencies: '@fontsource/inconsolata': ^5 @@ -9474,7 +9474,7 @@ snapshots: '@types/react': 19.2.14 react: 19.2.5 - '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/e7c91ef18e20f2fc70069696f4414018361ac512(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@fontsource/inconsolata': 5.2.8 From 62751787ca0c4f95572d3feadd880297d3933e5e Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 24 Apr 2026 13:02:47 +0100 Subject: [PATCH 04/11] Use actual package --- package.json | 2 +- pnpm-lock.yaml | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index f27cf8f4..a8dc49fa 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@typescript-eslint/parser": "^8.31.0", "@use-gesture/react": "^10.2.11", "@vector-im/compound-design-tokens": "^10.0.0", - "@vector-im/compound-web": "element-hq/compound-web#fc2e677326aaefec61ef74fb1d9de3c01eecfa7e", + "@vector-im/compound-web": "^9.3.0", "@vitejs/plugin-react": "^4.0.1", "@vitest/coverage-v8": "^4.0.18", "babel-plugin-transform-vite-meta-env": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06269963..41e3f943 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,8 +150,8 @@ importers: specifier: ^10.0.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.5) '@vector-im/compound-web': - specifier: element-hq/compound-web#fc2e677326aaefec61ef74fb1d9de3c01eecfa7e - version: https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + specifier: ^9.3.0 + version: 9.3.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vitejs/plugin-react': specifier: ^4.0.1 version: 4.7.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)) @@ -3037,9 +3037,8 @@ packages: react: optional: true - '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e': - resolution: {tarball: https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e} - version: 9.2.1 + '@vector-im/compound-web@9.3.0': + resolution: {integrity: sha512-Elu4Uw8RbfP6JaudQYkVibALYT6qpwubqfKhteTxIPWBWzSYM+P5T+B1uX+ra+grNcXwXUt2xfMxpqYQsAHgYA==} peerDependencies: '@fontsource/inconsolata': ^5 '@fontsource/inter': ^5 @@ -9474,7 +9473,7 @@ snapshots: '@types/react': 19.2.14 react: 19.2.5 - '@vector-im/compound-web@https://codeload.github.com/element-hq/compound-web/tar.gz/fc2e677326aaefec61ef74fb1d9de3c01eecfa7e(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@vector-im/compound-web@9.3.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@fontsource/inconsolata': 5.2.8 From 5aa45714bfa194a3224e0181a2e8970a0b693ff1 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 27 Apr 2026 09:34:46 +0100 Subject: [PATCH 05/11] Snap updates --- locales/en/app.json | 2 +- .../ReactionToggleButton.test.tsx.snap | 10 +++---- .../GroupCallErrorBoundary.test.tsx.snap | 18 ++++++------ .../__snapshots__/InCallView.test.tsx.snap | 28 +++++++++---------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/locales/en/app.json b/locales/en/app.json index 2545bb4b..b51c6ed9 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -143,9 +143,9 @@ "text": "This call already exists, would you like to join?", "title": "Join existing call?" }, - "layout_switch_label": "Layout", "layout_grid_label": "Grid", "layout_spotlight_label": "Spotlight", + "layout_switch_label": "Layout", "lobby": { "ask_to_join": "Request to join call", "join_as_guest": "Join as guest", diff --git a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap index 608c1a0f..a1e319d9 100644 --- a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap +++ b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap @@ -10,7 +10,7 @@ exports[`Can close reaction dialog 1`] = ` aria-expanded="true" aria-haspopup="true" aria-labelledby="_r_bb_" - class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" + class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53" data-kind="primary" data-size="lg" role="button" @@ -44,7 +44,7 @@ exports[`Can fully expand emoji picker 1`] = ` aria-expanded="true" aria-haspopup="true" aria-labelledby="_r_7m_" - class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" + class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53" data-kind="primary" data-size="lg" role="button" @@ -75,7 +75,7 @@ exports[`Can lower hand 1`] = ` aria-expanded="false" aria-haspopup="true" aria-labelledby="_r_36_" - class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" + class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53" data-kind="secondary" data-size="lg" role="button" @@ -109,7 +109,7 @@ exports[`Can open menu 1`] = ` aria-expanded="true" aria-haspopup="true" aria-labelledby="_r_0_" - class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" + class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53" data-kind="primary" data-size="lg" role="button" @@ -140,7 +140,7 @@ exports[`Can raise hand 1`] = ` aria-expanded="false" aria-haspopup="true" aria-labelledby="_r_1j_" - class="_button_13vu4_8 raisedButton _has-icon_13vu4_60 _icon-only_13vu4_53" + class="_button_1nw83_8 raisedButton _has-icon_1nw83_60 _icon-only_1nw83_53" data-kind="primary" data-size="lg" role="button" diff --git a/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap b/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap index 0d2d39bc..92a6fe54 100644 --- a/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap +++ b/src/room/__snapshots__/GroupCallErrorBoundary.test.tsx.snap @@ -134,7 +134,7 @@ exports[`ConnectionLostError: Action handling should reset error state 1`] = ` You were disconnected from the call.

-
{buttons}
} {setLayoutMode && layoutMode && showLayoutSwitcher && ( + name="layoutMode" aria-label={t("layout_switch_label")} leftLabel={t("layout_spotlight_label")} leftValue="spotlight" From 5db1a20f9f6e716f98a01b27d4ceff7eb50c84fe Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 28 Apr 2026 09:29:12 +0100 Subject: [PATCH 07/11] update snap --- src/room/__snapshots__/InCallView.test.tsx.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/room/__snapshots__/InCallView.test.tsx.snap b/src/room/__snapshots__/InCallView.test.tsx.snap index e800fd8f..d9f768e7 100644 --- a/src/room/__snapshots__/InCallView.test.tsx.snap +++ b/src/room/__snapshots__/InCallView.test.tsx.snap @@ -403,6 +403,7 @@ exports[`InCallView > rendering > renders 1`] = ` > @@ -421,6 +422,7 @@ exports[`InCallView > rendering > renders 1`] = ` From 1025d23ac7adc57a8b243d48f331809f744550fd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 08:35:17 +0200 Subject: [PATCH 08/11] Update dependency livekit-client to v2.18.9 (#3958) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 114 +++++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5d35a6e..a3ad458e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,16 +47,16 @@ importers: version: 11.7.12 '@livekit/components-core': specifier: ^0.12.0 - version: 0.12.13(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) + version: 0.12.13(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) '@livekit/components-react': specifier: ^2.0.0 - version: 2.9.20(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1) + version: 2.9.20(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1) '@livekit/protocol': specifier: ^1.42.2 version: 1.45.6 '@livekit/track-processors': specifier: ^0.7.1 - version: 0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22)) + version: 0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22)) '@mediapipe/tasks-vision': specifier: ^0.10.18 version: 0.10.34 @@ -227,7 +227,7 @@ importers: version: 5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@24.12.2)(typescript@5.9.3) livekit-client: specifier: ^2.18.1 - version: 2.18.8(@types/dom-mediacapture-record@1.0.22) + version: 2.18.9(@types/dom-mediacapture-record@1.0.22) lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -236,7 +236,7 @@ importers: version: 1.9.2 matrix-js-sdk: specifier: matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/349e8c5023b74b7ee17b2e9a0cba6dfce6818d68 matrix-widget-api: specifier: ^1.16.1 version: 1.17.0 @@ -2995,8 +2995,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.1': - resolution: {integrity: sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==} + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -3013,8 +3013,8 @@ packages: resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.1': - resolution: {integrity: sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==} + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.58.2': @@ -3029,8 +3029,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.59.1': - resolution: {integrity: sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==} + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -3058,6 +3058,10 @@ packages: resolution: {integrity: sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3079,8 +3083,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.59.1': - resolution: {integrity: sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==} + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -3105,8 +3109,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.1': - resolution: {integrity: sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==} + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -3124,12 +3128,13 @@ packages: resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.1': - resolution: {integrity: sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==} + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@use-gesture/core@10.3.1': resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} @@ -5021,8 +5026,8 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - livekit-client@2.18.8: - resolution: {integrity: sha512-E+bSpnBVng/1xG4RfL1Q51dHUpBwL14Wix4sR5bS0djEzKMEtrxcUyhWLltdwQ0USf1t0PaxW6WL4oVb2s4Fsw==} + livekit-client@2.18.9: + resolution: {integrity: sha512-l0cADcxxBCWCBMtU9eWY6RpdbRfgA5c1/05yngQXo08mcy3VOttmSE2pNZ74k2B2zQym149g5/Y1B3vq2FWwlw==} peerDependencies: '@types/dom-mediacapture-record': ^1 @@ -5096,8 +5101,8 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d: - resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d} + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/349e8c5023b74b7ee17b2e9a0cba6dfce6818d68: + resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/349e8c5023b74b7ee17b2e9a0cba6dfce6818d68} version: 41.4.0 engines: {node: '>=22.0.0'} @@ -5995,6 +6000,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -8204,21 +8214,21 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@livekit/components-core@0.12.13(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)': + '@livekit/components-core@0.12.13(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)': dependencies: '@floating-ui/dom': 1.7.4 - livekit-client: 2.18.8(@types/dom-mediacapture-record@1.0.22) + livekit-client: 2.18.9(@types/dom-mediacapture-record@1.0.22) loglevel: 1.9.1 rxjs: 7.8.2 tslib: 2.8.1 - '@livekit/components-react@2.9.20(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)': + '@livekit/components-react@2.9.20(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)': dependencies: - '@livekit/components-core': 0.12.13(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) + '@livekit/components-core': 0.12.13(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) clsx: 2.1.1 events: 3.3.0 jose: 6.2.2 - livekit-client: 2.18.8(@types/dom-mediacapture-record@1.0.22) + livekit-client: 2.18.9(@types/dom-mediacapture-record@1.0.22) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) tslib: 2.8.1 @@ -8234,11 +8244,11 @@ snapshots: dependencies: '@bufbuild/protobuf': 1.10.1 - '@livekit/track-processors@0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22))': + '@livekit/track-processors@0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))': dependencies: '@mediapipe/tasks-vision': 0.10.34 '@types/dom-mediacapture-transform': 0.1.11 - livekit-client: 2.18.8(@types/dom-mediacapture-record@1.0.22) + livekit-client: 2.18.9(@types/dom-mediacapture-record@1.0.22) '@matrix-org/matrix-sdk-crypto-wasm@18.2.0': {} @@ -9501,10 +9511,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -9525,10 +9535,10 @@ snapshots: '@typescript-eslint/types': 8.59.0 '@typescript-eslint/visitor-keys': 8.59.0 - '@typescript-eslint/scope-manager@8.59.1': + '@typescript-eslint/scope-manager@8.59.2': dependencies: - '@typescript-eslint/types': 8.59.1 - '@typescript-eslint/visitor-keys': 8.59.1 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 '@typescript-eslint/tsconfig-utils@8.58.2(typescript@5.9.3)': dependencies: @@ -9538,7 +9548,7 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -9562,6 +9572,8 @@ snapshots: '@typescript-eslint/types@8.59.1': {} + '@typescript-eslint/types@8.59.2': {} + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 5.62.0 @@ -9606,15 +9618,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.9.3) - '@typescript-eslint/types': 8.59.1 - '@typescript-eslint/visitor-keys': 8.59.1 + '@typescript-eslint/project-service': 8.59.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.0 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -9658,12 +9670,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.1(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.2(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.59.1 - '@typescript-eslint/types': 8.59.1 - '@typescript-eslint/typescript-estree': 8.59.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: @@ -9684,9 +9696,9 @@ snapshots: '@typescript-eslint/types': 8.59.0 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.59.1': + '@typescript-eslint/visitor-keys@8.59.2': dependencies: - '@typescript-eslint/types': 8.59.1 + '@typescript-eslint/types': 8.59.2 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} @@ -10847,7 +10859,7 @@ snapshots: eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.59.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 optionalDependencies: '@typescript-eslint/eslint-plugin': 8.59.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) @@ -11907,7 +11919,7 @@ snapshots: lines-and-columns@1.2.4: {} - livekit-client@2.18.8(@types/dom-mediacapture-record@1.0.22): + livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22): dependencies: '@livekit/mutex': 1.1.1 '@livekit/protocol': 1.45.3 @@ -11983,7 +11995,7 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/349e8c5023b74b7ee17b2e9a0cba6dfce6818d68: dependencies: '@babel/runtime': 7.29.2 '@matrix-org/matrix-sdk-crypto-wasm': 18.2.0 @@ -13060,6 +13072,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.0: {} + set-blocking@2.0.0: {} set-cookie-parser@2.7.2: {} From 78417ff2cb37baad149d7b762fdac7b3b29696df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 09:05:59 +0200 Subject: [PATCH 09/11] Update dependency @livekit/components-react to v2.9.21 (#3957) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- pnpm-lock.yaml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3ad458e..544da4cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,7 +50,7 @@ importers: version: 0.12.13(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) '@livekit/components-react': specifier: ^2.0.0 - version: 2.9.20(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1) + version: 2.9.21(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1) '@livekit/protocol': specifier: ^1.42.2 version: 1.45.6 @@ -1556,12 +1556,12 @@ packages: livekit-client: ^2.17.2 tslib: ^2.6.2 - '@livekit/components-react@2.9.20': - resolution: {integrity: sha512-hjkYOsJj9Jbghb7wM5cI8HoVisKeL6Zcy1VnRWTLm0sqVbto8GJp/17T4Udx85mCPY6Jgh8I1Cv0yVzgz7CQtg==} + '@livekit/components-react@2.9.21': + resolution: {integrity: sha512-6hU9VucJJL+gAhilNGe4MBCDCZVk64qyjP9Ck86krvOIdVU76WeWksddg1MYUP10AlUwwrfD7davz41pJTcMJw==} engines: {node: '>=18'} peerDependencies: '@livekit/krisp-noise-filter': ^0.2.12 || ^0.3.0 - livekit-client: ^2.17.2 + livekit-client: ^2.18.2 react: '>=18' react-dom: '>=18' tslib: ^2.6.2 @@ -4842,9 +4842,6 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.2: - resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} - jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} @@ -8222,12 +8219,12 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@livekit/components-react@2.9.20(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)': + '@livekit/components-react@2.9.21(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)': dependencies: '@livekit/components-core': 0.12.13(livekit-client@2.18.9(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) clsx: 2.1.1 events: 3.3.0 - jose: 6.2.2 + jose: 6.2.3 livekit-client: 2.18.9(@types/dom-mediacapture-record@1.0.22) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) @@ -11748,8 +11745,6 @@ snapshots: jiti@2.6.1: {} - jose@6.2.2: {} - jose@6.2.3: {} js-tokens@10.0.0: {} From f4ff790d2caa4638078416c9e0a37fc7653458e6 Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Mon, 11 May 2026 17:50:33 +0800 Subject: [PATCH 10/11] Add `MediaMuteAndSwitchButton` component (storybook only) (#3938) * add MeidaMuteAndSwitchButton * User button in footer * Add tests * update styling (dark bg on menu open + chevron white + chevron up) * fix tests * add storybook to CI only add storybook with storybook label test names another env name test TestName new default name remove label condition Update pr-deploy.yaml * Update pr-deploy.yaml * add toggle example to default component * hook up footer select actions * fix video audio button (swapped) and lable in story --- src/components/CallFooter.stories.tsx | 18 ++ src/components/CallFooter.tsx | 88 +++++-- .../MediaMuteAndSwitchButton.module.css | 37 +++ .../MediaMuteAndSwitchButton.stories.tsx | 117 +++++++++ .../MediaMuteAndSwitchButton.test.tsx | 226 +++++++++++++++++ src/components/MediaMuteAndSwitchButton.tsx | 230 ++++++++++++++++++ .../MediaMuteAndSwitchButton.test.tsx.snap | 43 ++++ 7 files changed, 741 insertions(+), 18 deletions(-) create mode 100644 src/components/MediaMuteAndSwitchButton.module.css create mode 100644 src/components/MediaMuteAndSwitchButton.stories.tsx create mode 100644 src/components/MediaMuteAndSwitchButton.test.tsx create mode 100644 src/components/MediaMuteAndSwitchButton.tsx create mode 100644 src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap diff --git a/src/components/CallFooter.stories.tsx b/src/components/CallFooter.stories.tsx index 20c7c4c0..7090a338 100644 --- a/src/components/CallFooter.stories.tsx +++ b/src/components/CallFooter.stories.tsx @@ -88,6 +88,24 @@ export const Default: Story = { }, }; +export const WithAudioAndVideoOptions: Story = { + ...Default, + args: { + ...Default.args, + audioEnabled: false, + videoEnabled: true, + audioOptions: [ + { label: "Microphone 1", id: "1" }, + { label: "Microphone 2", id: "2" }, + ], + videoOptions: [ + { label: "Camera 1", id: "1" }, + { label: "Camera 2", id: "2" }, + ], + selectedAudio: "2", + selectedVideo: "1", + }, +}; export const WithLogo: Story = { ...Default, args: { diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx index 9d59d2d1..afc5bdc9 100644 --- a/src/components/CallFooter.tsx +++ b/src/components/CallFooter.tsx @@ -30,6 +30,10 @@ import { } from "../button"; import styles from "./CallFooter.module.css"; import { type GridMode } from "../state/CallViewModel/CallViewModel"; +import { + MediaMuteAndSwitchButton, + type MenuOptions, +} from "./MediaMuteAndSwitchButton"; export interface AudioOutputSwitcher { targetOutput: string; @@ -79,6 +83,13 @@ export interface FooterProps { // debug stuff debugTileLayout?: boolean; tileStoreGeneration?: number; + + audioOptions?: MenuOptions[]; + videoOptions?: MenuOptions[]; + selectedAudio?: string; + selectedVideo?: string; + selectAudioDevice?: (deviceId: string) => void; + selectVideoDevice?: (deviceId: string) => void; } export const CallFooter: FC = ({ @@ -104,6 +115,13 @@ export const CallFooter: FC = ({ hangup, debugTileLayout, tileStoreGeneration, + + audioOptions, + videoOptions, + selectedAudio, + selectedVideo, + selectAudioDevice, + selectVideoDevice, }) => { const buttons: JSX.Element[] = []; const buttonSize = asPip ? "md" : "lg"; @@ -125,24 +143,58 @@ export const CallFooter: FC = ({ ); } - buttons.push( - , - , - ); + if ((audioOptions?.length ?? 0) > 0) { + buttons.push( + , + ); + } else { + buttons.push( + , + ); + } + if ((videoOptions?.length ?? 0) > 0) { + buttons.push( + , + ); + } else { + buttons.push( + , + ); + } if (toggleScreenSharing !== undefined) { buttons.push( diff --git a/src/components/MediaMuteAndSwitchButton.module.css b/src/components/MediaMuteAndSwitchButton.module.css new file mode 100644 index 00000000..e5bba238 --- /dev/null +++ b/src/components/MediaMuteAndSwitchButton.module.css @@ -0,0 +1,37 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +.container { + display: flex; + flex-direction: row; + align-items: center; + background-color: var(--cpd-color-bg-subtle-secondary); + border-radius: 32px; + transition: background-color 0.2s ease-in-out; +} +.containerOpen { + background-color: var(--cpd-color-bg-action-primary-pressed); +} +.chevronIconOpen > svg { + color: var(--cpd-color-icon-on-solid-primary); +} +.menuButton { + width: 40px; + background-color: transparent !important; +} +.itemIcon { + color: var(--cpd-color-text-secondary); +} + +.rotate { + animation: spinner 1.5s linear infinite; +} +@keyframes spinner { + to { + transform: rotate(360deg); + } +} diff --git a/src/components/MediaMuteAndSwitchButton.stories.tsx b/src/components/MediaMuteAndSwitchButton.stories.tsx new file mode 100644 index 00000000..bbf9f159 --- /dev/null +++ b/src/components/MediaMuteAndSwitchButton.stories.tsx @@ -0,0 +1,117 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { AdvancedSettingsIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { fn, userEvent, within, expect } from "storybook/test"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { MediaMuteAndSwitchButton } from "./MediaMuteAndSwitchButton"; + +const meta = { + component: MediaMuteAndSwitchButton, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: "SomeMenu", + iconsAndLabels: { + IconEnabled: AdvancedSettingsIcon, + IconDisabled: AdvancedSettingsIcon, + enabledLabel: "Enabled", + disabledLabel: "Disabled", + optionsButtonLabel: "Options", + }, + enabled: true, + options: [ + { label: "option 1", id: "1" }, + { label: "option 2", id: "2" }, + ], + selectedOption: "1", + onMuteClick: fn(), + onSelect: fn(), + }, +}; + +export const AudioMute: Story = { + args: { + ...Default.args, + title: "Microphone", + iconsAndLabels: "audio", + enabled: false, + options: [ + { label: "Microphone 1", id: "1" }, + { label: "Microphone 2", id: "2" }, + ], + toggles: [ + { + label: "example toggle", + id: "t0", + enabled: true, + }, + ], + selectedOption: "2", + }, + play: async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + // Both the mute button and the chevron trigger currently share the aria-label "Edit" + // (both are TODO placeholders in the component). The mute button is first in the DOM. + const muteButton = canvas.getByLabelText("Unmute microphone"); + await userEvent.click(muteButton); + await expect(args.onMuteClick).toHaveBeenCalled(); + }, +}; + +export const AudioUnmute: Story = { + args: { + title: "Microphone", + iconsAndLabels: "audio", + enabled: true, + options: [ + { label: "Microphone 1", id: "1" }, + { label: "Microphone 2", id: "2" }, + ], + toggles: [], + selectedOption: "2", + }, +}; + +export const VideoMute: Story = { + args: { + title: "Camera", + iconsAndLabels: "video", + enabled: false, + options: [ + { label: "Camera 1", id: "1" }, + { label: "Camera 2", id: "2" }, + ], + toggles: [], + selectedOption: "1", + }, +}; + +export const VideoUnmute: Story = { + args: { + title: "Camera", + iconsAndLabels: "video", + enabled: true, + options: [ + { label: "Camera 1", id: "1" }, + { label: "Camera 2", id: "2" }, + ], + toggles: [ + { + label: "Blur Background", + id: "background_blurring", + enabled: false, + }, + ], + selectedOption: "2", + }, +}; diff --git a/src/components/MediaMuteAndSwitchButton.test.tsx b/src/components/MediaMuteAndSwitchButton.test.tsx new file mode 100644 index 00000000..42a8d970 --- /dev/null +++ b/src/components/MediaMuteAndSwitchButton.test.tsx @@ -0,0 +1,226 @@ +/* +Copyright 2023, 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { describe, expect, test, vi } from "vitest"; +import { act, render, screen, type RenderResult } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { type JSX, useState } from "react"; + +import { MediaMuteAndSwitchButton } from "./MediaMuteAndSwitchButton"; + +describe("MediaMuteAndSwitchButton", () => { + test("renders", () => { + const { container } = render( + , + ); + expect(container).toMatchSnapshot(); + }); + + test("renders correct audio and video labels", () => { + const renderLabels = ( + type: "video" | "audio", + enabled: boolean, + ): RenderResult => { + return render( + , + ); + }; + const renderAudioEndabled = renderLabels("audio", true); + const renderAudioDisabled = renderLabels("audio", false); + const renderVideoEnabled = renderLabels("video", true); + const renderVideoDisabled = renderLabels("video", false); + + expect( + renderAudioEndabled.getByRole("button", { name: "Mute microphone" }), + ).toBeInTheDocument(); + expect( + renderAudioDisabled.getByRole("button", { name: "Unmute microphone" }), + ).toBeInTheDocument(); + expect( + renderVideoEnabled.getByRole("button", { name: "Start video" }), + ).toBeInTheDocument(); + expect( + renderVideoDisabled.getByRole("button", { name: "Stop video" }), + ).toBeInTheDocument(); + }); + + test("calls mute on mute press", async () => { + const user = userEvent.setup(); + const onMute = vi.fn(); + const { getByRole } = render( + , + ); + + await user.click(getByRole("button", { name: "Mute microphone" })); + + expect(onMute).toHaveBeenCalled(); + }); + + test("calls select callback on menu click", async () => { + const user = userEvent.setup(); + const onSelect = vi.fn(); + const { getByRole } = render( + , + ); + + await user.click(getByRole("button", { name: "Microphone" })); + await user.click(screen.getByRole("menuitem", { name: "Microphone 2" })); + + expect(onSelect).toHaveBeenCalledWith("mic2"); + }); + test("does not call select callback on already selected menu click", async () => { + const user = userEvent.setup(); + const onSelect = vi.fn(); + const { getByRole } = render( + , + ); + + await user.click(getByRole("button", { name: "Microphone" })); + await user.click(screen.getByRole("menuitem", { name: "Microphone 1" })); + + expect(onSelect).not.toHaveBeenCalled(); + }); + + test("renders menu spinner until selection updates for the component", async () => { + const user = userEvent.setup(); + const { promise, resolve } = Promise.withResolvers(); + const onSelectPressed = vi.fn(); + const onOptionUpdated = vi.fn(); + function Wrapper(): JSX.Element { + const [selectedOption, setSelectedOption] = useState("mic1"); + return ( + { + onSelectPressed(); + void promise.then(() => { + setSelectedOption(id); + onOptionUpdated(); + }); + }} + /> + ); + } + + const { getByRole } = render(); + + await user.click(getByRole("button", { name: "Microphone" })); + await user.click(screen.getByRole("menuitem", { name: "Microphone 2" })); + + expect(onSelectPressed).toHaveBeenCalled(); + expect(onOptionUpdated).not.toHaveBeenCalled(); + // After clicking, plannedSelection="mic2" but selectedOption is still "mic1", + // so a spinner should appear on the mic2 item + const mic2Item = screen.getByRole("menuitem", { name: "Microphone 2" }); + expect(mic2Item.querySelector(".rotate")).toBeTruthy(); + + // The currently-selected mic1 item should not have a spinner + const mic1Item = screen.getByRole("menuitem", { name: "Microphone 1" }); + expect(mic1Item.querySelector(".rotate")).toBeNull(); + await act(async () => { + // resolve the promise that acutally updates the select option. + resolve(); + await promise; + }); + + expect(onOptionUpdated).toHaveBeenCalled(); + // Spinner should now be gone since the selection has caught up + const mic2ItemAfter = screen.getByRole("menuitem", { + name: "Microphone 2", + }); + expect(mic2ItemAfter.querySelector(".rotate")).toBeNull(); + }); + + test("renders menu with toggle control and calls toggle callback", async () => { + const user = userEvent.setup(); + const onSelect = vi.fn(); + const { getByRole } = render( + , + ); + + await user.click(getByRole("button", { name: "Microphone" })); + + const toggle = screen.getByRole("menuitemcheckbox", { + name: "Background blur", + }); + expect(toggle).toBeInTheDocument(); + expect(toggle).toHaveAttribute("aria-checked", "false"); + + await user.click(toggle); + + expect(onSelect).toHaveBeenCalledWith("bg_blur"); + }); + + test("renders check icon to mark the selected menu item", async () => { + const user = userEvent.setup(); + const { getByRole } = render( + , + ); + + // open menu + await user.click(getByRole("button", { name: "Microphone" })); + + // The selected item (mic2) renders both an IconOptions SVG and a CheckIcon SVG + const mic1Item = screen.getByRole("menuitem", { name: "Microphone 2" }); + expect(mic1Item.querySelectorAll("svg").length).toBe(2); + + // The unselected item (mic1) only renders its IconOptions SVG + const mic2Item = screen.getByRole("menuitem", { name: "Microphone 1" }); + expect(mic2Item.querySelectorAll("svg").length).toBe(1); + }); +}); diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx new file mode 100644 index 00000000..7e38c7c6 --- /dev/null +++ b/src/components/MediaMuteAndSwitchButton.tsx @@ -0,0 +1,230 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { type ComponentType, useState, type FC } from "react"; +import { + Button, + Menu, + MenuItem, + ToggleMenuItem, +} from "@vector-im/compound-web"; +import { t } from "i18next"; +import { + CheckIcon, + ChevronUpIcon, + ChevronDownIcon, + MicOffSolidIcon, + MicOnIcon, + MicOnSolidIcon, + SpinnerIcon, + VideoCallIcon, + VideoCallOffSolidIcon, + VideoCallSolidIcon, +} from "@vector-im/compound-design-tokens/assets/web/icons"; +import classNames from "classnames"; +import { logger } from "matrix-js-sdk/lib/logger"; + +import styles from "./MediaMuteAndSwitchButton.module.css"; + +export interface MenuOptions { + label: string; + id: string; +} +export interface ToggleOption { + label: string; + enabled: boolean; + id: string; +} + +export interface IconsAndLabels { + /** The Icon used if the mute button is enabled */ + IconEnabled: ComponentType>; + /** The Icon used if the mute button is disabled */ + IconDisabled: ComponentType>; + /** The icon used for the different options */ + IconOptions?: ComponentType>; + enabledLabel: string; + disabledLabel: string; + optionsButtonLabel: string; +} + +export interface MediaMuteAndSwitchButtonProps { + /** The title used in the Switcher modal. */ + title: string; + /** If the Mute button is enabled */ + enabled?: boolean; + /** Callback if the mute button is clicked */ + onMuteClick?: () => void; + iconsAndLabels?: "video" | "audio" | IconsAndLabels; + /** The options available for the media device selector modal */ + options?: MenuOptions[]; + /** The option that will currently be rendered as the selected option */ + selectedOption?: string; + /** + * The available toggles (including there current state) + * The toggle state is not stored by this component. + * It is handled externally and needs to be set by listening to the `onSelect` callback and setting the right toggle item to `enabled` + */ + toggles?: ToggleOption[]; + /** + * For any toggle and option this method will be called. + * So toggles need to be implemented by listening here and setting the right toggle item to `enabled` + */ + onSelect?: (id: string) => void; +} + +export const MediaMuteAndSwitchButton: FC = ({ + title, + enabled, + onMuteClick, + iconsAndLabels: iconsAndLabelsWithDefaultCases, + options, + selectedOption, + toggles, + onSelect, +}) => { + const [plannedSelection, setPlannedSelection] = useState(null); + const [menuOpen, setMenuOpen] = useState(false); + let iconsAndLabels: IconsAndLabels | undefined; + switch (iconsAndLabelsWithDefaultCases) { + case "video": + iconsAndLabels = { + IconEnabled: VideoCallSolidIcon, + IconDisabled: VideoCallOffSolidIcon, + IconOptions: VideoCallIcon, + disabledLabel: t("stop_video_button_label"), + enabledLabel: t("start_video_button_label"), + optionsButtonLabel: t("settings.devices.microphone"), + }; + break; + case "audio": + iconsAndLabels = { + IconEnabled: MicOnSolidIcon, + IconDisabled: MicOffSolidIcon, + IconOptions: MicOnIcon, + disabledLabel: t("mute_microphone_button_label"), + enabledLabel: t("unmute_microphone_button_label"), + optionsButtonLabel: t("settings.devices.microphone"), + }; + break; + default: + iconsAndLabels = iconsAndLabelsWithDefaultCases; + break; + } + const { + IconEnabled, + IconDisabled, + IconOptions, + disabledLabel, + enabledLabel, + optionsButtonLabel, + } = iconsAndLabels ?? { + IconEnabled: undefined, + IconDisabled: undefined, + IconOptions: undefined, + disabledLabel: undefined, + enabledLabel: undefined, + optionsButtonLabel: undefined, + }; + { + logger.info( + "RENDER WITH: selectedOption !== option.id && plannedSelection === option.id", + selectedOption, + " !==", + "option.id", + " && ", + plannedSelection, + " === ", + "option.id", + ); + } + return ( +
+ {/* The mute button lives inside */} +
+ ); +}; diff --git a/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap b/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap new file mode 100644 index 00000000..84ea220a --- /dev/null +++ b/src/components/__snapshots__/MediaMuteAndSwitchButton.test.tsx.snap @@ -0,0 +1,43 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`MediaMuteAndSwitchButton > renders 1`] = ` +
+
+ +
+
+`; From 408a38a7d6b5b74ca6ae332f02d8b2f9237103ea Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 12 May 2026 15:46:38 +0200 Subject: [PATCH 11/11] bugfix: When starting muted, have to click twice to unmute in call --- playwright/create-call.spec.ts | 35 +++++++++++++++++++ .../CallViewModel/localMember/Publisher.ts | 6 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/playwright/create-call.spec.ts b/playwright/create-call.spec.ts index b71f39ad..01dd8163 100644 --- a/playwright/create-call.spec.ts +++ b/playwright/create-call.spec.ts @@ -58,3 +58,38 @@ test("Start a new call then leave and show the feedback screen", async ({ page.getByRole("link", { name: "Not now, return to home screen" }), ).toBeVisible(); }); + +test("BugFix: When unmuting in lobby, you had to click twice to unmute in call", async ({ + page, +}) => { + await page.goto("/"); + + await page.getByTestId("home_callName").click(); + await page.getByTestId("home_callName").fill("DoubleUnMute"); + await page.getByTestId("home_displayName").click(); + await page.getByTestId("home_displayName").fill("me"); + await page.getByTestId("home_go").click(); + + const microphoneButton = page.getByTestId("incall_mute"); + const cameraButton = page.getByTestId("incall_videomute"); + + await microphoneButton.click(); + await cameraButton.click(); + + // Should be muted now + await expect(microphoneButton).toHaveAccessibleName("Unmute microphone"); + await expect(cameraButton).toHaveAccessibleName("Start video"); + + // Create the call and join + await page.getByTestId("lobby_joinCall").click(); + + // Give sometime for the all to be connected + // Check the number of participants + await expect(page.locator("div").filter({ hasText: /^1$/ })).toBeVisible(); + + // Click again on the mute button. it should unmute + await microphoneButton.click(); + await expect(microphoneButton).toHaveAccessibleName("Mute microphone"); + await cameraButton.click(); + await expect(cameraButton).toHaveAccessibleName("Stop video"); +}); diff --git a/src/state/CallViewModel/localMember/Publisher.ts b/src/state/CallViewModel/localMember/Publisher.ts index b7841c49..a6435212 100644 --- a/src/state/CallViewModel/localMember/Publisher.ts +++ b/src/state/CallViewModel/localMember/Publisher.ts @@ -379,10 +379,11 @@ export class Publisher { if (!this.shouldPublish && enable) { await this.pauseUpstreams(lkRoom, [Track.Source.Microphone]); } + return enable; } catch (e) { this.logger.error("Failed to update LiveKit audio input mute state", e); + return lkRoom.localParticipant.isMicrophoneEnabled; } - return lkRoom.localParticipant.isMicrophoneEnabled; }); this.muteStates.video.setHandler(async (enable) => { try { @@ -393,10 +394,11 @@ export class Publisher { if (!this.shouldPublish && enable) { await this.pauseUpstreams(lkRoom, [Track.Source.Camera]); } + return enable; } catch (e) { this.logger.error("Failed to update LiveKit video input mute state", e); + return lkRoom.localParticipant.isCameraEnabled; } - return lkRoom.localParticipant.isCameraEnabled; }); }