diff --git a/src/grid/GridLayout.tsx b/src/grid/GridLayout.tsx index 51fbc6ea..cf46e8b4 100644 --- a/src/grid/GridLayout.tsx +++ b/src/grid/GridLayout.tsx @@ -14,7 +14,7 @@ import { import { distinctUntilChanged } from "rxjs"; import { useObservableEagerState } from "observable-hooks"; -import { type GridLayout as GridLayoutModel } from "../state/CallViewModel"; +import { type GridLayout as GridLayoutModel } from "../state/layout-types.ts"; import styles from "./GridLayout.module.css"; import { useInitial } from "../useInitial"; import { type CallLayout, arrangeTiles } from "./CallLayout"; diff --git a/src/grid/OneOnOneLayout.tsx b/src/grid/OneOnOneLayout.tsx index 675e4d0a..6c5ae69f 100644 --- a/src/grid/OneOnOneLayout.tsx +++ b/src/grid/OneOnOneLayout.tsx @@ -9,7 +9,7 @@ import { type ReactNode, useCallback, useMemo } from "react"; import { useObservableEagerState } from "observable-hooks"; import classNames from "classnames"; -import { type OneOnOneLayout as OneOnOneLayoutModel } from "../state/CallViewModel"; +import { type OneOnOneLayout as OneOnOneLayoutModel } from "../state/layout-types.ts"; import { type CallLayout, arrangeTiles } from "./CallLayout"; import styles from "./OneOnOneLayout.module.css"; import { type DragCallback, useUpdateLayout } from "./Grid"; diff --git a/src/grid/SpotlightExpandedLayout.tsx b/src/grid/SpotlightExpandedLayout.tsx index 9dd2a109..ac47f0d4 100644 --- a/src/grid/SpotlightExpandedLayout.tsx +++ b/src/grid/SpotlightExpandedLayout.tsx @@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details. import { type ReactNode, useCallback } from "react"; -import { type SpotlightExpandedLayout as SpotlightExpandedLayoutModel } from "../state/CallViewModel"; +import { type SpotlightExpandedLayout as SpotlightExpandedLayoutModel } from "../state/layout-types.ts"; import { type CallLayout } from "./CallLayout"; import { type DragCallback, useUpdateLayout } from "./Grid"; import styles from "./SpotlightExpandedLayout.module.css"; diff --git a/src/grid/SpotlightLandscapeLayout.tsx b/src/grid/SpotlightLandscapeLayout.tsx index 96343296..d87be1f1 100644 --- a/src/grid/SpotlightLandscapeLayout.tsx +++ b/src/grid/SpotlightLandscapeLayout.tsx @@ -10,7 +10,7 @@ import { useObservableEagerState } from "observable-hooks"; import classNames from "classnames"; import { type CallLayout } from "./CallLayout"; -import { type SpotlightLandscapeLayout as SpotlightLandscapeLayoutModel } from "../state/CallViewModel"; +import { type SpotlightLandscapeLayout as SpotlightLandscapeLayoutModel } from "../state/layout-types.ts"; import styles from "./SpotlightLandscapeLayout.module.css"; import { useUpdateLayout, useVisibleTiles } from "./Grid"; diff --git a/src/grid/SpotlightPortraitLayout.tsx b/src/grid/SpotlightPortraitLayout.tsx index ad11ed11..a6d1241c 100644 --- a/src/grid/SpotlightPortraitLayout.tsx +++ b/src/grid/SpotlightPortraitLayout.tsx @@ -10,7 +10,7 @@ import { useObservableEagerState } from "observable-hooks"; import classNames from "classnames"; import { type CallLayout, arrangeTiles } from "./CallLayout"; -import { type SpotlightPortraitLayout as SpotlightPortraitLayoutModel } from "../state/CallViewModel"; +import { type SpotlightPortraitLayout as SpotlightPortraitLayoutModel } from "../state/layout-types.ts"; import styles from "./SpotlightPortraitLayout.module.css"; import { useUpdateLayout, useVisibleTiles } from "./Grid"; import { useBehavior } from "../useBehavior"; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index fd631bae..3a27e250 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -59,11 +59,7 @@ import { type MuteStates } from "../state/MuteStates"; import { type MatrixInfo } from "./VideoPreview"; import { InviteButton } from "../button/InviteButton"; import { LayoutToggle } from "./LayoutToggle"; -import { - CallViewModel, - type GridMode, - type Layout, -} from "../state/CallViewModel"; +import { CallViewModel, GridMode } from "../state/CallViewModel"; import { Grid, type TileProps } from "../grid/Grid"; import { useInitial } from "../useInitial"; import { SpotlightTile } from "../tile/SpotlightTile"; @@ -113,6 +109,7 @@ import { useAudioContext } from "../useAudioContext"; import ringtoneMp3 from "../sound/ringtone.mp3?url"; import ringtoneOgg from "../sound/ringtone.ogg?url"; import { useTrackProcessorObservable$ } from "../livekit/TrackProcessorContext.tsx"; +import { Layout } from "../state/layout-types.ts"; const maxTapDurationMs = 400; diff --git a/src/state/CallViewModel.test.ts b/src/state/CallViewModel.test.ts index 055720c8..f03b648d 100644 --- a/src/state/CallViewModel.test.ts +++ b/src/state/CallViewModel.test.ts @@ -46,11 +46,8 @@ import { } from "matrix-js-sdk/lib/matrixrtc"; import { deepCompare } from "matrix-js-sdk/lib/utils"; -import { - CallViewModel, - type CallViewModelOptions, - type Layout, -} from "./CallViewModel"; +import { CallViewModel, type CallViewModelOptions } from "./CallViewModel"; +import { type Layout } from "./layout-types"; import { mockLocalParticipant, mockMatrixRoom, diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index dff8ae56..3cabf697 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -94,10 +94,6 @@ import { } from "../settings/settings"; import { isFirefox } from "../Platform"; import { setPipEnabled$ } from "../controls"; -import { - type GridTileViewModel, - type SpotlightTileViewModel, -} from "./TileViewModel"; import { TileStore } from "./TileStore"; import { gridLikeLayout } from "./GridLikeLayout"; import { spotlightExpandedLayout } from "./SpotlightExpandedLayout"; @@ -133,6 +129,15 @@ import { PublishConnection } from "./PublishConnection.ts"; import { type Async, async$, mapAsync, ready } from "./Async"; import { sharingScreen$, UserMedia } from "./UserMedia.ts"; import { ScreenShare } from "./ScreenShare.ts"; +import { + GridLayoutMedia, + Layout, + LayoutMedia, + OneOnOneLayoutMedia, + SpotlightExpandedLayoutMedia, + SpotlightLandscapeLayoutMedia, + SpotlightPortraitLayoutMedia, +} from "./layout-types.ts"; export interface CallViewModelOptions { encryptionSystem: EncryptionSystem; @@ -157,99 +162,6 @@ const smallMobileCallThreshold = 3; // with the interface const showFooterMs = 4000; -export interface GridLayoutMedia { - type: "grid"; - spotlight?: MediaViewModel[]; - grid: UserMediaViewModel[]; -} - -export interface SpotlightLandscapeLayoutMedia { - type: "spotlight-landscape"; - spotlight: MediaViewModel[]; - grid: UserMediaViewModel[]; -} - -export interface SpotlightPortraitLayoutMedia { - type: "spotlight-portrait"; - spotlight: MediaViewModel[]; - grid: UserMediaViewModel[]; -} - -export interface SpotlightExpandedLayoutMedia { - type: "spotlight-expanded"; - spotlight: MediaViewModel[]; - pip?: UserMediaViewModel; -} - -export interface OneOnOneLayoutMedia { - type: "one-on-one"; - local: UserMediaViewModel; - remote: UserMediaViewModel; -} - -export interface PipLayoutMedia { - type: "pip"; - spotlight: MediaViewModel[]; -} - -export type LayoutMedia = - | GridLayoutMedia - | SpotlightLandscapeLayoutMedia - | SpotlightPortraitLayoutMedia - | SpotlightExpandedLayoutMedia - | OneOnOneLayoutMedia - | PipLayoutMedia; - -export interface GridLayout { - type: "grid"; - spotlight?: SpotlightTileViewModel; - grid: GridTileViewModel[]; - setVisibleTiles: (value: number) => void; -} - -export interface SpotlightLandscapeLayout { - type: "spotlight-landscape"; - spotlight: SpotlightTileViewModel; - grid: GridTileViewModel[]; - setVisibleTiles: (value: number) => void; -} - -export interface SpotlightPortraitLayout { - type: "spotlight-portrait"; - spotlight: SpotlightTileViewModel; - grid: GridTileViewModel[]; - setVisibleTiles: (value: number) => void; -} - -export interface SpotlightExpandedLayout { - type: "spotlight-expanded"; - spotlight: SpotlightTileViewModel; - pip?: GridTileViewModel; -} - -export interface OneOnOneLayout { - type: "one-on-one"; - local: GridTileViewModel; - remote: GridTileViewModel; -} - -export interface PipLayout { - type: "pip"; - spotlight: SpotlightTileViewModel; -} - -/** - * A layout defining the media tiles present on screen and their visual - * arrangement. - */ -export type Layout = - | GridLayout - | SpotlightLandscapeLayout - | SpotlightPortraitLayout - | SpotlightExpandedLayout - | OneOnOneLayout - | PipLayout; - export type GridMode = "grid" | "spotlight"; export type WindowMode = "normal" | "narrow" | "flat" | "pip"; @@ -295,28 +207,6 @@ interface LayoutScanState { type MediaItem = UserMedia | ScreenShare; -function getRoomMemberFromRtcMember( - rtcMember: CallMembership, - room: MatrixRoom, -): { id: string; member: RoomMember | undefined } { - // WARN! This is not exactly the sender but the user defined in the state key. - // This will be available once we change to the new "member as object" format in the MatrixRTC object. - let id = rtcMember.sender + ":" + rtcMember.deviceId; - - if (!rtcMember.sender) { - return { id, member: undefined }; - } - if ( - rtcMember.sender === room.client.getUserId() && - rtcMember.deviceId === room.client.getDeviceId() - ) { - id = "local"; - } - - const member = room.getMember(rtcMember.sender) ?? undefined; - return { id, member }; -} - export class CallViewModel extends ViewModel { private readonly urlParams = getUrlParams(); @@ -2004,3 +1894,25 @@ function getE2eeKeyProvider( return keyProvider; } } + +function getRoomMemberFromRtcMember( + rtcMember: CallMembership, + room: MatrixRoom, +): { id: string; member: RoomMember | undefined } { + // WARN! This is not exactly the sender but the user defined in the state key. + // This will be available once we change to the new "member as object" format in the MatrixRTC object. + let id = rtcMember.sender + ":" + rtcMember.deviceId; + + if (!rtcMember.sender) { + return { id, member: undefined }; + } + if ( + rtcMember.sender === room.client.getUserId() && + rtcMember.deviceId === room.client.getDeviceId() + ) { + id = "local"; + } + + const member = room.getMember(rtcMember.sender) ?? undefined; + return { id, member }; +} diff --git a/src/state/GridLikeLayout.ts b/src/state/GridLikeLayout.ts index 0740f26c..0d130834 100644 --- a/src/state/GridLikeLayout.ts +++ b/src/state/GridLikeLayout.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type Layout, type LayoutMedia } from "./CallViewModel"; +import { type Layout, type LayoutMedia } from "./layout-types.ts"; import { type TileStore } from "./TileStore"; export type GridLikeLayoutType = diff --git a/src/state/OneOnOneLayout.ts b/src/state/OneOnOneLayout.ts index b8c7b8fb..10268945 100644 --- a/src/state/OneOnOneLayout.ts +++ b/src/state/OneOnOneLayout.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type OneOnOneLayout, type OneOnOneLayoutMedia } from "./CallViewModel"; +import { type OneOnOneLayout, type OneOnOneLayoutMedia } from "./layout-types"; import { type TileStore } from "./TileStore"; /** diff --git a/src/state/PipLayout.ts b/src/state/PipLayout.ts index ab066410..56e9aeb2 100644 --- a/src/state/PipLayout.ts +++ b/src/state/PipLayout.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type PipLayout, type PipLayoutMedia } from "./CallViewModel"; +import { type PipLayout, type PipLayoutMedia } from "./layout-types.ts"; import { type TileStore } from "./TileStore"; /** diff --git a/src/state/SpotlightExpandedLayout.ts b/src/state/SpotlightExpandedLayout.ts index 4baba0a1..8ccc49dd 100644 --- a/src/state/SpotlightExpandedLayout.ts +++ b/src/state/SpotlightExpandedLayout.ts @@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details. import { type SpotlightExpandedLayout, type SpotlightExpandedLayoutMedia, -} from "./CallViewModel"; +} from "./layout-types"; import { type TileStore } from "./TileStore"; /** diff --git a/src/state/layout-types.ts b/src/state/layout-types.ts new file mode 100644 index 00000000..f28ada46 --- /dev/null +++ b/src/state/layout-types.ts @@ -0,0 +1,102 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { GridTileViewModel, SpotlightTileViewModel } from "./TileViewModel.ts"; +import { MediaViewModel, UserMediaViewModel } from "./MediaViewModel.ts"; + +export interface GridLayoutMedia { + type: "grid"; + spotlight?: MediaViewModel[]; + grid: UserMediaViewModel[]; +} + +export interface SpotlightLandscapeLayoutMedia { + type: "spotlight-landscape"; + spotlight: MediaViewModel[]; + grid: UserMediaViewModel[]; +} + +export interface SpotlightPortraitLayoutMedia { + type: "spotlight-portrait"; + spotlight: MediaViewModel[]; + grid: UserMediaViewModel[]; +} + +export interface SpotlightExpandedLayoutMedia { + type: "spotlight-expanded"; + spotlight: MediaViewModel[]; + pip?: UserMediaViewModel; +} + +export interface OneOnOneLayoutMedia { + type: "one-on-one"; + local: UserMediaViewModel; + remote: UserMediaViewModel; +} + +export interface PipLayoutMedia { + type: "pip"; + spotlight: MediaViewModel[]; +} + +export type LayoutMedia = + | GridLayoutMedia + | SpotlightLandscapeLayoutMedia + | SpotlightPortraitLayoutMedia + | SpotlightExpandedLayoutMedia + | OneOnOneLayoutMedia + | PipLayoutMedia; + +export interface GridLayout { + type: "grid"; + spotlight?: SpotlightTileViewModel; + grid: GridTileViewModel[]; + setVisibleTiles: (value: number) => void; +} + +export interface SpotlightLandscapeLayout { + type: "spotlight-landscape"; + spotlight: SpotlightTileViewModel; + grid: GridTileViewModel[]; + setVisibleTiles: (value: number) => void; +} + +export interface SpotlightPortraitLayout { + type: "spotlight-portrait"; + spotlight: SpotlightTileViewModel; + grid: GridTileViewModel[]; + setVisibleTiles: (value: number) => void; +} + +export interface SpotlightExpandedLayout { + type: "spotlight-expanded"; + spotlight: SpotlightTileViewModel; + pip?: GridTileViewModel; +} + +export interface OneOnOneLayout { + type: "one-on-one"; + local: GridTileViewModel; + remote: GridTileViewModel; +} + +export interface PipLayout { + type: "pip"; + spotlight: SpotlightTileViewModel; +} + +/** + * A layout defining the media tiles present on screen and their visual + * arrangement. + */ +export type Layout = + | GridLayout + | SpotlightLandscapeLayout + | SpotlightPortraitLayout + | SpotlightExpandedLayout + | OneOnOneLayout + | PipLayout;