Allow voice messages to be scrubbed in the timeline (#8079)

* Use SeekBar for voice messages + move seeking logic to base class

* Appease the linter

* Update tests
This commit is contained in:
Travis Ralston 2022-03-29 14:07:35 -06:00 committed by GitHub
parent 2adc972eec
commit 5fa2ca83ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 81 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,15 +14,19 @@ 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 { replaceableComponent } from "../../../utils/replaceableComponent";
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;
@ -36,8 +40,11 @@ interface IState {
}
@replaceableComponent("views.audio_messages.AudioPlayerBase")
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
@ -56,6 +63,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 });
};