all repos — caroster @ 86e6a96df3e17500d2b41f081a7f089558cd424a

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

feat: ✨ Redirect user to current url after login & signup
Tim Izzo tim@octree.ch
Fri, 16 Sep 2022 18:22:32 +0000
commit

86e6a96df3e17500d2b41f081a7f089558cd424a

parent

46303167ec1138b375af3513350eb491415bb6a1

M backend/.strapi-updater.jsonbackend/.strapi-updater.json

@@ -1,5 +1,5 @@

{ "latest": "4.3.8", - "lastUpdateCheck": 1662991523844, - "lastNotification": 1662718956959 + "lastUpdateCheck": 1663340063052, + "lastNotification": 1663340063046 }
A frontend/containers/EventBar/UserIcon.tsx

@@ -0,0 +1,27 @@

+import Avatar from '@material-ui/core/Avatar'; +import Icon from '@material-ui/core/Icon'; +import {makeStyles} from '@material-ui/core/styles'; +import useProfile from '../../hooks/useProfile'; + +const UserIcon = () => { + const {profile} = useProfile(); + const classes = useStyles(); + + if (profile) + return ( + <Avatar className={classes.avatar}> + {`${profile.username[0]}`.toUpperCase()} + </Avatar> + ); + else return <Icon>more_vert</Icon>; +}; + +const useStyles = makeStyles(theme => ({ + avatar: { + width: theme.spacing(3), + height: theme.spacing(3), + fontSize: 16, + }, +})); + +export default UserIcon;
M frontend/containers/EventBar/index.tsxfrontend/containers/EventBar/index.tsx

@@ -4,82 +4,25 @@ import Toolbar from '@material-ui/core/Toolbar';

