Add voice broadcast playback pip (#9603)
This commit is contained in:
parent
569a364933
commit
a8e15ebe60
13 changed files with 372 additions and 41 deletions
|
@ -39,11 +39,14 @@ import { CallStore } from "../../../stores/CallStore";
|
||||||
import {
|
import {
|
||||||
useCurrentVoiceBroadcastPreRecording,
|
useCurrentVoiceBroadcastPreRecording,
|
||||||
useCurrentVoiceBroadcastRecording,
|
useCurrentVoiceBroadcastRecording,
|
||||||
|
VoiceBroadcastPlayback,
|
||||||
|
VoiceBroadcastPlaybackBody,
|
||||||
VoiceBroadcastPreRecording,
|
VoiceBroadcastPreRecording,
|
||||||
VoiceBroadcastPreRecordingPip,
|
VoiceBroadcastPreRecordingPip,
|
||||||
VoiceBroadcastRecording,
|
VoiceBroadcastRecording,
|
||||||
VoiceBroadcastRecordingPip,
|
VoiceBroadcastRecordingPip,
|
||||||
} from '../../../voice-broadcast';
|
} from '../../../voice-broadcast';
|
||||||
|
import { useCurrentVoiceBroadcastPlayback } from '../../../voice-broadcast/hooks/useCurrentVoiceBroadcastPlayback';
|
||||||
|
|
||||||
const SHOW_CALL_IN_STATES = [
|
const SHOW_CALL_IN_STATES = [
|
||||||
CallState.Connected,
|
CallState.Connected,
|
||||||
|
@ -57,6 +60,7 @@ const SHOW_CALL_IN_STATES = [
|
||||||
interface IProps {
|
interface IProps {
|
||||||
voiceBroadcastRecording?: Optional<VoiceBroadcastRecording>;
|
voiceBroadcastRecording?: Optional<VoiceBroadcastRecording>;
|
||||||
voiceBroadcastPreRecording?: Optional<VoiceBroadcastPreRecording>;
|
voiceBroadcastPreRecording?: Optional<VoiceBroadcastPreRecording>;
|
||||||
|
voiceBroadcastPlayback?: Optional<VoiceBroadcastPlayback>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -330,6 +334,15 @@ class PipView extends React.Component<IProps, IState> {
|
||||||
this.setState({ showWidgetInPip, persistentWidgetId, persistentRoomId });
|
this.setState({ showWidgetInPip, persistentWidgetId, persistentRoomId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createVoiceBroadcastPlaybackPipContent(voiceBroadcastPlayback: VoiceBroadcastPlayback): CreatePipChildren {
|
||||||
|
return ({ onStartMoving }) => <div onMouseDown={onStartMoving}>
|
||||||
|
<VoiceBroadcastPlaybackBody
|
||||||
|
playback={voiceBroadcastPlayback}
|
||||||
|
pip={true}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
private createVoiceBroadcastPreRecordingPipContent(
|
private createVoiceBroadcastPreRecordingPipContent(
|
||||||
voiceBroadcastPreRecording: VoiceBroadcastPreRecording,
|
voiceBroadcastPreRecording: VoiceBroadcastPreRecording,
|
||||||
): CreatePipChildren {
|
): CreatePipChildren {
|
||||||
|
@ -358,6 +371,10 @@ class PipView extends React.Component<IProps, IState> {
|
||||||
pipContent = this.createVoiceBroadcastPreRecordingPipContent(this.props.voiceBroadcastPreRecording);
|
pipContent = this.createVoiceBroadcastPreRecordingPipContent(this.props.voiceBroadcastPreRecording);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.voiceBroadcastPlayback) {
|
||||||
|
pipContent = this.createVoiceBroadcastPlaybackPipContent(this.props.voiceBroadcastPlayback);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.props.voiceBroadcastRecording) {
|
if (this.props.voiceBroadcastRecording) {
|
||||||
pipContent = this.createVoiceBroadcastRecordingPipContent(this.props.voiceBroadcastRecording);
|
pipContent = this.createVoiceBroadcastRecordingPipContent(this.props.voiceBroadcastRecording);
|
||||||
}
|
}
|
||||||
|
@ -430,9 +447,13 @@ const PipViewHOC: React.FC<IProps> = (props) => {
|
||||||
const voiceBroadcastRecordingsStore = sdkContext.voiceBroadcastRecordingsStore;
|
const voiceBroadcastRecordingsStore = sdkContext.voiceBroadcastRecordingsStore;
|
||||||
const { currentVoiceBroadcastRecording } = useCurrentVoiceBroadcastRecording(voiceBroadcastRecordingsStore);
|
const { currentVoiceBroadcastRecording } = useCurrentVoiceBroadcastRecording(voiceBroadcastRecordingsStore);
|
||||||
|
|
||||||
|
const voiceBroadcastPlaybacksStore = sdkContext.voiceBroadcastPlaybacksStore;
|
||||||
|
const { currentVoiceBroadcastPlayback } = useCurrentVoiceBroadcastPlayback(voiceBroadcastPlaybacksStore);
|
||||||
|
|
||||||
return <PipView
|
return <PipView
|
||||||
voiceBroadcastRecording={currentVoiceBroadcastRecording}
|
voiceBroadcastPlayback={currentVoiceBroadcastPlayback}
|
||||||
voiceBroadcastPreRecording={currentVoiceBroadcastPreRecording}
|
voiceBroadcastPreRecording={currentVoiceBroadcastPreRecording}
|
||||||
|
voiceBroadcastRecording={currentVoiceBroadcastRecording}
|
||||||
{...props}
|
{...props}
|
||||||
/>;
|
/>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,7 +30,11 @@ import TypingStore from "../stores/TypingStore";
|
||||||
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
|
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
|
||||||
import { WidgetPermissionStore } from "../stores/widgets/WidgetPermissionStore";
|
import { WidgetPermissionStore } from "../stores/widgets/WidgetPermissionStore";
|
||||||
import WidgetStore from "../stores/WidgetStore";
|
import WidgetStore from "../stores/WidgetStore";
|
||||||
import { VoiceBroadcastPreRecordingStore, VoiceBroadcastRecordingsStore } from "../voice-broadcast";
|
import {
|
||||||
|
VoiceBroadcastPlaybacksStore,
|
||||||
|
VoiceBroadcastPreRecordingStore,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
} from "../voice-broadcast";
|
||||||
|
|
||||||
export const SDKContext = createContext<SdkContextClass>(undefined);
|
export const SDKContext = createContext<SdkContextClass>(undefined);
|
||||||
SDKContext.displayName = "SDKContext";
|
SDKContext.displayName = "SDKContext";
|
||||||
|
@ -68,6 +72,7 @@ export class SdkContextClass {
|
||||||
protected _TypingStore?: TypingStore;
|
protected _TypingStore?: TypingStore;
|
||||||
protected _VoiceBroadcastRecordingsStore?: VoiceBroadcastRecordingsStore;
|
protected _VoiceBroadcastRecordingsStore?: VoiceBroadcastRecordingsStore;
|
||||||
protected _VoiceBroadcastPreRecordingStore?: VoiceBroadcastPreRecordingStore;
|
protected _VoiceBroadcastPreRecordingStore?: VoiceBroadcastPreRecordingStore;
|
||||||
|
protected _VoiceBroadcastPlaybacksStore?: VoiceBroadcastPlaybacksStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically construct stores which need to be created eagerly so they can register with
|
* Automatically construct stores which need to be created eagerly so they can register with
|
||||||
|
@ -166,4 +171,11 @@ export class SdkContextClass {
|
||||||
}
|
}
|
||||||
return this._VoiceBroadcastPreRecordingStore;
|
return this._VoiceBroadcastPreRecordingStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get voiceBroadcastPlaybacksStore(): VoiceBroadcastPlaybacksStore {
|
||||||
|
if (!this._VoiceBroadcastPlaybacksStore) {
|
||||||
|
this._VoiceBroadcastPlaybacksStore = VoiceBroadcastPlaybacksStore.instance();
|
||||||
|
}
|
||||||
|
return this._VoiceBroadcastPlaybacksStore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,11 @@ import { UPDATE_EVENT } from "./AsyncStore";
|
||||||
import { SdkContextClass } from "../contexts/SDKContext";
|
import { SdkContextClass } from "../contexts/SDKContext";
|
||||||
import { CallStore } from "./CallStore";
|
import { CallStore } from "./CallStore";
|
||||||
import { ThreadPayload } from "../dispatcher/payloads/ThreadPayload";
|
import { ThreadPayload } from "../dispatcher/payloads/ThreadPayload";
|
||||||
|
import {
|
||||||
|
doClearCurrentVoiceBroadcastPlaybackIfStopped,
|
||||||
|
doMaybeSetCurrentVoiceBroadcastPlayback,
|
||||||
|
} from "../voice-broadcast";
|
||||||
|
import { IRoomStateEventsActionPayload } from "../actions/MatrixActionCreators";
|
||||||
|
|
||||||
const NUM_JOIN_RETRY = 5;
|
const NUM_JOIN_RETRY = 5;
|
||||||
|
|
||||||
|
@ -195,6 +200,28 @@ export class RoomViewStore extends EventEmitter {
|
||||||
this.emit(UPDATE_EVENT);
|
this.emit(UPDATE_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private doMaybeSetCurrentVoiceBroadcastPlayback(room: Room): void {
|
||||||
|
doMaybeSetCurrentVoiceBroadcastPlayback(
|
||||||
|
room,
|
||||||
|
this.stores.client,
|
||||||
|
this.stores.voiceBroadcastPlaybacksStore,
|
||||||
|
this.stores.voiceBroadcastRecordingsStore,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onRoomStateEvents(event: MatrixEvent): void {
|
||||||
|
const roomId = event.getRoomId?.();
|
||||||
|
|
||||||
|
// no room or not current room
|
||||||
|
if (!roomId || roomId !== this.state.roomId) return;
|
||||||
|
|
||||||
|
const room = this.stores.client?.getRoom(roomId);
|
||||||
|
|
||||||
|
if (room) {
|
||||||
|
this.doMaybeSetCurrentVoiceBroadcastPlayback(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private onDispatch(payload): void { // eslint-disable-line @typescript-eslint/naming-convention
|
private onDispatch(payload): void { // eslint-disable-line @typescript-eslint/naming-convention
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
// view_room:
|
// view_room:
|
||||||
|
@ -219,6 +246,10 @@ export class RoomViewStore extends EventEmitter {
|
||||||
wasContextSwitch: false,
|
wasContextSwitch: false,
|
||||||
viewingCall: false,
|
viewingCall: false,
|
||||||
});
|
});
|
||||||
|
doClearCurrentVoiceBroadcastPlaybackIfStopped(this.stores.voiceBroadcastPlaybacksStore);
|
||||||
|
break;
|
||||||
|
case "MatrixActions.RoomState.events":
|
||||||
|
this.onRoomStateEvents((payload as IRoomStateEventsActionPayload).event);
|
||||||
break;
|
break;
|
||||||
case Action.ViewRoomError:
|
case Action.ViewRoomError:
|
||||||
this.viewRoomError(payload);
|
this.viewRoomError(payload);
|
||||||
|
@ -395,6 +426,10 @@ export class RoomViewStore extends EventEmitter {
|
||||||
metricsTrigger: payload.metricsTrigger as JoinRoomPayload["metricsTrigger"],
|
metricsTrigger: payload.metricsTrigger as JoinRoomPayload["metricsTrigger"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (room) {
|
||||||
|
this.doMaybeSetCurrentVoiceBroadcastPlayback(room);
|
||||||
|
}
|
||||||
} else if (payload.room_alias) {
|
} else if (payload.room_alias) {
|
||||||
// Try the room alias to room ID navigation cache first to avoid
|
// Try the room alias to room ID navigation cache first to avoid
|
||||||
// blocking room navigation on the homeserver.
|
// blocking room navigation on the homeserver.
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastControl,
|
VoiceBroadcastControl,
|
||||||
|
@ -36,10 +37,12 @@ import { SeekButton } from "../atoms/SeekButton";
|
||||||
const SEEK_TIME = 30;
|
const SEEK_TIME = 30;
|
||||||
|
|
||||||
interface VoiceBroadcastPlaybackBodyProps {
|
interface VoiceBroadcastPlaybackBodyProps {
|
||||||
|
pip?: boolean;
|
||||||
playback: VoiceBroadcastPlayback;
|
playback: VoiceBroadcastPlayback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProps> = ({
|
export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProps> = ({
|
||||||
|
pip = false,
|
||||||
playback,
|
playback,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
|
@ -107,8 +110,13 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
mx_VoiceBroadcastBody: true,
|
||||||
|
["mx_VoiceBroadcastBody--pip"]: pip,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_VoiceBroadcastBody">
|
<div className={classes}>
|
||||||
<VoiceBroadcastHeader
|
<VoiceBroadcastHeader
|
||||||
live={liveness}
|
live={liveness}
|
||||||
microphoneLabel={sender?.name}
|
microphoneLabel={sender?.name}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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 { useState } from "react";
|
||||||
|
|
||||||
|
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
|
||||||
|
import { VoiceBroadcastPlayback } from "../models/VoiceBroadcastPlayback";
|
||||||
|
import {
|
||||||
|
VoiceBroadcastPlaybacksStore,
|
||||||
|
VoiceBroadcastPlaybacksStoreEvent,
|
||||||
|
} from "../stores/VoiceBroadcastPlaybacksStore";
|
||||||
|
|
||||||
|
export const useCurrentVoiceBroadcastPlayback = (
|
||||||
|
voiceBroadcastPlaybackStore: VoiceBroadcastPlaybacksStore,
|
||||||
|
) => {
|
||||||
|
const [currentVoiceBroadcastPlayback, setVoiceBroadcastPlayback] = useState(
|
||||||
|
voiceBroadcastPlaybackStore.getCurrent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
useTypedEventEmitter(
|
||||||
|
voiceBroadcastPlaybackStore,
|
||||||
|
VoiceBroadcastPlaybacksStoreEvent.CurrentChanged,
|
||||||
|
(playback: VoiceBroadcastPlayback) => {
|
||||||
|
setVoiceBroadcastPlayback(playback);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentVoiceBroadcastPlayback,
|
||||||
|
};
|
||||||
|
};
|
|
@ -40,6 +40,8 @@ export * from "./stores/VoiceBroadcastPlaybacksStore";
|
||||||
export * from "./stores/VoiceBroadcastPreRecordingStore";
|
export * from "./stores/VoiceBroadcastPreRecordingStore";
|
||||||
export * from "./stores/VoiceBroadcastRecordingsStore";
|
export * from "./stores/VoiceBroadcastRecordingsStore";
|
||||||
export * from "./utils/checkVoiceBroadcastPreConditions";
|
export * from "./utils/checkVoiceBroadcastPreConditions";
|
||||||
|
export * from "./utils/doClearCurrentVoiceBroadcastPlaybackIfStopped";
|
||||||
|
export * from "./utils/doMaybeSetCurrentVoiceBroadcastPlayback";
|
||||||
export * from "./utils/getChunkLength";
|
export * from "./utils/getChunkLength";
|
||||||
export * from "./utils/getMaxBroadcastLength";
|
export * from "./utils/getMaxBroadcastLength";
|
||||||
export * from "./utils/hasRoomLiveVoiceBroadcast";
|
export * from "./utils/hasRoomLiveVoiceBroadcast";
|
||||||
|
|
|
@ -25,7 +25,7 @@ export enum VoiceBroadcastPlaybacksStoreEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventMap {
|
interface EventMap {
|
||||||
[VoiceBroadcastPlaybacksStoreEvent.CurrentChanged]: (recording: VoiceBroadcastPlayback) => void;
|
[VoiceBroadcastPlaybacksStoreEvent.CurrentChanged]: (recording: VoiceBroadcastPlayback | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,7 +53,14 @@ export class VoiceBroadcastPlaybacksStore
|
||||||
this.emit(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, current);
|
this.emit(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrent(): VoiceBroadcastPlayback {
|
public clearCurrent(): void {
|
||||||
|
if (this.current === null) return;
|
||||||
|
|
||||||
|
this.current = null;
|
||||||
|
this.emit(VoiceBroadcastPlaybacksStoreEvent.CurrentChanged, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrent(): VoiceBroadcastPlayback | null {
|
||||||
return this.current;
|
return this.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +87,15 @@ export class VoiceBroadcastPlaybacksStore
|
||||||
state: VoiceBroadcastPlaybackState,
|
state: VoiceBroadcastPlaybackState,
|
||||||
playback: VoiceBroadcastPlayback,
|
playback: VoiceBroadcastPlayback,
|
||||||
): void => {
|
): void => {
|
||||||
if ([
|
switch (state) {
|
||||||
VoiceBroadcastPlaybackState.Buffering,
|
case VoiceBroadcastPlaybackState.Buffering:
|
||||||
VoiceBroadcastPlaybackState.Playing,
|
case VoiceBroadcastPlaybackState.Playing:
|
||||||
].includes(state)) {
|
this.pauseExcept(playback);
|
||||||
this.pauseExcept(playback);
|
this.setCurrent(playback);
|
||||||
|
break;
|
||||||
|
case VoiceBroadcastPlaybackState.Stopped:
|
||||||
|
this.clearCurrent();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
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 { VoiceBroadcastPlaybacksStore, VoiceBroadcastPlaybackState } from "..";
|
||||||
|
|
||||||
|
export const doClearCurrentVoiceBroadcastPlaybackIfStopped = (
|
||||||
|
voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore,
|
||||||
|
) => {
|
||||||
|
if (voiceBroadcastPlaybacksStore.getCurrent()?.getState() === VoiceBroadcastPlaybackState.Stopped) {
|
||||||
|
// clear current if stopped
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
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 { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import {
|
||||||
|
hasRoomLiveVoiceBroadcast,
|
||||||
|
VoiceBroadcastPlaybacksStore,
|
||||||
|
VoiceBroadcastPlaybackState,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
} from "..";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a live voice broadcast is in the room and
|
||||||
|
* another voice broadcast is not currently being listened to or recorded
|
||||||
|
* the live broadcast in the room is set as the current broadcast to listen to.
|
||||||
|
* When there is no live broadcast in the room: clear current broadcast.
|
||||||
|
*
|
||||||
|
* @param {Room} room The room to check for a live voice broadcast
|
||||||
|
* @param {MatrixClient} client
|
||||||
|
* @param {VoiceBroadcastPlaybacksStore} voiceBroadcastPlaybacksStore
|
||||||
|
* @param {VoiceBroadcastRecordingsStore} voiceBroadcastRecordingsStore
|
||||||
|
*/
|
||||||
|
export const doMaybeSetCurrentVoiceBroadcastPlayback = (
|
||||||
|
room: Room,
|
||||||
|
client: MatrixClient,
|
||||||
|
voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore,
|
||||||
|
voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore,
|
||||||
|
): void => {
|
||||||
|
// do not disturb the current recording
|
||||||
|
if (voiceBroadcastRecordingsStore.hasCurrent()) return;
|
||||||
|
|
||||||
|
const currentPlayback = voiceBroadcastPlaybacksStore.getCurrent();
|
||||||
|
|
||||||
|
if (currentPlayback && currentPlayback.getState() !== VoiceBroadcastPlaybackState.Stopped) {
|
||||||
|
// do not disturb the current playback
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { infoEvent } = hasRoomLiveVoiceBroadcast(room);
|
||||||
|
|
||||||
|
if (infoEvent) {
|
||||||
|
// live broadcast in the room + no recording + not listening yet: set the current broadcast
|
||||||
|
const voiceBroadcastPlayback = voiceBroadcastPlaybacksStore.getByInfoEvent(infoEvent, client);
|
||||||
|
voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no broadcast; not listening: clear current
|
||||||
|
voiceBroadcastPlaybacksStore.clearCurrent();
|
||||||
|
};
|
|
@ -19,36 +19,42 @@ import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
||||||
|
|
||||||
interface Result {
|
interface Result {
|
||||||
|
// whether there is a live broadcast in the room
|
||||||
hasBroadcast: boolean;
|
hasBroadcast: boolean;
|
||||||
|
// info event of any live broadcast in the room
|
||||||
|
infoEvent: MatrixEvent | null;
|
||||||
|
// whether the broadcast was started by the user
|
||||||
startedByUser: boolean;
|
startedByUser: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const hasRoomLiveVoiceBroadcast = (room: Room, userId?: string): Result => {
|
||||||
* Finds out whether there is a live broadcast in a room.
|
|
||||||
* Also returns if the user started the broadcast (if any).
|
|
||||||
*/
|
|
||||||
export const hasRoomLiveVoiceBroadcast = (room: Room, userId: string): Result => {
|
|
||||||
let hasBroadcast = false;
|
let hasBroadcast = false;
|
||||||
let startedByUser = false;
|
let startedByUser = false;
|
||||||
|
let infoEvent: MatrixEvent | null = null;
|
||||||
|
|
||||||
const stateEvents = room.currentState.getStateEvents(VoiceBroadcastInfoEventType);
|
const stateEvents = room.currentState.getStateEvents(VoiceBroadcastInfoEventType);
|
||||||
stateEvents.forEach((event: MatrixEvent) => {
|
stateEvents.every((event: MatrixEvent) => {
|
||||||
const state = event.getContent()?.state;
|
const state = event.getContent()?.state;
|
||||||
|
|
||||||
if (state && state !== VoiceBroadcastInfoState.Stopped) {
|
if (state && state !== VoiceBroadcastInfoState.Stopped) {
|
||||||
hasBroadcast = true;
|
hasBroadcast = true;
|
||||||
|
infoEvent = event;
|
||||||
|
|
||||||
// state key = sender's MXID
|
// state key = sender's MXID
|
||||||
if (event.getStateKey() === userId) {
|
if (event.getStateKey() === userId) {
|
||||||
|
infoEvent = event;
|
||||||
startedByUser = true;
|
startedByUser = true;
|
||||||
// break here, because more than true / true is not possible
|
// break here, because more than true / true is not possible
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasBroadcast,
|
hasBroadcast,
|
||||||
|
infoEvent,
|
||||||
startedByUser,
|
startedByUser,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,11 @@ import { SpaceStoreClass } from "../src/stores/spaces/SpaceStore";
|
||||||
import { WidgetLayoutStore } from "../src/stores/widgets/WidgetLayoutStore";
|
import { WidgetLayoutStore } from "../src/stores/widgets/WidgetLayoutStore";
|
||||||
import { WidgetPermissionStore } from "../src/stores/widgets/WidgetPermissionStore";
|
import { WidgetPermissionStore } from "../src/stores/widgets/WidgetPermissionStore";
|
||||||
import WidgetStore from "../src/stores/WidgetStore";
|
import WidgetStore from "../src/stores/WidgetStore";
|
||||||
import { VoiceBroadcastPreRecordingStore, VoiceBroadcastRecordingsStore } from "../src/voice-broadcast";
|
import {
|
||||||
|
VoiceBroadcastPlaybacksStore,
|
||||||
|
VoiceBroadcastPreRecordingStore,
|
||||||
|
VoiceBroadcastRecordingsStore,
|
||||||
|
} from "../src/voice-broadcast";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class which provides the same API as SdkContextClass but adds additional unsafe setters which can
|
* A class which provides the same API as SdkContextClass but adds additional unsafe setters which can
|
||||||
|
@ -42,6 +46,7 @@ export class TestSdkContext extends SdkContextClass {
|
||||||
public _SpaceStore?: SpaceStoreClass;
|
public _SpaceStore?: SpaceStoreClass;
|
||||||
public _VoiceBroadcastRecordingsStore?: VoiceBroadcastRecordingsStore;
|
public _VoiceBroadcastRecordingsStore?: VoiceBroadcastRecordingsStore;
|
||||||
public _VoiceBroadcastPreRecordingStore?: VoiceBroadcastPreRecordingStore;
|
public _VoiceBroadcastPreRecordingStore?: VoiceBroadcastPreRecordingStore;
|
||||||
|
public _VoiceBroadcastPlaybacksStore?: VoiceBroadcastPlaybacksStore;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
import { Widget, ClientWidgetApi } from "matrix-widget-api";
|
import { Widget, ClientWidgetApi } from "matrix-widget-api";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import {
|
import {
|
||||||
|
@ -46,12 +47,15 @@ import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPay
|
||||||
import { TestSdkContext } from "../../../TestSdkContext";
|
import { TestSdkContext } from "../../../TestSdkContext";
|
||||||
import {
|
import {
|
||||||
VoiceBroadcastInfoState,
|
VoiceBroadcastInfoState,
|
||||||
|
VoiceBroadcastPlaybacksStore,
|
||||||
VoiceBroadcastPreRecording,
|
VoiceBroadcastPreRecording,
|
||||||
VoiceBroadcastPreRecordingStore,
|
VoiceBroadcastPreRecordingStore,
|
||||||
VoiceBroadcastRecording,
|
VoiceBroadcastRecording,
|
||||||
VoiceBroadcastRecordingsStore,
|
VoiceBroadcastRecordingsStore,
|
||||||
} from "../../../../src/voice-broadcast";
|
} from "../../../../src/voice-broadcast";
|
||||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||||
|
import { RoomViewStore } from "../../../../src/stores/RoomViewStore";
|
||||||
|
import { IRoomStateEventsActionPayload } from "../../../../src/actions/MatrixActionCreators";
|
||||||
|
|
||||||
describe("PipView", () => {
|
describe("PipView", () => {
|
||||||
useMockedCalls();
|
useMockedCalls();
|
||||||
|
@ -60,9 +64,11 @@ describe("PipView", () => {
|
||||||
let sdkContext: TestSdkContext;
|
let sdkContext: TestSdkContext;
|
||||||
let client: Mocked<MatrixClient>;
|
let client: Mocked<MatrixClient>;
|
||||||
let room: Room;
|
let room: Room;
|
||||||
|
let room2: Room;
|
||||||
let alice: RoomMember;
|
let alice: RoomMember;
|
||||||
let voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore;
|
let voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore;
|
||||||
let voiceBroadcastPreRecordingStore: VoiceBroadcastPreRecordingStore;
|
let voiceBroadcastPreRecordingStore: VoiceBroadcastPreRecordingStore;
|
||||||
|
let voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
stubClient();
|
stubClient();
|
||||||
|
@ -72,17 +78,27 @@ describe("PipView", () => {
|
||||||
room = new Room("!1:example.org", client, "@alice:example.org", {
|
room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||||
});
|
});
|
||||||
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
|
||||||
client.getRooms.mockReturnValue([room]);
|
|
||||||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||||
|
|
||||||
|
room2 = new Room("!2:example.com", client, "@alice:example.org", {
|
||||||
|
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||||
|
});
|
||||||
|
client.getRoom.mockImplementation((roomId: string) => {
|
||||||
|
if (roomId === room.roomId) return room;
|
||||||
|
if (roomId === room2.roomId) return room2;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
client.getRooms.mockReturnValue([room, room2]);
|
||||||
|
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||||
|
|
||||||
room.currentState.setStateEvents([
|
room.currentState.setStateEvents([
|
||||||
mkRoomCreateEvent(alice.userId, room.roomId),
|
mkRoomCreateEvent(alice.userId, room.roomId),
|
||||||
]);
|
]);
|
||||||
jest.spyOn(room, "getMember").mockImplementation(userId => userId === alice.userId ? alice : null);
|
jest.spyOn(room, "getMember").mockImplementation(userId => userId === alice.userId ? alice : null);
|
||||||
|
|
||||||
client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null);
|
room2.currentState.setStateEvents([
|
||||||
client.getRooms.mockReturnValue([room]);
|
mkRoomCreateEvent(alice.userId, room2.roomId),
|
||||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
]);
|
||||||
|
|
||||||
await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map(
|
await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map(
|
||||||
store => setupAsyncStoreWithClient(store, client),
|
store => setupAsyncStoreWithClient(store, client),
|
||||||
|
@ -91,9 +107,11 @@ describe("PipView", () => {
|
||||||
sdkContext = new TestSdkContext();
|
sdkContext = new TestSdkContext();
|
||||||
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
||||||
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
||||||
|
voiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore();
|
||||||
sdkContext.client = client;
|
sdkContext.client = client;
|
||||||
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
||||||
sdkContext._VoiceBroadcastPreRecordingStore = voiceBroadcastPreRecordingStore;
|
sdkContext._VoiceBroadcastPreRecordingStore = voiceBroadcastPreRecordingStore;
|
||||||
|
sdkContext._VoiceBroadcastPlaybacksStore = voiceBroadcastPlaybacksStore;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -146,15 +164,18 @@ describe("PipView", () => {
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget("1", room.roomId);
|
ActiveWidgetStore.instance.destroyPersistentWidget("1", room.roomId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setUpVoiceBroadcastRecording = () => {
|
const makeVoiceBroadcastInfoStateEvent = (): MatrixEvent => {
|
||||||
const voiceBroadcastInfoEvent = mkVoiceBroadcastInfoStateEvent(
|
return mkVoiceBroadcastInfoStateEvent(
|
||||||
room.roomId,
|
room.roomId,
|
||||||
VoiceBroadcastInfoState.Started,
|
VoiceBroadcastInfoState.Started,
|
||||||
alice.userId,
|
alice.userId,
|
||||||
client.getDeviceId() || "",
|
client.getDeviceId() || "",
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client);
|
const setUpVoiceBroadcastRecording = () => {
|
||||||
|
const infoEvent = makeVoiceBroadcastInfoStateEvent();
|
||||||
|
const voiceBroadcastRecording = new VoiceBroadcastRecording(infoEvent, client);
|
||||||
voiceBroadcastRecordingsStore.setCurrent(voiceBroadcastRecording);
|
voiceBroadcastRecordingsStore.setCurrent(voiceBroadcastRecording);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,6 +189,22 @@ describe("PipView", () => {
|
||||||
voiceBroadcastPreRecordingStore.setCurrent(voiceBroadcastPreRecording);
|
voiceBroadcastPreRecordingStore.setCurrent(voiceBroadcastPreRecording);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setUpRoomViewStore = () => {
|
||||||
|
new RoomViewStore(defaultDispatcher, sdkContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startVoiceBroadcastPlayback = (room: Room): MatrixEvent => {
|
||||||
|
const infoEvent = makeVoiceBroadcastInfoStateEvent();
|
||||||
|
room.currentState.setStateEvents([infoEvent]);
|
||||||
|
defaultDispatcher.dispatch<IRoomStateEventsActionPayload>({
|
||||||
|
action: "MatrixActions.RoomState.events",
|
||||||
|
event: infoEvent,
|
||||||
|
state: room.currentState,
|
||||||
|
lastStateEvent: null,
|
||||||
|
}, true);
|
||||||
|
return infoEvent;
|
||||||
|
};
|
||||||
|
|
||||||
it("hides if there's no content", () => {
|
it("hides if there's no content", () => {
|
||||||
renderPip();
|
renderPip();
|
||||||
expect(screen.queryByRole("complementary")).toBeNull();
|
expect(screen.queryByRole("complementary")).toBeNull();
|
||||||
|
@ -209,7 +246,7 @@ describe("PipView", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows a persistent widget with a return button when not viewing the room", () => {
|
it("shows a persistent widget with a return button when not viewing the room", () => {
|
||||||
viewRoom("!2:example.org");
|
viewRoom(room2.roomId);
|
||||||
renderPip();
|
renderPip();
|
||||||
|
|
||||||
withWidget(() => {
|
withWidget(() => {
|
||||||
|
@ -230,7 +267,7 @@ describe("PipView", () => {
|
||||||
|
|
||||||
it("should render the voice broadcast recording PiP", () => {
|
it("should render the voice broadcast recording PiP", () => {
|
||||||
// check for the „Live“ badge
|
// check for the „Live“ badge
|
||||||
screen.getByText("Live");
|
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -242,7 +279,62 @@ describe("PipView", () => {
|
||||||
|
|
||||||
it("should render the voice broadcast pre-recording PiP", () => {
|
it("should render the voice broadcast pre-recording PiP", () => {
|
||||||
// check for the „Go live“ button
|
// check for the „Go live“ button
|
||||||
screen.getByText("Go live");
|
expect(screen.queryByText("Go live")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when viewing a room with a live voice broadcast", () => {
|
||||||
|
let startEvent: MatrixEvent | null = null;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setUpRoomViewStore();
|
||||||
|
viewRoom(room.roomId);
|
||||||
|
startEvent = startVoiceBroadcastPlayback(room);
|
||||||
|
renderPip();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the voice broadcast playback pip", () => {
|
||||||
|
// check for the „resume voice broadcast“ button
|
||||||
|
expect(screen.queryByLabelText("play voice broadcast")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the broadcast stops", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
act(() => {
|
||||||
|
const stopEvent = mkVoiceBroadcastInfoStateEvent(
|
||||||
|
room.roomId,
|
||||||
|
VoiceBroadcastInfoState.Stopped,
|
||||||
|
alice.userId,
|
||||||
|
client.getDeviceId() || "",
|
||||||
|
startEvent,
|
||||||
|
);
|
||||||
|
room.currentState.setStateEvents([stopEvent]);
|
||||||
|
defaultDispatcher.dispatch<IRoomStateEventsActionPayload>({
|
||||||
|
action: "MatrixActions.RoomState.events",
|
||||||
|
event: stopEvent,
|
||||||
|
state: room.currentState,
|
||||||
|
lastStateEvent: stopEvent,
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render the voice broadcast playback pip", () => {
|
||||||
|
// check for the „resume voice broadcast“ button
|
||||||
|
expect(screen.queryByLabelText("play voice broadcast")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and leaving the room", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
act(() => {
|
||||||
|
viewRoom(room2.roomId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render the voice broadcast playback pip", () => {
|
||||||
|
// check for the „resume voice broadcast“ button
|
||||||
|
expect(screen.queryByLabelText("play voice broadcast")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hasRoomLiveVoiceBroadcast,
|
hasRoomLiveVoiceBroadcast,
|
||||||
|
@ -29,25 +29,27 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
const roomId = "!room:example.com";
|
const roomId = "!room:example.com";
|
||||||
let client: MatrixClient;
|
let client: MatrixClient;
|
||||||
let room: Room;
|
let room: Room;
|
||||||
|
let expectedEvent: MatrixEvent | null = null;
|
||||||
|
|
||||||
const addVoiceBroadcastInfoEvent = (
|
const addVoiceBroadcastInfoEvent = (
|
||||||
state: VoiceBroadcastInfoState,
|
state: VoiceBroadcastInfoState,
|
||||||
sender: string,
|
sender: string,
|
||||||
) => {
|
): MatrixEvent => {
|
||||||
room.currentState.setStateEvents([
|
const infoEvent = mkVoiceBroadcastInfoStateEvent(
|
||||||
mkVoiceBroadcastInfoStateEvent(
|
room.roomId,
|
||||||
room.roomId,
|
state,
|
||||||
state,
|
sender,
|
||||||
sender,
|
"ASD123",
|
||||||
"ASD123",
|
);
|
||||||
),
|
room.currentState.setStateEvents([infoEvent]);
|
||||||
]);
|
return infoEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
const itShouldReturnTrueTrue = () => {
|
const itShouldReturnTrueTrue = () => {
|
||||||
it("should return true/true", () => {
|
it("should return true/true", () => {
|
||||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||||
hasBroadcast: true,
|
hasBroadcast: true,
|
||||||
|
infoEvent: expectedEvent,
|
||||||
startedByUser: true,
|
startedByUser: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -57,6 +59,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
it("should return true/false", () => {
|
it("should return true/false", () => {
|
||||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||||
hasBroadcast: true,
|
hasBroadcast: true,
|
||||||
|
infoEvent: expectedEvent,
|
||||||
startedByUser: false,
|
startedByUser: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -66,6 +69,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
it("should return false/false", () => {
|
it("should return false/false", () => {
|
||||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||||
hasBroadcast: false,
|
hasBroadcast: false,
|
||||||
|
infoEvent: null,
|
||||||
startedByUser: false,
|
startedByUser: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -76,6 +80,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
expectedEvent = null;
|
||||||
room = new Room(roomId, client, client.getUserId());
|
room = new Room(roomId, client, client.getUserId());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,7 +106,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
|
|
||||||
describe("when there is a live broadcast from the current and another user", () => {
|
describe("when there is a live broadcast from the current and another user", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, client.getUserId());
|
expectedEvent = addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, client.getUserId());
|
||||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, otherUserId);
|
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, otherUserId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,7 +129,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
VoiceBroadcastInfoState.Resumed,
|
VoiceBroadcastInfoState.Resumed,
|
||||||
])("when there is a live broadcast (%s) from the current user", (state: VoiceBroadcastInfoState) => {
|
])("when there is a live broadcast (%s) from the current user", (state: VoiceBroadcastInfoState) => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
addVoiceBroadcastInfoEvent(state, client.getUserId());
|
expectedEvent = addVoiceBroadcastInfoEvent(state, client.getUserId());
|
||||||
});
|
});
|
||||||
|
|
||||||
itShouldReturnTrueTrue();
|
itShouldReturnTrueTrue();
|
||||||
|
@ -141,7 +146,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
||||||
|
|
||||||
describe("when there is a live broadcast from another user", () => {
|
describe("when there is a live broadcast from another user", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, otherUserId);
|
expectedEvent = addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, otherUserId);
|
||||||
});
|
});
|
||||||
|
|
||||||
itShouldReturnTrueFalse();
|
itShouldReturnTrueFalse();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue