Remove navbar

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2024-06-30 11:52:39 +02:00
parent 5d4bbce324
commit a4e185eedd
10 changed files with 342 additions and 332 deletions

View File

@ -1,161 +0,0 @@
import clsx from 'clsx';
import React, { useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { Link, Redirect } from 'react-router-dom';
import { logIn, verifyCredentials } from 'soapbox/actions/auth';
import { fetchInstance } from 'soapbox/actions/instance';
import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui';
import Search from 'soapbox/features/compose/components/search';
import { useAppDispatch, useFeatures, useOwnAccount, useRegistrationStatus } from 'soapbox/hooks';
import ProfileDropdown from './profile-dropdown';
import type { PlfeResponse } from 'soapbox/api';
const messages = defineMessages({
login: { id: 'navbar.login.action', defaultMessage: 'Log in' },
username: { id: 'navbar.login.username.placeholder', defaultMessage: 'Email or username' },
email: { id: 'navbar.login.email.placeholder', defaultMessage: 'E-mail address' },
password: { id: 'navbar.login.password.label', defaultMessage: 'Password' },
forgotPassword: { id: 'navbar.login.forgot_password', defaultMessage: 'Forgot password?' },
});
const Navbar = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const features = useFeatures();
const { isOpen } = useRegistrationStatus();
const { account } = useOwnAccount();
const node = useRef(null);
const [isLoading, setLoading] = useState<boolean>(false);
const [username, setUsername] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [mfaToken, setMfaToken] = useState<boolean>(false);
const handleSubmit: React.FormEventHandler = (event) => {
event.preventDefault();
setLoading(true);
dispatch(logIn(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: { response: PlfeResponse }) => {
setLoading(false);
const data: any = error.response?.json;
if (data?.error === 'mfa_required') {
setMfaToken(data.mfa_token);
}
});
};
if (mfaToken) return <Redirect to={`/login?token=${encodeURIComponent(mfaToken)}`} />;
return (
<nav
className={clsx(
'sticky top-0 z-50 hidden border-gray-200 bg-white shadow black:border-b black:border-b-gray-800 black:bg-black lg:block dark:border-gray-800 dark:bg-primary-900',
)}
ref={node}
data-testid='navbar'
>
<div className='mx-auto max-w-7xl px-8'>
<div className='relative flex h-16 justify-between'>
<HStack
space={4}
alignItems='center'
className={clsx('enter flex-1 items-stretch', {
'justify-center justify-start': account,
'justify-start': !account,
})}
>
{account && (
<div className='flex flex-1 items-center justify-start pl-0'>
<div className='block w-full max-w-xs'>
<Search openInRoute autosuggest />
</div>
</div>
)}
</HStack>
<HStack space={3} alignItems='center'>
{account ? (
<div className='relative flex items-center'>
<ProfileDropdown account={account}>
<Avatar src={account.avatar} size={34} />
</ProfileDropdown>
</div>
) : (
<>
<Form className='hidden items-center space-x-2 xl:flex rtl:space-x-reverse' onSubmit={handleSubmit}>
<Input
required
value={username}
onChange={(event) => setUsername(event.target.value)}
type='text'
placeholder={intl.formatMessage(features.logInWithUsername ? messages.username : messages.email)}
className='max-w-[200px]'
/>
<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/outline/help.svg')}
className='cursor-pointer bg-transparent text-gray-400 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-200'
iconClassName='h-5 w-5'
/>
</Tooltip>
</Link>
<Button
theme='primary'
type='submit'
disabled={isLoading}
>
{intl.formatMessage(messages.login)}
</Button>
</Form>
<div className='space-x-1.5 xl:hidden'>
<Button
theme='tertiary'
size='sm'
to='/login'
>
<FormattedMessage id='account.login' defaultMessage='Log in' />
</Button>
{(isOpen) && (
<Button theme='primary' to='/signup' size='sm'>
<FormattedMessage id='account.register' defaultMessage='Sign up' />
</Button>
)}
</div>
</>
)}
</HStack>
</div>
</div>
</nav>
);
};
export { Navbar as default };

View File

@ -4,7 +4,7 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { submitAccountNote } from 'soapbox/actions/account-notes';
import { HStack, Text, Textarea, Widget } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import type { Account as AccountEntity } from 'soapbox/schemas';
import type { AppDispatch } from 'soapbox/store';
@ -27,6 +27,7 @@ interface IAccountNotePanel {
const AccountNotePanel: React.FC<IAccountNotePanel> = ({ account }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const me = useAppSelector((state) => state.me);
const textarea = useRef<HTMLTextAreaElement>(null);
@ -46,7 +47,7 @@ const AccountNotePanel: React.FC<IAccountNotePanel> = ({ account }) => {
setValue(account.relationship?.note);
}, [account.relationship?.note]);
if (!account) {
if (!me || !account) {
return null;
}

View File

@ -1,35 +1,103 @@
import React from 'react';
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { Redirect } from 'react-router-dom';
import { logIn, switchAccount, verifyCredentials } from 'soapbox/actions/auth';
import { fetchInstance } from 'soapbox/actions/instance';
import { Button, Stack, Text } from 'soapbox/components/ui';
import { useAppSelector, useInstance, useRegistrationStatus } from 'soapbox/hooks';
import LoginForm from 'soapbox/features/auth-login/components/login-form';
import OtpAuthForm from 'soapbox/features/auth-login/components/otp-auth-form';
import { useAppDispatch, useAppSelector, useInstance, useRegistrationStatus } from 'soapbox/hooks';
import { getRedirectUrl } from 'soapbox/utils/redirect';
import type { PlfeResponse } from 'soapbox/api';
const SignUpPanel = () => {
const dispatch = useAppDispatch();
const instance = useInstance();
const { isOpen } = useRegistrationStatus();
const me = useAppSelector((state) => state.me);
if (me || !isOpen) return null;
const token = new URLSearchParams(window.location.search).get('token');
const [isLoading, setIsLoading] = useState(false);
const [mfaAuthNeeded, setMfaAuthNeeded] = useState(!!token);
const [mfaToken, setMfaToken] = useState(token || '');
const [shouldRedirect, setShouldRedirect] = useState(false);
const getFormData = (form: HTMLFormElement) =>
Object.fromEntries(
Array.from(form).map((i: any) => [i.name, i.value]),
);
const handleSubmit: React.FormEventHandler = (event) => {
const { username, password } = getFormData(event.target as HTMLFormElement);
dispatch(logIn(username, password))
.then(({ access_token }) => dispatch(verifyCredentials(access_token as string)))
// Refetch the instance for authenticated fetch
.then(async (account) => {
await dispatch(fetchInstance());
return account;
})
.then((account: { id: string }) => {
if (typeof me === 'string') {
dispatch(switchAccount(account.id));
} else {
setShouldRedirect(true);
}
}).catch((error: { response: PlfeResponse }) => {
const data: any = error.response?.json;
if (data?.error === 'mfa_required') {
setMfaAuthNeeded(true);
setMfaToken(data.mfa_token);
}
setIsLoading(false);
});
setIsLoading(true);
event.preventDefault();
};
if (shouldRedirect) {
const redirectUri = getRedirectUrl();
return <Redirect to={redirectUri} />;
}
if (mfaAuthNeeded) return <OtpAuthForm mfa_token={mfaToken} />;
if (me) return null;
return (
<Stack space={2} data-testid='sign-up-panel'>
{isOpen && (
<>
<Stack>
<Text size='lg' weight='bold'>
<FormattedMessage id='signup_panel.title' defaultMessage='New to {site_title}?' values={{ site_title: instance.title }} />
</Text>
<Text theme='muted' size='sm'>
<FormattedMessage id='signup_panel.subtitle' defaultMessage="Sign up now to discuss what's happening." />
</Text>
</Stack>
<Button
theme='primary'
to='/signup'
block
>
<FormattedMessage id='account.register' defaultMessage='Sign up' />
</Button>
</>
)}
<Stack>
<Text size='lg' weight='bold'>
<FormattedMessage id='signup_panel.title' defaultMessage='New to {site_title}?' values={{ site_title: instance.title }} />
</Text>
<Text theme='muted' size='sm'>
<FormattedMessage id='signup_panel.subtitle' defaultMessage="Sign up now to discuss what's happening." />
<FormattedMessage id='signup_panel.sign_in.title' defaultMessage='Already have an account?' />
</Text>
</Stack>
<Button
theme='primary'
to='/signup'
block
>
<FormattedMessage id='account.register' defaultMessage='Sign up' />
</Button>
<LoginForm handleSubmit={handleSubmit} isLoading={isLoading} />
</Stack>
);
};

View File

@ -38,7 +38,6 @@ import { isStandalone } from 'soapbox/utils/state';
import BackgroundShapes from './components/background-shapes';
import FloatingActionButton from './components/floating-action-button';
import Navbar from './components/navbar';
import {
Status,
CommunityTimeline,
@ -470,8 +469,6 @@ const UI: React.FC<IUI> = ({ children }) => {
<BackgroundShapes />
<div className='z-10 flex min-h-screen flex-col'>
<Navbar />
<Layout>
<Layout.Sidebar>
{!standalone && <SidebarNavigation />}
@ -488,11 +485,9 @@ const UI: React.FC<IUI> = ({ children }) => {
</div>
)}
{me && (
<Suspense>
<SidebarMenu />
</Suspense>
)}
<Suspense>
<SidebarMenu />
</Suspense>
{me && features.chats && (
<div className='hidden xl:block'>