Commit Graph

2308 Commits

Author SHA1 Message Date
lebaudantoine 8f27b89d21 (frontend) add a sound tester to the output select menu
Add a sound tester next to the selected output device in the speaker
select menu, so users can play a test sound and confirm they picked
the right speaker.

Inspired by the microphone gauge added previously, and requested by
users.
2026-08-12 14:52:09 +02:00
lebaudantoine b780d2845a (frontend) add an audio gauge to the microphone select menu
Add an audio level gauge next to the selected microphone in the mic
select menu, so users can see at a glance whether their microphone
is actually picking up sound.

Inspired by Google Meet's mic picker, and requested by users.
2026-08-12 14:52:09 +02:00
lebaudantoine 751d029ac9 🔧(frontend) sync persisted device ids with the actual selected devices
Now that the exact deviceId constraint has been dropped, the browser
can pick a different device than the one persisted in localStorage
(for example when the persisted device is no longer available).

Sync the persisted ids in localStorage with the device id that was
actually selected on the started track, so the local cache stays
consistent with what the app is really using.
2026-08-12 14:52:09 +02:00
lebaudantoine aaa51a4457 ️(frontend) revert old permission-toggle hotfix
Revert the old hotfix that allowed users to toggle their microphone
or camera while permissions were not granted, which then triggered
a `getUserMedia` call to prompt for them.

Now that the permission store is properly kept in sync with the
browser, this workaround is no longer needed as-is. The intended
behavior will be reimplemented cleanly in a later commit.
2026-08-12 14:52:09 +02:00
lebaudantoine c8a3ef6f61 🐛(frontend) fix permission store regression
`derive-valtio` was broken by a recent update, which cascaded into
various regressions in the permission store.

Take the opportunity to also refactor how permissions are handled.
The store is now a pure cache with a single writer: every signal
re-reads the browser via `syncPermissions()`, and the browser stays
the only source of truth.

Re-sync triggers, all event-driven (no polling):

* `devicechange`: granting permission reveals device labels/ids, so
  it fires on grant in every browser, including Safari. This
  replaces the previous 500ms Safari polling. Denials are still
  caught by the concurrent `getUserMedia` rejection through
  `notePermissionDeniedFromGum`.
* Window focus: covers the return from the browser or system
  permission UI.
* Permissions API `change` events, where the query is supported.
2026-08-12 14:52:09 +02:00
lebaudantoine 5d50671b3c 🔥(frontend) remove buggy device-id resolution code
Remove the current device-id resolution code that was buggy and
failed to resolve the device id correctly.

A replacement will be introduced in upcoming commits.
2026-08-12 14:52:09 +02:00
lebaudantoine 8615bf879c 📈(frontend) capture media diagnostics on media errors
Attach a media diagnostics snapshot to the room event handler for
media exceptions. The snapshot captures the state of the user's
setup at the moment of the error (available devices, permission
state, active tracks, etc.), so support has enough context to
troubleshoot user issues without asking them to reproduce.
2026-08-12 14:52:09 +02:00
lebaudantoine 186d16c46f 🐛(frontend) drop exact deviceId constraint on dynamic track creation
Dynamic track creation used an exact deviceId constraint based on
the device id persisted in localStorage. If that device was no
longer available on reconnect, the browser raised a DOMException
instead of falling back to another device.

Drop the exact constraint so the browser can pick any available
device when the persisted one is gone.
2026-08-12 14:52:09 +02:00
lebaudantoine fb3ee56702 ♻️(frontend) encapsulate PostHog capture calls in the telemetry module
Move the remaining direct `posthog.capture` calls behind the
telemetry module, so PostHog is only referenced from a single place.

Call sites now use the telemetry API instead of touching PostHog
directly, making it easier to swap the backend later without
changing every call site.
2026-08-12 14:52:09 +02:00
lebaudantoine 48c0cb320e ♻️(frontend) encapsulate error tracking behind a telemetry module
Introduce a telemetry module that exposes a `reportError` helper.
Under the hood it forwards errors to PostHog, but the module is the
only place that knows about PostHog.

