import { useEffect, useRef, useState } from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; import Header from '../components/store/header'; import { Autoplay } from 'swiper/modules'; import { motion, AnimatePresence } from 'framer-motion'; import 'swiper/css'; import 'swiper/css/pagination'; import 'swiper/css/scrollbar'; import 'swiper/css/autoplay'; import SpinnerIcon from '../components/LoadingSpinner'; import localforage from 'localforage'; import { StoreDownloadTheme } from '../../seqta/ui/themes/downloadTheme'; const textVariants = { hidden: { opacity: 0, y: 60 }, visible: { opacity: 1, y: 0, transition: { type: 'spring', bounce: 0, stiffness: 80, damping: 12 } }, }; const containerVariants = { hidden: { y: '100vh', }, visible: { y: 0, transition: { staggerChildren: 0.05, type: 'spring', bounce: 0, stiffness: 400, damping: 50 }, }, }; export type Theme = { name: string; description: string; coverImage: string; marqueeImage: string; id: string; } type ThemesResponse = { themes: Theme[]; } export const DeleteDownloadedTheme = async (themeID: string) => { console.debug('DeleteDownloaded Theme:', themeID) await localforage.removeItem(themeID); const availableThemesList = await localforage.getItem('availableThemes') as string[]; const updatedThemesList = availableThemesList.filter(theme => theme !== themeID); await localforage.setItem('availableThemes', updatedThemesList); } const Store = () => { const [searchTerm, setSearchTerm] = useState(''); const swiperCover = useRef(null); const [gridThemes, setGridThemes] = useState([]); const [filteredThemes, setFilteredThemes] = useState([]); const [coverThemes, setCoverThemes] = useState([]); const [installingThemes, setInstallingThemes] = useState([]); const [currentThemes, setCurrentThemes] = useState([]); const [displayTheme, setDisplayTheme] = useState(null); const [loading, setLoading] = useState(true); const fetchThemes = async () => { try { const response = await fetch(`https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Themes/main/store/themes.json?nocache=${(new Date()).getTime()}`, { cache: 'no-store' }); const data: ThemesResponse = await response.json(); setGridThemes(data.themes); // Select up to 3 random themes to display in coverThemes const shuffled = [...data.themes].sort(() => 0.5 - Math.random()); setCoverThemes(shuffled.slice(0, 3)); setLoading(false); } catch (error) { console.error('Failed to fetch themes', error); // Retry after 5 seconds setTimeout(fetchThemes, 5000); } }; useEffect(() => { (async () => { fetchThemes(); const availableThemes = await localforage.getItem('availableThemes') as string[]; setCurrentThemes(availableThemes) })(); }, []); useEffect(() => { setFilteredThemes(gridThemes.filter(theme => theme.name.toLowerCase().includes(searchTerm.toLowerCase()) || theme.description.toLowerCase().includes(searchTerm.toLowerCase()) )); }, [searchTerm, gridThemes]); const downloadTheme = (id: string) => { const themeContent = gridThemes.find(theme => theme.id === id); if (!themeContent) { alert('There was an error, The theme was not found!') return }; setInstallingThemes([...installingThemes, id]); StoreDownloadTheme({ themeContent }).then(() => { setInstallingThemes(installingThemes.filter(theme => theme !== id)); setCurrentThemes([...currentThemes, id]); }); }; const removeTheme = async (id: string) => { const themeContent = gridThemes.find(theme => theme.id === id); if (!themeContent) { alert('There was an error, The theme was not found!') return }; setInstallingThemes([...installingThemes, id]); DeleteDownloadedTheme(id).then(() => { setInstallingThemes(installingThemes.filter(theme => theme !== id)); setCurrentThemes(currentThemes.filter(theme => theme !== id)); }); } return (
{/* loader */}
{ coverThemes.map((theme, index) => ( setDisplayTheme(theme)} key={index}> Theme Preview

{theme.name}

{theme.description}

{/* shadow from the bottom of the image */}
)) } {displayTheme && ( setDisplayTheme(null)} > e.stopPropagation()} className="w-full max-w-xl h-[95%] p-8 pt-5 bg-white rounded-t-2xl dark:bg-zinc-800 overflow-scroll" exit={{ y: "100vh" }} transition={{ type: 'spring', stiffness: 300, damping: 30 }} variants={containerVariants} initial="hidden" animate="visible" > setDisplayTheme(null)} variants={textVariants} > {'\ued8a'} {displayTheme.name} {displayTheme.description} downloadTheme(displayTheme.id)} className="flex px-4 py-2 mt-4 ml-auto rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200 focus:outline-none focus:ring-2 focus:ring-zinc-800 focus:ring-offset-2" variants={textVariants} > { installingThemes.includes(displayTheme.id) ? <> Installing... : <> Install } More Themes
{gridThemes.filter(theme => theme.id !== displayTheme.id).map((theme, index) => ( { setDisplayTheme(null); setDisplayTheme(theme); }} className='w-full cursor-pointer' variants={textVariants}>
{theme.name}

{theme.description}

Theme Preview
))}
)} {/* pagination */}
{filteredThemes.map((theme, index) => (
{theme.name}

{theme.description}

Theme Preview
{ currentThemes.includes(theme.id) ? : }
))}
{filteredThemes.length == 0 && !loading && (

That doesnt exist! 😭😭😭

Sorry, we couldn’t find the theme you’re looking for. Maybe... you could create it?

)}
); }; export default Store;