import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; -import Avatar from '@material-ui/core/Avatar'; import Icon from '@material-ui/core/Icon'; import {makeStyles} from '@material-ui/core/styles'; import {useState} from 'react'; -import {useRouter} from 'next/router'; -import {useTranslation} from 'react-i18next'; import useProfile from '../../hooks/useProfile'; import useShare from '../../hooks/useShare'; import GenericMenu from '../GenericMenu'; +import useActions from './useActions'; +import UserIcon from './UserIcon'; const EventBar = ({event, onAdd}) => { - const {t} = useTranslation(); - const router = useRouter(); const {share} = useShare(); const [anchorEl, setAnchorEl] = useState(null); const {profile, connected} = useProfile(); const classes = useStyles(); - - const signUp = () => - router.push({ - pathname: '/auth/register', - state: {event: event?.id}, - }); - const signIn = () => router.push('/auth/login'); - const goToDashboard = () => router.push('/dashboard'); - const goProfile = () => router.push('/profile'); - - const noUserMenuActions = [ - { - label: t('event.actions.add_to_my_events'), - onClick: () => { - onAdd(true); - }, - id: 'AddToMyEventsTab', - }, - {divider: true}, - { - label: t('menu.login'), - onClick: signIn, - id: 'SignInTab', - }, - { - label: t('menu.register'), - onClick: signUp, - id: 'SignUpTab', - }, - {divider: true}, - ]; - - const loggedMenuActions = [ - { - label: t('menu.dashboard'), - onClick: goToDashboard, - id: 'GoToDashboardTab', - }, - { - label: t('menu.profile'), - onClick: goProfile, - id: 'ProfileTab', - }, - {divider: true}, - ]; - - const menuActions = connected ? loggedMenuActions : noUserMenuActions; + const menuActions = useActions({onAdd, eventId: event?.id}); const appLink = connected ? '/dashboard' : `/e/${event.uuid}` || ''; const userInfos = profile ? [{label: profile.username, id: 'Email'}, {divider: true}] : []; - - const UserIcon = profile ? ( - <Avatar className={classes.avatar}> - {`${profile.username[0]}`.toUpperCase()} - </Avatar> - ) : ( - <Icon>more_vert</Icon> - ); return ( <AppBar

@@ -136,7 +79,7 @@ edge="end"

id="MenuMoreInfo" onClick={e => setAnchorEl(e.currentTarget)} > - {UserIcon} + <UserIcon /> </IconButton> </> </Toolbar>

@@ -167,11 +110,6 @@ maxWidth: `calc(100vw - ${theme.spacing(30)}px)`,

}, iconButtons: { margin: theme.spacing(0), - }, - avatar: { - width: theme.spacing(3), - height: theme.spacing(3), - fontSize: 16, }, shareIcon: { marginRight: 0,
A frontend/containers/EventBar/useActions.ts

@@ -0,0 +1,66 @@

+import {useRouter} from 'next/router'; +import {useTranslation} from 'react-i18next'; +import useProfile from '../../hooks/useProfile'; +import useRedirectUrlStore from '../../stores/useRedirectUrl'; + +interface Props { + onAdd: (isAddToMyEvent: boolean) => void; + eventId?: string; +} + +const useActions = (props: Props) => { + const {onAdd, eventId} = props; + const {t} = useTranslation(); + const router = useRouter(); + const {connected} = useProfile(); + const setRedirectUrl = useRedirectUrlStore(s => s.setRedirectUrl); + + const noUserMenuActions = [ + { + label: t('event.actions.add_to_my_events'), + onClick: () => { + onAdd(true); + }, + id: 'AddToMyEventsTab', + }, + {divider: true}, + { + label: t('menu.login'), + onClick: () => { + setRedirectUrl(window.location.href); + router.push('/auth/login'); + }, + id: 'SignInTab', + }, + { + label: t('menu.register'), + onClick: () => { + setRedirectUrl(window.location.href); + router.push({ + pathname: '/auth/register', + state: {event: eventId}, + }); + }, + id: 'SignUpTab', + }, + {divider: true}, + ]; + + const loggedMenuActions = [ + { + label: t('menu.dashboard'), + onClick: () => router.push('/dashboard'), + id: 'GoToDashboardTab', + }, + { + label: t('menu.profile'), + onClick: () => router.push('/profile'), + id: 'ProfileTab', + }, + {divider: true}, + ]; + + return connected ? loggedMenuActions : noUserMenuActions; +}; + +export default useActions;
M frontend/containers/SignInForm/index.tsxfrontend/containers/SignInForm/index.tsx

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

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

@@ -12,19 +11,18 @@ import CardActions from '@material-ui/core/CardActions';

import {useTranslation} from 'react-i18next'; import {signIn} from 'next-auth/react'; import useToastsStore from '../../stores/useToastStore'; -import useLoginWithProvider from '../../hooks/useLoginWithProvider'; import useAddToEvents from '../../hooks/useAddToEvents'; +import useRedirectUrlStore from '../../stores/useRedirectUrl'; const SignIn = () => { const {t} = useTranslation(); - const router = useRouter(); - const {loginWithProvider} = useLoginWithProvider(); const [error, setError] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const addToast = useToastsStore(s => s.addToast); const {saveStoredEvents} = useAddToEvents(); const classes = useStyles(); + const getRedirectUrl = useRedirectUrlStore(s => s.getRedirectUrl); const canSubmit = useMemo( () => [email, password].filter(s => s.length < 4).length === 0,

@@ -34,32 +32,19 @@

const onSubmit = async e => { e.preventDefault?.(); try { + const callbackUrl = getRedirectUrl() || '/'; await signIn('credentials', { email, password, - callbackUrl: '/', + callbackUrl, }); - saveStoredEvents(); - router.push('/'); + saveStoredEvents(); // TODO Check it's correctly executed after sign-in } catch (error) { handleAuthError(error); } return false; }; - - useEffect(() => { - const authWithGoogle = async search => { - try { - await loginWithProvider('google', search); - } catch (error) { - handleAuthError(error); - } - }; - - const search = getURLSearch(router); - if (search) authWithGoogle(search); - }, [router.route]); // eslint-disable-line react-hooks/exhaustive-deps const handleAuthError = error => { const strapiError = error.message;

@@ -128,8 +113,6 @@ </CardActions>

</form> ); }; - -const getURLSearch = router => router.asPath.replace(router.route, ''); const useStyles = makeStyles(theme => ({ content: {
D frontend/hooks/useLoginWithProvider.ts

@@ -1,17 +0,0 @@

-const useLoginWithProvider = () => { - const setToken = () => {}; // DEV - const setUser = () => {}; // DEV - - const loginWithProvider = async (provider: string, search: string) => { - const resultRaw = await fetch(`/api/auth/${provider}/callback${search}`); - const result = await resultRaw.json(); - if (result.hasOwnProperty('error')) throw result; - setToken(result.jwt); - setUser(result.user); - return result; - }; - - return {loginWithProvider}; -}; - -export default useLoginWithProvider;
M frontend/pages/index.tsxfrontend/pages/index.tsx

@@ -7,6 +7,8 @@ import Paper from '../components/Paper';

import Logo from '../components/Logo'; import {useSession} from 'next-auth/react'; import pageUtils from '../lib/pageUtils'; +import {useEffect} from 'react'; +import useRedirectUrlStore from '../stores/useRedirectUrl'; const Home = () => { const {t} = useTranslation();

@@ -14,6 +16,12 @@ const router = useRouter();

const session = useSession(); const isAuthenticated = session.status === 'authenticated'; const isReady = session.status !== 'loading'; + const getRedirectUrl = useRedirectUrlStore(s => s.getRedirectUrl); + + useEffect(() => { + const redirectUrl = getRedirectUrl(); + if (redirectUrl) router.push(redirectUrl); + }, []); const noUserMenuActions = [ {
A frontend/stores/useRedirectUrl.ts

@@ -0,0 +1,26 @@

+import create from 'zustand'; +import {persist} from 'zustand/middleware'; + +type State = { + redirectUrl: string | null; + setRedirectUrl: (redirectUrl: string) => void; + getRedirectUrl: () => string; +}; + +const useRedirectUrlStore = create<State>( + persist((set, get) => ({ + redirectUrl: null, + setRedirectUrl: redirectUrl => set({redirectUrl}), + getRedirectUrl: () => { + const redirectUrl = get().redirectUrl; + set({redirectUrl: null}); + return redirectUrl; + }, + })), + { + name: 'currentUrl', + getStorage: () => sessionStorage, + } +); + +export default useRedirectUrlStore;