This commit is contained in:
seb
2026-07-31 07:02:51 +02:00
parent 9dac151854
commit a95b7f4b1e
14 changed files with 1064 additions and 831 deletions

View File

@@ -1,13 +1,10 @@
import { useState } from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import GoogleIcon from '@mui/icons-material/Google';
import { useSnackbar } from 'notistack';
import { useAuth } from '../auth/useAuth';
import { enqueueSnackbar } from 'notistack';
import { loginWithGoogle, selectAuthStatus, selectGoogleClientId } from '../store/authSlice';
import {
abortGoogleSignIn,
FEDCM_BLOCKED_MESSAGE,
@@ -17,12 +14,15 @@ import {
requestGoogleIdToken,
} from '../auth/googleSignIn';
export default function LoginPage() {
const { loginWithGoogle, googleClientId, status } = useAuth();
const { enqueueSnackbar } = useSnackbar();
const [busy, setBusy] = useState(false);
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = { busy: false };
this.handleLogin = this.handleLogin.bind(this);
}
const handleLogin = async () => {
async handleLogin() {
const { googleClientId } = this.props;
abortGoogleSignIn();
try {
const idToken = await requestGoogleIdToken(googleClientId, {
@@ -33,8 +33,8 @@ export default function LoginPage() {
});
},
});
setBusy(true);
await loginWithGoogle(idToken);
this.setState({ busy: true });
await this.props.loginWithGoogle(idToken).unwrap();
} catch (err) {
if (err instanceof GoogleSignInCancelledError) return;
logError('[login] Google sign-in failed', {
@@ -64,32 +64,47 @@ export default function LoginPage() {
.join(' · ');
enqueueSnackbar(`Login failed: ${detail}`, { variant: 'error' });
} finally {
setBusy(false);
this.setState({ busy: false });
}
};
}
const loading = status === 'loading';
const ready = Boolean(googleClientId);
render() {
const { busy } = this.state;
const { status, googleClientId } = this.props;
const loading = status === 'loading';
const ready = Boolean(googleClientId);
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
bgcolor: 'background.default',
}}
>
<Button
variant="contained"
size="large"
startIcon={<GoogleIcon />}
disabled={busy || loading || !ready}
onClick={handleLogin}
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
bgcolor: 'background.default',
}}
>
{busy ? 'Signing in…' : 'Sign in with Google'}
</Button>
</Box>
);
<Button
variant="contained"
size="large"
startIcon={<GoogleIcon />}
disabled={busy || loading || !ready}
onClick={this.handleLogin}
>
{busy ? 'Signing in…' : 'Sign in with Google'}
</Button>
</Box>
);
}
}
const mapStateToProps = (state) => ({
status: selectAuthStatus(state),
googleClientId: selectGoogleClientId(state),
});
const mapDispatchToProps = (dispatch) => ({
loginWithGoogle: (credential) => dispatch(loginWithGoogle(credential)),
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);