Skip to content

Commit

Permalink
Improve the layouts on small mobile calls
Browse files Browse the repository at this point in the history
Due to an oversight of mine, 2440037 actually removed the ability to see the one-on-one layout on mobile. This restores mobile one-on-one calls to working order and also avoids showing the spotlight tile unless there are more than a few participants.
  • Loading branch information
robintown committed Jul 25, 2024
1 parent 5c9d716 commit feac0fd
Showing 1 changed file with 79 additions and 55 deletions.
134 changes: 79 additions & 55 deletions src/state/CallViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
shareReplay,
skip,
startWith,
switchAll,
switchMap,
throttleTime,
timer,
Expand Down Expand Up @@ -75,6 +76,10 @@ import { duplicateTiles } from "../settings/settings";
// list again
const POST_FOCUS_PARTICIPANT_UPDATE_DELAY_MS = 3000;

// This is the number of participants that we think constitutes a "small" call
// on mobile. No spotlight tile should be shown below this threshold.
const smallMobileCallThreshold = 3;

export interface GridLayout {
type: "grid";
spotlight?: MediaViewModel[];
Expand Down Expand Up @@ -571,75 +576,96 @@ export class CallViewModel extends ViewModel {
this.gridModeUserSelection.next(value);
}

private readonly oneOnOne: Observable<boolean> = combineLatest(
[this.grid, this.screenShares],
(grid, screenShares) =>
grid.length == 2 &&
// There might not be a remote tile if only the local user is in the call
// and they're using the duplicate tiles option
grid.some((vm) => !vm.local) &&
screenShares.length === 0,
);

private readonly gridLayout: Observable<Layout> = combineLatest(
[this.grid, this.spotlight],
(grid, spotlight) => ({
type: "grid",
spotlight: spotlight.some((vm) => vm instanceof ScreenShareViewModel)
? spotlight
: undefined,
grid,
}),
);

private readonly spotlightLandscapeLayout: Observable<Layout> = combineLatest(
[this.grid, this.spotlight],
(grid, spotlight) => ({ type: "spotlight-landscape", spotlight, grid }),
);

private readonly spotlightPortraitLayout: Observable<Layout> = combineLatest(
[this.grid, this.spotlight],
(grid, spotlight) => ({ type: "spotlight-portrait", spotlight, grid }),
);

private readonly spotlightExpandedLayout: Observable<Layout> = combineLatest(
[this.spotlight, this.pip],
(spotlight, pip) => ({
type: "spotlight-expanded",
spotlight,
pip: pip ?? undefined,
}),
);

private readonly oneOnOneLayout: Observable<Layout> = this.grid.pipe(
map((grid) => ({
type: "one-on-one",
local: grid.find((vm) => vm.local) as LocalUserMediaViewModel,
remote: grid.find((vm) => !vm.local) as RemoteUserMediaViewModel,
})),
);

private readonly pipLayout: Observable<Layout> = this.spotlight.pipe(
map((spotlight): Layout => ({ type: "pip", spotlight })),
);

public readonly layout: Observable<Layout> = this.windowMode.pipe(
switchMap((windowMode) => {
const spotlightLandscapeLayout = combineLatest(
[this.grid, this.spotlight],
(grid, spotlight): Layout => ({
type: "spotlight-landscape",
spotlight,
grid,
}),
);
const spotlightExpandedLayout = combineLatest(
[this.spotlight, this.pip],
(spotlight, pip): Layout => ({
type: "spotlight-expanded",
spotlight,
pip: pip ?? undefined,
}),
);

switch (windowMode) {
case "normal":
return this.gridMode.pipe(
switchMap((gridMode) => {
switch (gridMode) {
case "grid":
return combineLatest(
[this.grid, this.spotlight, this.screenShares],
(grid, spotlight, screenShares): Layout =>
grid.length == 2 &&
// There might not be a remote tile if only the local user
// is in the call and they're using the duplicate tiles
// option
grid.some((vm) => !vm.local) &&
screenShares.length === 0
? {
type: "one-on-one",
local: grid.find(
(vm) => vm.local,
) as LocalUserMediaViewModel,
remote: grid.find(
(vm) => !vm.local,
) as RemoteUserMediaViewModel,
}
: {
type: "grid",
spotlight:
screenShares.length > 0 ? spotlight : undefined,
grid,
},
return this.oneOnOne.pipe(
switchMap((oneOnOne) =>
oneOnOne ? this.oneOnOneLayout : this.gridLayout,
),
);
case "spotlight":
return this.spotlightExpanded.pipe(
switchMap((expanded) =>
expanded
? spotlightExpandedLayout
: spotlightLandscapeLayout,
? this.spotlightExpandedLayout
: this.spotlightLandscapeLayout,
),
);
}
}),
);
case "narrow":
return combineLatest(
[this.grid, this.spotlight],
(grid, spotlight): Layout => ({
type: "spotlight-portrait",
spotlight,
grid,
}),
return this.oneOnOne.pipe(
switchMap((oneOnOne) =>
oneOnOne
? this.oneOnOneLayout
: combineLatest(
[this.grid, this.spotlight],
(grid, spotlight) =>
grid.length > smallMobileCallThreshold ||
spotlight.some((vm) => vm instanceof ScreenShareViewModel)
? this.spotlightPortraitLayout
: this.gridLayout,
).pipe(switchAll()),
),
);
case "flat":
return this.gridMode.pipe(
Expand All @@ -648,16 +674,14 @@ export class CallViewModel extends ViewModel {
case "grid":
// Yes, grid mode actually gets you a "spotlight" layout in
// this window mode.
return spotlightLandscapeLayout;
return this.spotlightLandscapeLayout;
case "spotlight":
return spotlightExpandedLayout;
return this.spotlightExpandedLayout;
}
}),
);
case "pip":
return this.spotlight.pipe(
map((spotlight): Layout => ({ type: "pip", spotlight })),
);
return this.pipLayout;
}
}),
shareReplay(1),
Expand Down

0 comments on commit feac0fd

Please sign in to comment.