Skip to content

feat: NV conference provider (Old PoC) - #41735

Draft
rodrigok wants to merge 63 commits into
developfrom
feat/native-video-conference
Draft

feat: NV conference provider (Old PoC)#41735
rodrigok wants to merge 63 commits into
developfrom
feat/native-video-conference

Conversation

@rodrigok

@rodrigok rodrigok commented Aug 10, 2026

Copy link
Copy Markdown
Member

PoC — not for merge as-is. Draft, stacked on #41657.
Base is feat/persistant-chat, so this diff shows only the native-provider work. Review #41657 first.

Proposed changes

Adds LiveKit as a native video conference provider, so a call can run inside Rocket.Chat instead of handing the user off to someone else's page.

Every provider until now has been a URL. The workspace knows a call is open and nothing else: not who is in it, not what was said, and certainly not how to record it. Running the call in-process changes what the server is able to know, and most of this diff is the consequence of that rather than the SFU itself — conferences now carry the participants actually connected, so presence stops being inferred from who clicked join.

On top of that: multi-party calls with a grid and spotlight, screen sharing, hand-raise and reactions, egress-based recording that lands as an ordinary upload posted in a thread under the call's message, and opt-in live captions from a worker that joins each room as a hidden participant — with note-taking on, the transcript is kept and summarised when the call ends.

The provider is enterprise and off unless configured. A workspace with no LiveKit deployment behaves exactly as before.

Deployment (CloudFormation + walkthrough): docs/features/native-video-conference/deploying-livekit.md.

Where this came from

Ported from #40726 (feat/vc-poc), minus that PR's persistent-chat work, which this stack already carries from #41657. That PR's history splits cleanly: commits up to 7e617cc8a8c are the LiveKit work, everything after is its own conference-window integration. Only the first range was taken, so none of client/views/conference/, client/views/root/, appLayout, desktop-api or startup/routes.tsx came across.

Reconciling against a newer develop needed six things:

  • app/ was restructured — the settings, notifications and file-upload imports across eight EE files point at their new homes (server/settings, server/lib/notifications/core/lib/Notifications, server/lib/media/file-upload, server/meteor-methods/messages/sendFileMessage).
  • useGoToRoom left ui-contexts for the app.
  • fuselage no longer accepts the w / h / bg shorthands.
  • MediaCallRoomActivity gained MediaCallViewProvider and container-height sizing on develop while the PR replaced that sizing with useTileGridLayout; the PR's layout wins, since it is what the rewritten section expects.
  • IVideoConference took both sides — this stack's membership predicates and the provider's participant / recording / transcript records.
  • @livekit/agents and @livekit/rtc-node were pinned at latest, which a committed lockfile cannot honour. Pinned to what resolved (^1.6.2, ^0.13.33).

Then it was wired to the conference window

Testing against a live LiveKit found the seam between the two features, and it was worse than "not wired": with both enabled, the room's call button led to a preflight that never went anywhere. An embedded provider answers the join with an empty url — there is no page to send anyone to — and the conference page read a missing url as "not joined yet", so it re-rendered the preflight forever.

  • The page now waits on having joined rather than on having a url, and renders the call itself when there is no page to embed.
  • The connection is registered with the bridge above the route, which is the same slot the in-room path uses — a call cannot be claimed twice, and it survives the page re-rendering.
  • One bottom bar. The call brings mic, camera, screen, hand and hang-up; the window already had a bar carrying members and chat. The call's controls are handed to that bar and take its centre. They are moved, not rebuilt — the same nodes in the same component — so the two placements cannot drift.
  • The floating mini-view is gone. It followed the user out of the call's room, which a call that always has a window of its own never needs; in that window it drew a second copy of the call over the members panel.
  • Twelve strings in the call's bar were hardcoded English or asked for keys that were never added, so recording, notes, captions and the camera tooltip rendered as Start_recording and friends. The hang-up button read "End call with Call" on group calls.

Steps to test or reproduce

