Voice notes mini-player
This commit is contained in:
parent
b5849f872a
commit
0e655ceeed
45 changed files with 1599 additions and 487 deletions
107
ts/components/PlaybackRateButton.tsx
Normal file
107
ts/components/PlaybackRateButton.tsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { animated, useSpring } from '@react-spring/web';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
|
||||
const SPRING_CONFIG = {
|
||||
mass: 0.5,
|
||||
tension: 350,
|
||||
friction: 20,
|
||||
velocity: 0.01,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
// undefined if not playing
|
||||
playbackRate: number | undefined;
|
||||
variant: 'message-outgoing' | 'message-incoming' | 'mini-player';
|
||||
onClick: () => void;
|
||||
visible?: boolean;
|
||||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export function PlaybackRateButton({
|
||||
playbackRate,
|
||||
variant,
|
||||
visible = true,
|
||||
i18n,
|
||||
onClick,
|
||||
}: Props): JSX.Element {
|
||||
const [isDown, setIsDown] = useState(false);
|
||||
|
||||
const [animProps] = useSpring(
|
||||
{
|
||||
config: SPRING_CONFIG,
|
||||
to: isDown ? { scale: 1.3 } : { scale: visible ? 1 : 0 },
|
||||
},
|
||||
[visible, isDown]
|
||||
);
|
||||
|
||||
// Clicking button toggle playback
|
||||
const onButtonClick = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
onClick();
|
||||
},
|
||||
[onClick]
|
||||
);
|
||||
|
||||
// Keyboard playback toggle
|
||||
const onButtonKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent) => {
|
||||
if (event.key !== 'Enter' && event.key !== 'Space') {
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
onClick();
|
||||
},
|
||||
[onClick]
|
||||
);
|
||||
|
||||
const playbackRateLabels: { [key: number]: string } = {
|
||||
1: i18n('MessageAudio--playbackRate1'),
|
||||
1.5: i18n('MessageAudio--playbackRate1p5'),
|
||||
2: i18n('MessageAudio--playbackRate2'),
|
||||
0.5: i18n('MessageAudio--playbackRatep5'),
|
||||
};
|
||||
|
||||
const label = playbackRate
|
||||
? playbackRateLabels[playbackRate].toString()
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<animated.div style={animProps}>
|
||||
<button
|
||||
type="button"
|
||||
className={classNames(
|
||||
'PlaybackRateButton',
|
||||
`PlaybackRateButton--${variant}`
|
||||
)}
|
||||
onClick={onButtonClick}
|
||||
onKeyDown={onButtonKeyDown}
|
||||
onMouseDown={() => setIsDown(true)}
|
||||
onMouseUp={() => setIsDown(false)}
|
||||
onMouseLeave={() => setIsDown(false)}
|
||||
aria-label={label}
|
||||
tabIndex={0}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</animated.div>
|
||||
);
|
||||
}
|
||||
|
||||
const playbackRates = [1, 1.5, 2, 0.5];
|
||||
|
||||
PlaybackRateButton.nextPlaybackRate = (currentRate: number): number => {
|
||||
// cycle through the rates
|
||||
return playbackRates[
|
||||
(playbackRates.indexOf(currentRate) + 1) % playbackRates.length
|
||||
];
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue