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

Commit cd80642

Browse files
authored
Add MessageEvent voice broadcast setting watch (#9399)
1 parent 7b55b89 commit cd80642

File tree

2 files changed

+170
-2
lines changed

2 files changed

+170
-2
lines changed

src/components/views/messages/MessageEvent.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import MjolnirBody from "./MjolnirBody";
4343
import MBeaconBody from "./MBeaconBody";
4444
import { IEventTileOps } from "../rooms/EventTile";
4545
import { VoiceBroadcastBody, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from '../../../voice-broadcast';
46+
import { Features } from '../../../settings/Settings';
47+
import { SettingLevel } from '../../../settings/SettingLevel';
4648

4749
// onMessageAllowed is handled internally
4850
interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper"> {
@@ -60,6 +62,10 @@ export interface IOperableEventTile {
6062
getEventTileOps(): IEventTileOps;
6163
}
6264

65+
interface State {
66+
voiceBroadcastEnabled: boolean;
67+
}
68+
6369
const baseBodyTypes = new Map<string, typeof React.Component>([
6470
[MsgType.Text, TextualBody],
6571
[MsgType.Notice, TextualBody],
@@ -78,14 +84,15 @@ const baseEvTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>([
7884
[VoiceBroadcastInfoEventType, VoiceBroadcastBody],
7985
]);
8086

81-
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
87+
export default class MessageEvent extends React.Component<IProps, State> implements IMediaBody, IOperableEventTile {
8288
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
8389
private mediaHelper: MediaEventHelper;
8490
private bodyTypes = new Map<string, typeof React.Component>(baseBodyTypes.entries());
8591
private evTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>(baseEvTypes.entries());
8692

8793
public static contextType = MatrixClientContext;
8894
public context!: React.ContextType<typeof MatrixClientContext>;
95+
private voiceBroadcastSettingWatcherRef: string;
8996

9097
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
9198
super(props, context);
@@ -95,15 +102,29 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
95102
}
96103

97104
this.updateComponentMaps();
105+
106+
this.state = {
107+
// only check voice broadcast settings for a voice broadcast event
108+
voiceBroadcastEnabled: this.props.mxEvent.getType() === VoiceBroadcastInfoEventType
109+
&& SettingsStore.getValue(Features.VoiceBroadcast),
110+
};
98111
}
99112

100113
public componentDidMount(): void {
101114
this.props.mxEvent.addListener(MatrixEventEvent.Decrypted, this.onDecrypted);
115+
116+
if (this.props.mxEvent.getType() === VoiceBroadcastInfoEventType) {
117+
this.watchVoiceBroadcastFeatureSetting();
118+
}
102119
}
103120

104121
public componentWillUnmount() {
105122
this.props.mxEvent.removeListener(MatrixEventEvent.Decrypted, this.onDecrypted);
106123
this.mediaHelper?.destroy();
124+
125+
if (this.voiceBroadcastSettingWatcherRef) {
126+
SettingsStore.unwatchSetting(this.voiceBroadcastSettingWatcherRef);
127+
}
107128
}
108129

109130
public componentDidUpdate(prevProps: Readonly<IProps>) {
@@ -147,6 +168,16 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
147168
this.forceUpdate();
148169
};
149170

171+
private watchVoiceBroadcastFeatureSetting(): void {
172+
this.voiceBroadcastSettingWatcherRef = SettingsStore.watchSetting(
173+
Features.VoiceBroadcast,
174+
null,
175+
(settingName: string, roomId: string, atLevel: SettingLevel, newValAtLevel, newValue: boolean) => {
176+
this.setState({ voiceBroadcastEnabled: newValue });
177+
},
178+
);
179+
}
180+
150181
public render() {
151182
const content = this.props.mxEvent.getContent();
152183
const type = this.props.mxEvent.getType();
@@ -174,7 +205,11 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
174205
BodyType = MLocationBody;
175206
}
176207

177-
if (type === VoiceBroadcastInfoEventType && content?.state === VoiceBroadcastInfoState.Started) {
208+
if (
209+
this.state.voiceBroadcastEnabled
210+
&& type === VoiceBroadcastInfoEventType
211+
&& content?.state === VoiceBroadcastInfoState.Started
212+
) {
178213
BodyType = VoiceBroadcastBody;
179214
}
180215
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 { render, RenderResult } from "@testing-library/react";
19+
import { mocked } from "jest-mock";
20+
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
21+
22+
import { Features } from "../../../../src/settings/Settings";
23+
import SettingsStore, { CallbackFn } from "../../../../src/settings/SettingsStore";
24+
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../../src/voice-broadcast";
25+
import { mkEvent, mkRoom, stubClient } from "../../../test-utils";
26+
import MessageEvent from "../../../../src/components/views/messages/MessageEvent";
27+
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
28+
29+
jest.mock("../../../../src/components/views/messages/UnknownBody", () => ({
30+
__esModule: true,
31+
default: () => (<div data-testid="unknown-body" />),
32+
}));
33+
34+
jest.mock("../../../../src/voice-broadcast/components/VoiceBroadcastBody", () => ({
35+
VoiceBroadcastBody: () => (<div data-testid="voice-broadcast-body" />),
36+
}));
37+
38+
describe("MessageEvent", () => {
39+
let room: Room;
40+
let client: MatrixClient;
41+
let event: MatrixEvent;
42+
43+
const renderMessageEvent = (): RenderResult => {
44+
return render(<MessageEvent
45+
mxEvent={event}
46+
onHeightChanged={jest.fn()}
47+
permalinkCreator={new RoomPermalinkCreator(room)}
48+
/>);
49+
};
50+
51+
beforeEach(() => {
52+
client = stubClient();
53+
room = mkRoom(client, "!room:example.com");
54+
jest.spyOn(SettingsStore, "getValue");
55+
jest.spyOn(SettingsStore, "watchSetting");
56+
jest.spyOn(SettingsStore, "unwatchSetting").mockImplementation(jest.fn());
57+
});
58+
59+
describe("when a voice broadcast start event occurs", () => {
60+
const voiceBroadcastSettingWatcherRef = "vb ref";
61+
let onVoiceBroadcastSettingChanged: CallbackFn;
62+
63+
beforeEach(() => {
64+
event = mkEvent({
65+
event: true,
66+
type: VoiceBroadcastInfoEventType,
67+
user: client.getUserId(),
68+
room: room.roomId,
69+
content: {
70+
state: VoiceBroadcastInfoState.Started,
71+
},
72+
});
73+
74+
mocked(SettingsStore.watchSetting).mockImplementation(
75+
(settingName: string, roomId: string | null, callbackFn: CallbackFn) => {
76+
if (settingName === Features.VoiceBroadcast) {
77+
onVoiceBroadcastSettingChanged = callbackFn;
78+
return voiceBroadcastSettingWatcherRef;
79+
}
80+
},
81+
);
82+
});
83+
84+
describe("and the voice broadcast feature is enabled", () => {
85+
let result: RenderResult;
86+
87+
beforeEach(() => {
88+
mocked(SettingsStore.getValue).mockImplementation((settingName: string) => {
89+
return settingName === Features.VoiceBroadcast;
90+
});
91+
result = renderMessageEvent();
92+
});
93+
94+
it("should render a VoiceBroadcast component", () => {
95+
result.getByTestId("voice-broadcast-body");
96+
});
97+
98+
describe("and switching the voice broadcast feature off", () => {
99+
beforeEach(() => {
100+
onVoiceBroadcastSettingChanged(Features.VoiceBroadcast, null, null, null, false);
101+
});
102+
103+
it("should render an UnknownBody component", () => {
104+
const result = renderMessageEvent();
105+
result.getByTestId("unknown-body");
106+
});
107+
});
108+
109+
describe("and unmounted", () => {
110+
beforeEach(() => {
111+
result.unmount();
112+
});
113+
114+
it("should unregister the settings watcher", () => {
115+
expect(SettingsStore.unwatchSetting).toHaveBeenCalled();
116+
});
117+
});
118+
});
119+
120+
describe("and the voice broadcast feature is disabled", () => {
121+
beforeEach(() => {
122+
mocked(SettingsStore.getValue).mockImplementation((settingName: string) => {
123+
return false;
124+
});
125+
});
126+
127+
it("should render an UnknownBody component", () => {
128+
const result = renderMessageEvent();
129+
result.getByTestId("unknown-body");
130+
});
131+
});
132+
});
133+
});

0 commit comments

Comments
 (0)