Display info dialogs if unable to start voice broadcasts (#9453)
parent
3c3df11d32
commit
bb0c175b7e
|
@ -113,7 +113,6 @@ import { isLocalRoom } from '../../utils/localRoom/isLocalRoom';
|
|||
import { ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload";
|
||||
import { RoomStatusBarUnsentMessages } from './RoomStatusBarUnsentMessages';
|
||||
import { LargeLoader } from './LargeLoader';
|
||||
import { VoiceBroadcastInfoEventType } from '../../voice-broadcast';
|
||||
import { isVideoRoom } from '../../utils/video-rooms';
|
||||
import { SDKContext } from '../../contexts/SDKContext';
|
||||
import { CallStore, CallStoreEvent } from "../../stores/CallStore";
|
||||
|
@ -199,7 +198,6 @@ export interface IRoomState {
|
|||
upgradeRecommendation?: IRecommendedVersion;
|
||||
canReact: boolean;
|
||||
canSendMessages: boolean;
|
||||
canSendVoiceBroadcasts: boolean;
|
||||
tombstone?: MatrixEvent;
|
||||
resizing: boolean;
|
||||
layout: Layout;
|
||||
|
@ -404,7 +402,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
statusBarVisible: false,
|
||||
canReact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
resizing: false,
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
lowBandwidth: SettingsStore.getValue("lowBandwidth"),
|
||||
|
@ -1377,12 +1374,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
);
|
||||
const canSendMessages = room.maySendMessage();
|
||||
const canSelfRedact = room.currentState.maySendEvent(EventType.RoomRedaction, me);
|
||||
const canSendVoiceBroadcasts = room.currentState.maySendEvent(VoiceBroadcastInfoEventType, me);
|
||||
|
||||
this.setState({
|
||||
canReact,
|
||||
canSendMessages,
|
||||
canSendVoiceBroadcasts,
|
||||
canSelfRedact,
|
||||
});
|
||||
}
|
||||
|
@ -2253,7 +2248,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
resizeNotifier={this.props.resizeNotifier}
|
||||
replyToEvent={this.state.replyToEvent}
|
||||
permalinkCreator={this.permalinkCreator}
|
||||
showVoiceBroadcastButton={this.state.canSendVoiceBroadcasts}
|
||||
/>;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ interface IProps {
|
|||
relation?: IEventRelation;
|
||||
e2eStatus?: E2EStatus;
|
||||
compact?: boolean;
|
||||
showVoiceBroadcastButton?: boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -384,10 +383,6 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
return this.state.showStickersButton && !isLocalRoom(this.props.room);
|
||||
}
|
||||
|
||||
private get showVoiceBroadcastButton(): boolean {
|
||||
return this.props.showVoiceBroadcastButton && this.state.showVoiceBroadcastButton;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const isWysiwygComposerEnabled = SettingsStore.getValue("feature_wysiwyg_composer");
|
||||
const controls = [
|
||||
|
@ -532,10 +527,10 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
showPollsButton={this.state.showPollsButton}
|
||||
showStickersButton={this.showStickersButton}
|
||||
toggleButtonMenu={this.toggleButtonMenu}
|
||||
showVoiceBroadcastButton={this.showVoiceBroadcastButton}
|
||||
showVoiceBroadcastButton={this.state.showVoiceBroadcastButton}
|
||||
onStartVoiceBroadcastClick={() => {
|
||||
startNewVoiceBroadcastRecording(
|
||||
this.props.room.roomId,
|
||||
this.props.room,
|
||||
MatrixClientPeg.get(),
|
||||
VoiceBroadcastRecordingsStore.instance(),
|
||||
);
|
||||
|
|
|
@ -45,7 +45,6 @@ const RoomContext = createContext<IRoomState>({
|
|||
canReact: false,
|
||||
canSelfRedact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
resizing: false,
|
||||
layout: Layout.Group,
|
||||
lowBandwidth: false,
|
||||
|
|
|
@ -637,6 +637,10 @@
|
|||
"Send <b>%(msgtype)s</b> messages as you in your active room": "Send <b>%(msgtype)s</b> messages as you in your active room",
|
||||
"See <b>%(msgtype)s</b> messages posted to this room": "See <b>%(msgtype)s</b> messages posted to this room",
|
||||
"See <b>%(msgtype)s</b> messages posted to your active room": "See <b>%(msgtype)s</b> messages posted to your active room",
|
||||
"Can't start a new voice broadcast": "Can't start a new voice broadcast",
|
||||
"You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.": "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.",
|
||||
"You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.": "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.",
|
||||
"Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.": "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.",
|
||||
"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.",
|
||||
"Yes, stop broadcast": "Yes, stop broadcast",
|
||||
|
|
|
@ -35,6 +35,7 @@ export * from "./components/molecules/VoiceBroadcastRecordingPip";
|
|||
export * from "./hooks/useVoiceBroadcastRecording";
|
||||
export * from "./stores/VoiceBroadcastPlaybacksStore";
|
||||
export * from "./stores/VoiceBroadcastRecordingsStore";
|
||||
export * from "./utils/hasRoomLiveVoiceBroadcast";
|
||||
export * from "./utils/shouldDisplayAsVoiceBroadcastRecordingTile";
|
||||
export * from "./utils/shouldDisplayAsVoiceBroadcastTile";
|
||||
export * from "./utils/startNewVoiceBroadcastRecording";
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
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 { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "..";
|
||||
|
||||
interface Result {
|
||||
hasBroadcast: boolean;
|
||||
startedByUser: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 startedByUser = false;
|
||||
|
||||
const stateEvents = room.currentState.getStateEvents(VoiceBroadcastInfoEventType);
|
||||
stateEvents.forEach((event: MatrixEvent) => {
|
||||
const state = event.getContent()?.state;
|
||||
|
||||
if (state && state !== VoiceBroadcastInfoState.Stopped) {
|
||||
hasBroadcast = true;
|
||||
|
||||
// state key = sender's MXID
|
||||
if (event.getStateKey() === userId) {
|
||||
startedByUser = true;
|
||||
// break here, because more than true / true is not possible
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
hasBroadcast,
|
||||
startedByUser,
|
||||
};
|
||||
};
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
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 { ISendEventResponse, MatrixClient, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { defer } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import {
|
||||
VoiceBroadcastInfoEventContent,
|
||||
VoiceBroadcastInfoEventType,
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
VoiceBroadcastRecording,
|
||||
} from "..";
|
||||
|
||||
/**
|
||||
* Starts a new Voice Broadcast Recording.
|
||||
* Sends a voice_broadcast_info state event and waits for the event to actually appear in the room state.
|
||||
*/
|
||||
export const startNewVoiceBroadcastRecording = async (
|
||||
roomId: string,
|
||||
client: MatrixClient,
|
||||
recordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): Promise<VoiceBroadcastRecording> => {
|
||||
const room = client.getRoom(roomId);
|
||||
const { promise, resolve } = defer<VoiceBroadcastRecording>();
|
||||
let result: ISendEventResponse = null;
|
||||
|
||||
const onRoomStateEvents = () => {
|
||||
if (!result) return;
|
||||
|
||||
const voiceBroadcastEvent = room.currentState.getStateEvents(
|
||||
VoiceBroadcastInfoEventType,
|
||||
client.getUserId(),
|
||||
);
|
||||
|
||||
if (voiceBroadcastEvent?.getId() === result.event_id) {
|
||||
room.off(RoomStateEvent.Events, onRoomStateEvents);
|
||||
const recording = new VoiceBroadcastRecording(
|
||||
voiceBroadcastEvent,
|
||||
client,
|
||||
);
|
||||
recordingsStore.setCurrent(recording);
|
||||
recording.start();
|
||||
resolve(recording);
|
||||
}
|
||||
};
|
||||
|
||||
room.on(RoomStateEvent.Events, onRoomStateEvents);
|
||||
|
||||
// XXX Michael W: refactor to live event
|
||||
result = await client.sendStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoEventType,
|
||||
{
|
||||
device_id: client.getDeviceId(),
|
||||
state: VoiceBroadcastInfoState.Started,
|
||||
chunk_length: 300,
|
||||
} as VoiceBroadcastInfoEventContent,
|
||||
client.getUserId(),
|
||||
);
|
||||
|
||||
return promise;
|
||||
};
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
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 React from "react";
|
||||
import { ISendEventResponse, MatrixClient, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { defer } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import { _t } from "../../languageHandler";
|
||||
import InfoDialog from "../../components/views/dialogs/InfoDialog";
|
||||
import Modal from "../../Modal";
|
||||
import {
|
||||
VoiceBroadcastInfoEventContent,
|
||||
VoiceBroadcastInfoEventType,
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
VoiceBroadcastRecording,
|
||||
hasRoomLiveVoiceBroadcast,
|
||||
} from "..";
|
||||
|
||||
const startBroadcast = async (
|
||||
room: Room,
|
||||
client: MatrixClient,
|
||||
recordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): Promise<VoiceBroadcastRecording> => {
|
||||
const { promise, resolve } = defer<VoiceBroadcastRecording>();
|
||||
let result: ISendEventResponse = null;
|
||||
|
||||
const onRoomStateEvents = () => {
|
||||
if (!result) return;
|
||||
|
||||
const voiceBroadcastEvent = room.currentState.getStateEvents(
|
||||
VoiceBroadcastInfoEventType,
|
||||
client.getUserId(),
|
||||
);
|
||||
|
||||
if (voiceBroadcastEvent?.getId() === result.event_id) {
|
||||
room.off(RoomStateEvent.Events, onRoomStateEvents);
|
||||
const recording = new VoiceBroadcastRecording(
|
||||
voiceBroadcastEvent,
|
||||
client,
|
||||
);
|
||||
recordingsStore.setCurrent(recording);
|
||||
recording.start();
|
||||
resolve(recording);
|
||||
}
|
||||
};
|
||||
|
||||
room.on(RoomStateEvent.Events, onRoomStateEvents);
|
||||
|
||||
// XXX Michael W: refactor to live event
|
||||
result = await client.sendStateEvent(
|
||||
room.roomId,
|
||||
VoiceBroadcastInfoEventType,
|
||||
{
|
||||
device_id: client.getDeviceId(),
|
||||
state: VoiceBroadcastInfoState.Started,
|
||||
chunk_length: 300,
|
||||
} as VoiceBroadcastInfoEventContent,
|
||||
client.getUserId(),
|
||||
);
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
const showAlreadyRecordingDialog = () => {
|
||||
Modal.createDialog(InfoDialog, {
|
||||
title: _t("Can't start a new voice broadcast"),
|
||||
description: <p>{ _t("You are already recording a voice broadcast. "
|
||||
+ "Please end your current voice broadcast to start a new one.") }</p>,
|
||||
hasCloseButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
const showInsufficientPermissionsDialog = () => {
|
||||
Modal.createDialog(InfoDialog, {
|
||||
title: _t("Can't start a new voice broadcast"),
|
||||
description: <p>{ _t("You don't have the required permissions to start a voice broadcast in this room. "
|
||||
+ "Contact a room administrator to upgrade your permissions.") }</p>,
|
||||
hasCloseButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
const showOthersAlreadyRecordingDialog = () => {
|
||||
Modal.createDialog(InfoDialog, {
|
||||
title: _t("Can't start a new voice broadcast"),
|
||||
description: <p>{ _t("Someone else is already recording a voice broadcast. "
|
||||
+ "Wait for their voice broadcast to end to start a new one.") }</p>,
|
||||
hasCloseButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts a new Voice Broadcast Recording, if
|
||||
* - the user has the permissions to do so in the room
|
||||
* - there is no other broadcast being recorded in the room, yet
|
||||
* Sends a voice_broadcast_info state event and waits for the event to actually appear in the room state.
|
||||
*/
|
||||
export const startNewVoiceBroadcastRecording = async (
|
||||
room: Room,
|
||||
client: MatrixClient,
|
||||
recordingsStore: VoiceBroadcastRecordingsStore,
|
||||
): Promise<VoiceBroadcastRecording | null> => {
|
||||
const currentUserId = client.getUserId();
|
||||
|
||||
if (!room.currentState.maySendStateEvent(VoiceBroadcastInfoEventType, currentUserId)) {
|
||||
showInsufficientPermissionsDialog();
|
||||
return null;
|
||||
}
|
||||
|
||||
const { hasBroadcast, startedByUser } = hasRoomLiveVoiceBroadcast(room, currentUserId);
|
||||
|
||||
if (hasBroadcast && startedByUser) {
|
||||
showAlreadyRecordingDialog();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasBroadcast) {
|
||||
showOthersAlreadyRecordingDialog();
|
||||
return null;
|
||||
}
|
||||
|
||||
return startBroadcast(room, client, recordingsStore);
|
||||
};
|
|
@ -147,7 +147,7 @@ describe("MessageComposer", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value);
|
||||
wrapper = wrapAndRender({ room, showVoiceBroadcastButton: true });
|
||||
wrapper = wrapAndRender({ room });
|
||||
});
|
||||
|
||||
it(`should pass the prop ${prop} = ${value}`, () => {
|
||||
|
@ -174,17 +174,6 @@ describe("MessageComposer", () => {
|
|||
});
|
||||
});
|
||||
|
||||
[false, undefined].forEach((value) => {
|
||||
it(`should pass showVoiceBroadcastButton = false if the MessageComposer prop is ${value}`, () => {
|
||||
SettingsStore.setValue(Features.VoiceBroadcast, null, SettingLevel.DEVICE, true);
|
||||
const wrapper = wrapAndRender({
|
||||
room,
|
||||
showVoiceBroadcastButton: value,
|
||||
});
|
||||
expect(wrapper.find(MessageComposerButtons).props().showVoiceBroadcastButton).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the send button", () => {
|
||||
const wrapper = wrapAndRender({ room });
|
||||
expect(wrapper.find("SendButton")).toHaveLength(0);
|
||||
|
|
|
@ -250,7 +250,6 @@ function createRoomState(room: Room, narrow: boolean): IRoomState {
|
|||
statusBarVisible: false,
|
||||
canReact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
layout: Layout.Group,
|
||||
lowBandwidth: false,
|
||||
alwaysShowTimestamps: false,
|
||||
|
|
|
@ -72,7 +72,6 @@ describe('<SendMessageComposer/>', () => {
|
|||
statusBarVisible: false,
|
||||
canReact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
layout: Layout.Group,
|
||||
lowBandwidth: false,
|
||||
alwaysShowTimestamps: false,
|
||||
|
|
|
@ -91,7 +91,6 @@ describe('WysiwygComposer', () => {
|
|||
statusBarVisible: false,
|
||||
canReact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
layout: Layout.Group,
|
||||
lowBandwidth: false,
|
||||
alwaysShowTimestamps: false,
|
||||
|
|
|
@ -123,7 +123,6 @@ describe('message', () => {
|
|||
statusBarVisible: false,
|
||||
canReact: false,
|
||||
canSendMessages: false,
|
||||
canSendVoiceBroadcasts: false,
|
||||
layout: Layout.Group,
|
||||
lowBandwidth: false,
|
||||
alwaysShowTimestamps: false,
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`startNewVoiceBroadcastRecording when the current user is allowed to send voice broadcast info state events when there already is a live broadcast of another user should show an info dialog 1`] = `
|
||||
[MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
[Function],
|
||||
Object {
|
||||
"description": <p>
|
||||
Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.
|
||||
</p>,
|
||||
"hasCloseButton": true,
|
||||
"title": "Can't start a new voice broadcast",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`startNewVoiceBroadcastRecording when the current user is allowed to send voice broadcast info state events when there already is a live broadcast of the current user should show an info dialog 1`] = `
|
||||
[MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
[Function],
|
||||
Object {
|
||||
"description": <p>
|
||||
You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.
|
||||
</p>,
|
||||
"hasCloseButton": true,
|
||||
"title": "Can't start a new voice broadcast",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`startNewVoiceBroadcastRecording when the current user is not allowed to send voice broadcast info state events should show an info dialog 1`] = `
|
||||
[MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
[Function],
|
||||
Object {
|
||||
"description": <p>
|
||||
You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.
|
||||
</p>,
|
||||
"hasCloseButton": true,
|
||||
"title": "Can't start a new voice broadcast",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
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,
|
||||
VoiceBroadcastInfoEventType,
|
||||
VoiceBroadcastInfoState,
|
||||
} from "../../../src/voice-broadcast";
|
||||
import { mkEvent, stubClient } from "../../test-utils";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "./test-utils";
|
||||
|
||||
describe("hasRoomLiveVoiceBroadcast", () => {
|
||||
const otherUserId = "@other:example.com";
|
||||
const roomId = "!room:example.com";
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
const addVoiceBroadcastInfoEvent = (
|
||||
state: VoiceBroadcastInfoState,
|
||||
sender: string,
|
||||
) => {
|
||||
room.currentState.setStateEvents([
|
||||
mkVoiceBroadcastInfoStateEvent(room.roomId, state, sender),
|
||||
]);
|
||||
};
|
||||
|
||||
const itShouldReturnTrueTrue = () => {
|
||||
it("should return true/true", () => {
|
||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||
hasBroadcast: true,
|
||||
startedByUser: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const itShouldReturnTrueFalse = () => {
|
||||
it("should return true/false", () => {
|
||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||
hasBroadcast: true,
|
||||
startedByUser: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const itShouldReturnFalseFalse = () => {
|
||||
it("should return false/false", () => {
|
||||
expect(hasRoomLiveVoiceBroadcast(room, client.getUserId())).toEqual({
|
||||
hasBroadcast: false,
|
||||
startedByUser: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
client = stubClient();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
room = new Room(roomId, client, client.getUserId());
|
||||
});
|
||||
|
||||
describe("when there is no voice broadcast info at all", () => {
|
||||
itShouldReturnFalseFalse();
|
||||
});
|
||||
|
||||
describe("when the »state« prop is missing", () => {
|
||||
beforeEach(() => {
|
||||
room.currentState.setStateEvents([
|
||||
mkEvent({
|
||||
event: true,
|
||||
room: room.roomId,
|
||||
user: client.getUserId(),
|
||||
type: VoiceBroadcastInfoEventType,
|
||||
skey: client.getUserId(),
|
||||
content: {},
|
||||
}),
|
||||
]);
|
||||
});
|
||||
itShouldReturnFalseFalse();
|
||||
});
|
||||
|
||||
describe("when there is a live broadcast from the current and another user", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, client.getUserId());
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Started, otherUserId);
|
||||
});
|
||||
|
||||
itShouldReturnTrueTrue();
|
||||
});
|
||||
|
||||
describe("when there are only stopped info events", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped, client.getUserId());
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped, otherUserId);
|
||||
});
|
||||
|
||||
itShouldReturnFalseFalse();
|
||||
});
|
||||
|
||||
describe.each([
|
||||
// all there are kind of live states
|
||||
VoiceBroadcastInfoState.Started,
|
||||
VoiceBroadcastInfoState.Paused,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
])("when there is a live broadcast (%s) from the current user", (state: VoiceBroadcastInfoState) => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(state, client.getUserId());
|
||||
});
|
||||
|
||||
itShouldReturnTrueTrue();
|
||||
});
|
||||
|
||||
describe("when there was a live broadcast, that has been stopped", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, client.getUserId());
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped, client.getUserId());
|
||||
});
|
||||
|
||||
itShouldReturnFalseFalse();
|
||||
});
|
||||
|
||||
describe("when there is a live broadcast from another user", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, otherUserId);
|
||||
});
|
||||
|
||||
itShouldReturnTrueFalse();
|
||||
});
|
||||
});
|
|
@ -15,8 +15,9 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { EventType, MatrixClient, MatrixEvent, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import Modal from "../../../src/Modal";
|
||||
import {
|
||||
startNewVoiceBroadcastRecording,
|
||||
VoiceBroadcastInfoEventType,
|
||||
|
@ -25,46 +26,29 @@ import {
|
|||
VoiceBroadcastRecording,
|
||||
} from "../../../src/voice-broadcast";
|
||||
import { mkEvent, stubClient } from "../../test-utils";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "./test-utils";
|
||||
|
||||
jest.mock("../../../src/voice-broadcast/models/VoiceBroadcastRecording", () => ({
|
||||
VoiceBroadcastRecording: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../src/Modal");
|
||||
|
||||
describe("startNewVoiceBroadcastRecording", () => {
|
||||
const roomId = "!room:example.com";
|
||||
const otherUserId = "@other:example.com";
|
||||
let client: MatrixClient;
|
||||
let recordingsStore: VoiceBroadcastRecordingsStore;
|
||||
let room: Room;
|
||||
let roomOnStateEventsCallbackRegistered: Promise<void>;
|
||||
let roomOnStateEventsCallbackRegisteredResolver: Function;
|
||||
let roomOnStateEventsCallback: () => void;
|
||||
let infoEvent: MatrixEvent;
|
||||
let otherEvent: MatrixEvent;
|
||||
let stateEvent: MatrixEvent;
|
||||
let result: VoiceBroadcastRecording | null;
|
||||
|
||||
beforeEach(() => {
|
||||
roomOnStateEventsCallbackRegistered = new Promise((resolve) => {
|
||||
roomOnStateEventsCallbackRegisteredResolver = resolve;
|
||||
});
|
||||
|
||||
room = {
|
||||
currentState: {
|
||||
getStateEvents: jest.fn().mockImplementation((type, userId) => {
|
||||
if (type === VoiceBroadcastInfoEventType && userId === client.getUserId()) {
|
||||
return stateEvent;
|
||||
}
|
||||
}),
|
||||
},
|
||||
on: jest.fn().mockImplementation((eventType, callback) => {
|
||||
if (eventType === RoomStateEvent.Events) {
|
||||
roomOnStateEventsCallback = callback;
|
||||
roomOnStateEventsCallbackRegisteredResolver();
|
||||
}
|
||||
}),
|
||||
off: jest.fn(),
|
||||
} as unknown as Room;
|
||||
|
||||
client = stubClient();
|
||||
room = new Room(roomId, client, client.getUserId());
|
||||
jest.spyOn(room.currentState, "maySendStateEvent");
|
||||
|
||||
mocked(client.getRoom).mockImplementation((getRoomId: string) => {
|
||||
if (getRoomId === roomId) {
|
||||
return room;
|
||||
|
@ -85,22 +69,14 @@ describe("startNewVoiceBroadcastRecording", () => {
|
|||
setCurrent: jest.fn(),
|
||||
} as unknown as VoiceBroadcastRecordingsStore;
|
||||
|
||||
infoEvent = mkEvent({
|
||||
event: true,
|
||||
type: VoiceBroadcastInfoEventType,
|
||||
content: {
|
||||
device_id: client.getDeviceId(),
|
||||
state: VoiceBroadcastInfoState.Started,
|
||||
},
|
||||
user: client.getUserId(),
|
||||
room: roomId,
|
||||
});
|
||||
infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, client.getUserId());
|
||||
otherEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMember,
|
||||
content: {},
|
||||
user: client.getUserId(),
|
||||
room: roomId,
|
||||
skey: "",
|
||||
});
|
||||
|
||||
mocked(VoiceBroadcastRecording).mockImplementation((
|
||||
|
@ -115,29 +91,96 @@ describe("startNewVoiceBroadcastRecording", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should create a new Voice Broadcast", (done) => {
|
||||
let ok = false;
|
||||
|
||||
startNewVoiceBroadcastRecording(roomId, client, recordingsStore).then((recording) => {
|
||||
expect(ok).toBe(true);
|
||||
expect(mocked(room.off)).toHaveBeenCalledWith(RoomStateEvent.Events, roomOnStateEventsCallback);
|
||||
expect(recording.infoEvent).toBe(infoEvent);
|
||||
expect(recording.start).toHaveBeenCalled();
|
||||
done();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
roomOnStateEventsCallbackRegistered.then(() => {
|
||||
// no state event, yet
|
||||
roomOnStateEventsCallback();
|
||||
describe("when the current user is allowed to send voice broadcast info state events", () => {
|
||||
beforeEach(() => {
|
||||
mocked(room.currentState.maySendStateEvent).mockReturnValue(true);
|
||||
});
|
||||
|
||||
// other state event
|
||||
stateEvent = otherEvent;
|
||||
roomOnStateEventsCallback();
|
||||
describe("when there currently is no other broadcast", () => {
|
||||
it("should create a new Voice Broadcast", async () => {
|
||||
mocked(client.sendStateEvent).mockImplementation(async (
|
||||
_roomId: string,
|
||||
_eventType: string,
|
||||
_content: any,
|
||||
_stateKey = "",
|
||||
) => {
|
||||
setTimeout(() => {
|
||||
// emit state events after resolving the promise
|
||||
room.currentState.setStateEvents([otherEvent]);
|
||||
room.currentState.setStateEvents([infoEvent]);
|
||||
}, 0);
|
||||
return { event_id: infoEvent.getId() };
|
||||
});
|
||||
const recording = await startNewVoiceBroadcastRecording(room, client, recordingsStore);
|
||||
|
||||
// the expected Voice Broadcast Info event
|
||||
stateEvent = infoEvent;
|
||||
ok = true;
|
||||
roomOnStateEventsCallback();
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
VoiceBroadcastInfoEventType,
|
||||
{
|
||||
chunk_length: 300,
|
||||
device_id: client.getDeviceId(),
|
||||
state: VoiceBroadcastInfoState.Started,
|
||||
},
|
||||
client.getUserId(),
|
||||
);
|
||||
expect(recording.infoEvent).toBe(infoEvent);
|
||||
expect(recording.start).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there already is a live broadcast of the current user", () => {
|
||||
beforeEach(async () => {
|
||||
room.currentState.setStateEvents([
|
||||
mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Running, client.getUserId()),
|
||||
]);
|
||||
|
||||
result = await startNewVoiceBroadcastRecording(room, client, recordingsStore);
|
||||
});
|
||||
|
||||
it("should not start a voice broadcast", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should show an info dialog", () => {
|
||||
expect(Modal.createDialog).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there already is a live broadcast of another user", () => {
|
||||
beforeEach(async () => {
|
||||
room.currentState.setStateEvents([
|
||||
mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Running, otherUserId),
|
||||
]);
|
||||
|
||||
result = await startNewVoiceBroadcastRecording(room, client, recordingsStore);
|
||||
});
|
||||
|
||||
it("should not start a voice broadcast", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should show an info dialog", () => {
|
||||
expect(Modal.createDialog).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the current user is not allowed to send voice broadcast info state events", () => {
|
||||
beforeEach(async () => {
|
||||
mocked(room.currentState.maySendStateEvent).mockReturnValue(false);
|
||||
result = await startNewVoiceBroadcastRecording(room, client, recordingsStore);
|
||||
});
|
||||
|
||||
it("should not start a voice broadcast", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should show an info dialog", () => {
|
||||
expect(Modal.createDialog).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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 { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../src/voice-broadcast";
|
||||
import { mkEvent } from "../../test-utils";
|
||||
|
||||
export const mkVoiceBroadcastInfoStateEvent = (
|
||||
roomId: string,
|
||||
state: VoiceBroadcastInfoState,
|
||||
sender: string,
|
||||
): MatrixEvent => {
|
||||
return mkEvent({
|
||||
event: true,
|
||||
room: roomId,
|
||||
user: sender,
|
||||
type: VoiceBroadcastInfoEventType,
|
||||
skey: sender,
|
||||
content: {
|
||||
state,
|
||||
},
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue