mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2026-03-13 10:00:26 +08:00
fix: issues of OTP login (#92)
This commit is contained in:
@@ -56,28 +56,42 @@ export async function signInOTP ({
|
||||
access_token: string;
|
||||
expires_at: number;
|
||||
refresh_token: string;
|
||||
code?: number;
|
||||
msg?: string;
|
||||
}>('/verify', {
|
||||
email,
|
||||
token: code,
|
||||
type: 'recovery',
|
||||
});
|
||||
|
||||
const newToken = response?.data;
|
||||
const data = response?.data;
|
||||
|
||||
if (newToken) {
|
||||
refreshSessionToken(JSON.stringify(newToken));
|
||||
if (data) {
|
||||
if (data.code !== 0) {
|
||||
emit(EventType.SESSION_INVALID);
|
||||
return Promise.reject({
|
||||
code: data.code,
|
||||
message: data.msg,
|
||||
});
|
||||
}
|
||||
|
||||
refreshSessionToken(JSON.stringify(data));
|
||||
emit(EventType.SESSION_VALID);
|
||||
afterAuth();
|
||||
} else {
|
||||
emit(EventType.SESSION_INVALID);
|
||||
return Promise.reject('Failed to sign in otp');
|
||||
return Promise.reject({
|
||||
code: 'invalid_token',
|
||||
message: 'Invalid token',
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
} catch (e: any) {
|
||||
|
||||
emit(EventType.SESSION_INVALID);
|
||||
return Promise.reject({
|
||||
code: e.code,
|
||||
message: e.msg,
|
||||
message: e.message,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { notify } from '@/components/_shared/notify';
|
||||
import { AFConfigContext } from '@/components/main/app.hooks';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { ReactComponent as Logo } from '@/assets/icons/logo.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -12,6 +12,7 @@ function CheckEmail ({ email, redirectTo }: {
|
||||
redirectTo: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [error, setError] = useState<string>('');
|
||||
const [isEnter, setEnter] = useState<boolean>(false);
|
||||
const [code, setCode] = useState<string>('');
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
@@ -25,8 +26,10 @@ function CheckEmail ({ email, redirectTo }: {
|
||||
redirectTo,
|
||||
code,
|
||||
});
|
||||
} catch (e) {
|
||||
notify.error(t('web.signInError'));
|
||||
// eslint-disable-next-line
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
setError(e.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -57,9 +60,19 @@ function CheckEmail ({ email, redirectTo }: {
|
||||
autoFocus
|
||||
size={'md'}
|
||||
className={'w-[320px]'}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setError('');
|
||||
setCode(e.target.value);
|
||||
}}
|
||||
value={code}
|
||||
placeholder={t('enterCode')}
|
||||
helpText={error}
|
||||
variant={error ? 'destructive' : 'default'}
|
||||
onKeyDown={e => {
|
||||
if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) {
|
||||
void handleSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@@ -70,7 +83,10 @@ function CheckEmail ({ email, redirectTo }: {
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<Progress size={'sm'} />
|
||||
<Progress
|
||||
size={'sm'}
|
||||
variant={'theme'}
|
||||
/>
|
||||
{t('editor.loading')}...
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { toast } from 'sonner';
|
||||
import { AFConfigContext } from '@/components/main/app.hooks';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys';
|
||||
import React, { useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
|
||||
function MagicLink ({ redirectTo }: { redirectTo: string }) {
|
||||
const { t } = useTranslation();
|
||||
const [email, setEmail] = React.useState<string>('');
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
const [, setLoading] = React.useState<boolean>(false);
|
||||
const service = useContext(AFConfigContext)?.service;
|
||||
const handleSubmit = async () => {
|
||||
const isValidEmail = isEmail(email);
|
||||
@@ -23,7 +23,7 @@ function MagicLink ({ redirectTo }: { redirectTo: string }) {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await service?.signInMagicLink({
|
||||
void service?.signInMagicLink({
|
||||
email,
|
||||
redirectTo,
|
||||
});
|
||||
@@ -45,22 +45,19 @@ function MagicLink ({ redirectTo }: { redirectTo: string }) {
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
value={email}
|
||||
placeholder={t('signIn.pleaseInputYourEmail')}
|
||||
onKeyDown={e => {
|
||||
if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) {
|
||||
void handleSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={loading}
|
||||
size={'lg'}
|
||||
className={'w-[320px]'}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<Progress />
|
||||
{t('editor.loading')}...
|
||||
</>
|
||||
) : (
|
||||
t('signIn.signInWithEmail')
|
||||
)}
|
||||
{t('signIn.signInWithEmail')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -59,27 +59,36 @@ const inputVariants = cva(
|
||||
|
||||
export interface InputProps
|
||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'variant'>,
|
||||
VariantProps<typeof inputVariants> {}
|
||||
VariantProps<typeof inputVariants> {
|
||||
helpText?: string;
|
||||
}
|
||||
|
||||
function Input ({
|
||||
className,
|
||||
type,
|
||||
variant,
|
||||
size,
|
||||
helpText,
|
||||
...props
|
||||
}: InputProps) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
inputVariants({ variant, size }),
|
||||
// Invalid state styling (applied via aria-invalid attribute)
|
||||
'aria-invalid:ring-border-error-thick aria-invalid:border-border-error-thick',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<div className={'flex flex-col gap-1'}>
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
inputVariants({ variant, size }),
|
||||
// Invalid state styling (applied via aria-invalid attribute)
|
||||
'aria-invalid:ring-border-error-thick aria-invalid:border-border-error-thick',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<div className={cn('help-text text-xs', variant === 'destructive' && 'text-text-error')}>
|
||||
{helpText}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user