fix/theme-playlists-performance #1
@@ -2,7 +2,7 @@ import { Box, Group, LoadingOverlay, SimpleGrid, Stack, Text } from '@mantine/co
|
||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||
import { NoItemsFound, SettingsHeader } from 'components';
|
||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FiChevronRight } from 'react-icons/fi';
|
||||
import { textClasses } from 'styles';
|
||||
import type { Theme, ThemePlaylist } from 'types';
|
||||
@@ -21,13 +21,17 @@ export function ThemePlaylists() {
|
||||
});
|
||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||
const themePlaylists: ThemePlaylist[] =
|
||||
themePlaylistData?.filter(
|
||||
(a: ThemePlaylist) =>
|
||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||
a.name.includes('Time of Day'),
|
||||
) || [];
|
||||
const defaultPlaylist = {
|
||||
|
||||
const themePlaylists: ThemePlaylist[] = useMemo(() => {
|
||||
return (
|
||||
themePlaylistData?.filter(
|
||||
(a: ThemePlaylist) =>
|
||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||
a.name.includes('Time of Day'),
|
||||
) || []
|
||||
);
|
||||
}, [themePlaylistData, debouncedSearch]);
|
||||
const defaultPlaylist: ThemePlaylist = {
|
||||
id: undefined,
|
||||
default: 0,
|
||||
name: 'New Playlist',
|
||||
@@ -40,30 +44,94 @@ export function ThemePlaylists() {
|
||||
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleOpen = (playlist: ThemePlaylist) => {
|
||||
setSelectedPlaylist(playlist);
|
||||
open();
|
||||
};
|
||||
const handleOpen = useCallback(
|
||||
(playlist: ThemePlaylist) => {
|
||||
setSelectedPlaylist(playlist);
|
||||
open();
|
||||
},
|
||||
[open],
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = useCallback(() => {
|
||||
setSelectedPlaylist(defaultPlaylist);
|
||||
close();
|
||||
};
|
||||
}, [close]);
|
||||
|
||||
const handleSavePlaylist = () => {
|
||||
const handleSavePlaylist = useCallback(() => {
|
||||
mutateAsync({
|
||||
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
|
||||
endpoint: themePlaylistsEndpoint,
|
||||
body: selectedPlaylist ?? {},
|
||||
});
|
||||
close();
|
||||
};
|
||||
}, [selectedPlaylist, mutateAsync, themePlaylistsEndpoint, close]);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedSearch === searchValue) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [debouncedSearch]);
|
||||
}, [debouncedSearch, searchValue]);
|
||||
|
||||
const allThemesIds = useMemo(
|
||||
() => allThemeData?.map((a: Theme) => a.id) ?? [],
|
||||
[allThemeData],
|
||||
);
|
||||
|
||||
const backgroundsThemes = useMemo(
|
||||
() =>
|
||||
allThemeData?.filter(
|
||||
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
|
||||
) ?? [],
|
||||
[allThemeData],
|
||||
);
|
||||
|
||||
const noBackgroundsThemes = useMemo(
|
||||
() =>
|
||||
allThemeData?.filter((a: any) =>
|
||||
a.backgroundImage.toLowerCase().includes('none'),
|
||||
) ?? [],
|
||||
[allThemeData],
|
||||
);
|
||||
|
||||
const timeOfDayPlaylist = useMemo(() => {
|
||||
const found = themePlaylists?.find((a: ThemePlaylist) => a.name === 'Time of Day');
|
||||
return {
|
||||
id: found?.id,
|
||||
name: 'Time of Day',
|
||||
default: 1,
|
||||
themeList: found?.themeList ?? '[]',
|
||||
};
|
||||
}, [themePlaylists]);
|
||||
|
||||
const allStack = useMemo(
|
||||
() => ({
|
||||
id: undefined,
|
||||
name: 'All',
|
||||
default: 1,
|
||||
themeList: `[${allThemesIds.join(',')}]`,
|
||||
}),
|
||||
[allThemesIds],
|
||||
);
|
||||
|
||||
const backgroundsStack = useMemo(
|
||||
() => ({
|
||||
id: undefined,
|
||||
name: 'Backgrounds',
|
||||
default: 1,
|
||||
themeList: `[${backgroundsThemes.map((a: Theme) => a.id).join(',')}]`,
|
||||
}),
|
||||
[backgroundsThemes],
|
||||
);
|
||||
|
||||
const noBackgroundsStack = useMemo(
|
||||
() => ({
|
||||
id: undefined,
|
||||
name: 'No Backgrounds',
|
||||
default: 1,
|
||||
themeList: `[${noBackgroundsThemes.map((a: Theme) => a.id).join(',')}]`,
|
||||
}),
|
||||
[noBackgroundsThemes],
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
@@ -108,7 +176,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
{allThemeData && (
|
||||
<>
|
||||
<Group justify='center' align='center' pos={'relative'}>
|
||||
<Stack w={'100%'} justify='center' align='center' pos={'relative'}>
|
||||
<Text
|
||||
p={0}
|
||||
className={textClasses.settingsSubTitle}
|
||||
@@ -116,195 +184,171 @@ export function ThemePlaylists() {
|
||||
>
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={{
|
||||
id:
|
||||
themePlaylists?.find(
|
||||
(a: ThemePlaylist) => a.name === 'Time of Day',
|
||||
)?.id ?? undefined,
|
||||
name: 'Time of Day',
|
||||
default: 1,
|
||||
themeList:
|
||||
themePlaylists?.find(
|
||||
(a: ThemePlaylist) => a.name === 'Time of Day',
|
||||
)?.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.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={cardPlaylist}
|
||||
handleOpen={handleOpen}
|
||||
allThemes={allThemeData}
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
) : (
|
||||
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<NoItemsFound />
|
||||
<Group>
|
||||
<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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={allStack}
|
||||
handleOpen={handleOpen}
|
||||
allThemes={allThemeData}
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
)}
|
||||
</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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={backgroundsStack}
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={noBackgroundsStack}
|
||||
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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={timeOfDayPlaylist}
|
||||
handleOpen={handleOpen}
|
||||
allThemes={allThemeData}
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
<Stack w={'100%'} justify='center' align='center' pos={'relative'}>
|
||||
<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.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 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>
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={cardPlaylist}
|
||||
handleOpen={handleOpen}
|
||||
allThemes={allThemeData}
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
) : (
|
||||
<Group w={'100%'} style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<NoItemsFound />
|
||||
</Group>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user