feat(video-conf): persistent-chat call flow, behind a new setting (3/4)
Stacked PR — 3 of 4. Base is #41956, itself based on #41934. Review order: #41934 (server) → #41956 (conference window, unmounted) → this one → #42000 (LiveKit provider). GitHub shows this diff against #41956, so it only reads correctly in that order.
Proposed changes (including videos or screenshots)
The behavioural half: the part that rewires the existing call flow rather than adding alongside it. Where #41956 was unmounted by construction, everything here is reachable — so it ships dark.
One new workspace setting, VideoConf_Conference_Window_Enabled (default false), gates every change in this PR. With it off, the client behaves exactly as it does on the base branch. Turning it on switches the workspace to the new call experience.
The important consequence: VideoConf_Enable_Persistent_Chat keeps meaning only what it means today — a discussion per call. Workspaces already using persistent chat see no change until they opt into the new flag as well. Several of these behaviours were gated on that older setting on the source branch, which would have flipped those workspaces silently; they are moved onto the new flag here.
A second setting, VideoConf_Persistent_Chat_Mode (thread | main_room), decides where a call's chat lives once the window is on. With the window off the server forces main_room, which is what it does today.
What turning the flag on changes
| Surface | With the flag on |
|---|---|
| Starting a direct call | The caller's own call window opens on the click that asked for it, so the room no longer shows a "Calling…" popup for it |
| Incoming calls | Listed in a new navbar ongoing-calls menu instead of appearing as popups; declining records a server-side decline |
| Provider hand-off | The call opens in a sized, named popout window rather than a plain tab, and closing it reports the departure |
| Room header / user card | The call button goes through the new preflight instead of the start-call popup; the user-card action also works with no room yet |
| Sidebar | The "Incoming Calls" group and its accept/reject row actions give way to the navbar menu |
With the flag off: no popups suppressed, no popout window, no window watcher, no navbar item, no video-conference.joinable polling, and the sidebar group and its row actions stay exactly as they are today.
Security
Opening a provider URL in a window of our own means handling a URL that came from a provider record. asCallUrl accepts only an absolute http(s) URL — parsed with no base, so a relative string cannot be resolved against our origin and javascript: / data: cannot get through — and the window is opened blank with opener cleared before navigating, so the provider's page has no handle on ours.
45 files, +3,354 / −147. Includes the feature's documentation (docs/features/video-conference-persistent-chat/) and a second e2e spec that drives the flag-on flow.
Issue(s)
https://rocketchat.atlassian.net/browse/NV-64
Steps to test or reproduce
Flag off (the default — this is the important half):
- Deploy with no settings changed.
- Start a 1:1 call and a group call with a URL-based provider (Jitsi/Meet). The "Calling…" popup, the incoming-call popups, the sidebar "Incoming Calls" group and its accept/reject buttons must all behave exactly as on
develop. - Confirm in devtools that no request is made to
video-conference.joinable.
Flag on:
- Enable Admin → Video Conference → Conference window.
- Start a direct call: the call window opens immediately for the caller; the callee is rung and can answer or decline from the navbar menu.
- Add a participant who is not in the room, and check the chat-access notice and its remedy.
- Close the call window and confirm the call ends for everyone once the last participant leaves.
- Try both
VideoConf_Persistent_Chat_Modevalues and check where the call's chat lands.
Further comments
- The e2e suite covers both halves: the existing
video-conference-ring.spec.tsis unchanged and proves the flag-off path;video-conference-call-window.spec.tsenables the setting and drives the new flow. - No
enableQuerytying the new setting toVideoConf_Enable_Persistent_Chat. The dependency is not real — with persistent chat off, the conference chat falls back to the call's own room — and chaining them would both hide the new experience behind an unrelated opt-in and change what persistent-chat workspaces see, which is the thing this PR must not do. useJoinableCallsreads the setting itself rather than taking anenabledprop, so no future caller can reintroduce the 20-second poll by forgetting to pass it.TimedVideoConfPopup'shasPopupis not gated symmetrically. Preserving the flag-off path byte-for-byte would have preserved a latent throw (focusFirston an empty scope), reachable before this work whenever an incoming call arrived for a room minimongo did not yet have. A flag-off test hit it, so it is fixed rather than reproduced.- The dark path had no coverage before this PR. It gains flag-off tests on the manager (start / join / decline /
isCalling/ring), the sidebar group, the open-call hook, the joinable-calls query and the popups, with mutation checks confirming each gate's test fails when the gate is removed. packages/jwt's HS256 helpers and the unconsumedMediaCallViewContextsurface are deliberately not here — they have no callers until the LiveKit provider (#42000), and travel with it.
Summary by CodeRabbit
-
New Features
- Added an optional dedicated video conference window with preflight controls.
- Added ongoing-call navigation, call history, conference membership, and configurable persistent chat placement.
- Improved incoming-call handling for conferences without direct room access.
- Added support for starting calls without an existing room when the conference window is enabled.
- Added safe handling and fallback behavior for conference links and blocked pop-up windows.
-
Documentation
- Added comprehensive documentation for persistent chat and conference windows.