This commit is contained in:
Sean Edmunds
2025-04-15 20:21:25 -05:00
parent 2e72b76e95
commit 78dec160b6
34 changed files with 225 additions and 466 deletions

22
package-lock.json generated
View File

@@ -15,7 +15,6 @@
"@tanstack/react-query-devtools": "^5.74.3", "@tanstack/react-query-devtools": "^5.74.3",
"@tanstack/react-query-persist-client": "^5.74.3", "@tanstack/react-query-persist-client": "^5.74.3",
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.1",
"jotai": "^2.12.3",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
@@ -3801,27 +3800,6 @@
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
"dev": true "dev": true
}, },
"node_modules/jotai": {
"version": "2.12.3",
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.3.tgz",
"integrity": "sha512-DpoddSkmPGXMFtdfnoIHfueFeGP643nqYUWC6REjUcME+PG2UkAtYnLbffRDw3OURI9ZUTcRWkRGLsOvxuWMCg==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=17.0.0",
"react": ">=17.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/js-sha3": { "node_modules/js-sha3": {
"version": "0.8.0", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",

View File

@@ -17,7 +17,6 @@
"@tanstack/react-query-devtools": "^5.74.3", "@tanstack/react-query-devtools": "^5.74.3",
"@tanstack/react-query-persist-client": "^5.74.3", "@tanstack/react-query-persist-client": "^5.74.3",
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.1",
"jotai": "^2.12.3",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",

View File

@@ -13,11 +13,10 @@ import {
createBrowserRouter, createBrowserRouter,
createRoutesFromElements, createRoutesFromElements,
} from 'react-router-dom'; } from 'react-router-dom';
import { MainLayout, ThemeProvider } from './components'; import { Loading, MainLayout, ThemeProvider } from './components';
import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import './index.css'; import './index.css';
import Loading from 'components/Loading';
import { Homepage, Settings } from 'pages'; import { Homepage, Settings } from 'pages';
import { ToastContainer } from 'react-toastify'; import { ToastContainer } from 'react-toastify';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';

View File

@@ -1,7 +1,7 @@
import { Divider, Modal, Stack } from '@mantine/core'; import { Divider, Modal, Stack } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { modalClasses } from 'styles'; import { modalClasses } from 'styles';
import type { Provider } from 'types'; import type { Application, Provider } from 'types';
import HelpSection from './HelpSection'; import HelpSection from './HelpSection';
export function HelpModal({ export function HelpModal({
@@ -12,8 +12,16 @@ export function HelpModal({
closeProviderModal, closeProviderModal,
applicationData, applicationData,
providerData, providerData,
}: any) { }: {
const { theme, isMobile } = useCommonHooks(); setSearchValue: (value: string) => void;
setProviderLabel: (label: string) => void;
setProviderURL: (url: string) => void;
providerModalOpened: boolean;
closeProviderModal: () => void;
applicationData: Application[];
providerData: Provider[];
}) {
const { theme, isMobile, modalStyle } = useCommonHooks();
return ( return (
<Modal.Root <Modal.Root
size={'60%'} size={'60%'}
@@ -28,14 +36,7 @@ export function HelpModal({
setProviderURL('https://www.google.com/search?q='); setProviderURL('https://www.google.com/search?q=');
closeProviderModal(); closeProviderModal();
}} }}
style={{ style={modalStyle}
'--color-text-pri': theme.primaryColor,
'--color-text-acc': theme.accentColor,
'--color-background': theme.backgroundColor,
'--color-border': theme.borderColor,
'--color-search-background': theme.searchColor,
'--image-background': `url(${theme.backgroundImage})`,
}}
closeOnEscape closeOnEscape
closeOnClickOutside closeOnClickOutside
> >
@@ -59,7 +60,7 @@ export function HelpModal({
<Divider my={8} color={theme.accentColor} /> <Divider my={8} color={theme.accentColor} />
<HelpSection <HelpSection
name={'Available Applications'} name={'Available Applications'}
items={applicationData.map((application: any) => { items={applicationData.map((application: Application) => {
return { return {
name: application.url, name: application.url,
command: `!${application.baseUrl.substring(1).toLowerCase()}`, command: `!${application.baseUrl.substring(1).toLowerCase()}`,

View File

@@ -2,7 +2,7 @@ import { Divider, Group } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import HintItem from './HintItem'; import HintItem from './HintItem';
function HelpSection({ name, items }: any) { function HelpSection({ name, items }: { name: string; items: object[] }) {
const { theme } = useCommonHooks(); const { theme } = useCommonHooks();
return ( return (
<> <>
@@ -30,7 +30,7 @@ function HelpSection({ name, items }: any) {
}} }}
> >
{items {items
?.sort((a: any, b: any) => (a.command > b.command ? 1 : -1)) ?.toSorted((a: any, b: any) => (a.command > b.command ? 1 : -1))
.map((item: any) => ( .map((item: any) => (
<HintItem key={item.command} command={item.command} name={item.name} /> <HintItem key={item.command} command={item.command} name={item.name} />
))} ))}

View File

@@ -1,6 +1,6 @@
import { Group, Text } from '@mantine/core'; import { Group, Text } from '@mantine/core';
function HintItem({ command, name }: any) { function HintItem({ command, name }: { command: string; name: string }) {
return ( return (
<Group <Group
gap={0} gap={0}

View File

@@ -1,17 +1,12 @@
import { Stack } from '@mantine/core'; import { Stack } from '@mantine/core';
import { stackClasses } from 'styles'; import { stackClasses } from 'styles';
function Loading({ loaded }: any) { export function Loading({ loaded }: any) {
return ( return (
<Stack <Stack
align='center'
justify='center'
pos={'fixed'}
className={stackClasses.fadeAway} className={stackClasses.fadeAway}
classNames={stackClasses} classNames={stackClasses}
data-loaded={loaded} data-loaded={loaded}
/> />
); );
} }
export default Loading;

View File

@@ -15,53 +15,9 @@ export function ThemeProvider({
'--image-background': `linear-gradient(${theme.backgroundColor}33, ${theme.backgroundColor}33), url(/backgrounds/${theme.backgroundImage})`, '--image-background': `linear-gradient(${theme.backgroundColor}33, ${theme.backgroundColor}33), url(/backgrounds/${theme.backgroundImage})`,
'--toastify-color-light': `hwb(from ${theme.primaryColor} h w b / 0.25)`, '--toastify-color-light': `hwb(from ${theme.primaryColor} h w b / 0.25)`,
'--toastify-color-dark': `hwb(from ${theme.primaryColor} h w b / 0.25)`, '--toastify-color-dark': `hwb(from ${theme.primaryColor} h w b / 0.25)`,
'--toastify-color-info': '#3498db',
'--toastify-color-success': '#07bc0c',
'--toastify-color-warning': '#f1c40f',
'--toastify-color-error': 'hsl(6, 78%, 57%)',
'--toastify-color-transparent': 'rgba(255, 255, 255, 0.7)',
'--toastify-icon-color-info': 'var(--toastify-color-info)',
'--toastify-icon-color-success': 'var(--toastify-color-success)',
'--toastify-icon-color-warning': 'var(--toastify-color-warning)',
'--toastify-icon-color-error': 'var(--toastify-color-error)',
'--toastify-container-width': 'fit-content',
'--toastify-toast-width': '320px',
'--toastify-toast-offset': '16px',
'--toastify-toast-top':
'max(var(--toastify-toast-offset), env(safe-area-inset-top))',
'--toastify-toast-right':
'max(var(--toastify-toast-offset), env(safe-area-inset-right))',
'--toastify-toast-left':
'max(var(--toastify-toast-offset), env(safe-area-inset-left))',
'--toastify-toast-bottom':
'max(var(--toastify-toast-offset), env(safe-area-inset-bottom))',
'--toastify-toast-background': '#fff',
'--toastify-toast-padding': '14px',
'--toastify-toast-min-height': '64px',
'--toastify-toast-max-height': '800px',
'--toastify-toast-bd-radius': '6px',
'--toastify-toast-shadow': '0px 4px 12px rgba(0, 0, 0, 0.1)',
'--toastify-font-family': 'sans-serif',
'--toastify-z-index': '9999',
'--toastify-text-color-light': theme.primaryColor, '--toastify-text-color-light': theme.primaryColor,
'--toastify-text-color-dark': theme.primaryColor, '--toastify-text-color-dark': theme.primaryColor,
/* Used only for colored theme */
'--toastify-text-color-info': '#fff',
'--toastify-text-color-success': '#fff',
'--toastify-text-color-warning': '#fff',
'--toastify-text-color-error': '#fff',
'--toastify-spinner-color': '#616161',
'--toastify-spinner-color-empty-area': '#e0e0e0',
'--toastify-color-progress-light': theme.accentColor, '--toastify-color-progress-light': theme.accentColor,
'--toastify-color-progress-dark': '#bb86fc',
'--toastify-color-progress-info': 'var(--toastify-color-info)',
'--toastify-color-progress-success': 'var(--toastify-color-success)',
'--toastify-color-progress-warning': 'var(--toastify-color-warning)',
'--toastify-color-progress-error': 'var(--toastify-color-error)',
/* used to control the opacity of the progress trail */
'--toastify-color-progress-bgo': 0.2, '--toastify-color-progress-bgo': 0.2,
}} }}
> >

View File

@@ -4,9 +4,11 @@ import { useSunStateAPI } from 'hooks/useAPI';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa'; import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa';
import { WiBarometer, WiHumidity, WiSunrise, WiSunset } from 'react-icons/wi'; import { WiBarometer, WiHumidity, WiSunrise, WiSunset } from 'react-icons/wi';
import { themeIconClasses } from 'styles';
import type { CurrentWeather as CW } from 'types';
import WeatherIcon from './WeatherIcon'; import WeatherIcon from './WeatherIcon';
export default function CurrentWeather({ current }: any) { export default function CurrentWeather({ current }: { current: CW }) {
const { currentLocation, isMobile } = useCommonHooks(); const { currentLocation, isMobile } = useCommonHooks();
const { data: sunStateData } = useSunStateAPI({ const { data: sunStateData } = useSunStateAPI({
lat: currentLocation.lat, lat: currentLocation.lat,
@@ -34,19 +36,21 @@ export default function CurrentWeather({ current }: any) {
{current ? ( {current ? (
<Stack <Stack
gap={0} gap={0}
w={'100%'}
justify='space-between' justify='space-between'
style={{ style={{
width: '100%',
backgroundColor: 'var(--color-search-background)', backgroundColor: 'var(--color-search-background)',
padding: '1rem', padding: '1rem',
borderRadius: '8px', borderRadius: '8px',
paddingBottom: '.5rem', paddingBottom: '.5rem',
}} }}
c={'var(--color-text-pri)'}
> >
<Group gap={0} px={16} w={'100%'} justify='space-between'> <Group
gap={0}
justify='space-between'
style={{ width: '100%', paddingLeft: 16 }}
>
<Text <Text
c={'var(--color-text-pri)'}
style={{ style={{
fontSize: '1.375rem', fontSize: '1.375rem',
textAlign: 'right', textAlign: 'right',
@@ -55,7 +59,7 @@ export default function CurrentWeather({ current }: any) {
Current Weather Current Weather
</Text> </Text>
</Group> </Group>
<Group gap={8} w={'100%'} justify='space-between'> <Group gap={8} justify='space-between'>
<Stack gap={0} w={isMobile ? '40%' : '45%'}> <Stack gap={0} w={isMobile ? '40%' : '45%'}>
<Group gap={0} justify='flex-end'> <Group gap={0} justify='flex-end'>
<WeatherIcon <WeatherIcon
@@ -78,7 +82,6 @@ export default function CurrentWeather({ current }: any) {
</Text> </Text>
</Group> </Group>
<Text <Text
c={'var(--color-text-pri)'}
style={{ style={{
fontSize: '1.375rem', fontSize: '1.375rem',
textAlign: 'right', textAlign: 'right',
@@ -97,10 +100,8 @@ export default function CurrentWeather({ current }: any) {
<Grid align='center'> <Grid align='center'>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<FaLongArrowAltUp size={32} /> <FaLongArrowAltUp size={32} />
</ThemeIcon> </ThemeIcon>
@@ -117,10 +118,8 @@ export default function CurrentWeather({ current }: any) {
</Grid.Col> </Grid.Col>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<FaLongArrowAltDown size={32} /> <FaLongArrowAltDown size={32} />
</ThemeIcon> </ThemeIcon>
@@ -139,10 +138,8 @@ export default function CurrentWeather({ current }: any) {
<Grid align='center'> <Grid align='center'>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<WiSunrise size={32} /> <WiSunrise size={32} />
</ThemeIcon> </ThemeIcon>
@@ -157,10 +154,8 @@ export default function CurrentWeather({ current }: any) {
</Grid.Col> </Grid.Col>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<WiSunset size={32} /> <WiSunset size={32} />
</ThemeIcon> </ThemeIcon>
@@ -177,10 +172,8 @@ export default function CurrentWeather({ current }: any) {
<Grid align='center'> <Grid align='center'>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<WiHumidity size={32} /> <WiHumidity size={32} />
</ThemeIcon> </ThemeIcon>
@@ -205,10 +198,8 @@ export default function CurrentWeather({ current }: any) {
<Grid align='center'> <Grid align='center'>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<FaWind size={32} /> <FaWind size={32} />
</ThemeIcon> </ThemeIcon>
@@ -233,10 +224,8 @@ export default function CurrentWeather({ current }: any) {
<Grid align='center'> <Grid align='center'>
<Grid.Col span={1}> <Grid.Col span={1}>
<ThemeIcon <ThemeIcon
h={'auto'} className={themeIconClasses.weatherIcon}
w={'auto'} classNames={themeIconClasses}
variant='subtle'
c={'var(--color-text-pri)'}
> >
<WiBarometer size={32} /> <WiBarometer size={32} />
</ThemeIcon> </ThemeIcon>

View File

@@ -19,9 +19,8 @@ export default function ExtendedForecast({ forecast }: any) {
paddingBottom: '.5rem', paddingBottom: '.5rem',
}} }}
> >
<Group gap={0} px={16} mb={8} w={'100%'}> <Group gap={0} style={{ width: '100%', marginBottom: '8px', paddingLeft: '16px' }}>
<Text <Text
c={'var(--color-text-pri)'}
style={{ style={{
fontSize: '1.375rem', fontSize: '1.375rem',
textAlign: 'left', textAlign: 'left',

View File

@@ -1,5 +1,4 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { import {
Area, Area,
CartesianGrid, CartesianGrid,
@@ -13,8 +12,6 @@ import {
} from 'recharts'; } from 'recharts';
export default function WeatherGraphs({ hourlyForecast }: any) { export default function WeatherGraphs({ hourlyForecast }: any) {
const { theme } = useCommonHooks();
function CustomTooltip({ payload, active }: any) { function CustomTooltip({ payload, active }: any) {
if (active) { if (active) {
return ( return (
@@ -50,7 +47,7 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
<ComposedChart data={hourlyForecast}> <ComposedChart data={hourlyForecast}>
<XAxis <XAxis
type='category' type='category'
stroke={theme.primaryColor} stroke={'var(--color-text-pri)'}
dataKey={(data: any) => { dataKey={(data: any) => {
return new Date(data.startTime).toLocaleTimeString('en-US', { return new Date(data.startTime).toLocaleTimeString('en-US', {
hour: 'numeric', hour: 'numeric',
@@ -59,7 +56,7 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
}} }}
/> />
<YAxis <YAxis
stroke={theme.primaryColor} stroke={'var(--color-text-pri)'}
unit={'°F'} unit={'°F'}
domain={['dataMin', 'dataMax']} domain={['dataMin', 'dataMax']}
/> />
@@ -70,8 +67,8 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
type='monotone' type='monotone'
name='Temperature' name='Temperature'
dataKey='temperature' dataKey='temperature'
stroke={theme.primaryColor} stroke={'var(--color-text-pri)'}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }} dot={{ stroke: 'var(--color-text-pri)', strokeWidth: 1 }}
/> />
<Line <Line
unit={'°F'} unit={'°F'}
@@ -80,8 +77,8 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
dataKey={(data: any) => { dataKey={(data: any) => {
return (data.dewpoint.value * 9) / 5 + 32; return (data.dewpoint.value * 9) / 5 + 32;
}} }}
stroke={theme.accentColor} stroke={'var(--color-text-acc)'}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }} dot={{ stroke: 'var(--color-text-pri)', strokeWidth: 1 }}
/> />
<Legend style={{ background: 'var(--color-search-background)' }} /> <Legend style={{ background: 'var(--color-search-background)' }} />
</ComposedChart> </ComposedChart>
@@ -90,7 +87,7 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
<ComposedChart data={hourlyForecast}> <ComposedChart data={hourlyForecast}>
<XAxis <XAxis
type='category' type='category'
stroke={theme.primaryColor} stroke={'var(--color-text-pri)'}
dataKey={(data: any) => { dataKey={(data: any) => {
return new Date(data.startTime).toLocaleTimeString('en-US', { return new Date(data.startTime).toLocaleTimeString('en-US', {
hour: 'numeric', hour: 'numeric',
@@ -98,7 +95,7 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
}); });
}} }}
/> />
<YAxis stroke={theme.primaryColor} unit={'%'} domain={[0, 100]} /> <YAxis stroke={'var(--color-text-pri)'} unit={'%'} domain={[0, 100]} />
<CartesianGrid stroke='#f5f5f522' /> <CartesianGrid stroke='#f5f5f522' />
<Tooltip content={<CustomTooltip />} /> <Tooltip content={<CustomTooltip />} />
<Area <Area
@@ -108,9 +105,9 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
dataKey={(data: any) => { dataKey={(data: any) => {
return data.probabilityOfPrecipitation.value; return data.probabilityOfPrecipitation.value;
}} }}
stroke={theme.primaryColor} stroke={'var(--color-text-pri)'}
fill={theme.primaryColor} fill={'var(--color-text-pri)'}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }} dot={{ stroke: 'var(--color-text-pri)', strokeWidth: 1 }}
/> />
<Area <Area
unit={'%'} unit={'%'}
@@ -119,9 +116,9 @@ export default function WeatherGraphs({ hourlyForecast }: any) {
dataKey={(data: any) => { dataKey={(data: any) => {
return data.relativeHumidity.value; return data.relativeHumidity.value;
}} }}
stroke={theme.accentColor} stroke={'var(--color-text-acc)'}
fill={theme.accentColor} fill={'var(--color-text-acc)'}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }} dot={{ stroke: 'var(--color-text-pri)', strokeWidth: 1 }}
/> />
<Legend style={{ background: 'var(--color-search-background)' }} /> <Legend style={{ background: 'var(--color-search-background)' }} />
</ComposedChart> </ComposedChart>

View File

@@ -4,20 +4,29 @@ import { modalClasses } from 'styles';
import { useCommonHooks, useWeatherAPI } from 'hooks'; import { useCommonHooks, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import type { CurrentWeather as CW, ExtendedForecast as EF } from 'types';
import CurrentWeather from './CurrentWeather'; import CurrentWeather from './CurrentWeather';
import ExtendedForecast from './ExtendedForecast'; import ExtendedForecast from './ExtendedForecast';
import WeatherGraphs from './WeatherGraphs'; import WeatherGraphs from './WeatherGraphs';
export function WeatherModal({ export function WeatherModal({
current,
extendedForecast,
weatherModalOpened,
setSearchValue, setSearchValue,
setProviderLabel, setProviderLabel,
setProviderURL, setProviderURL,
current,
extendedForecast,
closeWeatherModal, closeWeatherModal,
weatherModalOpened, }: {
}: any) { current: CW;
const { theme, isMobile, currentUser, currentLocation } = useCommonHooks(); extendedForecast: EF[];
weatherModalOpened: boolean;
setSearchValue: (value: string) => void;
setProviderLabel: (label: string) => void;
setProviderURL: (url: string) => void;
closeWeatherModal: () => void;
}) {
const { isMobile, currentUser, currentLocation, modalStyle } = useCommonHooks();
const { data: hourly } = useWeatherAPI({ const { data: hourly } = useWeatherAPI({
endpoint: 'hourly', endpoint: 'hourly',
office: currentLocation.office, office: currentLocation.office,
@@ -46,14 +55,7 @@ export function WeatherModal({
setProviderURL('https://www.google.com/search?q='); setProviderURL('https://www.google.com/search?q=');
closeWeatherModal(); closeWeatherModal();
}} }}
style={{ style={modalStyle}
'--color-text-pri': theme.primaryColor,
'--color-text-acc': theme.accentColor,
'--color-background': theme.backgroundColor,
'--color-border': theme.borderColor,
'--color-search-background': theme.searchColor,
'--image-background': `url(${theme.backgroundImage})`,
}}
closeOnEscape closeOnEscape
closeOnClickOutside closeOnClickOutside
> >
@@ -61,7 +63,7 @@ export function WeatherModal({
<Modal.Content> <Modal.Content>
<Modal.Header> <Modal.Header>
<Modal.Title> <Modal.Title>
<Group gap={8} style={{ borderBottom: `1px solid ${theme.borderColor}` }}> <Group gap={8} style={{ borderBottom: '1px solid var(--color-border)' }}>
<Text fz={18} fw={200}> <Text fz={18} fw={200}>
Weather Forecast for{' '} Weather Forecast for{' '}
</Text> </Text>

View File

@@ -2,3 +2,4 @@ export { HelpModal } from './HelpModal/HelpModal';
export { WeatherModal } from './WeatherModal/WeatherModal'; export { WeatherModal } from './WeatherModal/WeatherModal';
export { ThemeProvider } from './ThemeProvider'; export { ThemeProvider } from './ThemeProvider';
export { MainLayout } from './MainLayout'; export { MainLayout } from './MainLayout';
export { Loading } from './Loading';

View File

@@ -1,22 +1,29 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback, useRef } from 'react'; import { useRef } from 'react';
import { Flip, toast } from 'react-toastify'; import { Flip, toast } from 'react-toastify';
const url = `${import.meta.env.PROD ? import.meta.env.VITE_DOMAIN_PROD : import.meta.env.VITE_DOMAIN_DEV}/${import.meta.env.VITE_BASE_URL}`; const url = `${import.meta.env.PROD ? import.meta.env.VITE_DOMAIN_PROD : import.meta.env.VITE_DOMAIN_DEV}/${import.meta.env.VITE_BASE_URL}`;
const headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
};
const callEndpoint = async (url: string) => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`);
}
return await response.json();
};
export function useQueryAPI({ endpoint }: { endpoint: string }) { export function useQueryAPI({ endpoint }: { endpoint: string }) {
const callEndpoint = async (endpoint: string) => { const callEndpoint = async (endpoint: string) => {
const options: object = { const options: object = {
method: 'GET', method: 'GET',
headers: { headers: headers,
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
}; };
const response = await fetch(`${url}/${endpoint}`, options); const response = await fetch(`${url}/${endpoint}`, options);
@@ -44,15 +51,7 @@ export function useMutationAPI() {
) => { ) => {
const options: any = { const options: any = {
method: method, method: method,
headers: { headers: headers,
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
body: JSON.stringify(body), body: JSON.stringify(body),
}; };
notify(); notify();
@@ -124,7 +123,7 @@ export function useMutationAPI() {
endpoint: string; endpoint: string;
body: object | undefined | null; body: object | undefined | null;
}) => callEndpoint(method, endpoint, body), }) => callEndpoint(method, endpoint, body),
onMutate: ({ method, endpoint }) => { onMutate: ({ endpoint }) => {
queryClient.refetchQueries({ queryClient.refetchQueries({
queryKey: ['GET', endpoint], queryKey: ['GET', endpoint],
}); });
@@ -135,7 +134,7 @@ export function useMutationAPI() {
endpoint === 'api/applicationOrder' && endpoint === 'api/applicationOrder' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] }); queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] });
}, },
onSuccess: (data, { method, endpoint }) => { onSuccess: (_, { endpoint }) => {
queryClient.refetchQueries({ queryClient.refetchQueries({
queryKey: ['GET', endpoint], queryKey: ['GET', endpoint],
}); });
@@ -180,17 +179,10 @@ export function useWeatherAPI({
} else if (endpoint === 'stations') { } else if (endpoint === 'stations') {
url = `https://api.weather.gov/gridpoints/${office}/${x},${y}/stations`; url = `https://api.weather.gov/gridpoints/${office}/${x},${y}/stations`;
} }
const callEndpoint = async () => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`);
}
return await response.json();
};
return useQuery({ return useQuery({
queryKey: ['GET', url], queryKey: ['GET', url],
queryFn: () => callEndpoint(), queryFn: () => callEndpoint(url),
}); });
} }
@@ -200,16 +192,10 @@ export function useSunStateAPI({ lat, lon }: { lat: number; lon: number }) {
date.setDate(date.getDate() + 1); date.setDate(date.getDate() + 1);
const tomorrow = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; const tomorrow = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lon}&date_start=${today}&date_end=${tomorrow}`; const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lon}&date_start=${today}&date_end=${tomorrow}`;
const callEndpoint = async () => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText}`);
}
return await response.json();
};
return useQuery({ return useQuery({
queryKey: ['GET', url], queryKey: ['GET', url],
queryFn: () => callEndpoint(), queryFn: () => callEndpoint(url),
}); });
} }
@@ -238,15 +224,7 @@ export function useFileUploadAPI() {
) => { ) => {
const options = { const options = {
method: method, method: method,
headers: { headers: headers,
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
body: body ?? undefined, body: body ?? undefined,
}; };
const response = await fetch(url, options as unknown as Body); const response = await fetch(url, options as unknown as Body);
@@ -300,115 +278,3 @@ export function useFileUploadAPI() {
}, },
}); });
} }
export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
const pageSize = 500;
const queryClient = useQueryClient();
const fetchPage = async (page: number, retryCount = 0): Promise<any> => {
// Start building the URL
const urlObject = new URL(`${url}/${endpoint}`);
// Check and append custom query parameters from the endpoint
const customParams = new URLSearchParams(endpoint.split('?')[1]);
for (const [key, value] of customParams) {
if (!urlObject.searchParams.has(key)) {
urlObject.searchParams.append(key, value);
}
}
// Add 'page' and 'size' parameters
urlObject.searchParams.set('page', page.toString()); // Use 'set' to ensure uniqueness
urlObject.searchParams.set('size', pageSize.toString());
const options: any = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-site',
},
};
try {
const response = await fetch(url.toString(), options);
if (!response.ok) {
// If the response is not ok and status is 401 (not authorized), reload the page
if (response.status === 401) {
window.location.reload();
}
throw new Error(
`Error fetching page ${page}: ${response.status} ${response.statusText}`,
);
}
return response.json();
} catch (err) {
if (retryCount < 3) {
// Retry up to 3 times with a random delay
const delay = Math.floor(Math.random() * 3000) + 1000; // Random delay between 1 to 3 seconds
console.error(
`Retrying page ${page}, attempt ${retryCount + 1} after ${delay} ms`,
);
await new Promise((resolve) => setTimeout(resolve, delay));
return fetchPage(page, retryCount + 1);
}
console.error(`Error fetching page ${page} after 3 retries:`, err);
throw err; // Re-throw the error to handle it in the calling function
}
};
const callEndpoint = async () => {
try {
// Fetch the first page to get the total number of items and pages
const firstPageData = await fetchPage(1);
// If the first page doesn't have a 'pages' property, return the data as-is
if (firstPageData?.pages === undefined) {
return firstPageData;
}
if (firstPageData?.pages === 1) {
return firstPageData.items;
}
let totalPages = firstPageData.pages;
const allItems = [...firstPageData.items];
// Fetch each page sequentially
for (let page = 2; page <= totalPages; page++) {
try {
const pageData = await fetchPage(page);
allItems.push(...pageData.items);
// Check and update the totalPages if it has increased
if (pageData.pages > totalPages) {
totalPages = pageData.pages;
}
} catch (err) {
console.error(`Final error fetching page ${page}:`, err);
// Continue to the next page even if the current page fails
}
}
return allItems;
} catch (error) {
// Handling errors in fetching the first page
console.error('Error fetching the first page:', error);
return []; // Return empty array on error
}
};
const invalidate = useCallback(() => {
queryClient.invalidateQueries({ queryKey: ['GET', endpoint] });
}, [queryClient, endpoint]);
return {
...useQuery({
queryKey: ['GET', endpoint],
queryFn: callEndpoint,
}),
invalidate,
};
}

View File

@@ -8,11 +8,11 @@ export function useCommonHooks() {
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const isMobile = window.matchMedia('(max-width: 600px)').matches; const isMobile = window.matchMedia('(max-width: 600px)').matches;
const [theme, setTheme] = useAtom(appTheme); const [theme, setTheme] = useAtom(appTheme);
const [themeName, setThemeName] = useLocalStorage<any>({ const [themeName, setThemeName] = useLocalStorage<string>({
key: 'theme', key: 'theme',
defaultValue: 'blackboard', defaultValue: 'blackboard',
}); });
const [appFilter, setAppFilter] = useLocalStorage<[any]>({ const [appFilter, setAppFilter] = useLocalStorage<[string]>({
key: 'appFilter', key: 'appFilter',
defaultValue: ['All'], defaultValue: ['All'],
}); });
@@ -24,16 +24,16 @@ export function useCommonHooks() {
key: 'bookmarkSort', key: 'bookmarkSort',
defaultValue: 'A-Z', defaultValue: 'A-Z',
}); });
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[any]>({ const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<[string]>({
key: 'bookmarkFilter', key: 'bookmarkFilter',
defaultValue: ['All'], defaultValue: ['All'],
}); });
const [currentTheme, setCurrentTheme] = useLocalStorage<any>({ const [currentTheme, setCurrentTheme] = useLocalStorage<string>({
key: 'theme', key: 'theme',
defaultValue: 'blackboard', defaultValue: 'blackboard',
}); });
const [currentThemePlaylist, setCurrentThemePlaylist] = useLocalStorage<any>({ const [currentThemePlaylist, setCurrentThemePlaylist] = useLocalStorage<string>({
key: 'themePlaylist', key: 'themePlaylist',
defaultValue: 'All', defaultValue: 'All',
}); });
@@ -80,6 +80,13 @@ export function useCommonHooks() {
'i', 'i',
); // fragment locator ); // fragment locator
const reorder = (list: any, startIndex: any, endIndex: any) => {
const result = Array.from(list);
const [removed]: any = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const handleThemeChange = (themes: any, save: any) => { const handleThemeChange = (themes: any, save: any) => {
const enabled = themes const enabled = themes
.filter((t: any) => t.randomEnabled) .filter((t: any) => t.randomEnabled)
@@ -116,6 +123,7 @@ export function useCommonHooks() {
modalStyle, modalStyle,
urlPattern, urlPattern,
fixDigits, fixDigits,
reorder,
handleThemeChange, handleThemeChange,
handleDelete, handleDelete,
themeName, themeName,

View File

@@ -8,7 +8,6 @@ import type {
CurrentWeather as CW, CurrentWeather as CW,
ExtendedForecast as EF, ExtendedForecast as EF,
Message, Message,
OutlookWeather,
PersonalData, PersonalData,
} from 'types'; } from 'types';
import Applications from './components/Applications/Applications'; import Applications from './components/Applications/Applications';
@@ -85,31 +84,31 @@ export function Homepage() {
const { data: weatherMetadata } = useOSMAPI({ address: personal?.[0].physicalAddress }); const { data: weatherMetadata } = useOSMAPI({ address: personal?.[0].physicalAddress });
const { data: points } = useWeatherAPI({ const { data: points } = useWeatherAPI({
endpoint: 'points', endpoint: 'points',
lat: weatherMetadata?.[0].lat || currentLocation.lat, lat: weatherMetadata?.[0].lat ?? currentLocation.lat,
lon: weatherMetadata?.[0].lon || currentLocation.lon, lon: weatherMetadata?.[0].lon ?? currentLocation.lon,
}); });
const { data: basicWeather } = useWeatherAPI({ const { data: basicWeather } = useWeatherAPI({
endpoint: 'baseline', endpoint: 'baseline',
office: points?.properties.gridId || currentLocation.office, office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX || currentLocation.x, x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY || currentLocation.y, y: points?.properties.gridY ?? currentLocation.y,
}); });
const { data: stations } = useWeatherAPI({ const { data: stations } = useWeatherAPI({
endpoint: 'stations', endpoint: 'stations',
office: points?.properties.gridId || currentLocation.office, office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX || currentLocation.x, x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY || currentLocation.y, y: points?.properties.gridY ?? currentLocation.y,
}); });
const { data: weather } = useWeatherAPI({ const { data: weather } = useWeatherAPI({
endpoint: 'current', endpoint: 'current',
station: station:
stations?.features[0].properties.stationIdentifier || currentLocation.station, stations?.features[0].properties.stationIdentifier ?? currentLocation.station,
}); });
const { data: forecast } = useWeatherAPI({ const { data: forecast } = useWeatherAPI({
endpoint: 'forecast', endpoint: 'forecast',
office: points?.properties.gridId || currentLocation.office, office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX || currentLocation.x, x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY || currentLocation.y, y: points?.properties.gridY ?? currentLocation.y,
}); });
const [current, setCurrent] = useState<CW>({ const [current, setCurrent] = useState<CW>({
icon: '', icon: '',
@@ -125,17 +124,7 @@ export function Homepage() {
windDirection: 0, windDirection: 0,
pressure: 0, pressure: 0,
}); });
const [outlook, setOutlook] = useState<OutlookWeather[]>([
{
name: '',
icon: '',
isDayTime: true,
temperature: 0,
precipitation: 0,
windSpeed: '0 mph',
windDirection: 'N',
},
]);
const [extendedForecast, setExtendedForecast] = useState<EF[]>([ const [extendedForecast, setExtendedForecast] = useState<EF[]>([
{ {
detailedForecast: '', detailedForecast: '',
@@ -150,7 +139,7 @@ export function Homepage() {
useEffect(() => { useEffect(() => {
if (weather !== undefined && basicWeather !== undefined) { if (weather !== undefined && basicWeather !== undefined) {
setCurrent({ setCurrent({
icon: weather.features[0]?.properties.icon?.replace('medium', 'large') || '', icon: weather.features[0]?.properties.icon?.replace('medium', 'large') ?? '',
textDescription: weather.features[0]?.properties.textDescription, textDescription: weather.features[0]?.properties.textDescription,
temperature: Math.round( temperature: Math.round(
(weather.features[0]?.properties.temperature.value * 9) / 5 + 32, (weather.features[0]?.properties.temperature.value * 9) / 5 + 32,
@@ -162,7 +151,7 @@ export function Homepage() {
(basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32, (basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32,
), ),
precipitation: precipitation:
weather.features[0]?.properties.precipitationLastHour.value || 'N/A', weather.features[0]?.properties.precipitationLastHour.value ?? 'N/A',
humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value), humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value),
heatIndex: heatIndex:
basicWeather.properties.heatIndex?.values[0].value === null basicWeather.properties.heatIndex?.values[0].value === null
@@ -186,30 +175,6 @@ export function Homepage() {
}, [weather, basicWeather]); }, [weather, basicWeather]);
useEffect(() => { useEffect(() => {
if (forecast) { if (forecast) {
setOutlook([
{
name: forecast.properties.periods[0].name,
icon: forecast.properties.periods[0].icon.replace('medium', 'large'),
isDayTime: forecast.properties.periods[0].isDaytime,
temperature: forecast.properties.periods[0].temperature,
precipitation: Math.round(
forecast.properties.periods[0].probabilityOfPrecipitation.value,
),
windSpeed: forecast.properties.periods[0].windSpeed,
windDirection: forecast.properties.periods[0].windDirection,
},
{
name: forecast.properties.periods[1].name,
icon: forecast.properties.periods[1].icon.replace('medium', 'large'),
isDayTime: forecast.properties.periods[1].isDaytime,
temperature: forecast.properties.periods[1].temperature,
precipitation: Math.round(
forecast.properties.periods[1].probabilityOfPrecipitation.value,
),
windSpeed: forecast.properties.periods[1].windSpeed,
windDirection: forecast.properties.periods[1].windDirection,
},
]);
setExtendedForecast( setExtendedForecast(
forecast.properties.periods.map((item: any) => ({ forecast.properties.periods.map((item: any) => ({
detailedForecast: item.detailedForecast, detailedForecast: item.detailedForecast,
@@ -241,25 +206,18 @@ export function Homepage() {
<> <>
<Search <Search
current={current} current={current}
outlook={outlook}
extendedForecast={extendedForecast} extendedForecast={extendedForecast}
weather={weather}
basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal} closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal} openWeatherModal={openWeatherModal}
/> />
<Header <Header
date={date}
greet={displayedGreeting} greet={displayedGreeting}
date={date}
current={current} current={current}
setCurrent={setCurrent} setCurrent={setCurrent}
outlook={outlook}
extendedForecast={extendedForecast}
weather={weather} weather={weather}
basicWeather={basicWeather} basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal} openWeatherModal={openWeatherModal}
/> />
<Applications contentType={appFilter} /> <Applications contentType={appFilter} />
@@ -269,28 +227,19 @@ export function Homepage() {
<> <>
<Stack align='center'> <Stack align='center'>
<LargeHeader <LargeHeader
date={date}
greet={displayedGreeting} greet={displayedGreeting}
current={current} date={date}
setCurrent={setCurrent}
outlook={outlook}
extendedForecast={extendedForecast}
weather={weather} weather={weather}
basicWeather={basicWeather} basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened} setCurrent={setCurrent}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal}
/> />
<Grid grow columns={24} pos={'absolute'} bottom={'1rem'} w={'50%'}> <Grid grow columns={24} pos={'absolute'} bottom={'1rem'} w={'50%'}>
<CurrentWeather span={18} current={current} /> <CurrentWeather current={current} />
</Grid> </Grid>
</Stack> </Stack>
<Search <Search
current={current} current={current}
outlook={outlook}
extendedForecast={extendedForecast} extendedForecast={extendedForecast}
weather={weather}
basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal} closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal} openWeatherModal={openWeatherModal}

View File

@@ -6,7 +6,7 @@ import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc'; import * as vsc from 'react-icons/vsc';
import { stackClasses, textClasses } from 'styles'; import { stackClasses, textClasses } from 'styles';
function AppItem({ url, icon, name }: any) { function AppItem({ url, icon, name }: { url: string; icon: string; name: string }) {
const allIcons: any = { ...fa6, ...vsc, ...si }; const allIcons: any = { ...fa6, ...vsc, ...si };
const Icon = allIcons[icon]; const Icon = allIcons[icon];
const { isMobile } = useCommonHooks(); const { isMobile } = useCommonHooks();

View File

@@ -5,7 +5,7 @@ import { textClasses } from 'styles';
import type { Application } from 'types'; import type { Application } from 'types';
import AppItem from './AppItem'; import AppItem from './AppItem';
function Applications({ contentType }: any) { function Applications({ contentType }: { contentType: [string] }) {
const { data: apps, isFetching: isAppsFetching } = useQueryAPI({ const { data: apps, isFetching: isAppsFetching } = useQueryAPI({
endpoint: 'api/apps', endpoint: 'api/apps',
}); });
@@ -59,14 +59,13 @@ function Applications({ contentType }: any) {
contentType.includes(application.filterName) || contentType.includes(application.filterName) ||
contentType.includes('All'), contentType.includes('All'),
) )
.map((application: any) => { .map((application: Application) => {
return ( return (
<AppItem <AppItem
key={application.name} key={application.name}
name={application.name} name={application.name}
url={application.url} url={application.url}
icon={application.icon} icon={application.icon}
category={application.category}
/> />
); );
})} })}

View File

@@ -5,7 +5,7 @@ import { textClasses } from 'styles';
import type { Bookmark, BookmarkGroup } from 'types'; import type { Bookmark, BookmarkGroup } from 'types';
import LinkGroups from './LinkGroups'; import LinkGroups from './LinkGroups';
function Links({ contentType }: any) { function Links({ contentType }: { contentType: [string] }) {
const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({ const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({
endpoint: 'api/links', endpoint: 'api/links',
}); });

View File

@@ -1,8 +1,9 @@
import { Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { Bookmark } from 'types';
import LinkItem from './LinkItem'; import LinkItem from './LinkItem';
function LinkGroups({ links, category }: any) { function LinkGroups({ links, category }: { links: Bookmark[]; category: string }) {
return ( return (
<Stack <Stack
style={{ style={{
@@ -17,8 +18,8 @@ function LinkGroups({ links, category }: any) {
{category} {category}
</Text> </Text>
{links {links
?.sort((a: any, b: any) => (a.index > b.index ? 1 : -1)) ?.toSorted((a: Bookmark, b: Bookmark) => (a.index > b.index ? 1 : -1))
.map((link: any) => ( .map((link: Bookmark) => (
<LinkItem key={link.name} link={link} /> <LinkItem key={link.name} link={link} />
))} ))}
</Stack> </Stack>

View File

@@ -1,7 +1,8 @@
import { Text } from '@mantine/core'; import { Text } from '@mantine/core';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { Bookmark } from 'types';
function LinkItem({ link }: any) { function LinkItem({ link }: { link: Bookmark }) {
return ( return (
<Text <Text
className={textClasses.linkItem} className={textClasses.linkItem}

View File

@@ -2,6 +2,7 @@ import { Group, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { CurrentWeather as CW } from 'types';
function Header({ function Header({
greet, greet,
@@ -11,7 +12,15 @@ function Header({
weather, weather,
basicWeather, basicWeather,
openWeatherModal, openWeatherModal,
}: any) { }: {
greet: string;
date: string;
current: CW;
setCurrent: (current: CW) => void;
weather: any;
basicWeather: any;
openWeatherModal: () => void;
}) {
const [time, setTime] = useState(new Date()); const [time, setTime] = useState(new Date());
const { isMobile, currentUser, fixDigits } = useCommonHooks(); const { isMobile, currentUser, fixDigits } = useCommonHooks();
@@ -25,7 +34,7 @@ function Header({
useEffect(() => { useEffect(() => {
if (weather !== undefined && basicWeather !== undefined) { if (weather !== undefined && basicWeather !== undefined) {
setCurrent({ setCurrent({
icon: weather.features[0]?.properties.icon?.replace('medium', 'large') || '', icon: weather.features[0]?.properties.icon?.replace('medium', 'large') ?? '',
textDescription: weather.features[0]?.properties.textDescription, textDescription: weather.features[0]?.properties.textDescription,
temperature: Math.round( temperature: Math.round(
(weather.features[0]?.properties.temperature.value * 9) / 5 + 32, (weather.features[0]?.properties.temperature.value * 9) / 5 + 32,
@@ -37,7 +46,7 @@ function Header({
(basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32, (basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32,
), ),
precipitation: precipitation:
weather.features[0]?.properties.precipitationLastHour.value || 'N/A', weather.features[0]?.properties.precipitationLastHour.value ?? 'N/A',
humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value), humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value),
heatIndex: heatIndex:
basicWeather.properties.heatIndex?.values[0].value === null basicWeather.properties.heatIndex?.values[0].value === null
@@ -128,17 +137,17 @@ function Header({
> >
<Text <Text
component='h2' component='h2'
mr={4}
className={textClasses.dateTime} className={textClasses.dateTime}
classNames={textClasses} classNames={textClasses}
mr={4}
> >
{current.temperature}°F {current.temperature}°F
</Text> </Text>
<Text <Text
mb={6}
className={textClasses.dateTime} className={textClasses.dateTime}
classNames={textClasses} classNames={textClasses}
fz={14} fz={14}
mb={6}
> >
{current.highTemp}°F/{current.lowTemp}°F {current.highTemp}°F/{current.lowTemp}°F
</Text> </Text>

View File

@@ -2,8 +2,21 @@ import { Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { CurrentWeather as CW } from 'types';
function LargeHeader({ greet, date, setCurrent, weather, basicWeather }: any) { function LargeHeader({
greet,
date,
setCurrent,
weather,
basicWeather,
}: {
greet: string;
date: string;
weather: any;
basicWeather: any;
setCurrent: (current: CW) => void;
}) {
const [time, setTime] = useState(new Date()); const [time, setTime] = useState(new Date());
const { isMobile, currentUser, fixDigits } = useCommonHooks(); const { isMobile, currentUser, fixDigits } = useCommonHooks();
@@ -17,7 +30,7 @@ function LargeHeader({ greet, date, setCurrent, weather, basicWeather }: any) {
useEffect(() => { useEffect(() => {
if (weather !== undefined && basicWeather !== undefined) { if (weather !== undefined && basicWeather !== undefined) {
setCurrent({ setCurrent({
icon: weather.features[0]?.properties.icon?.replace('medium', 'large') || '', icon: weather.features[0]?.properties.icon?.replace('medium', 'large') ?? '',
textDescription: weather.features[0]?.properties.textDescription, textDescription: weather.features[0]?.properties.textDescription,
temperature: Math.round( temperature: Math.round(
(weather.features[0]?.properties.temperature.value * 9) / 5 + 32, (weather.features[0]?.properties.temperature.value * 9) / 5 + 32,
@@ -29,7 +42,7 @@ function LargeHeader({ greet, date, setCurrent, weather, basicWeather }: any) {
(basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32, (basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32,
), ),
precipitation: precipitation:
weather.features[0]?.properties.precipitationLastHour.value || 'N/A', weather.features[0]?.properties.precipitationLastHour.value ?? 'N/A',
humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value), humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value),
heatIndex: heatIndex:
basicWeather.properties.heatIndex?.values[0].value === null basicWeather.properties.heatIndex?.values[0].value === null

View File

@@ -12,16 +12,21 @@ import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiEdit, FiTrash } from 'react-icons/fi'; import { FiEdit, FiTrash } from 'react-icons/fi';
import { actionIconClasses, selectClasses } from 'styles'; import { actionIconClasses, selectClasses } from 'styles';
import type { Application, Provider } from 'types'; import type { Application, CurrentWeather, ExtendedForecast, Provider } from 'types';
export default function Search({ export default function Search({
current, current,
outlook,
extendedForecast, extendedForecast,
weatherModalOpened, weatherModalOpened,
closeWeatherModal, closeWeatherModal,
openWeatherModal, openWeatherModal,
}: any) { }: {
current: CurrentWeather;
extendedForecast: ExtendedForecast[];
weatherModalOpened: boolean;
closeWeatherModal: () => void;
openWeatherModal: () => void;
}) {
const { data: providers, isFetched: providersIsFetched } = useQueryAPI({ const { data: providers, isFetched: providersIsFetched } = useQueryAPI({
endpoint: 'api/providers', endpoint: 'api/providers',
}); });
@@ -29,14 +34,13 @@ export default function Search({
const { data: apps, isFetching: isAppsFetching } = useQueryAPI({ const { data: apps, isFetching: isAppsFetching } = useQueryAPI({
endpoint: 'api/apps', endpoint: 'api/apps',
}); });
const [applicationData, setApplicationData] = useState<any>([]); const [applicationData, setApplicationData] = useState<Application[]>([]);
const { data: history, isFetched: historyIsFetched } = useQueryAPI({ const { data: history, isFetched: historyIsFetched } = useQueryAPI({
endpoint: 'api/searchHistory', endpoint: 'api/searchHistory',
}); });
const [searchHistory, setSearchHistory] = useState<any>([]); const [searchHistory, setSearchHistory] = useState<any>([]);
const [filteredHistory, setFilteredHistory] = useState<any>([]); const [filteredHistory, setFilteredHistory] = useState<any>([]);
const { mutateAsync: createHistoryItem } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const { mutateAsync: deleteHistoryItem } = useMutationAPI();
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] = const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false); useDisclosure(false);
const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure(); const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure();
@@ -59,7 +63,7 @@ export default function Search({
const handleSearchHistoryItem = (item: any) => { const handleSearchHistoryItem = (item: any) => {
const url = const url =
providers.find((provider: any) => provider.prefix === item.option.prefix)?.url || providers.find((provider: any) => provider.prefix === item.option.prefix)?.url ??
'https://www.google.com/search?q='; 'https://www.google.com/search?q=';
open(`${url}${item.option.searchTerm}`, '_self'); open(`${url}${item.option.searchTerm}`, '_self');
}; };
@@ -73,7 +77,7 @@ export default function Search({
let subtext = ''; let subtext = '';
if (item.target.value.startsWith('/')) { if (item.target.value.startsWith('/')) {
option = option =
item.target.value.substr(1, item.target.value.indexOf(' ') - 1) || item.target.value.substr(1, item.target.value.indexOf(' ') - 1) ??
item.target.value.substr(1); item.target.value.substr(1);
subtext = item.target.value.substr(2 + option.length); subtext = item.target.value.substr(2 + option.length);
} else if (item.target.value.startsWith('!')) { } else if (item.target.value.startsWith('!')) {
@@ -87,7 +91,7 @@ export default function Search({
if (providerLabel === 'Show help') { if (providerLabel === 'Show help') {
openProviderModal(); openProviderModal();
} else if (item.target.value.startsWith('/')) { } else if (item.target.value.startsWith('/')) {
createHistoryItem({ mutateAsync({
method: 'POST', method: 'POST',
endpoint: 'api/searchHistory', endpoint: 'api/searchHistory',
body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext }, body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext },
@@ -100,16 +104,18 @@ export default function Search({
const url = applicationData.find( const url = applicationData.find(
(app: Application) => (app: Application) =>
app.baseUrl.substring(1).toLowerCase() === item.target.value.substr(1), app.baseUrl.substring(1).toLowerCase() === item.target.value.substr(1),
).url; )?.url;
const validUrl = const validUrl =
url.startsWith('http://') || url.startsWith('https://') ? url : `http://${url}`; url?.startsWith('http://') || url?.startsWith('https://')
? url
: `http://${url}`;
open(validUrl, '_self'); open(validUrl, '_self');
} else if (urlPattern.test(item.target.value)) { } else if (urlPattern.test(item.target.value)) {
if (/^(https?:\/\/).*/i.test(item.target.value)) if (/^(https?:\/\/).*/i.test(item.target.value))
window.location = item.target.value; window.location = item.target.value;
else open(`https://${item.target.value}`, '_self'); else open(`https://${item.target.value}`, '_self');
} else { } else {
createHistoryItem({ mutateAsync({
method: 'POST', method: 'POST',
endpoint: 'api/searchHistory', endpoint: 'api/searchHistory',
body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext }, body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext },
@@ -220,8 +226,8 @@ export default function Search({
setProviderLabel('Show weather'); setProviderLabel('Show weather');
} else if ( } else if (
applicationData?.find( applicationData?.find(
(app: any) => (app: Application) =>
app.baseUrl.substr(1).toLowerCase() === item.target.value.substr(1), app.baseUrl.substring(1).toLowerCase() === item.target.value.substr(1),
) )
) { ) {
setProviderLabel(`Launch ${item.target.value.substr(1)}`); setProviderLabel(`Launch ${item.target.value.substr(1)}`);
@@ -239,10 +245,10 @@ export default function Search({
const splittedSearch = search.toLowerCase().trim().split(' '); const splittedSearch = search.toLowerCase().trim().split(' ');
if (splittedSearch[0].startsWith('!')) { if (splittedSearch[0].startsWith('!')) {
return apps return apps
.filter((app: any) => .filter((app: Application) =>
app.name.toLowerCase().includes(splittedSearch[0].substring(1).toLowerCase()), app.name.toLowerCase().includes(splittedSearch[0].substring(1).toLowerCase()),
) )
.map((app: any) => { .map((app: Application) => {
return { return {
label: app.name, label: app.name,
value: app.baseUrl, value: app.baseUrl,
@@ -257,7 +263,7 @@ export default function Search({
.toLowerCase() .toLowerCase()
.trim() .trim()
.split(' ') .split(' ')
.some((word: any) => word.includes(searchWord)), .some((word: string) => word.includes(searchWord)),
); );
}) })
.slice(0, 10); .slice(0, 10);
@@ -327,7 +333,7 @@ export default function Search({
classNames={actionIconClasses} classNames={actionIconClasses}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
deleteHistoryItem({ mutateAsync({
method: 'DELETE', method: 'DELETE',
endpoint: 'api/searchHistory', endpoint: 'api/searchHistory',
body: { id: item.option.id }, body: { id: item.option.id },
@@ -417,7 +423,7 @@ export default function Search({
const option = `${item.prefix} ${item.searchTerm}`; const option = `${item.prefix} ${item.searchTerm}`;
const words = option.toLowerCase().trim().split(' '); const words = option.toLowerCase().trim().split(' ');
return splittedSearch.every((searchWord) => return splittedSearch.every((searchWord) =>
words.some((word: any) => word.includes(searchWord)), words.some((word: string) => word.includes(searchWord)),
); );
}) })
.slice(0, 10); .slice(0, 10);
@@ -482,20 +488,19 @@ export default function Search({
setSearchValue={setSearchValue} setSearchValue={setSearchValue}
setProviderLabel={setProviderLabel} setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL} setProviderURL={setProviderURL}
providerModalOpened={providerModalOpened}
applicationData={applicationData} applicationData={applicationData}
providerData={providerData} providerData={providerData}
providerModalOpened={providerModalOpened}
closeProviderModal={closeProviderModal} closeProviderModal={closeProviderModal}
/> />
<WeatherModal <WeatherModal
current={current}
extendedForecast={extendedForecast}
weatherModalOpened={weatherModalOpened}
setSearchValue={setSearchValue} setSearchValue={setSearchValue}
setProviderLabel={setProviderLabel} setProviderLabel={setProviderLabel}
setProviderURL={setProviderURL} setProviderURL={setProviderURL}
current={current}
outlook={outlook}
extendedForecast={extendedForecast}
closeWeatherModal={closeWeatherModal} closeWeatherModal={closeWeatherModal}
weatherModalOpened={weatherModalOpened}
/> />
</Stack> </Stack>
); );

View File

@@ -38,7 +38,7 @@ function SettingsButton() {
const { data: themePlaylists } = useQueryAPI({ const { data: themePlaylists } = useQueryAPI({
endpoint: 'api/themePlaylists', endpoint: 'api/themePlaylists',
}); });
const { mutateAsync: wakeRequest } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const [allThemeData, setAllThemeData] = useState<Theme[]>([]); const [allThemeData, setAllThemeData] = useState<Theme[]>([]);
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]); const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
const [selectedClient, setSelectedClient] = useState<WolClient>(wolClients?.[0]); const [selectedClient, setSelectedClient] = useState<WolClient>(wolClients?.[0]);
@@ -64,7 +64,7 @@ function SettingsButton() {
setCurrentThemePlaylist, setCurrentThemePlaylist,
} = useCommonHooks(); } = useCommonHooks();
const handleWakeRequest = () => { const handleWakeRequest = () => {
wakeRequest({ mutateAsync({
method: 'POST', method: 'POST',
endpoint: 'api/wakeClient', endpoint: 'api/wakeClient',
body: selectedClient || wolClients?.[0], body: selectedClient || wolClients?.[0],
@@ -151,7 +151,7 @@ function SettingsButton() {
defaultValue={wolClientsData?.[0]?.hostname} defaultValue={wolClientsData?.[0]?.hostname}
data={ data={
wolClientsData wolClientsData
?.map((wolClient: any) => { ?.map((wolClient: WolClient) => {
return { return {
value: wolClient.hostname, value: wolClient.hostname,
label: `${wolClient.hostname} (${wolClient.macAddress})`, label: `${wolClient.hostname} (${wolClient.macAddress})`,
@@ -241,7 +241,7 @@ function SettingsButton() {
label: playlist.name, label: playlist.name,
}; };
}) })
.toSorted((a: any, b: any) => a.label.localeCompare(b.label)) || [] .toSorted((a: any, b: any) => a.label.localeCompare(b.label)) ?? []
} }
w='92%' w='92%'
classNames={{ classNames={{

View File

@@ -27,7 +27,7 @@ export function Settings() {
const { tabValue } = useParams(); const { tabValue } = useParams();
return ( return (
<Stack h={'100%'} gap={0}> <Stack h={'100%'} gap={0}>
<Group pos={'relative'} gap={0} pb={'1rem'}> <Group gap={0} style={{ position: 'relative', paddingBottom: '1rem' }}>
<ActionIcon <ActionIcon
className={actionIconClasses.backButton} className={actionIconClasses.backButton}
classNames={actionIconClasses} classNames={actionIconClasses}
@@ -51,9 +51,6 @@ export function Settings() {
onChange={(value) => onChange={(value) =>
navigate(`/${import.meta.env.VITE_BASE_URL}/settings/${value}`) navigate(`/${import.meta.env.VITE_BASE_URL}/settings/${value}`)
} }
style={{
overflow: 'hidden',
}}
> >
<Tabs.List style={{ justifyContent: isMobile ? 'space-between' : undefined }}> <Tabs.List style={{ justifyContent: isMobile ? 'space-between' : undefined }}>
<Tabs.Tab px={isMobile ? '.9rem' : undefined} value='personalization'> <Tabs.Tab px={isMobile ? '.9rem' : undefined} value='personalization'>

View File

@@ -45,7 +45,7 @@ const ApplicationSettings = () => {
}); });
const [applicationData, setApplicationData] = useState<Application[]>([]); const [applicationData, setApplicationData] = useState<Application[]>([]);
const [applicationList, setApplicationList] = useState<Application[]>([]); const [applicationList, setApplicationList] = useState<Application[]>([]);
const { theme, isMobile } = useCommonHooks(); const { theme, isMobile, reorder } = useCommonHooks();
const [newApplication, setNewApplication] = useState<Application>(defaultApplication); const [newApplication, setNewApplication] = useState<Application>(defaultApplication);
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const [searchValue, setSearchValue] = useState(''); const [searchValue, setSearchValue] = useState('');
@@ -53,13 +53,6 @@ const ApplicationSettings = () => {
const [dragging, setDragging] = useState(false); const [dragging, setDragging] = useState(false);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const reorder = (list: any, startIndex: any, endIndex: any) => {
const result = Array.from(list);
const [removed]: any = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
function onDragEnd(result: any) { function onDragEnd(result: any) {
if (!result.destination) { if (!result.destination) {
return; return;

View File

@@ -28,19 +28,12 @@ const BookmarkSettings = () => {
const [groups, setGroups] = useState<BookmarkGroup[]>([]); const [groups, setGroups] = useState<BookmarkGroup[]>([]);
const [newGroup, setNewGroup] = useState<BookmarkGroup>(defaultGroup); const [newGroup, setNewGroup] = useState<BookmarkGroup>(defaultGroup);
const [groupList, setGroupList] = useState<BookmarkGroup[]>([]); const [groupList, setGroupList] = useState<BookmarkGroup[]>([]);
const { theme, isMobile } = useCommonHooks(); const { theme, isMobile, reorder } = useCommonHooks();
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const [searchValue, setSearchValue] = useState(''); const [searchValue, setSearchValue] = useState('');
const [debouncedGroupSearch] = useDebouncedValue(searchValue, 500); const [debouncedGroupSearch] = useDebouncedValue(searchValue, 500);
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false); const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const reorder = (list: any, startIndex: any, endIndex: any) => {
const result = Array.from(list);
const [removed]: any = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const handleClose = () => { const handleClose = () => {
setNewGroup(defaultGroup); setNewGroup(defaultGroup);
closeModal(); closeModal();

View File

@@ -27,7 +27,7 @@ function DebouncedGroupSettingsItem({
bookmarks, bookmarks,
searchTerm, searchTerm,
}: { bookmarkGroup: BookmarkGroup; bookmarks: Bookmark[]; searchTerm: string }) { }: { bookmarkGroup: BookmarkGroup; bookmarks: Bookmark[]; searchTerm: string }) {
const { theme, isMobile, handleDelete } = useCommonHooks(); const { theme, isMobile, handleDelete, reorder } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false); const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const [popoverOpened, setPopoverOpened] = useState(false); const [popoverOpened, setPopoverOpened] = useState(false);
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
@@ -44,12 +44,6 @@ function DebouncedGroupSettingsItem({
const [bookmarkList, setBookmarkList] = useState<Bookmark[]>(bookmarks); const [bookmarkList, setBookmarkList] = useState<Bookmark[]>(bookmarks);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const reorder = (list: any, startIndex: any, endIndex: any) => {
const result = Array.from(list);
const [removed]: any = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
function onDragEnd(result: any) { function onDragEnd(result: any) {
if (!result.destination) { if (!result.destination) {
return; return;

View File

@@ -62,7 +62,10 @@ const PersonalizationSettings = () => {
setSearchValue={() => {}} setSearchValue={() => {}}
searchPlaceholder={undefined} searchPlaceholder={undefined}
/> />
<Stack mt={'6rem'} mb={'1rem'} w={'100%'} align='center'> <Stack
align='center'
style={{ marginTop: '6rem', marginBottom: '1rem', width: '100%' }}
>
<Grid w={'75%'} pos={'relative'}> <Grid w={'75%'} pos={'relative'}>
{personalIsFetching && ( {personalIsFetching && (
<Overlay <Overlay

View File

@@ -55,6 +55,7 @@
} }
.fadeAway { .fadeAway {
position: fixed;
background-color: #1c1b22; background-color: #1c1b22;
transition: all .5s ease-in-out; transition: all .5s ease-in-out;
height: 100vh; height: 100vh;
@@ -62,7 +63,7 @@
opacity: 1; opacity: 1;
display: flex; display: flex;
z-index: 100; z-index: 100;
backdrop-filter: blur(5px); backdrop-filter: blur(15px);
&[data-loaded="true"] { &[data-loaded="true"] {
opacity: 0; opacity: 0;
background-color: var(--color-background); background-color: var(--color-background);

View File

@@ -31,4 +31,7 @@
.list::before { .list::before {
border-color: var(--color-text-acc); border-color: var(--color-text-acc);
} }
&.root {
overflow: hidden;
}
} }

View File

@@ -0,0 +1,6 @@
.weatherIcon {
height: auto !important;
width: auto !important;
color: var(--color-text-pri) !important;
background: transparent !important;
}

View File

@@ -12,6 +12,7 @@ import switchClasses from './Switch.module.css';
import tabClasses from './Tabs.module.css'; import tabClasses from './Tabs.module.css';
import textClasses from './Text.module.css'; import textClasses from './Text.module.css';
import textInputClasses from './TextInput.module.css'; import textInputClasses from './TextInput.module.css';
import themeIconClasses from './ThemeIcon.module.css';
export { export {
actionIconClasses, actionIconClasses,
@@ -28,4 +29,5 @@ export {
tabClasses, tabClasses,
textClasses, textClasses,
textInputClasses, textInputClasses,
themeIconClasses,
}; };