308 lines
9.4 KiB
TypeScript
308 lines
9.4 KiB
TypeScript
import { Box, Group, SimpleGrid, Stack, Text } from '@mantine/core';
|
|
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
|
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
|
import { useEffect, useState } from 'react';
|
|
import { FiChevronRight } from 'react-icons/fi';
|
|
import { textClasses } from 'styles';
|
|
import type { Theme, ThemePlaylist } from 'types';
|
|
import NoItemsFound from '../Shared/NoItemsFound';
|
|
import SettingsHeader from '../Shared/SettingsHeader';
|
|
import ThemePlaylistCard from './ThemePlaylistCard';
|
|
import ThemePlaylistModal from './ThemePlaylistModal';
|
|
|
|
function ThemePlaylists() {
|
|
const themesEndpoint = 'api/themes';
|
|
const { data: allThemeData } = useQueryAPI({
|
|
endpoint: themesEndpoint,
|
|
});
|
|
const themePlaylistsEndpoint = 'api/themePlaylists';
|
|
const { data: themePlaylistData, isFetched: isThemePlaylistDataFetched } = useQueryAPI({
|
|
endpoint: themePlaylistsEndpoint,
|
|
});
|
|
const [themePlaylists, setThemePlaylists] = useState<ThemePlaylist[]>([]);
|
|
const defaultPlaylist = {
|
|
id: undefined,
|
|
default: 0,
|
|
name: 'New Playlist',
|
|
themeList: '[]',
|
|
};
|
|
const { mutateAsync } = useMutationAPI();
|
|
const [opened, { open, close }] = useDisclosure(false);
|
|
const [selectedPlaylist, setSelectedPlaylist] =
|
|
useState<ThemePlaylist>(defaultPlaylist);
|
|
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
|
const [searchValue, setSearchValue] = useState('');
|
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const handleOpen = (playlist: ThemePlaylist) => {
|
|
setSelectedPlaylist(playlist);
|
|
open();
|
|
};
|
|
|
|
const handleClose = () => {
|
|
setSelectedPlaylist(defaultPlaylist);
|
|
close();
|
|
};
|
|
|
|
const handleSavePlaylist = () => {
|
|
mutateAsync({
|
|
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
|
|
endpoint: themePlaylistsEndpoint,
|
|
body: selectedPlaylist ?? {},
|
|
});
|
|
close();
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (themePlaylistData && isThemePlaylistDataFetched) {
|
|
setThemePlaylists(
|
|
themePlaylistData.filter(
|
|
(a: ThemePlaylist) =>
|
|
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
|
|
a.name.includes('Time of Day'),
|
|
),
|
|
);
|
|
}
|
|
if (debouncedSearch === searchValue) {
|
|
setIsLoading(false);
|
|
}
|
|
}, [themePlaylistData, isThemePlaylistDataFetched, debouncedSearch]);
|
|
|
|
return (
|
|
<Stack>
|
|
<ThemePlaylistModal
|
|
opened={opened}
|
|
handleClose={handleClose}
|
|
selectedPlaylist={selectedPlaylist}
|
|
defaultPlaylist={defaultPlaylist}
|
|
setSelectedPlaylist={setSelectedPlaylist}
|
|
handleSavePlaylist={handleSavePlaylist}
|
|
allThemes={allThemeData}
|
|
createPlaylist={mutateAsync}
|
|
/>
|
|
<SettingsHeader
|
|
setDragging={undefined}
|
|
openModal={open}
|
|
title={'Theme Playlists'}
|
|
tooltip={{
|
|
title: 'Theme playlists can be created here and applied to the homepage.',
|
|
highlightList: ['theme', 'themes', 'homepage', 'playlist'],
|
|
description:
|
|
'Playlists are lists of themes. They can be enabled either here or in the settings button on the homepage. When a playlist is enabled, it will shuffle the included themes on the homepage. If a theme is part of a playlist, it will appear while the playlist is active, regardless of random/shuffle.',
|
|
}}
|
|
searchValue={searchValue}
|
|
setSearchValue={setSearchValue}
|
|
searchPlaceholder={'Search Theme Playlists...'}
|
|
isLoading={isLoading}
|
|
setIsLoading={setIsLoading}
|
|
/>
|
|
<Stack mt={isMobile ? '10rem' : '6rem'}>
|
|
{allThemeData && (
|
|
<>
|
|
<Group justify='center' align='center' pos={'relative'}>
|
|
<Text
|
|
p={0}
|
|
className={textClasses.settingsSubTitle}
|
|
classNames={textClasses}
|
|
>
|
|
Defaults
|
|
</Text>
|
|
</Group>
|
|
<Group justify='center' align='center' pos={'relative'}>
|
|
<Group pos={'relative'}>
|
|
{'All' === currentThemePlaylist && (
|
|
<FiChevronRight
|
|
style={{
|
|
position: 'absolute',
|
|
left: -14,
|
|
zIndex: 10,
|
|
fontSize: '2rem',
|
|
fontWeight: 'bold',
|
|
borderRadius: '50%',
|
|
backgroundColor: 'var(--mantine-color-border-0)',
|
|
color: 'var(--mantine-color-background-0)',
|
|
}}
|
|
/>
|
|
)}
|
|
<Box>
|
|
<ThemePlaylistCard
|
|
cardPlaylist={{
|
|
id: undefined,
|
|
name: 'All',
|
|
default: 1,
|
|
themeList: `[${allThemeData?.map((a: Theme) => a.id) ?? ''}]`,
|
|
}}
|
|
handleOpen={handleOpen}
|
|
allThemes={allThemeData}
|
|
/>
|
|
</Box>
|
|
</Group>
|
|
<Group pos={'relative'}>
|
|
{'Backgrounds' === currentThemePlaylist && (
|
|
<FiChevronRight
|
|
style={{
|
|
position: 'absolute',
|
|
left: -14,
|
|
zIndex: 10,
|
|
fontSize: '2rem',
|
|
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: '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: '2rem',
|
|
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: '2rem',
|
|
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:
|
|
themePlaylists?.filter(
|
|
(a: ThemePlaylist) => a.name === 'Time of Day',
|
|
)[0]?.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 justify='center' align='center' pos={'relative'}>
|
|
<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 > 1 ? (
|
|
<SimpleGrid cols={isMobile ? 1 : 4}>
|
|
{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: '2rem',
|
|
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>
|
|
);
|
|
})}
|
|
</SimpleGrid>
|
|
) : (
|
|
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
|
|
<NoItemsFound />
|
|
</Group>
|
|
)}
|
|
</Group>
|
|
</>
|
|
)}
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
export default ThemePlaylists;
|