Skip to content

Start voice call in PiP (not in fullscreen)#34055

Open
toger5 wants to merge 10 commits into
developfrom
toger5/start-voice-call-in-pip
Open

Start voice call in PiP (not in fullscreen)#34055
toger5 wants to merge 10 commits into
developfrom
toger5/start-voice-call-in-pip

Conversation

@toger5

@toger5 toger5 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Fixes: https://github.com/element-hq/voip-internal/issues/608

For the reviewer:
Pressing the expand button in the call toast will get you to the lobby.
This seems undesired. We can either fix this as part of this PR or move this problem to when we adopt a new toast layout/ design.
Screenshot 2026-06-30 at 16 43 20
Screenshot 2026-06-30 at 16 43 26

This PR surfaced an existing bug:

  • pressing the join button on an ongoing call SHOULD either start a voice or a video call (the user cannot choose manually anymore) based on the intent of the current call (what others do). So that the new joiner has the correct camera preset.
  • This was NOT the case. It always started a video call when pressing join.
  • This change surfaces this as pressing join -> starts a video call -> does not start in pip.
  • This was fixed and a regression tests added.

Checklist

@toger5 toger5 added the T-Feature Request to add a new feature which does not exist right now label Jun 30, 2026
@toger5 toger5 changed the title start voice call in pip Start voice call in PiP (not in fullscreen) Jun 30, 2026

@Half-Shot Half-Shot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A playwright test would be lovely if you could find some time for it. We should have some examples already.

Comment thread .gitignore
Comment thread apps/web/jest.config.ts
expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({ view_call: true, voiceOnly: false }));
});

it("clicking the join button of an ongoing voice call joins as a voice call", async () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't tell, is this testing the PiP bit?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. Its a regression test for a bug that surfaced working on this PR.

Let me document this in the PR description.

This PR surfaced an existing bug:
 - pressing the join button on an ongoing call SHOULD either start a voice or a video call (the user cannot choose manually anymore) based on the intent of the current call (what others do). So that the new joiner has the correct camera preset.
 - This was NOT the case. It always started a video call when pressing join.
 - This change surfaces this as pressing join -> starts a video call -> does not start in pip.
 - This was fixed and a regression tests added.

@Half-Shot Half-Shot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably would still prefer the two changes you factored out to be merged first (and not in this PR :P), but the changes look good to me.

@toger5

toger5 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@Half-Shot

Probably would still prefer the two changes you factored out to be merged first

Both of the factored out changes have been merged in seperate PR's now:

So the diff of this PR does not include the changes anymore (now, that I updated the branch.)

toger5 added 2 commits July 2, 2026 13:57
`should be able to join a ${callType} call in progress` test
@toger5

toger5 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@Half-Shot I also added the pip container check to the existing: should be able to join a ${callType} call in progress test.

@Half-Shot Half-Shot closed this Jul 2, 2026
@Half-Shot Half-Shot reopened this Jul 2, 2026
@Half-Shot

Copy link
Copy Markdown
Member

(provoking the cla tool into running :/)

@robintown robintown left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skimmed this so far and it raised the following questions for me:

call = CallStore.instance.getCall(payload.room_id)!;
}

// Custom case where we start voice calls in pip

@robintown robintown Jul 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this change apply to both placing a call and receiving a call?

// Custom case where we start voice calls in pip
if (payload.voiceOnly ?? false) {
viewingCall = false;
ActiveWidgetStore.instance.setWidgetPersistence(call.widget.id, room.roomId, true);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that eagerly setting the widget persistence to true before it's even loaded won't create new stuck widget issues? (Imagine the widget fails to load or instantly hits an error screen, for example)

@toger5 toger5 Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting.
I played around with it. Those are my observations:

  • if you provide sth that does not speak widget api properly (random / broken url/ no network) you will not get contentLoaded -> infinite loading spinnger
    • we currenlty do NOT have a timeout in EW. Just in the widget api. There we log: Widget specified waitForIframeLoad=false but timed out waiting for contentLoaded event
    • Before this change: we could press the x button and it would kill the widget. Or we switch room and the non sticky widget would get killed.
    • With this change it is sticky already. We will still see the "widget" (in loading state) when switching rooms. with the back button we can get into fullscreen and then press the x button (the x will be there until we are connected to the call, its not based on the widget being sticky) the x button will take care of unsticking the widget.
  • if a call crashes during load we can do the same work around as above.

TLDR:

  • this is not an issue -> workaround: back button -> x button
  • We dont have any EW timeout for widget loading to autoclose. User has to take action. This is an independent issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The designs technically ask for there to be no header on the PiP when viewing the timeline of the matching DM room. How do you want to handle that?

Screenshot From 2026-07-02 16-07-05

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the back button in the header is required to close broken calls.
The white background will become a follow up PR.
We might also want a voice call to be fullscreen:

  • switch to video
  • go to EC settings

With the back button in the header we need the space anyways and i think it might make sense to add the name there in that case.

What does design/product think about this?
@americanrefugee @daniellekirkwood

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants