Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0cc4f4e

Browse files
authored
Add voice broadcast permissions (#9284)
* Add Voice Broadcast labs setting and composer button * Implement strict typing * Extend MessageComposer-test * Extend tests * Revert some strict type fixex * Implement voice broadcast permissions * Update variable casing
1 parent cb735c9 commit 0cc4f4e

File tree

10 files changed

+123
-3
lines changed

10 files changed

+123
-3
lines changed

src/components/structures/RoomView.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ import { isLocalRoom } from '../../utils/localRoom/isLocalRoom';
119119
import { ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload";
120120
import { RoomStatusBarUnsentMessages } from './RoomStatusBarUnsentMessages';
121121
import { LargeLoader } from './LargeLoader';
122+
import { VoiceBroadcastInfoEventType } from '../../voice-broadcast';
122123
import { isVideoRoom } from '../../utils/video-rooms';
123124

124125
const DEBUG = false;
@@ -200,6 +201,7 @@ export interface IRoomState {
200201
upgradeRecommendation?: IRecommendedVersion;
201202
canReact: boolean;
202203
canSendMessages: boolean;
204+
canSendVoiceBroadcasts: boolean;
203205
tombstone?: MatrixEvent;
204206
resizing: boolean;
205207
layout: Layout;
@@ -402,6 +404,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
402404
statusBarVisible: false,
403405
canReact: false,
404406
canSendMessages: false,
407+
canSendVoiceBroadcasts: false,
405408
resizing: false,
406409
layout: SettingsStore.getValue("layout"),
407410
lowBandwidth: SettingsStore.getValue("lowBandwidth"),
@@ -1346,8 +1349,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
13461349
);
13471350
const canSendMessages = room.maySendMessage();
13481351
const canSelfRedact = room.currentState.maySendEvent(EventType.RoomRedaction, me);
1352+
const canSendVoiceBroadcasts = room.currentState.maySendEvent(VoiceBroadcastInfoEventType, me);
13491353

1350-
this.setState({ canReact, canSendMessages, canSelfRedact });
1354+
this.setState({
1355+
canReact,
1356+
canSendMessages,
1357+
canSendVoiceBroadcasts,
1358+
canSelfRedact,
1359+
});
13511360
}
13521361
}
13531362

@@ -2220,6 +2229,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
22202229
resizeNotifier={this.props.resizeNotifier}
22212230
replyToEvent={this.state.replyToEvent}
22222231
permalinkCreator={this.permalinkCreator}
2232+
showVoiceBroadcastButton={this.state.canSendVoiceBroadcasts}
22232233
/>;
22242234
}
22252235

src/components/views/rooms/MessageComposer.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ interface IProps {
7979
relation?: IEventRelation;
8080
e2eStatus?: E2EStatus;
8181
compact?: boolean;
82+
showVoiceBroadcastButton?: boolean;
8283
}
8384

