Add login form to navbar

set-dependency-scanning-config-1
Justin 2022-05-12 16:56:09 -04:00
rodzic 938665f157
commit de1d630daa
4 zmienionych plików z 100 dodań i 17 usunięć

Wyświetl plik

@ -19,7 +19,7 @@ const useButtonStyles = ({
}: IButtonStyles) => {
const themes = {
primary:
'border-transparent text-white bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 focus:ring-2 focus:ring-offset-2',
'border-primary-600 text-white bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 focus:ring-2 focus:ring-offset-2',
secondary:
'border-transparent text-primary-700 bg-primary-100 hover:bg-primary-200 focus:ring-primary-500 focus:ring-2 focus:ring-offset-2',
ghost: 'shadow-none border-gray-200 text-gray-700 bg-white dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 focus:ring-primary-500 focus:ring-2 focus:ring-offset-2',

Wyświetl plik

@ -4,5 +4,5 @@
[data-reach-tooltip] {
@apply pointer-events-none absolute px-2.5 py-1.5 rounded shadow whitespace-nowrap text-xs font-medium bg-gray-800 text-white;
z-index: 1;
z-index: 100;
}

Wyświetl plik

@ -1,11 +1,14 @@
import { AxiosError } from 'axios';
import classNames from 'classnames';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import React, { useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { Link } from 'react-router-dom';
import { Link, Redirect } from 'react-router-dom';
import { logIn, verifyCredentials } from 'soapbox/actions/auth';
import { fetchInstance } from 'soapbox/actions/instance';
import SiteLogo from 'soapbox/components/site-logo';
import { Avatar, Button } from 'soapbox/components/ui';
import { Avatar, Button, Form, IconButton, Input, Tooltip } from 'soapbox/components/ui';
import Search from 'soapbox/features/compose/components/search';
import { useOwnAccount, useSoapboxConfig } from 'soapbox/hooks';
@ -13,16 +16,56 @@ import { openSidebar } from '../../../actions/sidebar';
import ProfileDropdown from './profile-dropdown';
const messages = defineMessages({
login: { id: 'navbar.login.action', defaultMessage: 'Log in' },
username: { id: 'navbar.login.username.placeholder', defaultMessage: 'Email or username' },
password: { id: 'navbar.login.password.label', defaultMessage: 'Password' },
forgotPassword: { id: 'navbar.login.forgot_password', defaultMessage: 'Forgot password?' },
});
const Navbar = () => {
const dispatch = useDispatch();
const node = React.useRef(null);
const intl = useIntl();
const node = useRef(null);
const account = useOwnAccount();
const soapboxConfig = useSoapboxConfig();
const singleUserMode = soapboxConfig.get('singleUserMode');
const [isLoading, setLoading] = useState<boolean>(false);
const [username, setUsername] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [mfaToken, setMfaToken] = useState<boolean>(false);
const onOpenSidebar = () => dispatch(openSidebar());
const handleSubmit: React.FormEventHandler = (event) => {
event.preventDefault();
setLoading(true);
dispatch(logIn(intl, username, password) as any)
.then(({ access_token }: { access_token: string }) => {
setLoading(false);
return (
dispatch(verifyCredentials(access_token) as any)
// Refetch the instance for authenticated fetch
.then(() => dispatch(fetchInstance()))
);
})
.catch((error: AxiosError) => {
setLoading(false);
const data: any = error.response?.data;
if (data?.error === 'mfa_required') {
setMfaToken(data.mfa_token);
}
});
};
if (mfaToken) return <Redirect to={`/login?token=${encodeURIComponent(mfaToken)}`} />;
return (
<nav className='bg-white dark:bg-slate-800 shadow z-50 sticky top-0' ref={node}>
<div className='max-w-7xl mx-auto px-2 sm:px-6 lg:px-8'>
@ -64,17 +107,57 @@ const Navbar = () => {
</ProfileDropdown>
</div>
) : (
<div className='space-x-1.5'>
<Button theme='secondary' to='/login' size='sm'>
<FormattedMessage id='account.login' defaultMessage='Log In' />
</Button>
<>
<Form className='hidden lg:flex space-x-2 items-center' onSubmit={handleSubmit}>
<Input
required
value={username}
onChange={(event) => setUsername(event.target.value)}
type='text'
placeholder={intl.formatMessage(messages.username)}
className='max-w-[200px]'
/>
{!singleUserMode && (
<Button theme='primary' to='/signup' size='sm'>
<FormattedMessage id='account.register' defaultMessage='Sign up' />
<Input
required
value={password}
onChange={(event) => setPassword(event.target.value)}
type='password'
placeholder={intl.formatMessage(messages.password)}
className='max-w-[200px]'
/>
<Link to='/reset-password'>
<Tooltip text={intl.formatMessage(messages.forgotPassword)}>
<IconButton
src={require('@tabler/icons/icons/help.svg')}
className='bg-transparent text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-200 cursor-pointer'
iconClassName='w-5 h-5'
/>
</Tooltip>
</Link>
<Button
theme='primary'
type='submit'
disabled={isLoading}
>
{intl.formatMessage(messages.login)}
</Button>
)}
</div>
</Form>
<div className='space-x-1.5 lg:hidden'>
<Button theme='secondary' to='/login' size='sm'>
<FormattedMessage id='account.login' defaultMessage='Log In' />
</Button>
{!singleUserMode && (
<Button theme='primary' to='/signup' size='sm'>
<FormattedMessage id='account.register' defaultMessage='Sign up' />
</Button>
)}
</div>
</>
)}
</div>
</div>

Wyświetl plik

@ -8,7 +8,7 @@ import { dismissAlert } from '../../../actions/alerts';
import { getAlerts } from '../../../selectors';
const CustomNotificationStack = (props) => (
<div role='assertive' data-testid='toast' className='z-1000 fixed inset-0 flex items-end px-4 py-6 pointer-events-none sm:p-6 sm:items-start'>
<div role='assertive' data-testid='toast' className='z-1000 fixed inset-0 flex items-end px-4 py-6 pointer-events-none pt-16 lg:pt-20 sm:items-start'>
<NotificationStack {...props} />
</div>
);