Merge remote-tracking branch 'origin/livekit' into hs/emoji-reactions

This commit is contained in:
Half-Shot
2024-11-07 09:10:42 +00:00
60 changed files with 2437 additions and 1097 deletions

22
src/utils/iter.test.ts Normal file
View File

@@ -0,0 +1,22 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { test, expect } from "vitest";
import { fillGaps } from "./iter";
test("fillGaps filters out gaps", () => {
expect([
...fillGaps([1, undefined, undefined, undefined, 3], [2]),
]).toStrictEqual([1, 2, 3]);
});
test("fillGaps adds extra filler elements to the end", () => {
expect([
...fillGaps([1, undefined, 3, undefined], [2, 4, 5, 6]),
]).toStrictEqual([1, 2, 3, 4, 5, 6]);
});

36
src/utils/iter.ts Normal file
View File

@@ -0,0 +1,36 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
/**
* Fills in the 'undefined' gaps in a collection by drawing items from a second
* collection, or simply filtering out the gap if no items are left. If filler
* items remain at the end, they will be appended to the resulting collection.
*/
export function fillGaps<A>(
gappy: Iterable<A | undefined>,
filler: Iterable<A>,
): Iterable<A> {
return {
[Symbol.iterator](): Iterator<A> {
const gappyIter = gappy[Symbol.iterator]();
const fillerIter = filler[Symbol.iterator]();
return {
next(): IteratorResult<A> {
let gappyItem: IteratorResult<A | undefined>;
do {
gappyItem = gappyIter.next();
if (!gappyItem.done && gappyItem.value !== undefined)
return gappyItem as IteratorYieldResult<A>;
const fillerItem = fillerIter.next();
if (!fillerItem.done) return fillerItem;
} while (!gappyItem.done);
return gappyItem;
},
};
},
};
}

View File

@@ -4,7 +4,7 @@ Copyright 2023, 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { map } from "rxjs";
import { map, Observable, of } from "rxjs";
import { RunHelpers, TestScheduler } from "rxjs/testing";
import { expect, vi } from "vitest";
import { RoomMember, Room as MatrixRoom } from "matrix-js-sdk/src/matrix";
@@ -99,8 +99,27 @@ export function mockMatrixRoom(room: Partial<MatrixRoom>): MatrixRoom {
return { ...mockEmitter(), ...room } as Partial<MatrixRoom> as MatrixRoom;
}
export function mockLivekitRoom(room: Partial<LivekitRoom>): LivekitRoom {
return { ...mockEmitter(), ...room } as Partial<LivekitRoom> as LivekitRoom;
export function mockLivekitRoom(
room: Partial<LivekitRoom>,
{
remoteParticipants,
}: { remoteParticipants?: Observable<RemoteParticipant[]> } = {},
): LivekitRoom {
const livekitRoom = {
...mockEmitter(),
...room,
} as Partial<LivekitRoom> as LivekitRoom;
if (remoteParticipants) {
livekitRoom.remoteParticipants = new Map();
remoteParticipants.subscribe((newRemoteParticipants) => {
livekitRoom.remoteParticipants.clear();
newRemoteParticipants.forEach((p) => {
livekitRoom.remoteParticipants.set(p.identity, p);
});
});
}
return livekitRoom;
}
export function mockLocalParticipant(
@@ -119,13 +138,15 @@ export async function withLocalMedia(
member: Partial<RoomMember>,
continuation: (vm: LocalUserMediaViewModel) => void | Promise<void>,
): Promise<void> {
const localParticipant = mockLocalParticipant({});
const vm = new LocalUserMediaViewModel(
"local",
mockMember(member),
mockLocalParticipant({}),
localParticipant,
{
kind: E2eeType.PER_PARTICIPANT,
},
mockLivekitRoom({ localParticipant }),
);
try {
await continuation(vm);
@@ -152,13 +173,15 @@ export async function withRemoteMedia(
participant: Partial<RemoteParticipant>,
continuation: (vm: RemoteUserMediaViewModel) => void | Promise<void>,
): Promise<void> {
const remoteParticipant = mockRemoteParticipant(participant);
const vm = new RemoteUserMediaViewModel(
"remote",
mockMember(member),
mockRemoteParticipant(participant),
remoteParticipant,
{
kind: E2eeType.PER_PARTICIPANT,
},
mockLivekitRoom({}, { remoteParticipants: of([remoteParticipant]) }),
);
try {
await continuation(vm);