chore: fix check email loading state (#93)

* chore: fix check email loading state

* chore: add disabled state
This commit is contained in:
Kilu.He
2025-04-14 13:41:43 +08:00
committed by GitHub
parent 60627857fb
commit c6a86e08e2
7 changed files with 39 additions and 33 deletions

View File

@@ -3097,8 +3097,12 @@
"copiedVideoLink": "Video original link copied to clipboard", "copiedVideoLink": "Video original link copied to clipboard",
"checkYourEmail": "Check your email", "checkYourEmail": "Check your email",
"checkEmailTip": "A temporary verification link has been sent. Please check your inbox at", "checkEmailTip": "A temporary verification link has been sent. Please check your inbox at",
"checkCodeTip": "A temporary verification code has been sent. Please check your inbox at",
"enterCodeManually": "Enter code manually", "enterCodeManually": "Enter code manually",
"backToLogin": "Back to login", "backToLogin": "Back to login",
"enterCode": "Enter code", "enterCode": "Enter code",
"continueToSignIn": "Continue to sign in" "continueToSignIn": "Continue to sign in",
"requireCode": "Please enter a valid verification code.",
"signing": "Signing in...",
"invalidOTPCode": "The code is invalid or has expired. Please try again."
} }

View File

@@ -90,8 +90,8 @@ export async function signInOTP ({
emit(EventType.SESSION_INVALID); emit(EventType.SESSION_INVALID);
return Promise.reject({ return Promise.reject({
code: e.code, code: e.response?.data?.code || e.response?.status,
message: e.message, message: e.response?.data?.msg || e.message,
}); });
} }

View File

