From de1d630daa14a25a62a2b5e6c4b9ed6391749854 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 12 May 2022 16:56:09 -0400 Subject: [PATCH] Add login form to navbar --- .../components/ui/button/useButtonStyles.ts | 2 +- app/soapbox/components/ui/tooltip/tooltip.css | Bin 201 -> 203 bytes app/soapbox/features/ui/components/navbar.tsx | 111 +++++++++++++++--- .../ui/containers/notifications_container.js | Bin 1666 -> 1674 bytes 4 files changed, 98 insertions(+), 15 deletions(-) diff --git a/app/soapbox/components/ui/button/useButtonStyles.ts b/app/soapbox/components/ui/button/useButtonStyles.ts index 1b52e9a053..e064c9e237 100644 --- a/app/soapbox/components/ui/button/useButtonStyles.ts +++ b/app/soapbox/components/ui/button/useButtonStyles.ts @@ -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', diff --git a/app/soapbox/components/ui/tooltip/tooltip.css b/app/soapbox/components/ui/tooltip/tooltip.css index 3140307dc42cf97870bb033454c50a1dc2b38896..6d439ddb3436215288093daf40849e6e2d9141f0 100644 GIT binary patch delta 13 UcmX@fc$#s-Q8ohuYpz-@03wkD9smFU delta 11 ScmX@jc#?6#Q5I{iS}p(@mjk>2 diff --git a/app/soapbox/features/ui/components/navbar.tsx b/app/soapbox/features/ui/components/navbar.tsx index 4b2b698897..ac81681d95 100644 --- a/app/soapbox/features/ui/components/navbar.tsx +++ b/app/soapbox/features/ui/components/navbar.tsx @@ -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(false); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [mfaToken, setMfaToken] = useState(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 ; + return (