mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
refactor(ui): simplify code verification hooks (#2898)
This commit is contained in:
parent
fe14f0563c
commit
b6c684874a
19 changed files with 407 additions and 696 deletions
|
@ -134,7 +134,7 @@ export const verifyForgotPasswordVerificationCodeIdentifier = async (
|
|||
return api.post(`${interactionPrefix}/submit`).json<Response>();
|
||||
};
|
||||
|
||||
export const signInWithVerifierIdentifier = async () => {
|
||||
export const signInWithVerifiedIdentifier = async () => {
|
||||
await api.delete(`${interactionPrefix}/profile`);
|
||||
|
||||
await api.put(`${interactionPrefix}/event`, {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { SignInIdentifier } from '@logto/schemas';
|
||||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
|
@ -10,7 +10,7 @@ import { UserFlow } from '@/types';
|
|||
import PasswordSignInLink from './PasswordSignInLink';
|
||||
import * as styles from './index.module.scss';
|
||||
import useResendVerificationCode from './use-resend-verification-code';
|
||||
import { getVerificationCodeHook } from './utils';
|
||||
import { getCodeVerificationHookByFlow } from './utils';
|
||||
|
||||
type Props = {
|
||||
type: UserFlow;
|
||||
|
@ -23,13 +23,18 @@ type Props = {
|
|||
const VerificationCode = ({ type, method, className, hasPasswordButton, target }: Props) => {
|
||||
const [code, setCode] = useState<string[]>([]);
|
||||
const { t } = useTranslation();
|
||||
const useVerificationCode = getVerificationCodeHook(type, method);
|
||||
|
||||
const useVerificationCode = getCodeVerificationHookByFlow(type);
|
||||
|
||||
const errorCallback = useCallback(() => {
|
||||
setCode([]);
|
||||
}, []);
|
||||
|
||||
const { errorMessage, clearErrorMessage, onSubmit } = useVerificationCode(target, errorCallback);
|
||||
const { errorMessage, clearErrorMessage, onSubmit } = useVerificationCode(
|
||||
method,
|
||||
target,
|
||||
errorCallback
|
||||
);
|
||||
|
||||
const { seconds, isRunning, onResendVerificationCode } = useResendVerificationCode(
|
||||
type,
|
||||
|
@ -39,9 +44,13 @@ const VerificationCode = ({ type, method, className, hasPasswordButton, target }
|
|||
|
||||
useEffect(() => {
|
||||
if (code.length === defaultLength && code.every(Boolean)) {
|
||||
void onSubmit(code.join(''));
|
||||
const payload =
|
||||
method === SignInIdentifier.Email
|
||||
? { email: target, verificationCode: code.join('') }
|
||||
: { phone: target, verificationCode: code.join('') };
|
||||
void onSubmit(payload);
|
||||
}
|
||||
}, [code, onSubmit, target]);
|
||||
}, [code, method, onSubmit, target]);
|
||||
|
||||
return (
|
||||
<form className={classNames(styles.form, className)}>
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import type { EmailVerificationCodePayload, PhoneVerificationCodePayload } from '@logto/schemas';
|
||||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useMemo, useCallback } from 'react';
|
||||
|
||||
import { addProfileWithVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import type { VerificationCodeIdentifier } from '@/types';
|
||||
import { SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import useGeneralVerificationCodeErrorHandler from './use-general-verification-code-error-handler';
|
||||
import useIdentifierErrorAlert, { IdentifierErrorType } from './use-identifier-error-alert';
|
||||
|
||||
const useContinueFlowCodeVerification = (
|
||||
_method: VerificationCodeIdentifier,
|
||||
target: string,
|
||||
errorCallback?: () => void
|
||||
) => {
|
||||
const { generalVerificationCodeErrorHandlers, errorMessage, clearErrorMessage } =
|
||||
useGeneralVerificationCodeErrorHandler();
|
||||
|
||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const identifierErrorHandler = useIdentifierErrorAlert();
|
||||
|
||||
const verifyVerificationCodeErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.phone_already_in_use': () => {
|
||||
void identifierErrorHandler(
|
||||
IdentifierErrorType.IdentifierAlreadyExists,
|
||||
SignInIdentifier.Phone,
|
||||
target
|
||||
);
|
||||
},
|
||||
'user.email_already_in_use': () => {
|
||||
void identifierErrorHandler(
|
||||
IdentifierErrorType.IdentifierAlreadyExists,
|
||||
SignInIdentifier.Email,
|
||||
target
|
||||
);
|
||||
},
|
||||
...requiredProfileErrorHandler,
|
||||
...generalVerificationCodeErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
errorCallback,
|
||||
target,
|
||||
identifierErrorHandler,
|
||||
requiredProfileErrorHandler,
|
||||
generalVerificationCodeErrorHandlers,
|
||||
]
|
||||
);
|
||||
|
||||
const { run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
verifyVerificationCodeErrorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (payload: EmailVerificationCodePayload | PhoneVerificationCodePayload) => {
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
const result = await verifyVerificationCode(payload, socialToBind);
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
},
|
||||
[verifyVerificationCode]
|
||||
);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useContinueFlowCodeVerification;
|
|
@ -1,64 +0,0 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useMemo, useCallback } from 'react';
|
||||
|
||||
import { addProfileWithVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useContinueSetEmailVerificationCode = (email: string, errorCallback?: () => void) => {
|
||||
const { sharedErrorHandlers, errorMessage, clearErrorMessage } = useSharedErrorHandler();
|
||||
|
||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.continue,
|
||||
SignInIdentifier.Email,
|
||||
email
|
||||
);
|
||||
|
||||
const verifyVerificationCodeErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.email_already_in_use': identifierNotExistErrorHandler,
|
||||
...requiredProfileErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
errorCallback,
|
||||
identifierNotExistErrorHandler,
|
||||
requiredProfileErrorHandler,
|
||||
sharedErrorHandlers,
|
||||
]
|
||||
);
|
||||
|
||||
const { run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
verifyVerificationCodeErrorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
const result = await verifyVerificationCode({ email, verificationCode }, socialToBind);
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
},
|
||||
[email, verifyVerificationCode]
|
||||
);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useContinueSetEmailVerificationCode;
|
|
@ -1,59 +0,0 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useMemo, useCallback } from 'react';
|
||||
|
||||
import { addProfileWithVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useContinueSetPhoneVerificationCode = (phone: string, errorCallback?: () => void) => {
|
||||
const { sharedErrorHandlers, errorMessage, clearErrorMessage } = useSharedErrorHandler();
|
||||
|
||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const identifierExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.continue,
|
||||
SignInIdentifier.Phone,
|
||||
phone
|
||||
);
|
||||
|
||||
const verifyVerificationCodeErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.phone_already_in_use': identifierExistErrorHandler,
|
||||
...requiredProfileErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[errorCallback, identifierExistErrorHandler, requiredProfileErrorHandler, sharedErrorHandlers]
|
||||
);
|
||||
|
||||
const { run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
verifyVerificationCodeErrorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
const result = await verifyVerificationCode({ phone, verificationCode }, socialToBind);
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
},
|
||||
[phone, verifyVerificationCode]
|
||||
);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useContinueSetPhoneVerificationCode;
|
|
@ -1,60 +0,0 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useMemo, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { verifyForgotPasswordVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useForgotPasswordEmailVerificationCode = (email: string, errorCallback?: () => void) => {
|
||||
const navigate = useNavigate();
|
||||
const { sharedErrorHandlers, errorMessage, clearErrorMessage } = useSharedErrorHandler();
|
||||
|
||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.forgotPassword,
|
||||
SignInIdentifier.Email,
|
||||
email
|
||||
);
|
||||
|
||||
const errorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.user_not_exist': identifierNotExistErrorHandler,
|
||||
'user.new_password_required_in_profile': () => {
|
||||
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
||||
},
|
||||
...sharedErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[identifierNotExistErrorHandler, sharedErrorHandlers, errorCallback, navigate]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
verifyForgotPasswordVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return verifyVerificationCode({ email, verificationCode });
|
||||
},
|
||||
[email, verifyVerificationCode]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result) {
|
||||
navigate(`/${UserFlow.signIn}`, { replace: true });
|
||||
}
|
||||
}, [navigate, result]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useForgotPasswordEmailVerificationCode;
|
|
@ -0,0 +1,71 @@
|
|||
import type { EmailVerificationCodePayload, PhoneVerificationCodePayload } from '@logto/schemas';
|
||||
import { useMemo, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { verifyForgotPasswordVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import type { VerificationCodeIdentifier } from '@/types';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useGeneralVerificationCodeErrorHandler from './use-general-verification-code-error-handler';
|
||||
import useIdentifierErrorAlert, { IdentifierErrorType } from './use-identifier-error-alert';
|
||||
|
||||
const useForgotPasswordFlowCodeVerification = (
|
||||
method: VerificationCodeIdentifier,
|
||||
target: string,
|
||||
errorCallback?: () => void
|
||||
) => {
|
||||
const navigate = useNavigate();
|
||||
const { generalVerificationCodeErrorHandlers, errorMessage, clearErrorMessage } =
|
||||
useGeneralVerificationCodeErrorHandler();
|
||||
|
||||
const identifierErrorHandler = useIdentifierErrorAlert();
|
||||
|
||||
const errorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.user_not_exist': () => {
|
||||
void identifierErrorHandler(IdentifierErrorType.IdentifierNotExist, method, target);
|
||||
},
|
||||
'user.new_password_required_in_profile': () => {
|
||||
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
||||
},
|
||||
...generalVerificationCodeErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
generalVerificationCodeErrorHandlers,
|
||||
errorCallback,
|
||||
identifierErrorHandler,
|
||||
method,
|
||||
target,
|
||||
navigate,
|
||||
]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
verifyForgotPasswordVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (payload: EmailVerificationCodePayload | PhoneVerificationCodePayload) => {
|
||||
return verifyVerificationCode(payload);
|
||||
},
|
||||
[verifyVerificationCode]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result) {
|
||||
navigate(`/${UserFlow.signIn}`, { replace: true });
|
||||
}
|
||||
}, [navigate, result]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useForgotPasswordFlowCodeVerification;
|
|
@ -1,60 +0,0 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useMemo, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { verifyForgotPasswordVerificationCodeIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useForgotPasswordPhoneVerificationCode = (phone: string, errorCallback?: () => void) => {
|
||||
const navigate = useNavigate();
|
||||
const { sharedErrorHandlers, errorMessage, clearErrorMessage } = useSharedErrorHandler();
|
||||
|
||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.forgotPassword,
|
||||
SignInIdentifier.Phone,
|
||||
phone
|
||||
);
|
||||
|
||||
const errorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'user.user_not_exist': identifierNotExistErrorHandler,
|
||||
'user.new_password_required_in_profile': () => {
|
||||
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
||||
},
|
||||
...sharedErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[identifierNotExistErrorHandler, sharedErrorHandlers, errorCallback, navigate]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
verifyForgotPasswordVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return verifyVerificationCode({ phone, verificationCode });
|
||||
},
|
||||
[phone, verifyVerificationCode]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result) {
|
||||
navigate(`/${UserFlow.signIn}`, { replace: true });
|
||||
}
|
||||
}, [navigate, result]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useForgotPasswordPhoneVerificationCode;
|
|
@ -2,11 +2,11 @@ import { useState, useMemo } from 'react';
|
|||
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
|
||||
const useSharedErrorHandler = () => {
|
||||
const useGeneralVerificationCodeErrorHandler = () => {
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
|
||||
// Have to wrap up in a useMemo hook otherwise the handler updates on every cycle
|
||||
const sharedErrorHandlers: ErrorHandlers = useMemo(
|
||||
const generalVerificationCodeErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
'verification_code.expired': (error) => {
|
||||
setErrorMessage(error.message);
|
||||
|
@ -20,11 +20,11 @@ const useSharedErrorHandler = () => {
|
|||
|
||||
return {
|
||||
errorMessage,
|
||||
sharedErrorHandlers,
|
||||
generalVerificationCodeErrorHandlers,
|
||||
clearErrorMessage: () => {
|
||||
setErrorMessage('');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default useSharedErrorHandler;
|
||||
export default useGeneralVerificationCodeErrorHandler;
|
|
@ -4,34 +4,44 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { UserFlow } from '@/types';
|
||||
import type { VerificationCodeIdentifier } from '@/types';
|
||||
|
||||
const useIdentifierErrorAlert = (
|
||||
flow: UserFlow,
|
||||
method: SignInIdentifier.Email | SignInIdentifier.Phone,
|
||||
value: string
|
||||
) => {
|
||||
export enum IdentifierErrorType {
|
||||
IdentifierNotExist = 'IdentifierNotExist',
|
||||
IdentifierAlreadyExists = 'IdentifierAlreadyExists',
|
||||
}
|
||||
|
||||
const useIdentifierErrorAlert = () => {
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Have to wrap up in a useCallback hook otherwise the handler updates on every cycle
|
||||
return useCallback(async () => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t(
|
||||
flow === UserFlow.register || flow === UserFlow.continue
|
||||
? 'description.create_account_id_exists_alert'
|
||||
: 'description.sign_in_id_does_not_exist_alert',
|
||||
{
|
||||
type: t(`description.${method === SignInIdentifier.Email ? 'email' : 'phone_number'}`),
|
||||
value,
|
||||
}
|
||||
),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
}, [flow, method, navigate, show, t, value]);
|
||||
return useCallback(
|
||||
async (
|
||||
errorType: IdentifierErrorType,
|
||||
identifierType: VerificationCodeIdentifier,
|
||||
identifier: string
|
||||
) => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t(
|
||||
errorType === IdentifierErrorType.IdentifierAlreadyExists
|
||||
? 'description.create_account_id_exists_alert'
|
||||
: 'description.sign_in_id_does_not_exist_alert',
|
||||
{
|
||||
type: t(
|
||||
`description.${identifierType === SignInIdentifier.Email ? 'email' : 'phone_number'}`
|
||||
),
|
||||
identifier,
|
||||
}
|
||||
),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
},
|
||||
[navigate, show, t]
|
||||
);
|
||||
};
|
||||
|
||||
export default useIdentifierErrorAlert;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
import type { EmailVerificationCodePayload, PhoneVerificationCodePayload } from '@logto/schemas';
|
||||
import { SignInIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
signInWithVerifiedIdentifier,
|
||||
} from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import type { VerificationCodeIdentifier } from '@/types';
|
||||
|
||||
import useGeneralVerificationCodeErrorHandler from './use-general-verification-code-error-handler';
|
||||
import useIdentifierErrorAlert, { IdentifierErrorType } from './use-identifier-error-alert';
|
||||
|
||||
const useRegisterFlowCodeVerification = (
|
||||
method: VerificationCodeIdentifier,
|
||||
target: string,
|
||||
errorCallback?: () => void
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { errorMessage, clearErrorMessage, generalVerificationCodeErrorHandlers } =
|
||||
useGeneralVerificationCodeErrorHandler();
|
||||
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const { run: signInWithIdentifierAsync } = useApi(
|
||||
signInWithVerifiedIdentifier,
|
||||
requiredProfileErrorHandlers
|
||||
);
|
||||
|
||||
const showIdentifierErrorAlert = useIdentifierErrorAlert();
|
||||
|
||||
const identifierExistErrorHandler = useCallback(async () => {
|
||||
// Should not redirect user to sign-in if is register-only mode
|
||||
if (signInMode === SignInMode.Register) {
|
||||
void showIdentifierErrorAlert(IdentifierErrorType.IdentifierAlreadyExists, method, target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.sign_in',
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.${method === SignInIdentifier.Email ? 'email' : 'phone_number'}`),
|
||||
value: target,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithIdentifierAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [
|
||||
method,
|
||||
navigate,
|
||||
show,
|
||||
showIdentifierErrorAlert,
|
||||
signInMode,
|
||||
signInWithIdentifierAsync,
|
||||
t,
|
||||
target,
|
||||
]);
|
||||
|
||||
const errorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.email_already_in_use': identifierExistErrorHandler,
|
||||
'user.phone_already_in_use': identifierExistErrorHandler,
|
||||
...generalVerificationCodeErrorHandlers,
|
||||
...requiredProfileErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
errorCallback,
|
||||
identifierExistErrorHandler,
|
||||
requiredProfileErrorHandlers,
|
||||
generalVerificationCodeErrorHandlers,
|
||||
]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (payload: EmailVerificationCodePayload | PhoneVerificationCodePayload) => {
|
||||
return verifyVerificationCode(payload);
|
||||
},
|
||||
[verifyVerificationCode]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [result]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useRegisterFlowCodeVerification;
|
|
@ -1,108 +0,0 @@
|
|||
import { SignInIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
signInWithVerifierIdentifier,
|
||||
} from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useRegisterWithEmailVerificationCode = (email: string, errorCallback?: () => void) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { errorMessage, clearErrorMessage, sharedErrorHandlers } = useSharedErrorHandler();
|
||||
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const { run: signInWithEmailAsync } = useApi(
|
||||
signInWithVerifierIdentifier,
|
||||
requiredProfileErrorHandlers
|
||||
);
|
||||
|
||||
const identifierExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.register,
|
||||
SignInIdentifier.Email,
|
||||
email
|
||||
);
|
||||
|
||||
const emailExistSignInErrorHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.sign_in',
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.email`),
|
||||
value: email,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithEmailAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [email, navigate, show, signInWithEmailAsync, t]);
|
||||
|
||||
const errorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.email_already_in_use':
|
||||
signInMode === SignInMode.Register
|
||||
? identifierExistErrorHandler
|
||||
: emailExistSignInErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
...requiredProfileErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
emailExistSignInErrorHandler,
|
||||
errorCallback,
|
||||
identifierExistErrorHandler,
|
||||
requiredProfileErrorHandlers,
|
||||
sharedErrorHandlers,
|
||||
signInMode,
|
||||
]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return verifyVerificationCode({ email, verificationCode });
|
||||
},
|
||||
[email, verifyVerificationCode]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [result]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useRegisterWithEmailVerificationCode;
|
|
@ -1,111 +0,0 @@
|
|||
import { SignInIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
signInWithVerifierIdentifier,
|
||||
} from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import { UserFlow } from '@/types';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useRegisterWithPhoneVerificationCode = (phone: string, errorCallback?: () => void) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { errorMessage, clearErrorMessage, sharedErrorHandlers } = useSharedErrorHandler();
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const { run: signInWithPhoneAsync } = useApi(
|
||||
signInWithVerifierIdentifier,
|
||||
requiredProfileErrorHandlers
|
||||
);
|
||||
|
||||
const identifierExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.register,
|
||||
SignInIdentifier.Phone,
|
||||
formatPhoneNumberWithCountryCallingCode(phone)
|
||||
);
|
||||
|
||||
const phoneExistSignInErrorHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.sign_in',
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.phone_number`),
|
||||
value: phone,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithPhoneAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [phone, navigate, show, signInWithPhoneAsync, t]);
|
||||
|
||||
const errorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.phone_already_in_use':
|
||||
signInMode === SignInMode.Register
|
||||
? identifierExistErrorHandler
|
||||
: phoneExistSignInErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
...requiredProfileErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
signInMode,
|
||||
identifierExistErrorHandler,
|
||||
phoneExistSignInErrorHandler,
|
||||
sharedErrorHandlers,
|
||||
requiredProfileErrorHandlers,
|
||||
errorCallback,
|
||||
]
|
||||
);
|
||||
|
||||
const { result, run: verifyVerificationCode } = useApi(
|
||||
addProfileWithVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [result]);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return verifyVerificationCode({
|
||||
phone,
|
||||
verificationCode,
|
||||
});
|
||||
},
|
||||
[phone, verifyVerificationCode]
|
||||
);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useRegisterWithPhoneVerificationCode;
|
|
@ -1,3 +1,4 @@
|
|||
import type { EmailVerificationCodePayload, PhoneVerificationCodePayload } from '@logto/schemas';
|
||||
import { SignInIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
@ -12,41 +13,51 @@ import useApi from '@/hooks/use-api';
|
|||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import type { VerificationCodeIdentifier } from '@/types';
|
||||
import { SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
import useGeneralVerificationCodeErrorHandler from './use-general-verification-code-error-handler';
|
||||
import useIdentifierErrorAlert, { IdentifierErrorType } from './use-identifier-error-alert';
|
||||
|
||||
const useSignInWithPhoneVerificationCode = (phone: string, errorCallback?: () => void) => {
|
||||
const useSignInFlowCodeVerification = (
|
||||
method: VerificationCodeIdentifier,
|
||||
target: string,
|
||||
errorCallback?: () => void
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { errorMessage, clearErrorMessage, sharedErrorHandlers } = useSharedErrorHandler();
|
||||
|
||||
const { errorMessage, clearErrorMessage, generalVerificationCodeErrorHandlers } =
|
||||
useGeneralVerificationCodeErrorHandler();
|
||||
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const { run: registerWithPhoneAsync } = useApi(
|
||||
const { run: registerWithIdentifierAsync } = useApi(
|
||||
registerWithVerifiedIdentifier,
|
||||
requiredProfileErrorHandlers
|
||||
);
|
||||
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
|
||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.signIn,
|
||||
SignInIdentifier.Phone,
|
||||
phone
|
||||
);
|
||||
const showIdentifierErrorAlert = useIdentifierErrorAlert();
|
||||
|
||||
const identifierNotExistErrorHandler = useCallback(async () => {
|
||||
// Should not redirect user to register if is sign-in only mode or bind social flow
|
||||
if (signInMode === SignInMode.SignIn || socialToBind) {
|
||||
void showIdentifierErrorAlert(IdentifierErrorType.IdentifierNotExist, method, target);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const phoneNotExistRegisterErrorHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.create',
|
||||
ModalContent: t('description.sign_in_id_does_not_exist', {
|
||||
type: t(`description.phone_number`),
|
||||
value: phone,
|
||||
ype: t(`description.${method === SignInIdentifier.Email ? 'email' : 'phone_number'}`),
|
||||
value: target,
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -56,32 +67,37 @@ const useSignInWithPhoneVerificationCode = (phone: string, errorCallback?: () =>
|
|||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithPhoneAsync({ phone });
|
||||
const result = await registerWithIdentifierAsync(
|
||||
method === SignInIdentifier.Email ? { email: target } : { phone: target }
|
||||
);
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [phone, navigate, show, registerWithPhoneAsync, t]);
|
||||
}, [
|
||||
method,
|
||||
navigate,
|
||||
registerWithIdentifierAsync,
|
||||
show,
|
||||
showIdentifierErrorAlert,
|
||||
signInMode,
|
||||
socialToBind,
|
||||
t,
|
||||
target,
|
||||
]);
|
||||
|
||||
const errorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.user_not_exist':
|
||||
// Block user auto register if is bind social or sign-in only flow
|
||||
signInMode === SignInMode.SignIn || socialToBind
|
||||
? identifierNotExistErrorHandler
|
||||
: phoneNotExistRegisterErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
'user.user_not_exist': identifierNotExistErrorHandler,
|
||||
...generalVerificationCodeErrorHandlers,
|
||||
...requiredProfileErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
signInMode,
|
||||
socialToBind,
|
||||
identifierNotExistErrorHandler,
|
||||
phoneNotExistRegisterErrorHandler,
|
||||
sharedErrorHandlers,
|
||||
requiredProfileErrorHandlers,
|
||||
errorCallback,
|
||||
identifierNotExistErrorHandler,
|
||||
requiredProfileErrorHandlers,
|
||||
generalVerificationCodeErrorHandlers,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -97,16 +113,10 @@ const useSignInWithPhoneVerificationCode = (phone: string, errorCallback?: () =>
|
|||
}, [result]);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return asyncSignInWithVerificationCodeIdentifier(
|
||||
{
|
||||
phone,
|
||||
verificationCode,
|
||||
},
|
||||
socialToBind
|
||||
);
|
||||
async (payload: EmailVerificationCodePayload | PhoneVerificationCodePayload) => {
|
||||
return asyncSignInWithVerificationCodeIdentifier(payload, socialToBind);
|
||||
},
|
||||
[phone, socialToBind, asyncSignInWithVerificationCodeIdentifier]
|
||||
[asyncSignInWithVerificationCodeIdentifier, socialToBind]
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -116,4 +126,4 @@ const useSignInWithPhoneVerificationCode = (phone: string, errorCallback?: () =>
|
|||
};
|
||||
};
|
||||
|
||||
export default useSignInWithPhoneVerificationCode;
|
||||
export default useSignInFlowCodeVerification;
|
|
@ -1,119 +0,0 @@
|
|||
import { SignInIdentifier, SignInMode } from '@logto/schemas';
|
||||
import { useMemo, useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
signInWithVerificationCodeIdentifier,
|
||||
registerWithVerifiedIdentifier,
|
||||
} from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||
import { useSieMethods } from '@/hooks/use-sie';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import useIdentifierErrorAlert from './use-identifier-error-alert';
|
||||
import useSharedErrorHandler from './use-shared-error-handler';
|
||||
|
||||
const useSignInWithEmailVerificationCode = (email: string, errorCallback?: () => void) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { errorMessage, clearErrorMessage, sharedErrorHandlers } = useSharedErrorHandler();
|
||||
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler(true);
|
||||
|
||||
const { run: registerWithEmailAsync } = useApi(
|
||||
registerWithVerifiedIdentifier,
|
||||
requiredProfileErrorHandlers
|
||||
);
|
||||
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
|
||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
||||
UserFlow.signIn,
|
||||
SignInIdentifier.Email,
|
||||
email
|
||||
);
|
||||
|
||||
const emailNotExistRegisterErrorHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.create',
|
||||
ModalContent: t('description.sign_in_id_does_not_exist', {
|
||||
type: t(`description.email`),
|
||||
value: email,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithEmailAsync({ email });
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [email, navigate, show, registerWithEmailAsync, t]);
|
||||
|
||||
const errorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.user_not_exist':
|
||||
// Block user auto register if is bind social or sign-in only flow
|
||||
signInMode === SignInMode.SignIn || socialToBind
|
||||
? identifierNotExistErrorHandler
|
||||
: emailNotExistRegisterErrorHandler,
|
||||
...sharedErrorHandlers,
|
||||
...requiredProfileErrorHandlers,
|
||||
callback: errorCallback,
|
||||
}),
|
||||
[
|
||||
emailNotExistRegisterErrorHandler,
|
||||
errorCallback,
|
||||
identifierNotExistErrorHandler,
|
||||
requiredProfileErrorHandlers,
|
||||
sharedErrorHandlers,
|
||||
signInMode,
|
||||
socialToBind,
|
||||
]
|
||||
);
|
||||
|
||||
const { result, run: asyncSignInWithVerificationCodeIdentifier } = useApi(
|
||||
signInWithVerificationCodeIdentifier,
|
||||
errorHandlers
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [result]);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (verificationCode: string) => {
|
||||
return asyncSignInWithVerificationCodeIdentifier(
|
||||
{
|
||||
email,
|
||||
verificationCode,
|
||||
},
|
||||
socialToBind
|
||||
);
|
||||
},
|
||||
[asyncSignInWithVerificationCodeIdentifier, email, socialToBind]
|
||||
);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
clearErrorMessage,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSignInWithEmailVerificationCode;
|
|
@ -1,36 +1,15 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useContinueSetEmailVerificationCode from './use-continue-set-email-verification-code-validation';
|
||||
import useContinueSetPhoneVerificationCode from './use-continue-set-phone-verification-code-validation';
|
||||
import useForgotPasswordEmailVerificationCode from './use-forgot-password-email-verification-code-validation';
|
||||
import useForgotPasswordPhoneVerificationCode from './use-forgot-password-phone-verification-code-validation';
|
||||
import useRegisterWithEmailVerificationCode from './use-register-with-email-verification-code-validation';
|
||||
import useRegisterWithPhoneVerificationCode from './use-register-with-phone-verification-code-validation';
|
||||
import useSignInWithEmailVerificationCode from './use-sign-in-with-email-verification-code-validation';
|
||||
import useSignInWithPhoneVerificationCode from './use-sign-in-with-phone-verification-code-validation';
|
||||
import useContinueFlowCodeVerification from './use-continue-flow-code-verification';
|
||||
import useForgotPasswordFlowCodeVerification from './use-forgot-password-flow-code-verification';
|
||||
import useRegisterFlowCodeVerification from './use-register-flow-code-verification';
|
||||
import useSignInFlowCodeVerification from './use-sign-in-flow-code-verification';
|
||||
|
||||
export const getVerificationCodeHook = (
|
||||
type: UserFlow,
|
||||
method: SignInIdentifier.Email | SignInIdentifier.Phone
|
||||
) => {
|
||||
switch (type) {
|
||||
case UserFlow.signIn:
|
||||
return method === SignInIdentifier.Email
|
||||
? useSignInWithEmailVerificationCode
|
||||
: useSignInWithPhoneVerificationCode;
|
||||
case UserFlow.register:
|
||||
return method === SignInIdentifier.Email
|
||||
? useRegisterWithEmailVerificationCode
|
||||
: useRegisterWithPhoneVerificationCode;
|
||||
case UserFlow.forgotPassword:
|
||||
return method === SignInIdentifier.Email
|
||||
? useForgotPasswordEmailVerificationCode
|
||||
: useForgotPasswordPhoneVerificationCode;
|
||||
default:
|
||||
return method === SignInIdentifier.Email
|
||||
? useContinueSetEmailVerificationCode
|
||||
: useContinueSetPhoneVerificationCode;
|
||||
}
|
||||
};
|
||||
export const codeVerificationHooks = Object.freeze({
|
||||
[UserFlow.signIn]: useSignInFlowCodeVerification,
|
||||
[UserFlow.register]: useRegisterFlowCodeVerification,
|
||||
[UserFlow.forgotPassword]: useForgotPasswordFlowCodeVerification,
|
||||
[UserFlow.continue]: useContinueFlowCodeVerification,
|
||||
});
|
||||
|
||||
export const getCodeVerificationHookByFlow = (flow: UserFlow) => codeVerificationHooks[flow];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { t } from 'i18next';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
import { is } from 'superstruct';
|
||||
import { is, validate } from 'superstruct';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import VerificationCodeContainer from '@/containers/VerificationCode';
|
||||
|
@ -15,7 +15,7 @@ import {
|
|||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
type Parameters = {
|
||||
type: UserFlow;
|
||||
type: string;
|
||||
method: string;
|
||||
};
|
||||
|
||||
|
@ -24,11 +24,12 @@ const VerificationCode = () => {
|
|||
const { signInMethods } = useSieMethods();
|
||||
const { state } = useLocation();
|
||||
|
||||
const invalidType = !is(type, userFlowGuard);
|
||||
const invalidMethod = !is(method, verificationCodeMethodGuard);
|
||||
const invalidState = !is(state, verificationCodeStateGuard);
|
||||
|
||||
if (invalidType || invalidMethod) {
|
||||
const [, flow] = validate(type, userFlowGuard);
|
||||
|
||||
if (!flow || invalidMethod) {
|
||||
return <ErrorPage />;
|
||||
}
|
||||
|
||||
|
@ -55,7 +56,7 @@ const VerificationCode = () => {
|
|||
}}
|
||||
>
|
||||
<VerificationCodeContainer
|
||||
type={type}
|
||||
type={flow}
|
||||
method={method}
|
||||
target={target}
|
||||
hasPasswordButton={type === UserFlow.signIn && methodSettings?.password}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { SignInIdentifier, MissingProfile } from '@logto/schemas';
|
||||
import * as s from 'superstruct';
|
||||
|
||||
import { UserFlow } from '.';
|
||||
|
||||
export const bindSocialStateGuard = s.object({
|
||||
relatedUser: s.object({
|
||||
type: s.union([s.literal('email'), s.literal('phone')]),
|
||||
|
@ -24,11 +26,11 @@ export const SignInMethodGuard = s.union([
|
|||
s.literal(SignInIdentifier.Username),
|
||||
]);
|
||||
|
||||
export const userFlowGuard = s.union([
|
||||
s.literal('sign-in'),
|
||||
s.literal('register'),
|
||||
s.literal('forgot-password'),
|
||||
s.literal('continue'),
|
||||
export const userFlowGuard = s.enums([
|
||||
UserFlow.signIn,
|
||||
UserFlow.register,
|
||||
UserFlow.forgotPassword,
|
||||
UserFlow.continue,
|
||||
]);
|
||||
|
||||
export const continueMethodGuard = s.union([
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import type { SignInExperience, ConnectorMetadata, AppearanceMode } from '@logto/schemas';
|
||||
import type {
|
||||
SignInExperience,
|
||||
ConnectorMetadata,
|
||||
AppearanceMode,
|
||||
SignInIdentifier,
|
||||
} from '@logto/schemas';
|
||||
|
||||
export enum UserFlow {
|
||||
signIn = 'sign-in',
|
||||
|
@ -18,6 +23,8 @@ export type Platform = 'web' | 'mobile';
|
|||
// TODO: @simeng, @sijie, @charles should we combine this with admin console?
|
||||
export type Theme = 'dark' | 'light';
|
||||
|
||||
export type VerificationCodeIdentifier = SignInIdentifier.Email | SignInIdentifier.Phone;
|
||||
|
||||
// Omit socialSignInConnectorTargets since it is being translated into socialConnectors
|
||||
export type SignInExperienceResponse = Omit<SignInExperience, 'socialSignInConnectorTargets'> & {
|
||||
socialConnectors: ConnectorMetadata[];
|
||||
|
|
Loading…
Add table
Reference in a new issue