diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index b7078766fb..283b11a437 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -34,6 +34,7 @@ import {UPDATE_EVENT} from "../../../stores/AsyncStore"; import ActiveWidgetStore from "../../../stores/ActiveWidgetStore"; import {replaceableComponent} from "../../../utils/replaceableComponent"; import VoiceRecordComposerTile from "./VoiceRecordComposerTile"; +import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore"; function ComposerAvatar(props) { const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); @@ -180,6 +181,7 @@ export default class MessageComposer extends React.Component { this.renderPlaceholderText = this.renderPlaceholderText.bind(this); WidgetStore.instance.on(UPDATE_EVENT, this._onWidgetUpdate); ActiveWidgetStore.on('update', this._onActiveWidgetUpdate); + VoiceRecordingStore.instance.on(UPDATE_EVENT, this._onVoiceStoreUpdate); this._dispatcherRef = null; this.state = { @@ -240,6 +242,7 @@ export default class MessageComposer extends React.Component { } WidgetStore.instance.removeListener(UPDATE_EVENT, this._onWidgetUpdate); ActiveWidgetStore.removeListener('update', this._onActiveWidgetUpdate); + VoiceRecordingStore.instance.off(UPDATE_EVENT, this._onVoiceStoreUpdate); dis.unregister(this.dispatcherRef); } @@ -327,8 +330,8 @@ export default class MessageComposer extends React.Component { }); } - onVoiceUpdate = (haveRecording: boolean) => { - this.setState({haveRecording}); + _onVoiceStoreUpdate = () => { + this.setState({haveRecording: !!VoiceRecordingStore.instance.activeRecording}); }; render() { @@ -352,7 +355,6 @@ export default class MessageComposer extends React.Component { permalinkCreator={this.props.permalinkCreator} replyToEvent={this.props.replyToEvent} onChange={this.onChange} - // TODO: @@ TravisR - Disabling the composer doesn't work disabled={this.state.haveRecording} />, ); @@ -373,8 +375,7 @@ export default class MessageComposer extends React.Component { if (SettingsStore.getValue("feature_voice_messages")) { controls.push(); + room={this.props.room} />); } if (!this.state.isComposerEmpty || this.state.haveRecording) { diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index e83aa8b994..1210a44958 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -24,10 +24,10 @@ import classNames from "classnames"; import LiveRecordingWaveform from "../voice_messages/LiveRecordingWaveform"; import {replaceableComponent} from "../../../utils/replaceableComponent"; import LiveRecordingClock from "../voice_messages/LiveRecordingClock"; +import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore"; interface IProps { room: Room; - onRecording: (haveRecording: boolean) => void; } interface IState { @@ -57,13 +57,12 @@ export default class VoiceRecordComposerTile extends React.PureComponent { + private static internalInstance: VoiceRecordingStore; + + public constructor() { + super(defaultDispatcher, {}); + } + + /** + * Gets the active recording instance, if any. + */ + public get activeRecording(): VoiceRecording | null { + return this.state.recording; + } + + public static get instance(): VoiceRecordingStore { + if (!VoiceRecordingStore.internalInstance) { + VoiceRecordingStore.internalInstance = new VoiceRecordingStore(); + } + return VoiceRecordingStore.internalInstance; + } + + protected async onAction(payload: ActionPayload): Promise { + // Nothing to do, but we're required to override the function + return; + } + + /** + * Starts a new recording if one isn't already in progress. Note that this simply + * creates a recording instance - whether or not recording is actively in progress + * can be seen via the VoiceRecording class. + * @returns {VoiceRecording} The recording. + */ + public startRecording(): VoiceRecording { + if (!this.matrixClient) throw new Error("Cannot start a recording without a MatrixClient"); + if (this.state.recording) throw new Error("A recording is already in progress"); + + const recording = new VoiceRecording(this.matrixClient); + + // noinspection JSIgnoredPromiseFromCall - we can safely run this async + this.updateState({recording}); + + return recording; + } + + /** + * Disposes of the current recording, no matter the state of it. + * @returns {Promise} Resolves when complete. + */ + public disposeRecording(): Promise { + if (this.state.recording) { + // Stop for good measure, but completely async because we're not concerned with this + // passing or failing. + this.state.recording.stop().catch(e => console.error("Error stopping recording", e)); + } + return this.updateState({recording: null}); + } +}