diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 78babc37eb..4f75b7b87e 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -227,8 +227,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent - - + + ; } diff --git a/src/components/views/voice_messages/LiveRecordingClock.tsx b/src/components/views/voice_messages/LiveRecordingClock.tsx index f88bb63ac7..6786ae36f8 100644 --- a/src/components/views/voice_messages/LiveRecordingClock.tsx +++ b/src/components/views/voice_messages/LiveRecordingClock.tsx @@ -12,15 +12,62 @@ limitations under the License. */ import React from "react"; -import Clock, { IProps as IClockProps } from "./Clock"; +import Clock from "./Clock"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { MarkedExecution } from "../../../utils/MarkedExecution"; +import { + IRecordingUpdate, + VoiceRecording, +} from "../../../voice/VoiceRecording"; + +interface IProps { + recorder?: VoiceRecording; +} + +interface IState { + seconds: number; +} /** * A clock for a live recording. */ @replaceableComponent("views.voice_messages.LiveRecordingClock") -export default class LiveRecordingClock extends React.PureComponent { +export default class LiveRecordingClock extends React.PureComponent { + private seconds = 0; + private rafId: number; + + state = { + seconds: 0, + } + + componentDidMount() { + this.props.recorder.liveData.onUpdate((update: IRecordingUpdate) => { + this.seconds = update.timeSeconds; + this.scheduledUpdate.mark(); + }); + } + + private scheduledUpdate = new MarkedExecution( + () => this.updateClock(), + () => this.onLiveDataUpdate(), + ) + + private onLiveDataUpdate() { + if (this.rafId) { + cancelAnimationFrame(this.rafId); + } + + this.rafId = requestAnimationFrame(() => this.scheduledUpdate.trigger()) + } + + private updateClock() { + this.setState({ + seconds: this.seconds, + }) + this.rafId = null; + } + public render() { - return ; + return ; } } diff --git a/src/components/views/voice_messages/LiveRecordingWaveform.tsx b/src/components/views/voice_messages/LiveRecordingWaveform.tsx index 3d3169d764..83449fddc8 100644 --- a/src/components/views/voice_messages/LiveRecordingWaveform.tsx +++ b/src/components/views/voice_messages/LiveRecordingWaveform.tsx @@ -12,18 +12,66 @@ limitations under the License. */ import React from "react"; -import Waveform, { IProps as IWaveformProps } from "./Waveform"; +import Waveform from "./Waveform"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { MarkedExecution } from "../../../utils/MarkedExecution"; +import { + IRecordingUpdate, + VoiceRecording, +} from "../../../voice/VoiceRecording"; + +interface IProps { + recorder?: VoiceRecording; +} + +interface IState { + waveform: number[] +} /** * A waveform which shows the waveform of a live recording */ @replaceableComponent("views.voice_messages.LiveRecordingWaveform") -export default class LiveRecordingWaveform extends React.PureComponent { +export default class LiveRecordingWaveform extends React.PureComponent { public static defaultProps = { progress: 1, }; + + private waveform: number[] = []; + private rafId: number; + + state = { + waveform: [], + } + + componentDidMount() { + this.props.recorder.liveData.onUpdate((update: IRecordingUpdate) => { + this.waveform = update.waveform; + this.scheduledUpdate.mark(); + }); + } + + private scheduledUpdate = new MarkedExecution( + () => this.updateWaveform(), + () => this.onLiveDataUpdate(), + ) + + private onLiveDataUpdate() { + if (this.rafId) { + cancelAnimationFrame(this.rafId); + } + + this.rafId = requestAnimationFrame(() => this.scheduledUpdate.trigger()) + } + + private updateWaveform() { + this.setState({ + waveform: this.waveform, + }) + this.rafId = null; + } + public render() { - return ; + return ; } } diff --git a/src/utils/MarkedExecution.ts b/src/utils/MarkedExecution.ts index 01cc91adce..0123d94f71 100644 --- a/src/utils/MarkedExecution.ts +++ b/src/utils/MarkedExecution.ts @@ -22,7 +22,7 @@ limitations under the License. * The function starts unmarked. */ export class MarkedExecution { - private marked = false; + private _marked = false; /** * Creates a MarkedExecution for the provided function. @@ -33,26 +33,33 @@ export class MarkedExecution { constructor(private fn: () => void, private onMarkCallback?: () => void) { } + /** + * Getter for the _marked property + */ + public get marked() { + return this._marked; + } + /** * Resets the mark without calling the function. */ public reset() { - this.marked = false; + this._marked = false; } /** * Marks the function to be called upon trigger(). */ public mark() { - if (!this.marked) this.onMarkCallback?.(); - this.marked = true; + if (!this._marked) this.onMarkCallback?.(); + this._marked = true; } /** * If marked, the function will be called, otherwise this does nothing. */ public trigger() { - if (!this.marked) return; + if (!this._marked) return; this.reset(); // reset first just in case the fn() causes a trigger() this.fn(); }