mirror of
https://github.com/vector-im/element-call.git
synced 2026-02-02 04:05:56 +00:00
Merge pull request #3608 from robintown/initial-layout
Fix the wrong layout being used until window size changes
This commit is contained in:
@@ -502,6 +502,48 @@ describe("CallViewModel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("layout reacts to window size", () => {
|
||||
withTestScheduler(({ behavior, schedule, expectObservable }) => {
|
||||
const windowSizeInputMarbles = "abc";
|
||||
const expectedLayoutMarbles = " abc";
|
||||
withCallViewModel(
|
||||
{
|
||||
remoteParticipants$: constant([aliceParticipant]),
|
||||
rtcMembers$: constant([localRtcMember, aliceRtcMember]),
|
||||
windowSize$: behavior(windowSizeInputMarbles, {
|
||||
a: { width: 300, height: 600 }, // Start very narrow, like a phone
|
||||
b: { width: 1000, height: 800 }, // Go to normal desktop window size
|
||||
c: { width: 200, height: 180 }, // Go to PiP size
|
||||
}),
|
||||
},
|
||||
(vm) => {
|
||||
expectObservable(summarizeLayout$(vm.layout$)).toBe(
|
||||
expectedLayoutMarbles,
|
||||
{
|
||||
a: {
|
||||
// This is the expected one-on-one layout for a narrow window
|
||||
type: "spotlight-expanded",
|
||||
spotlight: [`${aliceId}:0`],
|
||||
pip: `${localId}:0`,
|
||||
},
|
||||
b: {
|
||||
// In a larger window, expect the normal one-on-one layout
|
||||
type: "one-on-one",
|
||||
local: `${localId}:0`,
|
||||
remote: `${aliceId}:0`,
|
||||
},
|
||||
c: {
|
||||
// In a PiP-sized window, we of course expect a PiP layout
|
||||
type: "pip",
|
||||
spotlight: [`${aliceId}:0`],
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("spotlight speakers swap places", () => {
|
||||
withTestScheduler(({ behavior, schedule, expectObservable }) => {
|
||||
// Go immediately into spotlight mode for the test
|
||||
|
||||
@@ -147,6 +147,8 @@ export interface CallViewModelOptions {
|
||||
livekitRoomFactory?: (options?: RoomOptions) => LivekitRoom;
|
||||
/** Optional behavior overriding the local connection state, mainly for testing purposes. */
|
||||
connectionState$?: Behavior<ConnectionState>;
|
||||
/** Optional behavior overriding the computed window size, mainly for testing purposes. */
|
||||
windowSize$?: Behavior<{ width: number; height: number }>;
|
||||
}
|
||||
|
||||
// Do not play any sounds if the participant count has exceeded this
|
||||
@@ -952,11 +954,19 @@ export function createCallViewModel$(
|
||||
|
||||
const pipEnabled$ = scope.behavior(setPipEnabled$, false);
|
||||
|
||||
const windowSize$ =
|
||||
options.windowSize$ ??
|
||||
scope.behavior<{ width: number; height: number }>(
|
||||
fromEvent(window, "resize").pipe(
|
||||
startWith(null),
|
||||
map(() => ({ width: window.innerWidth, height: window.innerHeight })),
|
||||
),
|
||||
);
|
||||
|
||||
// A guess at what the window's mode should be based on its size and shape.
|
||||
const naturalWindowMode$ = scope.behavior<WindowMode>(
|
||||
fromEvent(window, "resize").pipe(
|
||||
map(() => {
|
||||
const height = window.innerHeight;
|
||||
const width = window.innerWidth;
|
||||
windowSize$.pipe(
|
||||
map(({ width, height }) => {
|
||||
if (height <= 400 && width <= 340) return "pip";
|
||||
// Our layouts for flat windows are better at adapting to a small width
|
||||
// than our layouts for narrow windows are at adapting to a small height,
|
||||
@@ -966,7 +976,6 @@ export function createCallViewModel$(
|
||||
return "normal";
|
||||
}),
|
||||
),
|
||||
"normal",
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,7 @@ export interface CallViewModelInputs {
|
||||
speaking: Map<Participant, Observable<boolean>>;
|
||||
mediaDevices: MediaDevices;
|
||||
initialSyncState: SyncState;
|
||||
windowSize$: Behavior<{ width: number; height: number }>;
|
||||
}
|
||||
|
||||
const localParticipant = mockLocalParticipant({ identity: "" });
|
||||
@@ -89,6 +90,7 @@ export function withCallViewModel(
|
||||
speaking = new Map(),
|
||||
mediaDevices = mockMediaDevices({}),
|
||||
initialSyncState = SyncState.Syncing,
|
||||
windowSize$ = constant({ width: 1000, height: 800 }),
|
||||
}: Partial<CallViewModelInputs> = {},
|
||||
continuation: (
|
||||
vm: CallViewModel,
|
||||
@@ -173,6 +175,7 @@ export function withCallViewModel(
|
||||
setE2EEEnabled: async () => Promise.resolve(),
|
||||
}),
|
||||
connectionState$,
|
||||
windowSize$,
|
||||
},
|
||||
raisedHands$,
|
||||
reactions$,
|
||||
|
||||
Reference in New Issue
Block a user