diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 58c179f37..33549a53c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -6,6 +6,8 @@ "selectRangeOfItems": "Select a range of items", "clearQueue": "Clear queue", "goToCurrent": "Go to current item", + "collapseAllFolders": "Collapse all folders", + "expandAllFolders": "Expand all folders", "createPlaylist": "Create $t(entity.playlist, {\"count\": 1})", "createRadioStation": "Create $t(entity.radioStation, {\"count\": 1})", "deletePlaylist": "Delete $t(entity.playlist, {\"count\": 1})", @@ -56,6 +58,7 @@ "albumPeak": "Album peak", "areYouSure": "Are you sure?", "ascending": "Ascending", + "back": "Back", "backward": "Backward", "biography": "Biography", "bitDepth": "Bit depth", @@ -1044,6 +1047,19 @@ "sidebarConfiguration": "Sidebar configuration", "playerItemConfiguration_description": "Configure what items are shown, and in what order, on the fullscreen player", "playerItemConfiguration": "Player item configuration", + "sidebarPlaylistFolders_description": "Create a folder view for playlists that include the configured separator in the name", + "sidebarPlaylistFolders": "Enable folders", + "sidebarPlaylistFolderSeparator_description": "Character (or string) that separates folder levels in a playlist name", + "sidebarPlaylistFolderSeparator": "Folder separator", + "sidebarPlaylistFolderView_description": "How folders are displayed in the sidebar", + "sidebarPlaylistFolderView": "Folder view", + "sidebarPlaylistFolderView_optionSingle": "Single folder", + "sidebarPlaylistFolderView_optionTree": "Tree view", + "sidebarPlaylistFolderView_optionNavigation": "Navigation view", + "sidebarPlaylistFolderTreeIndent_description": "Pixels each tree level is indented", + "sidebarPlaylistFolderTreeIndent": "Tree indent", + "sidebarPlaylistFolderTreeLineColor_description": "Color of the connecting tree lines (leave empty for theme default)", + "sidebarPlaylistFolderTreeLineColor": "Tree line color", "sidebarPlaylistList_description": "Show or hide the playlist list in the sidebar", "sidebarPlaylistList": "Sidebar playlist list", "sidebarPlaylistSorting_description": "Allows manual playlist sorting in the sidebar using drag and drop instead of the default server order", diff --git a/src/renderer/features/settings/components/general/sidebar-settings.tsx b/src/renderer/features/settings/components/general/sidebar-settings.tsx index 1cfa4404f..b551119ef 100644 --- a/src/renderer/features/settings/components/general/sidebar-settings.tsx +++ b/src/renderer/features/settings/components/general/sidebar-settings.tsx @@ -7,15 +7,28 @@ import { SettingsSection, } from '/@/renderer/features/settings/components/settings-section'; import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store'; +import { ColorInput } from '/@/shared/components/color-input/color-input'; +import { NumberInput } from '/@/shared/components/number-input/number-input'; +import { Select } from '/@/shared/components/select/select'; import { Switch } from '/@/shared/components/switch/switch'; import { TextInput } from '/@/shared/components/text-input/text-input'; import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback'; +type FolderView = 'navigation' | 'single' | 'tree'; + export const SidebarSettings = memo(() => { const { t } = useTranslation(); const settings = useGeneralSettings(); const { setSettings } = useSettingsStoreActions(); + const handleSetSidebarPlaylistFolders = (e: ChangeEvent) => { + setSettings({ + general: { + sidebarPlaylistFolders: e.target.checked, + }, + }); + }; + const handleSetSidebarPlaylistList = (e: ChangeEvent) => { setSettings({ general: { @@ -56,6 +69,45 @@ export const SidebarSettings = memo(() => { }); }, 500); + const [localSeparator, setLocalSeparator] = useState(settings.sidebarPlaylistFolderSeparator); + + useEffect(() => { + setLocalSeparator(settings.sidebarPlaylistFolderSeparator); + }, [settings.sidebarPlaylistFolderSeparator]); + + const debouncedSetSeparator = useDebouncedCallback((value: string) => { + if (value.length === 0) return; + setSettings({ + general: { + sidebarPlaylistFolderSeparator: value, + }, + }); + }, 500); + + const foldersEnabled = settings.sidebarPlaylistFolders; + const isTreeView = settings.sidebarPlaylistFolderView === 'tree'; + + const folderViewOptions: Array<{ label: string; value: FolderView }> = [ + { + label: t('setting.sidebarPlaylistFolderView_optionSingle', { + postProcess: 'sentenceCase', + }), + value: 'single', + }, + { + label: t('setting.sidebarPlaylistFolderView_optionTree', { + postProcess: 'sentenceCase', + }), + value: 'tree', + }, + { + label: t('setting.sidebarPlaylistFolderView_optionNavigation', { + postProcess: 'sentenceCase', + }), + value: 'navigation', + }, + ]; + const options: SettingOption[] = [ { control: ( @@ -98,6 +150,115 @@ export const SidebarSettings = memo(() => { }), title: t('setting.sidebarPlaylistSorting'), }, + { + control: ( + + ), + description: t('setting.sidebarPlaylistFolders', { + context: 'description', + postProcess: 'sentenceCase', + }), + title: t('setting.sidebarPlaylistFolders', { postProcess: 'sentenceCase' }), + }, + { + control: ( + { + const value = e.currentTarget.value; + setLocalSeparator(value); + debouncedSetSeparator(value); + }} + value={localSeparator} + width={120} + /> + ), + description: t('setting.sidebarPlaylistFolderSeparator', { + context: 'description', + postProcess: 'sentenceCase', + }), + indent: true, + isHidden: !foldersEnabled, + title: t('setting.sidebarPlaylistFolderSeparator', { postProcess: 'sentenceCase' }), + }, + { + control: ( +