Replace `console.error` calls used for error reporting with
`reportError`, so the codebase now goes through a single, consistent
API for telemetry.

This normalizes how errors are reported and makes it straightforward
to swap PostHog for another backend later on, without touching every
call site.
2026-08-12 14:52:09 +02:00
lebaudantoine d810c9e0de 🐛(frontend) drop resize listener in useIsMobileBrowser
`isMobileBrowser()` only reads `navigator.userAgent`, which does
not change during the lifetime of the document, so the previous
`resize` listener never had anything meaningful to update.

It did, however, dispatch `setIsMobile` on components rendered into
a Document Picture-in-Picture window (e.g. the reactions toolbar).
When the PiP window had already been closed, Firefox threw
"can't access dead object".

Compute the value once and skip the listener entirely.

Fix 019cb315-d827-73f2-b1cc-74e4dd71e982
2026-08-12 14:52:09 +02:00
lebaudantoine 134d9a188f 🐛(frontend) gate blur on WebGL2 transformer support
`ProcessorWrapper.isSupported` reports pipeline support but not
whether the WebGL2 transformer is available. On browsers where it
is not (e.g. Chrome/Edge on Windows with WebGL2 disabled by a GPU
blocklist), toggling blur throws at runtime.

Update `supportsBackgroundProcessors()` to check both, so the UI
only exposes blur when it can actually run.

fix 019f8e3b-f035-73e2-9d6a-d0dd2d0a1163
2026-08-12 14:52:09 +02:00
lebaudantoine ea7188059d 🐛(frontend) guard getRouteUrl('room', slug) against missing slug
InviteDialog.tsx and Info.tsx were the last call sites calling
getRouteUrl('room', slug) without a slug guard, unlike every other
caller (e.g. useCopyRoomToClipboard).

Compute roomUrl only when the slug exists (undefined in
InviteDialog, '' in Info to keep its unguarded .replace safe).
Guarding at the call site preserves the "no room data yet" state
instead of returning a bogus "/" URL from room.to.

Fix 019fd616-f158-7771-8cff-bac3090b8449
2026-08-12 14:52:09 +02:00
lebaudantoine b8958e6e87 🐛(frontend) unmount PiP portal synchronously on pagehide
When the PiP window closes, the browser destroys its document right
after `pagehide`. If the portal unmount is left to React's async
scheduling, it commits against a dead document and `removeChild`
throws "NotFoundError", crashing the app.

Subscribe `PictureInPicturePortal` to the Valtio store with
`sync: true`, and use `flushSync` in `usePictureInPicture` on
teardown so React unmounts the portal while the PiP document is
still alive.

Fix 019f42cf-86a9-7ad2-8e64-81b004ddc5de
2026-08-12 14:52:09 +02:00
lebaudantoine 23bb3c39d0 🐛(frontend) normalize thrown values into proper Error instances
LiveKit can surface raw DOM events (for example WebSocket "error"
events, whose only enumerable key is `isTrusted`) instead of Error
instances.

When such a value ends up being captured, our error reporting logs
it as "Event: Event captured as exception with keys: isTrusted",
which is unhelpful and hides the real cause.

Add a small helper that normalizes any unknown thrown or emitted
value into a proper Error, preserving the original payload as
context.

Fixes 01997b9a-db63-7fc2-8fe4-f21dd7fd608d.
2026-08-12 14:52:09 +02:00
lebaudantoine e0ff28ed48 🔖(patch) release 1.25.22 v1.25.2 2026-08-06 13:29:17 +02:00
lebaudantoine 61e8b597dc 🐛(frontend) harmonize cache configuration for MediaPipe assets
The wasm and js files shipped by MediaPipe were served with
different cache policies, which could leave the two out of sync on
the client (fresh js with stale wasm, or vice versa).

