Move settings to route instead of modal

This commit is contained in:
jeffvli 2023-03-30 03:01:31 -07:00
parent 0c13b09029
commit bc5f1f13f0
11 changed files with 159 additions and 151 deletions

View File

@ -1,5 +1,5 @@
import type { TabsProps as MantineTabsProps } from '@mantine/core';
import { Tabs as MantineTabs } from '@mantine/core';
import { Suspense } from 'react';
import { TabsPanelProps, TabsProps as MantineTabsProps, Tabs as MantineTabs } from '@mantine/core';
import styled from 'styled-components';
type TabsProps = MantineTabsProps;
@ -12,11 +12,14 @@ const StyledTabs = styled(MantineTabs)`
}
&.mantine-Tabs-tab {
padding: 0.5rem 1rem;
font-weight: 600;
font-size: 1rem;
background-color: var(--main-bg);
}
& .mantine-Tabs-panel {
padding: 0 1rem;
padding: 1.5rem 0.5rem;
}
button {
@ -35,27 +38,26 @@ const StyledTabs = styled(MantineTabs)`
button[data-active] {
color: var(--btn-subtle-fg);
background: none;
box-shadow: 2px 0 0 var(--primary-color) inset;
border-color: var(--primary-color);
&:hover {
background: none;
}
}
/* button[data-active]::before {
content: '';
border-left: 2px solid var(--primary-color);
position: absolute;
left: 0;
right: 0;
bottom: 0;
} */
`;
export const Tabs = ({ children, ...props }: TabsProps) => {
return <StyledTabs {...props}>{children}</StyledTabs>;
};
const Panel = ({ children, ...props }: TabsPanelProps) => {
return (
<StyledTabs.Panel {...props}>
<Suspense fallback={<></>}>{children}</Suspense>
</StyledTabs.Panel>
);
};
Tabs.List = StyledTabs.List;
Tabs.Panel = StyledTabs.Panel;
Tabs.Panel = Panel;
Tabs.Tab = StyledTabs.Tab;

View File

@ -27,7 +27,7 @@ const SIDE_QUEUE_OPTIONS = [
{ label: 'Floating', value: 'sideDrawerQueue' },
];
const WINDOWBAR_OPTIONS = [
const WINDOW_BAR_OPTIONS = [
{ label: 'Web (hidden)', value: Platform.WEB },
{ label: 'Windows', value: Platform.WINDOWS },
{ label: 'macOS', value: Platform.MACOS },
@ -41,7 +41,7 @@ export const GeneralTab = () => {
{
control: (
<Select
data={WINDOWBAR_OPTIONS}
data={WINDOW_BAR_OPTIONS}
disabled={!isElectron()}
value={settings.windowBarStyle}
onChange={(e) => {
@ -55,7 +55,7 @@ export const GeneralTab = () => {
}}
/>
),
description: 'Adjust the style of the windowbar',
description: 'Adjust the style of the application window bar',
isHidden: !isElectron(),
title: 'Window bar style',
},

View File

@ -3,7 +3,6 @@ import { Divider, Group, SelectItem, Stack } from '@mantine/core';
import {
FileInput,
NumberInput,
SegmentedControl,
Select,
Slider,
Switch,
@ -69,7 +68,7 @@ export const PlaybackTab = () => {
const playerOptions = [
{
control: (
<SegmentedControl
<Select
data={[
{
disabled: !isElectron(),
@ -98,7 +97,6 @@ export const PlaybackTab = () => {
control: (
<FileInput
placeholder={mpvPath}
size="sm"
width={225}
onChange={handleSetMpvPath}
/>
@ -115,9 +113,7 @@ export const PlaybackTab = () => {
autosize
defaultValue={mpvParameters}
minRows={4}
placeholder={
'Default parameters (one per line):\n--gapless-audio=weak\n--prefetch-playlist=yes'
}
placeholder={'(Add one per line):\n--gapless-audio=weak\n--prefetch-playlist=yes'}
width={225}
onBlur={(e) => {
if (isElectron()) {
@ -128,12 +124,14 @@ export const PlaybackTab = () => {
</Stack>
),
description: (
<Stack spacing={0}>
<Text
$noSelect
$secondary
size="sm"
>
Options to pass to the player{' '}
Options to pass to the player
</Text>
<Text>
<a
href="https://mpv.io/manual/stable/#audio"
rel="noreferrer"
@ -142,6 +140,7 @@ export const PlaybackTab = () => {
https://mpv.io/manual/stable/#audio
</a>
</Text>
</Stack>
),
isHidden: settings.type !== PlaybackType.LOCAL,
note: 'Restart required.',
@ -163,7 +162,7 @@ export const PlaybackTab = () => {
},
{
control: (
<SegmentedControl
<Select
data={[
{ label: 'Normal', value: PlaybackStyle.GAPLESS },
{ label: 'Crossfade', value: PlaybackStyle.CROSSFADE },
@ -346,7 +345,7 @@ export const PlaybackTab = () => {
const otherOptions = [
{
control: (
<SegmentedControl
<Select
data={[
{ label: 'Now', value: Play.NOW },
{ label: 'Next', value: Play.NEXT },
@ -367,28 +366,6 @@ export const PlaybackTab = () => {
isHidden: false,
title: 'Play button behavior',
},
{
control: (
<Switch
aria-label="Toggle skip buttons"
defaultChecked={settings.skipButtons?.enabled}
onChange={(e) =>
setSettings({
player: {
...settings,
skipButtons: {
...settings.skipButtons,
enabled: e.currentTarget.checked,
},
},
})
}
/>
),
description: 'Show or hide the skip buttons on the playerbar',
isHidden: false,
title: 'Show skip buttons',
},
{
control: (
<Group>
@ -437,6 +414,28 @@ export const PlaybackTab = () => {
isHidden: false,
title: 'Skip duration',
},
{
control: (
<Switch
aria-label="Toggle skip buttons"
defaultChecked={settings.skipButtons?.enabled}
onChange={(e) =>
setSettings({
player: {
...settings,
skipButtons: {
...settings.skipButtons,
enabled: e.currentTarget.checked,
},
},
})
}
/>
),
description: 'Show or hide the skip buttons on the playerbar',
isHidden: false,
title: 'Show skip buttons',
},
];
return (
@ -461,7 +460,7 @@ export const PlaybackTab = () => {
))}
<Text
$secondary
size="xs"
size="sm"
>
*The scrobble will be submitted if one or more of the above conditions is met
</Text>

View File

@ -0,0 +1,48 @@
import { lazy } from 'react';
import { Box } from '@mantine/core';
import { Tabs } from '/@/renderer/components';
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
const GeneralTab = lazy(() =>
import('/@/renderer/features/settings/components/general-tab').then((module) => ({
default: module.GeneralTab,
})),
);
const PlaybackTab = lazy(() =>
import('/@/renderer/features/settings/components/playback-tab').then((module) => ({
default: module.PlaybackTab,
})),
);
export const SettingsContent = () => {
const currentTab = useSettingsStore((state) => state.tab);
const { setSettings } = useSettingsStoreActions();
return (
<Box
h="100%"
p="1rem"
sx={{ overflow: 'scroll' }}
>
<Tabs
keepMounted={false}
orientation="horizontal"
value={currentTab}
variant="default"
onTabChange={(e) => e && setSettings({ tab: e })}
>
<Tabs.List>
<Tabs.Tab value="general">General</Tabs.Tab>
<Tabs.Tab value="playback">Playback</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="general">
<GeneralTab />
</Tabs.Panel>
<Tabs.Panel value="playback">
<PlaybackTab />
</Tabs.Panel>
</Tabs>
</Box>
);
};

View File

@ -0,0 +1,14 @@
import { RiSettings2Fill } from 'react-icons/ri';
import { PageHeader } from '/@/renderer/components';
import { LibraryHeaderBar } from '/@/renderer/features/shared';
export const SettingsHeader = () => {
return (
<PageHeader>
<LibraryHeaderBar>
<RiSettings2Fill size="2rem" />
<LibraryHeaderBar.Title>Settings</LibraryHeaderBar.Title>
</LibraryHeaderBar>
</PageHeader>
);
};

View File

@ -29,7 +29,7 @@ export const SettingsOptions = ({ title, description, control, note }: SettingsO
<Group>
<Text
$noSelect
size="sm"
size="md"
>
{title}
</Text>

View File

@ -1,72 +0,0 @@
import { Box } from '@mantine/core';
import { Tabs } from '/@/renderer/components';
import type { Variants } from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion';
import { GeneralTab } from '/@/renderer/features/settings/components/general-tab';
import { PlaybackTab } from '/@/renderer/features/settings/components/playback-tab';
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
export const Settings = () => {
const currentTab = useSettingsStore((state) => state.tab);
const { setSettings } = useSettingsStoreActions();
const tabVariants: Variants = {
in: {
opacity: 1,
transition: {
duration: 0.3,
},
x: 0,
},
out: {
opacity: 0,
x: 50,
},
};
return (
<Box
m={5}
sx={{ height: '50vh', maxHeight: '500px', overflowX: 'hidden' }}
>
<AnimatePresence initial={false}>
<Tabs
keepMounted={false}
orientation="vertical"
styles={{
tab: {
fontSize: '1rem',
padding: '0.5rem 1rem',
},
}}
value={currentTab}
variant="pills"
onTabChange={(e) => e && setSettings({ tab: e })}
>
<Tabs.List>
<Tabs.Tab value="general">General</Tabs.Tab>
<Tabs.Tab value="playback">Playback</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="general">
<motion.div
animate="in"
initial="out"
variants={tabVariants}
>
<GeneralTab />
</motion.div>
</Tabs.Panel>
<Tabs.Panel value="playback">
<motion.div
animate="in"
initial="out"
variants={tabVariants}
>
<PlaybackTab />
</motion.div>
</Tabs.Panel>
</Tabs>
</AnimatePresence>
</Box>
);
};

View File

@ -0,0 +1,21 @@
import { Flex } from '@mantine/core';
import { SettingsContent } from '/@/renderer/features/settings/components/settings-content';
import { SettingsHeader } from '/@/renderer/features/settings/components/settings-header';
import { AnimatedPage } from '/@/renderer/features/shared';
const SettingsRoute = () => {
return (
<AnimatedPage>
<Flex
direction="column"
h="100%"
w="100%"
>
<SettingsHeader />
<SettingsContent />
</Flex>
</AnimatedPage>
);
};
export default SettingsRoute;

View File

@ -9,10 +9,10 @@ import {
RiWindowFill,
} from 'react-icons/ri';
import { useNavigate } from 'react-router';
import { DropdownMenu, Text } from '/@/renderer/components';
import { Link } from 'react-router-dom';
import { DropdownMenu } from '/@/renderer/components';
import { ServerList } from '/@/renderer/features/servers';
import { EditServerForm } from '/@/renderer/features/servers/components/edit-server-form';
import { Settings } from '/@/renderer/features/settings';
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useServerList, useAuthStoreActions } from '/@/renderer/store';
import { ServerListItem, ServerType } from '/@/renderer/types';
@ -51,19 +51,6 @@ export const AppMenu = () => {
});
};
const handleSettingsModal = () => {
openModal({
children: <Settings />,
size: 'xl',
title: (
<Group position="center">
<RiSettings3Fill size={20} />
<Text>Settings</Text>
</Group>
),
});
};
const handleBrowserDevTools = () => {
browser?.devtools();
};
@ -100,8 +87,9 @@ export const AppMenu = () => {
Manage servers
</DropdownMenu.Item>
<DropdownMenu.Item
component={Link}
icon={<RiSettings3Fill />}
onClick={handleSettingsModal}
to={AppRoute.SETTINGS}
>
Settings
</DropdownMenu.Item>

View File

@ -58,6 +58,8 @@ const AlbumDetailRoute = lazy(
() => import('/@/renderer/features/albums/routes/album-detail-route'),
);
const SettingsRoute = lazy(() => import('/@/renderer/features/settings/routes/settings-route'));
const RouteErrorBoundary = lazy(
() => import('/@/renderer/features/action-required/components/route-error-boundary'),
);
@ -84,6 +86,11 @@ export const AppRouter = () => {
errorElement={<RouteErrorBoundary />}
path={AppRoute.HOME}
/>
<Route
element={<SettingsRoute />}
errorElement={<RouteErrorBoundary />}
path={AppRoute.SETTINGS}
/>
<Route
element={<NowPlayingRoute />}
errorElement={<RouteErrorBoundary />}

View File

@ -20,4 +20,5 @@ export enum AppRoute {
PLAYLISTS_DETAIL_SONGS = '/playlists/:playlistId/songs',
SEARCH = '/search',
SERVERS = '/servers',
SETTINGS = '/settings',
}