Files
TriggerFrontend/src/pages/settings/components/Theming/Themes.tsx
2025-04-03 14:54:24 -05:00

172 lines
5.2 KiB
TypeScript

import { Box, Group, Overlay, Stack, Switch, Text } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { FaDice } from 'react-icons/fa';
import { FiChevronRight } from 'react-icons/fi';
import type { Theme } from 'types';
import SettingsHeader from '../Shared/SettingsHeader';
import ThemeCard from './ThemeCard';
import ThemeModal from './ThemeModal';
function ThemesSettings() {
const themesEndpoint = 'api/themes';
const backgroundsEndpoint = 'api/backgrounds';
const { data: allThemes } = useQueryAPI({
endpoint: 'api/themes',
});
const { data: backgroundData, isFetched: isBackgroundFetched } = useQueryAPI({
endpoint: backgroundsEndpoint,
});
const { data: themePlaylists } = useQueryAPI({
endpoint: 'api/themePlaylists',
});
const { mutateAsync } = useMutationAPI();
const [opened, { open, close }] = useDisclosure(false);
const [colorId, setColorId] = useState<string>('');
const [colorValue, setColorValue] = useState('#000000');
const [backgroundList, setBackgroundList] = useState<string[]>(['None']);
const [selectedTheme, setSelectedTheme] = useState<any>({});
const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false);
const [searchValue, setSearchValue] = useState('');
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const { currentTheme, setCurrentTheme, theme, isMobile, setCurrentThemePlaylist } =
useCommonHooks();
const handleOpen = (theme: Theme) => {
setSelectedTheme(theme);
open();
};
const handleClose = () => {
close();
closePicker();
setSelectedTheme({});
};
const handleSaveTheme = () => {
mutateAsync({
method: selectedTheme.id === undefined ? 'POST' : 'PATCH',
endpoint: themesEndpoint,
body: selectedTheme,
});
close();
};
useEffect(() => {
if (backgroundData && isBackgroundFetched) {
setBackgroundList(['None', ...backgroundData]);
}
}, [backgroundData, isBackgroundFetched]);
return (
<Stack>
<ThemeModal
opened={opened}
handleClose={handleClose}
selectedTheme={selectedTheme}
setSelectedTheme={setSelectedTheme}
theme={theme}
setColorId={setColorId}
colorId={colorId}
setColorValue={setColorValue}
openPicker={openPicker}
backgroundList={backgroundList}
pickerOpen={pickerOpen}
colorValue={colorValue}
closePicker={closePicker}
handleSaveTheme={handleSaveTheme}
/>
<Stack h={'100%'} w={'100%'}>
<SettingsHeader
setDragging={undefined}
openModal={open}
title={'Themes'}
tooltip={{
title: 'Themes can be created here and applied to the homepage.',
highlightList: ['Clients', 'homepage'],
description:
'Themes are combinations of 5 colors and a background image, and can be added to a playlist. If a theme has been disabled, it will only be disabled with random/shuffle is enabled. If a theme is part of a playlist, it will appear while the playlist is active, regardless of random/shuffle.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Switch
label={
isMobile ? (
<FaDice size={20} color={theme.primaryColor} />
) : (
<Text c={theme.primaryColor}>Random Theme</Text>
)
}
labelPosition='left'
pos={'absolute'}
right={isMobile ? '2rem' : '1rem'}
top={isMobile ? '9.5rem' : '2rem'}
checked={currentTheme === 'random'}
onChange={(e) => {
setCurrentTheme(e.target.checked ? 'random' : theme.name);
setCurrentThemePlaylist('All');
}}
color={theme.primaryColor}
style={{
zIndex: 1,
}}
/>
<Group justify='center' align='center' pos={'relative'} mt={'10rem'}>
<Overlay
display={currentTheme === 'random' ? 'flex' : 'none'}
blur={5}
style={{
borderRadius: '10px',
marginTop: '-15px',
marginBottom: '-10px',
boxShadow: '0px 0px 10px 3px rgba(0, 0, 0, .8)',
}}
pos={'absolute'}
/>
{allThemes
?.filter((a: Theme) =>
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
)
.map((cardTheme: Theme) => (
<Group key={cardTheme.name} pos={'relative'}>
{cardTheme.name === currentTheme && (
<FiChevronRight
style={{
position: 'absolute',
left: -14,
zIndex: 1,
fontSize: '2em',
fontWeight: 'bold',
borderRadius: '50%',
backgroundColor: cardTheme.borderColor,
color:
cardTheme.borderColor === cardTheme.primaryColor
? cardTheme.accentColor
: cardTheme.primaryColor,
}}
/>
)}
<Box
style={{
zIndex:
currentTheme === 'random' && cardTheme.name === theme.name
? 200
: 0,
}}
>
<ThemeCard
allThemes={allThemes}
cardTheme={cardTheme}
handleOpen={handleOpen}
themePlaylists={themePlaylists}
/>
</Box>
</Group>
))}
</Group>
</Stack>
</Stack>
);
}
export default ThemesSettings;