Align the cache configuration across the MediaPipe assets so they
are always cached and invalidated together.
2026-08-06 13:16:37 +02:00
lebaudantoine f3626a2dc6 🐛(frontend) serve MediaPipe assets under a versioned path
The MediaPipe assets were served under /assets, where the cache
behavior differs between wasm and js files. As a result, clients
could end up with a fresh js loader paired with a stale wasm binary
(or vice versa), leaving MediaPipe out of sync.

Copy the assets under a versioned route so the URL changes whenever
the dependency version bumps. Clients then reload both the js and
the wasm together, keeping them in sync.
2026-08-06 13:16:37 +02:00
lebaudantoine 41e937c1f1 🔖(patch)) release 1.25.1 v1.25.1 2026-08-06 11:31:08 +02:00
lebaudantoine d988c72208 🚑️(frontend) fix background crash from MediaPipe WASM version mismatch 2026-08-06 11:27:26 +02:00
lebaudantoine b96d591db3 🔖(minor) bump release to 1.25.0 v1.25.0 2026-08-05 18:12:42 +02:00
lebaudantoine bffc51ac4c 🐛(frontend) add trailing slash on the fetch-room URL
The fetch-room URL was missing its trailing slash, which caused the
backend to issue a 301 redirect. Query parameters were being dropped
in the process, leading to incorrect requests.

Append the trailing slash so the request hits the correct endpoint
directly, without going through a redirect.
2026-08-05 18:04:52 +02:00
lebaudantoine 22a1713c60 🐛(frontend) fix concurrent PATCH races on room settings
Rapid toggles could persist a stale configuration: each PATCH
replaces the full room config, and every call site built it from a
render-time snapshot. A toggle issued before the previous one
resolved therefore overwrote the newer value with an older one.

Handle the cache centrally in usePatchRoom so the next toggle always
reads an up-to-date configuration.
2026-08-05 17:23:48 +02:00
lebaudantoine c23f449520 🐛(frontend) stop passing username as a query param when undefined
Skip adding the username query parameter when its value is
undefined, so the request URL no longer ends up with an
`?username=undefined` (or similar) that the backend has to handle.
2026-08-05 17:23:48 +02:00
lebaudantoine 0536896373 (sdk) add a room configuration popup from CreateMeetingButton
Introduce a room configuration popup opened from the SDK's
CreateMeetingButton, laid out like the Google Meet "call options"
dialog: logo header, grey section bands, and a footer bar with the
close action.

Like CreatePopup, it runs in a dedicated popup window so it can
access session cookies, which would be blocked in an iframe. If the
user is not authenticated, they are redirected to login and come
back to this popup afterwards.

Permissions are enforced server-side. The room is fetched with the
user's session, and settings are only shown when the room is
administrable by this user. Since #1482 removed the
is_administrable flag from the room serializer (roles now live in
the LiveKit participant attributes, only available in-meeting),
administrability is detected here through the presence of the
`accesses` field, which the backend only serializes for
administrators and owners. The PATCH endpoint enforces the same
permissions server-side regardless.

The settings mirror the in-room Admin panel. Unlike the Admin panel,
there is no LiveKit connection here, so changes are only persisted
in the room configuration (and applied when a session starts):
participants of an ongoing session are not live-synced or notified.
2026-08-05 17:23:48 +02:00
lebaudantoine f49c61d9bf (sdk) allow passing a background color to the calendar iframe
Let integrators pass a custom background color to the iframe used by
the calendar SDK, so it can match the surrounding product's theme.
2026-08-05 17:23:48 +02:00
lebaudantoine b593516802 🔒️(backend) derive connection-test room max age from token TTL
Refactor CONNECTION_TEST_ROOM_MAX_AGE_SECONDS so it is no longer an
independent setting but a quantity derived from (or added on top of)
the token TTL.

This prevents a misconfiguration where the token would outlive the
delete-room callback. In that case, an attacker holding a valid
token could recreate the room after the callback fired and escape
the intended cleanup.
2026-08-05 15:33:13 +02:00
Arnaud Robin 1328098c45 🐛(frontend) keep Unicode initials intact in avatar
Some characters span multiple UTF-16 code units. Taking a naive first
index for avatar initials can split them and show a broken glyph when
the camera is off.