8485
interface IState {
@@ -107,6 +108,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
107108

108109
public static defaultProps = {
109110
compact: false,
111+
showVoiceBroadcastButton: false,
110112
};
111113

112114
public constructor(props: IProps) {
@@ -368,6 +370,10 @@ export default class MessageComposer extends React.Component<IProps, IState> {
368370
return this.state.showStickersButton && !isLocalRoom(this.props.room);
369371
}
370372

373+
private get showVoiceBroadcastButton(): boolean {
374+
return this.props.showVoiceBroadcastButton && this.state.showVoiceBroadcastButton;
375+
}
376+
371377
public render() {
372378
const controls = [
373379
this.props.e2eStatus ?
@@ -495,7 +501,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
495501
showPollsButton={this.state.showPollsButton}
496502
showStickersButton={this.showStickersButton}
497503
toggleButtonMenu={this.toggleButtonMenu}
498-
showVoiceBroadcastButton={this.state.showVoiceBroadcastButton}
504+
showVoiceBroadcastButton={this.showVoiceBroadcastButton}
499505
onStartVoiceBroadcastClick={() => {
500506
// Sends a voice message. To be replaced by voice broadcast during development.
501507
this.voiceRecordingButton.current?.onRecordStartEndClick();

src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import ErrorDialog from '../../../dialogs/ErrorDialog';
3030
import PowerSelector from "../../../elements/PowerSelector";
3131
import SettingsFieldset from '../../SettingsFieldset';
3232
import SettingsStore from "../../../../../settings/SettingsStore";
33+
import { VoiceBroadcastInfoEventType } from '../../../../../voice-broadcast';
3334

3435
interface IEventShowOpts {
3536
isState?: boolean;
@@ -61,6 +62,7 @@ const plEventsToShow: Record<string, IEventShowOpts> = {
6162

6263
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
6364
"im.vector.modular.widgets": { isState: true, hideForSpace: true },
65+
[VoiceBroadcastInfoEventType]: { isState: true, hideForSpace: true },
6466
};
6567

6668
// parse a string as an integer; if the input is undefined, or cannot be parsed
@@ -244,6 +246,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
244246

245247
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
246248
"im.vector.modular.widgets": isSpaceRoom ? null : _td("Modify widgets"),
249+
[VoiceBroadcastInfoEventType]: _td("Voice broadcasts"),
247250
};
248251

249252
if (SettingsStore.getValue("feature_pinning")) {

src/contexts/RoomContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const RoomContext = createContext<IRoomState>({
4545
canReact: false,
4646
canSelfRedact: false,
4747
canSendMessages: false,
48+
canSendVoiceBroadcasts: false,
4849
resizing: false,
4950
layout: Layout.Group,
5051
lowBandwidth: false,

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,7 @@
16421642
"Send reactions": "Send reactions",
16431643
"Remove messages sent by me": "Remove messages sent by me",
16441644
"Modify widgets": "Modify widgets",
1645+
"Voice broadcasts": "Voice broadcasts",
16451646
"Manage pinned events": "Manage pinned events",
16461647
"Default role": "Default role",
16471648
"Send messages": "Send messages",

src/voice-broadcast/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export const VoiceBroadcastInfoEventType = "io.element.voice_broadcast_info";

test/components/views/rooms/MessageComposer-test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe("MessageComposer", () => {
137137

138138
beforeEach(() => {
139139
SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value);
140-
wrapper = wrapAndRender({ room });
140+
wrapper = wrapAndRender({ room, showVoiceBroadcastButton: true });
141141
});
142142

143143
it(`should pass the prop ${prop} = ${value}`, () => {
@@ -164,6 +164,17 @@ describe("MessageComposer", () => {
164164
});
165165
});
166166

167+
[false, undefined].forEach((value) => {
168+
it(`should pass showVoiceBroadcastButton = false if the MessageComposer prop is ${value}`, () => {
169+
SettingsStore.setValue(Features.VoiceBroadcast, null, SettingLevel.DEVICE, true);
170+
const wrapper = wrapAndRender({
171+
room,
172+
showVoiceBroadcastButton: value,
173+
});
174+
expect(wrapper.find(MessageComposerButtons).props().showVoiceBroadcastButton).toBe(false);
175+
});
176+
});
177+
167178
it("should not render the send button", () => {
168179
const wrapper = wrapAndRender({ room });
169180
expect(wrapper.find("SendButton")).toHaveLength(0);

test/components/views/rooms/MessageComposerButtons-test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ function createRoomState(room: Room, narrow: boolean): IRoomState {
250250
statusBarVisible: false,
251251
canReact: false,
252252
canSendMessages: false,
253+
canSendVoiceBroadcasts: false,
253254
layout: Layout.Group,
254255
lowBandwidth: false,
255256
alwaysShowTimestamps: false,

test/components/views/rooms/SendMessageComposer-test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ describe('<SendMessageComposer/>', () => {
7272
statusBarVisible: false,
7373
canReact: false,
7474
canSendMessages: false,
75+
canSendVoiceBroadcasts: false,
7576
layout: Layout.Group,
7677
lowBandwidth: false,
7778
alwaysShowTimestamps: false,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from "react";
18+
import { fireEvent, render, RenderResult } from "@testing-library/react";
19+
import { EventType, MatrixClient } from "matrix-js-sdk/src/matrix";
20+
21+
import RolesRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab";
22+
import { mkStubRoom, stubClient } from "../../../../../test-utils";
23+
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
24+
import { VoiceBroadcastInfoEventType } from "../../../../../../src/voice-broadcast";
25+
26+
describe("RolesRoomSettingsTab", () => {
27+
const roomId = "!room:example.com";
28+
let rolesRoomSettingsTab: RenderResult;
29+
let cli: MatrixClient;
30+
31+
const getVoiceBroadcastsSelect = () => {
32+
return rolesRoomSettingsTab.container.querySelector("select[label='Voice broadcasts']");
33+
};
34+
35+
const getVoiceBroadcastsSelectedOption = () => {
36+
return rolesRoomSettingsTab.container.querySelector("select[label='Voice broadcasts'] option:checked");
37+
};
38+
39+
beforeEach(() => {
40+
stubClient();
41+
cli = MatrixClientPeg.get();
42+
rolesRoomSettingsTab = render(<RolesRoomSettingsTab roomId={roomId} />);
43+
mkStubRoom(roomId, "test room", cli);
44+
});
45+
46+
it("should initially show »Moderator« permission for »Voice broadcasts«", () => {
47+
expect(getVoiceBroadcastsSelectedOption().textContent).toBe("Moderator");
48+
});
49+
50+
describe("when setting »Default« permission for »Voice broadcasts«", () => {
51+
beforeEach(() => {
52+
fireEvent.change(getVoiceBroadcastsSelect(), {
53+
target: { value: 0 },
54+
});
55+
});
56+
57+
it("should update the power levels", () => {
58+
expect(cli.sendStateEvent).toHaveBeenCalledWith(
59+
roomId,
60+
EventType.RoomPowerLevels,
61+
{
62+
events: {
63+
[VoiceBroadcastInfoEventType]: 0,
64+
},
65+
},
66+
);
67+
});
68+
});
69+
});

0 commit comments

Comments
 (0)