mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2025-11-29 19:08:33 +08:00
chore: fix check email loading state (#93)
* chore: fix check email loading state * chore: add disabled state
This commit is contained in:
@@ -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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user