all repos — caroster @ e5e05d4f020e8e0eff917468ce530cdbc16f7e40

[Octree] Group carpool to your event https://caroster.io

feat: ✨ Add Languages to EventMenu

#230
Simon Mulquin simon@octree.ch
Fri, 21 Jan 2022 08:53:21 +0000
commit

e5e05d4f020e8e0eff917468ce530cdbc16f7e40

parent

b4e4a01eef100beb301bf892d0050953223e5a3a

M frontend/containers/EventBar/index.tsxfrontend/containers/EventBar/index.tsx

@@ -15,7 +15,7 @@ import useEventStore from '../../stores/useEventStore';

import useTourStore from '../../stores/useTourStore'; import useProfile from '../../hooks/useProfile'; import useSettings from '../../hooks/useSettings'; -import EventMenu from '../EventMenu'; +import GenericMenu from '../GenericMenu'; import EventDetails from '../EventDetails'; const EventBar = ({event, onAdd, onSave, onShare}) => {

@@ -177,7 +177,7 @@ )}

</IconButton> </> )} - <EventMenu + <GenericMenu anchorEl={anchorEl} setAnchorEl={setAnchorEl} actions={[

@@ -242,8 +242,8 @@ width: theme.spacing(3),

height: theme.spacing(3), fontSize: 16, }, - withDivider: { - borderBottom: `1px solid ${theme.palette.divider}`, + shareIcon: { + marginRight: 0, }, }));
D frontend/containers/EventMenu/index.js

@@ -1,98 +0,0 @@

-import Menu from '@material-ui/core/Menu'; -import Divider from '@material-ui/core/Divider'; -import MenuItem from '@material-ui/core/MenuItem'; -import {makeStyles} from '@material-ui/core/styles'; -import {useTranslation} from 'react-i18next'; -import {Typography} from '@material-ui/core'; -import useAuthStore from '../../stores/useAuthStore'; -import useProfile from '../../hooks/useProfile'; -import useSettings from '../../hooks/useSettings'; - -const EventMenu = ({anchorEl, setAnchorEl, actions = []}) => { - const {t} = useTranslation(); - const settings = useSettings(); - const logout = useAuthStore(s => s.logout); - const {user} = useProfile(); - - const classes = useStyles(); - const logoutMenuItem = user && { - label: t('menu.logout'), - onClick: () => { - logout(); - window.location.href = settings['about_link']; - }, - id: 'LogoutTabs', - }; - const aboutMenuItem = { - label: t('menu.about'), - onClick: () => (window.location.href = settings['about_link']), - id: 'AboutTabs', - }; - - return ( - <Menu - anchorEl={anchorEl} - anchorOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - keepMounted - transformOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - open={!!anchorEl} - onClose={() => setAnchorEl(null)} - > - {actions && - [...actions, aboutMenuItem, logoutMenuItem] - .filter(Boolean) - .map(({label, id, onClick, divider = false, ...attributes}, idx) => { - if (divider) { - return ( - <Divider - key={idx} - variant="fullWidth" - className={classes.divider} - /> - ); - } - if (!!onClick) - return ( - <MenuItem - onClick={() => { - onClick(); - setAnchorEl(null); - }} - key={idx} - id={id || `MenuItem${idx}`} - {...attributes} - > - {label} - </MenuItem> - ); - - return ( - <Typography - variant="body1" - key={idx} - id={id || `MenuItem${idx}`} - className={classes.textItem} - > - {label} - </Typography> - ); - })} - </Menu> - ); -}; -const useStyles = makeStyles(theme => ({ - divider: { - margin: theme.spacing(1, 0), - }, - textItem: { - margin: theme.spacing(1, 2), - '&:focus': {outline: 0}, - }, -})); -export default EventMenu;
A frontend/containers/GenericMenu/Action.tsx

@@ -0,0 +1,58 @@

+import {ReactNode} from 'react'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; +import MenuItem from '@material-ui/core/MenuItem'; +import {makeStyles} from '@material-ui/core/styles'; + +type ActionType = { + divider?: boolean; + isComponentLabel?: boolean; + label: ReactNode | string; + id: string; + onClick: () => void; +}; + +interface Props { + action: ActionType; +} + +const Action = (props: Props) => { + const {action} = props; + const { + divider, + onClick, + id, + label, + isComponentLabel, + ...menuItemProps + } = action; + const classes = useStyles(); + + if (divider) + return <Divider variant="fullWidth" className={classes.divider} />; + else if (isComponentLabel) return action.label; + else if (onClick) + return ( + <MenuItem id={id} onClick={onClick} {...menuItemProps}> + {label} + </MenuItem> + ); + else + return ( + <Typography variant="body1" id={id} className={classes.textItem}> + {label} + </Typography> + ); +}; + +const useStyles = makeStyles(theme => ({ + divider: { + margin: theme.spacing(1, 0), + }, + textItem: { + margin: theme.spacing(1, 2), + '&:focus': {outline: 0}, + }, +})); + +export default Action;
A frontend/containers/GenericMenu/index.js

