mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 04:20:07 +02:00
support custom smart playlist tags
This commit is contained in:
@@ -1,24 +1,40 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Fragment } from 'react/jsx-runtime';
|
||||
|
||||
import { ApplicationSettings } from '/@/renderer/features/settings/components/general/application-settings';
|
||||
import { ControlSettings } from '/@/renderer/features/settings/components/general/control-settings';
|
||||
import { LyricSettings } from '/@/renderer/features/settings/components/general/lyric-settings';
|
||||
import { QueryBuilderSettings } from '/@/renderer/features/settings/components/general/query-builder-settings';
|
||||
import { ScrobbleSettings } from '/@/renderer/features/settings/components/general/scrobble-settings';
|
||||
import { SidebarSettings } from '/@/renderer/features/settings/components/general/sidebar-settings';
|
||||
import { ThemeSettings } from '/@/renderer/features/settings/components/general/theme-settings';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { hasFeature } from '/@/shared/api/utils';
|
||||
import { Divider } from '/@/shared/components/divider/divider';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
|
||||
const sections = [
|
||||
{ component: ApplicationSettings, key: 'application' },
|
||||
{ component: ThemeSettings, key: 'theme' },
|
||||
{ component: ControlSettings, key: 'control' },
|
||||
{ component: SidebarSettings, key: 'sidebar' },
|
||||
{ component: ScrobbleSettings, key: 'scrobble' },
|
||||
{ component: LyricSettings, key: 'lyrics' },
|
||||
];
|
||||
import { ServerFeature } from '/@/shared/types/features-types';
|
||||
|
||||
export const GeneralTab = () => {
|
||||
const server = useCurrentServer();
|
||||
const supportsSmartPlaylists = hasFeature(server, ServerFeature.PLAYLISTS_SMART);
|
||||
|
||||
const sections = useMemo(() => {
|
||||
const baseSections = [
|
||||
{ component: ApplicationSettings, key: 'application' },
|
||||
{ component: ThemeSettings, key: 'theme' },
|
||||
{ component: ControlSettings, key: 'control' },
|
||||
{ component: SidebarSettings, key: 'sidebar' },
|
||||
{ component: ScrobbleSettings, key: 'scrobble' },
|
||||
{ component: LyricSettings, key: 'lyrics' },
|
||||
];
|
||||
|
||||
if (supportsSmartPlaylists) {
|
||||
baseSections.push({ component: QueryBuilderSettings, key: 'queryBuilder' });
|
||||
}
|
||||
|
||||
return baseSections;
|
||||
}, [supportsSmartPlaylists]);
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
{sections.map(({ component: Section, key }, index) => (
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
SettingOption,
|
||||
SettingsSection,
|
||||
} from '/@/renderer/features/settings/components/settings-section';
|
||||
import { useQueryBuilderSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextInput } from '/@/shared/components/text-input/text-input';
|
||||
|
||||
const QUERY_VALUE_INPUT_TYPES = [
|
||||
{ label: 'Boolean', value: 'boolean' },
|
||||
{ label: 'Date', value: 'date' },
|
||||
{ label: 'Date Range', value: 'dateRange' },
|
||||
{ label: 'Number', value: 'number' },
|
||||
{ label: 'Playlist', value: 'playlist' },
|
||||
{ label: 'String', value: 'string' },
|
||||
] as const;
|
||||
|
||||
export const QueryBuilderSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const queryBuilder = useQueryBuilderSettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
|
||||
const handleAddCustomField = () => {
|
||||
setSettings({
|
||||
queryBuilder: {
|
||||
tag: [
|
||||
...queryBuilder.tag,
|
||||
{
|
||||
label: '',
|
||||
type: 'string',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveCustomField = (index: number) => {
|
||||
setSettings({
|
||||
queryBuilder: {
|
||||
tag: queryBuilder.tag.filter((_, i) => i !== index),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateCustomField = (
|
||||
index: number,
|
||||
field: 'label' | 'type' | 'value',
|
||||
newValue: string,
|
||||
) => {
|
||||
setSettings({
|
||||
queryBuilder: {
|
||||
tag: queryBuilder.tag.map((item, i) =>
|
||||
i === index ? { ...item, [field]: newValue } : item,
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const customFieldsOptions: SettingOption[] = [
|
||||
{
|
||||
control: (
|
||||
<Stack gap="md">
|
||||
{queryBuilder.tag.length > 0 && (
|
||||
<Stack gap="sm">
|
||||
{queryBuilder.tag.map((field, index) => (
|
||||
<Group gap="sm" key={index}>
|
||||
<TextInput
|
||||
onChange={(e) =>
|
||||
handleUpdateCustomField(
|
||||
index,
|
||||
'label',
|
||||
e.currentTarget.value,
|
||||
)
|
||||
}
|
||||
placeholder={t(
|
||||
'setting.queryBuilderCustomFields_inputLabel',
|
||||
{
|
||||
postProcess: 'sentenceCase',
|
||||
},
|
||||
)}
|
||||
value={field.label}
|
||||
width="30%"
|
||||
/>
|
||||
<Select
|
||||
data={QUERY_VALUE_INPUT_TYPES}
|
||||
onChange={(e) =>
|
||||
handleUpdateCustomField(index, 'type', e || 'string')
|
||||
}
|
||||
value={field.type}
|
||||
width="25%"
|
||||
/>
|
||||
<TextInput
|
||||
onChange={(e) =>
|
||||
handleUpdateCustomField(
|
||||
index,
|
||||
'value',
|
||||
e.currentTarget.value,
|
||||
)
|
||||
}
|
||||
placeholder={t(
|
||||
'setting.queryBuilderCustomFields_inputTag',
|
||||
{
|
||||
postProcess: 'sentenceCase',
|
||||
},
|
||||
)}
|
||||
value={field.value}
|
||||
width="30%"
|
||||
/>
|
||||
<ActionIcon
|
||||
icon="remove"
|
||||
onClick={() => handleRemoveCustomField(index)}
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
/>
|
||||
</Group>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
<Group grow>
|
||||
<Button onClick={handleAddCustomField} variant="filled">
|
||||
{t('common.add', { postProcess: 'titleCase' })}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
),
|
||||
description: t('setting.queryBuilderCustomFields', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
title: t('setting.queryBuilderCustomFields', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
options={customFieldsOptions}
|
||||
title={t('page.setting.queryBuilder', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user