[enhancement]: support serach on settings page

This commit is contained in:
Kendall Garner 2024-05-05 13:25:05 -07:00
parent 683bb0222c
commit 645697367d
No known key found for this signature in database
GPG key ID: 18D2767419676C87
21 changed files with 439 additions and 385 deletions

View file

@ -0,0 +1,126 @@
import isEqual from 'lodash/isEqual';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SortableItem } from '/@/renderer/store';
import { useSettingSearchContext } from '/@/renderer/features/settings/context/search-context';
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
import { Button } from '/@/renderer/components';
import { Reorder } from 'framer-motion';
import { Divider } from '@mantine/core';
import { DraggableItem } from '/@/renderer/features/settings/components/general/draggable-item';
export type DraggableItemsProps<K, T> = {
description: string;
itemLabels: Array<[K, string]>;
setItems: (items: T[]) => void;
settings: T[];
title: string;
};
export const DraggableItems = <K extends string, T extends SortableItem<K>>({
description,
itemLabels,
settings,
setItems,
title,
}: DraggableItemsProps<K, T>) => {
const { t } = useTranslation();
const keyword = useSettingSearchContext();
const [open, setOpen] = useState(false);
const translatedItemMap = useMemo(
() =>
Object.fromEntries(
itemLabels.map((label) => [label[0], t(label[1], { postProcess: 'sentenceCase' })]),
) as Record<K, string>,
[itemLabels, t],
);
const [localItems, setLocalItems] = useState(settings);
const handleChangeDisabled = useCallback((id: string, e: boolean) => {
setLocalItems((items) =>
items.map((item) => {
if (item.id === id) {
return {
...item,
disabled: !e,
};
}
return item;
}),
);
}, []);
const titleText = t(title, { postProcess: 'sentenceCase' });
const descriptionText = t(description, {
context: 'description',
postProcess: 'sentenceCase',
});
const shouldShow = useMemo(() => {
return (
keyword === '' ||
title.toLocaleLowerCase().includes(keyword) ||
description.toLocaleLowerCase().includes(keyword)
);
}, [description, keyword, title]);
if (!shouldShow) {
return <></>;
}
const isSaveButtonDisabled = isEqual(settings, localItems);
const handleSave = () => {
setItems(localItems);
};
return (
<>
<SettingsOptions
control={
<>
{open && (
<Button
compact
disabled={isSaveButtonDisabled}
variant="filled"
onClick={handleSave}
>
{t('common.save', { postProcess: 'titleCase' })}
</Button>
)}
<Button
compact
variant="filled"
onClick={() => setOpen(!open)}
>
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
</Button>
</>
}
description={descriptionText}
title={titleText}
/>
{open && (
<Reorder.Group
axis="y"
values={localItems}
onReorder={setLocalItems}
>
{localItems.map((item) => (
<DraggableItem
key={item.id}
handleChangeDisabled={handleChangeDisabled}
item={item}
value={translatedItemMap[item.id]}
/>
))}
</Reorder.Group>
)}
<Divider />
</>
);
};

View file

