diff --git a/src/renderer/features/player/components/right-controls.tsx b/src/renderer/features/player/components/right-controls.tsx index 421ab20e8..584a16e62 100644 --- a/src/renderer/features/player/components/right-controls.tsx +++ b/src/renderer/features/player/components/right-controls.tsx @@ -1,5 +1,5 @@ import { t } from 'i18next'; -import { useCallback, WheelEvent } from 'react'; +import { useCallback, useEffect, useState, WheelEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { PopoverPlayQueue } from '/@/renderer/features/now-playing/components/popover-play-queue'; @@ -34,6 +34,7 @@ import { Rating } from '/@/shared/components/rating/rating'; import { useHotkeys } from '/@/shared/hooks/use-hotkeys'; import { useMediaQuery } from '/@/shared/hooks/use-media-query'; import { useThrottledCallback } from '/@/shared/hooks/use-throttled-callback'; +import { useThrottledValue } from '/@/shared/hooks/use-throttled-value'; import { LibraryItem, QueueSong, ServerType } from '/@/shared/types/domain-types'; const calculateVolumeUp = (volume: number, volumeWheelStep: number) => { @@ -359,6 +360,20 @@ const VolumeButton = () => { const { mediaToggleMute, setVolume } = usePlayer(); const isMinWidth = useMediaQuery('(max-width: 480px)'); + const [sliderValue, setSliderValue] = useState(volume); + + const throttledVolume = useThrottledValue(sliderValue, 100); + + // Sync throttled value to actual volume + useEffect(() => { + setVolume(throttledVolume); + }, [throttledVolume, setVolume]); + + // Sync external volume changes to local state + useEffect(() => { + setSliderValue(volume); + }, [volume]); + const handleVolumeDown = useCallback(() => { setVolume(Math.max(0, volume - 1)); }, [setVolume, volume]); @@ -367,12 +382,9 @@ const VolumeButton = () => { setVolume(Math.min(100, volume + 1)); }, [setVolume, volume]); - const handleVolumeSlider = useCallback( - (e: number) => { - setVolume(e); - }, - [setVolume], - ); + const handleVolumeSlider = useCallback((e: number) => { + setSliderValue(e); + }, []); const handleMute = useCallback(() => { mediaToggleMute(); @@ -392,8 +404,8 @@ const VolumeButton = () => { [setVolume, volume, volumeWheelStep], ); - const handleVolumeDownThrottled = useThrottledCallback(handleVolumeDown, 50); - const handleVolumeUpThrottled = useThrottledCallback(handleVolumeUp, 50); + const handleVolumeDownThrottled = useThrottledCallback(handleVolumeDown, 100); + const handleVolumeUpThrottled = useThrottledCallback(handleVolumeUp, 100); useHotkeys([ [bindings.volumeDown.isGlobal ? '' : bindings.volumeDown.hotkey, handleVolumeDownThrottled], @@ -428,7 +440,7 @@ const VolumeButton = () => { onChange={handleVolumeSlider} onWheel={handleVolumeWheel} size={6} - value={volume} + value={sliderValue} w={volumeWidth} /> ) : null} diff --git a/src/shared/hooks/use-throttled-value.ts b/src/shared/hooks/use-throttled-value.ts new file mode 100644 index 000000000..7f5e4ff3e --- /dev/null +++ b/src/shared/hooks/use-throttled-value.ts @@ -0,0 +1,3 @@ +import { useThrottledValue as useMantineThrottledValue } from '@mantine/hooks'; + +export const useThrottledValue = useMantineThrottledValue;