diff options
Diffstat (limited to 'modern/src/login/LoginPage.jsx')
-rw-r--r-- | modern/src/login/LoginPage.jsx | 263 |
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; |