Skip to content

Commit 3cafed4

Browse files
committed
Run voice recording updates through a dedicated store
1 parent b0a04c9 commit 3cafed4

File tree

3 files changed

+91
-9
lines changed

3 files changed

+91
-9
lines changed

src/components/views/rooms/MessageComposer.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {UPDATE_EVENT} from "../../../stores/AsyncStore";
3434
import ActiveWidgetStore from "../../../stores/ActiveWidgetStore";
3535
import {replaceableComponent} from "../../../utils/replaceableComponent";
3636
import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
37+
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
3738

3839
function ComposerAvatar(props) {
3940
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
@@ -180,6 +181,7 @@ export default class MessageComposer extends React.Component {
180181
this.renderPlaceholderText = this.renderPlaceholderText.bind(this);
181182
WidgetStore.instance.on(UPDATE_EVENT, this._onWidgetUpdate);
182183
ActiveWidgetStore.on('update', this._onActiveWidgetUpdate);
184+
VoiceRecordingStore.instance.on(UPDATE_EVENT, this._onVoiceStoreUpdate);
183185
this._dispatcherRef = null;
184186

185187
this.state = {
@@ -240,6 +242,7 @@ export default class MessageComposer extends React.Component {
240242
}
241243
WidgetStore.instance.removeListener(UPDATE_EVENT, this._onWidgetUpdate);
242244
ActiveWidgetStore.removeListener('update', this._onActiveWidgetUpdate);
245+
VoiceRecordingStore.instance.off(UPDATE_EVENT, this._onVoiceStoreUpdate);
243246
dis.unregister(this.dispatcherRef);
244247
}
245248

@@ -327,8 +330,8 @@ export default class MessageComposer extends React.Component {
327330
});
328331
}
329332

330-
onVoiceUpdate = (haveRecording: boolean) => {
331-
this.setState({haveRecording});
333+
_onVoiceStoreUpdate = () => {
334+
this.setState({haveRecording: !!VoiceRecordingStore.instance.activeRecording});
332335
};
333336

334337
render() {
@@ -352,7 +355,6 @@ export default class MessageComposer extends React.Component {
352355
permalinkCreator={this.props.permalinkCreator}
353356
replyToEvent={this.props.replyToEvent}
354357
onChange={this.onChange}
355-
// TODO: @@ TravisR - Disabling the composer doesn't work
356358
disabled={this.state.haveRecording}
357359
/>,
358360
);
@@ -373,8 +375,7 @@ export default class MessageComposer extends React.Component {
373375
if (SettingsStore.getValue("feature_voice_messages")) {
374376
controls.push(<VoiceRecordComposerTile
375377
key="controls_voice_record"
376-
room={this.props.room}
377-
onRecording={this.onVoiceUpdate} />);
378+
room={this.props.room} />);
378379
}
379380

380381
if (!this.state.isComposerEmpty || this.state.haveRecording) {

src/components/views/rooms/VoiceRecordComposerTile.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import classNames from "classnames";
2424
import LiveRecordingWaveform from "../voice_messages/LiveRecordingWaveform";
2525
import {replaceableComponent} from "../../../utils/replaceableComponent";
2626
import LiveRecordingClock from "../voice_messages/LiveRecordingClock";
27+
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
2728

2829
interface IProps {
2930
room: Room;
30-
onRecording: (haveRecording: boolean) => void;
3131
}
3232

3333
interface IState {
@@ -57,13 +57,12 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
5757
msgtype: "org.matrix.msc2516.voice",
5858
url: mxc,
5959
});
60+
await VoiceRecordingStore.instance.disposeRecording();
6061
this.setState({recorder: null});
61-
this.props.onRecording(false);
6262
return;
6363
}
64-
const recorder = new VoiceRecorder(MatrixClientPeg.get());
64+
const recorder = VoiceRecordingStore.instance.startRecording();
6565
await recorder.start();
66-
this.props.onRecording(true);
6766
this.setState({recorder});
6867
};
6968

src/stores/VoiceRecordingStore.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright 2021 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 {AsyncStoreWithClient} from "./AsyncStoreWithClient";
18+
import defaultDispatcher from "../dispatcher/dispatcher";
19+
import {ActionPayload} from "../dispatcher/payloads";
20+
import {VoiceRecording} from "../voice/VoiceRecording";
21+
22+
interface IState {
23+
recording?: VoiceRecording;
24+
}
25+
26+
export class VoiceRecordingStore extends AsyncStoreWithClient<IState> {
27+
private static internalInstance: VoiceRecordingStore;
28+
29+
public constructor() {
30+
super(defaultDispatcher, {});
31+
}
32+
33+
/**
34+
* Gets the active recording instance, if any.
35+
*/
36+
public get activeRecording(): VoiceRecording | null {
37+
return this.state.recording;
38+
}
39+
40+
public static get instance(): VoiceRecordingStore {
41+
if (!VoiceRecordingStore.internalInstance) {
42+
VoiceRecordingStore.internalInstance = new VoiceRecordingStore();
43+
}
44+
return VoiceRecordingStore.internalInstance;
45+
}
46+
47+
protected async onAction(payload: ActionPayload): Promise<void> {
48+
// Nothing to do, but we're required to override the function
49+
return;
50+
}
51+
52+
/**
53+
* Starts a new recording if one isn't already in progress. Note that this simply
54+
* creates a recording instance - whether or not recording is actively in progress
55+
* can be seen via the VoiceRecording class.
56+
* @returns {VoiceRecording} The recording.
57+
*/
58+
public startRecording(): VoiceRecording {
59+
if (!this.matrixClient) throw new Error("Cannot start a recording without a MatrixClient");
60+
if (this.state.recording) throw new Error("A recording is already in progress");
61+
62+
const recording = new VoiceRecording(this.matrixClient);
63+
64+
// noinspection JSIgnoredPromiseFromCall - we can safely run this async
65+
this.updateState({recording});
66+
67+
return recording;
68+
}
69+
70+
/**
71+
* Disposes of the current recording, no matter the state of it.
72+
* @returns {Promise<void>} Resolves when complete.
73+
*/
74+
public disposeRecording(): Promise<void> {
75+
if (this.state.recording) {
76+
// Stop for good measure, but completely async because we're not concerned with this
77+
// passing or failing.
78+
this.state.recording.stop().catch(e => console.error("Error stopping recording", e));
79+
}
80+
return this.updateState({recording: null});
81+
}
82+
}

0 commit comments

Comments
 (0)