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
@@ -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>
);
};
});