2023-02-24 23:18:57 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2023-03-20 18:03:21 +00:00
|
|
|
import classnames from 'classnames';
|
2023-02-24 23:18:57 +00:00
|
|
|
import React, { useCallback } from 'react';
|
|
|
|
import type { LocalizerType } from '../types/Util';
|
|
|
|
import { durationToPlaybackText } from '../util/durationToPlaybackText';
|
2023-03-02 20:55:40 +00:00
|
|
|
import { PlaybackButton } from './PlaybackButton';
|
2023-02-24 23:18:57 +00:00
|
|
|
import { PlaybackRateButton } from './PlaybackRateButton';
|
2023-04-20 17:03:43 +00:00
|
|
|
import { UserText } from './UserText';
|
2023-02-24 23:18:57 +00:00
|
|
|
|
|
|
|
export enum PlayerState {
|
|
|
|
loading = 'loading',
|
|
|
|
playing = 'playing',
|
|
|
|
paused = 'paused',
|
|
|
|
}
|
|
|
|
|
|
|
|
export type Props = Readonly<{
|
|
|
|
i18n: LocalizerType;
|
|
|
|
title: string;
|
|
|
|
currentTime: number;
|
2023-02-28 13:07:40 +00:00
|
|
|
// not available until audio has loaded
|
|
|
|
duration: number | undefined;
|
2023-02-24 23:18:57 +00:00
|
|
|
playbackRate: number;
|
|
|
|
state: PlayerState;
|
2023-03-20 18:03:21 +00:00
|
|
|
// if false or not provided, position:absolute. Otherwise, it's position: relative
|
|
|
|
shouldFlow?: boolean;
|
2023-02-24 23:18:57 +00:00
|
|
|
onPlay: () => void;
|
|
|
|
onPause: () => void;
|
|
|
|
onPlaybackRate: (rate: number) => void;
|
|
|
|
onClose: () => void;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
export function MiniPlayer({
|
|
|
|
i18n,
|
|
|
|
title,
|
|
|
|
state,
|
|
|
|
currentTime,
|
|
|
|
duration,
|
|
|
|
playbackRate,
|
2023-03-20 18:03:21 +00:00
|
|
|
shouldFlow,
|
2023-02-24 23:18:57 +00:00
|
|
|
onPlay,
|
|
|
|
onPause,
|
|
|
|
onPlaybackRate,
|
|
|
|
onClose,
|
|
|
|
}: Props): JSX.Element {
|
|
|
|
const updatePlaybackRate = useCallback(() => {
|
|
|
|
onPlaybackRate(PlaybackRateButton.nextPlaybackRate(playbackRate));
|
|
|
|
}, [playbackRate, onPlaybackRate]);
|
|
|
|
|
|
|
|
const handleClick = useCallback(() => {
|
|
|
|
switch (state) {
|
|
|
|
case PlayerState.playing:
|
|
|
|
onPause();
|
|
|
|
break;
|
|
|
|
case PlayerState.paused:
|
|
|
|
onPlay();
|
|
|
|
break;
|
|
|
|
case PlayerState.loading:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new TypeError(`Missing case: ${state}`);
|
|
|
|
}
|
|
|
|
}, [state, onPause, onPlay]);
|
|
|
|
|
|
|
|
let label: string | undefined;
|
2023-03-02 20:55:40 +00:00
|
|
|
let mod: 'play' | 'pause' | 'pending';
|
2023-02-24 23:18:57 +00:00
|
|
|
switch (state) {
|
|
|
|
case PlayerState.playing:
|
2023-03-30 00:03:25 +00:00
|
|
|
label = i18n('icu:MessageAudio--pause');
|
2023-03-02 20:55:40 +00:00
|
|
|
mod = 'pause';
|
2023-02-24 23:18:57 +00:00
|
|
|
break;
|
|
|
|
case PlayerState.paused:
|
2023-03-30 00:03:25 +00:00
|
|
|
label = i18n('icu:MessageAudio--play');
|
2023-03-02 20:55:40 +00:00
|
|
|
mod = 'play';
|
2023-02-24 23:18:57 +00:00
|
|
|
break;
|
|
|
|
case PlayerState.loading:
|
2023-03-30 00:03:25 +00:00
|
|
|
label = i18n('icu:MessageAudio--pending');
|
2023-03-02 20:55:40 +00:00
|
|
|
mod = 'pending';
|
2023-02-24 23:18:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new TypeError(`Missing case ${state}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2023-03-20 18:03:21 +00:00
|
|
|
<div
|
|
|
|
className={classnames(
|
|
|
|
'MiniPlayer',
|
|
|
|
shouldFlow ? 'MiniPlayer--flow' : null
|
|
|
|
)}
|
|
|
|
>
|
2023-03-02 20:55:40 +00:00
|
|
|
<PlaybackButton
|
|
|
|
context="incoming"
|
|
|
|
variant="mini"
|
|
|
|
mod={mod}
|
|
|
|
label={label}
|
2023-02-24 23:18:57 +00:00
|
|
|
onClick={handleClick}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div className="MiniPlayer__state">
|
2023-04-20 17:03:43 +00:00
|
|
|
<UserText text={title} />
|
2023-02-24 23:18:57 +00:00
|
|
|
<span className="MiniPlayer__middot">·</span>
|
2023-02-28 13:07:40 +00:00
|
|
|
{duration !== undefined && (
|
|
|
|
<span>
|
|
|
|
{durationToPlaybackText(
|
|
|
|
state === PlayerState.loading ? duration : currentTime
|
|
|
|
)}
|
|
|
|
</span>
|
|
|
|
)}
|
2023-02-24 23:18:57 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<PlaybackRateButton
|
|
|
|
i18n={i18n}
|
|
|
|
variant="mini-player"
|
|
|
|
playbackRate={playbackRate}
|
|
|
|
onClick={updatePlaybackRate}
|
|
|
|
visible={state === 'playing'}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
className="MiniPlayer__close-button"
|
|
|
|
onClick={onClose}
|
2023-03-30 00:03:25 +00:00
|
|
|
aria-label={i18n('icu:close')}
|
2023-02-24 23:18:57 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|