@@ -1,24 +1,32 @@
import { AFConfigContext } from '@/components/main/app.hooks'; import { AFConfigContext } from '@/components/main/app.hooks';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Progress } from '@/components/ui/progress';
import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys';
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { ReactComponent as Logo } from '@/assets/icons/logo.svg'; import { ReactComponent as Logo } from '@/assets/icons/logo.svg';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
function CheckEmail ({ email, redirectTo }: { function CheckEmail ({ email, redirectTo }: {
email: string; email: string;
redirectTo: string; redirectTo: string;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
const [isEnter, setEnter] = useState<boolean>(false); const [isEnter, setEnter] = useState<boolean>(false);
const [code, setCode] = useState<string>(''); const [code, setCode] = useState<string>('');
const [loading, setLoading] = React.useState<boolean>(false);
const service = useContext(AFConfigContext)?.service; const service = useContext(AFConfigContext)?.service;
const handleSubmit = async () => { const handleSubmit = async () => {
if (loading) return;
if (!code) {
setError(t('requireCode'));
return;
}
setLoading(true); setLoading(true);
const id = toast.loading(t('signing'));
try { try {
await service?.signInOTP({ await service?.signInOTP({
@@ -28,15 +36,19 @@ function CheckEmail ({ email, redirectTo }: {
}); });
// eslint-disable-next-line // eslint-disable-next-line
} catch (e: any) { } catch (e: any) {
console.log(e); if (e.code === 403) {
setError(e.message); setError(t('invalidOTPCode'));
} else {
setError(e.message);
}
} finally { } finally {
setLoading(false); setLoading(false);
toast.dismiss(id);
} }
}; };
return ( return (
<div className={'flex flex-col gap-5 items-center justify-center w-full px-4'}> <div className={'flex text-text-primary flex-col gap-5 items-center justify-center w-full px-4'}>
<div <div
onClick={() => { onClick={() => {
window.location.href = '/'; window.location.href = '/';
@@ -46,10 +58,10 @@ function CheckEmail ({ email, redirectTo }: {
<Logo className={'h-10 w-10'} /> <Logo className={'h-10 w-10'} />
</div> </div>
<div className={'text-xl text-text-primary font-semibold'}> <div className={'text-xl text-text-primary font-semibold'}>
{t('checkYourEmail')} {isEnter ? t('enterCode') : t('checkYourEmail')}
</div> </div>
<div className={'flex text-sm w-[320px] text-center items-center flex-col justify-center'}> <div className={'flex text-sm w-[320px] text-center items-center flex-col justify-center'}>
<div className={'font-normal'}>{t('checkEmailTip')}</div> <div className={'font-normal'}>{isEnter ? t('checkCodeTip') : t('checkEmailTip')}</div>
<div className={'font-semibold'}> <div className={'font-semibold'}>
{email} {email}
</div> </div>
@@ -77,21 +89,10 @@ function CheckEmail ({ email, redirectTo }: {
<Button <Button
onClick={handleSubmit} onClick={handleSubmit}
disabled={loading}
size={'lg'} size={'lg'}
className={'w-[320px]'} className={'w-[320px]'}
> >
{loading ? ( {t('continueToSignIn')}
<>
<Progress
size={'sm'}
variant={'theme'}
/>
{t('editor.loading')}...
</>
) : (
t('continueToSignIn')
)}
</Button> </Button>
</div> </div>
) : <Button ) : <Button
@@ -103,12 +104,11 @@ function CheckEmail ({ email, redirectTo }: {
</Button>} </Button>}
<Button <Button
size={'lg'} variant={'link'}
variant={'ghost'}
onClick={() => { onClick={() => {
window.location.href = `/login?redirectTo=${redirectTo}`; window.location.href = `/login?redirectTo=${redirectTo}`;
}} }}
className={'w-[320px] hover:bg-transparent text-text-theme hover:text-text-theme-hover h-5'} className={'w-[320px]'}
> >
{t('backToLogin')} {t('backToLogin')}
</Button> </Button>

View File

@@ -10,7 +10,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className={'py-10 flex flex-col h-full items-center justify-between gap-5 px-4'}> <div className={'py-10 text-text-primary flex flex-col h-full items-center justify-between gap-5 px-4'}>
<div className={'flex flex-1 flex-col items-center justify-center w-full gap-5'}> <div className={'flex flex-1 flex-col items-center justify-center w-full gap-5'}>
<div <div
onClick={() => { onClick={() => {
@@ -37,7 +37,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
<a <a
href={'https://appflowy.com/terms'} href={'https://appflowy.com/terms'}
target={'_blank'} target={'_blank'}
className={'text-text-theme underline'} className={'text-text-secondary underline'}
> >
{t('web.termOfUse')} {t('web.termOfUse')}
</a>{' '} </a>{' '}
@@ -45,7 +45,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
<a <a
href={'https://appflowy.com/privacy'} href={'https://appflowy.com/privacy'}
target={'_blank'} target={'_blank'}
className={'text-text-theme underline'} className={'text-text-secondary underline'}
> >
{t('web.privacyPolicy')} {t('web.privacyPolicy')}
</a> </a>
@@ -60,7 +60,7 @@ export function Login ({ redirectTo }: { redirectTo: string }) {
window.location.href = 'https://appflowy.com'; window.location.href = 'https://appflowy.com';
}} }}
className={ className={
'flex w-full cursor-pointer items-center justify-center gap-2 text-xs font-medium text-text-title opacity-60 hover:opacity-100' 'flex w-full cursor-pointer text-text-secondary items-center justify-center gap-2 text-xs font-medium'
} }
> >
<span>{t('web.visitOurWebsite')}</span> <span>{t('web.visitOurWebsite')}</span>

View File

@@ -124,9 +124,9 @@ function LoginProvider ({ redirectTo }: { redirectTo: string }) {
whileTap="tap" whileTap="tap"
> >
<Button <Button
variant={'ghost'} variant={'link'}
onClick={() => setExpand(true)} onClick={() => setExpand(true)}
className={'w-full !h-5 text-text-theme hover:bg-transparent hover:text-text-theme-hover'} className={'w-full'}
> >
{t('web.moreOptions')} {t('web.moreOptions')}
</Button> </Button>

View File

@@ -19,6 +19,7 @@ const buttonVariants = cva(
'bg-transparent text-text-error hover:bg-fill-error-select hover:text-text-error-hover border border-border-error-thick hover:border-border-error-thick-hover disabled:text-text-tertiary disabled:border-border-grey-tertiary', 'bg-transparent text-text-error hover:bg-fill-error-select hover:text-text-error-hover border border-border-error-thick hover:border-border-error-thick-hover disabled:text-text-tertiary disabled:border-border-grey-tertiary',
ghost: ghost:
'hover:bg-fill-primary-alpha-5 text-text-primary disabled:bg-fill-transparent disabled:text-text-tertiary', 'hover:bg-fill-primary-alpha-5 text-text-primary disabled:bg-fill-transparent disabled:text-text-tertiary',
link: 'hover:bg-transparent text-text-theme hover:text-text-theme-hover !h-fit',
}, },
size: { size: {
sm: 'h-7 text-sm px-4 rounded-300 gap-2 font-normal', sm: 'h-7 text-sm px-4 rounded-300 gap-2 font-normal',

View File

@@ -84,9 +84,10 @@ function Input ({
)} )}
{...props} {...props}
/> />
<div className={cn('help-text text-xs', variant === 'destructive' && 'text-text-error')}> {helpText && <div className={cn('help-text text-xs', variant === 'destructive' && 'text-text-error')}>
{helpText} {helpText}
</div> </div>}
</div> </div>
); );