feat: add regex filter setting for sidebar playlists (#1589)

* added regex filter for sidebar playlists

---------

Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
Exceen
2026-02-02 03:25:37 +01:00
committed by GitHub
parent 0999b93b47
commit ce46e0f351
4 changed files with 83 additions and 1 deletions
+3
View File
@@ -979,6 +979,9 @@
"sidebarPlaylistList": "sidebar playlist list",
"sidebarPlaylistSorting_description": "allows manual playlist sorting in the sidebar using drag and drop instead of the default server order",
"sidebarPlaylistSorting": "sidebar playlist sorting",
"sidebarPlaylistListFilterRegex_description": "hide playlists in the sidebar that match this regular expression",
"sidebarPlaylistListFilterRegex_placeholder": "e.g. ^Daily Mix.*",
"sidebarPlaylistListFilterRegex": "playlist filter regex",
"sidePlayQueueStyle_description": "sets the style of the side play queue",
"sidePlayQueueStyle_optionAttached": "attached",
"sidePlayQueueStyle_optionDetached": "detached",
@@ -1,4 +1,4 @@
import { ChangeEvent, memo } from 'react';
import { ChangeEvent, memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SidebarReorder } from '/@/renderer/features/settings/components/general/sidebar-reorder';
@@ -8,6 +8,8 @@ import {
} from '/@/renderer/features/settings/components/settings-section';
import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
import { Switch } from '/@/shared/components/switch/switch';
import { TextInput } from '/@/shared/components/text-input/text-input';
import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback';
export const SidebarSettings = memo(() => {
const { t } = useTranslation();
@@ -38,6 +40,22 @@ export const SidebarSettings = memo(() => {
});
};
const [localFilterRegex, setLocalFilterRegex] = useState(
settings.sidebarPlaylistListFilterRegex,
);
useEffect(() => {
setLocalFilterRegex(settings.sidebarPlaylistListFilterRegex);
}, [settings.sidebarPlaylistListFilterRegex]);
const debouncedSetFilterRegex = useDebouncedCallback((value: string) => {
setSettings({
general: {
sidebarPlaylistListFilterRegex: value,
},
});
}, 500);
const options: SettingOption[] = [
{
control: (
@@ -52,6 +70,26 @@ export const SidebarSettings = memo(() => {
}),
title: t('setting.sidebarPlaylistList', { postProcess: 'sentenceCase' }),
},
{
control: (
<TextInput
onChange={(e) => {
const value = e.currentTarget.value;
setLocalFilterRegex(value);
debouncedSetFilterRegex(value);
}}
placeholder={t('setting.sidebarPlaylistListFilterRegex_placeholder', {
postProcess: 'sentenceCase',
})}
value={localFilterRegex}
/>
),
description: t('setting.sidebarPlaylistListFilterRegex', {
context: 'description',
postProcess: 'sentenceCase',
}),
title: t('setting.sidebarPlaylistListFilterRegex', { postProcess: 'sentenceCase' }),
},
{
control: (
<Switch
@@ -25,6 +25,12 @@ import {
usePermissions,
useSidebarPlaylistSorting,
} from '/@/renderer/store';
import {
useCurrentServer,
useCurrentServerId,
usePermissions,
useSidebarPlaylistListFilterRegex,
} from '/@/renderer/store';
import { formatDurationString } from '/@/renderer/utils';
import { Accordion } from '/@/shared/components/accordion/accordion';
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
@@ -357,6 +363,7 @@ export const SidebarPlaylistList = () => {
const { t } = useTranslation();
const server = useCurrentServer();
const sidebarPlaylistSorting = useSidebarPlaylistSorting();
const filterRegex = useSidebarPlaylistListFilterRegex();
const playlistsQuery = useQuery(
playlistsQueries.list({
@@ -400,10 +407,23 @@ export const SidebarPlaylistList = () => {
return { ...base, items: playlistsQuery.data?.items };
}
let regex: null | RegExp = null;
if (filterRegex) {
try {
regex = new RegExp(filterRegex, 'i');
} catch {
// Invalid regex, ignore filtering
}
}
const ownedPlaylistItems: Array<Playlist> = [];
for (const playlist of playlistsQuery.data?.items ?? []) {
if (!playlist.owner || playlist.owner === server.username) {
// Filter out playlists that match the regex
if (regex && regex.test(playlist.name)) {
continue;
}
ownedPlaylistItems.push(playlist);
}
}
@@ -429,6 +449,7 @@ export const SidebarPlaylistList = () => {
server.username,
sidebarPlaylistSorting,
playlistOrder,
filterRegex,
]);
const handleReorder = (
@@ -533,6 +554,7 @@ export const SidebarSharedPlaylistList = () => {
const { t } = useTranslation();
const server = useCurrentServer();
const sidebarPlaylistSorting = useSidebarPlaylistSorting();
const filterRegex = useSidebarPlaylistListFilterRegex();
const playlistsQuery = useQuery(
playlistsQueries.list({
@@ -580,10 +602,23 @@ export const SidebarSharedPlaylistList = () => {
return { ...base, items: playlistsQuery.data?.items };
}
let regex: null | RegExp = null;
if (filterRegex) {
try {
regex = new RegExp(filterRegex, 'i');
} catch {
// Invalid regex, ignore filtering
}
}
const sharedPlaylistItems: Array<Playlist> = [];
for (const playlist of playlistsQuery.data?.items ?? []) {
if (playlist.owner && playlist.owner !== server.username) {
// Filter out playlists that match the regex
if (regex && regex.test(playlist.name)) {
continue;
}
sharedPlaylistItems.push(playlist);
}
}
@@ -609,6 +644,7 @@ export const SidebarSharedPlaylistList = () => {
server.username,
sidebarPlaylistSorting,
playlistOrder,
filterRegex,
]);
const handleReorder = (
+5
View File
@@ -454,6 +454,7 @@ export const GeneralSettingsSchema = z.object({
sidebarItems: z.array(SidebarItemTypeSchema),
sidebarPanelOrder: z.array(SidebarPanelTypeSchema),
sidebarPlaylistList: z.boolean(),
sidebarPlaylistListFilterRegex: z.string(),
sidebarPlaylistSorting: z.boolean(),
sideQueueType: SideQueueTypeSchema,
skipButtons: SkipButtonsSchema,
@@ -1030,6 +1031,7 @@ const initialState: SettingsState = {
sidebarItems,
sidebarPanelOrder: ['queue', 'lyrics', 'visualizer'],
sidebarPlaylistList: true,
sidebarPlaylistListFilterRegex: '',
sidebarPlaylistSorting: false,
sideQueueType: 'sideQueue',
skipButtons: {
@@ -2193,6 +2195,9 @@ export const useSidebarPlaylistList = () =>
export const useSidebarPlaylistSorting = () =>
useSettingsStore((state) => state.general.sidebarPlaylistSorting, shallow);
export const useSidebarPlaylistListFilterRegex = () =>
useSettingsStore((state) => state.general.sidebarPlaylistListFilterRegex, shallow);
export const useSidebarItems = () =>
useSettingsStore((state) => state.general.sidebarItems, shallow);