fix/theme-playlists-performance #1

Merged
smedmunds merged 2 commits from fix/theme-playlists-performance into master 2026-10-12 11:21:30 -05:00
Showing only changes of commit 68fa451d19 - Show all commits

View File

@@ -2,7 +2,7 @@ import { Box, Group, LoadingOverlay, SimpleGrid, Stack, Text } from '@mantine/co
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { NoItemsFound, SettingsHeader } from 'components'; import { NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState, useMemo, useCallback } from 'react';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
@@ -21,13 +21,15 @@ export function ThemePlaylists() {
}); });
const [searchValue, setSearchValue] = useState<string | number>(''); const [searchValue, setSearchValue] = useState<string | number>('');
const [debouncedSearch] = useDebouncedValue(searchValue, 500); const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const themePlaylists: ThemePlaylist[] =
themePlaylistData?.filter( const themePlaylists: ThemePlaylist[] = useMemo(() => {
return themePlaylistData?.filter(
(a: ThemePlaylist) => (a: ThemePlaylist) =>
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) || a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
a.name.includes('Time of Day'), a.name.includes('Time of Day'),
) || []; ) || [];
const defaultPlaylist = { }, [themePlaylistData, debouncedSearch]);
const defaultPlaylist: ThemePlaylist = {
id: undefined, id: undefined,
default: 0, default: 0,
name: 'New Playlist', name: 'New Playlist',
@@ -40,30 +42,73 @@ export function ThemePlaylists() {
const { currentThemePlaylist, theme, isMobile } = useCommonHooks(); const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const handleOpen = (playlist: ThemePlaylist) => { const handleOpen = useCallback((playlist: ThemePlaylist) => {
setSelectedPlaylist(playlist); setSelectedPlaylist(playlist);
open(); open();
}; }, [open]);
const handleClose = () => { const handleClose = useCallback(() => {
setSelectedPlaylist(defaultPlaylist); setSelectedPlaylist(defaultPlaylist);
close(); close();
}; }, [close]);
const handleSavePlaylist = () => { const handleSavePlaylist = useCallback(() => {
mutateAsync({ mutateAsync({
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH', method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
endpoint: themePlaylistsEndpoint, endpoint: themePlaylistsEndpoint,
body: selectedPlaylist ?? {}, body: selectedPlaylist ?? {},
}); });
close(); close();
}; }, [selectedPlaylist, mutateAsync, themePlaylistsEndpoint, close]);
useEffect(() => { useEffect(() => {
if (debouncedSearch === searchValue) { if (debouncedSearch === searchValue) {
setIsLoading(false); 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 ( return (
<Stack> <Stack>
@@ -135,117 +180,91 @@ export function ThemePlaylists() {
)} )}
<Box> <Box>
<ThemePlaylistStack <ThemePlaylistStack
cardPlaylist={{ cardPlaylist={allStack}
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} handleOpen={handleOpen}
allThemes={allThemeData} allThemes={allThemeData}
/> />
</Box> </Box>
</Group> </Group>
</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 justify='center' align='center' pos={'relative'}> <Group justify='center' align='center' pos={'relative'}>
<Text <Text
p={0} p={0}
@@ -282,9 +301,9 @@ export function ThemePlaylists() {
borderRadius: '50%', borderRadius: '50%',
backgroundColor: 'var(--mantine-color-border-0)', backgroundColor: 'var(--mantine-color-border-0)',
color: color:
theme.borderColor === theme.primaryColor theme.borderColor === theme.primaryColor
? 'var(--mantine-color-accent-0)' ? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-primary-0)', : 'var(--mantine-color-primary-0)',
}} }}
/> />
)} )}
@@ -293,7 +312,7 @@ export function ThemePlaylists() {
cardPlaylist={cardPlaylist} cardPlaylist={cardPlaylist}
handleOpen={handleOpen} handleOpen={handleOpen}
allThemes={allThemeData} allThemes={allThemeData}
/> />
</Box> </Box>
</Group> </Group>
); );