@ -1,4 +1,4 @@
import { Divider, Stack } from '@mantine/core';
import { Stack } from '@mantine/core';
import { ApplicationSettings } from '/@/renderer/features/settings/components/general/application-settings';
import { ControlSettings } from '/@/renderer/features/settings/components/general/control-settings';
import { SidebarSettings } from '/@/renderer/features/settings/components/general/sidebar-settings';
@ -7,26 +7,18 @@ import { RemoteSettings } from '/@/renderer/features/settings/components/general
import { CacheSettings } from '/@/renderer/features/settings/components/window/cache-settngs';
import isElectron from 'is-electron';
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
import { SidebarReorder } from '/@/renderer/features/settings/components/general/sidebar-reorder';
export const GeneralTab = () => {
return (
<Stack spacing="md">
<ApplicationSettings />
<Divider />
<ThemeSettings />
<Divider />
<ControlSettings />
<Divider />
<HomeSettings />
<Divider />
<SidebarReorder />
<SidebarSettings />
{isElectron() && (
<>
<Divider />
<RemoteSettings />
</>
)}
<Divider />
{isElectron() && <RemoteSettings />}
<CacheSettings />
</Stack>
);

View file

@ -1,107 +1,28 @@
import { useCallback, useMemo, useState } from 'react';
import { Reorder } from 'framer-motion';
import isEqual from 'lodash/isEqual';
import { useTranslation } from 'react-i18next';
import { Button } from '/@/renderer/components';
import {
useSettingsStoreActions,
useGeneralSettings,
HomeItem,
} from '../../../../store/settings.store';
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
import { DraggableItem } from '/@/renderer/features/settings/components/general/draggable-item';
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
const HOME_ITEMS: Array<[string, string]> = [
[HomeItem.RANDOM, 'page.home.explore'],
[HomeItem.RECENTLY_PLAYED, 'page.home.recentlyPlayed'],
[HomeItem.RECENTLY_ADDED, 'page.home.newlyAdded'],
[HomeItem.MOST_PLAYED, 'page.home.mostPlayed'],
];
export const HomeSettings = () => {
const { t } = useTranslation();
const { homeItems } = useGeneralSettings();
const { setHomeItems } = useSettingsStoreActions();
const [open, setOpen] = useState(false);
const translatedSidebarItemMap = useMemo(
() => ({
[HomeItem.RANDOM]: t('page.home.explore', { postProcess: 'sentenceCase' }),
[HomeItem.RECENTLY_PLAYED]: t('page.home.recentlyPlayed', {
postProcess: 'sentenceCase',
}),
[HomeItem.RECENTLY_ADDED]: t('page.home.newlyAdded', { postProcess: 'sentenceCase' }),
[HomeItem.MOST_PLAYED]: t('page.home.mostPlayed', { postProcess: 'sentenceCase' }),
}),
[t],
);
const [localHomeItems, setLocalHomeItems] = useState(homeItems);
const handleSave = () => {
setHomeItems(localHomeItems);
};
const handleChangeDisabled = useCallback((id: string, e: boolean) => {
setLocalHomeItems((items) =>
items.map((item) => {
if (item.id === id) {
return {
...item,
disabled: !e,
};
}
return item;
}),
);
}, []);
const isSaveButtonDisabled = isEqual(homeItems, localHomeItems);
return (
<>
<SettingsOptions
control={
<>
{open && (
<Button
compact
disabled={isSaveButtonDisabled}
variant="filled"
onClick={handleSave}
>
{t('common.save', { postProcess: 'titleCase' })}
</Button>
)}
<Button
compact
variant="filled"
onClick={() => setOpen(!open)}
>
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
</Button>
</>
}
description={t('setting.homeConfiguration', {
context: 'description',
postProcess: 'sentenceCase',
})}
title={t('setting.homeConfiguration', { postProcess: 'sentenceCase' })}
/>
{open && (
<Reorder.Group
axis="y"
values={localHomeItems}
onReorder={setLocalHomeItems}
>
{localHomeItems.map((item) => (
<DraggableItem
key={item.id}
handleChangeDisabled={handleChangeDisabled}
item={item}
value={
translatedSidebarItemMap[
item.id as keyof typeof translatedSidebarItemMap
]
}
/>
))}
</Reorder.Group>
)}
</>
<DraggableItems
description="setting.homeConfiguration"
itemLabels={HOME_ITEMS}
setItems={setHomeItems}
settings={homeItems}
title="setting.homeConfiguration"
/>
);
};

View file

@ -0,0 +1,30 @@
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
const SIDEBAR_ITEMS: Array<[string, string]> = [
['Albums', 'page.sidebar.albums'],
['Artists', 'page.sidebar.artists'],
['Folders', 'page.sidebar.folders'],
['Genres', 'page.sidebar.genres'],
['Home', 'page.sidebar.home'],
['Now Playing', 'page.sidebar.nowPlaying'],
['Playlists', 'page.sidebar.playlists'],
['Search', 'page.sidebar.search'],
['Settings', 'page.sidebar.settings'],
['Tracks', 'page.sidebar.tracks'],
];
export const SidebarReorder = () => {
const { sidebarItems } = useGeneralSettings();
const { setSidebarItems } = useSettingsStoreActions();
return (
<DraggableItems
description="setting.sidebarCollapsedNavigation"
itemLabels={SIDEBAR_ITEMS}
setItems={setSidebarItems}
settings={sidebarItems}
title="setting.sidebarConfiguration"
/>
);
};

View file

@ -1,54 +1,16 @@
import { ChangeEvent, useCallback, useMemo, useState } from 'react';
import { Reorder } from 'framer-motion';
import isEqual from 'lodash/isEqual';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Switch } from '/@/renderer/components';
import { Switch } from '/@/renderer/components';
import { useSettingsStoreActions, useGeneralSettings } from '../../../../store/settings.store';
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
import { DraggableItem } from '/@/renderer/features/settings/components/general/draggable-item';
import {
SettingOption,
SettingsSection,
} from '/@/renderer/features/settings/components/settings-section';
export const SidebarSettings = () => {
const { t } = useTranslation();
const settings = useGeneralSettings();
const { setSidebarItems, setSettings } = useSettingsStoreActions();
const [open, setOpen] = useState(false);
const translatedSidebarItemMap = useMemo(
() => ({
Albums: t('page.sidebar.albums', { postProcess: 'titleCase' }),
Artists: t('page.sidebar.artists', { postProcess: 'titleCase' }),
Folders: t('page.sidebar.folders', { postProcess: 'titleCase' }),
Genres: t('page.sidebar.genres', { postProcess: 'titleCase' }),
Home: t('page.sidebar.home', { postProcess: 'titleCase' }),
'Now Playing': t('page.sidebar.nowPlaying', { postProcess: 'titleCase' }),
Playlists: t('page.sidebar.playlists', { postProcess: 'titleCase' }),
Search: t('page.sidebar.search', { postProcess: 'titleCase' }),
Settings: t('page.sidebar.settings', { postProcess: 'titleCase' }),
Tracks: t('page.sidebar.tracks', { postProcess: 'titleCase' }),
}),
[t],
);
const [localSidebarItems, setLocalSidebarItems] = useState(settings.sidebarItems);
const handleSave = () => {
setSidebarItems(localSidebarItems);
};
const handleChangeDisabled = useCallback((id: string, e: boolean) => {
setLocalSidebarItems((items) =>
items.map((item) => {
if (item.id === id) {
return {
...item,
disabled: !e,
};
}
return item;
}),
);
}, []);
const { setSettings } = useSettingsStoreActions();
const handleSetSidebarPlaylistList = (e: ChangeEvent<HTMLInputElement>) => {
setSettings({
@ -68,84 +30,34 @@ export const SidebarSettings = () => {
});
};
const isSaveButtonDisabled = isEqual(settings.sidebarItems, localSidebarItems);
const options: SettingOption[] = [
{
control: (
<Switch
checked={settings.sidebarPlaylistList}
onChange={handleSetSidebarPlaylistList}
/>
),
description: t('setting.sidebarPlaylistList', {
context: 'description',
postProcess: 'sentenceCase',
}),
title: t('setting.sidebarPlaylistList', { postProcess: 'sentenceCase' }),
},
{
control: (
<Switch
checked={settings.sidebarCollapsedNavigation}
onChange={handleSetSidebarCollapsedNavigation}
/>
),
description: t('setting.sidebarPlaylistList', {
context: 'description',
postProcess: 'sentenceCase',
}),
title: t('setting.sidebarCollapsedNavigation', { postProcess: 'sentenceCase' }),
},
];
return (
<>
<SettingsOptions
control={
<Switch
checked={settings.sidebarPlaylistList}
onChange={handleSetSidebarPlaylistList}
/>
}
description={t('setting.sidebarPlaylistList', {
context: 'description',
postProcess: 'sentenceCase',
})}
title={t('setting.sidebarPlaylistList', { postProcess: 'sentenceCase' })}
/>
<SettingsOptions
control={
<Switch
checked={settings.sidebarCollapsedNavigation}
onChange={handleSetSidebarCollapsedNavigation}
/>
}
description={t('setting.sidebarPlaylistList', {
context: 'description',
postProcess: 'sentenceCase',
})}
title={t('setting.sidebarCollapsedNavigation', { postProcess: 'sentenceCase' })}
/>
<SettingsOptions
control={
<>
{open && (
<Button
compact
disabled={isSaveButtonDisabled}
variant="filled"
onClick={handleSave}
>
{t('common.save', { postProcess: 'titleCase' })}
</Button>
)}
<Button
compact
variant="filled"
onClick={() => setOpen(!open)}
>
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
</Button>
</>
}
description={t('setting.sidebarCollapsedNavigation', {
context: 'description',
postProcess: 'sentenceCase',
})}
title={t('setting.sidebarConfiguration', { postProcess: 'sentenceCase' })}
/>
{open && (
<Reorder.Group
axis="y"
values={localSidebarItems}
onReorder={setLocalSidebarItems}
>
{localSidebarItems.map((item) => (
<DraggableItem
key={item.id}
handleChangeDisabled={handleChangeDisabled}
item={item}
value={
translatedSidebarItemMap[
item.id as keyof typeof translatedSidebarItemMap
]
}
/>
))}
</Reorder.Group>
)}
</>
);
return <SettingsSection options={options} />;
};