Skip to content

Create a proper one-on-one call layout for portrait screens#3916

Merged
robintown merged 4 commits into
livekitfrom
one-on-one-portrait
May 18, 2026
Merged

Create a proper one-on-one call layout for portrait screens#3916
robintown merged 4 commits into
livekitfrom
one-on-one-portrait

Conversation

@robintown
Copy link
Copy Markdown
Member

@robintown robintown commented Apr 23, 2026

Large-ish PR adding a new layout. If needed I can attempt to split some of the refactoring out.

Screencast.From.2026-05-06.17-33-19.webm

Closes https://github.com/element-hq/voip-internal/issues/469
Closes https://github.com/element-hq/voip-internal/issues/528

@robintown robintown added the PR-Improvement Release note category. A PR that improves EC's performance or stability. label Apr 23, 2026
@robintown robintown force-pushed the one-on-one-portrait branch from f35b28b to 1eba3da Compare April 27, 2026 17:29
@robintown robintown force-pushed the one-on-one-portrait branch from 1eba3da to 1c8b20e Compare May 6, 2026 15:41
@robintown robintown force-pushed the one-on-one-portrait branch from 1c8b20e to fe87e03 Compare May 11, 2026 11:32
@robintown robintown force-pushed the one-on-one-portrait branch from fe87e03 to 74da6cb Compare May 11, 2026 15:49
@robintown robintown marked this pull request as ready for review May 11, 2026 15:57
@robintown robintown requested a review from a team as a code owner May 11, 2026 15:57
@robintown robintown requested a review from Half-Shot May 11, 2026 15:57
Comment thread src/grid/CallLayout.ts
Comment on lines -35 to -42
/**
* The alignment of the floating spotlight tile, if present.
*/
spotlightAlignment$: BehaviorSubject<Alignment>;
/**
* The alignment of the small picture-in-picture tile, if present.
*/
pipAlignment$: BehaviorSubject<Alignment>;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These have been moved into the view model, where they belong

Comment on lines -25 to -29
.spotlight {
position: absolute;
inline-size: 404px;
block-size: 233px;
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This was a dead, unused class

Comment thread src/room/InCallView.tsx
useAppBarHidden(!showHeader);

let header: ReactNode = null;
if (showHeader) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removed this if (showHeader) condition to instead show and hide the header via CSS transitions

Comment on lines -1182 to +1225
: // The expanded spotlight layout makes for a better one-on-one
// experience in narrow windows
spotlightExpandedLayoutMedia$,
: of(oneOnOne),
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the branch that the one-on-one portrait layout replaces.

Comment on lines -1207 to -1223
// There is a cyclical dependency here: the layout algorithms want to know
// which tiles are on screen, but to know which tiles are on screen we have to
// first render a layout. To deal with this we assume initially that no tiles
// are visible, and loop the data back into the layouts with a Subject.
const visibleTiles$ = new Subject<number>();
const setVisibleTiles = (value: number): void => visibleTiles$.next(value);

const layoutInternals$ = scope.behavior<LayoutScanState & { layout: Layout }>(
combineLatest([
layoutMedia$,
visibleTiles$.pipe(startWith(0), distinctUntilChanged()),
]).pipe(
scan<
[LayoutMedia, number],
LayoutScanState & { layout: Layout },
LayoutScanState
>(
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

To allow the layoutInternals$ to reference portraitPipSize$, which references showFooter$, I had to move it after showFooter$.

Comment on lines 1270 to +1254
const showSpotlightIndicators$ = scope.behavior<boolean>(
layout$.pipe(map((l) => l.type !== "grid")),
layoutMedia$.pipe(map((l) => l.type !== "grid")),
);

const showSpeakingIndicators$ = scope.behavior<boolean>(
layout$.pipe(
switchMap((l) => {
layoutMedia$.pipe(
map((l) => {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These behaviors are now defined in terms of layoutMedia$ since layout$ was moved to later in the block.

Copy link
Copy Markdown
Member

@BillCarsonFr BillCarsonFr left a comment

Choose a reason for hiding this comment

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

I can't bring much to the review as I have not yet wrapped my head around how the layout works. But I have tested on iOS, I noticed that tap to show/hide the buttons is not working and that the self tile cannot be moved (these are tracked as separate issue, so are known limitations for now).
I noticed a small bug, the ringing... label is under the status bar

@robintown
Copy link
Copy Markdown
Member Author

I noticed a small bug, the ringing... label is under the status bar

Fixed in cfbdbca

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

Labels

PR-Improvement Release note category. A PR that improves EC's performance or stability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants