adding localization for identity component

This commit is contained in:
Roland Osborne 2024-02-14 23:25:11 -08:00
parent 696dace1bb
commit a6970986d1
8 changed files with 119 additions and 76 deletions

View File

@ -62,7 +62,7 @@ export const DarkTheme = {
headerArea: '#111111', headerArea: '#111111',
footerArea: '#111111', footerArea: '#111111',
itemArea: '#222222', itemArea: '#222222',
hoverArea: '#444444', hoverArea: '#2f2f2f',
selectedArea: '#333333', selectedArea: '#333333',
enabledArea: '#448866', enabledArea: '#448866',
disabledArea: '#888888', disabledArea: '#888888',

View File

@ -0,0 +1,26 @@
export const en = {
code: 'en',
account: 'Account',
contacts: 'Contacts',
logout: 'Logout',
confirmLogout: 'Are you sure you want to logout?',
contactsUpdated: 'Updated contact status',
disconnected: 'Disconnected from server',
allDevices: 'Logout of all devices',
ok: 'OK',
cancel: 'Cancel',
};
export const fr = {
code: 'fr',
account: 'Compte',
contacts: 'Contacts',
logout: 'Déconnexion',
confirmLogout: 'Êtes-vous sûr de vouloir vous déconnecter?',
contactsUpdated: 'Vos contacts ont changer',
disconnected: 'Déconnecté du serveur',
allDevices: 'Déconnexion de tous les appareils',
ok: 'OK',
cancel: 'Annuler',
};

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { LightTheme, DarkTheme } from 'constants/Colors'; import { LightTheme, DarkTheme } from 'constants/Colors';
import { en, fr } from 'constants/Strings';
export function useSettingsContext() { export function useSettingsContext() {
@ -7,8 +8,11 @@ export function useSettingsContext() {
display: null, display: null,
width: null, width: null,
height: null, height: null,
darkTheme: DarkTheme, theme: null,
lightTheme: LightTheme, colors: {},
menuStyle: {},
language: 'en',
strings: en,
}); });
const SMALL_MEDIUM = 650; const SMALL_MEDIUM = 650;
@ -44,13 +48,26 @@ export function useSettingsContext() {
const scheme = localStorage.getItem('color_scheme'); const scheme = localStorage.getItem('color_scheme');
if (scheme === 'dark') { if (scheme === 'dark') {
updateState({ darkTheme: DarkTheme, lightTheme: DarkTheme }); updateState({ theme: scheme, colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.headerArea, color: DarkTheme.mainText } });
} }
else if (scheme === 'light') { else if (scheme === 'light') {
updateState({ darkTheme: LightTheme, lightTheme: LightTheme }); updateState({ theme: scheme, colors: LightTheme, menuStyle: { backgroundColor: LightTheme.headerArea, color: LightTheme.mainText } })
} }
else { else {
updateState({ darkTheme: DarkTheme, lightTheme: LightTheme }); if(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
updateState({ theme: null, colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.headerArea, color: DarkTheme.mainText } });
}
else {
updateState({ theme: null, colors: LightTheme, menuStyle: { backgroundColor: LightTheme.headerArea, color: LightTheme.mainText } });
}
}
const language = localStorage.getItem('language');
if (language && language.startsWith('fr')) {
updateState({ language: 'fr', strings: fr });
}
else {
updateState({ language: 'en', strings: en });
} }
return () => { return () => {
@ -61,18 +78,32 @@ export function useSettingsContext() {
}, []); }, []);
const actions = { const actions = {
setDarkTheme() { setDarkTheme: () => {
localStorage.setItem('color_scheme', 'dark'); localStorage.setItem('color_scheme', 'dark');
updateState({ darkTheme: DarkTheme, lightTheme: DarkTheme }); updateState({ theme: 'dark', colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.headerArea, color: DarkTheme.mainText } });
}, },
setLightTheme() { setLightTheme : () => {
localStorage.setItem('color_scheme', 'light'); localStorage.setItem('color_scheme', 'light');
updateState({ darkTheme: LightTheme, lightTheme: LightTheme }); updateState({ theme: 'light', colors: LightTheme, menuStyle: { backgroundColor: LightTheme.headerArea, color: LightTheme.mainText } });
}, },
steDefaultTheme() { setDefaultTheme: () => {
localStorage.clearItem('color_scheme'); localStorage.clearItem('color_scheme');
updateState({ darkTheme: DarkTheme, lightTheme: LightTheme }); if(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
updateState({ theme: null, colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.headerArea, color: DarkTheme.mainText } });
} }
else {
updateState({ theme: null, colors: LightTheme, menuStyle: { backgroundColor: LightTheme.headerArea, color: LightTheme.mainText } });
}
},
setLanguage: (code: string) => {
localStorage.setItem('language', code);
if (code && code.startsWith('fr')) {
updateState({ language: 'fr', strings: fr });
}
else {
updateState({ language: 'en', strings: en });
}
},
} }
return { state, actions } return { state, actions }

View File

@ -14,6 +14,14 @@
padding-bottom: 16px !important; padding-bottom: 16px !important;
} }
.ant-switch {
background: #888888 !important;
}
.ant-switch-checked {
background: #448866 !important;
}
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',

View File

@ -159,7 +159,7 @@ export function Session() {
} }
return ( return (
<ThemeProvider theme={{ light: settings.state.lightTheme, dark: settings.state.darkTheme }}> <ThemeProvider theme={settings.state.colors}>
<SessionWrapper> <SessionWrapper>
{ (state.display === 'xlarge') && ( { (state.display === 'xlarge') && (
<div class="desktop-layout noselect"> <div class="desktop-layout noselect">

View File

@ -13,30 +13,32 @@ export function Identity({ openAccount, openCards, cardUpdated }) {
const logout = () => { const logout = () => {
modal.confirm({ modal.confirm({
title: 'Are you sure you want to logout?', title: <span style={state.menuStyle}>{state.strings.confirmLogout}</span>,
icon: <LogoutOutlined />, icon: <LogoutOutlined />,
content: <LogoutContent onClick={(e) => e.stopPropagation()}> content: <LogoutContent onClick={(e) => e.stopPropagation()}>
<span className="logoutMode">Logout of All Devices </span> <span className="logoutMode">{ state.strings.allDevices }</span>
<Switch onChange={(e) => {all.current = e}} size="small" /> <Switch onChange={(e) => all.current = e} size="small" />
</LogoutContent>, </LogoutContent>,
bodyStyle: { padding: 16 }, bodyStyle: { padding: 16, ...state.menuStyle },
okText: state.strings.ok,
onOk() { onOk() {
actions.logout(all.current); actions.logout(all.current);
}, },
cancelText: state.strings.cancel,
onCancel() {}, onCancel() {},
}); });
} }
const menu = ( const menu = (
<Menu> <Menu style={state.menuStyle}>
<Menu.Item key="0"> <Menu.Item style={state.menuStyle} key="0">
<div onClick={openAccount}>Account</div> <div onClick={openAccount}>{ state.strings.account }</div>
</Menu.Item> </Menu.Item>
<Menu.Item key="1"> <Menu.Item style={state.menuStyle} key="1">
<div onClick={openCards}>Contacts</div> <div onClick={openCards}>{ state.strings.contacts }</div>
</Menu.Item> </Menu.Item>
<Menu.Item key="2"> <Menu.Item style={state.menuStyle} key="2">
<div onClick={logout}>Logout</div> <div onClick={logout}>{ state.strings.logout }</div>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
); );
@ -48,12 +50,12 @@ export function Identity({ openAccount, openCards, cardUpdated }) {
{ state.init && ( { state.init && (
<Logo url={state.url} width={40} height={40} radius={4} /> <Logo url={state.url} width={40} height={40} radius={4} />
)} )}
<div class="label"> <div className="label">
<div class="name">{state.name}</div> <div className="name">{state.name}</div>
<div class="handle"> <div className="handle">
<div class="notice"> <div className="notice">
{ state.status !== 'connected' && ( { state.status !== 'connected' && (
<Tooltip placement="right" title="disconnected from server"> <Tooltip placement="right" title={state.strings.disconnected}>
<ErrorNotice> <ErrorNotice>
<ExclamationCircleOutlined /> <ExclamationCircleOutlined />
</ErrorNotice> </ErrorNotice>
@ -61,9 +63,9 @@ export function Identity({ openAccount, openCards, cardUpdated }) {
)} )}
</div> </div>
<div>{state.handle}</div> <div>{state.handle}</div>
<div class="notice"> <div className="notice">
{ cardUpdated && ( { cardUpdated && (
<Tooltip placement="right" title="contacts have updated"> <Tooltip placement="right" title={state.strings.contactsUpdated}>
<InfoNotice> <InfoNotice>
<InfoCircleOutlined /> <InfoCircleOutlined />
</InfoNotice> </InfoNotice>
@ -72,7 +74,7 @@ export function Identity({ openAccount, openCards, cardUpdated }) {
</div> </div>
</div> </div>
</div> </div>
<div class="drop"> <div className="drop">
<DownOutlined /> <DownOutlined />
</div> </div>
</IdentityWrapper> </IdentityWrapper>

View File

@ -1,5 +1,4 @@
import styled from 'styled-components'; import styled from 'styled-components';
import { Colors } from 'constants/Colors';
export const IdentityWrapper = styled.div` export const IdentityWrapper = styled.div`
width: 100%; width: 100%;
@ -9,26 +8,14 @@ export const IdentityWrapper = styled.div`
align-items: center; align-items: center;
padding-left: 16px; padding-left: 16px;
padding-right: 16px; padding-right: 16px;
@media (prefers-color-scheme: light) { background-color: ${props => props.theme.headerArea};
background-color: ${props => props.theme.light.headerArea}; border-bottom: 1px solid ${props => props.theme.sectionLine};
border-bottom: 1px solid ${props => props.theme.light.sectionLine}; color: ${props => props.theme.mainText};
color: ${props => props.theme.light.mainText};
}
@media (prefers-color-scheme: dark) {
background-color: ${props => props.theme.dark.headerArea};
border-bottom: 1px solid ${props => props.theme.dark.sectionLine};
color: ${props => props.theme.dark.mainText};
}
flex-shrink: 0; flex-shrink: 0;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
@media (prefers-color-scheme: light) { background-color: ${props => props.theme.hoverArea};
background-color: ${props => props.theme.light.hoverArea};
}
@media (prefers-color-scheme: dark) {
background-color: ${props => props.theme.dark.hoverArea};
}
.drop { .drop {
font-weight: bold; font-weight: bold;
@ -39,14 +26,8 @@ export const IdentityWrapper = styled.div`
padding-left: 4px; padding-left: 4px;
padding-right: 4px; padding-right: 4px;
border-radius: 8px; border-radius: 8px;
@media (prefers-color-scheme: light) { border: 1px solid ${props => props.theme.sectionLine};
border: 1px solid ${props => props.theme.light.sectionLine}; color: ${props => props.theme.mainText};
color: ${props => props.theme.light.mainText};
}
@media (prefers-color-scheme: dark) {
border: 1px solid ${props => props.theme.dark.sectionLine};
color: ${props => props.theme.dark.mainText};
}
} }
.label { .label {
@ -88,30 +69,15 @@ export const LogoutContent = styled.div`
.logoutMode { .logoutMode {
padding-right: 8px; padding-right: 8px;
@media (prefers-color-scheme: light) { color: ${props => props.theme.mainText};
color: ${props => props.theme.light.mainText};
}
@media (prefers-color-scheme: dark) {
color: ${props => props.theme.dark.mainText};
}
} }
` `
export const ErrorNotice = styled.div` export const ErrorNotice = styled.div`
@media (prefers-color-scheme: light) { color: ${props => props.theme.alertText};
color: ${props => props.theme.light.alertText};
}
@media (prefers-color-scheme: dark) {
color: ${props => props.theme.dark.alertText};
}
` `
export const InfoNotice = styled.div` export const InfoNotice = styled.div`
@media (prefers-color-scheme: light) { color: ${props => props.theme.linkText};
color: ${props => props.theme.light.linkText};
}
@media (prefers-color-scheme: dark) {
color: ${props => props.theme.dark.linkText};
}
` `

View File

@ -1,6 +1,7 @@
import { useState, useEffect, useContext } from 'react'; import { useState, useEffect, useContext } from 'react';
import { ProfileContext } from 'context/ProfileContext'; import { ProfileContext } from 'context/ProfileContext';
import { AppContext } from 'context/AppContext'; import { AppContext } from 'context/AppContext';
import { SettingsContext } from 'context/SettingsContext';
export function useIdentity() { export function useIdentity() {
@ -10,10 +11,14 @@ export function useIdentity() {
handle: null, handle: null,
status: null, status: null,
init: false, init: false,
strings: {},
colors: {},
menuStyle: {},
}); });
const app = useContext(AppContext); const app = useContext(AppContext);
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
const settings = useContext(SettingsContext);
const updateState = (value) => { const updateState = (value) => {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
@ -32,6 +37,11 @@ export function useIdentity() {
updateState({ status }); updateState({ status });
}, [app.state]); }, [app.state]);
useEffect(() => {
const { colors, strings, menuStyle } = settings.state;
updateState({ colors, strings, menuStyle });
}, [settings.state]);
const actions = { const actions = {
logout: (all) => { logout: (all) => {
app.actions.logout(all); app.actions.logout(all);