Confirm listen to a broadcast while recording (#9831)
This commit is contained in:
parent
91e078d96b
commit
0f7a2ce6df
20 changed files with 213 additions and 29 deletions
|
@ -172,7 +172,7 @@ export class SdkContextClass {
|
||||||
|
|
||||||
public get voiceBroadcastPlaybacksStore(): VoiceBroadcastPlaybacksStore {
|
public get voiceBroadcastPlaybacksStore(): VoiceBroadcastPlaybacksStore {
|
||||||
if (!this._VoiceBroadcastPlaybacksStore) {
|
if (!this._VoiceBroadcastPlaybacksStore) {
|
||||||
this._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore();
|
this._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(this.voiceBroadcastRecordingsStore);
|
||||||
}
|
}
|
||||||
return this._VoiceBroadcastPlaybacksStore;
|
return this._VoiceBroadcastPlaybacksStore;
|
||||||
}
|
}
|
||||||
|
|
|
@ -659,6 +659,10 @@
|
||||||
"Stop live broadcasting?": "Stop live broadcasting?",
|
"Stop live broadcasting?": "Stop live broadcasting?",
|
||||||
"Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.": "Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.",
|
"Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.": "Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.",
|
||||||
"Yes, stop broadcast": "Yes, stop broadcast",
|
"Yes, stop broadcast": "Yes, stop broadcast",
|
||||||
|
"Listen to live broadcast?": "Listen to live broadcast?",
|
||||||
|
"If you start listening to this live broadcast, your current live broadcast recording will be ended.": "If you start listening to this live broadcast, your current live broadcast recording will be ended.",
|
||||||
|
"Yes, end my recording": "Yes, end my recording",
|
||||||
|
"No": "No",
|
||||||
"30s backward": "30s backward",
|
"30s backward": "30s backward",
|
||||||
"30s forward": "30s forward",
|
"30s forward": "30s forward",
|
||||||
"Go live": "Go live",
|
"Go live": "Go live",
|
||||||
|
@ -815,7 +819,6 @@
|
||||||
"Learn more": "Learn more",
|
"Learn more": "Learn more",
|
||||||
"Share anonymous data to help us identify issues. Nothing personal. No third parties. <LearnMoreLink>Learn More</LearnMoreLink>": "Share anonymous data to help us identify issues. Nothing personal. No third parties. <LearnMoreLink>Learn More</LearnMoreLink>",
|
"Share anonymous data to help us identify issues. Nothing personal. No third parties. <LearnMoreLink>Learn More</LearnMoreLink>": "Share anonymous data to help us identify issues. Nothing personal. No third parties. <LearnMoreLink>Learn More</LearnMoreLink>",
|
||||||
"Yes": "Yes",
|
"Yes": "Yes",
|
||||||
"No": "No",
|
|
||||||
"Help improve %(analyticsOwner)s": "Help improve %(analyticsOwner)s",
|
"Help improve %(analyticsOwner)s": "Help improve %(analyticsOwner)s",
|
||||||
"You have unverified sessions": "You have unverified sessions",
|
"You have unverified sessions": "You have unverified sessions",
|
||||||
"Review to ensure your account is safe": "Review to ensure your account is safe",
|
"Review to ensure your account is safe": "Review to ensure your account is safe",
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { defer } from "matrix-js-sdk/src/utils";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import BaseDialog from "../../../components/views/dialogs/BaseDialog";
|
||||||
|
import DialogButtons from "../../../components/views/elements/DialogButtons";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
import Modal from "../../../Modal";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onFinished: (confirmed: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConfirmListenBroadcastStopCurrentDialog: React.FC<Props> = ({ onFinished }) => {
|
||||||
|
return (
|
||||||
|
<BaseDialog title={_t("Listen to live broadcast?")} hasCancel={true} onFinished={onFinished}>
|
||||||
|
<p>
|
||||||
|
{_t(
|
||||||
|
"If you start listening to this live broadcast, " +
|
||||||
|
"your current live broadcast recording will be ended.",
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<DialogButtons
|
||||||
|
onPrimaryButtonClick={() => onFinished(true)}
|
||||||
|
primaryButton={_t("Yes, end my recording")}
|
||||||
|
cancelButton={_t("No")}
|
||||||
|
onCancel={() => onFinished(false)}
|
||||||
|
/>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const showConfirmListenBroadcastStopCurrentDialog = async (): Promise<boolean> => {
|
||||||
|
const { promise, resolve } = defer<boolean>();
|
||||||
|
|
||||||
|
Modal.createDialog(ConfirmListenBroadcastStopCurrentDialog, {
|
||||||
|
onFinished: resolve,
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
|
@ -30,6 +30,7 @@ export * from "./components/atoms/VoiceBroadcastControl";
|
||||||
export * from "./components/atoms/VoiceBroadcastHeader";
|
export * from "./components/atoms/VoiceBroadcastHeader";
|
||||||
export * from "./components/atoms/VoiceBroadcastPlaybackControl";
|
export * from "./components/atoms/VoiceBroadcastPlaybackControl";
|
||||||
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
||||||
|
export * from "./components/molecules/ConfirmListeBroadcastStopCurrent";
|
||||||
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastSmallPlaybackBody";
|
export * from "./components/molecules/VoiceBroadcastSmallPlaybackBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
||||||
|
|
|
@ -36,6 +36,8 @@ import {
|
||||||
VoiceBroadcastInfoEventType,
|
VoiceBroadcastInfoEventType,
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
VoiceBroadcastInfoEventContent,
|
VoiceBroadcastInfoEventContent,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
showConfirmListenBroadcastStopCurrentDialog,
|
||||||
} from "..";
|
} from "..";
|
||||||
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
|
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
|
||||||
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
|
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
|
||||||
|
@ -86,7 +88,7 @@ export class VoiceBroadcastPlayback
|
||||||
public readonly liveData = new SimpleObservable<number[]>();
|
public readonly liveData = new SimpleObservable<number[]>();
|
||||||
private liveness: VoiceBroadcastLiveness = "not-live";
|
private liveness: VoiceBroadcastLiveness = "not-live";
|
||||||
|
|
||||||
// set vial addInfoEvent() in constructor
|
// set via addInfoEvent() in constructor
|
||||||
private infoState!: VoiceBroadcastInfoState;
|
private infoState!: VoiceBroadcastInfoState;
|
||||||
private lastInfoEvent!: MatrixEvent;
|
private lastInfoEvent!: MatrixEvent;
|
||||||
|
|
||||||
|
@ -94,7 +96,11 @@ export class VoiceBroadcastPlayback
|
||||||
private chunkRelationHelper!: RelationsHelper;
|
private chunkRelationHelper!: RelationsHelper;
|
||||||
private infoRelationHelper!: RelationsHelper;
|
private infoRelationHelper!: RelationsHelper;
|
||||||
|
|
||||||
public constructor(public readonly infoEvent: MatrixEvent, private client: MatrixClient) {
|
public constructor(
|
||||||
|
public readonly infoEvent: MatrixEvent,
|
||||||
|
private client: MatrixClient,
|
||||||
|
private recordings: VoiceBroadcastRecordingsStore,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.addInfoEvent(this.infoEvent);
|
this.addInfoEvent(this.infoEvent);
|
||||||
this.infoEvent.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
|
this.infoEvent.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
|
||||||
|
@ -399,6 +405,21 @@ export class VoiceBroadcastPlayback
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
|
if (this.state === VoiceBroadcastPlaybackState.Playing) return;
|
||||||
|
|
||||||
|
const currentRecording = this.recordings.getCurrent();
|
||||||
|
|
||||||
|
if (currentRecording && currentRecording.getState() !== VoiceBroadcastInfoState.Stopped) {
|
||||||
|
const shouldStopRecording = await showConfirmListenBroadcastStopCurrentDialog();
|
||||||
|
|
||||||
|
if (!shouldStopRecording) {
|
||||||
|
// keep recording
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.recordings.getCurrent()?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
const chunkEvents = this.chunkEvents.getEvents();
|
const chunkEvents = this.chunkEvents.getEvents();
|
||||||
|
|
||||||
const toPlay =
|
const toPlay =
|
||||||
|
|
|
@ -17,7 +17,12 @@ limitations under the License.
|
||||||
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
|
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
|
||||||
|
|
||||||
import { VoiceBroadcastPlayback, VoiceBroadcastPlaybackEvent, VoiceBroadcastPlaybackState } from "..";
|
import {
|
||||||
|
VoiceBroadcastPlayback,
|
||||||
|
VoiceBroadcastPlaybackEvent,
|
||||||
|
VoiceBroadcastPlaybackState,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
} from "..";
|
||||||
import { IDestroyable } from "../../utils/IDestroyable";
|
import { IDestroyable } from "../../utils/IDestroyable";
|
||||||
|
|
||||||
export enum VoiceBroadcastPlaybacksStoreEvent {
|
export enum VoiceBroadcastPlaybacksStoreEvent {
|
||||||
|
@ -42,7 +47,7 @@ export class VoiceBroadcastPlaybacksStore
|
||||||
/** Playbacks indexed by their info event id. */
|
/** Playbacks indexed by their info event id. */
|
||||||
private playbacks = new Map<string, VoiceBroadcastPlayback>();
|
private playbacks = new Map<string, VoiceBroadcastPlayback>();
|
||||||
|
|
||||||
public constructor() {
|
public constructor(private recordings: VoiceBroadcastRecordingsStore) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +74,7 @@ export class VoiceBroadcastPlaybacksStore
|
||||||
const infoEventId = infoEvent.getId();
|
const infoEventId = infoEvent.getId();
|
||||||
|
|
||||||
if (!this.playbacks.has(infoEventId)) {
|
if (!this.playbacks.has(infoEventId)) {
|
||||||
this.addPlayback(new VoiceBroadcastPlayback(infoEvent, client));
|
this.addPlayback(new VoiceBroadcastPlayback(infoEvent, client, this.recordings));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.playbacks.get(infoEventId);
|
return this.playbacks.get(infoEventId);
|
||||||
|
|
|
@ -406,6 +406,7 @@ describe("LegacyCallHandler", () => {
|
||||||
"d42",
|
"d42",
|
||||||
),
|
),
|
||||||
MatrixClientPeg.get(),
|
MatrixClientPeg.get(),
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore,
|
||||||
);
|
);
|
||||||
SdkContextClass.instance.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
SdkContextClass.instance.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
||||||
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
||||||
|
|
|
@ -112,7 +112,7 @@ describe("PipView", () => {
|
||||||
sdkContext = new TestSdkContext();
|
sdkContext = new TestSdkContext();
|
||||||
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
||||||
voiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore();
|
voiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(voiceBroadcastRecordingsStore);
|
||||||
sdkContext.client = client;
|
sdkContext.client = client;
|
||||||
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
||||||
sdkContext._VoiceBroadcastPreRecordingStore = voiceBroadcastPreRecordingStore;
|
sdkContext._VoiceBroadcastPreRecordingStore = voiceBroadcastPreRecordingStore;
|
||||||
|
|
|
@ -132,7 +132,7 @@ describe("RoomViewStore", function () {
|
||||||
stores._SlidingSyncManager = slidingSyncManager;
|
stores._SlidingSyncManager = slidingSyncManager;
|
||||||
stores._PosthogAnalytics = new MockPosthogAnalytics();
|
stores._PosthogAnalytics = new MockPosthogAnalytics();
|
||||||
stores._SpaceStore = new MockSpaceStore();
|
stores._SpaceStore = new MockSpaceStore();
|
||||||
stores._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore();
|
stores._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(stores.voiceBroadcastRecordingsStore);
|
||||||
roomViewStore = new RoomViewStore(dis, stores);
|
roomViewStore = new RoomViewStore(dis, stores);
|
||||||
stores._RoomViewStore = roomViewStore;
|
stores._RoomViewStore = roomViewStore;
|
||||||
});
|
});
|
||||||
|
@ -271,6 +271,7 @@ describe("RoomViewStore", function () {
|
||||||
"d42",
|
"d42",
|
||||||
),
|
),
|
||||||
mockClient,
|
mockClient,
|
||||||
|
stores.voiceBroadcastRecordingsStore,
|
||||||
);
|
);
|
||||||
stores.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
stores.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
||||||
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
VoiceBroadcastRecording,
|
VoiceBroadcastRecording,
|
||||||
VoiceBroadcastPlaybackBody,
|
VoiceBroadcastPlaybackBody,
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { stubClient, wrapInSdkContext } from "../../test-utils";
|
import { stubClient, wrapInSdkContext } from "../../test-utils";
|
||||||
import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils";
|
import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils";
|
||||||
|
@ -91,7 +92,7 @@ describe("VoiceBroadcastBody", () => {
|
||||||
);
|
);
|
||||||
room.addEventsToTimeline([infoEvent], true, room.getLiveTimeline());
|
room.addEventsToTimeline([infoEvent], true, room.getLiveTimeline());
|
||||||
testRecording = new VoiceBroadcastRecording(infoEvent, client);
|
testRecording = new VoiceBroadcastRecording(infoEvent, client);
|
||||||
testPlayback = new VoiceBroadcastPlayback(infoEvent, client);
|
testPlayback = new VoiceBroadcastPlayback(infoEvent, client, new VoiceBroadcastRecordingsStore());
|
||||||
mocked(VoiceBroadcastRecordingBody).mockImplementation(({ recording }): ReactElement | null => {
|
mocked(VoiceBroadcastRecordingBody).mockImplementation(({ recording }): ReactElement | null => {
|
||||||
if (testRecording === recording) {
|
if (testRecording === recording) {
|
||||||
return <div data-testid="voice-broadcast-recording-body" />;
|
return <div data-testid="voice-broadcast-recording-body" />;
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { stubClient } from "../../../test-utils";
|
||||||
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
|
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
|
||||||
import dis from "../../../../src/dispatcher/dispatcher";
|
import dis from "../../../../src/dispatcher/dispatcher";
|
||||||
import { Action } from "../../../../src/dispatcher/actions";
|
import { Action } from "../../../../src/dispatcher/actions";
|
||||||
|
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||||
|
|
||||||
jest.mock("../../../../src/dispatcher/dispatcher");
|
jest.mock("../../../../src/dispatcher/dispatcher");
|
||||||
|
|
||||||
|
@ -66,7 +67,11 @@ describe("VoiceBroadcastPlaybackBody", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
playback = new VoiceBroadcastPlayback(
|
||||||
|
infoEvent,
|
||||||
|
client,
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore,
|
||||||
|
);
|
||||||
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
||||||
jest.spyOn(playback, "getLiveness");
|
jest.spyOn(playback, "getLiveness");
|
||||||
jest.spyOn(playback, "getState");
|
jest.spyOn(playback, "getState");
|
||||||
|
|
|
@ -66,8 +66,8 @@ describe("VoiceBroadcastPreRecordingPip", () => {
|
||||||
client = stubClient();
|
client = stubClient();
|
||||||
room = new Room("!room@example.com", client, client.getUserId() || "");
|
room = new Room("!room@example.com", client, client.getUserId() || "");
|
||||||
sender = new RoomMember(room.roomId, client.getUserId() || "");
|
sender = new RoomMember(room.roomId, client.getUserId() || "");
|
||||||
playbacksStore = new VoiceBroadcastPlaybacksStore();
|
|
||||||
recordingsStore = new VoiceBroadcastRecordingsStore();
|
recordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
|
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
|
||||||
mocked(requestMediaPermissions).mockResolvedValue({
|
mocked(requestMediaPermissions).mockResolvedValue({
|
||||||
getTracks: (): Array<MediaStreamTrack> => [],
|
getTracks: (): Array<MediaStreamTrack> => [],
|
||||||
} as unknown as MediaStream);
|
} as unknown as MediaStream);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
} from "../../../../src/voice-broadcast";
|
} from "../../../../src/voice-broadcast";
|
||||||
import { stubClient } from "../../../test-utils";
|
import { stubClient } from "../../../test-utils";
|
||||||
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
|
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
|
||||||
|
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||||
|
|
||||||
// mock RoomAvatar, because it is doing too much fancy stuff
|
// mock RoomAvatar, because it is doing too much fancy stuff
|
||||||
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
|
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
|
||||||
|
@ -60,7 +61,11 @@ describe("<VoiceBroadcastSmallPlaybackBody />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
playback = new VoiceBroadcastPlayback(
|
||||||
|
infoEvent,
|
||||||
|
client,
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore,
|
||||||
|
);
|
||||||
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
|
||||||
jest.spyOn(playback, "getLiveness");
|
jest.spyOn(playback, "getLiveness");
|
||||||
jest.spyOn(playback, "getState");
|
jest.spyOn(playback, "getState");
|
||||||
|
|
|
@ -15,10 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
|
import { screen } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { Playback, PlaybackState } from "../../../src/audio/Playback";
|
import { Playback, PlaybackState } from "../../../src/audio/Playback";
|
||||||
import { PlaybackManager } from "../../../src/audio/PlaybackManager";
|
import { PlaybackManager } from "../../../src/audio/PlaybackManager";
|
||||||
|
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||||
import { MediaEventHelper } from "../../../src/utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../../src/utils/MediaEventHelper";
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
|
@ -26,6 +29,7 @@ import {
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
VoiceBroadcastPlaybackEvent,
|
VoiceBroadcastPlaybackEvent,
|
||||||
VoiceBroadcastPlaybackState,
|
VoiceBroadcastPlaybackState,
|
||||||
|
VoiceBroadcastRecording,
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { flushPromises, stubClient } from "../../test-utils";
|
import { flushPromises, stubClient } from "../../test-utils";
|
||||||
import { createTestPlayback } from "../../test-utils/audio";
|
import { createTestPlayback } from "../../test-utils/audio";
|
||||||
|
@ -65,6 +69,17 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
let chunk2Playback: Playback;
|
let chunk2Playback: Playback;
|
||||||
let chunk3Playback: Playback;
|
let chunk3Playback: Playback;
|
||||||
|
|
||||||
|
const queryConfirmListeningDialog = () => {
|
||||||
|
return screen.queryByText(
|
||||||
|
"If you start listening to this live broadcast, your current live broadcast recording will be ended.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForDialog = async () => {
|
||||||
|
await flushPromises();
|
||||||
|
await flushPromises();
|
||||||
|
};
|
||||||
|
|
||||||
const itShouldSetTheStateTo = (state: VoiceBroadcastPlaybackState) => {
|
const itShouldSetTheStateTo = (state: VoiceBroadcastPlaybackState) => {
|
||||||
it(`should set the state to ${state}`, () => {
|
it(`should set the state to ${state}`, () => {
|
||||||
expect(playback.getState()).toBe(state);
|
expect(playback.getState()).toBe(state);
|
||||||
|
@ -119,7 +134,11 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mkPlayback = async () => {
|
const mkPlayback = async () => {
|
||||||
const playback = new VoiceBroadcastPlayback(infoEvent, client);
|
const playback = new VoiceBroadcastPlayback(
|
||||||
|
infoEvent,
|
||||||
|
client,
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore,
|
||||||
|
);
|
||||||
jest.spyOn(playback, "removeAllListeners");
|
jest.spyOn(playback, "removeAllListeners");
|
||||||
jest.spyOn(playback, "destroy");
|
jest.spyOn(playback, "destroy");
|
||||||
playback.on(VoiceBroadcastPlaybackEvent.StateChanged, onStateChanged);
|
playback.on(VoiceBroadcastPlaybackEvent.StateChanged, onStateChanged);
|
||||||
|
@ -178,7 +197,11 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
onStateChanged = jest.fn();
|
onStateChanged = jest.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(async () => {
|
||||||
|
SdkContextClass.instance.voiceBroadcastPlaybacksStore.getCurrent()?.stop();
|
||||||
|
SdkContextClass.instance.voiceBroadcastPlaybacksStore.clearCurrent();
|
||||||
|
await SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent()?.stop();
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent();
|
||||||
playback.destroy();
|
playback.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -347,6 +370,59 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("and currently recording a broadcast", () => {
|
||||||
|
let recording: VoiceBroadcastRecording;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
recording = new VoiceBroadcastRecording(
|
||||||
|
mkVoiceBroadcastInfoStateEvent(
|
||||||
|
roomId,
|
||||||
|
VoiceBroadcastInfoState.Started,
|
||||||
|
client.getSafeUserId(),
|
||||||
|
client.deviceId,
|
||||||
|
),
|
||||||
|
client,
|
||||||
|
);
|
||||||
|
jest.spyOn(recording, "stop");
|
||||||
|
SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording);
|
||||||
|
playback.start();
|
||||||
|
await waitForDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display a confirm modal", () => {
|
||||||
|
expect(queryConfirmListeningDialog()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when confirming the dialog", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await userEvent.click(screen.getByText("Yes, end my recording"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should stop the recording", () => {
|
||||||
|
expect(recording.stop).toHaveBeenCalled();
|
||||||
|
expect(SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent()).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not start the playback", () => {
|
||||||
|
expect(playback.getState()).toBe(VoiceBroadcastPlaybackState.Playing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when not confirming the dialog", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await userEvent.click(screen.getByText("No"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not stop the recording", () => {
|
||||||
|
expect(recording.stop).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start the playback", () => {
|
||||||
|
expect(playback.getState()).toBe(VoiceBroadcastPlaybackState.Stopped);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when there is a stopped voice broadcast", () => {
|
describe("when there is a stopped voice broadcast", () => {
|
||||||
|
@ -375,6 +451,12 @@ describe("VoiceBroadcastPlayback", () => {
|
||||||
expect(chunk2Playback.play).not.toHaveBeenCalled();
|
expect(chunk2Playback.play).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("and calling start again", () => {
|
||||||
|
it("should not play the first chunk a second time", () => {
|
||||||
|
expect(chunk1Playback.play).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("and the chunk playback progresses", () => {
|
describe("and the chunk playback progresses", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
chunk1Playback.clockInfo.liveData.update([11]);
|
chunk1Playback.clockInfo.liveData.update([11]);
|
|
@ -40,8 +40,8 @@ describe("VoiceBroadcastPreRecording", () => {
|
||||||
client = stubClient();
|
client = stubClient();
|
||||||
room = new Room(roomId, client, client.getUserId() || "");
|
room = new Room(roomId, client, client.getUserId() || "");
|
||||||
sender = new RoomMember(roomId, client.getUserId() || "");
|
sender = new RoomMember(roomId, client.getUserId() || "");
|
||||||
playbacksStore = new VoiceBroadcastPlaybacksStore();
|
|
||||||
recordingsStore = new VoiceBroadcastRecordingsStore();
|
recordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
|
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
VoiceBroadcastPlaybacksStore,
|
VoiceBroadcastPlaybacksStore,
|
||||||
VoiceBroadcastPlaybacksStoreEvent,
|
VoiceBroadcastPlaybacksStoreEvent,
|
||||||
VoiceBroadcastPlaybackState,
|
VoiceBroadcastPlaybackState,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { mkStubRoom, stubClient } from "../../test-utils";
|
import { mkStubRoom, stubClient } from "../../test-utils";
|
||||||
import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils";
|
import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils";
|
||||||
|
@ -59,12 +60,13 @@ describe("VoiceBroadcastPlaybacksStore", () => {
|
||||||
|
|
||||||
infoEvent1 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
|
infoEvent1 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
|
||||||
infoEvent2 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
|
infoEvent2 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
|
||||||
playback1 = new VoiceBroadcastPlayback(infoEvent1, client);
|
const recordings = new VoiceBroadcastRecordingsStore();
|
||||||
|
playback1 = new VoiceBroadcastPlayback(infoEvent1, client, recordings);
|
||||||
jest.spyOn(playback1, "off");
|
jest.spyOn(playback1, "off");
|
||||||
playback2 = new VoiceBroadcastPlayback(infoEvent2, client);
|
playback2 = new VoiceBroadcastPlayback(infoEvent2, client, recordings);
|
||||||
jest.spyOn(playback2, "off");
|
jest.spyOn(playback2, "off");
|
||||||
|
|
||||||
playbacks = new VoiceBroadcastPlaybacksStore();
|
playbacks = new VoiceBroadcastPlaybacksStore(recordings);
|
||||||
jest.spyOn(playbacks, "removeAllListeners");
|
jest.spyOn(playbacks, "removeAllListeners");
|
||||||
onCurrentChanged = jest.fn();
|
onCurrentChanged = jest.fn();
|
||||||
playbacks.on(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, onCurrentChanged);
|
playbacks.on(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, onCurrentChanged);
|
||||||
|
|
|
@ -25,8 +25,6 @@ import {
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { stubClient } from "../../test-utils";
|
import { stubClient } from "../../test-utils";
|
||||||
|
|
||||||
jest.mock("../../../src/voice-broadcast/stores/VoiceBroadcastRecordingsStore");
|
|
||||||
|
|
||||||
describe("VoiceBroadcastPreRecordingStore", () => {
|
describe("VoiceBroadcastPreRecordingStore", () => {
|
||||||
const roomId = "!room:example.com";
|
const roomId = "!room:example.com";
|
||||||
let client: MatrixClient;
|
let client: MatrixClient;
|
||||||
|
@ -41,8 +39,8 @@ describe("VoiceBroadcastPreRecordingStore", () => {
|
||||||
client = stubClient();
|
client = stubClient();
|
||||||
room = new Room(roomId, client, client.getUserId() || "");
|
room = new Room(roomId, client, client.getUserId() || "");
|
||||||
sender = new RoomMember(roomId, client.getUserId() || "");
|
sender = new RoomMember(roomId, client.getUserId() || "");
|
||||||
playbacksStore = new VoiceBroadcastPlaybacksStore();
|
|
||||||
recordingsStore = new VoiceBroadcastRecordingsStore();
|
recordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
|
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
VoiceBroadcastPlayback,
|
VoiceBroadcastPlayback,
|
||||||
VoiceBroadcastPlaybacksStore,
|
VoiceBroadcastPlaybacksStore,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
} from "../../../src/voice-broadcast";
|
} from "../../../src/voice-broadcast";
|
||||||
import { pauseNonLiveBroadcastFromOtherRoom } from "../../../src/voice-broadcast/utils/pauseNonLiveBroadcastFromOtherRoom";
|
import { pauseNonLiveBroadcastFromOtherRoom } from "../../../src/voice-broadcast/utils/pauseNonLiveBroadcastFromOtherRoom";
|
||||||
import { stubClient } from "../../test-utils";
|
import { stubClient } from "../../test-utils";
|
||||||
|
@ -32,6 +33,7 @@ describe("pauseNonLiveBroadcastFromOtherRoom", () => {
|
||||||
let client: MatrixClient;
|
let client: MatrixClient;
|
||||||
let playback: VoiceBroadcastPlayback;
|
let playback: VoiceBroadcastPlayback;
|
||||||
let playbacks: VoiceBroadcastPlaybacksStore;
|
let playbacks: VoiceBroadcastPlaybacksStore;
|
||||||
|
let recordings: VoiceBroadcastRecordingsStore;
|
||||||
|
|
||||||
const mkPlayback = (infoState: VoiceBroadcastInfoState, roomId: string): VoiceBroadcastPlayback => {
|
const mkPlayback = (infoState: VoiceBroadcastInfoState, roomId: string): VoiceBroadcastPlayback => {
|
||||||
const infoEvent = mkVoiceBroadcastInfoStateEvent(
|
const infoEvent = mkVoiceBroadcastInfoStateEvent(
|
||||||
|
@ -40,7 +42,7 @@ describe("pauseNonLiveBroadcastFromOtherRoom", () => {
|
||||||
client.getSafeUserId(),
|
client.getSafeUserId(),
|
||||||
client.getDeviceId()!,
|
client.getDeviceId()!,
|
||||||
);
|
);
|
||||||
const playback = new VoiceBroadcastPlayback(infoEvent, client);
|
const playback = new VoiceBroadcastPlayback(infoEvent, client, recordings);
|
||||||
jest.spyOn(playback, "pause");
|
jest.spyOn(playback, "pause");
|
||||||
playbacks.setCurrent(playback);
|
playbacks.setCurrent(playback);
|
||||||
return playback;
|
return playback;
|
||||||
|
@ -49,7 +51,8 @@ describe("pauseNonLiveBroadcastFromOtherRoom", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
client = stubClient();
|
client = stubClient();
|
||||||
room = new Room(roomId, client, client.getSafeUserId());
|
room = new Room(roomId, client, client.getSafeUserId());
|
||||||
playbacks = new VoiceBroadcastPlaybacksStore();
|
recordings = new VoiceBroadcastRecordingsStore();
|
||||||
|
playbacks = new VoiceBroadcastPlaybacksStore(recordings);
|
||||||
jest.spyOn(playbacks, "clearCurrent");
|
jest.spyOn(playbacks, "clearCurrent");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -80,10 +80,10 @@ describe("setUpVoiceBroadcastPreRecording", () => {
|
||||||
);
|
);
|
||||||
preRecording = null;
|
preRecording = null;
|
||||||
preRecordingStore = new VoiceBroadcastPreRecordingStore();
|
preRecordingStore = new VoiceBroadcastPreRecordingStore();
|
||||||
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
|
||||||
jest.spyOn(playback, "pause");
|
|
||||||
playbacksStore = new VoiceBroadcastPlaybacksStore();
|
|
||||||
recordingsStore = new VoiceBroadcastRecordingsStore();
|
recordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
|
playback = new VoiceBroadcastPlayback(infoEvent, client, recordingsStore);
|
||||||
|
jest.spyOn(playback, "pause");
|
||||||
|
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when the preconditions fail", () => {
|
describe("when the preconditions fail", () => {
|
||||||
|
|
|
@ -84,7 +84,7 @@ describe("startNewVoiceBroadcastRecording", () => {
|
||||||
skey: "",
|
skey: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
playbacksStore = new VoiceBroadcastPlaybacksStore();
|
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
|
||||||
recordingsStore = {
|
recordingsStore = {
|
||||||
setCurrent: jest.fn(),
|
setCurrent: jest.fn(),
|
||||||
getCurrent: jest.fn(),
|
getCurrent: jest.fn(),
|
||||||
|
@ -112,7 +112,7 @@ describe("startNewVoiceBroadcastRecording", () => {
|
||||||
let playback: VoiceBroadcastPlayback;
|
let playback: VoiceBroadcastPlayback;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
playback = new VoiceBroadcastPlayback(infoEvent, client);
|
playback = new VoiceBroadcastPlayback(infoEvent, client, recordingsStore);
|
||||||
jest.spyOn(playback, "pause");
|
jest.spyOn(playback, "pause");
|
||||||
playbacksStore.setCurrent(playback);
|
playbacksStore.setCurrent(playback);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue