mirror of
https://github.com/element-hq/element-call.git
synced 2026-11-12 11:48:08 +00:00
221 lines
5.7 KiB
TypeScript
221 lines
5.7 KiB
TypeScript
/*
|
|
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 { type LocalUserMediaViewModel } from "./media/LocalUserMediaViewModel.ts";
|
|
import { type MediaViewModel } from "./media/MediaViewModel.ts";
|
|
import { type RingingMediaViewModel } from "./media/RingingMediaViewModel.ts";
|
|
import { type UserMediaViewModel } from "./media/UserMediaViewModel.ts";
|
|
import {
|
|
type GridTileViewModel,
|
|
type SpotlightTileViewModel,
|
|
} from "./TileViewModel.ts";
|
|
import { type Behavior } from "./Behavior.ts";
|
|
import { shallowEquals as arrayShallowEquals } from "../utils/array.ts";
|
|
import { type FloatingTile } from "./FloatingTile.ts";
|
|
|
|
export interface GridLayoutMedia {
|
|
type: "grid";
|
|
edgeToEdge: false;
|
|
spotlight?: MediaViewModel[];
|
|
grid: UserMediaViewModel[];
|
|
}
|
|
|
|
export interface SpotlightLandscapeLayoutMedia {
|
|
type: "spotlight-landscape";
|
|
edgeToEdge: boolean;
|
|
spotlight: MediaViewModel[];
|
|
grid: UserMediaViewModel[];
|
|
}
|
|
|
|
export interface SpotlightPortraitLayoutMedia {
|
|
type: "spotlight-portrait";
|
|
edgeToEdge: false;
|
|
spotlight: MediaViewModel[];
|
|
grid: UserMediaViewModel[];
|
|
}
|
|
|
|
export interface SpotlightExpandedLayoutMedia {
|
|
type: "spotlight-expanded";
|
|
edgeToEdge: boolean;
|
|
spotlight: MediaViewModel[];
|
|
pip?: UserMediaViewModel;
|
|
}
|
|
|
|
export interface OneOnOneDesktopLayoutMedia {
|
|
type: "one-on-one-desktop";
|
|
edgeToEdge: false;
|
|
spotlight: UserMediaViewModel;
|
|
pip: LocalUserMediaViewModel | RingingMediaViewModel;
|
|
}
|
|
|
|
export interface OneOnOneMobileLayoutMedia {
|
|
type: "one-on-one-mobile";
|
|
edgeToEdge: true;
|
|
spotlight: UserMediaViewModel | RingingMediaViewModel;
|
|
pip?: LocalUserMediaViewModel;
|
|
}
|
|
|
|
export interface PipLayoutMedia {
|
|
type: "pip";
|
|
edgeToEdge: boolean;
|
|
spotlight: MediaViewModel[];
|
|
}
|
|
|
|
export type LayoutMedia =
|
|
| GridLayoutMedia
|
|
| SpotlightLandscapeLayoutMedia
|
|
| SpotlightPortraitLayoutMedia
|
|
| SpotlightExpandedLayoutMedia
|
|
| OneOnOneDesktopLayoutMedia
|
|
| OneOnOneMobileLayoutMedia
|
|
| PipLayoutMedia;
|
|
|
|
export interface Alignment {
|
|
inline: "start" | "end";
|
|
block: "start" | "end";
|
|
}
|
|
|
|
export interface Drag {
|
|
/**
|
|
* The X offset of the dragged tile relative to its container.
|
|
*/
|
|
x: number;
|
|
/**
|
|
* The Y offset of the dragged tile relative to its container.
|
|
*/
|
|
y: number;
|
|
/**
|
|
* The X coordinate of the dragged tile, as a scalar of the container's width.
|
|
*/
|
|
xRatio: number;
|
|
/**
|
|
* The Y coordinate of the dragged tile, as a scalar of the container's height.
|
|
*/
|
|
yRatio: number;
|
|
/**
|
|
* Whether this event ends the drag gesture.
|
|
*/
|
|
endOfGesture: boolean;
|
|
}
|
|
|
|
export type DragCallback = (drag: Drag) => void;
|
|
|
|
export interface GridLayout {
|
|
type: "grid";
|
|
spotlight?: FloatingTile & { vm: SpotlightTileViewModel };
|
|
grid: GridTileViewModel[];
|
|
setVisibleTiles: (value: number) => void;
|
|
}
|
|
|
|
export interface SpotlightLandscapeLayout {
|
|
type: "spotlight-landscape";
|
|
spotlight: { vm: SpotlightTileViewModel };
|
|
grid: GridTileViewModel[];
|
|
setVisibleTiles: (value: number) => void;
|
|
}
|
|
|
|
export interface SpotlightPortraitLayout {
|
|
type: "spotlight-portrait";
|
|
spotlight: { vm: SpotlightTileViewModel };
|
|
grid: GridTileViewModel[];
|
|
setVisibleTiles: (value: number) => void;
|
|
}
|
|
|
|
export interface SpotlightExpandedLayout {
|
|
type: "spotlight-expanded";
|
|
spotlight: SpotlightTileViewModel;
|
|
pip?: FloatingTile & { vm: GridTileViewModel };
|
|
}
|
|
|
|
export interface OneOnOneDesktopLayout {
|
|
type: "one-on-one-desktop";
|
|
spotlight: GridTileViewModel;
|
|
pip: FloatingTile & { vm: GridTileViewModel };
|
|
}
|
|
|
|
export interface OneOnOneMobileLayout {
|
|
type: "one-on-one-mobile";
|
|
spotlight: SpotlightTileViewModel;
|
|
pip?: FloatingTile & { vm: GridTileViewModel; size$: Behavior<"sm" | "lg"> };
|
|
}
|
|
|
|
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
|
|
| OneOnOneDesktopLayout
|
|
| OneOnOneMobileLayout
|
|
| PipLayout;
|
|
|
|
/**
|
|
* Tests whether the top-level properties of object `a` are equal to those of
|
|
* object `b` by the given comparator.
|
|
*/
|
|
function equals(
|
|
a: object,
|
|
b: object,
|
|
comparator: (aValue: unknown, bValue: unknown) => boolean = (
|
|
aValue,
|
|
bValue,
|
|
) => aValue === bValue,
|
|
): boolean {
|
|
// If a and b have the same number of keys and every key in a is also in b,
|
|
// then they have the same keys.
|
|
const aKeys = Object.keys(a);
|
|
const bKeys = Object.keys(b);
|
|
if (aKeys.length !== bKeys.length) return false;
|
|
|
|
for (const key of aKeys) {
|
|
if (!(key in b)) return false;
|
|
|
|
// Now check that they have the same values.
|
|
const aValue: unknown = (a as any)[key];
|
|
const bValue: unknown = (b as any)[key];
|
|
if (!comparator(aValue, bValue)) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Tests whether the top-level properties, tile wrapper properties, and array
|
|
* elements of layout `a` are equal to those of layout `b`. Useful for deduping
|
|
* redundant layout updates.
|
|
*/
|
|
export function layoutShallowEquals(a: Layout, b: Layout): boolean {
|
|
return equals(a, b, (aValue, bValue) => {
|
|
// Special case for tile wrapper objects so we can detect when the
|
|
// spotlight or PiP tile is essentially the same
|
|
if (
|
|
typeof aValue === "object" &&
|
|
aValue !== null &&
|
|
"vm" in aValue &&
|
|
typeof bValue === "object" &&
|
|
bValue !== null &&
|
|
"vm" in bValue
|
|
)
|
|
return equals(aValue, bValue);
|
|
|
|
// Special case for arrays so we can detect when the grid tile arrays are
|
|
// essentially the same
|
|
if (Array.isArray(aValue) && Array.isArray(bValue))
|
|
return arrayShallowEquals(aValue, bValue);
|
|
|
|
return aValue === bValue;
|
|
});
|
|
}
|