Optically fix initials centering with a more complex approach.
2026-08-05 14:47:29 +02:00
lebaudantoine 58205f81d4 🐛(frontend) fix icon centering in the Switch primitive
Icons inside the Switch primitive were not properly centered.

Use relative sizes for the icons and switch to a grid-based
placement strategy so they stay centered regardless of the switch
size.
2026-08-05 13:47:50 +02:00
lebaudantoine b7892431be 💄(frontend) show pointer cursor on interactive switches
Set the cursor to a pointer on Switch components when they are
actually interactive, so it is visually clear that they can be
toggled.
2026-08-05 13:47:50 +02:00
lebaudantoine 00a2bd9558 🐛(backend) serialize lazy title in summary payload
_generate_title returned a lazy gettext_lazy proxy in the
recording_datetime is None branch, which json.dumps cannot
serialize.

This crashed requests.post(json=payload) with "Object of type
__proxy__ is not JSON serializable" whenever the LiveKit egress
lookup failed (started_at=None).

Force evaluation with a non-lazy method.

Add a regression test asserting the v2 payload is a real str and
is JSON-serializable when timestamps are unavailable.
The existing without_metadata test missed this: mocked
requests.post never serialized, and a lazy proxy compares equal
to its string.
2026-08-05 12:48:45 +02:00
lebaudantoine bc003f928e ⚗️(frontend) add candidate pair diagnostic to WebRTC checks
Add a custom diagnostic step that reports which ICE candidate pair
was selected on the WebRTC connection, as well as all working pairs
observed during the check.

Experimental and vibe-coded for now; the output is meant to help
debugging and will likely be revisited.
2026-08-05 12:16:40 +02:00
Arnaud Robin d756825fd7 (frontend) add connection test feature
Introduce a new connection test page to allow users to verify
their device and network compatibility with the application.
The feature also supports generating and downloading a detailed report
of the test results.
2026-08-05 12:16:40 +02:00
Arnaud Robin b01a47bfd7 (backend) add connection-test API
Currently users have no way to reliably test their connection before
joining a room. To address this, we plan to build a connection-test
page.

The testing requires a dedicated LiveKit token, issued without going
through the room API, which is tied to registered meetings, lobby
rules, and longer-lived access tokens.

Introduce a new viewset for all diagnostics-related features. The
first route issues a token for diagnostics, even for anonymous
users. Each request creates a new dedicated room so users never
share the same LiveKit room during tests. Tokens are short-lived
(default 10 minutes) to limit reuse, and the endpoint is throttled
to prevent abuse.

A Celery worker also schedules a callback that deletes the room
after a certain delay, in every case.
2026-08-05 12:16:40 +02:00
davd-gzl 06e73d7a5e 🐛(frontend) stop the installed app reopening the room it came from
site.webmanifest declared no start_url, so the page that linked
it became one and an install started inside a room reopened that
room on every launch. It now declares "/", moves out of public/
and takes VITE_APP_TITLE for name and short_name, which shipped
empty and leaned on the browser falling back to the title.

The frontend Dockerfile now declares that build argument, so the
value compose.yml passes stops being dropped.
2026-08-04 20:38:37 +02:00
lebaudantoine 6c69c3d6e3 🎨(frontend) apply lint fixes
Run the frontend linter/formatter and commit the resulting fixes to
keep the codebase clean and consistent.
2026-08-04 20:28:44 +02:00
leo 7fd4d20ea7 ⬆️(dependencies) update python dependencies
Update python dependencies. Fix linting due to ruff bump.

Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-04 20:27:26 +02:00
lebaudantoine 15c9ee225a ️(frontend) revert GridLayout re-render optimization too
Out of precaution, also revert the previous GridLayout re-render
optimization to avoid any layout regression alongside the
CarouselLayout revert.

