From 43cefe19bb7fd6d009d4db5e4385b911e2e177c8 Mon Sep 17 00:00:00 2001 From: Tristan Teufel Date: Mon, 4 Mar 2024 17:53:23 +0100 Subject: [PATCH] Refactor UI components and add localization support, Extract LocalStorage Hook, Fixed Styled Props other then string --- src/app/ChannelsForType.tsx | 14 +++++---- src/app/Login.tsx | 2 +- src/app/components/StyledIcons.tsx | 6 ++-- src/app/controls/DoorControl.tsx | 18 +++++++----- src/app/controls/RainDetectionControl.tsx | 36 +++++++++++++---------- src/app/controls/SwitchControl.tsx | 11 ++++--- src/app/controls/ThermostatControl.tsx | 2 +- src/hooks/useLocalStorage.tsx | 25 ++++++++++++++++ src/i18n/utils.ts | 12 ++++++++ 9 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 src/hooks/useLocalStorage.tsx diff --git a/src/app/ChannelsForType.tsx b/src/app/ChannelsForType.tsx index 2e2d5f9..c4d5cdb 100644 --- a/src/app/ChannelsForType.tsx +++ b/src/app/ChannelsForType.tsx @@ -18,14 +18,17 @@ import { Icon } from '@iconify/react'; import { Channel, ChannelType } from './../types/types'; import { RainDetectionControl } from './controls/RainDetectionControl'; import { DoorControl } from './controls/DoorControl'; +import { useLocalStorage } from './../hooks/useLocalStorage'; interface ExpandMoreProps { - expanded: string; + expanded: boolean; } -const ExpandMore = styled(Icon)(({ expanded }) => ({ - transform: expanded === 'true' ? 'rotate(180deg)' : 'rotate(0deg)', +const ExpandMore = styled(Icon, { + shouldForwardProp: (prop) => prop !== 'expanded', +})(({ expanded }) => ({ + transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)', marginLeft: 'auto', transition: 'transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', fontSize: '25px', @@ -86,10 +89,9 @@ export const ChannelsForType: React.FC = ({ const [hasTransitionExited, setHasTransitionExited] = useState< boolean >(true); - const [expanded, setExpanded] = useState(localStorage.getItem(channelType) === 'true'); + const [expanded, setExpanded] = useLocalStorage(channelType, false); const handleExpandClick = () => { - localStorage.setItem(channelType, (!expanded).toString()); setExpanded(!expanded) }; @@ -113,7 +115,7 @@ export const ChannelsForType: React.FC = ({ > {t(channelType)} - + {hasTransitionExited ? : null} diff --git a/src/app/Login.tsx b/src/app/Login.tsx index ad736ce..243b6e0 100644 --- a/src/app/Login.tsx +++ b/src/app/Login.tsx @@ -59,7 +59,7 @@ export const Login = () => { diff --git a/src/app/components/StyledIcons.tsx b/src/app/components/StyledIcons.tsx index 4e2751f..c6b7599 100644 --- a/src/app/components/StyledIcons.tsx +++ b/src/app/components/StyledIcons.tsx @@ -3,11 +3,11 @@ import { styled } from "@mui/material"; interface StyledIconButtonProps { icon: string; - active?: string; + active?: boolean; } -export const StyledIconButton = styled(Icon)` - color: ${(props) => (props.active === 'true' ? '#0077B6' : '#000')}; +export const StyledIconButton = styled(Icon, { shouldForwardProp: (prop) => prop !== 'active', })` + color: ${(props) => (props.active ? '#0077B6' : '#000')}; font-size: 40px; background-color: lightGrey; border-radius: 10px; diff --git a/src/app/controls/DoorControl.tsx b/src/app/controls/DoorControl.tsx index 6d0a573..7c5da04 100644 --- a/src/app/controls/DoorControl.tsx +++ b/src/app/controls/DoorControl.tsx @@ -3,6 +3,7 @@ import { StyledIconButton } from '../components/StyledIcons'; import { KeymaticChannel } from './../../types/types'; import { useSetValueMutation } from './../../hooks/useApi'; import { styled } from '@mui/system'; +import { useTranslations } from './../../i18n/utils'; const StyledOuterBox = styled(Box)({ display: 'flex', @@ -17,10 +18,12 @@ const StyledOuterBox = styled(Box)({ }); interface StyledTypographyProps { - uncertain: string; + uncertain: boolean } - const StyledTypography = styled(Typography)(({ uncertain }) => ({ - display: uncertain === 'true' ? 'block' : 'none', + const StyledTypography = styled(Typography, { + shouldForwardProp: (prop) => prop !== 'uncertain', + })(({ uncertain }) => ({ + display: uncertain ? 'block' : 'none', })); interface DoorControlProps { @@ -29,6 +32,7 @@ interface DoorControlProps { } export const DoorControl: React.FC = ({ channel, refetch }) => { + const t = useTranslations(); const setValueMutation = useSetValueMutation(); const { datapoints: { STATE, STATE_UNCERTAIN }, @@ -77,12 +81,12 @@ export const DoorControl: React.FC = ({ channel, refetch }) => = ({ channel, refetch }) => /> - Door state is uncertain + {t('DOOR_STATE_UNKNOWN')} } diff --git a/src/app/controls/RainDetectionControl.tsx b/src/app/controls/RainDetectionControl.tsx index 7beef5a..de8693c 100644 --- a/src/app/controls/RainDetectionControl.tsx +++ b/src/app/controls/RainDetectionControl.tsx @@ -1,21 +1,21 @@ import { Box, CardHeader, Typography, styled } from '@mui/material'; import { RainDetectionTransmitterChannel } from './../../types/types'; import { StyledHeaderIcon } from '../components/StyledIcons'; +import { useTranslations } from './../../i18n/utils'; const StyledBox = styled(Box)({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - width: '100%', - gap: '20px' - }); - - const StyledIconBox = styled(Box)({ - display: 'flex', - alignItems: 'center', - gap: '5px', - }); - + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + width: '100%', + gap: '20px', +}); + +const StyledIconBox = styled(Box)({ + display: 'flex', + alignItems: 'center', + gap: '5px', +}); interface RainDetectionControlProps { channel: RainDetectionTransmitterChannel; @@ -30,6 +30,8 @@ export const RainDetectionControl: React.FC = ({ const isRaining = RAINING === 'true'; const isHeating = HEATER_STATE === 'true'; + + const t = useTranslations(); return ( = ({ ) : ( )} - {isRaining ? 'Raining' : 'Not Raining'} + + {isRaining ? t('RAINING') : t('NOT_RAINING')} + {isHeating ? ( @@ -52,7 +56,9 @@ export const RainDetectionControl: React.FC = ({ ) : ( )} - {isHeating ? 'Heating' : 'Not Heating'} + + {isHeating ? t('HEATING') : t('NOT_HEATING')} + } diff --git a/src/app/controls/SwitchControl.tsx b/src/app/controls/SwitchControl.tsx index c5469e1..e1ca93e 100644 --- a/src/app/controls/SwitchControl.tsx +++ b/src/app/controls/SwitchControl.tsx @@ -1,8 +1,6 @@ import { styled } from '@mui/system'; import { Box } from '@mui/material'; -import { - useSetValueMutation, -} from '../../hooks/useApi'; +import { useSetValueMutation } from '../../hooks/useApi'; import { ChannelHeader } from '../components/ChannelHeader'; import { SwitchVirtualReceiverChannel } from 'src/types/types'; @@ -38,7 +36,12 @@ export const SwitchControl = ({ channel, refetch }: ControlProps) => { return ( - + ); }; diff --git a/src/app/controls/ThermostatControl.tsx b/src/app/controls/ThermostatControl.tsx index fa48d30..117f153 100644 --- a/src/app/controls/ThermostatControl.tsx +++ b/src/app/controls/ThermostatControl.tsx @@ -12,7 +12,7 @@ interface ControlProps { channel: HeatingClimateControlTransceiverChannel; } -const StyledButton = styled('button')(({ theme }) => ({ +const StyledButton = styled('button')(() => ({ backgroundColor: 'lightgrey', fontWeight: 'bold', borderRadius: '10px', diff --git a/src/hooks/useLocalStorage.tsx b/src/hooks/useLocalStorage.tsx new file mode 100644 index 0000000..7d45867 --- /dev/null +++ b/src/hooks/useLocalStorage.tsx @@ -0,0 +1,25 @@ +import { useState } from 'react'; + +export const useLocalStorage = (key: string, initialValue: boolean): [boolean, (value: boolean) => void] => { + + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + console.log(error); + return initialValue; + } + }); + + const setValue = (value: boolean) => { + try { + setStoredValue(value); + window.localStorage.setItem(key, JSON.stringify(value)); + } catch (error) { + console.log(error); + } + }; + + return [storedValue, setValue]; +} \ No newline at end of file diff --git a/src/i18n/utils.ts b/src/i18n/utils.ts index 28147d5..f70205c 100644 --- a/src/i18n/utils.ts +++ b/src/i18n/utils.ts @@ -18,6 +18,12 @@ export const ui = { KEYMATIC: 'Keymatic', errorOccuredWhileLogin: 'Error occured while login', signInTitle: 'Sign in', + RAINING: 'Raining', + NOT_RAINING: 'Not Raining', + HEATING: 'Heating', + NOT_HEATING: 'Not Heating', + DOOR_STATE_UNKNOWN: 'Door state is uncertain', + SIGN_IN: 'Sign in', }, de: { SWITCH_VIRTUAL_RECEIVER: 'Schalter', @@ -28,6 +34,12 @@ export const ui = { KEYMATIC: 'Keymatic', errorOccuredWhileLogin: 'Fehler beim Login', signInTitle: 'Anmelden', + RAINING: 'Regen', + NOT_RAINING: 'Kein Regen', + HEATING: 'Heizen', + NOT_HEATING: 'Nicht Heizen', + DOOR_STATE_UNKNOWN: 'Türzustand ist ungewiss', + SIGN_IN: 'Anmelden', }, } as const;