From cfea4bd50c1e9fc6e955209af8cab85a30352c20 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 14 Nov 2024 12:08:17 +0000 Subject: [PATCH] Always deal with MediaItem at the LayoutMedia layer That way we only convert to the view model when we do LayoutMedia => Layout --- src/state/CallViewModel.ts | 71 ++++++++++++++-------------- src/state/GridLikeLayout.ts | 4 +- src/state/OneOnOneLayout.ts | 8 ++-- src/state/PipLayout.ts | 5 +- src/state/SpotlightExpandedLayout.ts | 7 ++- 5 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index b344716ee..bfd2aba59 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -91,37 +91,37 @@ const showFooterMs = 4000; export interface GridLayoutMedia { type: "grid"; - spotlight?: MediaViewModel[]; - grid: UserMediaViewModel[]; + spotlight?: MediaItem[]; + grid: UserMedia[]; } export interface SpotlightLandscapeLayoutMedia { type: "spotlight-landscape"; - spotlight: MediaViewModel[]; - grid: UserMediaViewModel[]; + spotlight: MediaItem[]; + grid: UserMedia[]; } export interface SpotlightPortraitLayoutMedia { type: "spotlight-portrait"; - spotlight: MediaViewModel[]; - grid: UserMediaViewModel[]; + spotlight: MediaItem[]; + grid: UserMedia[]; } export interface SpotlightExpandedLayoutMedia { type: "spotlight-expanded"; - spotlight: MediaViewModel[]; - pip?: UserMediaViewModel; + spotlight: MediaItem[]; + pip?: UserMedia; } export interface OneOnOneLayoutMedia { type: "one-on-one"; - local: UserMediaViewModel; - remote: UserMediaViewModel; + local: UserMedia; + remote: UserMedia; } export interface PipLayoutMedia { type: "pip"; - spotlight: MediaViewModel[]; + spotlight: MediaItem[]; } export type LayoutMedia = @@ -478,10 +478,9 @@ export class CallViewModel extends ViewModel { ), ); - private readonly localUserMedia: Observable = - this.mediaItems.pipe( - map((ms) => ms.find((m) => m.vm.local)!.vm as LocalUserMediaViewModel), - ); + private readonly localUserMedia: Observable = this.userMedia.pipe( + map((ms) => ms.find((m) => m.vm.local) as UserMedia), + ); private readonly screenShares: Observable = this.mediaItems.pipe( @@ -491,7 +490,7 @@ export class CallViewModel extends ViewModel { this.scope.state(), ); - private readonly spotlightSpeaker: Observable = + private readonly spotlightSpeaker: Observable = this.userMedia.pipe( switchMap((mediaItems) => mediaItems.length === 0 @@ -523,11 +522,10 @@ export class CallViewModel extends ViewModel { }, null, ), - map((speaker) => speaker.vm), this.scope.state(), ); - private readonly grid: Observable = this.userMedia.pipe( + private readonly grid: Observable = this.userMedia.pipe( switchMap((mediaItems) => { const bins = mediaItems.map((m) => combineLatest( @@ -558,27 +556,27 @@ export class CallViewModel extends ViewModel { return bins.length === 0 ? of([]) : combineLatest(bins, (...bins) => - bins.sort(([, bin1], [, bin2]) => bin1 - bin2).map(([m]) => m.vm), + bins.sort(([, bin1], [, bin2]) => bin1 - bin2).map(([m]) => m), ); }), ); private readonly spotlightAndPip: Observable< - [Observable, Observable] + [Observable, Observable] > = this.screenShares.pipe( map((screenShares) => screenShares.length > 0 - ? ([of(screenShares.map((m) => m.vm)), this.spotlightSpeaker] as const) + ? ([of(screenShares.map((m) => m)), this.spotlightSpeaker] as const) : ([ this.spotlightSpeaker.pipe(map((speaker) => [speaker!])), this.spotlightSpeaker.pipe( switchMap((speaker) => - speaker.local + speaker.vm.local ? of(null) : this.localUserMedia.pipe( - switchMap((vm) => - vm.alwaysShow.pipe( - map((alwaysShow) => (alwaysShow ? vm : null)), + switchMap((lm) => + (lm.vm as LocalUserMediaViewModel).alwaysShow.pipe( + map((alwaysShow) => (alwaysShow ? lm : null)), ), ), ), @@ -588,7 +586,7 @@ export class CallViewModel extends ViewModel { ), ); - private readonly spotlight: Observable = + private readonly spotlight: Observable = this.spotlightAndPip.pipe( switchMap(([spotlight]) => spotlight), this.scope.state(), @@ -597,12 +595,14 @@ export class CallViewModel extends ViewModel { private readonly hasRemoteScreenShares: Observable = this.spotlight.pipe( map((spotlight) => - spotlight.some((vm) => !vm.local && vm instanceof ScreenShareViewModel), + spotlight.some( + (m) => !m.vm.local && m.vm instanceof ScreenShareViewModel, + ), ), distinctUntilChanged(), ); - private readonly pip: Observable = + private readonly pip: Observable = this.spotlightAndPip.pipe(switchMap(([, pip]) => pip)); private readonly pipEnabled: Observable = setPipEnabled.pipe( @@ -676,7 +676,7 @@ export class CallViewModel extends ViewModel { [this.grid, this.spotlight], (grid, spotlight) => ({ type: "grid", - spotlight: spotlight.some((vm) => vm instanceof ScreenShareViewModel) + spotlight: spotlight.some((m) => m.vm instanceof ScreenShareViewModel) ? spotlight : undefined, grid, @@ -708,15 +708,16 @@ export class CallViewModel extends ViewModel { this.mediaItems.pipe( map((mediaItems) => { if (mediaItems.length !== 2) return null; - const local = mediaItems.find((vm) => vm.vm.local)! - .vm as LocalUserMediaViewModel; - const remote = mediaItems.find((vm) => !vm.vm.local)?.vm as - | RemoteUserMediaViewModel - | undefined; + const local = mediaItems.find( + (m) => m instanceof UserMedia && m.vm.local, + ) as UserMedia | undefined; + const remote = mediaItems.find( + (m) => m instanceof UserMedia && !m.vm.local, + ) as UserMedia | undefined; // There might not be a remote tile if there are screen shares, or if // only the local user is in the call and they're using the duplicate // tiles option - if (remote === undefined) return null; + if (!local || !remote) return null; return { type: "one-on-one", local, remote }; }), diff --git a/src/state/GridLikeLayout.ts b/src/state/GridLikeLayout.ts index 7fcada952..0d0b13700 100644 --- a/src/state/GridLikeLayout.ts +++ b/src/state/GridLikeLayout.ts @@ -26,10 +26,10 @@ export function gridLikeLayout( const update = prevTiles.from(visibleTiles); if (media.spotlight !== undefined) update.registerSpotlight( - media.spotlight, + media.spotlight.map((m) => m.vm), media.type === "spotlight-portrait", ); - for (const mediaVm of media.grid) update.registerGridTile(mediaVm); + for (const mediaVm of media.grid) update.registerGridTile(mediaVm.vm); const tiles = update.build(); return [ diff --git a/src/state/OneOnOneLayout.ts b/src/state/OneOnOneLayout.ts index 29ed9fc08..0e0a162d9 100644 --- a/src/state/OneOnOneLayout.ts +++ b/src/state/OneOnOneLayout.ts @@ -18,14 +18,14 @@ export function oneOnOneLayout( prevTiles: TileStore, ): [OneOnOneLayout, TileStore] { const update = prevTiles.from(visibleTiles); - update.registerGridTile(media.local); - update.registerGridTile(media.remote); + update.registerGridTile(media.local.vm); + update.registerGridTile(media.remote.vm); const tiles = update.build(); return [ { type: media.type, - local: tiles.gridTilesByMedia.get(media.local)!, - remote: tiles.gridTilesByMedia.get(media.remote)!, + local: tiles.gridTilesByMedia.get(media.local.vm)!, + remote: tiles.gridTilesByMedia.get(media.remote.vm)!, }, tiles, ]; diff --git a/src/state/PipLayout.ts b/src/state/PipLayout.ts index 35edeefe7..3ff8be86b 100644 --- a/src/state/PipLayout.ts +++ b/src/state/PipLayout.ts @@ -18,7 +18,10 @@ export function pipLayout( prevTiles: TileStore, ): [PipLayout, TileStore] { const update = prevTiles.from(visibleTiles); - update.registerSpotlight(media.spotlight, true); + update.registerSpotlight( + media.spotlight.map((m) => m.vm), + true, + ); const tiles = update.build(); return [ { diff --git a/src/state/SpotlightExpandedLayout.ts b/src/state/SpotlightExpandedLayout.ts index 83c5a95e3..131e65f12 100644 --- a/src/state/SpotlightExpandedLayout.ts +++ b/src/state/SpotlightExpandedLayout.ts @@ -21,8 +21,11 @@ export function spotlightExpandedLayout( prevTiles: TileStore, ): [SpotlightExpandedLayout, TileStore] { const update = prevTiles.from(visibleTiles); - update.registerSpotlight(media.spotlight, true); - if (media.pip !== undefined) update.registerGridTile(media.pip); + update.registerSpotlight( + media.spotlight.map((m) => m.vm), + true, + ); + if (media.pip !== undefined) update.registerGridTile(media.pip.vm); const tiles = update.build(); return [