mirror of https://github.com/vector-im/riot-web
move live recording logic down the component tree
parent
56467485f5
commit
21caa6df12
|
@ -227,8 +227,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
|
|
||||||
// only other UI is the recording-in-progress UI
|
// only other UI is the recording-in-progress UI
|
||||||
return <div className="mx_VoiceMessagePrimaryContainer mx_VoiceRecordComposerTile_recording">
|
return <div className="mx_VoiceMessagePrimaryContainer mx_VoiceRecordComposerTile_recording">
|
||||||
<LiveRecordingClock seconds={this.state.seconds} />
|
<LiveRecordingClock recorder={this.state.recorder} />
|
||||||
<LiveRecordingWaveform relHeights={this.state.relHeights} />
|
<LiveRecordingWaveform recorder={this.state.recorder} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,62 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Clock, { IProps as IClockProps } from "./Clock";
|
import Clock from "./Clock";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
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.
|
* A clock for a live recording.
|
||||||
*/
|
*/
|
||||||
@replaceableComponent("views.voice_messages.LiveRecordingClock")
|
@replaceableComponent("views.voice_messages.LiveRecordingClock")
|
||||||
export default class LiveRecordingClock extends React.PureComponent<IClockProps> {
|
export default class LiveRecordingClock extends React.PureComponent<IProps, IState> {
|
||||||
|
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() {
|
public render() {
|
||||||
return <Clock seconds={this.props.seconds} />;
|
return <Clock seconds={this.state.seconds} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,66 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Waveform, { IProps as IWaveformProps } from "./Waveform";
|
import Waveform from "./Waveform";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
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
|
* A waveform which shows the waveform of a live recording
|
||||||
*/
|
*/
|
||||||
@replaceableComponent("views.voice_messages.LiveRecordingWaveform")
|
@replaceableComponent("views.voice_messages.LiveRecordingWaveform")
|
||||||
export default class LiveRecordingWaveform extends React.PureComponent<IWaveformProps> {
|
export default class LiveRecordingWaveform extends React.PureComponent<IProps, IState> {
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
progress: 1,
|
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() {
|
public render() {
|
||||||
return <Waveform relHeights={this.props.relHeights} />;
|
return <Waveform relHeights={this.state.waveform} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ limitations under the License.
|
||||||
* The function starts unmarked.
|
* The function starts unmarked.
|
||||||
*/
|
*/
|
||||||
export class MarkedExecution {
|
export class MarkedExecution {
|
||||||
private marked = false;
|
private _marked = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a MarkedExecution for the provided function.
|
* Creates a MarkedExecution for the provided function.
|
||||||
|
@ -33,26 +33,33 @@ export class MarkedExecution {
|
||||||
constructor(private fn: () => void, private onMarkCallback?: () => void) {
|
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.
|
* Resets the mark without calling the function.
|
||||||
*/
|
*/
|
||||||
public reset() {
|
public reset() {
|
||||||
this.marked = false;
|
this._marked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the function to be called upon trigger().
|
* Marks the function to be called upon trigger().
|
||||||
*/
|
*/
|
||||||
public mark() {
|
public mark() {
|
||||||
if (!this.marked) this.onMarkCallback?.();
|
if (!this._marked) this.onMarkCallback?.();
|
||||||
this.marked = true;
|
this._marked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If marked, the function will be called, otherwise this does nothing.
|
* If marked, the function will be called, otherwise this does nothing.
|
||||||
*/
|
*/
|
||||||
public trigger() {
|
public trigger() {
|
||||||
if (!this.marked) return;
|
if (!this._marked) return;
|
||||||
this.reset(); // reset first just in case the fn() causes a trigger()
|
this.reset(); // reset first just in case the fn() causes a trigger()
|
||||||
this.fn();
|
this.fn();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue