From 5982e1add5e2bccc063540eafbbf56ec23a8b0cd Mon Sep 17 00:00:00 2001 From: Sean Edmunds Date: Tue, 1 Jul 2025 15:54:59 -0500 Subject: [PATCH] Bug fixes --- package-lock.json | 70 +-- package.json | 10 +- src/hooks/useAPI.tsx | 126 +++-- .../components/Theming/BackgroundModal.tsx | 491 ++++++++---------- .../components/Theming/DeleteBackground.tsx | 95 ++++ .../components/Theming/EditBackground.tsx | 123 +++++ .../components/Theming/ThemeMiniCard.tsx | 22 +- .../components/Theming/ThemeModal.tsx | 87 +++- .../components/Theming/ThemePlaylistCard.tsx | 29 +- .../components/Theming/ThemePlaylistModal.tsx | 1 - .../Theming/ThemePreivewMiddleware.tsx | 4 +- .../settings/components/Theming/Themes.tsx | 2 +- 12 files changed, 628 insertions(+), 432 deletions(-) create mode 100644 src/pages/settings/components/Theming/DeleteBackground.tsx create mode 100644 src/pages/settings/components/Theming/EditBackground.tsx diff --git a/package-lock.json b/package-lock.json index 32c08b3..7fead2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,19 +13,19 @@ "@mantine/form": "^8.1.2", "@mantine/hooks": "^8.1.2", "@mantine/notifications": "^8.1.2", - "@tanstack/react-query": "^5.81.4", - "@tanstack/react-query-devtools": "^5.81.4", - "@tanstack/react-query-persist-client": "^5.81.4", + "@tanstack/react-query": "^5.81.5", + "@tanstack/react-query-devtools": "^5.81.5", + "@tanstack/react-query-persist-client": "^5.81.5", "idb-keyval": "^6.2.2", "jotai": "^2.12.5", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", - "react-router-dom": "^7.6.2", + "react-router-dom": "^7.6.3", "recharts": "^3.0.2" }, "devDependencies": { - "@types/node": "^24.0.4", + "@types/node": "^24.0.8", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", @@ -1630,9 +1630,9 @@ "license": "MIT" }, "node_modules/@tanstack/query-core": { - "version": "5.81.4", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.81.4.tgz", - "integrity": "sha512-z0yBgRFTPIEYcjFFiahOwtEU3kEKpF5Rwls7UjBCsRghmhwIXyxd5U2p0yEmUfcwm85W4XZkt4dc1OWc5my3Yw==", + "version": "5.81.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.81.5.tgz", + "integrity": "sha512-ZJOgCy/z2qpZXWaj/oxvodDx07XcQa9BF92c0oINjHkoqUPsmm3uG08HpTaviviZ/N9eP1f9CM7mKSEkIo7O1Q==", "license": "MIT", "funding": { "type": "github", @@ -1650,12 +1650,12 @@ } }, "node_modules/@tanstack/query-persist-client-core": { - "version": "5.81.4", - "resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.81.4.tgz", - "integrity": "sha512-8SlCCzIPBYCOfWRPUGkzJAdPMu+7JT42J51n6rWvaLVj20mzrVX2EKTtm7QoP19LdX+prS8ndCMClrILQi7nUA==", + "version": "5.81.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.81.5.tgz", + "integrity": "sha512-imiMWYXf8KuXavOgVRc5IDixqP0VKEN6IxZ/vyuX87Wk0eX7QmRtjLn1AD4Hz7GxYPM3RalRzP5Bshc30hgOcg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.81.4" + "@tanstack/query-core": "5.81.5" }, "funding": { "type": "github", @@ -1663,12 +1663,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.81.4", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.81.4.tgz", - "integrity": "sha512-bBrf5kTNr9t5BnskvyHQGwe2LRZsgnmx1sClyRQbAHxhSJfnT6z9h27IfGpY6JtfEH5y5R9jJegTG/93zyox/A==", + "version": "5.81.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.81.5.tgz", + "integrity": "sha512-lOf2KqRRiYWpQT86eeeftAGnjuTR35myTP8MXyvHa81VlomoAWNEd8x5vkcAfQefu0qtYCvyqLropFZqgI2EQw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.81.4" + "@tanstack/query-core": "5.81.5" }, "funding": { "type": "github", @@ -1679,9 +1679,9 @@ } }, "node_modules/@tanstack/react-query-devtools": { - "version": "5.81.4", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.81.4.tgz", - "integrity": "sha512-zFg7rzOmM9Q+Ym/TujWR4HD+jKwQd+N8WuWZRmHU8sjI2flyxWTWIEQvl9BFt+HiNA6leS3ODZ9SHYmEb3zduw==", + "version": "5.81.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.81.5.tgz", + "integrity": "sha512-lCGMu4RX0uGnlrlLeSckBfnW/UV+KMlTBVqa97cwK7Z2ED5JKnZRSjNXwoma6sQBTJrcULvzgx2K6jEPvNUpDw==", "license": "MIT", "dependencies": { "@tanstack/query-devtools": "5.81.2" @@ -1691,24 +1691,24 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "^5.81.4", + "@tanstack/react-query": "^5.81.5", "react": "^18 || ^19" } }, "node_modules/@tanstack/react-query-persist-client": { - "version": "5.81.4", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.81.4.tgz", - "integrity": "sha512-8SlkNDZmWUZLKYYm6DvYybJYrZngnERx/7W1KLUQ3IPaANM9tfmtQjBQBhr1dagyebNK1h/c69IY24dfUaNVIA==", + "version": "5.81.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.81.5.tgz", + "integrity": "sha512-ZV69n636C22RkbFK0rzel80oWjyWbMKiUwUE2MKhYWTSr42+JmqoehDwJ5sWcO8CxHNm4vWF8pyG+goRzluMYw==", "license": "MIT", "dependencies": { - "@tanstack/query-persist-client-core": "5.81.4" + "@tanstack/query-persist-client-core": "5.81.5" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "^5.81.4", + "@tanstack/react-query": "^5.81.5", "react": "^18 || ^19" } }, @@ -1847,9 +1847,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.4.tgz", - "integrity": "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==", + "version": "24.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz", + "integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==", "dev": true, "license": "MIT", "dependencies": { @@ -3694,9 +3694,9 @@ } }, "node_modules/react-router": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.2.tgz", - "integrity": "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz", + "integrity": "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -3716,12 +3716,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.2.tgz", - "integrity": "sha512-Q8zb6VlTbdYKK5JJBLQEN06oTUa/RAbG/oQS1auK1I0TbJOXktqm+QENEVJU6QvWynlXPRBXI3fiOQcSEA78rA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz", + "integrity": "sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==", "license": "MIT", "dependencies": { - "react-router": "7.6.2" + "react-router": "7.6.3" }, "engines": { "node": ">=20.0.0" diff --git a/package.json b/package.json index 2eb9106..384f59b 100644 --- a/package.json +++ b/package.json @@ -15,19 +15,19 @@ "@mantine/form": "^8.1.2", "@mantine/hooks": "^8.1.2", "@mantine/notifications": "^8.1.2", - "@tanstack/react-query": "^5.81.4", - "@tanstack/react-query-devtools": "^5.81.4", - "@tanstack/react-query-persist-client": "^5.81.4", + "@tanstack/react-query": "^5.81.5", + "@tanstack/react-query-devtools": "^5.81.5", + "@tanstack/react-query-persist-client": "^5.81.5", "idb-keyval": "^6.2.2", "jotai": "^2.12.5", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", - "react-router-dom": "^7.6.2", + "react-router-dom": "^7.6.3", "recharts": "^3.0.2" }, "devDependencies": { - "@types/node": "^24.0.4", + "@types/node": "^24.0.8", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", diff --git a/src/hooks/useAPI.tsx b/src/hooks/useAPI.tsx index e7ea664..0a7a0e6 100644 --- a/src/hooks/useAPI.tsx +++ b/src/hooks/useAPI.tsx @@ -60,7 +60,7 @@ export function useMutationAPI() { ), autoClose: false, - withCloseButton: false, + withCloseButton: true, icon: , }); const options: { method: string; headers: HeadersInit | undefined; body: string } = { @@ -73,44 +73,40 @@ export function useMutationAPI() { const responseData = await response.json(); if (typeof responseData === 'object') { if (responseData.error.code === 'ER_DUP_ENTRY') { - setTimeout(() => { - notifications.update({ - id: id, - loading: false, - title: ( - - {method} Failure! - - ), - message: ( - - Entry with that name already exists. - - ), - autoClose: 2500, - icon: , - }); - }, 2500); + notifications.update({ + id: id, + loading: false, + title: ( + + {method} Failure! + + ), + message: ( + + Entry with that name already exists. + + ), + autoClose: 2500, + icon: , + }); } } return await responseData; } - setTimeout(() => { - notifications.update({ - id: id, - loading: false, - title: ( - - {method} Success! - - ), - message: ( - Called endpoint {endpoint} - ), - autoClose: 2500, - icon: , - }); - }, 2500); + notifications.update({ + id: id, + loading: false, + title: ( + + {method} Success! + + ), + message: ( + Called endpoint {endpoint} + ), + autoClose: 2500, + icon: , + }); return await response.json(); }; @@ -249,42 +245,38 @@ export function useFileUploadAPI() { const responseData = await response.json(); if (typeof responseData === 'object') { if (responseData.error.code === 'ER_DUP_ENTRY') { - setTimeout(() => { - notifications.update({ - id: id, - loading: false, - title: ( - - {method} Failure! - - ), - message: ( - - Entry with that name already exists. - - ), - autoClose: 2500, - icon: , - }); - }, 2500); + notifications.update({ + id: id, + loading: false, + title: ( + + {method} Failure! + + ), + message: ( + + Entry with that name already exists. + + ), + autoClose: 2500, + icon: , + }); } } return await responseData; } - setTimeout(() => { - notifications.update({ - id: id, - loading: false, - title: ( - - {method} Success! - - ), - message: {response.statusText}, - autoClose: 2500, - icon: , - }); - }, 2500); + notifications.update({ + id: id, + loading: false, + title: ( + + {method} Success! + + ), + message: {response.statusText}, + autoClose: 2500, + icon: , + }); return await response.json(); }; diff --git a/src/pages/settings/components/Theming/BackgroundModal.tsx b/src/pages/settings/components/Theming/BackgroundModal.tsx index 929cd1a..c037732 100644 --- a/src/pages/settings/components/Theming/BackgroundModal.tsx +++ b/src/pages/settings/components/Theming/BackgroundModal.tsx @@ -6,18 +6,26 @@ import { Group, Image, Modal, - Overlay, ScrollArea, SimpleGrid, Stack, Text, TextInput, } from '@mantine/core'; +import { useDebouncedValue } from '@mantine/hooks'; import { useCommonHooks } from 'hooks'; -import { useFileUploadAPI, useMutationAPI } from 'hooks/useAPI'; -import { useState } from 'react'; -import { FiEdit, FiTrash } from 'react-icons/fi'; -import { modalClasses, scrollAreaClasses, textInputClasses } from 'styles'; +import { useFileUploadAPI } from 'hooks/useAPI'; +import { useEffect, useState } from 'react'; +import { FiEdit, FiLoader, FiPlus, FiTrash, FiX } from 'react-icons/fi'; +import { + actionIconClasses, + buttonClasses, + modalClasses, + scrollAreaClasses, + textInputClasses, +} from 'styles'; +import DeleteBackground from './DeleteBackground'; +import EditBackground from './EditBackground'; function BackgroundModal({ backgroundModalOpened, @@ -33,19 +41,26 @@ function BackgroundModal({ const [background, setBackground] = useState(null); const [fileName, setFileName] = useState(''); const [newFileName, setNewFileName] = useState(''); - const [rename, setRename] = useState(''); const [err, setErr] = useState(false); const [confirm, setConfirm] = useState(false); const [editing, setEditing] = useState(false); + const [adding, setAdding] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const [debouncedSearch] = useDebouncedValue(searchValue, 500); const { isMobile } = useCommonHooks(); const { mutateAsync: uploadAsync } = useFileUploadAPI(); - const { mutateAsync } = useMutationAPI(); const fd = new FormData(); const handleFileUpload = (e: any) => { setErr(false); setBackground(e); setFileName(e?.name); }; + useEffect(() => { + if (debouncedSearch === searchValue) { + setIsLoading(false); + } + }, [debouncedSearch]); return ( - {confirm && ( - - - Are you sure you want to delete {fileName}? - - {inUseBackgrounds.includes(fileName) && ( - - This background is currently in use! This will result in a blank - background on the theme. - - )} - - THIS ACTION CANNOT BE UNDONE!!! - - + { + setIsLoading(true); + setSearchValue(event.target.value); }} + style={{ + zIndex: 100, + paddingTop: 0, + }} + rightSection={ + isLoading ? ( + + + + ) : ( + searchValue && ( + setSearchValue('')} + > + + + ) + ) + } /> - - - - - + + + )} + {confirm && ( + )} {editing && ( - - - Editing {fileName} - - {inUseBackgrounds.includes(fileName) && ( - - Updating the name will update the associated themes. - - )} - - setRename(e.target.value)} - /> - - - - - - - + )} - {!editing && !confirm && ( + {!editing && !confirm && !adding && ( {backgroundList .filter((background) => background !== 'None') + .filter( + (background) => + !debouncedSearch || + background + .toLocaleLowerCase() + .includes(debouncedSearch.toLocaleLowerCase()), + ) .map((background) => ( )} - - {confirm || - (editing && ( - - ))} - - - - setNewFileName(e.target.value)} - pr={'xl'} - pl={0} - styles={{ - input: { - color: 'var(--mantine-color-primary-0)', - borderColor: 'var(--mantine-color-primary-0)', - }, - }} - /> - - - {background ? ( - - ) : ( - - )} - - - - + + + + setNewFileName(e.target.value)} + pr={'xl'} + pl={0} + styles={{ + input: { + color: 'var(--mantine-color-primary-0)', + borderColor: 'var(--mantine-color-primary-0)', + }, + }} + /> + + + {background ? ( + + ) : ( + + )} + + + + + + + + )} diff --git a/src/pages/settings/components/Theming/DeleteBackground.tsx b/src/pages/settings/components/Theming/DeleteBackground.tsx new file mode 100644 index 0000000..096a0f0 --- /dev/null +++ b/src/pages/settings/components/Theming/DeleteBackground.tsx @@ -0,0 +1,95 @@ +import { Button, Card, Group, Stack, Text } from '@mantine/core'; +import { useMutationAPI } from 'hooks'; + +function DeleteBackground({ + fileName, + setFileName, + inUseBackgrounds, + setBackground, + setNewFileName, + setConfirm, +}: { + fileName: string; + setFileName: (searchValue: string) => void; + inUseBackgrounds: string[]; + setBackground: React.Dispatch>; + setNewFileName: (searchValue: string) => void; + setConfirm: (value: boolean) => void; +}) { + const { mutateAsync } = useMutationAPI(); + return ( + + + Are you sure you want to delete {fileName}? + + {inUseBackgrounds.includes(fileName) && ( + + This background is currently in use! This will result in a blank background on + the theme. + + )} + + THIS ACTION CANNOT BE UNDONE!!! + + + + + + + + ); +} + +export default DeleteBackground; diff --git a/src/pages/settings/components/Theming/EditBackground.tsx b/src/pages/settings/components/Theming/EditBackground.tsx new file mode 100644 index 0000000..9e56a40 --- /dev/null +++ b/src/pages/settings/components/Theming/EditBackground.tsx @@ -0,0 +1,123 @@ +import { Button, Card, Group, Stack, Text, TextInput } from '@mantine/core'; +import { useMutationAPI } from 'hooks'; +import { useState } from 'react'; +import { textInputClasses } from 'styles'; + +function EditBackground({ + fileName, + setFileName, + inUseBackgrounds, + setBackground, + setNewFileName, + setEditing, +}: { + fileName: string; + setFileName: (searchValue: string) => void; + inUseBackgrounds: string[]; + setBackground: React.Dispatch>; + setNewFileName: (searchValue: string) => void; + setEditing: React.Dispatch>; +}) { + const [rename, setRename] = useState(''); + const { mutateAsync } = useMutationAPI(); + return ( + + + Editing {fileName} + + {inUseBackgrounds.includes(fileName) && ( + + Updating the name will update the associated themes. + + )} + + setRename(e.target.value)} + /> + + + + + + + + ); +} + +export default EditBackground; diff --git a/src/pages/settings/components/Theming/ThemeMiniCard.tsx b/src/pages/settings/components/Theming/ThemeMiniCard.tsx index 2e125eb..46819f4 100644 --- a/src/pages/settings/components/Theming/ThemeMiniCard.tsx +++ b/src/pages/settings/components/Theming/ThemeMiniCard.tsx @@ -1,9 +1,7 @@ import { Group, Text } from '@mantine/core'; import type { Theme } from 'types'; -function ThemeMiniCard({ allThemes, item }: { allThemes: Theme[]; item: number }) { - const theme = allThemes?.find((theme: Theme) => theme.id === item); - +function ThemeMiniCard({ miniTheme }: { miniTheme: Theme | undefined }) { return ( - {allThemes.find((theme: Theme) => theme.id === item)?.name} + {miniTheme?.name} ); diff --git a/src/pages/settings/components/Theming/ThemeModal.tsx b/src/pages/settings/components/Theming/ThemeModal.tsx index 84909b7..70ca343 100644 --- a/src/pages/settings/components/Theming/ThemeModal.tsx +++ b/src/pages/settings/components/Theming/ThemeModal.tsx @@ -47,6 +47,7 @@ function ThemeModal({ const [hovered, setHovered] = useState(-1); const [colorId, setColorId] = useState(''); const [colorValue, setColorValue] = useState('#000000'); + const [defaultTheme, setDefaultTheme] = useState({ ...selectedTheme }); const [designerTheme, setDesignerTheme] = useState({ ...selectedTheme, }); @@ -118,9 +119,19 @@ function ThemeModal({ ); }); + const handleSwatchClick = () => { + closeBackgroundPicker(); + openPicker(); + setDesignerTheme(selectedTheme); + }; + useEffect(() => { setDesignerTheme(selectedTheme); + if (defaultTheme.name !== selectedTheme.name) { + setDefaultTheme(selectedTheme); + } }, [selectedTheme]); + return ( { - closeBackgroundPicker(); setColorId('primaryColor'); - openPicker(); setColorValue(selectedTheme.primaryColor); + handleSwatchClick(); }} color={selectedTheme.primaryColor} bd={`1px solid ${selectedTheme.primaryColor}`} @@ -188,10 +198,9 @@ function ThemeModal({ > { - closeBackgroundPicker(); setColorId('accentColor'); - openPicker(); setColorValue(selectedTheme.accentColor); + handleSwatchClick(); }} color={selectedTheme.accentColor} bd={`1px solid ${selectedTheme.primaryColor}`} @@ -207,10 +216,9 @@ function ThemeModal({ > { - closeBackgroundPicker(); setColorId('backgroundColor'); - openPicker(); setColorValue(selectedTheme.backgroundColor); + handleSwatchClick(); }} color={selectedTheme.backgroundColor} bd={`1px solid ${selectedTheme.primaryColor}`} @@ -226,10 +234,9 @@ function ThemeModal({ > { - closeBackgroundPicker(); setColorId('borderColor'); - openPicker(); setColorValue(selectedTheme.borderColor); + handleSwatchClick(); }} color={selectedTheme.borderColor} bd={`1px solid ${selectedTheme.primaryColor}`} @@ -245,10 +252,9 @@ function ThemeModal({ > { - closeBackgroundPicker(); setColorId('searchColor'); - openPicker(); setColorValue(selectedTheme.searchColor); + handleSwatchClick(); }} color={selectedTheme.searchColor} bd={`1px solid ${selectedTheme.primaryColor}`} @@ -266,6 +272,7 @@ function ThemeModal({ onClick={() => { closePicker(); openBackgroundPicker(); + setDesignerTheme(selectedTheme); }} pos={'absolute'} right={0} @@ -289,19 +296,35 @@ function ThemeModal({ Change {colorId} - { - setColorValue(e); - setDesignerTheme({ - ...designerTheme, - [colorId]: e, - }); - }} - format={colorId === 'backgroundColor' ? 'hex' : 'hexa'} - w={'90%'} - mb={colorId === 'backgroundColor' ? '1.5rem' : undefined} - /> + {colorId === 'backgroundColor' && ( + { + setColorValue(e); + setDesignerTheme({ + ...designerTheme, + backgroundColor: e, + }); + }} + format={'hex'} + w={'90%'} + mb={'1.5rem'} + /> + )} + {colorId !== 'backgroundColor' && ( + { + setColorValue(e); + setDesignerTheme({ + ...designerTheme, + [colorId]: e, + }); + }} + format={'hexa'} + w={'90%'} + /> + )} )} - {!pickerOpen && !backgroundPicker && } + {!pickerOpen && !backgroundPicker && ( + + + + )} -