aboutsummaryrefslogtreecommitdiff
path: root/modern/src/login/LoginPage.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'modern/src/login/LoginPage.jsx')
-rw-r--r--modern/src/login/LoginPage.jsx263
1 files changed, 0 insertions, 263 deletions
diff --git a/modern/src/login/LoginPage.jsx b/modern/src/login/LoginPage.jsx
deleted file mode 100644
index 27aad7c9..00000000
--- a/modern/src/login/LoginPage.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import dayjs from 'dayjs';
-import {
- useMediaQuery, Select, MenuItem, FormControl, Button, TextField, Link, Snackbar, IconButton, Tooltip, LinearProgress, Box,
-} from '@mui/material';
-import ReactCountryFlag from 'react-country-flag';
-import makeStyles from '@mui/styles/makeStyles';
-import CloseIcon from '@mui/icons-material/Close';
-import LockOpenIcon from '@mui/icons-material/LockOpen';
-import { useTheme } from '@mui/material/styles';
-import { useDispatch, useSelector } from 'react-redux';
-import { useNavigate } from 'react-router-dom';
-import { sessionActions } from '../store';
-import { useLocalization, useTranslation } from '../common/components/LocalizationProvider';
-import LoginLayout from './LoginLayout';
-import usePersistedState from '../common/util/usePersistedState';
-import { handleLoginTokenListeners, nativeEnvironment, nativePostMessage } from '../common/components/NativeInterface';
-import LogoImage from './LogoImage';
-import { useCatch } from '../reactHelper';
-
-const useStyles = makeStyles((theme) => ({
- options: {
- position: 'fixed',
- top: theme.spacing(2),
- right: theme.spacing(2),
- display: 'flex',
- flexDirection: 'row',
- gap: theme.spacing(1),
- },
- container: {
- display: 'flex',
- flexDirection: 'column',
- gap: theme.spacing(2),
- },
- extraContainer: {
- display: 'flex',
- flexDirection: 'row',
- justifyContent: 'center',
- gap: theme.spacing(4),
- marginTop: theme.spacing(2),
- },
- registerButton: {
- minWidth: 'unset',
- },
- link: {
- cursor: 'pointer',
- },
-}));
-
-const LoginPage = () => {
- const classes = useStyles();
- const dispatch = useDispatch();
- const navigate = useNavigate();
- const theme = useTheme();
- const t = useTranslation();
-
- const { languages, language, setLanguage } = useLocalization();
- const languageList = Object.entries(languages).map((values) => ({ code: values[0], country: values[1].country, name: values[1].name }));
-
- const [failed, setFailed] = useState(false);
-
- const [email, setEmail] = usePersistedState('loginEmail', '');
- const [password, setPassword] = useState('');
- const [code, setCode] = useState('');
-
- const registrationEnabled = useSelector((state) => state.session.server.registration);
- const languageEnabled = useSelector((state) => !state.session.server.attributes['ui.disableLoginLanguage']);
- const changeEnabled = useSelector((state) => !state.session.server.attributes.disableChange);
- const emailEnabled = useSelector((state) => state.session.server.emailEnabled);
- const openIdEnabled = useSelector((state) => state.session.server.openIdEnabled);
- const openIdForced = useSelector((state) => state.session.server.openIdEnabled && state.session.server.openIdForce);
- const [codeEnabled, setCodeEnabled] = useState(false);
-
- const [announcementShown, setAnnouncementShown] = useState(false);
- const announcement = useSelector((state) => state.session.server.announcement);
-
- const generateLoginToken = async () => {
- if (nativeEnvironment) {
- let token = '';
- try {
- const expiration = dayjs().add(6, 'months').toISOString();
- const response = await fetch('/api/session/token', {
- method: 'POST',
- body: new URLSearchParams(`expiration=${expiration}`),
- });
- if (response.ok) {
- token = await response.text();
- }
- } catch (error) {
- token = '';
- }
- nativePostMessage(`login|${token}`);
- }
- };
-
- const handlePasswordLogin = async (event) => {
- event.preventDefault();
- setFailed(false);
- try {
- const query = `email=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`;
- const response = await fetch('/api/session', {
- method: 'POST',
- body: new URLSearchParams(code.length ? `${query}&code=${code}` : query),
- });
- if (response.ok) {
- const user = await response.json();
- generateLoginToken();
- dispatch(sessionActions.updateUser(user));
- navigate('/');
- } else if (response.status === 401 && response.headers.get('WWW-Authenticate') === 'TOTP') {
- setCodeEnabled(true);
- } else {
- throw Error(await response.text());
- }
- } catch (error) {
- setFailed(true);
- setPassword('');
- }
- };
-
- const handleTokenLogin = useCatch(async (token) => {
- const response = await fetch(`/api/session?token=${encodeURIComponent(token)}`);
- if (response.ok) {
- const user = await response.json();
- dispatch(sessionActions.updateUser(user));
- navigate('/');
- } else {
- throw Error(await response.text());
- }
- });
-
- const handleOpenIdLogin = () => {
- document.location = '/api/session/openid/auth';
- };
-
- useEffect(() => nativePostMessage('authentication'), []);
-
- useEffect(() => {
- const listener = (token) => handleTokenLogin(token);
- handleLoginTokenListeners.add(listener);
- return () => handleLoginTokenListeners.delete(listener);
- }, []);
-
- if (openIdForced) {
- handleOpenIdLogin();
- return (<LinearProgress />);
- }
-
- return (
- <LoginLayout>
- <div className={classes.options}>
- {nativeEnvironment && changeEnabled && (
- <Tooltip title={t('settingsServer')}>
- <IconButton onClick={() => navigate('/change-server')}>
- <LockOpenIcon />
- </IconButton>
- </Tooltip>
- )}
- {languageEnabled && (
- <FormControl>
- <Select value={language} onChange={(e) => setLanguage(e.target.value)}>
- {languageList.map((it) => (
- <MenuItem key={it.code} value={it.code}>
- <Box component="span" sx={{ mr: 1 }}>
- <ReactCountryFlag countryCode={it.country} svg />
- </Box>
- {it.name}
- </MenuItem>
- ))}
- </Select>
- </FormControl>
- )}
- </div>
- <div className={classes.container}>
- {useMediaQuery(theme.breakpoints.down('lg')) && <LogoImage color={theme.palette.primary.main} />}
- <TextField
- required
- error={failed}
- label={t('userEmail')}
- name="email"
- value={email}
- autoComplete="email"
- autoFocus={!email}
- onChange={(e) => setEmail(e.target.value)}
- helperText={failed && 'Invalid username or password'}
- />
- <TextField
- required
- error={failed}
- label={t('userPassword')}
- name="password"
- value={password}
- type="password"
- autoComplete="current-password"
- autoFocus={!!email}
- onChange={(e) => setPassword(e.target.value)}
- />
- {codeEnabled && (
- <TextField
- required
- error={failed}
- label={t('loginTotpCode')}
- name="code"
- value={code}
- type="number"
- onChange={(e) => setCode(e.target.value)}
- />
- )}
- <Button
- onClick={handlePasswordLogin}
- type="submit"
- variant="contained"
- color="secondary"
- disabled={!email || !password || (codeEnabled && !code)}
- >
- {t('loginLogin')}
- </Button>
- {openIdEnabled && (
- <Button
- onClick={() => handleOpenIdLogin()}
- variant="contained"
- color="secondary"
- >
- {t('loginOpenId')}
- </Button>
- )}
- <div className={classes.extraContainer}>
- {registrationEnabled && (
- <Link
- onClick={() => navigate('/register')}
- className={classes.link}
- underline="none"
- variant="caption"
- >
- {t('loginRegister')}
- </Link>
- )}
- {emailEnabled && (
- <Link
- onClick={() => navigate('/reset-password')}
- className={classes.link}
- underline="none"
- variant="caption"
- >
- {t('loginReset')}
- </Link>
- )}
- </div>
- </div>
- <Snackbar
- open={!!announcement && !announcementShown}
- message={announcement}
- action={(
- <IconButton size="small" color="inherit" onClick={() => setAnnouncementShown(true)}>
- <CloseIcon fontSize="small" />
- </IconButton>
- )}
- />
- </LoginLayout>
- );
-};
-
-export default LoginPage;