Merge branch 'develop' into travis/remove-skinning

This commit is contained in:
Travis Ralston 2022-03-31 19:25:43 -06:00
commit 97efdf7094
54 changed files with 1559 additions and 431 deletions

View file

@ -1,5 +1,5 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, ReactNode, RefObject } from "react";
import React, { ReactNode } from "react";
import PlayPauseButton from "./PlayPauseButton";
import { formatBytes } from "../../../utils/FormattingUtils";
@ -23,40 +23,8 @@ import { _t } from "../../../languageHandler";
import SeekBar from "./SeekBar";
import PlaybackClock from "./PlaybackClock";
import AudioPlayerBase from "./AudioPlayerBase";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
export default class AudioPlayer extends AudioPlayerBase {
private playPauseRef: RefObject<PlayPauseButton> = createRef();
private seekRef: RefObject<SeekBar> = createRef();
private onKeyDown = (ev: React.KeyboardEvent) => {
let handled = true;
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Space:
this.playPauseRef.current?.toggleState();
break;
case KeyBindingAction.ArrowLeft:
this.seekRef.current?.left();
break;
case KeyBindingAction.ArrowRight:
this.seekRef.current?.right();
break;
default:
handled = false;
break;
}
// stopPropagation() prevents the FocusComposer catch-all from triggering,
// but we need to do it on key down instead of press (even though the user
// interaction is typically on press).
if (handled) {
ev.stopPropagation();
}
};
protected renderFileSize(): string {
const bytes = this.props.playback.sizeBytes;
if (!bytes) return null;

View file

@ -1,5 +1,5 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,14 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactNode } from "react";
import React, { createRef, ReactNode, RefObject } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { Playback, PlaybackState } from "../../../audio/Playback";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { _t } from "../../../languageHandler";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import SeekBar from "./SeekBar";
import PlayPauseButton from "./PlayPauseButton";
interface IProps {
export interface IProps {
// Playback instance to render. Cannot change during component lifecycle: create
// an all-new component instead.
playback: Playback;
@ -34,8 +38,11 @@ interface IState {
error?: boolean;
}
export default abstract class AudioPlayerBase extends React.PureComponent<IProps, IState> {
constructor(props: IProps) {
export default abstract class AudioPlayerBase<T extends IProps = IProps> extends React.PureComponent<T, IState> {
protected seekRef: RefObject<SeekBar> = createRef();
protected playPauseRef: RefObject<PlayPauseButton> = createRef();
constructor(props: T) {
super(props);
// Playback instances can be reused in the composer
@ -54,6 +61,33 @@ export default abstract class AudioPlayerBase extends React.PureComponent<IProps
});
}
protected onKeyDown = (ev: React.KeyboardEvent) => {
let handled = true;
const action = getKeyBindingsManager().getAccessibilityAction(ev);
switch (action) {
case KeyBindingAction.Space:
this.playPauseRef.current?.toggleState();
break;
case KeyBindingAction.ArrowLeft:
this.seekRef.current?.left();
break;
case KeyBindingAction.ArrowRight:
this.seekRef.current?.right();
break;
default:
handled = false;
break;
}
// stopPropagation() prevents the FocusComposer catch-all from triggering,
// but we need to do it on key down instead of press (even though the user
// interaction is typically on press).
if (handled) {
ev.stopPropagation();
}
};
private onPlaybackUpdate = (ev: PlaybackState) => {
this.setState({ playbackPhase: ev });
};

View file

@ -1,5 +1,5 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,28 +18,49 @@ import React, { ReactNode } from "react";
import PlayPauseButton from "./PlayPauseButton";
import PlaybackClock from "./PlaybackClock";
import AudioPlayerBase, { IProps as IAudioPlayerBaseProps } from "./AudioPlayerBase";
import SeekBar from "./SeekBar";
import PlaybackWaveform from "./PlaybackWaveform";
import AudioPlayerBase from "./AudioPlayerBase";
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
export default class RecordingPlayback extends AudioPlayerBase {
static contextType = RoomContext;
public context!: React.ContextType<typeof RoomContext>;
interface IProps extends IAudioPlayerBaseProps {
/**
* When true, use a waveform instead of a seek bar
*/
withWaveform?: boolean;
}
private get isWaveformable(): boolean {
return this.context.timelineRenderingType !== TimelineRenderingType.Notification
&& this.context.timelineRenderingType !== TimelineRenderingType.File
&& this.context.timelineRenderingType !== TimelineRenderingType.Pinned;
export default class RecordingPlayback extends AudioPlayerBase<IProps> {
// This component is rendered in two ways: the composer and timeline. They have different
// rendering properties (specifically the difference of a waveform or not).
private renderWaveformLook(): ReactNode {
return <>
<PlaybackClock playback={this.props.playback} />
<PlaybackWaveform playback={this.props.playback} />
</>;
}
private renderSeekableLook(): ReactNode {
return <>
<SeekBar
playback={this.props.playback}
tabIndex={-1} // prevent tabbing into the bar
playbackPhase={this.state.playbackPhase}
ref={this.seekRef}
/>
<PlaybackClock playback={this.props.playback} />
</>;
}
protected renderComponent(): ReactNode {
const shapeClass = !this.isWaveformable ? 'mx_VoiceMessagePrimaryContainer_noWaveform' : '';
return (
<div className={'mx_MediaBody mx_VoiceMessagePrimaryContainer ' + shapeClass}>
<PlayPauseButton playback={this.props.playback} playbackPhase={this.state.playbackPhase} />
<PlaybackClock playback={this.props.playback} />
{ this.isWaveformable && <PlaybackWaveform playback={this.props.playback} /> }
<div className="mx_MediaBody mx_VoiceMessagePrimaryContainer" onKeyDown={this.onKeyDown}>
<PlayPauseButton
playback={this.props.playback}
playbackPhase={this.state.playbackPhase}
ref={this.playPauseRef}
/>
{ this.props.withWaveform ? this.renderWaveformLook() : this.renderSeekableLook() }
</div>
);
}