@@ -0,0 +1,67 @@

+import Menu from '@material-ui/core/Menu'; +import {useTranslation} from 'react-i18next'; +import useAuthStore from '../../stores/useAuthStore'; +import useProfile from '../../hooks/useProfile'; +import useSettings from '../../hooks/useSettings'; +import Languages from '../Languages'; +import Action from './Action'; + +const GenericMenu = ({anchorEl, setAnchorEl, actions = []}) => { + const {t} = useTranslation(); + const settings = useSettings(); + const logout = useAuthStore(s => s.logout); + const {user} = useProfile(); + + const logoutMenuItem = user && { + label: t('menu.logout'), + onClick: () => { + logout(); + window.location.href = settings['about_link']; + setAnchorEl(null); + }, + id: 'LogoutTabs', + }; + const aboutMenuItem = { + label: t('menu.about'), + onClick: () => { + window.location.href = settings['about_link']; + setAnchorEl(null); + }, + id: 'AboutTabs', + }; + const languageMenuItem = { + label: <Languages />, + isComponentLabel: true, + id: 'LanguageSelection', + }; + + const validActions = [ + ...actions, + aboutMenuItem, + languageMenuItem, + logoutMenuItem, + ].filter(Boolean); + + return ( + <Menu + anchorEl={anchorEl} + anchorOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + keepMounted + transformOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + open={!!anchorEl} + onClose={() => setAnchorEl(null)} + > + {validActions?.map((action, index) => ( + <Action action={action} key={index} setAnchorEl={setAnchorEl} /> + ))} + </Menu> + ); +}; + +export default GenericMenu;
D frontend/containers/GenericToolbar/GenericMenu.js

@@ -1,76 +0,0 @@

-import Divider from '@material-ui/core/Divider'; -import {makeStyles} from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; - -const GenericMenu = ({anchorEl, setAnchorEl, actions = []}) => { - const classes = useStyles(); - if (actions.length === 0) return null; - return ( - <Menu - anchorEl={anchorEl} - anchorOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - keepMounted - transformOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - open={!!anchorEl} - onClose={() => setAnchorEl(null)} - > - {actions && - actions.map( - ({onClick, id, label, divider = false, ...menuItemProps}, idx) => { - if (divider) - return ( - <Divider - key={idx} - variant="fullWidth" - className={classes.divider} - /> - ); - if (onClick) - return ( - <MenuItem - onClick={() => { - if (!!onClick) onClick(); - setAnchorEl(null); - }} - key={idx} - id={id || `MenuItem${idx}`} - {...menuItemProps} - > - {label} - </MenuItem> - ); - else - return ( - <Typography - variant="body1" - key={idx} - id={id || `MenuItem${idx}`} - className={classes.textItem} - > - {label} - </Typography> - ); - } - )} - </Menu> - ); -}; - -const useStyles = makeStyles(theme => ({ - divider: { - margin: theme.spacing(1, 0), - }, - textItem: { - margin: theme.spacing(1, 2), - '&:focus': {outline: 0}, - }, -})); -export default GenericMenu;
M frontend/containers/GenericToolbar/index.jsfrontend/containers/GenericToolbar/index.js

@@ -1,4 +1,4 @@

-import {useState, useEffect, useMemo} from 'react'; +import {useState, useEffect} from 'react'; import {useRouter} from 'next/router'; import {makeStyles} from '@material-ui/core/styles'; import AppBar from '@material-ui/core/AppBar';

@@ -7,36 +7,15 @@ import Typography from '@material-ui/core/Typography';

