mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
@@ -149,6 +149,13 @@ export const timeDisplay = style({
|
||||
},
|
||||
});
|
||||
|
||||
export const playbackRateDisplay = style({
|
||||
fontSize: cssVar('fontXs'),
|
||||
fontWeight: 500,
|
||||
color: cssVarV2('text/secondary'),
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
export const miniRoot = style({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
|
||||
@@ -12,6 +12,7 @@ const AudioWrapper = () => {
|
||||
const [seekTime, setSeekTime] = useState(0);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [playbackRate, setPlaybackRate] = useState(1.0);
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
const audioUrlRef = useRef<string | null>(null);
|
||||
|
||||
@@ -151,6 +152,13 @@ const AudioWrapper = () => {
|
||||
[playbackState]
|
||||
);
|
||||
|
||||
const handlePlaybackRateChange = useCallback((rate: number) => {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.playbackRate = rate;
|
||||
setPlaybackRate(rate);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current;
|
||||
if (!audio || !audioFile) return;
|
||||
@@ -298,6 +306,8 @@ const AudioWrapper = () => {
|
||||
onPause={handlePause}
|
||||
onStop={handleStop}
|
||||
onSeek={handleSeek}
|
||||
playbackRate={playbackRate}
|
||||
onPlaybackRateChange={handlePlaybackRateChange}
|
||||
/>
|
||||
<AudioPlayer
|
||||
name={audioFile.name}
|
||||
@@ -311,6 +321,8 @@ const AudioWrapper = () => {
|
||||
onPause={handlePause}
|
||||
onStop={handleStop}
|
||||
onSeek={handleSeek}
|
||||
playbackRate={playbackRate}
|
||||
onPlaybackRateChange={handlePlaybackRateChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -8,8 +8,9 @@ import bytes from 'bytes';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { type MouseEventHandler, type ReactNode, useCallback } from 'react';
|
||||
|
||||
import { IconButton } from '../button';
|
||||
import { Button, IconButton } from '../button';
|
||||
import { AnimatedPlayIcon } from '../lottie';
|
||||
import { Menu, MenuItem } from '../menu';
|
||||
import * as styles from './audio-player.css';
|
||||
import { AudioWaveform } from './audio-waveform';
|
||||
|
||||
@@ -40,8 +41,15 @@ export interface AudioPlayerProps {
|
||||
onPause: MouseEventHandler;
|
||||
onStop: MouseEventHandler;
|
||||
onSeek: (newTime: number) => void;
|
||||
|
||||
// Playback rate
|
||||
playbackRate: number;
|
||||
onPlaybackRateChange: (rate: number) => void;
|
||||
}
|
||||
|
||||
// Playback rate options
|
||||
const playbackRates = [0.5, 0.75, 1, 1.5, 1.75, 2, 3];
|
||||
|
||||
export const AudioPlayer = ({
|
||||
name,
|
||||
size,
|
||||
@@ -55,6 +63,8 @@ export const AudioPlayer = ({
|
||||
onPause,
|
||||
onSeek,
|
||||
onClick,
|
||||
playbackRate,
|
||||
onPlaybackRateChange,
|
||||
}: AudioPlayerProps) => {
|
||||
// Handle progress bar click
|
||||
const handleProgressClick = useCallback(
|
||||
@@ -80,6 +90,13 @@ export const AudioPlayer = ({
|
||||
[loading, playbackState, onPause, onPlay]
|
||||
);
|
||||
|
||||
const handlePlaybackRateChange = useCallback(
|
||||
(rate: number) => {
|
||||
onPlaybackRateChange(rate);
|
||||
},
|
||||
[onPlaybackRateChange]
|
||||
);
|
||||
|
||||
// Calculate progress percentage
|
||||
const progressPercentage = duration > 0 ? seekTime / duration : 0;
|
||||
return (
|
||||
@@ -97,6 +114,27 @@ export const AudioPlayer = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.upperRight}>
|
||||
<Menu
|
||||
rootOptions={{ modal: false }}
|
||||
children={
|
||||
<Button variant="plain" className={styles.playbackRateDisplay}>
|
||||
{playbackRate}x
|
||||
</Button>
|
||||
}
|
||||
items={
|
||||
<>
|
||||
{playbackRates.map(rate => (
|
||||
<MenuItem
|
||||
key={rate}
|
||||
selected={rate === playbackRate}
|
||||
onClick={() => handlePlaybackRateChange(rate)}
|
||||
>
|
||||
{rate}x
|
||||
</MenuItem>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{notesEntry}
|
||||
<AnimatedPlayIcon
|
||||
onClick={handlePlayToggle}
|
||||
|
||||
Reference in New Issue
Block a user