✨(frontend) new settings dialog to handle user account/language
add a settings button directly in the homepage to change language or see user account settings
This commit is contained in:
@@ -231,7 +231,7 @@ const config: Config = {
|
|||||||
DEFAULT: { value: '{spacing.1}' },
|
DEFAULT: { value: '{spacing.1}' },
|
||||||
lg: { value: '{spacing.2}' },
|
lg: { value: '{spacing.2}' },
|
||||||
},
|
},
|
||||||
paragraph: { value: '{spacing.1}' },
|
paragraph: { value: '{spacing.0.5}' },
|
||||||
heading: { value: '{spacing.1}' },
|
heading: { value: '{spacing.1}' },
|
||||||
gutter: { value: '{spacing.1}' },
|
gutter: { value: '{spacing.1}' },
|
||||||
textfield: { value: '{spacing.1}' },
|
textfield: { value: '{spacing.1}' },
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { authUrl, useUser } from '@/features/auth'
|
|||||||
import { navigateToNewRoom } from '@/features/rooms'
|
import { navigateToNewRoom } from '@/features/rooms'
|
||||||
import { Screen } from '@/layout/Screen'
|
import { Screen } from '@/layout/Screen'
|
||||||
import { JoinMeetingDialogContent } from '../components/JoinMeetingDialogContent'
|
import { JoinMeetingDialogContent } from '../components/JoinMeetingDialogContent'
|
||||||
|
import { SettingsButton } from '@/features/settings'
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const { t } = useTranslation('home')
|
const { t } = useTranslation('home')
|
||||||
@@ -39,6 +40,8 @@ export const Home = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<JoinMeetingDialogContent />
|
<JoinMeetingDialogContent />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<SettingsButton />
|
||||||
</HStack>
|
</HStack>
|
||||||
</Div>
|
</Div>
|
||||||
</VerticallyOffCenter>
|
</VerticallyOffCenter>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiSettings3Line } from '@remixicon/react'
|
||||||
|
import { Dialog, Button } from '@/primitives'
|
||||||
|
import { SettingsDialog } from './SettingsDialog'
|
||||||
|
|
||||||
|
export const SettingsButton = () => {
|
||||||
|
const { t } = useTranslation('settings')
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<Button square invisible aria-label={t('settingsButtonLabel')}>
|
||||||
|
<RiSettings3Line />
|
||||||
|
</Button>
|
||||||
|
<SettingsDialog />
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import { useLanguageLabels } from '@/i18n/useLanguageLabels'
|
||||||
|
import { A, Badge, DialogContent, Field, H, P } from '@/primitives'
|
||||||
|
import { authUrl, logoutUrl, useUser } from '@/features/auth'
|
||||||
|
|
||||||
|
export const SettingsDialog = () => {
|
||||||
|
const { t, i18n } = useTranslation('settings')
|
||||||
|
const { user, isLoggedIn } = useUser()
|
||||||
|
const { languagesList, currentLanguage } = useLanguageLabels()
|
||||||
|
return (
|
||||||
|
<DialogContent title={t('dialog.heading')}>
|
||||||
|
<H lvl={2}>{t('account.heading')}</H>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<>
|
||||||
|
<P>
|
||||||
|
<Trans
|
||||||
|
i18nKey="settings:account.currentlyLoggedAs"
|
||||||
|
values={{ user: user?.email }}
|
||||||
|
components={[<Badge />]}
|
||||||
|
/>
|
||||||
|
</P>
|
||||||
|
<P>
|
||||||
|
<A href={logoutUrl()}>{t('logout', { ns: 'global' })}</A>
|
||||||
|
</P>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<P>{t('account.youAreNotLoggedIn')}</P>
|
||||||
|
<P>
|
||||||
|
<A href={authUrl()}>{t('login', { ns: 'global' })}</A>
|
||||||
|
</P>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<H lvl={2}>{t('language.heading')}</H>
|
||||||
|
<Field
|
||||||
|
type="select"
|
||||||
|
label={t('language.label')}
|
||||||
|
items={languagesList}
|
||||||
|
defaultSelectedKey={currentLanguage.key}
|
||||||
|
onSelectionChange={(lang) => {
|
||||||
|
i18n.changeLanguage(lang as string)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
src/frontend/src/features/settings/index.ts
Normal file
1
src/frontend/src/features/settings/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { SettingsButton } from './components/SettingsButton'
|
||||||
@@ -9,7 +9,7 @@ export const LanguageSelector = () => {
|
|||||||
<Popover aria-label={t('languageSelector.popoverLabel')}>
|
<Popover aria-label={t('languageSelector.popoverLabel')}>
|
||||||
<Button
|
<Button
|
||||||
aria-label={t('languageSelector.buttonLabel', {
|
aria-label={t('languageSelector.buttonLabel', {
|
||||||
currentLanguage,
|
currentLanguage: currentLanguage.label,
|
||||||
})}
|
})}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
|||||||
@@ -16,5 +16,11 @@ export const useLanguageLabels = () => {
|
|||||||
value: lang,
|
value: lang,
|
||||||
label: langageLabels[lang],
|
label: langageLabels[lang],
|
||||||
}))
|
}))
|
||||||
return { languagesList, currentLanguage: langageLabels[i18n.language] }
|
return {
|
||||||
|
languagesList,
|
||||||
|
currentLanguage: {
|
||||||
|
key: i18n.language,
|
||||||
|
label: langageLabels[i18n.language],
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/frontend/src/locales/de/settings.json
Normal file
15
src/frontend/src/locales/de/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"currentlyLoggedAs": "",
|
||||||
|
"heading": "",
|
||||||
|
"youAreNotLoggedIn": ""
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"heading": ""
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"heading": "",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"settingsButtonLabel": ""
|
||||||
|
}
|
||||||
15
src/frontend/src/locales/en/settings.json
Normal file
15
src/frontend/src/locales/en/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"currentlyLoggedAs": "You are currently logged in as <0>{{user}}</0>",
|
||||||
|
"heading": "Account",
|
||||||
|
"youAreNotLoggedIn": "You are not logged in."
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"heading": "Account and settings"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"heading": "Language",
|
||||||
|
"label": "Language"
|
||||||
|
},
|
||||||
|
"settingsButtonLabel": "Settings"
|
||||||
|
}
|
||||||
15
src/frontend/src/locales/fr/settings.json
Normal file
15
src/frontend/src/locales/fr/settings.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"currentlyLoggedAs": "Vous êtes actuellement connecté en tant que <0>{{user}}</0>",
|
||||||
|
"heading": "Compte",
|
||||||
|
"youAreNotLoggedIn": "Vous n'êtes pas connecté."
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"heading": "Compte et paramètres"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"heading": "Langue",
|
||||||
|
"label": "Langue de l'application"
|
||||||
|
},
|
||||||
|
"settingsButtonLabel": "Paramètres"
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import { cva, type RecipeVariantProps } from '@/styled-system/css'
|
|||||||
const button = cva({
|
const button = cva({
|
||||||
base: {
|
base: {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
transition: 'background 200ms, outline 200ms',
|
transition: 'background 200ms, outline 200ms, border-color 200ms',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
border: '1px solid transparent',
|
border: '1px solid transparent',
|
||||||
color: 'colorPalette.text',
|
color: 'colorPalette.text',
|
||||||
@@ -27,14 +27,23 @@ const button = cva({
|
|||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
paddingX: '1',
|
paddingX: '1',
|
||||||
paddingY: '0.625',
|
paddingY: '0.625',
|
||||||
|
'--square-padding': '{spacing.0.625}',
|
||||||
},
|
},
|
||||||
sm: {
|
sm: {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
paddingX: '0.5',
|
paddingX: '0.5',
|
||||||
paddingY: '0.25',
|
paddingY: '0.25',
|
||||||
|
'--square-padding': '{spacing.0.25}',
|
||||||
},
|
},
|
||||||
xs: {
|
xs: {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
|
'--square-padding': '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
square: {
|
||||||
|
true: {
|
||||||
|
paddingX: 'var(--square-padding)',
|
||||||
|
paddingY: 'var(--square-padding)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
@@ -50,10 +59,10 @@ const button = cva({
|
|||||||
color: 'colorPalette',
|
color: 'colorPalette',
|
||||||
backgroundColor: 'transparent!',
|
backgroundColor: 'transparent!',
|
||||||
borderColor: 'currentcolor!',
|
borderColor: 'currentcolor!',
|
||||||
'_ra-hover': {
|
'&[data-hovered]': {
|
||||||
backgroundColor: 'colorPalette.subtle!',
|
backgroundColor: 'colorPalette.subtle!',
|
||||||
},
|
},
|
||||||
'_ra-pressed': {
|
'&[data-pressed]': {
|
||||||
backgroundColor: 'colorPalette.subtle!',
|
backgroundColor: 'colorPalette.subtle!',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -62,6 +71,13 @@ const button = cva({
|
|||||||
true: {
|
true: {
|
||||||
borderColor: 'none!',
|
borderColor: 'none!',
|
||||||
backgroundColor: 'none!',
|
backgroundColor: 'none!',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'none!',
|
||||||
|
borderColor: 'currentcolor',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
borderColor: 'currentcolor',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user