Create a proper one-on-one call layout for portrait screens#3916
Conversation
f35b28b to
1eba3da
Compare
1eba3da to
1c8b20e
Compare
1c8b20e to
fe87e03
Compare
fe87e03 to
74da6cb
Compare
74da6cb to
b562a0f
Compare
| /** | ||
| * 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>; |
There was a problem hiding this comment.
These have been moved into the view model, where they belong
| .spotlight { | ||
| position: absolute; | ||
| inline-size: 404px; | ||
| block-size: 233px; | ||
| } |
There was a problem hiding this comment.
This was a dead, unused class
| useAppBarHidden(!showHeader); | ||
|
|
||
| let header: ReactNode = null; | ||
| if (showHeader) { |
There was a problem hiding this comment.
Removed this if (showHeader) condition to instead show and hide the header via CSS transitions
| : // The expanded spotlight layout makes for a better one-on-one | ||
| // experience in narrow windows | ||
| spotlightExpandedLayoutMedia$, | ||
| : of(oneOnOne), |
There was a problem hiding this comment.
This is the branch that the one-on-one portrait layout replaces.
| // 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 | ||
| >( |
There was a problem hiding this comment.
To allow the layoutInternals$ to reference portraitPipSize$, which references showFooter$, I had to move it after showFooter$.
| 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) => { |
There was a problem hiding this comment.
These behaviors are now defined in terms of layoutMedia$ since layout$ was moved to later in the block.
BillCarsonFr
left a comment
There was a problem hiding this comment.
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
Fixed in cfbdbca |
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