The useSize-based re-render optimization will be reintroduced in a
dedicated small PR and release. That will also be a good occasion to
polish the layout code along the way.
2026-08-04 20:01:41 +02:00
lebaudantoine d86768bb59 ️(frontend) revert CarouselLayout re-render optimization
The previous optimization of the CarouselLayout was broken: the
approach did not hold in practice, and strict-mode rendering was
hiding the issue during development.

Revert the change for now and revisit the optimization later with a
sounder approach.
2026-08-04 20:01:41 +02:00
lebaudantoine 7bd5ab7a13 (frontend) expose media state to external gateways
Add a hidden div in the DOM that reflects the current state of the
microphone and camera, so that external SIP media gateways (e.g. the
Renater one) can observe it and keep an accurate view of the media
state.

Also emit a custom event from the page whenever the microphone or
camera state changes, so external consumers can subscribe to updates
instead of polling the DOM.
2026-08-04 19:04:16 +02:00
lebaudantoine ac8eae7295 (backend) apply user preferences on unconfigured room creation
Update the API so that, when a user creates a new meeting without
passing an explicit configuration, the user's persisted preferences
are applied as defaults.

This allows a user to, for example, enable the waiting room by
default on every meeting they create.
2026-08-04 19:04:16 +02:00
lebaudantoine 15b1ab7e0a (frontend) let users set default configuration for generated links
Extend the existing out-of-room settings so users can configure a
default room configuration that is applied to every link they
generate from the app.
2026-08-04 19:04:16 +02:00
lebaudantoine 2e509eff28 (backend) persist user preferences for room defaults on the User model
Add attributes on the User model to persist per-user preferences for
the default link access level and the default room configuration.

The frontend will let users update these preferences and then reuse
them when generating a link through the webapp.

Persisting them on the backend (rather than in application memory
only) ensures the preferences survive across sessions and devices.
2026-08-04 19:04:16 +02:00
lebaudantoine ca56ae87c2 (backend) expose the default room access level in settings
Expose the default access level for rooms in the backend settings
response, so the frontend can initialize the global room preferences
UI with the current default value.
2026-08-04 19:04:15 +02:00
lebaudantoine 2c5a766d02 🔥(ci) remove unused Anthropic security step
Drop the Anthropic security step from the project CI, as it is no
longer used and only added noise to the pipeline.
2026-08-04 19:04:15 +02:00
davd-gzl a67467b193 🩹(all) clear the SonarCloud reliability finding and the lint debt
The SonarCloud gate fails on main, so every commit lands red, and
gh run list hides it: it lists only Actions workflows, and the
failure is an app check run.

Reliability rests on one bug, in test_file_service.py, which wrapped
an assertion in an except Exception re-raised through pytest.fail.
Removing it takes the rating from D to A.

Two pieces of debt ride along. The SDK callback id now comes from
crypto.getRandomValues, since it guards an endpoint with no auth. And
core/tasks gets the __init__.py that lets pylint see it, with the
debt that exposes, which is why #1533 fails lint-back.
2026-08-04 19:04:15 +02:00
Florent Chehab 24d5a5a035 🧑‍💻(backend) use solo pool celery worker in dev
Change to reduce memory usage in dev.
2026-08-04 19:04:15 +02:00
lebaudantoine 9e500a59ea 🧑‍💻(backend) add commented the roomkit env variables
Useful for an easier devex when working on the feature.
2026-08-04 19:04:15 +02:00
lebaudantoine 8a0d4b1ad6 ♻️(backend) refactor tests to rely on decorators
Slightly refactor the existing tests to use decorators for common
setup and configuration.
2026-08-04 19:04:15 +02:00
lebaudantoine d0726ba631 🐛(backend) ensure SIP dispatch rule instead of creating it
The roomkit can now create a SIP dispatch rule before the LiveKit
webhook that used to trigger this creation is fired. In practice,
when the roomkit connects to the room, it also triggers the
webhook, leading to a duplicated dispatch rule.

Switch from "create dispatch rule" to "ensure dispatch rule exists"
semantics, so subsequent calls are idempotent and no duplicate rule
is created.
2026-08-04 19:04:14 +02:00