Theme updates and style fixes

This commit is contained in:
Sean Edmunds
2025-05-23 13:32:38 -05:00
parent ee69cc9d09
commit 79ebb1b9e9
16 changed files with 488 additions and 386 deletions

View File

@@ -88,6 +88,16 @@ function App() {
t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]), t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]),
), ),
); );
} else if (currentThemePlaylist === 'Backgrounds') {
handleThemeChange(
allThemes.filter((a: any) => !a.backgroundImage.toLowerCase().includes('none')),
false,
);
} else if (currentThemePlaylist === 'No Backgrounds') {
handleThemeChange(
allThemes.filter((a: any) => a.backgroundImage.toLowerCase().includes('none')),
false,
);
} else { } else {
const themeList = JSON.parse( const themeList = JSON.parse(
themePlaylists.find((t: ThemePlaylist) => t.name === currentThemePlaylist) themePlaylists.find((t: ThemePlaylist) => t.name === currentThemePlaylist)

View File

@@ -189,7 +189,8 @@ function SettingsButton() {
<Group mt={'-1.5rem'} mb={'-.25rem'} w={'95%'} justify='space-between'> <Group mt={'-1.5rem'} mb={'-.25rem'} w={'95%'} justify='space-between'>
<Tooltip <Tooltip
label='Shuffle Themes' label='Shuffle Themes'
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
withArrow withArrow
> >
<ActionIcon <ActionIcon
@@ -217,7 +218,8 @@ function SettingsButton() {
</Tooltip> </Tooltip>
<Tooltip <Tooltip
label='Random Theme' label='Random Theme'
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
withArrow withArrow
> >
<ActionIcon <ActionIcon

View File

@@ -25,12 +25,15 @@ function AppSettingsItem({
<Box pos={'relative'}> <Box pos={'relative'}>
{enabled === 0 && ( {enabled === 0 && (
<Tooltip <Tooltip
label={<Text c={'var(--mantine-color-primary-0)'}>This app is disabled</Text>} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
label='This app is disabled.'
> >
<ActionIcon <ActionIcon
className={actionIconClasses.settingsTooltip} className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses} classNames={actionIconClasses}
pos={'absolute'} pos={'absolute'}
right={0}
size={'xl'} size={'xl'}
variant='subtle' variant='subtle'
style={{ zIndex: 1 }} style={{ zIndex: 1 }}

View File

@@ -2,7 +2,6 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import { import {
Box, Box,
Button, Button,
Card,
type ComboboxItem, type ComboboxItem,
type ComboboxLikeRenderOptionInput, type ComboboxLikeRenderOptionInput,
Divider, Divider,
@@ -281,142 +280,104 @@ const ApplicationSettings = () => {
isLoading={isLoading} isLoading={isLoading}
setIsLoading={setIsLoading} setIsLoading={setIsLoading}
/> />
{dragging ? ( <DragDropContext onDragEnd={onDragEnd}>
<DragDropContext onDragEnd={onDragEnd}>
<Stack
mt={isMobile ? '9rem' : '6rem'}
mb={isMobile ? '6rem' : '1rem'}
w={'100%'}
align='center'
>
<Droppable droppableId={'groupList'}>
{(provided) => (
<Stack
w={'75%'}
align='center'
justify='center'
ref={provided.innerRef}
{...provided.droppableProps}
>
{applications?.map((app: Application) => (
<Draggable
key={app.id}
draggableId={`${app.id}`}
index={Number(app.index)}
>
{(provided) => (
<Box
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card
w={'400px'}
style={{
backgroundColor: 'var(--mantine-color-search-0)',
border: '1px solid var(--mantine-color-border-0)',
borderRadius: '8px',
}}
>
<Group gap={0} align='center' justify='space-between'>
<Stack gap={0}>
<Text c={'var(--mantine-color-accent-0)'} fz={14}>
Name
</Text>
<Text
p={4}
style={{
borderRadius: '4px',
paddingLeft: '8px',
color: 'var(--mantine-color-primary-0)',
backgroundColor: 'var(--mantine-color-search-0)',
}}
>
{app.name}
</Text>
</Stack>
<Stack gap={0}>
<Text c={'var(--mantine-color-accent-0)'} fz={14}>
Filter Name
</Text>
<Text
p={4}
style={{
borderRadius: '4px',
paddingLeft: '8px',
color: 'var(--mantine-color-primary-0)',
backgroundColor: 'var(--mantine-color-search-0)',
}}
>
{app.filterName}
</Text>
</Stack>
</Group>
</Card>
</Box>
)}
</Draggable>
))}
{provided.placeholder}
</Stack>
)}
</Droppable>
</Stack>
</DragDropContext>
) : (
<Stack <Stack
mt={isMobile ? '9rem' : '6rem'} mt={isMobile ? '9rem' : '6rem'}
mb={isMobile ? '6rem' : '1rem'} mb={isMobile ? '6rem' : '1rem'}
w={'100%'} w={'100%'}
align='center' align='center'
> >
<Grid columns={18} w={'75%'}> {applications.length > 0 ? (
{applications.length > 0 ? ( <Droppable droppableId={'groupList'}>
applications?.map((application: Application) => ( {(provided) => (
<GridCol span={6} key={application.id}> <Grid
<Popover columns={18}
withArrow w={dragging ? '30%' : '75%'}
withOverlay ref={provided.innerRef}
closeOnClickOutside={!isOpen} {...provided.droppableProps}
classNames={popoverClasses} >
> {applications?.map((application: Application, index: number) => (
<Popover.Target> <GridCol span={dragging ? 18 : 6} key={application.id}>
<Box {dragging ? (
onClick={(e) => { <Draggable
e.stopPropagation(); key={application.id}
}} draggableId={`${application.id}`}
> index={Number(application.index)}
<AppSettingsItem >
url={application.url} {(provided) => (
icon={application.icon} <Box
name={application.name} ref={provided.innerRef}
enabled={application.enabled} {...provided.draggableProps}
allIcons={allIcons} {...provided.dragHandleProps}
/> >
</Box> <Box pos={'relative'} h={'100%'}>
</Popover.Target> <Text
<Popover.Dropdown w={isMobile ? '100%' : undefined}> style={{
<DebouncedApplication color: 'var(--mantine-color-primary-0)',
application={application} zIndex: 1,
setIsOpen={setIsOpen} alignContent: 'center',
allIcons={allIcons} fontSize: '1.5em',
/> position: 'absolute',
</Popover.Dropdown> right: 0,
</Popover> paddingRight: '1rem',
</GridCol> height: '100%',
)) }}
) : ( >
<GridCol #{index + 1}
w={'100%'} </Text>
span={18} <AppSettingsItem
style={{ display: 'flex', justifyContent: 'center' }} url={application.url}
> icon={application.icon}
<NoItemsFound /> name={application.name}
</GridCol> enabled={application.enabled}
)} allIcons={allIcons}
</Grid> />
</Box>
</Box>
)}
</Draggable>
) : (
<Popover
withArrow
withOverlay
closeOnClickOutside={!isOpen}
classNames={popoverClasses}
>
<Popover.Target>
<Box
onClick={(e) => {
e.stopPropagation();
}}
>
<AppSettingsItem
url={application.url}
icon={application.icon}
name={application.name}
enabled={application.enabled}
allIcons={allIcons}
/>
</Box>
</Popover.Target>
<Popover.Dropdown w={isMobile ? '100%' : undefined}>
<DebouncedApplication
application={application}
setIsOpen={setIsOpen}
allIcons={allIcons}
/>
</Popover.Dropdown>
</Popover>
)}
</GridCol>
))}
{provided.placeholder}
</Grid>
)}
</Droppable>
) : (
<NoItemsFound />
)}
</Stack> </Stack>
)} </DragDropContext>
</Stack> </Stack>
); );
}; };

View File

@@ -72,7 +72,8 @@ function DebouncedApplication({
} }
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >
@@ -89,7 +90,8 @@ function DebouncedApplication({
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Enabled'} label={'Enabled'}
> >

View File

@@ -60,7 +60,8 @@ function DebouncedBookmarkItem({
title='Delete Bookmark' title='Delete Bookmark'
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >

View File

@@ -168,7 +168,8 @@ function DebouncedGroupSettingsItem({
}} }}
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >

View File

@@ -43,7 +43,8 @@ function DebouncedMessage({
} }
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >

View File

@@ -65,7 +65,8 @@ function DebouncedSearchProvider({
} }
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >

View File

@@ -10,6 +10,7 @@ import {
TextInput, TextInput,
} from '@mantine/core'; } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useState } from 'react';
import { FiHelpCircle, FiLoader, FiPlus } from 'react-icons/fi'; import { FiHelpCircle, FiLoader, FiPlus } from 'react-icons/fi';
import { import {
actionIconClasses, actionIconClasses,
@@ -46,6 +47,7 @@ function SettingsHeader({
setIsLoading: (isLoading: boolean) => void; setIsLoading: (isLoading: boolean) => void;
}) { }) {
const { isMobile } = useCommonHooks(); const { isMobile } = useCommonHooks();
const [disabled, setDisabled] = useState(false);
return ( return (
<Group <Group
align='center' align='center'
@@ -119,6 +121,7 @@ function SettingsHeader({
</Group> </Group>
{searchValue !== undefined && ( {searchValue !== undefined && (
<TextInput <TextInput
disabled={disabled}
w={isMobile ? undefined : '35%'} w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'} pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem} className={textInputClasses.settingsItem}
@@ -156,6 +159,7 @@ function SettingsHeader({
top={isMobile ? '3.5rem' : undefined} top={isMobile ? '3.5rem' : undefined}
onChange={(e) => { onChange={(e) => {
setDragging(e.target.checked); setDragging(e.target.checked);
setDisabled(e.target.checked);
}} }}
/> />
)} )}

View File

@@ -197,7 +197,7 @@ function ThemeCard({
setShiftEdit(0); setShiftEdit(0);
}} }}
onClick={() => { onClick={() => {
setShiftEdit(0); handleOpen(cardTheme);
}} }}
gap={0} gap={0}
style={{ style={{
@@ -216,9 +216,6 @@ function ThemeCard({
}} }}
> >
<FiEdit <FiEdit
onClick={() => {
handleOpen(cardTheme);
}}
style={{ style={{
fontSize: '2em', fontSize: '2em',
fontWeight: 'bold', fontWeight: 'bold',
@@ -234,7 +231,15 @@ function ThemeCard({
setShiftShuffle(0); setShiftShuffle(0);
}} }}
onClick={() => { onClick={() => {
setShiftShuffle(0); const themeList = JSON.parse(
themePlaylists?.find(
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
)?.themeList ?? '[]',
);
const filteredThemes = allThemes.filter((t: Theme) =>
themeList.includes(t.id),
);
handleThemeChange(filteredThemes, false);
}} }}
gap={0} gap={0}
style={{ style={{
@@ -254,17 +259,6 @@ function ThemeCard({
}} }}
> >
<FiShuffle <FiShuffle
onClick={() => {
const themeList = JSON.parse(
themePlaylists?.find(
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
)?.themeList ?? '[]',
);
const filteredThemes = allThemes.filter((t: Theme) =>
themeList.includes(t.id),
);
handleThemeChange(filteredThemes, false);
}}
style={{ style={{
fontSize: '2em', fontSize: '2em',
fontWeight: 'bold', fontWeight: 'bold',

View File

@@ -8,9 +8,9 @@ import {
Text, Text,
} from '@mantine/core'; } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import { FiEdit, FiRefreshCcw, FiTrash } from 'react-icons/fi'; import { FiEdit, FiTrash } from 'react-icons/fi';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal'; import DeleteItemModal from '../Shared/DeleteItemModal';
import ThemeMiniCard from './ThemeMiniCard'; import ThemeMiniCard from './ThemeMiniCard';
@@ -24,14 +24,12 @@ function ThemePlaylistCard({
handleOpen: (cardPlaylist: ThemePlaylist) => void; handleOpen: (cardPlaylist: ThemePlaylist) => void;
allThemes: Theme[]; allThemes: Theme[];
}) { }) {
const themePlaylistEndpoint = 'api/themePlaylists';
const [hover, setHover] = useState(false); const [hover, setHover] = useState(false);
const { setThemeName, setCurrentThemePlaylist, theme, handleDelete } = useCommonHooks(); const { setThemeName, setCurrentThemePlaylist, theme, handleDelete } = useCommonHooks();
const syncableLists = ['All', 'Backgrounds', 'No Backgrounds']; const defaultLists = ['All', 'Backgrounds', 'No Backgrounds'];
const { mutateAsync } = useMutationAPI();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false); const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const themeList = JSON.parse( const themeList = JSON.parse(
cardPlaylist.themeList.name === 'Time of Day' ? '[]' : cardPlaylist.themeList, cardPlaylist?.themeList?.name === 'Time of Day' ? '[]' : cardPlaylist.themeList,
); );
return ( return (
<Group <Group
@@ -84,7 +82,7 @@ function ThemePlaylistCard({
> >
{cardPlaylist.name} {cardPlaylist.name}
</Text> </Text>
{syncableLists.includes(cardPlaylist.name) ? ( {!defaultLists.includes(cardPlaylist.name) && (
<ActionIcon <ActionIcon
style={{ style={{
position: 'absolute', position: 'absolute',
@@ -100,104 +98,35 @@ function ThemePlaylistCard({
: 'var(--mantine-color-background-0)', : 'var(--mantine-color-background-0)',
zIndex: 3, zIndex: 3,
}} }}
onClick={(event) => { onClick={() => {
event.stopPropagation(); handleOpen(cardPlaylist);
if (cardPlaylist.name === 'All') {
mutateAsync({
method: 'PATCH',
endpoint: themePlaylistEndpoint,
body: {
id: cardPlaylist.id,
name: 'All',
themeList: JSON.stringify(allThemes.map((theme: Theme) => theme.id)),
},
});
}
if (cardPlaylist.name === 'Backgrounds') {
mutateAsync({
method: 'PATCH',
endpoint: themePlaylistEndpoint,
body: {
id: cardPlaylist.id,
name: 'Backgrounds',
themeList: JSON.stringify(
allThemes
.filter(
(theme: Theme) => theme.backgroundImage.toLowerCase() !== 'none',
)
.map((theme: Theme) => theme.id),
),
},
});
}
if (cardPlaylist.name === 'No Backgrounds') {
mutateAsync({
method: 'PATCH',
endpoint: themePlaylistEndpoint,
body: {
id: cardPlaylist.id,
name: 'No Backgrounds',
themeList: JSON.stringify(
allThemes
.filter(
(theme: Theme) => theme.backgroundImage.toLowerCase() === 'none',
)
.map((theme: Theme) => theme.id),
),
},
});
}
}} }}
> >
<FiRefreshCcw /> <FiEdit />
</ActionIcon>
)}
{cardPlaylist.default === 0 && (
<ActionIcon
style={{
position: 'absolute',
top: -10,
right: 40,
paddingRight: 4,
paddingLeft: 4,
backgroundColor: 'var(--mantine-color-border-0)',
borderRadius: '4px',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
zIndex: 3,
}}
onClick={() => {
openModal();
}}
>
<FiTrash />
</ActionIcon> </ActionIcon>
) : (
<>
<ActionIcon
style={{
position: 'absolute',
top: -10,
right: 6,
paddingRight: 4,
paddingLeft: 4,
backgroundColor: 'var(--mantine-color-border-0)',
borderRadius: '4px',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
zIndex: 3,
}}
onClick={() => {
handleOpen(cardPlaylist);
}}
>
<FiEdit />
</ActionIcon>
{cardPlaylist.default === 0 && (
<ActionIcon
style={{
position: 'absolute',
top: -10,
right: 40,
paddingRight: 4,
paddingLeft: 4,
backgroundColor: 'var(--mantine-color-border-0)',
borderRadius: '4px',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
zIndex: 3,
}}
onClick={() => {
openModal();
}}
>
<FiTrash />
</ActionIcon>
)}
</>
)} )}
<Button <Button
w={'275px'} w={'275px'}

View File

@@ -4,7 +4,7 @@ import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import NoItemsFound from '../Shared/NoItemsFound'; import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader'; import SettingsHeader from '../Shared/SettingsHeader';
import ThemePlaylistCard from './ThemePlaylistCard'; import ThemePlaylistCard from './ThemePlaylistCard';
@@ -66,7 +66,6 @@ function ThemePlaylists() {
setIsLoading(false); setIsLoading(false);
} }
}, [themePlaylistData, isThemePlaylistDataFetched, debouncedSearch]); }, [themePlaylistData, isThemePlaylistDataFetched, debouncedSearch]);
return ( return (
<Stack> <Stack>
<ThemePlaylistModal <ThemePlaylistModal
@@ -96,102 +95,207 @@ function ThemePlaylists() {
isLoading={isLoading} isLoading={isLoading}
setIsLoading={setIsLoading} setIsLoading={setIsLoading}
/> />
<Group mt={'6rem'} justify='center' align='center' pos={'relative'}> {allThemeData ? (
<Text p={0} className={textClasses.settingsSubTitle} classNames={textClasses}> <>
Defaults <Group mt={'6rem'} justify='center' align='center' pos={'relative'}>
</Text> <Text
</Group> p={0}
<Group justify='center' align='center' pos={'relative'}> className={textClasses.settingsSubTitle}
{themePlaylists.length > 0 ? ( classNames={textClasses}
themePlaylists >
.filter((playlist: ThemePlaylist) => playlist.default === 1) Defaults
.map((cardPlaylist: ThemePlaylist) => { </Text>
return (
<Group key={cardPlaylist.name} pos={'relative'}>
{cardPlaylist.name === currentThemePlaylist && (
<FiChevronRight
style={{
position: 'absolute',
left: -14,
zIndex: 10,
fontSize: '2em',
fontWeight: 'bold',
borderRadius: '50%',
backgroundColor: 'var(--mantine-color-border-0)',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
}}
/>
)}
<Box>
<ThemePlaylistCard
cardPlaylist={cardPlaylist}
handleOpen={handleOpen}
allThemes={allThemeData}
/>
</Box>
</Group>
);
})
) : (
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
<NoItemsFound />
</Group> </Group>
)} <Group justify='center' align='center' pos={'relative'}>
</Group> <Group pos={'relative'}>
<Group justify='center' align='center' pos={'relative'}> {'All' === currentThemePlaylist && (
<Text p={0} className={textClasses.settingsSubTitle} classNames={textClasses}> <FiChevronRight
User Made style={{
</Text> position: 'absolute',
</Group> left: -14,
<Group zIndex: 10,
justify='center' fontSize: '2em',
align='center' fontWeight: 'bold',
pos={'relative'} borderRadius: '50%',
mb={isMobile ? '6rem' : undefined} backgroundColor: 'var(--mantine-color-border-0)',
> color:
{themePlaylists.length > 0 ? ( theme.borderColor === theme.primaryColor
themePlaylists ? 'var(--mantine-color-accent-0)'
.filter((playlist: ThemePlaylist) => playlist.default === 0) : 'var(--mantine-color-background-0)',
.sort((a: ThemePlaylist, b: ThemePlaylist) => (a.name > b.name ? 1 : -1)) }}
.map((cardPlaylist: ThemePlaylist) => { />
return ( )}
<Group key={cardPlaylist.name} pos={'relative'}> <Box>
{cardPlaylist.name === currentThemePlaylist && ( <ThemePlaylistCard
<FiChevronRight cardPlaylist={{
style={{ id: undefined,
position: 'absolute', name: 'All',
left: -14, default: 1,
zIndex: 10, themeList: `[${allThemeData?.map((a: Theme) => a.id) ?? ''}]`,
fontSize: '2em', }}
fontWeight: 'bold', handleOpen={handleOpen}
borderRadius: '50%', allThemes={allThemeData}
backgroundColor: 'var(--mantine-color-border-0)', />
color: </Box>
theme.borderColor === theme.primaryColor </Group>
? 'var(--mantine-color-accent-0)' <Group pos={'relative'}>
: 'var(--mantine-color-primary-0)', {'Backgrounds' === currentThemePlaylist && (
}} <FiChevronRight
/> style={{
)} position: 'absolute',
<Box> left: -14,
<ThemePlaylistCard zIndex: 10,
cardPlaylist={cardPlaylist} fontSize: '2em',
handleOpen={handleOpen} fontWeight: 'bold',
allThemes={allThemeData} borderRadius: '50%',
/> backgroundColor: 'var(--mantine-color-border-0)',
</Box> color:
</Group> theme.borderColor === theme.primaryColor
); ? 'var(--mantine-color-accent-0)'
}) : 'var(--mantine-color-background-0)',
) : ( }}
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}> />
<NoItemsFound /> )}
<Box>
<ThemePlaylistCard
cardPlaylist={{
id: undefined,
name: 'Backgrounds',
default: 1,
themeList: `[${allThemeData?.filter((a: any) => !a.backgroundImage.toLowerCase().includes('none')).map((a: Theme) => a.id) ?? ''}]`,
}}
handleOpen={handleOpen}
allThemes={allThemeData}
/>
</Box>
</Group>
<Group pos={'relative'}>
{'No Backgrounds' === currentThemePlaylist && (
<FiChevronRight
style={{
position: 'absolute',
left: -14,
zIndex: 10,
fontSize: '2em',
fontWeight: 'bold',
borderRadius: '50%',
backgroundColor: 'var(--mantine-color-border-0)',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
}}
/>
)}
<Box>
<ThemePlaylistCard
cardPlaylist={{
id: undefined,
name: 'No Backgrounds',
default: 1,
themeList: `[${allThemeData?.filter((a: any) => a.backgroundImage.toLowerCase().includes('none')).map((a: Theme) => a.id) ?? ''}]`,
}}
handleOpen={handleOpen}
allThemes={allThemeData}
/>
</Box>
</Group>
<Group pos={'relative'}>
{'Time of Day' === currentThemePlaylist && (
<FiChevronRight
style={{
position: 'absolute',
left: -14,
zIndex: 10,
fontSize: '2em',
fontWeight: 'bold',
borderRadius: '50%',
backgroundColor: 'var(--mantine-color-border-0)',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-background-0)',
}}
/>
)}
<Box>
<ThemePlaylistCard
cardPlaylist={{
id: undefined,
name: 'Time of Day',
default: 1,
themeList:
themePlaylists?.filter(
(a: ThemePlaylist) => a.name === 'Time of Day',
)[0]?.themeList ?? '[]',
}}
handleOpen={handleOpen}
allThemes={allThemeData}
/>
</Box>
</Group>
</Group> </Group>
)} <Group justify='center' align='center' pos={'relative'}>
</Group> <Text
p={0}
className={textClasses.settingsSubTitle}
classNames={textClasses}
>
User Made
</Text>
</Group>
<Group
justify='center'
align='center'
pos={'relative'}
mb={isMobile ? '6rem' : undefined}
>
{themePlaylists.length > 0 ? (
themePlaylists
.filter((playlist: ThemePlaylist) => playlist.default === 0)
.sort((a: ThemePlaylist, b: ThemePlaylist) =>
a.name > b.name ? 1 : -1,
)
.map((cardPlaylist: ThemePlaylist) => {
return (
<Group key={cardPlaylist.name} pos={'relative'}>
{cardPlaylist.name === currentThemePlaylist && (
<FiChevronRight
style={{
position: 'absolute',
left: -14,
zIndex: 10,
fontSize: '2em',
fontWeight: 'bold',
borderRadius: '50%',
backgroundColor: 'var(--mantine-color-border-0)',
color:
theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-primary-0)',
}}
/>
)}
<Box>
<ThemePlaylistCard
cardPlaylist={cardPlaylist}
handleOpen={handleOpen}
allThemes={allThemeData}
/>
</Box>
</Group>
);
})
) : (
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
<NoItemsFound />
</Group>
)}
</Group>
</>
) : (
<></>
)}
</Stack> </Stack>
</Stack> </Stack>
); );

View File

@@ -1,10 +1,11 @@
import { Box, Group, Overlay, Stack, Switch, Text } from '@mantine/core'; import { Box, Button, Group, Overlay, Stack, Switch, Text, Tooltip } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FaDice } from 'react-icons/fa'; import { FaDice } from 'react-icons/fa';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
import { switchClasses } from 'styles'; import { useNavigate } from 'react-router-dom';
import { switchClasses, textClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import NoItemsFound from '../Shared/NoItemsFound'; import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader'; import SettingsHeader from '../Shared/SettingsHeader';
@@ -45,8 +46,15 @@ function ThemesSettings() {
const [searchValue, setSearchValue] = useState(''); const [searchValue, setSearchValue] = useState('');
const [debouncedSearch] = useDebouncedValue(searchValue, 500); const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { currentTheme, setCurrentTheme, theme, isMobile, setCurrentThemePlaylist } = const navigate = useNavigate();
useCommonHooks(); const {
currentTheme,
setCurrentTheme,
theme,
isMobile,
currentThemePlaylist,
setCurrentThemePlaylist,
} = useCommonHooks();
const handleOpen = (theme: Theme) => { const handleOpen = (theme: Theme) => {
setSelectedTheme(theme); setSelectedTheme(theme);
@@ -102,7 +110,6 @@ function ThemesSettings() {
setIsLoading(false); setIsLoading(false);
} }
}, [allThemeData, isAllThemeDataFetched, debouncedSearch]); }, [allThemeData, isAllThemeDataFetched, debouncedSearch]);
return ( return (
<Stack w={'100%'} align='center'> <Stack w={'100%'} align='center'>
<ThemeModal <ThemeModal
@@ -136,36 +143,13 @@ function ThemesSettings() {
isLoading={isLoading} isLoading={isLoading}
setIsLoading={setIsLoading} setIsLoading={setIsLoading}
/> />
<Switch
label={
isMobile ? (
<FaDice size={20} color={'var(--mantine-color-primary-0)'} />
) : (
<Text c={'var(--mantine-color-primary-0)'}>Random Theme</Text>
)
}
labelPosition='left'
pos={'absolute'}
right={isMobile ? '2rem' : '1rem'}
top={isMobile ? '3.5rem' : '2rem'}
checked={currentTheme === 'random'}
onChange={(e) => {
setCurrentTheme(e.target.checked ? 'random' : theme.name);
setCurrentThemePlaylist('All');
}}
className={switchClasses.default}
classNames={switchClasses}
style={{
zIndex: 250,
}}
/>
<Group <Group
w={'85%'} w={'85%'}
justify='center' justify='center'
align='center' align='center'
pos={'relative'} pos={'relative'}
mt={isMobile ? '10rem' : '7rem'} mt={isMobile ? '10rem' : '7rem'}
mb={isMobile ? '6rem' : '1rem'} mb={isMobile ? '8rem' : '6.5rem'}
> >
<Overlay <Overlay
display={currentTheme === 'random' && debouncedSearch === '' ? 'flex' : 'none'} display={currentTheme === 'random' && debouncedSearch === '' ? 'flex' : 'none'}
@@ -202,7 +186,16 @@ function ThemesSettings() {
<Box <Box
style={{ style={{
zIndex: zIndex:
currentTheme === 'random' && cardTheme.name === theme.name ? 195 : 0, currentTheme === 'random' &&
(themePlaylists
.filter(
(t: ThemePlaylist) =>
t.name.toLowerCase() === currentThemePlaylist.toLowerCase(),
)[0]
?.themeList.includes(`${cardTheme.id}`) ||
cardTheme.name === theme.name)
? 195
: 0,
}} }}
> >
<ThemeCard <ThemeCard
@@ -223,6 +216,96 @@ function ThemesSettings() {
</Group> </Group>
)} )}
</Group> </Group>
<Group
align='center'
justify='center'
style={{
backdropFilter: 'blur(10px) brightness(.75)',
zIndex: 199,
paddingTop: '1.5rem',
paddingBottom: '1.5rem',
paddingLeft: '1rem',
paddingRight: '1rem',
height: 'min-content',
width: '100%',
marginTop: 0,
position: 'absolute',
bottom: 0,
}}
>
<Group w={isMobile ? undefined : '100%'}>
<Text
miw={isMobile ? 100 : undefined}
className={textClasses.settingsSubTitle}
classNames={textClasses}
>
Current Settings:
</Text>
<Group pos={isMobile ? undefined : 'absolute'} w={'100%'} justify='center'>
<Group align='center' justify='center'>
<Text
style={{
color: 'var(--mantine-color-accent-0)',
fontWeight: 'bold',
}}
>
Active Playlist:
</Text>
<Text
style={{
color: 'var(--mantine-color-primary-0)',
fontWeight: 'bold',
fontSize: '1.2em',
}}
>
{currentThemePlaylist === 'All' ? 'All' : currentThemePlaylist}
</Text>
</Group>
<Button
onClick={() => {
if (currentThemePlaylist === 'All') {
navigate(`/${import.meta.env.VITE_BASE_URL}/settings/themePlaylists`);
} else {
setCurrentThemePlaylist('All');
setCurrentTheme('random');
}
}}
>
{currentThemePlaylist === 'All' ? 'Select Playlist' : 'Disable Playlist'}
</Button>
<Tooltip
label='Shuffle is always on when a playlist is active.'
c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
disabled={currentThemePlaylist === 'All'}
>
<Group>
<Switch
disabled={currentThemePlaylist !== 'All'}
label={
isMobile ? (
<FaDice size={20} color={'var(--mantine-color-primary-0)'} />
) : (
<Text c={'var(--mantine-color-primary-0)'}>Shuffle Enabled</Text>
)
}
labelPosition='left'
checked={currentTheme === 'random'}
onChange={(e) => {
setCurrentTheme(e.target.checked ? 'random' : theme.name);
setCurrentThemePlaylist('All');
}}
className={switchClasses.default}
classNames={switchClasses}
style={{
zIndex: 250,
}}
/>
</Group>
</Tooltip>
</Group>
</Group>
</Group>
</Stack> </Stack>
); );
} }

View File

@@ -64,7 +64,8 @@ function DebouncedWolClient({
} }
/> />
<Tooltip <Tooltip
color={'var(--mantine-color-primary-0)'} c={'var(--mantine-color-primary-0)'}
bg={'var(--mantine-color-background-0)'}
bd={'solid 1px var(--mantine-color-border-0)'} bd={'solid 1px var(--mantine-color-border-0)'}
label={'Delete'} label={'Delete'}
> >

View File

@@ -22,11 +22,16 @@
&[data-active="true"] { &[data-active="true"] {
border-color: var(--mantine-color-primary-0); border-color: var(--mantine-color-primary-0);
} }
width: 100%;
border-radius: 0%;
}
.tabLabel {
text-align: end;
} }
.list { .list {
border-top: solid 2px var(--mantine-color-accent-0); border-top: solid 2px var(--mantine-color-accent-0);
padding-left: 2rem; padding-top: 1.75rem;
padding-top: 2rem; align-items: end;
} }
.list::before { .list::before {
border-color: var(--mantine-color-accent-0); border-color: var(--mantine-color-accent-0);