import IconButton from '@material-ui/core/IconButton'; import Avatar from '@material-ui/core/Avatar'; import Icon from '@material-ui/core/Icon'; -import {useTranslation} from 'react-i18next'; -import useAuthStore from '../../stores/useAuthStore'; import useProfile from '../../hooks/useProfile'; -import useSettings from '../../hooks/useSettings'; -import GenericMenu from './GenericMenu'; +import GenericMenu from '../GenericMenu'; const GenericToolbar = ({title, actions = [], goBack = null}) => { - const {t} = useTranslation(); const router = useRouter(); const [anchorEl, setAnchorEl] = useState(null); const classes = useStyles(); const {user} = useProfile(); - const logout = useAuthStore(s => s.logout); - const settings = useSettings(); - const validActions = useMemo(() => actions.filter(Boolean), [actions]); - - const logoutMenuItem = user && { - label: t('menu.logout'), - onClick: () => { - logout(); - window.location.href = settings['about_link']; - }, - id: 'LogoutTabs', - }; - const aboutMenuItem = { - label: t('menu.about'), - onClick: () => (window.location.href = settings['about_link']), - id: 'AboutTabs', - }; const userInfos = user ? [{label: user.username, id: 'Email'}, {divider: true}] : [];

@@ -69,7 +48,7 @@ <Typography variant="h6" noWrap id="MenuHeaderTitle">

{title} </Typography> </div> - {validActions.length > 0 && ( + {actions.length > 0 && ( <> <IconButton color="inherit"

@@ -89,13 +68,7 @@

<GenericMenu anchorEl={anchorEl} setAnchorEl={setAnchorEl} - actions={[ - ...userInfos, - ...validActions, - {divider: true}, - aboutMenuItem, - logoutMenuItem, - ].filter(Boolean)} + actions={[...userInfos, ...actions, {divider: true}]} /> </> )}

@@ -117,9 +90,6 @@ name: {

flexGrow: 1, display: 'flex', alignItems: 'center', - }, - shareIcon: { - marginRight: theme.spacing(0), }, avatar: { width: theme.spacing(3),
M frontend/containers/Languages/index.tsxfrontend/containers/Languages/index.tsx

@@ -1,9 +1,7 @@

import {useState, useEffect} from 'react'; -import Box from '@material-ui/core/Box'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import Menu from '@material-ui/core/Menu'; +import MenuList from '@material-ui/core/MenuList'; import MenuItem from '@material-ui/core/MenuItem'; +import {makeStyles} from '@material-ui/core/styles'; import {useTranslation} from 'react-i18next'; import useLangStore from '../../stores/useLangStore'; import useProfile from '../../hooks/useProfile';

@@ -15,11 +13,12 @@ import moment from 'moment';

const Languages = () => { const {t, i18n} = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); + const [isSelecting, setSelecting] = useState(false); const language = useLangStore(s => s.language); const setLanguage = useLangStore(s => s.setLanguage); const {profile, connected} = useProfile(); const [updateProfile] = useUpdateMeMutation(); + const { languagesList } = useStyles({ isSelecting }); useEffect(() => { if (navigator?.language === 'en')

@@ -37,12 +36,12 @@ if (profile?.lang) setLanguage(profile.lang);

}, [profile]); const handleClick = event => { - setAnchorEl(event.currentTarget); + setSelecting(!isSelecting); }; const onConfirm = (lang: Enum_Userspermissionsuser_Lang) => { setLanguage(lang); - setAnchorEl(null); + setSelecting(false); if (connected) { updateProfile({

@@ -57,22 +56,10 @@ };

return ( <> - <Box position="fixed" bottom={0} left={0} zIndex={1050} p={1}> - <IconButton - color="primary" - aria-label="Languages" - onClick={handleClick} - > - <Icon>language</Icon> - </IconButton> - </Box> - <Menu - id="LanguagesMenu" - anchorEl={anchorEl} - keepMounted - open={Boolean(anchorEl)} - onClose={() => setAnchorEl(null)} - > + <MenuItem onClick={handleClick} > + {t('menu.language')} + </MenuItem> + <MenuList className={languagesList} dense> <MenuItem disabled={language === Enum_Userspermissionsuser_Lang.Fr} onClick={() => onConfirm(Enum_Userspermissionsuser_Lang.Fr)}

@@ -81,9 +68,19 @@ <MenuItem

disabled={language === Enum_Userspermissionsuser_Lang.En} onClick={() => onConfirm(Enum_Userspermissionsuser_Lang.En)} >{t`languages.en`}</MenuItem> - </Menu> + </MenuList> </> ); }; + + +const useStyles = makeStyles(theme => ({ + languagesList: ({ isSelecting }: { isSelecting: boolean }) => ({ + visibility: isSelecting ? 'visible' : 'hidden', + maxHeight: isSelecting ? 'none' : 0, + padding: isSelecting ? `0 ${theme.spacing(.5)}px` : 0, + overflow: 'hidden', + }), +})); export default Languages;
M frontend/layouts/Default.tsxfrontend/layouts/Default.tsx

@@ -2,7 +2,6 @@ import {ReactNode} from 'react';

import {Helmet} from 'react-helmet'; import useGTM from '../hooks/useGTM'; import GenericToolbar from '../containers/GenericToolbar'; -import Languages from '../containers/Languages'; interface Props { children: ReactNode;

@@ -40,7 +39,6 @@ goBack={goBack}

/> )} {children} - <Languages /> </div> ); };
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -56,6 +56,7 @@ "menu": {

"about": "About Caroster", "tour": "Caroster tour", "dashboard": "Dashboard", + "language": "Language", "login": "Login", "logout": "Logout", "register": "Sign-Up",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -56,6 +56,7 @@ "menu": {

"about": "À propos de Caroster", "tour": "Découverte de Caroster", "dashboard": "Tableau de bord", + "language": "Langue", "login": "Se connecter", "logout": "Se déconnecter", "register": "Créer un compte",