diff --git a/src/App.tsx b/src/App.tsx index 6585f87..ca6d386 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,4 @@ import { - Box, DEFAULT_THEME, MantineProvider, createTheme, @@ -16,7 +15,7 @@ import { createBrowserRouter, createRoutesFromElements, } from 'react-router-dom'; -import { ThemeProvider } from './components'; +import { MainLayout, ThemeProvider } from './components'; import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT import '@mantine/dates/styles.css'; //if using mantine date picker features import 'mantine-react-table/styles.css'; //import MRT styles @@ -57,15 +56,20 @@ function App() { const router = createBrowserRouter( createRoutesFromElements( - } /> - } - /> - } - /> + }> + } + /> + } + /> + } + /> + , ), ); @@ -82,22 +86,8 @@ function App() { - <> - - - - + + diff --git a/src/Root.tsx b/src/Root.tsx new file mode 100644 index 0000000..bce7d5a --- /dev/null +++ b/src/Root.tsx @@ -0,0 +1,50 @@ +import { QueryClient } from '@tanstack/react-query'; +import { + PersistQueryClientProvider, + type PersistedClient, + type Persister, +} from '@tanstack/react-query-persist-client'; +import { del, get, set } from 'idb-keyval'; +import React, { type ComponentType } from 'react'; +import { createRoot } from 'react-dom/client'; + +const container = document.getElementById('root') as HTMLElement; +const root = createRoot(container); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + staleTime: 1000 * 60 * 1, // 1 min + }, + }, +}); + +export function createIDBPersister(idbValidKey: IDBValidKey = 'reactQuery') { + return { + persistClient: async (client: PersistedClient) => { + set(idbValidKey, client); + }, + restoreClient: async () => { + return await get(idbValidKey); + }, + removeClient: async () => { + await del(idbValidKey); + }, + } as Persister; +} + +function render(App: ComponentType) { + root.render( + + + + + , + ); +} + +export default render; diff --git a/src/components/MainLayout.tsx b/src/components/MainLayout.tsx new file mode 100644 index 0000000..13623ab --- /dev/null +++ b/src/components/MainLayout.tsx @@ -0,0 +1,21 @@ +import { Box } from '@mantine/core'; +import { Outlet } from 'react-router-dom'; + +export function MainLayout({ theme }: any) { + return ( + + + + ); +} diff --git a/src/components/WeatherModal/WeatherModal.tsx b/src/components/WeatherModal/WeatherModal.tsx index 6a22143..65cf2ce 100644 --- a/src/components/WeatherModal/WeatherModal.tsx +++ b/src/components/WeatherModal/WeatherModal.tsx @@ -14,7 +14,6 @@ export function WeatherModal({ hourly, closeWeatherModal, weatherModalOpened, - setValue, setProviderLabel, setProviderURL, isMobile, @@ -50,7 +49,6 @@ export function WeatherModal({ opened={weatherModalOpened} onClose={() => { closeWeatherModal(); - setValue(''); setProviderLabel('Google Search'); setProviderURL('https://www.google.com/search?q='); }} @@ -119,7 +117,9 @@ export function WeatherModal({ > {hourlyForecast.length > 0 && hourlyForecast.map((h: any) => { - return ; + return ( + + ); })} diff --git a/src/components/index.ts b/src/components/index.ts index e2d61ee..5e5ab22 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,6 +2,7 @@ export { HelpModal } from './HelpModal/HelpModal'; export { WeatherModal } from './WeatherModal/WeatherModal'; export { DataTable } from './DataTable/DataTable'; export { ThemeProvider } from './ThemeProvider'; +export { MainLayout } from './MainLayout'; export { ApplicationColumns } from './DataTable/columns/ApplicationColumns'; export { BookmarkColumns } from './DataTable/columns/BookmarkColumns'; export { CategoryColumns } from './DataTable/columns/CategoryColumns'; diff --git a/src/main.tsx b/src/main.tsx index ed7d40d..ff43d4f 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,21 +1,10 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App.tsx'; +import App from './App'; +import render from './Root'; -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours - staleTime: 1000 * 60 * 1, // 1 min - }, - }, -}); - -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - - - , -); +Promise.all([render, App]) + .then(([render, App]) => { + render(App); + }) + .catch((error) => { + console.error('Error loading the app:', error); + }); diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx index e3dc00d..e1c2da4 100644 --- a/src/pages/homepage/Homepage.tsx +++ b/src/pages/homepage/Homepage.tsx @@ -55,7 +55,7 @@ export function Homepage({ theme }: any) { if (data) { setDisplayedGreeting(greet(data)); } - }, [isFetching]); + }, [data, isFetching]); useEffect(() => { if (weather && basicWeather) { diff --git a/src/pages/homepage/components/Applications/Applications.tsx b/src/pages/homepage/components/Applications/Applications.tsx index 5dd6398..4eb3a65 100644 --- a/src/pages/homepage/components/Applications/Applications.tsx +++ b/src/pages/homepage/components/Applications/Applications.tsx @@ -8,10 +8,10 @@ function Applications({ contentType, isMobile }: any) { const { data, isFetching } = useQueryAPI({ endpoint: 'api/apps' }); const [applications, setApplications] = useState([]); useEffect(() => { - if (!isFetching) { + if (data && !isFetching) { setApplications(data); } - }, [isFetching]); + }, [data, isFetching]); return ( <> {applications.length > 0 && contentType.length > 0 && ( diff --git a/src/pages/homepage/components/Header/Header.tsx b/src/pages/homepage/components/Header/Header.tsx index 8fd99b9..b41cb3a 100644 --- a/src/pages/homepage/components/Header/Header.tsx +++ b/src/pages/homepage/components/Header/Header.tsx @@ -16,7 +16,6 @@ function Header({ weatherModalOpened, closeWeatherModal, openWeatherModal, - setValue, setProviderLabel, setProviderURL, }: any) { @@ -155,7 +154,6 @@ function Header({ hourly={hourly} closeWeatherModal={closeWeatherModal} weatherModalOpened={weatherModalOpened} - setValue={setValue} setProviderLabel={setProviderLabel} setProviderURL={setProviderURL} isMobile={isMobile} diff --git a/src/pages/homepage/components/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton.tsx index 770f997..eaba864 100644 --- a/src/pages/homepage/components/SettingsButton.tsx +++ b/src/pages/homepage/components/SettingsButton.tsx @@ -2,6 +2,7 @@ import { Button, Menu, MultiSelect, NavLink } from '@mantine/core'; import { useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; import { FiMenu, FiMonitor } from 'react-icons/fi'; +import { useNavigate } from 'react-router-dom'; import { buttonClasses, menuClasses, navLinkClasses, selectClasses } from 'styles'; function SettingsButton({ @@ -12,7 +13,7 @@ function SettingsButton({ }: any) { const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' }); const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]); - + const navigate = useNavigate(); useEffect(() => { if (data) { const list: [any] = @@ -42,9 +43,9 @@ function SettingsButton({ navigate(`/${import.meta.env.VITE_BASE_URL}/settings`)} /> @@ -27,7 +29,7 @@ export function Settings({ theme }: any) { className={actionIconClasses.backButton} classNames={actionIconClasses} component='a' - href={`/${import.meta.env.VITE_BASE_URL}`} + onClick={() => navigate(`/${import.meta.env.VITE_BASE_URL}`)} > @@ -74,7 +76,7 @@ export function Settings({ theme }: any) { - + diff --git a/src/pages/settings/components/ThemeCard.tsx b/src/pages/settings/components/ThemeCard.tsx index cde9d3d..0e6c5dc 100644 --- a/src/pages/settings/components/ThemeCard.tsx +++ b/src/pages/settings/components/ThemeCard.tsx @@ -7,12 +7,15 @@ function ThemeCard({ handleOpen, handleDeleteTheme, theme, + allThemes, + setTheme, cardTheme, currentTheme, setValue, createTheme, }: any) { const [hover, setHover] = useState(false); + const [shift, setShift] = useState(0); return ( { setValue(cardTheme.name); }} @@ -145,23 +148,46 @@ function ThemeCard({ {currentTheme === 'random' && cardTheme.name === theme.name ? ( { + setShift(2); + }} + onMouseLeave={() => { + setShift(0); + }} + onClick={() => { + setShift(0); + }} gap={0} style={{ zIndex: 300, position: 'absolute', right: 0, - backgroundColor: 'white', + top: 27 - shift, + backgroundColor: shift === 0 ? 'white' : 'var(--color-background)', borderRadius: '50%', padding: '8px', margin: '4px', - border: 'solid 2px black', + border: 'solid 2px', + borderColor: shift === 0 ? 'black' : 'var(--color-text-pri)', + transition: 'all 0.2s', }} > { + const enabled = allThemes + .filter((t: any) => t.randomEnabled) + .filter((t: any) => t.name !== theme.name); + if (enabled.length === 0) { + setTheme(allThemes[0]); + } else { + const index = Math.floor(Math.random() * enabled.length); + setTheme(enabled[index]); + } + }} style={{ fontSize: '2em', fontWeight: 'bold', - color: 'black', + color: shift === 0 ? 'black' : 'var(--color-text-pri)', }} /> diff --git a/src/pages/settings/components/Themes.tsx b/src/pages/settings/components/Themes.tsx index 5bf5c05..aa2c658 100644 --- a/src/pages/settings/components/Themes.tsx +++ b/src/pages/settings/components/Themes.tsx @@ -9,7 +9,7 @@ import { modals } from '@mantine/modals'; import { buttonClasses, textClasses } from 'styles'; import ThemeCard from './ThemeCard'; import ThemeModal from './ThemeModal'; -function ThemesSettings({ theme }: any) { +function ThemesSettings({ theme, setTheme, allThemes }: any) { const [opened, { open, close }] = useDisclosure(false); const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false); const [colorId, setColorId] = useState(''); @@ -18,23 +18,14 @@ function ThemesSettings({ theme }: any) { key: 'theme', defaultValue: 'blackboard', }); - const { data, isFetched } = useQueryAPI({ - endpoint: 'api/themes', - }); const { data: backgrounds, isFetched: isBackgroundFetched } = useQueryAPI({ endpoint: 'api/backgrounds', }); - const [themesList, setThemesList] = useState([]); const [backgroundList, setBackgroundList] = useState(['None']); const [selectedTheme, setSelectedTheme] = useState({}); const { mutateAsync: createTheme } = useMutationAPI(); const { mutateAsync: deleteTheme } = useMutationAPI(); - - useEffect(() => { - if (isFetched) { - setThemesList(data); - } - }, [data, isFetched]); + const isMobile = window.matchMedia('(max-width: 600px)').matches; useEffect(() => { if (backgrounds && isBackgroundFetched) { @@ -128,7 +119,7 @@ function ThemesSettings({ theme }: any) { color={theme.primaryColor} /> - + - {themesList.map((cardTheme: any) => ( + {allThemes?.map((cardTheme: any) => ( {cardTheme.name === currentTheme && (