mirror of
https://github.com/jeffvli/feishin.git
synced 2024-11-20 06:27:09 +01:00
[enhancement]: support serach on settings page
This commit is contained in:
parent
683bb0222c
commit
645697367d
@ -2,7 +2,7 @@ root = true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
@ -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 />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -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 { ApplicationSettings } from '/@/renderer/features/settings/components/general/application-settings';
|
||||||
import { ControlSettings } from '/@/renderer/features/settings/components/general/control-settings';
|
import { ControlSettings } from '/@/renderer/features/settings/components/general/control-settings';
|
||||||
import { SidebarSettings } from '/@/renderer/features/settings/components/general/sidebar-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 { CacheSettings } from '/@/renderer/features/settings/components/window/cache-settngs';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
|
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
|
||||||
|
import { SidebarReorder } from '/@/renderer/features/settings/components/general/sidebar-reorder';
|
||||||
|
|
||||||
export const GeneralTab = () => {
|
export const GeneralTab = () => {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
<ApplicationSettings />
|
<ApplicationSettings />
|
||||||
<Divider />
|
|
||||||
<ThemeSettings />
|
<ThemeSettings />
|
||||||
<Divider />
|
|
||||||
<ControlSettings />
|
<ControlSettings />
|
||||||
<Divider />
|
|
||||||
<HomeSettings />
|
<HomeSettings />
|
||||||
<Divider />
|
<SidebarReorder />
|
||||||
<SidebarSettings />
|
<SidebarSettings />
|
||||||
{isElectron() && (
|
{isElectron() && <RemoteSettings />}
|
||||||
<>
|
|
||||||
<Divider />
|
|
||||||
<RemoteSettings />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Divider />
|
|
||||||
<CacheSettings />
|
<CacheSettings />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
@ -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 {
|
import {
|
||||||
useSettingsStoreActions,
|
useSettingsStoreActions,
|
||||||
useGeneralSettings,
|
useGeneralSettings,
|
||||||
HomeItem,
|
HomeItem,
|
||||||
} from '../../../../store/settings.store';
|
} from '../../../../store/settings.store';
|
||||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
|
||||||
import { DraggableItem } from '/@/renderer/features/settings/components/general/draggable-item';
|
|
||||||
|
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 = () => {
|
export const HomeSettings = () => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const { homeItems } = useGeneralSettings();
|
const { homeItems } = useGeneralSettings();
|
||||||
const { setHomeItems } = useSettingsStoreActions();
|
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 (
|
return (
|
||||||
<>
|
<DraggableItems
|
||||||
<SettingsOptions
|
description="setting.homeConfiguration"
|
||||||
control={
|
itemLabels={HOME_ITEMS}
|
||||||
<>
|
setItems={setHomeItems}
|
||||||
{open && (
|
settings={homeItems}
|
||||||
<Button
|
title="setting.homeConfiguration"
|
||||||
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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -1,54 +1,16 @@
|
|||||||
import { ChangeEvent, useCallback, useMemo, useState } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { Reorder } from 'framer-motion';
|
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Switch } from '/@/renderer/components';
|
import { Switch } from '/@/renderer/components';
|
||||||
import { useSettingsStoreActions, useGeneralSettings } from '../../../../store/settings.store';
|
import { useSettingsStoreActions, useGeneralSettings } from '../../../../store/settings.store';
|
||||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
import {
|
||||||
import { DraggableItem } from '/@/renderer/features/settings/components/general/draggable-item';
|
SettingOption,
|
||||||
|
SettingsSection,
|
||||||
|
} from '/@/renderer/features/settings/components/settings-section';
|
||||||
|
|
||||||
export const SidebarSettings = () => {
|
export const SidebarSettings = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const settings = useGeneralSettings();
|
const settings = useGeneralSettings();
|
||||||
const { setSidebarItems, setSettings } = useSettingsStoreActions();
|
const { 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 handleSetSidebarPlaylistList = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleSetSidebarPlaylistList = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
setSettings({
|
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 (
|
return <SettingsSection options={options} />;
|
||||||
<>
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ import { Button, TextInput, Checkbox } from '/@/renderer/components';
|
|||||||
import { BindingActions, useHotkeySettings, useSettingsStoreActions } from '/@/renderer/store';
|
import { BindingActions, useHotkeySettings, useSettingsStoreActions } from '/@/renderer/store';
|
||||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
|
import { useSettingSearchContext } from '/@/renderer/features/settings/context/search-context';
|
||||||
|
|
||||||
const ipc = isElectron() ? window.electron.ipc : null;
|
const ipc = isElectron() ? window.electron.ipc : null;
|
||||||
|
|
||||||
@ -107,6 +108,7 @@ export const HotkeyManagerSettings = () => {
|
|||||||
const { bindings, globalMediaHotkeys } = useHotkeySettings();
|
const { bindings, globalMediaHotkeys } = useHotkeySettings();
|
||||||
const { setSettings } = useSettingsStoreActions();
|
const { setSettings } = useSettingsStoreActions();
|
||||||
const [selected, setSelected] = useState<BindingActions | null>(null);
|
const [selected, setSelected] = useState<BindingActions | null>(null);
|
||||||
|
const keyword = useSettingSearchContext();
|
||||||
|
|
||||||
const debouncedSetHotkey = debounce(
|
const debouncedSetHotkey = debounce(
|
||||||
(binding: BindingActions, e: KeyboardEvent<HTMLInputElement>) => {
|
(binding: BindingActions, e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
@ -216,6 +218,21 @@ export const HotkeyManagerSettings = () => {
|
|||||||
return duplicateKeys;
|
return duplicateKeys;
|
||||||
}, [bindings]);
|
}, [bindings]);
|
||||||
|
|
||||||
|
const filteredBindings = useMemo(() => {
|
||||||
|
const base = Object.keys(bindings);
|
||||||
|
|
||||||
|
if (keyword === '') {
|
||||||
|
return base.filter((binding) => BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.filter((binding) => {
|
||||||
|
const item = BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP];
|
||||||
|
if (!item) return false;
|
||||||
|
|
||||||
|
return item.toLocaleLowerCase().includes(keyword);
|
||||||
|
});
|
||||||
|
}, [bindings, keyword]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsOptions
|
<SettingsOptions
|
||||||
@ -227,76 +244,72 @@ export const HotkeyManagerSettings = () => {
|
|||||||
title={t('setting.applicationHotkeys', { postProcess: 'sentenceCase' })}
|
title={t('setting.applicationHotkeys', { postProcess: 'sentenceCase' })}
|
||||||
/>
|
/>
|
||||||
<HotkeysContainer>
|
<HotkeysContainer>
|
||||||
{Object.keys(bindings)
|
{filteredBindings.map((binding) => (
|
||||||
.filter((binding) => BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP])
|
<Group
|
||||||
.map((binding) => (
|
key={`hotkey-${binding}`}
|
||||||
<Group
|
noWrap
|
||||||
key={`hotkey-${binding}`}
|
>
|
||||||
noWrap
|
<TextInput
|
||||||
>
|
readOnly
|
||||||
<TextInput
|
style={{ userSelect: 'none' }}
|
||||||
readOnly
|
value={BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP]}
|
||||||
style={{ userSelect: 'none' }}
|
/>
|
||||||
value={BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP]}
|
<TextInput
|
||||||
/>
|
readOnly
|
||||||
<TextInput
|
icon={<RiKeyboardBoxLine />}
|
||||||
readOnly
|
id={`hotkey-${binding}`}
|
||||||
icon={<RiKeyboardBoxLine />}
|
style={{
|
||||||
id={`hotkey-${binding}`}
|
opacity: selected === (binding as BindingActions) ? 0.8 : 1,
|
||||||
|
outline: duplicateHotkeyMap.includes(
|
||||||
|
bindings[binding as keyof typeof BINDINGS_MAP].hotkey!,
|
||||||
|
)
|
||||||
|
? '1px dashed red'
|
||||||
|
: undefined,
|
||||||
|
}}
|
||||||
|
value={bindings[binding as keyof typeof BINDINGS_MAP].hotkey}
|
||||||
|
onBlur={() => setSelected(null)}
|
||||||
|
onChange={() => {}}
|
||||||
|
onKeyDownCapture={(e) => {
|
||||||
|
if (selected !== (binding as BindingActions)) return;
|
||||||
|
handleSetHotkey(binding as BindingActions, e);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{isElectron() && (
|
||||||
|
<Checkbox
|
||||||
|
checked={bindings[binding as keyof typeof BINDINGS_MAP].isGlobal}
|
||||||
|
disabled={
|
||||||
|
bindings[binding as keyof typeof BINDINGS_MAP].hotkey === ''
|
||||||
|
}
|
||||||
|
size="xl"
|
||||||
style={{
|
style={{
|
||||||
opacity: selected === (binding as BindingActions) ? 0.8 : 1,
|
opacity: bindings[binding as keyof typeof BINDINGS_MAP]
|
||||||
outline: duplicateHotkeyMap.includes(
|
.allowGlobal
|
||||||
bindings[binding as keyof typeof BINDINGS_MAP].hotkey!,
|
? 1
|
||||||
)
|
: 0,
|
||||||
? '1px dashed red'
|
|
||||||
: undefined,
|
|
||||||
}}
|
|
||||||
value={bindings[binding as keyof typeof BINDINGS_MAP].hotkey}
|
|
||||||
onBlur={() => setSelected(null)}
|
|
||||||
onChange={() => {}}
|
|
||||||
onKeyDownCapture={(e) => {
|
|
||||||
if (selected !== (binding as BindingActions)) return;
|
|
||||||
handleSetHotkey(binding as BindingActions, e);
|
|
||||||
}}
|
}}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleSetGlobalHotkey(binding as BindingActions, e)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{isElectron() && (
|
)}
|
||||||
<Checkbox
|
<Button
|
||||||
checked={
|
variant="default"
|
||||||
bindings[binding as keyof typeof BINDINGS_MAP].isGlobal
|
w={100}
|
||||||
}
|
onClick={() => {
|
||||||
disabled={
|
setSelected(binding as BindingActions);
|
||||||
bindings[binding as keyof typeof BINDINGS_MAP].hotkey === ''
|
document.getElementById(`hotkey-${binding}`)?.focus();
|
||||||
}
|
}}
|
||||||
size="xl"
|
>
|
||||||
style={{
|
<RiEditLine />
|
||||||
opacity: bindings[binding as keyof typeof BINDINGS_MAP]
|
</Button>
|
||||||
.allowGlobal
|
<Button
|
||||||
? 1
|
variant="default"
|
||||||
: 0,
|
onClick={() => handleClearHotkey(binding as BindingActions)}
|
||||||
}}
|
>
|
||||||
onChange={(e) =>
|
<RiDeleteBinLine />
|
||||||
handleSetGlobalHotkey(binding as BindingActions, e)
|
</Button>
|
||||||
}
|
</Group>
|
||||||
/>
|
))}
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
w={100}
|
|
||||||
onClick={() => {
|
|
||||||
setSelected(binding as BindingActions);
|
|
||||||
document.getElementById(`hotkey-${binding}`)?.focus();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RiEditLine />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
onClick={() => handleClearHotkey(binding as BindingActions)}
|
|
||||||
>
|
|
||||||
<RiDeleteBinLine />
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
))}
|
|
||||||
</HotkeysContainer>
|
</HotkeysContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Divider, Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { WindowHotkeySettings } from './window-hotkey-settings';
|
import { WindowHotkeySettings } from './window-hotkey-settings';
|
||||||
import { HotkeyManagerSettings } from '/@/renderer/features/settings/components/hotkeys/hotkey-manager-settings';
|
import { HotkeyManagerSettings } from '/@/renderer/features/settings/components/hotkeys/hotkey-manager-settings';
|
||||||
@ -6,12 +6,7 @@ import { HotkeyManagerSettings } from '/@/renderer/features/settings/components/
|
|||||||
export const HotkeysTab = () => {
|
export const HotkeysTab = () => {
|
||||||
return (
|
return (
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
{isElectron() && (
|
{isElectron() && <WindowHotkeySettings />}
|
||||||
<>
|
|
||||||
<WindowHotkeySettings />
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<HotkeyManagerSettings />
|
<HotkeyManagerSettings />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,7 @@ const getAudioDevice = async () => {
|
|||||||
return (devices || []).filter((dev: MediaDeviceInfo) => dev.kind === 'audiooutput');
|
return (devices || []).filter((dev: MediaDeviceInfo) => dev.kind === 'audiooutput');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AudioSettings = () => {
|
export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const settings = usePlaybackSettings();
|
const settings = usePlaybackSettings();
|
||||||
const { setSettings } = useSettingsStoreActions();
|
const { setSettings } = useSettingsStoreActions();
|
||||||
@ -201,5 +201,10 @@ export const AudioSettings = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SettingsSection options={audioOptions} />;
|
return (
|
||||||
|
<SettingsSection
|
||||||
|
divider={!hasFancyAudio}
|
||||||
|
options={audioOptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -118,5 +118,10 @@ export const LyricSettings = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SettingsSection options={lyricOptions} />;
|
return (
|
||||||
|
<SettingsSection
|
||||||
|
divider={false}
|
||||||
|
options={lyricOptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Divider, Group, Stack } from '@mantine/core';
|
import { Group, Stack } from '@mantine/core';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import {
|
import {
|
||||||
FileInput,
|
FileInput,
|
||||||
@ -414,9 +414,7 @@ export const MpvSettings = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsSection options={options} />
|
<SettingsSection options={options} />
|
||||||
<Divider />
|
|
||||||
<SettingsSection options={generalOptions} />
|
<SettingsSection options={generalOptions} />
|
||||||
<Divider />
|
|
||||||
<SettingsSection options={replayGainOptions} />
|
<SettingsSection options={replayGainOptions} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { lazy, Suspense, useMemo } from 'react';
|
import { lazy, Suspense, useMemo } from 'react';
|
||||||
import { Divider, Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { AudioSettings } from '/@/renderer/features/settings/components/playback/audio-settings';
|
import { AudioSettings } from '/@/renderer/features/settings/components/playback/audio-settings';
|
||||||
import { ScrobbleSettings } from '/@/renderer/features/settings/components/playback/scrobble-settings';
|
import { ScrobbleSettings } from '/@/renderer/features/settings/components/playback/scrobble-settings';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
@ -15,13 +15,12 @@ export const PlaybackTab = () => {
|
|||||||
const hasFancyAudio = useMemo(() => {
|
const hasFancyAudio = useMemo(() => {
|
||||||
return isElectron() || 'AudioContext' in window;
|
return isElectron() || 'AudioContext' in window;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
<AudioSettings />
|
<AudioSettings hasFancyAudio={hasFancyAudio} />
|
||||||
<Suspense fallback={<></>}>{hasFancyAudio && <MpvSettings />}</Suspense>
|
<Suspense fallback={<></>}>{hasFancyAudio && <MpvSettings />}</Suspense>
|
||||||
<Divider />
|
|
||||||
<ScrobbleSettings />
|
<ScrobbleSettings />
|
||||||
<Divider />
|
|
||||||
<LyricSettings />
|
<LyricSettings />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
@ -2,13 +2,23 @@ import { Flex, Group } from '@mantine/core';
|
|||||||
import { closeAllModals, openModal } from '@mantine/modals';
|
import { closeAllModals, openModal } from '@mantine/modals';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RiSettings2Fill } from 'react-icons/ri';
|
import { RiSettings2Fill } from 'react-icons/ri';
|
||||||
import { Button, ConfirmModal, PageHeader } from '/@/renderer/components';
|
import { Button, ConfirmModal, PageHeader, SearchInput } from '/@/renderer/components';
|
||||||
import { LibraryHeaderBar } from '/@/renderer/features/shared';
|
import { LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||||
import { useSettingsStoreActions } from '../../../store/settings.store';
|
import { useSettingsStoreActions } from '../../../store/settings.store';
|
||||||
|
import { useSettingSearchContext } from '/@/renderer/features/settings/context/search-context';
|
||||||
|
import { useContainerQuery } from '/@/renderer/hooks';
|
||||||
|
|
||||||
export const SettingsHeader = () => {
|
export type SettingsHeaderProps = {
|
||||||
|
setSearch: (search: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsHeader = ({ setSearch }: SettingsHeaderProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { reset } = useSettingsStoreActions();
|
const { reset } = useSettingsStoreActions();
|
||||||
|
const search = useSettingSearchContext();
|
||||||
|
const cq = useContainerQuery();
|
||||||
|
|
||||||
|
console.log(cq);
|
||||||
|
|
||||||
const handleResetToDefault = () => {
|
const handleResetToDefault = () => {
|
||||||
reset();
|
reset();
|
||||||
@ -27,7 +37,7 @@ export const SettingsHeader = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex>
|
<Flex ref={cq.ref}>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<LibraryHeaderBar>
|
<LibraryHeaderBar>
|
||||||
<Flex
|
<Flex
|
||||||
@ -41,13 +51,22 @@ export const SettingsHeader = () => {
|
|||||||
{t('common.setting', { count: 2, postProcess: 'titleCase' })}
|
{t('common.setting', { count: 2, postProcess: 'titleCase' })}
|
||||||
</LibraryHeaderBar.Title>
|
</LibraryHeaderBar.Title>
|
||||||
</Group>
|
</Group>
|
||||||
<Button
|
<Group>
|
||||||
compact
|
<SearchInput
|
||||||
variant="default"
|
defaultValue={search}
|
||||||
onClick={openResetConfirmModal}
|
openedWidth={cq.isMd ? 250 : cq.isSm ? 200 : 150}
|
||||||
>
|
onChange={(event) =>
|
||||||
{t('common.resetToDefault', { postProcess: 'sentenceCase' })}
|
setSearch(event.target.value.toLocaleLowerCase())
|
||||||
</Button>
|
}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
variant="default"
|
||||||
|
onClick={openResetConfirmModal}
|
||||||
|
>
|
||||||
|
{t('common.resetToDefault', { postProcess: 'sentenceCase' })}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Flex>
|
</Flex>
|
||||||
</LibraryHeaderBar>
|
</LibraryHeaderBar>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
||||||
|
import { useSettingSearchContext } from '/@/renderer/features/settings/context/search-context';
|
||||||
|
import { Divider } from '@mantine/core';
|
||||||
|
|
||||||
export type SettingOption = {
|
export type SettingOption = {
|
||||||
control: ReactNode;
|
control: ReactNode;
|
||||||
@ -10,20 +12,27 @@ export type SettingOption = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface SettingsSectionProps {
|
interface SettingsSectionProps {
|
||||||
|
divider?: boolean;
|
||||||
options: SettingOption[];
|
options: SettingOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsSection = ({ options }: SettingsSectionProps) => {
|
export const SettingsSection = ({ divider, options }: SettingsSectionProps) => {
|
||||||
|
const keyword = useSettingSearchContext();
|
||||||
|
const hasKeyword = keyword !== '';
|
||||||
|
|
||||||
|
const values = options.filter(
|
||||||
|
(o) => !o.isHidden && (!hasKeyword || o.title.toLocaleLowerCase().includes(keyword)),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{options
|
{values.map((option) => (
|
||||||
.filter((o) => !o.isHidden)
|
<SettingsOptions
|
||||||
.map((option) => (
|
key={`option-${option.title}`}
|
||||||
<SettingsOptions
|
{...option}
|
||||||
key={`option-${option.title}`}
|
/>
|
||||||
{...option}
|
))}
|
||||||
/>
|
{divider !== false && values.length > 0 && <Divider />}
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,8 +3,11 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, ConfirmModal, toast } from '/@/renderer/components';
|
import { Button, ConfirmModal, toast } from '/@/renderer/components';
|
||||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
SettingOption,
|
||||||
|
SettingsSection,
|
||||||
|
} from '/@/renderer/features/settings/components/settings-section';
|
||||||
|
|
||||||
const browser = isElectron() ? window.electron.browser : null;
|
const browser = isElectron() ? window.electron.browser : null;
|
||||||
|
|
||||||
@ -50,42 +53,46 @@ export const CacheSettings = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const options: SettingOption[] = [
|
||||||
|
{
|
||||||
|
control: (
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
disabled={isClearing}
|
||||||
|
variant="filled"
|
||||||
|
onClick={() => openResetConfirmModal(false)}
|
||||||
|
>
|
||||||
|
{t('common.clear', { postProcess: 'sentenceCase' })}
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
description: t('setting.clearQueryCache', {
|
||||||
|
context: 'description',
|
||||||
|
}),
|
||||||
|
title: t('setting.clearQueryCache'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
control: (
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
disabled={isClearing}
|
||||||
|
variant="filled"
|
||||||
|
onClick={() => openResetConfirmModal(true)}
|
||||||
|
>
|
||||||
|
{t('common.clear', { postProcess: 'sentenceCase' })}
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
description: t('setting.clearCache', {
|
||||||
|
context: 'description',
|
||||||
|
}),
|
||||||
|
isHidden: !browser,
|
||||||
|
title: t('setting.clearCache'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<SettingsSection
|
||||||
<SettingsOptions
|
divider={false}
|
||||||
control={
|
options={options}
|
||||||
<Button
|
/>
|
||||||
compact
|
|
||||||
disabled={isClearing}
|
|
||||||
variant="filled"
|
|
||||||
onClick={() => openResetConfirmModal(false)}
|
|
||||||
>
|
|
||||||
{t('common.clear', { postProcess: 'sentenceCase' })}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
description={t('setting.clearQueryCache', {
|
|
||||||
context: 'description',
|
|
||||||
})}
|
|
||||||
title={t('setting.clearQueryCache')}
|
|
||||||
/>
|
|
||||||
{browser && (
|
|
||||||
<SettingsOptions
|
|
||||||
control={
|
|
||||||
<Button
|
|
||||||
compact
|
|
||||||
disabled={isClearing}
|
|
||||||
variant="filled"
|
|
||||||
onClick={() => openResetConfirmModal(true)}
|
|
||||||
>
|
|
||||||
{t('common.clear', { postProcess: 'sentenceCase' })}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
description={t('setting.clearCache', {
|
|
||||||
context: 'description',
|
|
||||||
})}
|
|
||||||
title={t('setting.clearCache')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -52,5 +52,10 @@ export const PasswordSettings = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SettingsSection options={updateOptions} />;
|
return (
|
||||||
|
<SettingsSection
|
||||||
|
divider={false}
|
||||||
|
options={updateOptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
import { Switch } from '/@/renderer/components';
|
import { Switch } from '/@/renderer/components';
|
||||||
|
|
||||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||||
|
const utils = isElectron() ? window.electron.utils : null;
|
||||||
|
|
||||||
export const UpdateSettings = () => {
|
export const UpdateSettings = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -42,5 +43,10 @@ export const UpdateSettings = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SettingsSection options={updateOptions} />;
|
return (
|
||||||
|
<SettingsSection
|
||||||
|
divider={utils?.isLinux()}
|
||||||
|
options={updateOptions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Divider, Stack } from '@mantine/core';
|
import { Stack } from '@mantine/core';
|
||||||
import { UpdateSettings } from '/@/renderer/features/settings/components/window/update-settings';
|
import { UpdateSettings } from '/@/renderer/features/settings/components/window/update-settings';
|
||||||
import { WindowSettings } from '/@/renderer/features/settings/components/window/window-settings';
|
import { WindowSettings } from '/@/renderer/features/settings/components/window/window-settings';
|
||||||
import { DiscordSettings } from '/@/renderer/features/settings/components/window/discord-settings';
|
import { DiscordSettings } from '/@/renderer/features/settings/components/window/discord-settings';
|
||||||
@ -11,13 +11,10 @@ export const WindowTab = () => {
|
|||||||
return (
|
return (
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
<WindowSettings />
|
<WindowSettings />
|
||||||
<Divider />
|
|
||||||
<DiscordSettings />
|
<DiscordSettings />
|
||||||
<Divider />
|
|
||||||
<UpdateSettings />
|
<UpdateSettings />
|
||||||
{utils?.isLinux() && (
|
{utils?.isLinux() && (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
|
||||||
<PasswordSettings />
|
<PasswordSettings />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
export const SettingSearchContext = createContext<string>('');
|
||||||
|
|
||||||
|
export const useSettingSearchContext = () => {
|
||||||
|
const ctxValue = useContext(SettingSearchContext);
|
||||||
|
return ctxValue;
|
||||||
|
};
|
@ -2,18 +2,24 @@ import { Flex } from '@mantine/core';
|
|||||||
import { SettingsContent } from '/@/renderer/features/settings/components/settings-content';
|
import { SettingsContent } from '/@/renderer/features/settings/components/settings-content';
|
||||||
import { SettingsHeader } from '/@/renderer/features/settings/components/settings-header';
|
import { SettingsHeader } from '/@/renderer/features/settings/components/settings-header';
|
||||||
import { AnimatedPage } from '/@/renderer/features/shared';
|
import { AnimatedPage } from '/@/renderer/features/shared';
|
||||||
|
import { SettingSearchContext } from '/@/renderer/features/settings/context/search-context';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
const SettingsRoute = () => {
|
const SettingsRoute = () => {
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedPage>
|
<AnimatedPage>
|
||||||
<Flex
|
<SettingSearchContext.Provider value={search}>
|
||||||
direction="column"
|
<Flex
|
||||||
h="100%"
|
direction="column"
|
||||||
w="100%"
|
h="100%"
|
||||||
>
|
w="100%"
|
||||||
<SettingsHeader />
|
>
|
||||||
<SettingsContent />
|
<SettingsHeader setSearch={setSearch} />
|
||||||
</Flex>
|
<SettingsContent />
|
||||||
|
</Flex>
|
||||||
|
</SettingSearchContext.Provider>
|
||||||
</AnimatedPage>
|
</AnimatedPage>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -282,6 +282,7 @@ export interface SettingsSlice extends SettingsState {
|
|||||||
actions: {
|
actions: {
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
resetSampleRate: () => void;
|
resetSampleRate: () => void;
|
||||||
|
setGenreBehavior: (target: GenreTarget) => void;
|
||||||
setHomeItems: (item: SortableItem<HomeItem>[]) => void;
|
setHomeItems: (item: SortableItem<HomeItem>[]) => void;
|
||||||
setSettings: (data: Partial<SettingsState>) => void;
|
setSettings: (data: Partial<SettingsState>) => void;
|
||||||
setSidebarItems: (items: SidebarItemType[]) => void;
|
setSidebarItems: (items: SidebarItemType[]) => void;
|
||||||
|
Loading…
Reference in New Issue
Block a user