---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 30482888..770a7361 100644
--- a/README.md
+++ b/README.md
@@ -118,8 +118,8 @@ For operating and deploying Element Call on your own server, refer to the
## 🧭 MatrixRTC Backend Discovery and Selection
For proper Element Call operation each site deployment needs a MatrixRTC backend
-setup as outlined in the [Self-Hosting](#self_hosting). A typical federated site
-deployment for three different sites A, B and C is depicted below.
+setup as outlined in the [Self-Hosting Guide](./docs/self_hosting.md). A typical
+federated site deployment for three different sites A, B and C is depicted below.
From b0c1c48ad4f4da0874808515df8cb0a4744a80a1 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 08:26:42 +0200
Subject: [PATCH 055/100] Add missing article
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 770a7361..27e94c9d 100644
--- a/README.md
+++ b/README.md
@@ -127,7 +127,7 @@ federated site deployment for three different sites A, B and C is depicted below
### Backend Discovery
-MatrixRTC backend (according to
+The MatrixRTC backend (according to
[MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)) is
announced by the Matrix site's `.well-known/matrix/client` file and discovered
via the `org.matrix.msc4143.rtc_foci` key, e.g.:
From abce4d16fc43d2817254831ffac5a42a15955c5f Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 08:29:47 +0200
Subject: [PATCH 056/100] Add missing article
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 27e94c9d..65b504d4 100644
--- a/README.md
+++ b/README.md
@@ -151,7 +151,7 @@ via `livekit_service_url`.
- Each call participant proposes their discovered MatrixRTC backend from
`org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
-- For **LiveKit** MatrixRTC backend
+- For the **LiveKit** MatrixRTC backend
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
the **first participant who joined the call** defines via the `foci_preferred`
key in their `org.matrix.msc3401.call.member` which actual MatrixRTC backend
From 0160f60c6d0103458cd5d72ef22965aaa7fa8be1 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 08:33:36 +0200
Subject: [PATCH 057/100] Reorder sentence fragments for better readability
Signed-off-by: Johannes Marbach
---
README.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 65b504d4..9421d9e5 100644
--- a/README.md
+++ b/README.md
@@ -153,9 +153,8 @@ via `livekit_service_url`.
`org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
- For the **LiveKit** MatrixRTC backend
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
- the **first participant who joined the call** defines via the `foci_preferred`
- key in their `org.matrix.msc3401.call.member` which actual MatrixRTC backend
- will be used for this call.
+ the **first participant who joined the call** defines which backend will be used for this call via
+ the `foci_preferred` key in their `org.matrix.msc3401.call.member` state event.
- During the actual call join flow, the **[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)**
provides the client with the **LiveKit SFU WebSocket URL** and an
**access JWT token** in order to exchange media via WebRTC.
From 69b5209071911046fef78d4ad05d415a0e663cc9 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 08:40:08 +0200
Subject: [PATCH 058/100] Fix typo
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9421d9e5..827ea6ec 100644
--- a/README.md
+++ b/README.md
@@ -209,7 +209,7 @@ A docker compose file `dev-backend-docker-compose.yml` is provided to start the
whole stack of components which is required for a local development environment
including federation:
-- Minimum Synapse Setup (servernameis: `synapse.m.localhost`, `synapse.othersite.m.localhost`)
+- Minimum Synapse Setup (servernames: `synapse.m.localhost`, `synapse.othersite.m.localhost`)
- MatrixRTC Authorization Service (Note requires Federation API and hence a TLS reverse proxy)
- Minimum LiveKit SFU setup using dev defaults for config
- Minimum `localhost` Certificate Authority (CA) for Transport Layer Security (TLS)
From 9d522913f938c950246e60277d2696438c04d250 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 09:46:41 +0200
Subject: [PATCH 059/100] Add missing colon
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 827ea6ec..dc4432eb 100644
--- a/README.md
+++ b/README.md
@@ -210,7 +210,7 @@ whole stack of components which is required for a local development environment
including federation:
- Minimum Synapse Setup (servernames: `synapse.m.localhost`, `synapse.othersite.m.localhost`)
-- MatrixRTC Authorization Service (Note requires Federation API and hence a TLS reverse proxy)
+- MatrixRTC Authorization Service (Note: requires Federation API and hence a TLS reverse proxy)
- Minimum LiveKit SFU setup using dev defaults for config
- Minimum `localhost` Certificate Authority (CA) for Transport Layer Security (TLS)
- Hostnames: `m.localhost`, `*.m.localhost`, `*.othersite.m.localhost`
From 198d9616464f605de907c60cc1fe24be2300938a Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 09:48:46 +0200
Subject: [PATCH 060/100] Add missing apostrophe
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index dc4432eb..a421bdcf 100644
--- a/README.md
+++ b/README.md
@@ -214,7 +214,7 @@ including federation:
- Minimum LiveKit SFU setup using dev defaults for config
- Minimum `localhost` Certificate Authority (CA) for Transport Layer Security (TLS)
- Hostnames: `m.localhost`, `*.m.localhost`, `*.othersite.m.localhost`
- - Add [./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt) to your web browsers trusted
+ - Add [./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt) to your web browser's trusted
certificates
- Minimum TLS reverse proxy for
- Synapse homeserver: `synapse.m.localhost` and `synapse.othersite.m.localhost`
From 0ca2b6d4ea88d6097ddca8b057f0801774f96b85 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 09:49:58 +0200
Subject: [PATCH 061/100] Remove extra space and add colon
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a421bdcf..a3a0c1bf 100644
--- a/README.md
+++ b/README.md
@@ -230,7 +230,7 @@ Run backend components:
```sh
pnpm backend
-# or for podman-compose
+# or for podman-compose:
# podman-compose -f dev-backend-docker-compose.yml up
```
From 80f573e8997326d10f4c5baabdc4c5c7ba6c2b71 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 09:50:42 +0200
Subject: [PATCH 062/100] Add missing apostrophe
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a3a0c1bf..758d6a8a 100644
--- a/README.md
+++ b/README.md
@@ -241,7 +241,7 @@ pnpm backend
> `https://synapse.m.localhost/.well-known/matrix/client`. This can be either
> done by adding the minimum localhost CA
> ([./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt)) to your web
-> browsers trusted certificates or by simply copying and pasting each URL into
+> browser's trusted certificates or by simply copying and pasting each URL into
> your browser’s address bar and follow the prompts to add the exception.
### Playwright tests
From 18a128b86527b6b62379b7b52a777a8929ccce05 Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 09:56:37 +0200
Subject: [PATCH 063/100] Fix linking link
Signed-off-by: Johannes Marbach
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 758d6a8a..31253cda 100644
--- a/README.md
+++ b/README.md
@@ -201,7 +201,7 @@ pnpm dev
See also:
-- [Developing with linked packages](./linking.md)
+- [Developing with linked packages](./docs/linking.md)
### Backend
From 49f6c74818580337556660cc95a86c36b040c10d Mon Sep 17 00:00:00 2001
From: Johannes Marbach
Date: Mon, 18 May 2026 10:50:00 +0200
Subject: [PATCH 064/100] Minor documentation fixes
Signed-off-by: Johannes Marbach
---
docs/controls.md | 2 +-
docs/embedded_standalone.md | 4 ++--
docs/linking.md | 4 ++--
docs/linking_concept_reasoning.md | 6 +++---
docs/self_hosting.md | 12 ++++++------
docs/url_params.md | 22 +++++++++++-----------
6 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/docs/controls.md b/docs/controls.md
index e5e0746d..b97fe795 100644
--- a/docs/controls.md
+++ b/docs/controls.md
@@ -12,7 +12,7 @@ A few aspects of Element Call's interface can be controlled through a global API
On mobile platforms (iOS, Android), web views do not reliably support selecting audio output devices such as the main speaker, earpiece, or headset. To address this limitation, the following functions allow the hosting application (e.g., Element Web, Element X) to manage audio devices via exposed JavaScript interfaces. These functions must be enabled using the URL parameter `controlledAudioDevices` to take effect.
-- `controls.setAvailableAudioDevices(devices: { id: string, name: string, forEarpiece?: boolean, isEarpiece?: boolean isSpeaker?: boolean, isExternalHeadset?, boolean; }[]): void` Sets the list of available audio outputs. `forEarpiece` is used on iOS only.
+- `controls.setAvailableAudioDevices(devices: { id: string, name: string, forEarpiece?: boolean, isEarpiece?: boolean isSpeaker?: boolean, isExternalHeadset?: boolean }[]): void` Sets the list of available audio outputs. `forEarpiece` is used on iOS only.
It flags the device that should be used if the user selects earpiece mode. This should be the main stereo loudspeaker of the device.
- `controls.onAudioDeviceSelect: ((id: string) => void) | undefined` Callback called whenever the user or application selects a new audio output.
- `controls.setAudioDevice(id: string): void` Sets the selected audio device in Element Call's menu. This should be used if the OS decides to automatically switch to Bluetooth, for example.
diff --git a/docs/embedded_standalone.md b/docs/embedded_standalone.md
index 24ad2a7d..456ce120 100644
--- a/docs/embedded_standalone.md
+++ b/docs/embedded_standalone.md
@@ -25,7 +25,7 @@ The basics are:
1. Add the appropriate platform dependency as given for a [release](https://github.com/element-hq/element-call/releases), or use the embedded tarball. e.g. `npm install @element-hq/element-call-embedded@0.9.0`
2. Include the assets from the platform dependency in the build process. e.g. copy the assets during a [Webpack](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/webpack.config.js#L677-L682) build.
-3. Use the `index.html` entrypointof the imported assets when you are constructing the WebView or iframe. e.g. using a [relative path in a webapp](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/src/models/Call.ts#L680), or on the the Android [WebViewAssetLoader](https://github.com/element-hq/element-x-android/blob/fe5aab6588ecdcf9354a3bfbd9e97c1b31175a8f/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt#L20)
+3. Use the `index.html` entrypoint of the imported assets when you are constructing the WebView or iframe. e.g. using a [relative path in a webapp](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/src/models/Call.ts#L680), or on the the Android [WebViewAssetLoader](https://github.com/element-hq/element-x-android/blob/fe5aab6588ecdcf9354a3bfbd9e97c1b31175a8f/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt#L20)
4. Set any of the [embedded-only URL parameters](./url_params.md#embedded-only-parameters) that you need.
## Widget vs standalone mode
@@ -35,5 +35,5 @@ Element Call is developed using the [js-sdk](https://github.com/matrix-org/matri
As a widget, the app only uses the core calling (MatrixRTC) parts. The rest (authentication, sending events, getting room state updates about calls) is done by the hosting client.
Element Call and the hosting client are connected via the widget API.
-Element Call detects that it is run as a widget if a widgetId is defined in the url parameters. If `widgetId` is present then Element Call will try to connect to the client via the widget postMessage API using the parameters provided in [Url Format and parameters
+Element Call detects that it is run as a widget if `widgetId` is defined in the url parameters. If `widgetId` is present then Element Call will try to connect to the client via the widget postMessage API using the parameters provided in [Url Format and parameters
](./url_params.md).
diff --git a/docs/linking.md b/docs/linking.md
index 1016fffb..3a18844d 100644
--- a/docs/linking.md
+++ b/docs/linking.md
@@ -1,6 +1,6 @@
## Quickstart guide
-run
+Run:
```bash
./scripts/setup-linking.sh
@@ -50,7 +50,7 @@ before committing a change.
To make this less of a foot gun we added a git hook.
A `pre-commit` hook will check if linking is currently used. If it detects
a `.pnpmfile.cjs` file it will abort the commit with an explanatory message.
-You will than need to run `pnpm links:off` and commit again.
+You will then need to run `pnpm links:off` and commit again.
To activate the hooks configure git with (when using the setup script (`./scripts/setup-linking.sh`) this is already done):
diff --git a/docs/linking_concept_reasoning.md b/docs/linking_concept_reasoning.md
index 7c135a96..d065ba0b 100644
--- a/docs/linking_concept_reasoning.md
+++ b/docs/linking_concept_reasoning.md
@@ -11,7 +11,7 @@ When the renovate bot creates a PR it runs `pnpm install --ignore-pnpmfile`. Thi
This breaks builds that **don't** ignore the `.pnpmfile.cjs`-file. (CI that runs on the renovate PR)
From here we have two possible paths:
-- ignore `.pnpmfile.cjs` in all CI builds CI will also fail if we accidently add it locally.
+- ignore `.pnpmfile.cjs` in all CI builds (CI will also fail if we accidently add it locally).
- fixup the `pnpm-lock.yaml` in the renovate PR to contain the correct `pnpmfileChecksum`.
Ignoring in all CI builds means that CI will always fail if we enable the linking system.
@@ -22,9 +22,9 @@ Only if we remember setting it back/disbale linking (or let ourselves remember b
#### Summary
- We will always run into conflicts with the `pnpmfileChecksum` because in renovate prs it will be empty (`--ignore-pnpmfile`)
-- To keep it simple we set `--ignore-pnpmfile` in all of ours CI to see issues immediately.
+- To keep it simple we set `--ignore-pnpmfile` in all of our CI builds to see issues immediately.
- The only solution is to never have a `.pnpmfile.cjs` in the repository when pushing.
- This way there will never be a commit with `pnpmfileChecksum` in the lockfile.
- - renovate (which uses `--ignore-pnpmfile` which we cannot disable) and other CI will work
+ - renovate (which uses `--ignore-pnpmfile` which we cannot disable) and other CI will work.
- We are able to use the linking system locally if we `cp` this file from the scripts folder into `./` on demand.
- `pnpm links:on` and `pnpm links:off` + `./scripts/setup-linking.sh` will help us with this.
diff --git a/docs/self_hosting.md b/docs/self_hosting.md
index dc1dd687..e8ea2f6d 100644
--- a/docs/self_hosting.md
+++ b/docs/self_hosting.md
@@ -58,7 +58,7 @@ rc_message:
rc_delayed_event_mgmt:
# This needs to match at least the heart-beat frequency plus a bit of headroom
- # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
+ # Currently the heart-beat is every 5 seconds which translates into a rate of 0.2Hz
per_second: 1
burst_count: 20
```
@@ -70,7 +70,7 @@ make sure that your Synapse server has either a `federation` or `openid`
### MatrixRTC Backend
-In order to **guarantee smooth operation** of Element Call MatrixRTC backend is
+In order to **guarantee smooth operation** of Element Call, a MatrixRTC backend is
required for each site deployment.

@@ -190,8 +190,8 @@ backend mxrtc_auth_backend
> [!IMPORTANT]
> As defined in
-> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)
-> MatrixRTC backend must be announced to the client via your **Matrix site's
+> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143),
+> the MatrixRTC backend(s) must be announced to the client via your **Matrix site's
> `.well-known/matrix/client`** file (e.g.
> `example.com/.well-known/matrix/client` matching the site deployment example
> from above). The configuration is a list of Foci configs:
@@ -222,7 +222,7 @@ Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization
> [!NOTE]
> Most `org.matrix.msc4143.rtc_foci` configurations will only have one entry in
-> the array
+> the array.
## Building Element Call
@@ -291,7 +291,7 @@ be able to handle those yet and it may behave unreliably.
Therefore, to use a self-hosted homeserver, this is recommended to be a new
server where any user account created has not joined any normal rooms anywhere
-in the Matrix federated network. The homeserver used can be setup to disable
+in the Matrix federated network. The homeserver used can be set up to disable
federation, so as to prevent spam registrations (if you keep registrations open)
and to ensure Element Call continues to work in case any user decides to log in
to their Element Call account using the standard Element app and joins normal
diff --git a/docs/url_params.md b/docs/url_params.md
index e88e7095..4d567e84 100644
--- a/docs/url_params.md
+++ b/docs/url_params.md
@@ -12,7 +12,7 @@ https://element_call.domain/room/#
```
The URL is split into two sections. The `https://element_call.domain/room/#`
-contains the app and the intend that the link brings you into a specific room
+contains the app and the intent that the link brings you into a specific room
(`https://call.element.io/#` would be the homepage). The fragment is used for
query parameters to make sure they never get sent to the element_call.domain
server. Here we have the actual Matrix room ID and the password which are used
@@ -76,16 +76,16 @@ These parameters are relevant to both [widget](./embedded_standalone.md) and [st
These parameters are only supported in [widget](./embedded_standalone.md) mode.
-| Name | Values | Required | Description |
-| --------------- | ----------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `baseUrl` | | Yes | The base URL of the homeserver to use for media lookups. |
-| `deviceId` | Matrix device ID | Yes | The Matrix device ID for the widget host. |
-| `parentUrl` | | Yes | The url used to send widget action postMessages. This should be the domain of the client or the webview the widget is hosted in. (in case the widget is not in an Iframe but in a dedicated webview we send the postMessages same WebView the widget lives in. Filtering is done in the widget so it ignores the messages it receives from itself) |
-| `posthogUserId` | Posthog user identifier | No | This replaces the `analyticsID` parameter |
-| `preload` | `true` or `false` | No, defaults to `false` | Pauses app before joining a call until an `io.element.join` widget action is seen, allowing preloading. |
-| `returnToLobby` | `true` or `false` | No, defaults to `false` | Displays the lobby in widget mode after leaving a call; shows a blank page if set to `false`. Useful for video rooms. |
-| `userId` | [Matrix User Identifier](https://spec.matrix.org/v1.12/appendices/#user-identifiers) | Yes | The Matrix user ID. |
-| `widgetId` | [MSC2774](https://github.com/matrix-org/matrix-spec-proposals/pull/2774) format widget ID | Yes | The id used by the widget. The presence of this parameter implies that element call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl`. |
+| Name | Values | Required | Description |
+| --------------- | ----------------------------------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `baseUrl` | | Yes | The base URL of the homeserver to use for media lookups. |
+| `deviceId` | Matrix device ID | Yes | The Matrix device ID for the widget host. |
+| `parentUrl` | | Yes | The url used to send widget action postMessages. This should be the domain of the client or the webview the widget is hosted in. (In case the widget is not in an Iframe but in a dedicated webview, we send the postMessages in the same WebView the widget lives in. Filtering is done in the widget so it ignores the messages it receives from itself.) |
+| `posthogUserId` | Posthog user identifier | No | This replaces the `analyticsID` parameter |
+| `preload` | `true` or `false` | No, defaults to `false` | Pauses app before joining a call until an `io.element.join` widget action is seen, allowing preloading. |
+| `returnToLobby` | `true` or `false` | No, defaults to `false` | Displays the lobby in widget mode after leaving a call; shows a blank page if set to `false`. Useful for video rooms. |
+| `userId` | [Matrix User Identifier](https://spec.matrix.org/v1.12/appendices/#user-identifiers) | Yes | The Matrix user ID. |
+| `widgetId` | [MSC2774](https://github.com/matrix-org/matrix-spec-proposals/pull/2774) format widget ID | Yes | The id used by the widget. The presence of this parameter implies that element call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl`. |
### Embedded-only parameters
From cfbdbca5bbf9d06479c7d538022d5d698a05eb48 Mon Sep 17 00:00:00 2001
From: Robin
Date: Mon, 18 May 2026 13:08:52 +0200
Subject: [PATCH 065/100] Fix media foreground elements being obscured by
header/footer
---
src/room/InCallView.tsx | 17 +++++++++++++++++
src/tile/MediaView.module.css | 7 +++++++
src/tile/SpotlightTile.module.css | 4 +++-
3 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx
index 671b6b9a..af9e1fb8 100644
--- a/src/room/InCallView.tsx
+++ b/src/room/InCallView.tsx
@@ -90,6 +90,13 @@ import { useLatest } from "../useLatest.ts";
import { CallFooter } from "../components/CallFooter.tsx";
import { SettingsIconButton } from "../button/Button.tsx";
+declare module "react" {
+ interface CSSProperties {
+ "--call-view-safe-area-inset-top"?: string;
+ "--call-view-safe-area-inset-bottom"?: string;
+ }
+}
+
const logger = rootLogger.getChild("[InCallView]");
export interface ActiveCallProps extends Omit<
@@ -510,6 +517,16 @@ export const InCallView: FC = ({
insetBlockStart:
edgeToEdge || headerBounds.height === 0 ? 0 : headerBounds.bottom,
height: edgeToEdge ? "100%" : gridBounds.height,
+ // If edge-to-edge, compute new safe area insets that account for the
+ // header and footer.
+ "--call-view-safe-area-inset-top":
+ edgeToEdge && header && showHeader
+ ? `calc(env(safe-area-inset-top) + ${headerBounds.height}px)`
+ : undefined,
+ "--call-view-safe-area-inset-bottom":
+ edgeToEdge && showFooter
+ ? `calc(env(safe-area-inset-bottom) + ${footerBounds.height}px)`
+ : undefined,
}}
model={layout}
Layout={layers.fixed}
diff --git a/src/tile/MediaView.module.css b/src/tile/MediaView.module.css
index 49199253..240f14d1 100644
--- a/src/tile/MediaView.module.css
+++ b/src/tile/MediaView.module.css
@@ -85,6 +85,7 @@ unconditionally select the container so we can use cqmin units */
calc(var(--media-view-border-radius) - var(--cpd-space-3x))
);
padding: var(--fg-inset);
+ transition: padding 0.3s;
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: 1fr auto;
@@ -94,6 +95,12 @@ unconditionally select the container so we can use cqmin units */
contain: strict;
}
+@media (prefers-reduced-motion) {
+ .fg {
+ transition: none;
+ }
+}
+
.nameTag {
grid-area: nameTag;
place-self: end start;
diff --git a/src/tile/SpotlightTile.module.css b/src/tile/SpotlightTile.module.css
index 037cf10d..54e31106 100644
--- a/src/tile/SpotlightTile.module.css
+++ b/src/tile/SpotlightTile.module.css
@@ -35,7 +35,9 @@ Please see LICENSE in the repository root for full details.
.maximised .item {
/* Ensure that foreground elements lie within the safe area */
- --media-view-fg-inset: 10px calc(env(safe-area-inset-right) + 10px) 10px
+ --media-view-fg-inset: calc(var(--call-view-safe-area-inset-top, 0px) + 10px)
+ calc(env(safe-area-inset-right) + 10px)
+ calc(var(--call-view-safe-area-inset-bottom, 0px) + 10px)
calc(env(safe-area-inset-left) + 10px);
}
From dfbb6c770ee7911ed932c47e3adf64deed323c87 Mon Sep 17 00:00:00 2001
From: Valere
Date: Mon, 18 May 2026 13:25:56 +0200
Subject: [PATCH 066/100] devx: Allow to run local dev EC on mobile in same
wifi
---
backend/dev_tls_local-ca.crt | 34 +++++-----
backend/dev_tls_local-ca.key | 52 +++++++--------
backend/dev_tls_m.localhost.crt | 38 +++++------
backend/dev_tls_m.localhost.key | 52 +++++++--------
backend/dev_tls_setup | 5 +-
docs/_developer.md | 113 ++++++++++++++++++++++++++++++++
vite.config.ts | 1 +
7 files changed, 205 insertions(+), 90 deletions(-)
create mode 100644 docs/_developer.md
diff --git a/backend/dev_tls_local-ca.crt b/backend/dev_tls_local-ca.crt
index 963089ad..859381b1 100644
--- a/backend/dev_tls_local-ca.crt
+++ b/backend/dev_tls_local-ca.crt
@@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
-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
+MIIDGjCCAgKgAwIBAgIUOlA2wgQUGZkKqNDvvifFWEsJfvYwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTRWxlbWVudCBDYWxsIERldiBDQTAeFw0yNjA1MTgwOTM0
+MzFaFw0yODA3MjYwOTM0MzFaMB4xHDAaBgNVBAMME0VsZW1lbnQgQ2FsbCBEZXYg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcImv3pStfIUo7PbOO
+XRVUXuDlApBOrg2dCnvZQ1Jfaf4MftGHj/pURkF4eoBuyH4k4+NLzWD0VcU1cM74
+RnxowJt4AceCGe5RK/rqal5fapXc2vYMM8P6xaQR86gkxohpufsLgTnSweh74yqN
+B5WHUnCX00/X0bh1ho2BMUvGM9+dI4MdgKdaQDgWK4zg9hwp2Z6Yq7SkJ/D8+sGW
+WGpn3osDakL8HTBqop+YVJgF40db50yFurfcfQ0gjVtT4JW8ejO9j8PS/S2oQ/s5
+mA1B470XhLtT5qTjGm2bp3WpYkTi5widps8PDzBp5eNr0HrvJqcw7BGpbvBlLa+3
+7dhLAgMBAAGjUDBOMB0GA1UdDgQWBBRDfyRM4yKUqW6vu/2KUSXGb8vswDAfBgNV
+HSMEGDAWgBRDfyRM4yKUqW6vu/2KUSXGb8vswDAMBgNVHRMEBTADAQH/MA0GCSqG
+SIb3DQEBCwUAA4IBAQBoAhD4W4Yi/VJ2pTKrzhstn1UF1rgQnRddnn97v5BaEV/X
+uuBXbSO+/ewjQUupRjePZFp9FFe9co1OiduKcDExlvPU1eIqkWAwDWjMDpI+Lw5q
+KI5yHzplmMrT/7jn9Tepl9atrIcfDeFkP1dGtdRPyU6ARJEEWJSKeH9ftmImAsbM
+ykXAqSyRl8+bPx1ISG4cNihOxFd38VPDHIW53umaRBgRcN4GcvloKBGrVtRFNM//
+H+md8HmNQMP+e7FamETxs28DxjsdpygxjiFNY/T2eD67dH50ZxC3qCxEG6TJsoAg
+TYJafnqEcGDfiWQyNZRBypuaRsRmmTR27hCPVgi8
-----END CERTIFICATE-----
diff --git a/backend/dev_tls_local-ca.key b/backend/dev_tls_local-ca.key
index 04da3869..abbfb9a8 100644
--- a/backend/dev_tls_local-ca.key
+++ b/backend/dev_tls_local-ca.key
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHODfkrFsOkqCn
-XnTbQWz3LkUtNCoVvM7wFouirRnITJYG+lFwF+zNl89Eaq+uUN4bwd8ml1ZuR9p+
-1azCSlklD5adhCR/ErknfUWamQEf6amSs3p0NnqnhXbnDEEbQOwNaPU/aGc6aw0+
-I9O6NQ/H830GlVuKd24Bfv0mx6Imo0Hi9jxKYhqFh80nmltk2uyXefaJxuo1jXBh
-wLyCDW8RVj55QvkZyBUzid8yslxrlo0LHKCCjZflwJJv5f+jaubkH5c0qxVaoR4+
-LiytX/4viIwt3Mhj04ppudTvt973mTbjRG5haCz9y7OkT1mMWhc0xrdMFX+gjPER
-YS2HRu/RAgMBAAECggEABhB9CxYAE5p9D3s9nWsJcSDUdELRQSYlOoPFLmeMkF9c
-dcvq7LmduMh1Q8TnoivOBxRIwbj7pZHEYfYJM0TmH82wrQzXu5KLVltm4gTkVt9b
-DR8vjBgYdb8HVpM17Cl2xhW62XpJIiseFRUsHc/9sf2Egc3MIpPuIleGR0budbSW
-ybBkqEokTYTSiAztcu3G+VN0U9MsJgLMa8HApya7M48ojdrhzngVHZRUOXul9o7u
-zYJWSxPHIIYp5C4pYQBAx8OttThwKK1A9lwbQ2EJx0KnTbBC6O5Gna/jENpGd1h2
-rzK/9MONtsjln7IejP+4mDlNupS6SF3zzHPBHjqKAQKBgQDtXUIKPiVTFS45yWtK
-XD62s3j8jfIi+22b/C30fCPtppn0cm/0zY+vovgWVUBnQXkExafRthZCuxnE8ry7
-E29S40+4z9yivAC9dz7vHZUbyIFP6VG9WyhUYo+/WqOIePyh+iBISQ9TA1DneIYz
-+VZ8iU5GvdybUPl2C5WN8seaoQKBgQDW3EwVN2EEkChLRJbQYN2qpjn+0vYESMJ8
-K0sgMRtgh4+/T2Xb9b8O/dd87Fi/4oaUqWZ2E2sdsXq8P/IEo0cv6SRfHMy7GyxL
-RM7ztwUfMC4LVWi0ZIXMrm4gRDGN2XjGvhkX6fU2lSf6azWL1K3wI3amNV2b7P7d
-ItpvdkH3MQKBgQCXf29YJEQkXB9t6J3fDzND3xb4cwy5wSo7ZeBa7CTuWOhoeeX1
-JIJyAp0/e9goT0SThChRlFtu6gZPivJkoMnr6IOInLrg7we15fc4HPR/kCDgxTVT
-m2wJOAMxigNYZogwRfn2yRLL1BD+PBHD+H936xcX1bSJOUyPSGOC/xLhIQKBgQCb
-kCDd85ygyycBaAWxlZCor3WqFF/fNjbp5Aaepi9mMoBXSUs8eK7+UbelURHozEAY
-fpYaw3B4rTlp9vppdTZjb+/PlXB9v+zQCl+0gTyKGj4cIpiOk4F0co51eipOw7f4
-XUaZ0+CgxlmNq/W26iONjH+pU1YVQQA+Z6+zp/GW4QKBgQCrzYgeugxxqgJzyIRu
-0njJkIg+T5gHvsQrtpzq7LVob+HBiBiT7eDOeGDXTK8F//sk969QVrDMQsTMvGW9
-sG1oTqxciALTMqkJTf8+hT9Uogir0/iTbJUzTt5vPYpQOEQwQHIXMUTjZ9C6NDKT
-QlmeMCxeWyPYqoMfwKmdtDP/Iw==
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcImv3pStfIUo7
+PbOOXRVUXuDlApBOrg2dCnvZQ1Jfaf4MftGHj/pURkF4eoBuyH4k4+NLzWD0VcU1
+cM74RnxowJt4AceCGe5RK/rqal5fapXc2vYMM8P6xaQR86gkxohpufsLgTnSweh7
+4yqNB5WHUnCX00/X0bh1ho2BMUvGM9+dI4MdgKdaQDgWK4zg9hwp2Z6Yq7SkJ/D8
++sGWWGpn3osDakL8HTBqop+YVJgF40db50yFurfcfQ0gjVtT4JW8ejO9j8PS/S2o
+Q/s5mA1B470XhLtT5qTjGm2bp3WpYkTi5widps8PDzBp5eNr0HrvJqcw7BGpbvBl
+La+37dhLAgMBAAECggEAEsS4gc5jBk50I+bo3KYn2DqHgj/qpOqbTFNkS9uh3UJa
+fZoJCeiuyM6hNCBVq/uB3mFeg1Au5XAiAqiK2KFwdw8gIS7lkqgXU76brO4YZhPj
+6+aOSS03079KV7YYckNDRqJKoTlpgAI7Nhk6ljVhLiEk07tdD65wJACGpg8M8sg9
+dyAz+ANs9gs65iF5LYjH61O/AFlLqCRQh5/z0mjGX6G9uN27nxeUY4+n4QMAcd9D
+Gcygxjt+4nlQayNAlKMwVfps9bWNtI3Ye9knY4WGkrv5cJbW3bgjV6qrvQsbukbq
+xEYzcIlUiWGO9Tv7MN6rk5uQOyoKT/KUnfRmdVd3YQKBgQDMhWm6Q+WuI7Pyn57R
+tmY4rs+fSqTmv6xAOcozKJxffGaEwSUuNA15NvR/7iedcNqmH3XT9j90ZNVHe090
+ocm1HDUvzC9G5GRrdO6JTTksRaIMZEhosWxqH3DIuBJPLGbF/4obGE7//PJtmDEp
+QVL9Aa0WrcwAWhRzUdvCE+taMwKBgQDDbyZIvtlEr1w2V0bjXO536rRksBapc2ZJ
+XRKtrXivuVtiZYNDB0I7CCJ52cka61n3kyZz2mhQmLq4cAZXyKYWE2i643O+kc3S
+lpZEFSfDZ+3YlhxMxG9oEcgUSwVdbPlAhd/UR8V6n8o2Fm+gug9h6E2zY4fgHLJF
+8hOWoD4hiQKBgA7YXD1F8mT6eHRS+78zIyZYIf/o9iE9pm4fA7tE5lzT9ckLD/zT
+kGrM/2BN1BhMecJ3JCFXjXGQZB7FJ5ZKrA52VrH6ezAFIfjeyvWyYkUBZOrLWKoo
+vrrRP2mCWuneSjNzAf5HfGx+WsZztpXNBQ4SUhMEWHtqDnP0bCQhOAMbAoGAPfLv
+qcOFT3ZevoLv34ZHuQ9W20vOAyynUb4E+7SvOtSAmTIgZ5DXd6recs2MJ9JOlGG6
+oKKsyk9/cJNiD1V1AC5q1kLfH5tMKOK/AxnJnvFEvZDnq5Xg0pZAW95j9vdiEwfc
+qYeOm44nJPn7rHEOCzT93E1CdtHh2LYha2+kAjECgYEAh4qODleBi+2fnf2eq494
+/tAot3szu2+gjyCN00vGjtzoAuDKTYgo0cbU1ILk0Pgpp1NcIvdHz/wQnG9RLX7e
+Dfy1Q+UkyBK67SJUPvcYqBEaZ6ddyijJDunqh+U3nIBGP+IntKIKMIKiLF6wzTKz
+NRpK1HNmllp+O692ZtxoNDU=
-----END PRIVATE KEY-----
diff --git a/backend/dev_tls_m.localhost.crt b/backend/dev_tls_m.localhost.crt
index e6c64f03..8a7d3db7 100644
--- a/backend/dev_tls_m.localhost.crt
+++ b/backend/dev_tls_m.localhost.crt
@@ -1,21 +1,21 @@
-----BEGIN CERTIFICATE-----
-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
+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
-----END CERTIFICATE-----
diff --git a/backend/dev_tls_m.localhost.key b/backend/dev_tls_m.localhost.key
index 0373a6f1..f19463a2 100644
--- a/backend/dev_tls_m.localhost.key
+++ b/backend/dev_tls_m.localhost.key
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDK1DNwTQWmyK71
-Ar56NvmSMQ8sqUY3jGqqPVORjfDUtDCrPPdCxT+ZlnsAgdonElWoWqczMrSyBRgf
-JlZMd4lEvt6VEEiZGUvA/lG1XIVgrx1kMSHKBoJj7lCBN6r3IWmYe6CxgfZurgp+
-7Z22i6cGMOnQ0XduX5Asup6zk5V7AE6i9eKrJsUjYmRBXtk099IitkER4TMqh6Wx
-JmFF+eV9P/axfxkon+bQWITwP1PLC1UOTK7lR0EcVan5aY6WMs/6RfO4Gw/dvuiV
-G1jCrVcaKNGTPYqmQqs+MOvyIqJ9kYELRZu+6bhPWSXk2ESpSIUIPH9twfnmWrnc
-neIJR24/AgMBAAECggEANRp6vzfDN4gKWoaV5TyYegCTNv+4rRl74cd9pjmx6Jam
-uWaUXCx1etpNqPPWcG1Z9OKLLRnk+kjgKGOqq4mObGvGreNeBot7bHOJZADtwMMI
-YG+Gp7StlclS1YoEHoDmezA/AcqDgTXa9KF0rdMBb1sGFJCLAuBNSJCxtVV6CQIz
-X26uT0m+Wx8MQyQWA7Sqy6DQNJo++IZkvr7a3cidqBOUPs+QvnIV5JsUb2gp5tGn
-zk+ObeRjoFFWYAN/NK7bneRenkP40m3MSL8ZfaEuuonui7CrxM1SiQyq2N1u/Aoy
-OE1JtNaVPbLBo6kG5al7Sj4Z0zhRt+iv93S2lZMkBQKBgQD2+FpLTqyLO1NDOFkE
-kxU+LdLOx0OV9wASC0ApPOu1dHMG6ksByr7TWeiu6GJDgajusPB7NVPOt2cm4iWU
-xPxXPO5l87uiSvu80h5uG4Qdj8KEijHkdap2wbVkU/mm8lBKC36jyBQIlJKySyXY
-zSEMfLK9jQPKz5cKKT3dVj/fAwKBgQDSPq9oks6K96MAB66o6cm214otQlnTQkPM
-xgjtjddX+Lp9tgihGvtSfPbyy89oUDHCfKvW/AHG52e5dec5YUi6mVdHEWbk33Kt
-BoQuxeK3XseIDlD/JD9Dd7KfUyO5w2jtYLfNdqez41O4qj2N52m1KwJYTwMsc8Kq
-izVgkC5hFQKBgBFAc/5CtqbbNAvECePZ6mf3h3xOSxhUsrqP8qFu0gBQ7CAVibvM
-T9wvsaNWNFcG3age0A2rQfl0sk3zCjEEOaRWa0jP59GEb2VXQCzs2yO9gRcFGEsf
-NRMqoOMrQos47gbeGrCSL2QSDNVLjo9AdQiMRWgcS6GFMsXQ77NgbQHFAoGBAI4a
-YGTGFWRITJvQlXUFz5kNxg8hMaVgvILDt3UY0dxb+XDOgLajjgsK+77Pkrhmu7tA
-mMUOQAU4kxr/XfGil43H5v3Z/Tnk7ZWVOfKDPeHC5gpH4ucQkNIBLXISt6rvMRSA
-srrk4CTuGcBPEJvBNemF0Gfvv61j8MdkoAdMbIyhAoGAfGR6yZLBmRMsW5PKmcpT
-nq2oSeUpmtGZra6pWz/3XU7AgrCLcx1DmqEjm4w7y5NQJmxyMZqqdTJILCjr3Srt
-+2F0NqQL6Li+xQGibAvDj0Jxyol38RvFC0J/w2vQmuF0hTuH95yknSd7FPXK+DPG
-qYgXLjun9dht6kx9vGJ69wI=
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0hora/UCYMtrL
+Jc6BOjonPUPibYpbNiZYnvnqI4doKbV0LBT2TfokT7tpgdPCtHKV0RknsVSL8vhl
+XpkRqIiWPml8sZaa0+5NDGCQxexS2WVBlsoNCmAaqi/HNSFop6xaxGpQ3bu0iV3o
+IkUihveXAl6HC0VYyGifQ8D5onzepW2ayhemu47YRNSo8wETY5vIi0i/iajTTaw6
+JvwS+8Kv5/QV5prdvcFlG/oBs12p0+KoRyxskyzcdBdyIarvfY+9nDZwym5GfN32
+xO/iqtDuDQzwQ09h2OsfHJCw70IpHcgXLlEQF2DsFbmbVpWSU6HcMm6B7Yw1YeE6
+4W4PRJp3AgMBAAECggEAIgdIbk4VmnrfjjKCsg5JPvNH9AsE7PuQj9zrq+xljkdq
+aksS6ni5YZXb9F/iDE4aWU4waTB+iODUXLtPrCnyESwTk0sgYe/39/MQ0slUKivL
+b+keDgY6JlyVI/5KXWFZ1kQ27CZXxwiruGGZWZBKZF8wdVE1Ea65Neg+HHA6DHee
+Jck002gtgO/J1MMbB1MzdtGcsejYLrA+mO6YddQhA65xdQMljTEfyUwgTVv0pWde
+biyKegGK77vlsOyoCkMpVYORG5NMV1Kxs+htA79yuIW71tWHqVbcRMyoM+BaHzPh
+7uprs+8vYDFrO39LseczA8gURWwUsCgQ0yQ6Ix5W7QKBgQDnSK4AzjPpDEArdHuV
+VGKyzrfPtzH0VV/yTH9hvByNG6i/x8sE/r2KPi5nRMi4PAjjqmxyO1G5qwDOfzvK
+sBvwFrTRpmbnqGITVKPPivdoI9+RveN+FxhOXVA8NylAOv/dtSoakYwg3e507UsC
+RuFW3Re0Oc+0XFq4C8rQyLkIOwKBgQDH0T9gww+XbwIGCtiNpnEziU9FXBKSVwXf
+dCxYcTLPATq3BqHmP4OUA0v+sa3wPcnBkXF7q6eoB9+S6ZYQA/b2BXGU5/j9xYd4
+29cF4DlPkhTwF9S8b+h1zhlGIn96Lw/vZuj7Bc3wuwxvB17d8dpyo8bZynIe7BvF
+KSPJz+2O9QKBgFFyd8xS0VcFeGeVKpwozmUXhQWCBvZ7RkGGjOk3HHrYvbFjw2vr
+5YWUZjT5tRGkGqFJ98y2dQ5EWRFfHwg+wmfnJyAZUG3OD1OtX86Lqpqi321siHtz
+2JxoIgRCjKVQ4aAK11vp24YLgZjto5eWrG4xh9Jw9WMXjt73UCH8PaTXAoGBAIff
+TY1qlmuO3H1nWqHXkBpPQEwVs7s22ZN817q8HqSMXXSfWe/LOJmpND/YakJ2gX7S
+e6xwqOylje3EUHpLd98LDJUIuFM3wkr4klo4gkANQZeRXONV5WhV4PHD+5MF9XwB
+KmOnKsaLKoVFKckZ8EUMAOePtdI5ExkaRG+yqAMRAoGAJyUFK+V9ST1N/6wYgqor
+vywMSRE2cF2WvVIxdMvWffmpoj40bG6lAlaSWm29E2T8SVvAKsRid0wDgCQ4QTEn
+ft7yUDjqVALCJVCrOFHDY0BPStkm6njMWagr/0lGr9zUWqbBOKJhNfDJlykv8gaF
+8kWTgabrMCKmpTi7fBWbzZA=
-----END PRIVATE KEY-----
diff --git a/backend/dev_tls_setup b/backend/dev_tls_setup
index 9d40f5d9..08a1949b 100644
--- a/backend/dev_tls_setup
+++ b/backend/dev_tls_setup
@@ -3,7 +3,7 @@
# Step 1: Create a Root CA key and cert
openssl genrsa -out dev_tls_local-ca.key 2048
openssl req -x509 -new -nodes \
- -days 3650 \
+ -days 800 \
-subj "/CN=Element Call Dev CA" \
-key dev_tls_local-ca.key \
-out dev_tls_local-ca.crt \
@@ -21,7 +21,7 @@ openssl x509 \
-CA dev_tls_local-ca.crt -CAkey dev_tls_local-ca.key \
-CAcreateserial \
-out dev_tls_m.localhost.crt \
- -days 3650 \
+ -days 800 \
-sha256 \
-extfile <( cat <
Date: Mon, 18 May 2026 14:31:03 +0200
Subject: [PATCH 067/100] Review (everything except translation feedback)
---
src/components/CallFooter.stories.tsx | 4 +-
src/components/CallFooter.tsx | 7 +-
src/components/CallFooterViewModel.test.ts | 4 +-
src/components/CallFooterViewModel.tsx | 40 +-
.../MediaMuteAndSwitchButton.stories.tsx | 9 +-
src/components/MediaMuteAndSwitchButton.tsx | 47 +-
.../MediaMuteAndSwitchButton.test.tsx.snap | 8 -
src/room/GroupCallView.test.tsx | 10 +-
src/room/InCallView.test.tsx | 3 +-
src/room/InCallView.tsx | 80 ++-
src/room/LobbyView.test.tsx | 19 +-
.../GroupCallErrorBoundary.test.tsx.snap | 12 +-
.../__snapshots__/InCallView.test.tsx.snap | 41 +-
.../__snapshots__/LobbyView.test.tsx.snap | 576 +-----------------
.../DeveloperSettingsTab.test.tsx.snap | 16 +-
src/utils/test-viewmodel.ts | 21 +-
16 files changed, 170 insertions(+), 727 deletions(-)
diff --git a/src/components/CallFooter.stories.tsx b/src/components/CallFooter.stories.tsx
index 14f82506..f46f656f 100644
--- a/src/components/CallFooter.stories.tsx
+++ b/src/components/CallFooter.stories.tsx
@@ -30,7 +30,7 @@ const reactionData = {
* - Add additional react context
* The paraeters are all params from the FooterSnapshot,
* the Snapshot of the vm, the wrapper will create a mocked vm from it and pass it to the CallFooter.
- * children used for the "Back to Recents" button in the lobby stories, but can be used for anything really
+ * `children` is used for the "Back to Recents" button in the lobby stories, but can be used for anything really.
* @returns A component that renders the CallFooter based on primitive snapshot params (not a view model). Which is what we want for storybook.
*/
function CallFooterStoryWrapper({
@@ -71,7 +71,6 @@ const fnArgType = {
export const Default: Story = {
args: {
showLogo: false,
- showSettingsButton: true,
layoutMode: "grid",
audioEnabled: true,
videoEnabled: true,
@@ -85,7 +84,6 @@ export const Default: Story = {
showFooter: true,
hideControls: false,
asOverlay: false,
- showLayoutSwitcher: false,
sharingScreen: false,
audioOutputSwitcher: undefined,
reactionIdentifier: undefined,
diff --git a/src/components/CallFooter.tsx b/src/components/CallFooter.tsx
index bd309941..dfec25a9 100644
--- a/src/components/CallFooter.tsx
+++ b/src/components/CallFooter.tsx
@@ -82,8 +82,6 @@ export interface FooterState {
asOverlay: boolean;
buttonSize: "md" | "lg";
- showSettingsButton: boolean;
- showLayoutSwitcher: boolean;
showLogo: boolean;
layoutMode: GridMode | undefined;
@@ -143,12 +141,11 @@ export const CallFooter: FC = ({ ref, children, vm }) => {
const selectVideoButtonOption = useBehavior(vm.selectVideoButtonOption$);
const videoToggles = useBehavior(vm.videoToggles$);
const buttonSize = useBehavior(vm.buttonSize$);
- const showSettingsButton = useBehavior(vm.showSettingsButton$);
const showLogo = useBehavior(vm.showLogo$);
const buttons: JSX.Element[] = [];
- if (showSettingsButton) {
+ if (openSettings !== undefined) {
// Add the settings button to the center group so it's visible on small
// screens. On larger screens the SettingsIconButton with
// showForScreenWidth="wide" in the settingsLogoContainer is used instead.
@@ -291,7 +288,7 @@ export const CallFooter: FC = ({ ref, children, vm }) => {
})}
>
- {showSettingsButton && (
+ {openSettings !== undefined && (
{
expect(vm.audioOptions$.value).toEqual([]);
expect(vm.videoOptions$.value).toEqual([]);
}
- it("are empty when both the platform is iOS", () => {
+ it("are both empty when the platform is iOS", () => {
checkEmptyFor("ios", gridLayout);
});
- it("are empty when both the layout is pip", () => {
+ it("are both empty when the layout is pip", () => {
checkEmptyFor("desktop", pipLayout);
});
diff --git a/src/components/CallFooterViewModel.tsx b/src/components/CallFooterViewModel.tsx
index c2cacd8c..4be52f0d 100644
--- a/src/components/CallFooterViewModel.tsx
+++ b/src/components/CallFooterViewModel.tsx
@@ -188,30 +188,35 @@ export function createCallFooterViewModel(
callModel.windowMode$.pipe(map((mode) => mode === "flat")),
),
buttonSize$: scope.behavior(
- isPip$.pipe(map((pip) => (pip ? "md" : "lg") as "md" | "lg")),
+ isPip$.pipe(map((pip) => (pip ? "md" : "lg"))),
),
- showSettingsButton$: scope.behavior(
+
+ openSettings$: scope.behavior(
combineLatest([
isPip$,
callModel.showHeader$,
- callModel.settingsOpen$,
+ callModel.setSettingsOpen$,
]).pipe(
- map(
- ([isPip, showHeader, settingsOpen]) =>
- settingsOpen !== undefined &&
- !isPip &&
- showControls &&
- !(headerStyle === HeaderStyle.AppBar && showHeader),
+ map(([isPip, showHeader, setSettingsOpen]) =>
+ !isPip &&
+ !(headerStyle === HeaderStyle.AppBar && showHeader) &&
+ showControls
+ ? (): void => setSettingsOpen(true)
+ : undefined,
),
),
),
- showLayoutSwitcher$: scope.behavior(
- isPip$.pipe(map((l) => !isPip$ && showControls)),
- ),
+
showLogo$: scope.behavior(isPip$.pipe(map((isPip) => showLogo && !isPip))),
layoutMode$: callModel.gridMode$,
- setLayoutMode$: constant(callModel.setGridMode),
+ setLayoutMode$: scope.behavior(
+ isPip$.pipe(
+ map((isPip) =>
+ !isPip && showControls ? callModel.setGridMode : undefined,
+ ),
+ ),
+ ),
sharingScreen$: callModel.sharingScreen$,
toggleScreenSharing$: constant(callModel.toggleScreenSharing ?? undefined),
@@ -222,15 +227,6 @@ export function createCallFooterViewModel(
),
),
- openSettings$: scope.behavior(
- combineLatest([callModel.showHeader$, callModel.setSettingsOpen$]).pipe(
- map(([showHeader, setSettingsOpen]) =>
- headerStyle === HeaderStyle.AppBar && showHeader
- ? undefined
- : (): void => setSettingsOpen(true),
- ),
- ),
- ),
hangup$: constant(callModel.hangup),
reactionIdentifier$: constant(reactionIdentifier),
diff --git a/src/components/MediaMuteAndSwitchButton.stories.tsx b/src/components/MediaMuteAndSwitchButton.stories.tsx
index a109dd4c..fe68fc9b 100644
--- a/src/components/MediaMuteAndSwitchButton.stories.tsx
+++ b/src/components/MediaMuteAndSwitchButton.stories.tsx
@@ -5,7 +5,6 @@ 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";
@@ -21,13 +20,7 @@ type Story = StoryObj;
export const Default: Story = {
args: {
title: "SomeMenu",
- iconsAndLabels: {
- IconEnabled: AdvancedSettingsIcon,
- IconDisabled: AdvancedSettingsIcon,
- enabledLabel: "Enabled",
- disabledLabel: "Disabled",
- optionsButtonLabel: "Options",
- },
+ iconsAndLabels: "audio",
enabled: true,
options: [
{ label: "option 1", id: "1" },
diff --git a/src/components/MediaMuteAndSwitchButton.tsx b/src/components/MediaMuteAndSwitchButton.tsx
index e25f23f1..4a6737f7 100644
--- a/src/components/MediaMuteAndSwitchButton.tsx
+++ b/src/components/MediaMuteAndSwitchButton.tsx
@@ -36,18 +36,6 @@ export interface ToggleOption {
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;
@@ -55,7 +43,7 @@ export interface MediaMuteAndSwitchButtonProps {
enabled?: boolean;
/** Callback if the mute button is clicked */
onMuteClick?: () => void;
- iconsAndLabels?: "video" | "audio" | IconsAndLabels;
+ iconsAndLabels: "video" | "audio";
/** The options available for the media device selector modal */
options?: MenuOptions[];
/** The option that will currently be rendered as the selected option */
@@ -115,30 +103,7 @@ export const MediaMuteAndSwitchButton: FC = ({
data-testid="incall_mute"
/>
);
- break;
- default:
- button = (
-