Requires a LiveKit deployment; see the deploy doc. Then, as admin:

  1. Admin → Video Conference — enable LiveKit, set URL / API key / secret, make it the default provider.
  2. Open a channel and press Video call. The preflight opens in a dedicated window; set mic/camera there.
  3. Confirm — the call runs in that window with its chat and members panels beside it, and the controls in the single bottom bar.

Verified

Against a live LiveKit (wss://livekit.dev.rocket.chat):

video-conference.capabilities reports the provider embedded: true
video-conference.start creates the conference, posts the call block
video-conference.join returns url: "", records the participant
…livekit.transport.config mints a real room token
The call connects; live 1280×720 camera track; members panel populates
Preflight honoured started with mic off / camera on — the inverse of the provider defaults — and the call came up muted with video
One bar 1 footer; 8 call controls + 2 panel toggles in it
Layout call column min-width: 0, no horizontal overflow

yarn typecheck clean · jest 309 suites / 2214 passing · mocha 2258 · definition 152 · eslint clean over the ported tree.

Not verified — please exercise before this goes anywhere near merge

  • A second participant. Grid and spotlight layout, per-tile speaking indicators, hand-raise queue positions and reactions have all only been seen with one person in the call.
  • Recording. The egress path, S3 upload, and the thread reply were never triggered.
  • Captions / transcripts / AI summary. The agent worker was never dispatched.
  • The 1:1 VoIP path. MediaCallRoomSection now renders CallStage for direct calls too, replacing develop's MediaCallCardList. That is the ported PR's design, but it changes existing VoIP call rendering and deserves a look on a running workspace.
  • Docker: Dockerfile.alpine gained a musl build of the LiveKit FFI binding; the image was never built here.

Further comments

MEDIA-CALLS-FEATURE.md and deploy/livekit/deploy.md moved under docs/features/native-video-conference/, where this repo keeps feature docs. The CloudFormation templates stay in deploy/livekit/.

🤖 Generated with Claude Code

Review in cubic

@dionisio-bot

dionisio-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f9365fb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@rocket.chat/core-typings Minor
@rocket.chat/model-typings Minor
@rocket.chat/models Minor
@rocket.chat/rest-typings Minor
@rocket.chat/jwt Minor
@rocket.chat/ui-voip Minor
@rocket.chat/meteor Minor
@rocket.chat/license Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (2a7de45) to head (f9365fb).
⚠️ Report is 29 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##           develop   #41735       +/-   ##
============================================
- Coverage    69.33%        0   -69.34%     
============================================
  Files         4255        0     -4255     
  Lines       168644        0   -168644     
  Branches     30052        0    -30052     
============================================
- Hits        116929        0   -116929     
+ Misses       46534        0    -46534     
+ Partials      5181        0     -5181     
Flag Coverage Δ
e2e ?
e2e-api ?
unit ?

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rodrigok
rodrigok force-pushed the feat/persistant-chat branch from 9e8f454 to e36e37e Compare August 14, 2026 00:52
@rodrigok
rodrigok force-pushed the feat/native-video-conference branch 2 times, most recently from 95764dc to d57634e Compare August 14, 2026 02:03
Comment on lines +10 to +11
label
.replace(/\s*\([0-9a-f]{4}:[0-9a-f]{4}\)\s*$/i, '')
@rodrigok
rodrigok force-pushed the feat/persistant-chat branch from 20377ad to 3ab5aea Compare August 18, 2026 22:47
@rodrigok
rodrigok force-pushed the feat/native-video-conference branch from 3dc75f8 to 4dc27ae Compare August 18, 2026 23:01
@rodrigok
rodrigok force-pushed the feat/persistant-chat branch from 3ab5aea to 9d8f48f Compare August 19, 2026 17:09
@rodrigok
rodrigok force-pushed the feat/native-video-conference branch from 0ef8550 to 0061d3c Compare August 19, 2026 17:17
@rodrigok
rodrigok force-pushed the feat/persistant-chat branch from 9d8f48f to a1b8a88 Compare August 19, 2026 22:33
@rodrigok
rodrigok force-pushed the feat/native-video-conference branch 2 times, most recently from 73a6f9d to cb10cdf Compare August 19, 2026 22:39
rodrigok and others added 15 commits August 20, 2026 22:24
… bar

Move the conference chrome from a vertical side rail to a control bar along the
bottom of the call — the position third-party providers put their own toolbar in,
so an embedded provider and the future native conference read the same.

`SideRail`/`SideRailPanel` are renamed and split into `CallBar` (+ `CallBarActions`,
`CallBarAction`) and `CallPanel`, so both names describe their position and can be
reused by the native video conference.

The page is now a column — a row holding the call and the panel, then the bar — so
`CallPanel` is a sibling of the call area rather than a child of the bar. Toggling
the chat animates its own width without ever reflowing the bar, by construction
instead of by careful sizing. The panel moves to the inline end to match the chat
action, which puts its close button at the far end of its header.

`CallBarActions placement='end'` is taken out of flow and anchored to the inline
end, so adding or removing end actions never pulls the centred controls
off-centre — the shape the native conference needs once it owns mic/camera/
screen-share/hang-up.

Add `useProviderCallBridge`, letting a provider that renders its own toolbar hide
ours and drive the chat panel (`set-call-bar-visible`, `set-chat-visible`,
`toggle-chat`) so the user never sees two competing sets of controls. The iframe is
cross-origin, so `event.origin` cannot be allow-listed against our own origin;
instead every message must come from the exact window we embedded, which no other
frame or tab can forge. Unknown commands and malformed payloads are rejected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A call belongs in its own window rather than a tab in the user's strip — it
mirrors what the desktop app already does with its dedicated video window, and
keeps the call visible while the user works in the main app.

`useVideoConfOpenCall` now tries a popout (1280x800, capped to the available
screen and centred) and falls back to an ordinary tab when it is refused; some
browsers and extensions block popup-shaped windows while still allowing a plain
one. Only when both are blocked does `VideoConfBlockModal` ask the user to allow
it. A window that comes back already closed counts as blocked too.

`noopener` is deliberately absent from the features string: the conference page
posts navigation requests back to its opener, and `noopener` would both sever
that link and make `window.open` return null — indistinguishable from a blocked
popup, so the popout would silently degrade to a tab forever. There is a test
pinning that.

Reusing the shared conference window is unchanged, and now covered: already
showing this conference focuses it without features, so a window the user has
arranged is not resized or recentred; a different conference navigates it; a
closed one is reopened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…loading

`UsernameCheck` shows `HomeSkeleton` — a sidebar list, room and composer skeleton
— while it resolves the user. It sits in the shared authentication chain, so the
conference, which renders standalone, was flashing a whole fake app shell it
never goes on to show.

This is the same shape of problem as the navigation chrome itself: the chrome
moved to `MainLayout`, but its loading counterpart stayed behind in the auth
chain. `AuthenticationCheck` and `UsernameCheck` now take an optional `loading`
node. It still defaults to `HomeSkeleton`, so no existing route changes, and the
conference route passes `PageLoading` instead — which also matches what the
conference shows while joining, making startup one continuous state rather than a
skeleton followed by a spinner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The chat panel is 400px, but it renders the full room UI, which is spaced for the
main app. The default message start padding plus the avatar gutter's left margin
spent more of that width on empty space than the panel can spare, and the
composer sat flush against the edges — opting into the embedded layout zeroes its
inline padding, which is sized for the tiny `?layout=embedded` iframe where every
pixel counts.

`narrowRoomStyle` restores the composer's padding and trims the message start
padding and gutter margin, scoped to this subtree so the room's normal full-width
appearance and every external embed are untouched. Only the start padding is
trimmed; the message toolbar and timestamp column sit against the end padding and
need the room. The panel header is condensed to match.

The call iframe is named with `aria-label` rather than `title`: a `title` on a
full-viewport iframe also renders as a hover tooltip, floating a label over the
call for as long as the pointer is inside it.

Also documents the popout ladder and the standalone loading placeholder from the
two preceding commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Documents the verified add-participants behaviour per room type, and the agreed
design for separating "being in the call" from "being able to read the chat".

Adding someone to a conference currently puts them in a room — the conference's
room, or a fresh discussion — and join authorization is then derived from room
membership. That conflates two different things. The planned model makes the
existing `users[]` the membership list with a per-entry `joined` flag, authorizes
on room access *or* conference membership, rings the added users, and surfaces
members who can't see the chat with a remedy instead of forcing that choice
up-front.

The section is fenced as planned-not-implemented and doubles as the progress
tracker. It records the decisions behind the design, and the traps each phase has
to clear — notably that `addUserById` uses `$addToSet` with a whole document (so
marking a member joined later would append a duplicate rather than update), and
that every `users[]` entry already stored predates the flag and represents
someone who joined, so a reader filtering on it would show historical conferences
as empty.

Also documents the verified per-room-type matrix for the current flow, including
the divergence where a repeated "don't keep history" add rebuilds from the
original room and drops earlier invitees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adding someone to a conference used to put them in a room — either the
conference's room, keeping its history, or a fresh discussion — because joining
was authorized purely from room membership. That conflated being in the call with
being able to read its chat.

`users[]` becomes the conference's membership list, with a per-entry `joined` flag
and `joinedAt`; `ts` keeps meaning "became a member". `join`/`info` now authorize
on conference membership *or* access to the conference's room or discussion, so
someone added from outside the room can join the call without being handed the
room's history. `add-participants` registers members, touches no room, drops
`keepHistory`, and returns the ids it added.

Two traps this had to clear. `addUserById` used `$addToSet` with a whole
document, which compares entire objects — once an entry can be mutated, a second
write would append a duplicate instead of updating. It is replaced by
`addMemberById` (a `$push` guarded on `users._id` in the query, atomic and
idempotent, which also removes a racy in-memory dedup) and `setUserJoinedById`
(`$set` through `arrayFilters`). And every `users[]` entry already stored predates
the flag while representing someone who joined, so `hasJoinedVideoConference`
reads an absent flag as joined and is the only place that decides — no reader
tests the field, and no backfill or deploy ordering is required.

Every reader that means "who is in the call" now filters through that helper: the
message block's counts, name list, avatar stacks and empty state, the call history
list's avatars, and the direct-call ring-stop check. The `video-conference`
stream's `allowRead` deliberately keeps using full membership, so someone added
can follow `discussionUpdated` before joining. `assignDiscussionToConference` now
subscribes the union of the conference's members and the original room's members,
rather than only those who had joined.

Ringing is decided per call event against the list being rung, via
`shouldRingVideoConference`. Starting a call rings the room, so a large room still
rings nobody; an add rings only the people added and is capped at the same limit,
so an add always rings.

This also makes server-originated ringing work at all. The server has long set
`ringing: true` on group conferences and broadcast a `ring` action, but no client
ever handled it — the only match for `'ring'` in the client was a word in the
E2EE wordlist. Real DM ringing is driven client-side by the caller repeating
`call` on an interval. `VideoConfManager` now handles `ring`, which means group
conferences that were silently not ringing will start to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Declining a conference is now stored on the member's `users[]` entry as `declined`
/ `declinedAt`, through `POST /v1/video-conference.decline`.

The endpoint takes no target user and writes only against the caller's own
membership, so declining can never be a way to end someone else's conference. The
client-published `rejected` message stays, because the 1:1 flow depends on it —
the caller's client is waiting on it to stop dialling — but it is a claim one
client makes about another user's call, so it is no longer what gets stored.

Someone rung as a member of the conference's room has no entry yet, so declining
creates one; without it there is nowhere to record the decline. Declining is not
exclusive with joining — a member can decline and then join later.

Adds a regression test pinning that a rejection for a call we are not placing
never reaches `video-conference.cancel`. That currently holds only because
`onDirectCallRejected` bails when the callId doesn't match `currentCallData`, and
the teardown sits behind that same guard, so widening it would silently let one
person's decline end everybody's call.

Also extracts `mapVideoConfUserFromApi`: membership now carries three optional
timestamps, each arriving as a string over REST, and deserializing them one at a
time meant every new field surfaced as a type error at a distant consumer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
These assert the shape of the update document rather than its effect, which is the
level the bug they exist to prevent lives at: `$addToSet` on a whole document
stops de-duplicating the moment an entry can be mutated, so the guard has to be in
the query. A regression would reintroduce duplicate member entries, which is
invisible until someone counts participants.

Also pins that declining does not clear `joined` — a member can dismiss the ring
and join later — and that `joinedAt` is omitted rather than stored as undefined.

`BaseRaw` is stubbed because it participates in a circular import that leaves it
uninitialized when this module is loaded directly by jest; only its constructor
matters here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The call-history page could only ever record VoIP calls: `CallHistoryItem` was a
union of two `type: 'media-call'` variants, both 1:1-contact-shaped. A conference
is room-and-many-participants shaped, so it joins the union as a sibling variant,
`IVideoConferenceHistoryItem`, carrying the room, the conference title, and how
many members actually joined.

Ending a group conference writes one item per member, since history is per-user.
`buildConferenceCallHistoryItems` is kept pure and DB-free so the per-member
semantics are unit-testable: `direction` is outbound for the creator and inbound
for everyone else, and `state` is `ended` for a member who joined, `not-answered`
for one who did not. Only members get an item and a member either joined or was
rung and didn't, so not joining is not answering — whether they declined or just
ignored it. Reporting an ignored ring as a normal ended call would hide a missed
conference.

`setStatus(ENDED)` is public and reachable more than once for the same call, so
the write is guarded on the call not already having ended; without that, every
member collects a duplicate entry. A history failure is logged and swallowed
rather than failing the end of the call.

Every item type shares one collection, so the existing direction/state filters
needed no change; only the free-text search gained a branch matching the
conference title. The table dispatches conference items to a new row showing the
room and joined count instead of a contact.

Two type holes that widening the union opened are closed here too:
`MediaCallHistoryExternal` typed its payload as
`Exclude<CallHistoryItem, IInternalMediaCallHistoryItem>`, which silently began
including conference items and would have broken `item.duration`, and its
`isExternalCallHistoryItem` guard tested `type !== 'media-call'` — previously dead,
now wrong. Both are narrowed to the external media-call variant they mean.

Deep-linking to a conference item's detail route still falls through to the
generic "call info could not be loaded" panel; a conference detail view is out of
scope and noted in the docs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Conference membership deliberately grants no room access, so a member can be in
the call while unable to read its chat. Rather than forcing that choice on whoever
adds a participant, it is surfaced once it actually matters.

`video-conference.info` reports `membersWithoutChatAccess`, and
`POST /v1/video-conference.share-chat` fixes it. Access is asked per member with
`canAccessRoomIdAsync` rather than derived from subscriptions, because reading a
room doesn't always require one — a public channel is readable by anyone unless it
belongs to a private team. Conferences are small enough that getting that case
wrong matters more than the extra reads.

The remedy asks the room whether it can take new members —
`allowMemberAction(room, RoomMemberActions.INVITE)` — instead of testing for a DM.
The room type owns that rule, and it covers cases a `t === 'd'` check misses, such
as a federated DM that can grow. A room that can take members is joined; one that
can't has its chat moved to a discussion carrying everyone.

`ChatAccessNotice` renders in the chat panel, where the remedy is in context, and
moves up to the conference page while the panel is closed — so it can't be missed,
and is never shown twice.

This also closes the divergence documented earlier:
`createConferenceDiscussionWithParticipants` now builds from `discussionRid || rid`
rather than always the original room, so a second discussion no longer silently
drops everyone added since the first. It is reached as a remedy now, which is why
the fix matters again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Captures what isn't visible from the code: that group ringing was dead code before
this (the server broadcast `ring` and no client handled it, so enabling it makes
conferences that were silently not ringing start to), that declining creates a
membership entry because there is nowhere else to record it, where the test
coverage lives and why those runners, and the gaps left open — no members panel, no
conference detail view in call history, one access check per member on info, and
the 10-user cap per add that guarantees an add always rings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ringing on add is the first case where a call rings someone with no access
to the room it belongs to, and the incoming popup was built entirely around
that room: it read it from the client store and returned null when it wasn't
there, while still calling focusManager.focusFirst() — which then looked for
the parent of a node the focus scope never got.

Incoming popups now render without a room, describing the call from the
conference record the popup already fetches. The popups that act on a room,
starting or placing a call, still require one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1:1 accept is a handshake: the callee publishes `accepted` and waits for the
caller's client to reply `confirmed`, giving up after 5s. A server-originated
ring has no caller waiting, so running that handshake left a user who had
just been added staring at "No response from remote user".

Incoming calls now carry a `handshake` flag. Without it, accepting joins the
conference outright — membership is what authorizes joining — and declining
records the decline without publishing `rejected` to whoever added them,
which their client would read as their own call being turned down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… chat

The notice for members who can't read the chat applied the only remedy the
room allowed, behind a "Give access" button, on a hand-rolled banner with
unreadable contrast.

Both ways out give something away, in different directions: inviting exposes
the room's whole history to an outsider, while moving the chat to a discussion
leaves the earlier history behind for everyone already there. So the notice now
opens a modal that names the members and spells out each consequence next to
its own button. Which action leads is a privacy judgement — private rooms and
DMs lead with the discussion, public rooms with the invite — and `share-chat`
takes a `mode`, re-deriving `canInvite` server-side rather than trusting it.

The banner is now the one rooms use for announcements, so it inherits readable
contrast. `AnnouncementBanner`'s className was too narrow to accept css output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@rodrigok
rodrigok force-pushed the feat/persistant-chat branch 4 times, most recently from 9377e17 to 93f4ac4 Compare August 29, 2026 20:07
@rodrigok rodrigok changed the title feat: LiveKit as a native video conference provider (PoC) feat: NV conference provider (Old PoC) Aug 29, 2026
@rodrigok
rodrigok changed the base branch from feat/persistant-chat to develop August 29, 2026 21:24
rodrigok added a commit that referenced this pull request Aug 30, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Aug 31, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 1, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 2, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 2, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 2, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 3, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 3, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 4, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
rodrigok added a commit that referenced this pull request Sep 4, 2026
The rebuilt branch had every native file and nothing connecting them, so a call
opened its window onto an empty iframe. This is the wiring.

Taken from #41735 where it is the newer work: the stage rewrite of
`MediaCallRoomSection` and `MediaCallRoomActivity`, the device pickers, the
members panel with its raised hands and per-participant mute, the preflight with
its device selectors and camera preview, and the page that puts them together.
Kept from here where *this* is the newer work: #41944's `ActionButton`,
`ToggleButton`, `ActionToggleChat` and `usePlayMediaStream` — that PR was merged
after the native branch was cut, so taking its ui-voip wholesale would have
undone the blink fix and the badge's accessible name.

Two things the native page drops that this stack had, restored: the top bar's
elapsed time and call name, which only an iframe provider needs because a native
one supplies its own header; and the bottom control bar is now rendered only for
a provider that has controls to put in it, rather than as an empty strip under
an iframe.

The two LiveKit endpoints are declared in `rest-typings` rather than left
untyped as they are on that branch — this stack's API layer infers request and
response from the endpoint map, and an undeclared path silently widens it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants