cetirizine

An experimental matrix client written in reactjs utilizing tailwind and storybook
git clone git://archive.git.mtrnord.blog/MTRNord/cetirizine.git
Log | Files | Refs | README | LICENSE

commit db8aac076f6056cb69665d1d090adc629bc32892
parent 3f144dbf7e4a4b2e0cc7c27e26d7545f1a9d43d2
Author: MTRNord <mtrnord1@gmail.com>
Date:   Fri, 28 Apr 2023 22:33:57 +0200

fix: Improve login flow

Diffstat:
Msrc/app/api/api.ts | 5++---
Msrc/components/login/login.tsx | 57+++++++++++++++++++++++++++++++++++++++------------------
2 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/src/app/api/api.ts b/src/app/api/api.ts @@ -22,8 +22,6 @@ interface IAuthState { logged_in: boolean; } const initialAuthState: IAuthState = { - host: undefined, - accessToken: undefined, logged_in: false, } export const auth = createReducer(initialAuthState, (builder) => builder @@ -86,6 +84,7 @@ export const matrixApi = createApi({ try { const { data, meta } = await queryFulfilled; const response = (meta as { request: Request, response: Response }).response; + if (response.status === 200) { dispatch(setLoggedIn(true)) dispatch(setAccessToken(data.access_token)) @@ -118,4 +117,4 @@ const rawBaseQuery = (baseUrl: string) => fetchBaseQuery({ // Export hooks for usage in functional components, which are // auto-generated based on the defined endpoints -export const { useGetLoginFlowsQuery, useDoLoginMutation, useLazyGetWellKnownQuery } = matrixApi +export const { useLazyGetLoginFlowsQuery, useDoLoginMutation, useLazyGetWellKnownQuery } = matrixApi diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx @@ -1,36 +1,57 @@ -import { useDeferredValue, useState } from 'react'; +import { useState } from 'react'; import { useAppDispatch } from '../../app/hooks'; -import { setHost, useDoLoginMutation, useLazyGetWellKnownQuery } from '../../app/api/api'; +import { setHost, useDoLoginMutation, useLazyGetLoginFlowsQuery, useLazyGetWellKnownQuery } from '../../app/api/api'; import Button from '../button/button'; import Header from '../header/header'; import Input from '../input/basic/input'; import { Navigate } from 'react-router-dom'; +import { IRateLimitError } from '../../app/api/apiTypes'; export function Login() { - const [login, { isLoading: loginPending, error: loginError }] = useDoLoginMutation(); - const [triggerWellKnown, _result, _lastPromiseInfo] = useLazyGetWellKnownQuery(); - const deferredLoginError = useDeferredValue(loginError); + const [login, { isLoading: loginPending, isSuccess, error: loginErrorRaw }] = useDoLoginMutation(); + const [triggerWellKnown] = useLazyGetWellKnownQuery(); + const [triggerLoginFlows] = useLazyGetLoginFlowsQuery(); + const [loginError, setLoginError] = useState(loginErrorRaw); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); - const [loginPendingLast, setLoginPendingLast] = useState(false); const dispatch = useAppDispatch(); - if (loginPendingLast !== loginPending) { + if (isSuccess) { return <Navigate to="/" />; } const startLogin = async () => { dispatch(setHost(`https://${username.split(':')[1]}`)); - await triggerWellKnown(undefined); - login({ - type: 'm.login.password', - identifier: { - type: 'm.id.user', + const { isError, error } = await triggerWellKnown(undefined); + if (isError) { + setLoginError(error); + return; + } + const { data } = await triggerLoginFlows(undefined, true); + if ((data?.flows?.filter((flow) => flow.type === 'm.login.password')?.length || 0) > 0) { + const payload = await login({ + type: 'm.login.password', + identifier: { + type: 'm.id.user', + user: username, + }, user: username, - }, - user: username, - password: password - }) - setLoginPendingLast(true); + password: password + }); + if ('error' in payload) { + if (JSON.stringify(payload.error).includes('M_LIMIT_EXCEEDED')) { + setLoginError({ status: "CUSTOM_ERROR", error: 'Too many attempts, try again later' }); + return; + } + if (JSON.stringify(payload.error).includes('M_FORBIDDEN')) { + setLoginError({ status: "CUSTOM_ERROR", error: (payload.error as any).data.error }); + return; + } + setLoginError(payload.error); + return; + } + } else { + setLoginError({ status: "CUSTOM_ERROR", error: 'No password login flow found' }); + } } // TODO: We need to make sure that we do well-known before login return ( @@ -39,7 +60,7 @@ export function Login() { startLogin(); }}> <Header>Login</Header> - {deferredLoginError ? <h2 className='text-red-500 font-normal text-sm'>{'error' in deferredLoginError ? deferredLoginError.error : ('data' in deferredLoginError ? JSON.stringify(deferredLoginError.data) : deferredLoginError.message)}</h2> : <div className='min-h-[1.25rem]'></div>} + {loginError ? <h2 className='text-red-500 font-normal text-sm'>{'error' in loginError ? loginError.error : ('data' in loginError ? JSON.stringify(loginError.data) : loginError.message)}</h2> : <div className='min-h-[1.25rem]'></div>} <Input readonly={loginPending} value={username}