optimize settings store

This commit is contained in:
jeffvli
2026-01-02 03:13:17 -08:00
parent 0cfc4119ba
commit a66c67e86d
72 changed files with 479 additions and 354 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ import { WebAudioContext } from '/@/renderer/features/player/context/webaudio-co
import { useSyncSettingsToMain } from '/@/renderer/hooks/use-sync-settings-to-main';
import { ReleaseNotesModal } from './release-notes-modal';
import { AppRouter } from '/@/renderer/router/app-router';
import { useCssSettings, useHotkeySettings, useSettingsStore } from '/@/renderer/store';
import { useCssSettings, useHotkeySettings, useLanguage } from '/@/renderer/store';
import { useAppTheme } from '/@/renderer/themes/use-app-theme';
import { sanitizeCss } from '/@/renderer/utils/sanitize';
import { WebAudio } from '/@/shared/types/types';
@@ -26,7 +26,7 @@ const ipc = isElectron() ? window.api.ipc : null;
export const App = () => {
const { mode, theme } = useAppTheme();
const language = useSettingsStore((store) => store.general.language);
const language = useLanguage();
const { content, enabled } = useCssSettings();
const { bindings } = useHotkeySettings();
@@ -19,7 +19,7 @@ import { ItemControls } from '/@/renderer/components/item-list/types';
import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists';
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
import { AppRoute } from '/@/renderer/router/routes';
import { useGeneralSettings } from '/@/renderer/store';
import { useShowRatings } from '/@/renderer/store';
import {
formatDateAbsolute,
formatDateAbsoluteUTC,
@@ -77,7 +77,7 @@ export const ItemCard = ({
type = 'poster',
withControls,
}: ItemCardProps) => {
const { showRatings } = useGeneralSettings();
const showRatings = useShowRatings();
const imageUrl = getImageUrl(data);
const rows = providedRows || [];
@@ -7,6 +7,7 @@ import {
getServerById,
useAuthStore,
useCurrentServerId,
useImageRes,
useSettingsStore,
} from '/@/renderer/store';
import { BaseImage, ImageProps } from '/@/shared/components/image/image';
@@ -73,7 +74,7 @@ export const useItemImageUrl = (args: UseItemImageUrlProps) => {
const { id, imageUrl, itemType, size, type, useRemoteUrl } = args;
const serverId = useCurrentServerId();
const imageRes = useSettingsStore((store) => store.general.imageRes);
const imageRes = useImageRes();
const sizeByType: number | undefined = type ? imageRes[type] : undefined;
return useMemo(() => {
@@ -21,7 +21,7 @@ import { searchLibraryItems } from '/@/renderer/features/shared/utils';
import { useContainerQuery } from '/@/renderer/hooks';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, usePlayerSong } from '/@/renderer/store';
import { useGeneralSettings, useSettingsStore } from '/@/renderer/store/settings.store';
import { useExternalLinks, useSettingsStore } from '/@/renderer/store/settings.store';
import { sentenceCase, titleCase } from '/@/renderer/utils';
import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify';
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
@@ -312,7 +312,7 @@ export const AlbumDetailContent = () => {
);
const { ref, ...cq } = useContainerQuery();
const { externalLinks, lastFM, musicBrainz } = useGeneralSettings();
const { externalLinks, lastFM, musicBrainz } = useExternalLinks();
const genreCarousels = useMemo(() => {
const genreLimit = 2;
@@ -14,7 +14,7 @@ import {
LibraryHeaderMenu,
} from '/@/renderer/features/shared/components/library-header';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { useCurrentServer, useShowRatings } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils';
import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types';
@@ -29,7 +29,7 @@ export const AlbumDetailHeader = forwardRef<HTMLDivElement>((_props, ref) => {
const { albumId } = useParams() as { albumId: string };
const { t } = useTranslation();
const server = useCurrentServer();
const { showRatings } = useGeneralSettings();
const showRatings = useShowRatings();
const detailQuery = useQuery(
albumQueries.detail({ query: { id: albumId }, serverId: server?.id }),
);
@@ -16,13 +16,13 @@ import { LibraryContainer } from '/@/renderer/features/shared/components/library
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useFastAverageColor, useWaitForColorCalculation } from '/@/renderer/hooks';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { useAlbumBackground, useCurrentServer } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
const AlbumDetailRoute = () => {
const scrollAreaRef = useRef<HTMLDivElement>(null);
const headerRef = useRef<HTMLDivElement>(null);
const { albumBackground, albumBackgroundBlur } = useGeneralSettings();
const { albumBackground, albumBackgroundBlur } = useAlbumBackground();
const { albumId } = useParams() as { albumId: string };
const server = useCurrentServer();
@@ -49,7 +49,13 @@ import {
useCurrentServerId,
usePlayerSong,
} from '/@/renderer/store';
import { useGeneralSettings, useSettingsStore } from '/@/renderer/store/settings.store';
import {
useArtistItems,
useArtistRadioCount,
useArtistReleaseTypeItems,
useExternalLinks,
useSettingsStore,
} from '/@/renderer/store/settings.store';
import { titleCase } from '/@/renderer/utils';
import { sanitize } from '/@/renderer/utils/sanitize';
import { sortAlbumList } from '/@/shared/api/utils';
@@ -589,8 +595,9 @@ export const AlbumArtistDetailContent = ({
albumsQuery,
detailQuery,
}: AlbumArtistDetailContentProps) => {
const { artistItems, artistRadioCount, externalLinks, lastFM, musicBrainz } =
useGeneralSettings();
const artistItems = useArtistItems();
const artistRadioCount = useArtistRadioCount();
const { externalLinks, lastFM, musicBrainz } = useExternalLinks();
const { albumArtistId, artistId } = useParams() as {
albumArtistId?: string;
artistId?: string;
@@ -1064,7 +1071,7 @@ interface ArtistAlbumsProps {
const ArtistAlbums = ({ albumsQuery }: ArtistAlbumsProps) => {
const { t } = useTranslation();
const { artistReleaseTypeItems } = useGeneralSettings();
const artistReleaseTypeItems = useArtistReleaseTypeItems();
const [searchTerm, setSearchTerm] = useState('');
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 300);
const albumArtistDetailSort = useAppStore((state) => state.albumArtistDetailSort);
@@ -14,7 +14,7 @@ import {
LibraryHeaderMenu,
} from '/@/renderer/features/shared/components/library-header';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { useCurrentServer, useShowRatings } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { formatDurationString } from '/@/renderer/utils';
import { Group } from '/@/shared/components/group/group';
@@ -30,7 +30,7 @@ export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivEleme
};
const routeId = (artistId || albumArtistId) as string;
const server = useCurrentServer();
const { showRatings } = useGeneralSettings();
const showRatings = useShowRatings();
const { t } = useTranslation();
const detailQuery = useSuspenseQuery(
artistsQueries.albumArtistDetail({
@@ -17,7 +17,7 @@ import { LibraryContainer } from '/@/renderer/features/shared/components/library
import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar';
import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-error-boundary';
import { useFastAverageColor, useWaitForColorCalculation } from '/@/renderer/hooks';
import { useCurrentServer, useCurrentServerId, useGeneralSettings } from '/@/renderer/store';
import { useArtistBackground, useCurrentServer, useCurrentServerId } from '/@/renderer/store';
import { Spinner } from '/@/shared/components/spinner/spinner';
import { AlbumListSort, LibraryItem, SortOrder } from '/@/shared/types/domain-types';
@@ -26,7 +26,7 @@ const AlbumArtistDetailRouteContent = () => {
const headerRef = useRef<HTMLDivElement>(null);
const server = useCurrentServer();
const serverId = useCurrentServerId();
const { artistBackground, artistBackgroundBlur } = useGeneralSettings();
const { artistBackground, artistBackgroundBlur } = useArtistBackground();
const { albumArtistId, artistId } = useParams() as {
albumArtistId?: string;
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import { queryKeys } from '/@/renderer/api/query-keys';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { useCurrentServerId, useGeneralSettings, usePlayButtonBehavior } from '/@/renderer/store';
import { useArtistRadioCount, useCurrentServerId, usePlayButtonBehavior } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { AlbumArtist, Artist } from '/@/shared/types/domain-types';
import { Play } from '/@/shared/types/types';
@@ -16,7 +16,7 @@ interface PlayArtistRadioActionProps {
}
export const PlayArtistRadioAction = ({ artist, disabled }: PlayArtistRadioActionProps) => {
const { artistRadioCount } = useGeneralSettings();
const artistRadioCount = useArtistRadioCount();
const { t } = useTranslation();
const player = usePlayer();
const serverId = useCurrentServerId();
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useSetRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
import { useCurrentServer, useCurrentServerId, useGeneralSettings } from '/@/renderer/store';
import { useCurrentServer, useCurrentServerId, useShowRatings } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { Rating } from '/@/shared/components/rating/rating';
import { LibraryItem } from '/@/shared/types/domain-types';
@@ -17,7 +17,7 @@ export const SetRatingAction = ({ ids, itemType }: SetRatingActionProps) => {
const { t } = useTranslation();
const server = useCurrentServer();
const serverId = useCurrentServerId();
const { showRatings } = useGeneralSettings();
const showRatings = useShowRatings();
const setRatingMutation = useSetRating({});
@@ -9,7 +9,7 @@ import {
DiscordLinkType,
useAppStore,
useDiscordSettings,
useGeneralSettings,
useLastfmApiKey,
usePlayerSong,
usePlayerStore,
useTimestampStoreBase,
@@ -31,7 +31,7 @@ const truncate = (field: string) =>
export const useDiscordRpc = () => {
const discordSettings = useDiscordSettings();
const generalSettings = useGeneralSettings();
const lastfmApiKey = useLastfmApiKey();
const privateMode = useAppStore((state) => state.privateMode);
const [lastUniqueId, setlastUniqueId] = useState('');
@@ -220,12 +220,12 @@ export const useDiscordRpc = () => {
if (
activity.largeImageKey === undefined &&
generalSettings.lastfmApiKey &&
lastfmApiKey &&
song?.album &&
song?.albumArtists.length
) {
const albumInfo = await fetch(
`https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=${generalSettings.lastfmApiKey}&artist=${encodeURIComponent(song.albumArtists[0].name)}&album=${encodeURIComponent(song.album)}&format=json`,
`https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=${lastfmApiKey}&artist=${encodeURIComponent(song.albumArtists[0].name)}&album=${encodeURIComponent(song.album)}&format=json`,
);
const albumInfoJson = await albumInfo.json();
@@ -292,7 +292,7 @@ export const useDiscordRpc = () => {
discordSettings.showAsListening,
discordSettings.showServerImage,
discordSettings.showPaused,
generalSettings.lastfmApiKey,
lastfmApiKey,
discordSettings.clientId,
discordSettings.displayType,
discordSettings.linkType,
@@ -13,7 +13,8 @@ import { SongInfiniteCarousel } from '/@/renderer/features/songs/components/song
import {
HomeItem,
useCurrentServer,
useGeneralSettings,
useHomeFeature,
useHomeItems,
useWindowSettings,
} from '/@/renderer/store';
import { Spinner } from '/@/shared/components/spinner/spinner';
@@ -32,7 +33,8 @@ const HomeRoute = () => {
const scrollAreaRef = useRef<HTMLDivElement>(null);
const server = useCurrentServer();
const { windowBarStyle } = useWindowSettings();
const { homeFeature, homeItems } = useGeneralSettings();
const homeFeature = useHomeFeature();
const homeItems = useHomeItems();
const isJellyfin = server?.type === ServerType.JELLYFIN;
@@ -20,12 +20,12 @@ import {
mapShuffledToQueueIndex,
subscribeCurrentTrack,
subscribePlayerQueue,
useFollowCurrentSong,
useListSettings,
usePlayerActions,
usePlayerQueueType,
usePlayerSong,
usePlayerStore,
useSettingsStore,
} from '/@/renderer/store';
import { Flex } from '/@/shared/components/flex/flex';
import { LoadingOverlay } from '/@/shared/components/loading-overlay/loading-overlay';
@@ -51,7 +51,7 @@ export const PlayQueue = forwardRef<ItemListHandle, QueueProps>(({ listKey, sear
const mergedRef = useMergedRef(ref, tableRef);
const { getQueue } = usePlayerActions();
const queueType = usePlayerQueueType();
const followCurrentSong = useSettingsStore((state) => state.general.followCurrentSong);
const followCurrentSong = useFollowCurrentSong();
const [debouncedSearchTerm] = useDebouncedValue(searchTerm, 200);
@@ -12,12 +12,15 @@ import { Lyrics } from '/@/renderer/features/lyrics/lyrics';
import { PlayQueue } from '/@/renderer/features/now-playing/components/play-queue';
import { PlayQueueListControls } from '/@/renderer/features/now-playing/components/play-queue-list-controls';
import {
useCombinedLyricsAndVisualizer,
useFullScreenPlayerStore,
useGeneralSettings,
usePlaybackSettings,
usePlayerSong,
useSettingsStore,
useSettingsStoreActions,
useShowLyricsInSidebar,
useShowVisualizerInSidebar,
useSidebarPanelOrder,
} from '/@/renderer/store';
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
import { Flex } from '/@/shared/components/flex/flex';
@@ -43,12 +46,10 @@ export const SidebarPlayQueue = () => {
const [search, setSearch] = useState<string | undefined>(undefined);
const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
const [shouldRender, setShouldRender] = useState(!isFullScreenPlayerExpanded);
const {
combinedLyricsAndVisualizer,
showLyricsInSidebar,
showVisualizerInSidebar,
sidebarPanelOrder,
} = useGeneralSettings();
const combinedLyricsAndVisualizer = useCombinedLyricsAndVisualizer();
const showLyricsInSidebar = useShowLyricsInSidebar();
const showVisualizerInSidebar = useShowVisualizerInSidebar();
const sidebarPanelOrder = useSidebarPanelOrder();
const { type, webAudio } = usePlaybackSettings();
const showVisualizer = showVisualizerInSidebar && type === PlayerType.WEB && webAudio;
const showPanel = showLyricsInSidebar || showVisualizer;
@@ -217,9 +218,9 @@ export const SidebarPlayQueue = () => {
const PanelReorderControls = ({ panelType }: { panelType: 'lyrics' | 'visualizer' }) => {
const { t } = useTranslation();
const generalSettings = useGeneralSettings();
const { combinedLyricsAndVisualizer, sidebarPanelOrder } = generalSettings;
const { setSettings } = useSettingsStoreActions();
const sidebarPanelOrder = useSidebarPanelOrder();
const combinedLyricsAndVisualizer = useCombinedLyricsAndVisualizer();
const currentIndex = sidebarPanelOrder.indexOf(panelType);
const canMoveUp = currentIndex > 0;
@@ -238,11 +239,10 @@ const PanelReorderControls = ({ panelType }: { panelType: 'lyrics' | 'visualizer
setSettings({
general: {
...generalSettings,
sidebarPanelOrder: newOrder,
},
});
}, [canMoveUp, currentIndex, generalSettings, sidebarPanelOrder, setSettings]);
}, [canMoveUp, currentIndex, sidebarPanelOrder, setSettings]);
const handleMoveDown = useCallback(() => {
if (!canMoveDown) return;
@@ -255,17 +255,15 @@ const PanelReorderControls = ({ panelType }: { panelType: 'lyrics' | 'visualizer
setSettings({
general: {
...generalSettings,
sidebarPanelOrder: newOrder,
},
});
}, [canMoveDown, currentIndex, generalSettings, sidebarPanelOrder, setSettings]);
}, [canMoveDown, currentIndex, sidebarPanelOrder, setSettings]);
const handleClose = useCallback(() => {
if (combinedLyricsAndVisualizer && panelType === 'lyrics') {
setSettings({
general: {
...generalSettings,
showLyricsInSidebar: false,
showVisualizerInSidebar: false,
},
@@ -273,19 +271,17 @@ const PanelReorderControls = ({ panelType }: { panelType: 'lyrics' | 'visualizer
} else if (panelType === 'lyrics') {
setSettings({
general: {
...generalSettings,
showLyricsInSidebar: false,
},
});
} else if (panelType === 'visualizer') {
setSettings({
general: {
...generalSettings,
showVisualizerInSidebar: false,
},
});
}
}, [combinedLyricsAndVisualizer, generalSettings, panelType, setSettings]);
}, [combinedLyricsAndVisualizer, panelType, setSettings]);
return (
<div className={styles.panelReorderControls}>
@@ -13,17 +13,18 @@ import {
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import {
useButtonSize,
usePlayerRepeat,
usePlayerShuffle,
usePlayerSong,
usePlayerStatus,
useSettingsStore,
useSkipButtons,
} from '/@/renderer/store';
import { Icon } from '/@/shared/components/icon/icon';
import { PlayerRepeat, PlayerShuffle, PlayerStatus } from '/@/shared/types/types';
export const CenterControls = () => {
const skip = useSettingsStore((state) => state.general.skipButtons);
const skip = useSkipButtons();
const isRadioActive = useIsRadioActive();
@@ -85,7 +86,7 @@ const RadioCenterPlayButton = ({ disabled }: { disabled?: boolean }) => {
const RadioStopButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { stop } = useRadioControls();
return (
@@ -104,7 +105,7 @@ const RadioStopButton = ({ disabled }: { disabled?: boolean }) => {
const StopButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { mediaStop } = usePlayer();
return (
@@ -123,7 +124,7 @@ const StopButton = ({ disabled }: { disabled?: boolean }) => {
const ShuffleButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const shuffle = usePlayerShuffle();
const { toggleShuffle } = usePlayer();
@@ -156,7 +157,7 @@ const ShuffleButton = ({ disabled }: { disabled?: boolean }) => {
const PreviousButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { mediaPrevious } = usePlayer();
return (
@@ -175,7 +176,7 @@ const PreviousButton = ({ disabled }: { disabled?: boolean }) => {
const SkipBackwardButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { mediaSkipBackward } = usePlayer();
return (
@@ -211,7 +212,7 @@ const CenterPlayButton = ({ disabled }: { disabled?: boolean }) => {
const SkipForwardButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { mediaSkipForward } = usePlayer();
return (
@@ -233,7 +234,7 @@ const SkipForwardButton = ({ disabled }: { disabled?: boolean }) => {
const NextButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const { mediaNext } = usePlayer();
return (
@@ -252,7 +253,7 @@ const NextButton = ({ disabled }: { disabled?: boolean }) => {
const RepeatButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
const repeat = usePlayerRepeat();
const { toggleRepeat } = usePlayer();
@@ -298,7 +299,7 @@ const RepeatButton = ({ disabled }: { disabled?: boolean }) => {
const ShuffleAllButton = ({ disabled }: { disabled?: boolean }) => {
const { t } = useTranslation();
const buttonSize = useSettingsStore((state) => state.general.buttonSize);
const buttonSize = useButtonSize();
return (
<PlayerButton
@@ -7,8 +7,7 @@ import styles from './full-screen-player-image.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { AppRoute } from '/@/renderer/router/routes';
import { usePlayerData, usePlayerSong } from '/@/renderer/store';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import { useNativeAspectRatio, usePlayerData, usePlayerSong } from '/@/renderer/store';
import { Badge } from '/@/shared/components/badge/badge';
import { Center } from '/@/shared/components/center/center';
import { Flex } from '/@/shared/components/flex/flex';
@@ -48,7 +47,7 @@ const ImageWithPlaceholder = ({
className,
...props
}: HTMLMotionProps<'img'> & { placeholder?: string }) => {
const nativeAspectRatio = useSettingsStore((store) => store.general.nativeAspectRatio);
const nativeAspectRatio = useNativeAspectRatio();
if (!props.src) {
return (
@@ -7,9 +7,9 @@ import styles from './mobile-fullscreen-player.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import {
useFullScreenPlayerStore,
useImageRes,
usePlayerData,
usePlayerSong,
useSettingsStore,
} from '/@/renderer/store';
import { Center } from '/@/shared/components/center/center';
import { Icon } from '/@/shared/components/icon/icon';
@@ -78,7 +78,7 @@ export const MobileFullscreenPlayerAlbumArt = () => {
const mainImageRef = useRef<HTMLImageElement | null>(null);
const [mainImageDimensions, setMainImageDimensions] = useState({ idealSize: 1000 });
const albumArtRes = useSettingsStore((store) => store.general.imageRes.fullScreenPlayer);
const { fullScreenPlayer: albumArtRes } = useImageRes();
const { useImageAspectRatio } = useFullScreenPlayerStore();
const currentSong = usePlayerSong();
const { nextSong } = usePlayerData();
@@ -12,10 +12,12 @@ import {
usePlayerStatus,
} from '/@/renderer/store';
import {
useGeneralSettings,
useCombinedLyricsAndVisualizer,
usePlaybackSettings,
useSettingsStore,
useSettingsStoreActions,
useShowLyricsInSidebar,
useShowVisualizerInSidebar,
} from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Popover } from '/@/shared/components/popover/popover';
@@ -49,7 +51,9 @@ export const PlayerConfig = () => {
const { setCrossfadeDuration, setCrossfadeStyle, setQueueType, setSpeed, setTransitionType } =
usePlayerActions();
const preservePitch = useSettingsStore((state) => state.playback.preservePitch);
const generalSettings = useGeneralSettings();
const showLyricsInSidebar = useShowLyricsInSidebar();
const showVisualizerInSidebar = useShowVisualizerInSidebar();
const combinedLyricsAndVisualizer = useCombinedLyricsAndVisualizer();
const playbackSettings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
@@ -325,11 +329,10 @@ export const PlayerConfig = () => {
{
component: (
<Switch
defaultChecked={generalSettings.showLyricsInSidebar}
defaultChecked={showLyricsInSidebar}
onChange={(e) => {
setSettings({
general: {
...generalSettings,
showLyricsInSidebar: e.currentTarget.checked,
},
});
@@ -342,11 +345,10 @@ export const PlayerConfig = () => {
{
component: (
<Switch
defaultChecked={generalSettings.showVisualizerInSidebar}
defaultChecked={showVisualizerInSidebar}
onChange={(e) => {
setSettings({
general: {
...generalSettings,
showVisualizerInSidebar: e.currentTarget.checked,
},
});
@@ -359,11 +361,10 @@ export const PlayerConfig = () => {
{
component: (
<Switch
defaultChecked={generalSettings.combinedLyricsAndVisualizer}
defaultChecked={combinedLyricsAndVisualizer}
onChange={(e) => {
setSettings({
general: {
...generalSettings,
combinedLyricsAndVisualizer: e.currentTarget.checked,
},
});
@@ -395,7 +396,9 @@ export const PlayerConfig = () => {
setTransitionType,
setCrossfadeStyle,
setPreservePitch,
generalSettings,
showLyricsInSidebar,
showVisualizerInSidebar,
combinedLyricsAndVisualizer,
]);
return (
@@ -10,8 +10,8 @@ import { useSongUrl } from '/@/renderer/features/player/audio-player/hooks/use-s
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import {
BarAlign,
useGeneralSettings,
usePlaybackSettings,
usePlayerbarSlider,
usePlayerSong,
usePlayerTimestamp,
} from '/@/renderer/store';
@@ -22,7 +22,7 @@ import { Text } from '/@/shared/components/text/text';
export const PlayerbarWaveform = () => {
const currentSong = usePlayerSong();
const { transcode } = usePlaybackSettings();
const { playerbarSlider } = useGeneralSettings();
const playerbarSlider = usePlayerbarSlider();
const currentTime = usePlayerTimestamp();
const containerRef = useRef<HTMLDivElement>(null);
const { mediaSeekToTimestamp } = usePlayer();
@@ -9,11 +9,11 @@ import { MobilePlayerbar } from '/@/renderer/features/player/components/mobile-p
import { RightControls } from '/@/renderer/features/player/components/right-controls';
import { useIsMobile } from '/@/renderer/hooks/use-is-mobile';
import { useFullScreenPlayerStore, useSetFullScreenPlayerStore } from '/@/renderer/store';
import { useGeneralSettings } from '/@/renderer/store/settings.store';
import { usePlayerbarOpenDrawer } from '/@/renderer/store';
import { PlaybackSelectors } from '/@/shared/constants/playback-selectors';
export const Playerbar = () => {
const { playerbarOpenDrawer } = useGeneralSettings();
const playerbarOpenDrawer = usePlayerbarOpenDrawer();
const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
const isMobile = useIsMobile();
@@ -14,16 +14,17 @@ import {
useAutoDJSettings,
useCurrentServer,
useFullScreenPlayerStore,
useGeneralSettings,
useHotkeySettings,
usePlayerData,
usePlayerMuted,
usePlayerSong,
usePlayerVolume,
useSetFullScreenPlayerStore,
useSettingsStore,
useSettingsStoreActions,
useSidebarRightExpanded,
useSideQueueType,
useVolumeWheelStep,
useVolumeWidth,
} from '/@/renderer/store';
import { useFullScreenPlayerStoreActions } from '/@/renderer/store/full-screen-player.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
@@ -114,7 +115,7 @@ const QueueButton = () => {
const { t } = useTranslation();
const isSidebarRightExpanded = useSidebarRightExpanded();
const { setSideBar } = useAppStoreActions();
const { sideQueueType } = useGeneralSettings();
const sideQueueType = useSideQueueType();
const { bindings } = useHotkeySettings();
@@ -355,8 +356,8 @@ const VolumeButton = () => {
const { bindings } = useHotkeySettings();
const volume = usePlayerVolume();
const muted = usePlayerMuted();
const { volumeWheelStep } = useGeneralSettings();
const volumeWidth = useSettingsStore((state) => state.general.volumeWidth);
const volumeWheelStep = useVolumeWheelStep();
const volumeWidth = useVolumeWidth();
const { mediaToggleMute, setVolume } = usePlayer();
const isMinWidth = useMediaQuery('(max-width: 480px)');
@@ -8,6 +8,7 @@ import {
usePlaybackSettings,
usePlayerStore,
useSettingsStore,
useSkipButtons,
useTimestampStoreBase,
} from '/@/renderer/store';
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
@@ -18,7 +19,7 @@ const mediaSession = navigator.mediaSession;
export const useMediaSession = () => {
const { mediaSession: mediaSessionEnabled } = usePlaybackSettings();
const player = usePlayer();
const skip = useSettingsStore((state) => state.general.skipButtons);
const skip = useSkipButtons();
const playbackType = useSettingsStore((state) => state.playback.type);
const isMediaSessionEnabled = useMemo(() => {
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { Fragment } from 'react/jsx-runtime';
import { AnalyticsSettings } from '/@/renderer/features/settings/components/advanced/analytics-settings';
@@ -16,7 +17,7 @@ const sections = [
{ component: CacheSettings, key: 'cache' },
];
export const AdvancedTab = () => {
export const AdvancedTab = memo(() => {
return (
<Stack gap="md">
{sections.map(({ component: Section, key }, index) => (
@@ -27,4 +28,4 @@ export const AdvancedTab = () => {
))}
</Stack>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -6,7 +7,7 @@ import {
} from '/@/renderer/features/settings/components/settings-section';
import { Switch } from '/@/shared/components/switch/switch';
export const AnalyticsSettings = () => {
export const AnalyticsSettings = memo(() => {
const { t } = useTranslation();
const handleToggleAnalytics = (disable: boolean) => {
@@ -36,4 +37,4 @@ export const AnalyticsSettings = () => {
title={t('page.setting.analytics', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,6 +1,6 @@
import { openModal } from '@mantine/modals';
import { t } from 'i18next';
import { useCallback } from 'react';
import { memo, useCallback } from 'react';
import { ExportImportSettingsModal } from '/@/renderer/components/export-import-settings-modal/export-import-settings-modal';
import {
@@ -10,7 +10,7 @@ import {
import { useSettingsForExport } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
export const ExportImportSettings = () => {
export const ExportImportSettings = memo(() => {
const settingForExport = useSettingsForExport();
const onExportSettings = useCallback(() => {
@@ -68,4 +68,4 @@ export const ExportImportSettings = () => {
title={t('page.setting.exportImport', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -9,7 +10,7 @@ import { Select } from '/@/shared/components/select/select';
const DEFAULT_LOG_LEVEL: LogLevel = process.env.NODE_ENV === 'production' ? 'info' : 'debug';
export const LoggerSettings = () => {
export const LoggerSettings = memo(() => {
const { t } = useTranslation();
const getCurrentLogLevel = (): LogLevel => {
@@ -84,4 +85,4 @@ export const LoggerSettings = () => {
title={t('page.setting.logger', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
@@ -10,7 +10,7 @@ import { Switch } from '/@/shared/components/switch/switch';
import { Text } from '/@/shared/components/text/text';
import { Textarea } from '/@/shared/components/textarea/textarea';
export const StylesSettings = () => {
export const StylesSettings = memo(() => {
const [open, setOpen] = useState(false);
const { t } = useTranslation();
@@ -108,4 +108,4 @@ export const StylesSettings = () => {
)}
</>
);
};
});
@@ -2,7 +2,7 @@ import type { IpcRendererEvent } from 'electron';
import { t } from 'i18next';
import isElectron from 'is-electron';
import { useCallback, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import i18n, { languages } from '/@/i18n/i18n';
@@ -78,7 +78,7 @@ if (isElectron()) {
});
}
export const ApplicationSettings = () => {
export const ApplicationSettings = memo(() => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const fontSettings = useFontSettings();
@@ -618,4 +618,4 @@ export const ApplicationSettings = () => {
title={t('page.setting.application', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import i18n from '/@/i18n/i18n';
@@ -34,7 +34,7 @@ const options = [
},
];
export const ImageResolutionSettings = () => {
export const ImageResolutionSettings = memo(() => {
const { t } = useTranslation();
const { setSettings } = useSettingsStoreActions();
const settings = useGeneralSettings();
@@ -108,4 +108,4 @@ export const ImageResolutionSettings = () => {
)}
</>
);
};
});
@@ -1,3 +1,5 @@
import { memo } from 'react';
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
import {
ArtistItem,
@@ -14,7 +16,7 @@ const ARTIST_ITEMS: Array<[ArtistItem, string]> = [
[ArtistItem.SIMILAR_ARTISTS, 'page.albumArtistDetail.relatedArtists'],
];
export const ArtistSettings = () => {
export const ArtistSettings = memo(() => {
const { artistItems } = useGeneralSettings();
const { setArtistItems } = useSettingsStoreActions();
@@ -27,7 +29,7 @@ export const ArtistSettings = () => {
title="setting.artistConfiguration"
/>
);
};
});
const ARTIST_RELEASE_TYPE_ITEMS: Array<[ArtistReleaseTypeItem, string]> = [
[ArtistReleaseTypeItem.APPEARS_ON, 'page.albumArtistDetail.appearsOn'],
@@ -50,7 +52,7 @@ const ARTIST_RELEASE_TYPE_ITEMS: Array<[ArtistReleaseTypeItem, string]> = [
[ArtistReleaseTypeItem.RELEASE_TYPE_SPOKENWORD, 'releaseType.secondary.spokenWord'],
];
export const ArtistReleaseTypeSettings = () => {
export const ArtistReleaseTypeSettings = memo(() => {
const { artistReleaseTypeItems } = useGeneralSettings();
const { setArtistReleaseTypeItems } = useSettingsStoreActions();
@@ -63,4 +65,4 @@ export const ArtistReleaseTypeSettings = () => {
title="setting.artistReleaseTypeConfiguration"
/>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -21,7 +22,7 @@ import { Text } from '/@/shared/components/text/text';
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
import { Play } from '/@/shared/types/types';
export const ControlSettings = () => {
export const ControlSettings = memo(() => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const playerbarSlider = usePlayerbarSlider();
@@ -486,4 +487,4 @@ export const ControlSettings = () => {
title={t('page.setting.controls', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { memo, useMemo } from 'react';
import { Fragment } from 'react/jsx-runtime';
import { ApplicationSettings } from '/@/renderer/features/settings/components/general/application-settings';
@@ -14,7 +14,7 @@ import { Divider } from '/@/shared/components/divider/divider';
import { Stack } from '/@/shared/components/stack/stack';
import { ServerFeature } from '/@/shared/types/features-types';
export const GeneralTab = () => {
export const GeneralTab = memo(() => {
const server = useCurrentServer();
const supportsSmartPlaylists = hasFeature(server, ServerFeature.PLAYLISTS_SMART);
@@ -45,4 +45,4 @@ export const GeneralTab = () => {
))}
</Stack>
);
};
});
@@ -1,3 +1,5 @@
import { memo } from 'react';
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
import {
HomeItem,
@@ -15,7 +17,7 @@ const HOME_ITEMS: Array<[string, string]> = [
[HomeItem.MOST_PLAYED, 'page.home.mostPlayed'],
];
export const HomeSettings = () => {
export const HomeSettings = memo(() => {
const { homeItems } = useGeneralSettings();
const { setHomeItems } = useSettingsStoreActions();
@@ -28,4 +30,4 @@ export const HomeSettings = () => {
title="setting.homeConfiguration"
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { languages } from '/@/i18n/i18n';
@@ -16,7 +17,7 @@ import { LyricSource } from '/@/shared/types/domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
export const LyricSettings = () => {
export const LyricSettings = memo(() => {
const { t } = useTranslation();
const settings = useLyricsSettings();
const { setSettings } = useSettingsStoreActions();
@@ -210,4 +211,4 @@ export const LyricSettings = () => {
title={t('page.setting.lyrics', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,22 +1,19 @@
import { useQuery } from '@tanstack/react-query';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import {
useCurrentServerId,
useGeneralSettings,
useSettingsStore,
useSettingsStoreActions,
} from '/@/renderer/store';
import { useCurrentServerId, useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Code } from '/@/shared/components/code/code';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { TextInput } from '/@/shared/components/text-input/text-input';
import { Text } from '/@/shared/components/text/text';
import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback';
import { Played } from '/@/shared/types/domain-types';
export const PathSettings = () => {
export const PathSettings = memo(() => {
const { t } = useTranslation();
const serverId = useCurrentServerId();
const randomSong = useQuery({
@@ -31,6 +28,37 @@ export const PathSettings = () => {
const { pathReplace, pathReplaceWith } = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
const [localPathReplace, setLocalPathReplace] = useState(pathReplace);
const [localPathReplaceWith, setLocalPathReplaceWith] = useState(pathReplaceWith);
useEffect(() => {
setLocalPathReplace(pathReplace);
}, [pathReplace]);
useEffect(() => {
setLocalPathReplaceWith(pathReplaceWith);
}, [pathReplaceWith]);
const debouncedSetPathReplace = useDebouncedCallback((value: string) => {
setSettings({
general: {
pathReplace: value,
},
});
randomSong.refetch();
}, 500);
const debouncedSetPathReplaceWith = useDebouncedCallback((value: string) => {
setSettings({
general: {
pathReplaceWith: value,
},
});
randomSong.refetch();
}, 500);
return (
<Stack>
<Group>
@@ -50,34 +78,28 @@ export const PathSettings = () => {
</Code>
<Group grow>
<TextInput
onChange={(e) =>
setSettings({
general: {
...useSettingsStore.getState().general,
pathReplace: e.currentTarget.value,
},
})
}
onChange={(e) => {
const value = e.currentTarget.value;
setLocalPathReplace(value);
debouncedSetPathReplace(value);
}}
placeholder={t('setting.pathReplace_optionRemovePrefix', {
postProcess: 'sentenceCase',
})}
value={pathReplace}
value={localPathReplace}
/>
<TextInput
onChange={(e) =>
setSettings({
general: {
...useSettingsStore.getState().general,
pathReplaceWith: e.currentTarget.value,
},
})
}
onChange={(e) => {
const value = e.currentTarget.value;
setLocalPathReplaceWith(value);
debouncedSetPathReplaceWith(value);
}}
placeholder={t('setting.pathReplace_optionAddPrefix', {
postProcess: 'sentenceCase',
})}
value={pathReplaceWith}
value={localPathReplaceWith}
/>
</Group>
</Stack>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -21,7 +22,7 @@ const QUERY_VALUE_INPUT_TYPES = [
{ label: 'String', value: 'string' },
] as const;
export const QueryBuilderSettings = () => {
export const QueryBuilderSettings = memo(() => {
const { t } = useTranslation();
const queryBuilder = useQueryBuilderSettings();
const { setSettings } = useSettingsStoreActions();
@@ -148,4 +149,4 @@ export const QueryBuilderSettings = () => {
})}
/>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -10,7 +11,7 @@ import { Slider } from '/@/shared/components/slider/slider';
import { Switch } from '/@/shared/components/switch/switch';
import { toast } from '/@/shared/components/toast/toast';
export const ScrobbleSettings = () => {
export const ScrobbleSettings = memo(() => {
const { t } = useTranslation();
const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
@@ -24,9 +25,7 @@ export const ScrobbleSettings = () => {
onChange={(e) => {
setSettings({
playback: {
...settings,
scrobble: {
...settings.scrobble,
enabled: e.currentTarget.checked,
},
},
@@ -51,9 +50,7 @@ export const ScrobbleSettings = () => {
onChange={(e) => {
setSettings({
playback: {
...settings,
scrobble: {
...settings.scrobble,
scrobbleAtPercentage: e,
},
},
@@ -79,9 +76,7 @@ export const ScrobbleSettings = () => {
if (e === '') return;
setSettings({
playback: {
...settings,
scrobble: {
...settings.scrobble,
scrobbleAtDuration: Number(e),
},
},
@@ -125,9 +120,7 @@ export const ScrobbleSettings = () => {
setSettings({
playback: {
...settings,
scrobble: {
...settings.scrobble,
notify: e.currentTarget.checked,
},
},
@@ -150,4 +143,4 @@ export const ScrobbleSettings = () => {
title={t('page.setting.scrobble', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,4 @@
import { ChangeEvent } from 'react';
import { ChangeEvent, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { SidebarReorder } from '/@/renderer/features/settings/components/general/sidebar-reorder';
@@ -9,7 +9,7 @@ import {
import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
import { Switch } from '/@/shared/components/switch/switch';
export const SidebarSettings = () => {
export const SidebarSettings = memo(() => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
@@ -17,7 +17,6 @@ export const SidebarSettings = () => {
const handleSetSidebarPlaylistList = (e: ChangeEvent<HTMLInputElement>) => {
setSettings({
general: {
...settings,
sidebarPlaylistList: e.target.checked,
},
});
@@ -26,7 +25,6 @@ export const SidebarSettings = () => {
const handleSetSidebarCollapsedNavigation = (e: ChangeEvent<HTMLInputElement>) => {
setSettings({
general: {
...settings,
sidebarCollapsedNavigation: e.target.checked,
},
});
@@ -67,7 +65,6 @@ export const SidebarSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
showLyricsInSidebar: e.currentTarget.checked,
},
});
@@ -88,7 +85,6 @@ export const SidebarSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
showVisualizerInSidebar: e.currentTarget.checked,
},
});
@@ -109,7 +105,6 @@ export const SidebarSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
combinedLyricsAndVisualizer: e.currentTarget.checked,
},
});
@@ -131,4 +126,4 @@ export const SidebarSettings = () => {
title={t('page.setting.sidebar', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,5 +1,5 @@
import isElectron from 'is-electron';
import { useMemo } from 'react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import i18n from '/@/i18n/i18n';
@@ -85,7 +85,7 @@ const renderThemeOption = ({ option }: { option: { label: string; value: string
);
};
export const ThemeSettings = () => {
export const ThemeSettings = memo(() => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
@@ -101,7 +101,6 @@ export const ThemeSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
followSystemTheme: e.currentTarget.checked,
},
});
@@ -135,7 +134,6 @@ export const ThemeSettings = () => {
setSettings({
general: {
...settings,
theme,
},
});
@@ -167,7 +165,6 @@ export const ThemeSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
themeDark: e as AppTheme,
},
});
@@ -191,7 +188,6 @@ export const ThemeSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
themeLight: e as AppTheme,
},
});
@@ -214,7 +210,6 @@ export const ThemeSettings = () => {
onChange={(e) => {
setSettings({
general: {
...settings,
useThemeAccentColor: e.currentTarget.checked,
},
});
@@ -238,7 +233,6 @@ export const ThemeSettings = () => {
onChangeEnd={(e) => {
setSettings({
general: {
...settings,
accent: e,
},
});
@@ -270,4 +264,4 @@ export const ThemeSettings = () => {
title={t('page.setting.theme', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,6 +1,6 @@
import isElectron from 'is-electron';
import debounce from 'lodash/debounce';
import { ChangeEvent, KeyboardEvent, useCallback, useMemo, useState } from 'react';
import { ChangeEvent, KeyboardEvent, memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './hotkeys-manager-settings.module.css';
@@ -120,9 +120,9 @@ const BINDINGS_MAP: Record<BindingActions, string> = {
zoomOut: i18n.t('setting.hotkey', { context: 'zoomOut', postProcess: 'sentenceCase' }),
};
export const HotkeyManagerSettings = () => {
export const HotkeyManagerSettings = memo(() => {
const { t } = useTranslation();
const { bindings, globalMediaHotkeys } = useHotkeySettings();
const { bindings } = useHotkeySettings();
const { setSettings } = useSettingsStoreActions();
const [selected, setSelected] = useState<BindingActions | null>(null);
const keyword = useSettingSearchContext();
@@ -162,7 +162,6 @@ export const HotkeyManagerSettings = () => {
setSettings({
hotkeys: {
bindings: updatedBindings,
globalMediaHotkeys,
},
});
@@ -188,13 +187,12 @@ export const HotkeyManagerSettings = () => {
setSettings({
hotkeys: {
bindings: updatedBindings,
globalMediaHotkeys,
},
});
ipc?.send('set-global-shortcuts', updatedBindings);
},
[bindings, globalMediaHotkeys, setSettings],
[bindings, setSettings],
);
const handleClearHotkey = useCallback(
@@ -207,13 +205,12 @@ export const HotkeyManagerSettings = () => {
setSettings({
hotkeys: {
bindings: updatedBindings,
globalMediaHotkeys,
},
});
ipc?.send('set-global-shortcuts', updatedBindings);
},
[bindings, globalMediaHotkeys, setSettings],
[bindings, setSettings],
);
const duplicateHotkeyMap = useMemo(() => {
@@ -367,4 +364,4 @@ export const HotkeyManagerSettings = () => {
options={options}
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { Fragment } from 'react/jsx-runtime';
import { HotkeyManagerSettings } from '/@/renderer/features/settings/components/hotkeys/hotkey-manager-settings';
@@ -13,7 +14,7 @@ const sections = [
{ component: HotkeyManagerSettings, key: 'hotkey-manager' },
];
export const HotkeysTab = () => {
export const HotkeysTab = memo(() => {
return (
<Stack gap="md">
{sections.map(({ component: Section, hidden, key }, index) => (
@@ -24,4 +25,4 @@ export const HotkeysTab = () => {
))}
</Stack>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -6,11 +7,7 @@ import {
SettingsSection,
} from '/@/renderer/features/settings/components/settings-section';
import { openRestartRequiredToast } from '/@/renderer/features/settings/restart-toast';
import {
useHotkeySettings,
usePlaybackSettings,
useSettingsStoreActions,
} from '/@/renderer/store/settings.store';
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { Switch } from '/@/shared/components/switch/switch';
import { PlayerType } from '/@/shared/types/types';
@@ -18,11 +15,9 @@ const isLinux = isElectron() ? window.api.utils.isLinux() : false;
const isDesktop = isElectron();
const localSettings = isElectron() ? window.api.localSettings : null;
export const MediaSessionSettings = () => {
export const MediaSessionSettings = memo(() => {
const { t } = useTranslation();
const { mediaSession, type: playbackType } = usePlaybackSettings();
const playbackSettings = usePlaybackSettings();
const hotkeySettings = useHotkeySettings();
const { setSettings } = useSettingsStoreActions();
function handleMediaSessionChange(e: boolean) {
@@ -31,7 +26,6 @@ export const MediaSessionSettings = () => {
localSettings!.set('global_media_hotkeys', false);
setSettings({
hotkeys: {
...hotkeySettings,
globalMediaHotkeys: false,
},
});
@@ -40,7 +34,6 @@ export const MediaSessionSettings = () => {
localSettings!.set('mediaSession', e);
setSettings({
playback: {
...playbackSettings,
mediaSession: e,
},
});
@@ -70,4 +63,4 @@ export const MediaSessionSettings = () => {
];
return <SettingsSection options={mediaSessionOptions} />;
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -11,10 +12,9 @@ import { Switch } from '/@/shared/components/switch/switch';
const localSettings = isElectron() ? window.api.localSettings : null;
export const WindowHotkeySettings = () => {
export const WindowHotkeySettings = memo(() => {
const { t } = useTranslation();
const settings = useHotkeySettings();
const playbackSettings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
const { mediaSession } = usePlaybackSettings();
@@ -28,7 +28,6 @@ export const WindowHotkeySettings = () => {
localSettings!.set('global_media_hotkeys', e.currentTarget.checked);
setSettings({
hotkeys: {
...settings,
globalMediaHotkeys: e.currentTarget.checked,
},
});
@@ -45,7 +44,6 @@ export const WindowHotkeySettings = () => {
localSettings!.set('mediaSession', false);
setSettings({
playback: {
...playbackSettings,
mediaSession: false,
},
});
@@ -64,4 +62,4 @@ export const WindowHotkeySettings = () => {
];
return <SettingsSection options={options} />;
};
});
@@ -1,5 +1,5 @@
import isElectron from 'is-electron';
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -20,7 +20,7 @@ const getAudioDevice = async () => {
return (devices || []).filter((dev: MediaDeviceInfo) => dev.kind === 'audiooutput');
};
export const AudioSettings = () => {
export const AudioSettings = memo(() => {
const { t } = useTranslation();
const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
@@ -61,7 +61,7 @@ export const AudioSettings = () => {
defaultValue={settings.type}
disabled={status === PlayerStatus.PLAYING}
onChange={(e) => {
setSettings({ playback: { ...settings, type: e as PlayerType } });
setSettings({ playback: { type: e as PlayerType } });
ipc?.send('settings-set', { property: 'playbackType', value: e });
}}
/>
@@ -84,7 +84,7 @@ export const AudioSettings = () => {
data={audioDevices}
defaultValue={settings.audioDeviceId}
disabled={settings.type !== PlayerType.WEB}
onChange={(e) => setSettings({ playback: { ...settings, audioDeviceId: e } })}
onChange={(e) => setSettings({ playback: { audioDeviceId: e } })}
/>
),
description: t('setting.audioDevice', {
@@ -100,7 +100,7 @@ export const AudioSettings = () => {
defaultChecked={settings.webAudio}
onChange={(e) => {
setSettings({
playback: { ...settings, webAudio: e.currentTarget.checked },
playback: { webAudio: e.currentTarget.checked },
});
}}
/>
@@ -121,7 +121,7 @@ export const AudioSettings = () => {
defaultChecked={settings.preservePitch}
onChange={(e) => {
setSettings({
playback: { ...settings, preservePitch: e.currentTarget.checked },
playback: { preservePitch: e.currentTarget.checked },
});
}}
/>
@@ -142,7 +142,6 @@ export const AudioSettings = () => {
onChange={(e) => {
setSettings({
playback: {
...settings,
audioFadeOnStatusChange: e.currentTarget.checked,
},
});
@@ -165,4 +164,4 @@ export const AudioSettings = () => {
title={t('page.setting.audio', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -7,7 +8,7 @@ import {
import { useAutoDJSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { NumberInput } from '/@/shared/components/number-input/number-input';
export const AutoDJSettings = () => {
export const AutoDJSettings = memo(() => {
const { t } = useTranslation();
const settings = useAutoDJSettings();
const { setSettings } = useSettingsStoreActions();
@@ -23,7 +24,6 @@ export const AutoDJSettings = () => {
onChange={(e) => {
setSettings({
autoDJ: {
...settings,
itemCount: Number(e),
},
});
@@ -47,7 +47,6 @@ export const AutoDJSettings = () => {
onChange={(e) => {
setSettings({
autoDJ: {
...settings,
timing: Number(e),
},
});
@@ -69,4 +68,4 @@ export const AutoDJSettings = () => {
title={t('setting.autoDJ', { postProcess: 'titleCase' })}
/>
);
};
});
@@ -1,5 +1,5 @@
import isElectron from 'is-electron';
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -68,7 +68,7 @@ export const getMpvProperties = (settings: SettingsState['playback']['mpvPropert
return properties;
};
export const MpvSettings = () => {
export const MpvSettings = memo(() => {
const { t } = useTranslation();
const settings = usePlaybackSettings();
const { setSettings } = useSettingsStoreActions();
@@ -114,9 +114,7 @@ export const MpvSettings = () => {
) => {
setSettings({
playback: {
...settings,
mpvProperties: {
...settings.mpvProperties,
[setting]: value,
},
},
@@ -146,7 +144,6 @@ export const MpvSettings = () => {
const handleSetExtraParameters = (data: string[]) => {
setSettings({
playback: {
...settings,
mpvExtraParameters: data,
},
});
@@ -421,4 +418,4 @@ export const MpvSettings = () => {
<SettingsSection options={replayGainOptions} />
</>
);
};
});
@@ -1,5 +1,6 @@
import isElectron from 'is-electron';
import { lazy, Suspense, useMemo } from 'react';
import { lazy, memo, Suspense, useMemo } from 'react';
import { shallow } from 'zustand/shallow';
import { AudioSettings } from '/@/renderer/features/settings/components/playback/audio-settings';
import { AutoDJSettings } from '/@/renderer/features/settings/components/playback/auto-dj-settings';
@@ -16,9 +17,14 @@ const MpvSettings = lazy(() =>
}),
);
export const PlaybackTab = () => {
const audioType = useSettingsStore((state) => state.playback.type);
const useWebAudio = useSettingsStore((state) => state.playback.webAudio);
export const PlaybackTab = memo(() => {
const { audioType, useWebAudio } = useSettingsStore(
(state) => ({
audioType: state.playback.type,
useWebAudio: state.playback.webAudio,
}),
shallow,
);
const hasFancyAudio = useMemo(() => {
return (
@@ -39,4 +45,4 @@ export const PlaybackTab = () => {
<AutoDJSettings />
</Stack>
);
};
});
@@ -1,5 +1,5 @@
import { nanoid } from 'nanoid/non-secure';
import { useCallback, useMemo } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -262,7 +262,7 @@ const FilterValueInput = ({
}
};
export const PlayerFilterSettings = () => {
export const PlayerFilterSettings = memo(() => {
const { t } = useTranslation();
const filters = useSettingsStore((state) => state.playback.filters);
const { setPlaybackFilters } = useSettingsStoreActions();
@@ -432,4 +432,4 @@ export const PlayerFilterSettings = () => {
title={t('page.setting.playerFilters', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,3 +1,4 @@
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -9,7 +10,7 @@ import { NumberInput } from '/@/shared/components/number-input/number-input';
import { Switch } from '/@/shared/components/switch/switch';
import { TextInput } from '/@/shared/components/text-input/text-input';
export const TranscodeSettings = () => {
export const TranscodeSettings = memo(() => {
const { t } = useTranslation();
const { transcode } = usePlaybackSettings();
const { setTranscodingConfig } = useSettingsStoreActions();
@@ -92,4 +93,4 @@ export const TranscodeSettings = () => {
title={t('page.setting.transcoding', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo } from 'react';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
@@ -13,38 +13,40 @@ interface SettingsOptionProps {
title: React.ReactNode | string;
}
export const SettingsOptions = ({ control, description, note, title }: SettingsOptionProps) => {
return (
<>
<Group justify="space-between" style={{ alignItems: 'center' }} wrap="nowrap">
<Stack
gap="xs"
style={{
alignSelf: 'flex-start',
display: 'flex',
maxWidth: '50%',
}}
>
<Group>
<Text isNoSelect size="md">
{title}
</Text>
{note && (
<Tooltip label={note} openDelay={0}>
<Icon icon="info" />
</Tooltip>
export const SettingsOptions = memo(
({ control, description, note, title }: SettingsOptionProps) => {
return (
<>
<Group justify="space-between" style={{ alignItems: 'center' }} wrap="nowrap">
<Stack
gap="xs"
style={{
alignSelf: 'flex-start',
display: 'flex',
maxWidth: '50%',
}}
>
<Group>
<Text isNoSelect size="md">
{title}
</Text>
{note && (
<Tooltip label={note} openDelay={0}>
<Icon icon="info" />
</Tooltip>
)}
</Group>
{React.isValidElement(description) ? (
description
) : (
<Text isMuted isNoSelect size="sm">
{description}
</Text>
)}
</Group>
{React.isValidElement(description) ? (
description
) : (
<Text isMuted isNoSelect size="sm">
{description}
</Text>
)}
</Stack>
<Group justify="flex-end">{control}</Group>
</Group>
</>
);
};
</Stack>
<Group justify="flex-end">{control}</Group>
</Group>
</>
);
},
);
@@ -1,7 +1,7 @@
import { closeAllModals, openModal } from '@mantine/modals';
import { useQueryClient } from '@tanstack/react-query';
import isElectron from 'is-electron';
import { useCallback, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -14,7 +14,7 @@ import { toast } from '/@/shared/components/toast/toast';
const browser = isElectron() ? window.api.browser : null;
export const CacheSettings = () => {
export const CacheSettings = memo(() => {
const [isClearing, setIsClearing] = useState(false);
const queryClient = useQueryClient();
const { t } = useTranslation();
@@ -115,4 +115,4 @@ export const CacheSettings = () => {
)}
</>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -16,7 +17,7 @@ import { Select } from '/@/shared/components/select/select';
import { Switch } from '/@/shared/components/switch/switch';
import { TextInput } from '/@/shared/components/text-input/text-input';
export const DiscordSettings = () => {
export const DiscordSettings = memo(() => {
const { t } = useTranslation();
const settings = useDiscordSettings();
const generalSettings = useGeneralSettings();
@@ -30,7 +31,6 @@ export const DiscordSettings = () => {
onChange={(e) => {
setSettings({
discord: {
...settings,
enabled: e.currentTarget.checked,
},
});
@@ -58,7 +58,6 @@ export const DiscordSettings = () => {
onBlur={(e) => {
setSettings({
discord: {
...settings,
clientId: e.currentTarget.value,
},
});
@@ -84,7 +83,6 @@ export const DiscordSettings = () => {
onChange={(e) => {
setSettings({
discord: {
...settings,
showPaused: e.currentTarget.checked,
},
});
@@ -107,7 +105,6 @@ export const DiscordSettings = () => {
onChange={(e) => {
setSettings({
discord: {
...settings,
showAsListening: e.currentTarget.checked,
},
});
@@ -150,7 +147,6 @@ export const DiscordSettings = () => {
if (!e) return;
setSettings({
discord: {
...settings,
displayType: e as DiscordDisplayType,
},
});
@@ -195,7 +191,6 @@ export const DiscordSettings = () => {
if (!e) return;
setSettings({
discord: {
...settings,
linkType: e as DiscordLinkType,
},
});
@@ -222,7 +217,6 @@ export const DiscordSettings = () => {
onChange={(e) => {
setSettings({
discord: {
...settings,
showServerImage: e.currentTarget.checked,
},
});
@@ -248,7 +242,6 @@ export const DiscordSettings = () => {
onBlur={(e) => {
setSettings({
general: {
...generalSettings,
lastfmApiKey: e.currentTarget.value,
},
});
@@ -274,4 +267,4 @@ export const DiscordSettings = () => {
title={t('page.setting.discord', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -17,7 +18,7 @@ const PASSWORD_SETTINGS: { label: string; value: string }[] = [
{ label: 'KDE 6 (kwallet6)', value: 'kwallet6' },
];
export const PasswordSettings = () => {
export const PasswordSettings = memo(() => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
@@ -53,4 +54,4 @@ export const PasswordSettings = () => {
];
return <SettingsSection divider={false} options={updateOptions} />;
};
});
@@ -1,5 +1,6 @@
import isElectron from 'is-electron';
import debounce from 'lodash/debounce';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { SettingsSection } from '/@/renderer/features/settings/components/settings-section';
@@ -12,7 +13,7 @@ import { toast } from '/@/shared/components/toast/toast';
const remote = isElectron() ? window.api.remote : null;
export const RemoteSettings = () => {
export const RemoteSettings = memo(() => {
const { t } = useTranslation();
const settings = useRemoteSettings();
const { setSettings } = useSettingsStoreActions();
@@ -25,7 +26,6 @@ export const RemoteSettings = () => {
if (errorMsg === null) {
setSettings({
remote: {
...settings,
enabled,
},
});
@@ -44,7 +44,6 @@ export const RemoteSettings = () => {
if (!errorMsg) {
setSettings({
remote: {
...settings,
port,
},
});
@@ -115,7 +114,6 @@ export const RemoteSettings = () => {
remote!.updateUsername(username);
setSettings({
remote: {
...settings,
username,
},
});
@@ -139,7 +137,6 @@ export const RemoteSettings = () => {
remote!.updatePassword(password);
setSettings({
remote: {
...settings,
password,
},
});
@@ -161,4 +158,4 @@ export const RemoteSettings = () => {
title={t('page.setting.remote', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -13,10 +14,10 @@ const localSettings = isElectron() ? window.api.localSettings : null;
const utils = isElectron() ? window.api.utils : null;
function disableAutoUpdates(): boolean {
return !isElectron() || utils?.disableAutoUpdates();
return Boolean(!isElectron() || utils?.disableAutoUpdates());
}
export const UpdateSettings = () => {
export const UpdateSettings = memo(() => {
const { t } = useTranslation();
const settings = useWindowSettings();
const { setSettings } = useSettingsStoreActions();
@@ -49,7 +50,6 @@ export const UpdateSettings = () => {
localSettings?.set('release_channel', value);
setSettings({
window: {
...settings,
releaseChannel: value as 'beta' | 'latest',
},
});
@@ -74,7 +74,6 @@ export const UpdateSettings = () => {
localSettings?.set('disable_auto_updates', e.currentTarget.checked);
setSettings({
window: {
...settings,
disableAutoUpdate: e.currentTarget.checked,
},
});
@@ -96,4 +95,4 @@ export const UpdateSettings = () => {
title={t('page.setting.updates', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -20,7 +21,7 @@ const WINDOW_BAR_OPTIONS = [
const localSettings = isElectron() ? window.api.localSettings : null;
export const WindowSettings = () => {
export const WindowSettings = memo(() => {
const { t } = useTranslation();
const settings = useWindowSettings();
const { setSettings } = useSettingsStoreActions();
@@ -50,7 +51,6 @@ export const WindowSettings = () => {
localSettings?.set('window_window_bar_style', e as Platform);
setSettings({
window: {
...settings,
windowBarStyle: e as Platform,
},
});
@@ -77,7 +77,6 @@ export const WindowSettings = () => {
if (e.currentTarget.checked) {
setSettings({
window: {
...settings,
tray: true,
},
});
@@ -88,7 +87,6 @@ export const WindowSettings = () => {
setSettings({
window: {
...settings,
exitToTray: false,
minimizeToTray: false,
startMinimized: false,
@@ -120,7 +118,6 @@ export const WindowSettings = () => {
localSettings?.set('window_minimize_to_tray', e.currentTarget.checked);
setSettings({
window: {
...settings,
minimizeToTray: e.currentTarget.checked,
},
});
@@ -145,7 +142,6 @@ export const WindowSettings = () => {
localSettings?.set('window_exit_to_tray', e.currentTarget.checked);
setSettings({
window: {
...settings,
exitToTray: e.currentTarget.checked,
},
});
@@ -170,7 +166,6 @@ export const WindowSettings = () => {
localSettings?.set('window_start_minimized', e.currentTarget.checked);
setSettings({
window: {
...settings,
startMinimized: e.currentTarget.checked,
},
});
@@ -198,7 +193,6 @@ export const WindowSettings = () => {
);
setSettings({
window: {
...settings,
preventSleepOnPlayback: e.currentTarget.checked,
},
});
@@ -220,4 +214,4 @@ export const WindowSettings = () => {
title={t('page.setting.application', { postProcess: 'sentenceCase' })}
/>
);
};
});
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import { memo } from 'react';
import { Fragment } from 'react/jsx-runtime';
import { DiscordSettings } from '/@/renderer/features/settings/components/window/discord-settings';
@@ -17,7 +18,7 @@ const sections = [
{ component: PasswordSettings, hidden: !utils?.isLinux(), key: 'password' },
];
export const WindowTab = () => {
export const WindowTab = memo(() => {
return (
<Stack gap="md">
{sections.map(({ component: Section, hidden, key }, index) => (
@@ -28,4 +29,4 @@ export const WindowTab = () => {
))}
</Stack>
);
};
});
@@ -17,7 +17,8 @@ import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
import {
SidebarItemType,
useCurrentServer,
useGeneralSettings,
useSidebarCollapsedNavigation,
useSidebarItems,
useWindowSettings,
} from '/@/renderer/store';
import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
@@ -32,7 +33,8 @@ export const CollapsedSidebar = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { windowBarStyle } = useWindowSettings();
const { sidebarCollapsedNavigation, sidebarItems } = useGeneralSettings();
const sidebarCollapsedNavigation = useSidebarCollapsedNavigation();
const sidebarItems = useSidebarItems();
const currentServer = useCurrentServer();
const translatedSidebarItemMap = useMemo(
@@ -11,7 +11,11 @@ import {
SidebarPlaylistList,
SidebarSharedPlaylistList,
} from '/@/renderer/features/sidebar/components/sidebar-playlist-list';
import { SidebarItemType, useGeneralSettings } from '/@/renderer/store/settings.store';
import {
SidebarItemType,
useSidebarItems,
useSidebarPlaylistList,
} from '/@/renderer/store/settings.store';
import { Accordion } from '/@/shared/components/accordion/accordion';
import { Group } from '/@/shared/components/group/group';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
@@ -19,7 +23,7 @@ import { Text } from '/@/shared/components/text/text';
export const MobileSidebar = () => {
const { t } = useTranslation();
const { sidebarPlaylistList } = useGeneralSettings();
const sidebarPlaylistList = useSidebarPlaylistList();
const translatedSidebarItemMap = useMemo(
() => ({
@@ -38,7 +42,7 @@ export const MobileSidebar = () => {
[t],
);
const { sidebarItems } = useGeneralSettings();
const sidebarItems = useSidebarItems();
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
if (!sidebarItems) return [];
@@ -25,7 +25,8 @@ import {
} from '/@/renderer/store';
import {
SidebarItemType,
useGeneralSettings,
useSidebarItems,
useSidebarPlaylistList,
useWindowSettings,
} from '/@/renderer/store/settings.store';
import { Accordion } from '/@/shared/components/accordion/accordion';
@@ -41,7 +42,7 @@ import { Platform } from '/@/shared/types/types';
export const Sidebar = () => {
const { t } = useTranslation();
const { sidebarPlaylistList } = useGeneralSettings();
const sidebarPlaylistList = useSidebarPlaylistList();
const translatedSidebarItemMap = useMemo(
() => ({
@@ -62,7 +63,7 @@ export const Sidebar = () => {
[t],
);
const { sidebarItems } = useGeneralSettings();
const sidebarItems = useSidebarItems();
const { windowBarStyle } = useWindowSettings();
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
const isRadioPlaying = useRadioStore((state) => state.isPlaying);
@@ -127,9 +127,7 @@ const useUpdateAudioMotionAnalyzer = () => {
) => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
[property]: value,
},
},
@@ -149,9 +147,7 @@ const useUpdateButterchurn = () => {
) => {
setSettings({
visualizer: {
...visualizer,
butterchurn: {
...visualizer.butterchurn,
[property]: value,
},
},
@@ -177,7 +173,6 @@ export const VisualizerSettingsForm = () => {
const handleTypeChange = (value: string) => {
setSettings({
visualizer: {
...visualizer,
type: value as 'audiomotionanalyzer' | 'butterchurn',
},
});
@@ -464,9 +459,7 @@ const PresetSettings = () => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
...presetValue,
},
},
@@ -501,9 +494,7 @@ const PresetSettings = () => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
presets: updatedPresets,
},
},
@@ -517,9 +508,7 @@ const PresetSettings = () => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
presets: [...visualizer.audiomotionanalyzer.presets, newPreset],
},
},
@@ -644,9 +633,7 @@ const PresetSettings = () => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
presets: updatedPresets,
},
},
@@ -776,9 +763,7 @@ const PresetSettings = () => {
setSettings({
visualizer: {
...visualizer,
audiomotionanalyzer: {
...visualizer.audiomotionanalyzer,
...configValue,
},
},
@@ -6,13 +6,13 @@ import styles from './visualizer.module.css';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { openVisualizerSettingsModal } from '/@/renderer/features/player/utils/open-visualizer-settings-modal';
import { ComponentErrorBoundary } from '/@/renderer/features/shared/components/component-error-boundary';
import { useSettingsStore } from '/@/renderer/store';
import { useAccent, useSettingsStore } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
const VisualizerInner = () => {
const { webAudio } = useWebAudio();
const canvasRef = createRef<HTMLDivElement>();
const accent = useSettingsStore((store) => store.general.accent);
const accent = useAccent();
const visualizer = useSettingsStore((store) => store.visualizer);
const opacity = useSettingsStore((store) => store.visualizer.audiomotionanalyzer.opacity);
const [motion, setMotion] = useState<AudioMotionAnalyzer>();
@@ -277,12 +277,9 @@ const VisualizerInner = () => {
visualizer.loadPreset(nextPreset, currentSettings.blendTime || 0.0);
// Update currentPreset in settings
const currentVisualizer = useSettingsStore.getState().visualizer;
setSettings({
visualizer: {
...currentVisualizer,
butterchurn: {
...currentVisualizer.butterchurn,
currentPreset: nextPresetName,
},
},
@@ -2,14 +2,14 @@ import isElectron from 'is-electron';
import { useLocation } from 'react-router';
import { AppRoute } from '/@/renderer/router/routes';
import { useGeneralSettings, useSidebarRightExpanded, useWindowSettings } from '/@/renderer/store';
import { useSidebarRightExpanded, useSideQueueType, useWindowSettings } from '/@/renderer/store';
import { Platform } from '/@/shared/types/types';
export const useShouldPadTitlebar = () => {
const location = useLocation();
const isSidebarExpanded = useSidebarRightExpanded();
const isQueuePage = location.pathname === AppRoute.NOW_PLAYING;
const { sideQueueType } = useGeneralSettings();
const sideQueueType = useSideQueueType();
const { windowBarStyle } = useWindowSettings();
const conditions = [
-1
View File
@@ -13,7 +13,6 @@ import { Platform, PlayerType } from '/@/shared/types/types';
if (!isElectron()) {
useSettingsStore.getState().actions.setSettings({
playback: {
...useSettingsStore.getState().playback,
type: PlayerType.WEB,
},
});
@@ -9,8 +9,7 @@ import styles from './main-content.module.css';
import { FullScreenOverlay } from '/@/renderer/layouts/default-layout/full-screen-overlay';
import { LeftSidebar } from '/@/renderer/layouts/default-layout/left-sidebar';
import { RightSidebar } from '/@/renderer/layouts/default-layout/right-sidebar';
import { useAppStore, useAppStoreActions } from '/@/renderer/store';
import { useGeneralSettings } from '/@/renderer/store/settings.store';
import { useAppStore, useAppStoreActions, useSideQueueType } from '/@/renderer/store';
import { constrainRightSidebarWidth, constrainSidebarWidth } from '/@/renderer/utils';
import { Spinner } from '/@/shared/components/spinner/spinner';
@@ -27,7 +26,7 @@ export const MainContent = ({ shell }: { shell?: boolean }) => {
shallow,
);
const { setSideBar } = useAppStoreActions();
const { sideQueueType } = useGeneralSettings();
const sideQueueType = useSideQueueType();
const [isResizing, setIsResizing] = useState(false);
const [isResizingRight, setIsResizingRight] = useState(false);
@@ -3,10 +3,10 @@ import clsx from 'clsx';
import styles from './player-bar.module.css';
import { Playerbar } from '/@/renderer/features/player/components/playerbar';
import { useGeneralSettings } from '/@/renderer/store/settings.store';
import { usePlayerbarOpenDrawer } from '/@/renderer/store';
export const PlayerBar = () => {
const { playerbarOpenDrawer } = useGeneralSettings();
const playerbarOpenDrawer = usePlayerbarOpenDrawer();
return (
<div
@@ -4,7 +4,7 @@ import styles from './right-sidebar.module.css';
import { SidebarPlayQueue } from '/@/renderer/features/now-playing/components/sidebar-play-queue';
import { ResizeHandle } from '/@/renderer/features/shared/components/resize-handle';
import { useAppStore, useGeneralSettings } from '/@/renderer/store';
import { useAppStore, useSideQueueType } from '/@/renderer/store';
// const queueDrawerVariants: Variants = {
// closed: (windowBarStyle) => ({
@@ -55,7 +55,7 @@ export const RightSidebar = forwardRef(
ref: Ref<HTMLDivElement>,
) => {
const rightExpanded = useAppStore((state) => state.sidebar.rightExpanded);
const { sideQueueType } = useGeneralSettings();
const sideQueueType = useSideQueueType();
return (
<>
+5 -6
View File
@@ -10,9 +10,9 @@ import { MobileLayout } from '/@/renderer/layouts/mobile-layout/mobile-layout';
import { AppRoute } from '/@/renderer/router/routes';
import {
useCommandPalette,
useGeneralSettings,
useHotkeySettings,
useSettingsStoreActions,
useZoomFactor,
} from '/@/renderer/store';
import { HotkeyItem, useHotkeys } from '/@/shared/hooks/use-hotkeys';
@@ -45,23 +45,22 @@ export const ResponsiveLayout = ({ shell }: ResponsiveLayoutProps) => {
const LayoutHotkeys = () => {
const navigate = useNavigate();
const localSettings = isElectron() ? window.api.localSettings : null;
const settings = useGeneralSettings();
const zoomFactor = useZoomFactor();
const { setSettings } = useSettingsStoreActions();
const { bindings } = useHotkeySettings();
const { opened, ...handlers } = useCommandPalette();
const updateZoom = (increase: number) => {
const newVal = settings.zoomFactor + increase;
const newVal = zoomFactor + increase;
if (newVal > 300 || newVal < 50 || !isElectron()) return;
setSettings({
general: {
...settings,
zoomFactor: newVal,
},
});
localSettings?.setZoomFactor(settings.zoomFactor);
localSettings?.setZoomFactor(zoomFactor);
};
localSettings?.setZoomFactor(settings.zoomFactor);
localSettings?.setZoomFactor(zoomFactor);
const zoomHotkeys: HotkeyItem[] = [
[bindings.zoomIn.hotkey, () => updateZoom(5)],
+137 -6
View File
@@ -1,4 +1,5 @@
import isElectron from 'is-electron';
import merge from 'lodash/merge';
import { generatePath } from 'react-router';
import { z } from 'zod';
import { devtools, persist } from 'zustand/middleware';
@@ -42,6 +43,17 @@ type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
const deepMergeIntoState = <T extends Record<string, any>>(
state: T,
updates: DeepPartial<T>,
): void => {
// Skip 'actions' property
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { actions, ...updatesWithoutActions } = updates as any;
merge(state, updatesWithoutActions);
};
const HomeItemSchema = z.enum([
'genres',
'mostPlayed',
@@ -767,7 +779,7 @@ export interface SettingsSlice extends z.infer<typeof SettingsStateSchema> {
setHomeItems: (item: SortableItem<HomeItem>[]) => void;
setList: (type: ItemListKey, data: DeepPartial<ItemListSettings>) => void;
setPlaybackFilters: (filters: PlayerFilter[]) => void;
setSettings: (data: Partial<SettingsState>) => void;
setSettings: (data: DeepPartial<SettingsState>) => void;
setSidebarItems: (items: SidebarItemType[]) => void;
setTable: (type: ItemListKey, data: DataTableProps) => void;
setTranscodingConfig: (config: TranscodingConfig) => void;
@@ -1642,7 +1654,7 @@ const getInitialState = (): SettingsState => {
export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
persist(
devtools(
immer((set, get) => ({
immer((set) => ({
actions: {
reset: () => {
const freshState = getInitialState();
@@ -1723,7 +1735,9 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
});
},
setSettings: (data) => {
set({ ...get(), ...data });
set((state) => {
deepMergeIntoState(state, data);
});
},
setSidebarItems: (items: SidebarItemType[]) => {
set((state) => {
@@ -1980,11 +1994,128 @@ export const useListSettings = (type: ItemListKey) =>
shallow,
) as ItemListSettings;
export const usePrimaryColor = () => useSettingsStore((store) => store.general.accent);
export const usePrimaryColor = () => useSettingsStore((store) => store.general.accent, shallow);
export const usePlayerbarSlider = () => useSettingsStore((store) => store.general.playerbarSlider);
export const usePlayerbarSlider = () =>
useSettingsStore((store) => store.general.playerbarSlider, shallow);
export const useGenreTarget = () => useSettingsStore((store) => store.general.genreTarget);
export const useGenreTarget = () => useSettingsStore((store) => store.general.genreTarget, shallow);
export const useLanguage = () => useSettingsStore((state) => state.general.language, shallow);
export const useAccent = () => useSettingsStore((state) => state.general.accent, shallow);
export const useNativeAspectRatio = () =>
useSettingsStore((state) => state.general.nativeAspectRatio, shallow);
export const useButtonSize = () => useSettingsStore((state) => state.general.buttonSize, shallow);
export const useSkipButtons = () => useSettingsStore((state) => state.general.skipButtons, shallow);
export const useImageRes = () => useSettingsStore((state) => state.general.imageRes, shallow);
export const useVolumeWidth = () => useSettingsStore((state) => state.general.volumeWidth, shallow);
export const useFollowCurrentSong = () =>
useSettingsStore((state) => state.general.followCurrentSong, shallow);
export const useThemeSettings = () =>
useSettingsStore(
(state) => ({
followSystemTheme: state.general.followSystemTheme,
theme: state.general.theme,
themeDark: state.general.themeDark,
themeLight: state.general.themeLight,
useThemeAccentColor: state.general.useThemeAccentColor,
}),
shallow,
);
export const useSideQueueType = () =>
useSettingsStore((state) => state.general.sideQueueType, shallow);
export const useVolumeWheelStep = () =>
useSettingsStore((state) => state.general.volumeWheelStep, shallow);
export const useSidebarPlaylistList = () =>
useSettingsStore((state) => state.general.sidebarPlaylistList, shallow);
export const useSidebarItems = () =>
useSettingsStore((state) => state.general.sidebarItems, shallow);
export const useSidebarCollapsedNavigation = () =>
useSettingsStore((state) => state.general.sidebarCollapsedNavigation, shallow);
export const usePlayerbarOpenDrawer = () =>
useSettingsStore((state) => state.general.playerbarOpenDrawer, shallow);
export const useShowRatings = () => useSettingsStore((state) => state.general.showRatings, shallow);
export const useArtistRadioCount = () =>
useSettingsStore((state) => state.general.artistRadioCount, shallow);
export const useArtistBackground = () =>
useSettingsStore(
(state) => ({
artistBackground: state.general.artistBackground,
artistBackgroundBlur: state.general.artistBackgroundBlur,
}),
shallow,
);
export const useAlbumBackground = () =>
useSettingsStore(
(state) => ({
albumBackground: state.general.albumBackground,
albumBackgroundBlur: state.general.albumBackgroundBlur,
}),
shallow,
);
export const useExternalLinks = () =>
useSettingsStore(
(state) => ({
externalLinks: state.general.externalLinks,
lastFM: state.general.lastFM,
musicBrainz: state.general.musicBrainz,
}),
shallow,
);
export const useHomeFeature = () => useSettingsStore((state) => state.general.homeFeature, shallow);
export const useHomeItems = () => useSettingsStore((state) => state.general.homeItems, shallow);
export const useArtistItems = () => useSettingsStore((state) => state.general.artistItems, shallow);
export const useArtistReleaseTypeItems = () =>
useSettingsStore((state) => state.general.artistReleaseTypeItems, shallow);
export const useZoomFactor = () => useSettingsStore((state) => state.general.zoomFactor, shallow);
export const usePathReplace = () =>
useSettingsStore(
(state) => ({
pathReplace: state.general.pathReplace,
pathReplaceWith: state.general.pathReplaceWith,
}),
shallow,
);
export const useLastfmApiKey = () =>
useSettingsStore((state) => state.general.lastfmApiKey, shallow);
export const useSidebarPanelOrder = () =>
useSettingsStore((state) => state.general.sidebarPanelOrder, shallow);
export const useCombinedLyricsAndVisualizer = () =>
useSettingsStore((state) => state.general.combinedLyricsAndVisualizer, shallow);
export const useShowLyricsInSidebar = () =>
useSettingsStore((state) => state.general.showLyricsInSidebar, shallow);
export const useShowVisualizerInSidebar = () =>
useSettingsStore((state) => state.general.showVisualizerInSidebar, shallow);
export const useAutoDJSettings = () => useSettingsStore((store) => store.autoDJ, shallow);
+12 -7
View File
@@ -1,7 +1,12 @@
import { useMantineColorScheme } from '@mantine/core';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import {
useAccent,
useFontSettings,
useNativeAspectRatio,
useThemeSettings,
} from '/@/renderer/store/settings.store';
import { createMantineTheme } from '/@/renderer/themes/mantine-theme';
import { getAppTheme } from '/@/shared/themes/app-theme';
import { AppTheme, AppThemeConfiguration } from '/@/shared/themes/app-theme-types';
@@ -36,15 +41,15 @@ export const THEME_DATA = [
];
export const useAppTheme = (overrideTheme?: AppTheme) => {
const accent = useSettingsStore((store) => store.general.accent);
const nativeImageAspect = useSettingsStore((store) => store.general.nativeAspectRatio);
const { builtIn, custom, system, type } = useSettingsStore((state) => state.font);
const accent = useAccent();
const nativeImageAspect = useNativeAspectRatio();
const { builtIn, custom, system, type } = useFontSettings();
const textStyleRef = useRef<HTMLStyleElement | null>(null);
const loadedStylesheetsRef = useRef<Set<string>>(new Set());
const getCurrentTheme = () => window.matchMedia('(prefers-color-scheme: dark)').matches;
const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme());
const { followSystemTheme, theme, themeDark, themeLight, useThemeAccentColor } =
useSettingsStore((state) => state.general);
useThemeSettings();
const mqListener = (e: any) => {
setIsDarkTheme(e.matches);
@@ -263,11 +268,11 @@ export const useColorScheme = () => {
};
export const useAppThemeColors = () => {
const accent = useSettingsStore((store) => store.general.accent);
const accent = useAccent();
const getCurrentTheme = () => window.matchMedia('(prefers-color-scheme: dark)').matches;
const [isDarkTheme] = useState(getCurrentTheme());
const { followSystemTheme, theme, themeDark, themeLight, useThemeAccentColor } =
useSettingsStore((state) => state.general);
useThemeSettings();
const getSelectedTheme = () => {
if (followSystemTheme) {