diff --git a/src/renderer/features/player/components/center-controls.tsx b/src/renderer/features/player/components/center-controls.tsx index 6b065eee..ba20fa72 100644 --- a/src/renderer/features/player/components/center-controls.tsx +++ b/src/renderer/features/player/components/center-controls.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import format from 'format-duration'; +import formatDuration from 'format-duration'; import isElectron from 'is-electron'; import { IoIosPause } from 'react-icons/io'; import { @@ -16,7 +16,6 @@ import styled from 'styled-components'; import { Text } from '/@/renderer/components'; import { useCenterControls } from '../hooks/use-center-controls'; import { PlayerButton } from './player-button'; -import { Slider } from './slider'; import { useCurrentSong, useCurrentStatus, @@ -28,6 +27,7 @@ import { } from '/@/renderer/store'; import { usePlayerType, useSettingsStore } from '/@/renderer/store/settings.store'; import { PlayerStatus, PlaybackType, PlayerShuffle, PlayerRepeat } from '/@/renderer/types'; +import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider'; interface CenterControlsProps { playersRef: any; @@ -61,6 +61,7 @@ const SliderValueWrapper = styled.div<{ position: 'left' | 'right' }>` const SliderWrapper = styled.div` display: flex; flex: 6; + align-items: center; height: 100%; `; @@ -91,8 +92,8 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => { const currentTime = useCurrentTime(); const currentPlayerRef = player === 1 ? player1 : player2; - const duration = format((songDuration || 0) * 1000); - const formattedTime = format(currentTime * 1000 || 0); + const duration = formatDuration((songDuration || 0) * 1000); + const formattedTime = formatDuration(currentTime * 1000 || 0); useEffect(() => { let interval: any; @@ -210,13 +211,14 @@ export const CenterControls = ({ playersRef }: CenterControlsProps) => { - formatDuration(value * 1000)} max={songDuration} min={0} - tooltipType="time" + size={6} value={currentTime} - onAfterChange={(e) => { + w="100%" + onChange={(e) => { handleSeekSlider(e); setIsSeeking(false); }} diff --git a/src/renderer/features/player/components/playerbar-slider.tsx b/src/renderer/features/player/components/playerbar-slider.tsx new file mode 100644 index 00000000..e8d4f1a3 --- /dev/null +++ b/src/renderer/features/player/components/playerbar-slider.tsx @@ -0,0 +1,40 @@ +import { rem, Slider, SliderProps } from '@mantine/core'; + +export const PlayerbarSlider = ({ ...props }: SliderProps) => { + return ( + + ); +}; diff --git a/src/renderer/features/player/components/right-controls.tsx b/src/renderer/features/player/components/right-controls.tsx index c38a2977..e1047256 100644 --- a/src/renderer/features/player/components/right-controls.tsx +++ b/src/renderer/features/player/components/right-controls.tsx @@ -7,7 +7,6 @@ import { RiHeartLine, RiHeartFill, } from 'react-icons/ri'; -import styled from 'styled-components'; import { useAppStoreActions, useCurrentServer, @@ -19,34 +18,10 @@ import { } from '/@/renderer/store'; import { useRightControls } from '../hooks/use-right-controls'; import { PlayerButton } from './player-button'; -import { Slider } from './slider'; import { LibraryItem, ServerType } from '/@/renderer/api/types'; import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared'; import { Rating } from '/@/renderer/components'; - -const RightControlsContainer = styled.div` - display: flex; - flex-direction: row; - justify-content: flex-end; - width: 100%; - height: 100%; -`; - -const VolumeSliderWrapper = styled.div` - display: flex; - gap: 0.3rem; - align-items: center; - width: 90px; -`; - -const MetadataStack = styled.div` - display: flex; - flex-direction: column; - gap: 0.3rem; - align-items: flex-end; - justify-content: center; - overflow: visible; -`; +import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider'; export const RightControls = () => { const volume = useVolume(); @@ -55,7 +30,7 @@ export const RightControls = () => { const currentSong = useCurrentSong(); const { setSidebar } = useAppStoreActions(); const { rightExpanded: isQueueExpanded } = useSidebarStore(); - const { handleVolumeSlider, handleVolumeSliderState, handleMute } = useRightControls(); + const { handleVolumeSlider, handleVolumeWheel, handleMute } = useRightControls(); const addToFavoritesMutation = useCreateFavorite(); const removeFromFavoritesMutation = useDeleteFavorite(); @@ -115,77 +90,82 @@ export const RightControls = () => { align="flex-end" direction="column" h="100%" - p="1rem" + px="1rem" + py="0.5rem" > - {showRating && ( - + + {showRating && ( - - )} - - + )} + + + + ) : ( + + ) + } + sx={{ + svg: { + fill: !currentSong?.userFavorite ? undefined : 'var(--primary-color) !important', + }, + }} + tooltip={{ + label: currentSong?.userFavorite ? 'Unfavorite' : 'Favorite', + openDelay: 500, + }} + variant="secondary" + onClick={handleToggleFavorite} + /> + } + tooltip={{ label: 'View queue', openDelay: 500 }} + variant="secondary" + onClick={() => setSidebar({ rightExpanded: !isQueueExpanded })} + /> + + muted ? ( + + ) : volume > 50 ? ( + ) : ( - + ) } - sx={{ - svg: { - fill: !currentSong?.userFavorite ? undefined : 'var(--primary-color) !important', - }, - }} - tooltip={{ - label: currentSong?.userFavorite ? 'Unfavorite' : 'Favorite', - openDelay: 500, - }} + tooltip={{ label: muted ? 'Muted' : volume, openDelay: 500 }} variant="secondary" - onClick={handleToggleFavorite} + onClick={handleMute} /> - } - tooltip={{ label: 'View queue', openDelay: 500 }} - variant="secondary" - onClick={() => setSidebar({ rightExpanded: !isQueueExpanded })} + - - - - ) : volume > 50 ? ( - - ) : ( - - ) - } - tooltip={{ label: muted ? 'Muted' : volume, openDelay: 500 }} - variant="secondary" - onClick={handleMute} - /> - - - - + + ); }; diff --git a/src/renderer/features/player/hooks/use-center-controls.ts b/src/renderer/features/player/hooks/use-center-controls.ts index d8926bfb..f1df79e8 100644 --- a/src/renderer/features/player/hooks/use-center-controls.ts +++ b/src/renderer/features/player/hooks/use-center-controls.ts @@ -13,6 +13,7 @@ import { } from '/@/renderer/store'; import { usePlayerType, useSettingsStore } from '/@/renderer/store/settings.store'; import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble'; +import debounce from 'lodash/debounce'; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; const mpvPlayerListener = isElectron() ? window.electron.mpvPlayerListener : null; @@ -439,19 +440,21 @@ export const useCenterControls = (args: { playersRef: any }) => { } }; + const debouncedSeek = debounce((e: number) => { + if (isMpvPlayer) { + mpvPlayer.seekTo(e); + } else { + currentPlayerRef.seekTo(e); + } + }, 100); + const handleSeekSlider = useCallback( (e: number | any) => { setCurrentTime(e); - handleScrobbleFromSeek(e); - - if (isMpvPlayer) { - mpvPlayer.seekTo(e); - } else { - currentPlayerRef.seekTo(e); - } + debouncedSeek(e); }, - [currentPlayerRef, handleScrobbleFromSeek, isMpvPlayer, setCurrentTime], + [debouncedSeek, handleScrobbleFromSeek, setCurrentTime], ); const handleQuit = useCallback(() => { diff --git a/src/renderer/features/player/hooks/use-right-controls.ts b/src/renderer/features/player/hooks/use-right-controls.ts index 6829e91f..06bc4d2b 100644 --- a/src/renderer/features/player/hooks/use-right-controls.ts +++ b/src/renderer/features/player/hooks/use-right-controls.ts @@ -1,6 +1,7 @@ -import { useEffect } from 'react'; +import { useEffect, WheelEvent } from 'react'; import isElectron from 'is-electron'; import { useMuted, usePlayerControls, useVolume } from '/@/renderer/store'; +import { useGeneralSettings } from '/@/renderer/store/settings.store'; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; @@ -8,6 +9,7 @@ export const useRightControls = () => { const { setVolume, setMuted } = usePlayerControls(); const volume = useVolume(); const muted = useMuted(); + const { volumeWheelStep } = useGeneralSettings(); // Ensure that the mpv player volume is set on startup useEffect(() => { @@ -31,6 +33,18 @@ export const useRightControls = () => { setVolume(e); }; + const handleVolumeWheel = (e: WheelEvent) => { + let newVolume; + if (e.deltaY > 0) { + newVolume = volume - volumeWheelStep; + } else { + newVolume = volume + volumeWheelStep; + } + + mpvPlayer.volume(newVolume); + setVolume(newVolume); + }; + const handleMute = () => { setMuted(!muted); mpvPlayer.mute(); @@ -40,5 +54,6 @@ export const useRightControls = () => { handleMute, handleVolumeSlider, handleVolumeSliderState, + handleVolumeWheel, }; }; diff --git a/src/renderer/features/player/index.ts b/src/renderer/features/player/index.ts index c1974d47..bf7f3353 100644 --- a/src/renderer/features/player/index.ts +++ b/src/renderer/features/player/index.ts @@ -1,6 +1,5 @@ export * from './components/center-controls'; export * from './components/left-controls'; export * from './components/playerbar'; -export * from './components/slider'; export * from './context/play-queue-handler-context'; export * from './hooks/use-playqueue-add'; diff --git a/src/renderer/features/settings/components/general-tab.tsx b/src/renderer/features/settings/components/general-tab.tsx index 9956665e..1aae8fc5 100644 --- a/src/renderer/features/settings/components/general-tab.tsx +++ b/src/renderer/features/settings/components/general-tab.tsx @@ -1,5 +1,5 @@ import { Divider, Stack } from '@mantine/core'; -import { Select, Switch } from '/@/renderer/components'; +import { Select, Slider, Switch } from '/@/renderer/components'; import isElectron from 'is-electron'; import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option'; import { THEME_DATA } from '/@/renderer/hooks'; @@ -195,6 +195,31 @@ export const GeneralTab = () => { }, ]; + const miscOptions = [ + { + control: ( + { + setSettings({ + general: { + ...settings, + volumeWheelStep: e, + }, + }); + }} + /> + ), + description: + 'The amount of volume to change when scrolling the mouse wheel on the volume slider', + isHidden: false, + title: 'Volume wheel step', + }, + ]; + return ( {options @@ -223,6 +248,15 @@ export const GeneralTab = () => { {...option} /> ))} + + {miscOptions + .filter((o) => !o.isHidden) + .map((option) => ( + + ))} ); }; diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts index 8dd5136e..e6979cef 100644 --- a/src/renderer/store/settings.store.ts +++ b/src/renderer/store/settings.store.ts @@ -37,6 +37,7 @@ export interface SettingsState { theme: AppTheme; themeDark: AppTheme; themeLight: AppTheme; + volumeWheelStep: number; }; player: { audioDeviceId?: string | null; @@ -90,6 +91,7 @@ export const useSettingsStore = create()( theme: AppTheme.DEFAULT_DARK, themeDark: AppTheme.DEFAULT_DARK, themeLight: AppTheme.DEFAULT_LIGHT, + volumeWheelStep: 5, }, player: { audioDeviceId: undefined, @@ -222,7 +224,7 @@ export const useSettingsStore = create()( return merge(currentState, persistedState); }, name: 'store_settings', - version: 2, + version: 3, }, ), );