mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -05:00
refactor(ui): refactor the hooks
refactor the hooks
This commit is contained in:
parent
5f8875c688
commit
2fc4ec9410
9 changed files with 352 additions and 194 deletions
|
@ -7,13 +7,13 @@ import { useTimer } from 'react-timer-hook';
|
|||
import { getSendPasscodeApi, getVerifyPasscodeApi } from '@/apis/utils';
|
||||
import Passcode, { defaultLength } from '@/components/Passcode';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import usePasswordlessErrorHandler from '@/containers/PasscodeValidation/use-passwordless-error-handler';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
import { getPasscodeValidationErrorHandlersByFlowAndMethod } from './utils';
|
||||
|
||||
type Props = {
|
||||
type: UserFlow;
|
||||
|
@ -43,22 +43,28 @@ const PasscodeValidation = ({ type, method, className, target }: Props) => {
|
|||
expiryTimestamp: getTimeout(),
|
||||
});
|
||||
|
||||
const { passwordlessErrorHandlers } = usePasswordlessErrorHandler(type, target);
|
||||
// Get the flow specific error handler hook
|
||||
const useFlowErrorHandler = useMemo(
|
||||
() => getPasscodeValidationErrorHandlersByFlowAndMethod(type, method),
|
||||
[method, type]
|
||||
);
|
||||
|
||||
const { errorHandler } = useFlowErrorHandler(target);
|
||||
|
||||
const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
...errorHandler,
|
||||
'passcode.expired': (error) => {
|
||||
setError(error.message);
|
||||
},
|
||||
'passcode.code_mismatch': (error) => {
|
||||
setError(error.message);
|
||||
},
|
||||
...passwordlessErrorHandlers,
|
||||
callback: () => {
|
||||
setCode([]);
|
||||
},
|
||||
}),
|
||||
[passwordlessErrorHandlers]
|
||||
[errorHandler]
|
||||
);
|
||||
|
||||
const { result: verifyPasscodeResult, run: verifyPassCode } = useApi(
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
|
||||
const useForgotPasswordWithEmailErrorHandler = (email: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const emailNotExistForgotPasswordHandler = useCallback(async () => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t('description.forgot_password_id_does_not_exits', {
|
||||
type: t(`description.email`),
|
||||
value: email,
|
||||
}),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
}, [navigate, show, t, email]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.email_not_exists': emailNotExistForgotPasswordHandler,
|
||||
}),
|
||||
[emailNotExistForgotPasswordHandler]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useForgotPasswordWithEmailErrorHandler;
|
|
@ -0,0 +1,38 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
const useForgotPasswordWithSmsErrorHandler = (phone: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const phoneNotExistForgotPasswordHandler = useCallback(async () => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t('description.forgot_password_id_does_not_exits', {
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(phone),
|
||||
}),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
}, [navigate, show, t, phone]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.phone_not_exists': phoneNotExistForgotPasswordHandler,
|
||||
}),
|
||||
[phoneNotExistForgotPasswordHandler]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useForgotPasswordWithSmsErrorHandler;
|
|
@ -1,190 +0,0 @@
|
|||
import { useCallback, useMemo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { registerWithEmail, registerWithSms } from '@/apis/register';
|
||||
import { signInWithEmail, signInWithSms } from '@/apis/sign-in';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import { UserFlow, SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
const usePasswordlessErrorHandler = (type: UserFlow, target: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
const { run: registerWithSmsAsync } = useApi(registerWithSms);
|
||||
const { run: registerWithEmailAsync } = useApi(registerWithEmail);
|
||||
const { run: signInWithSmsAsync } = useApi(signInWithSms);
|
||||
const { run: signInWithEmailAsync } = useApi(signInWithEmail);
|
||||
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
|
||||
const phoneNotExistSignInHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.sign_in_id_does_not_exists', {
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(target),
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithSmsAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, registerWithSmsAsync, show, t, target]);
|
||||
|
||||
const emailNotExistSignInHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.sign_in_id_does_not_exists', {
|
||||
type: t(`description.email`),
|
||||
value: target,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithEmailAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, registerWithEmailAsync, show, t, target]);
|
||||
|
||||
const phoneExistRegisterHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(target),
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithSmsAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, show, signInWithSmsAsync, t, target]);
|
||||
|
||||
const emailExistRegisterHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.email`),
|
||||
value: target,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithEmailAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, show, signInWithEmailAsync, t, target]);
|
||||
|
||||
const phoneNotExistForgotPasswordHandler = useCallback(async () => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t('description.forgot_password_id_does_not_exits', {
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(target),
|
||||
}),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
}, [navigate, show, t, target]);
|
||||
|
||||
const emailNotExistForgotPasswordHandler = useCallback(async () => {
|
||||
await show({
|
||||
type: 'alert',
|
||||
ModalContent: t('description.forgot_password_id_does_not_exits', {
|
||||
type: t(`description.email`),
|
||||
value: target,
|
||||
}),
|
||||
cancelText: 'action.got_it',
|
||||
});
|
||||
navigate(-1);
|
||||
}, [navigate, show, t, target]);
|
||||
|
||||
const passwordlessErrorHandlers = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.phone_not_exists': async (error) => {
|
||||
if (type === 'forgot-password') {
|
||||
await phoneNotExistForgotPasswordHandler();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Directly display the error if user is trying to bind with social
|
||||
if (socialToBind) {
|
||||
setToast(error.message);
|
||||
}
|
||||
|
||||
await phoneNotExistSignInHandler();
|
||||
},
|
||||
'user.email_not_exists': async (error) => {
|
||||
if (type === 'forgot-password') {
|
||||
await emailNotExistForgotPasswordHandler();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Directly display the error if user is trying to bind with social
|
||||
if (socialToBind) {
|
||||
setToast(error.message);
|
||||
}
|
||||
|
||||
await emailNotExistSignInHandler();
|
||||
},
|
||||
'user.phone_exists_register': async () => {
|
||||
await phoneExistRegisterHandler();
|
||||
},
|
||||
'user.email_exists_register': async () => {
|
||||
await emailExistRegisterHandler();
|
||||
},
|
||||
}),
|
||||
[
|
||||
emailExistRegisterHandler,
|
||||
emailNotExistForgotPasswordHandler,
|
||||
emailNotExistSignInHandler,
|
||||
phoneExistRegisterHandler,
|
||||
phoneNotExistForgotPasswordHandler,
|
||||
phoneNotExistSignInHandler,
|
||||
setToast,
|
||||
socialToBind,
|
||||
type,
|
||||
]
|
||||
);
|
||||
|
||||
return {
|
||||
passwordlessErrorHandlers,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePasswordlessErrorHandler;
|
|
@ -0,0 +1,53 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { signInWithSms } from '@/apis/sign-in';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
const useRegisterWithSmsErrorHandler = (phone: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { run: signInWithSmsAsync } = useApi(signInWithSms);
|
||||
|
||||
const phoneExistRegisterHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.sign_in',
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(phone),
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithSmsAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, phone, show, signInWithSmsAsync, t]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.phone_exists_register': async () => {
|
||||
await phoneExistRegisterHandler();
|
||||
},
|
||||
}),
|
||||
[phoneExistRegisterHandler]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useRegisterWithSmsErrorHandler;
|
|
@ -0,0 +1,63 @@
|
|||
import { useCallback, useMemo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { registerWithEmail } from '@/apis/register';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import { SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
|
||||
const useSignInWithEmailErrorHandler = (email: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
const { run: registerWithEmailAsync } = useApi(registerWithEmail);
|
||||
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
|
||||
const emailNotExistSignInHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
confirmText: 'action.create',
|
||||
ModalContent: t('description.sign_in_id_does_not_exists', {
|
||||
type: t(`description.email`),
|
||||
value: email,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithEmailAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, registerWithEmailAsync, show, t, email]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.email_not_exists': async (error) => {
|
||||
// Directly display the error if user is trying to bind with social
|
||||
if (socialToBind) {
|
||||
setToast(error.message);
|
||||
}
|
||||
|
||||
await emailNotExistSignInHandler();
|
||||
},
|
||||
}),
|
||||
[emailNotExistSignInHandler, setToast, socialToBind]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSignInWithEmailErrorHandler;
|
|
@ -0,0 +1,64 @@
|
|||
import { useCallback, useMemo, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { registerWithSms } from '@/apis/register';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import { PageContext } from '@/hooks/use-page-context';
|
||||
import { SearchParameters } from '@/types';
|
||||
import { getSearchParameters } from '@/utils';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
const useSignInWithSmsErrorHandler = (phone: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
const { run: registerWithSmsAsync } = useApi(registerWithSms);
|
||||
|
||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||
|
||||
const phoneNotExistSignInHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.sign_in_id_does_not_exists', {
|
||||
confirmText: 'action.create',
|
||||
type: t(`description.phone_number`),
|
||||
value: formatPhoneNumberWithCountryCallingCode(phone),
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await registerWithSmsAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, registerWithSmsAsync, show, t, phone]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.phone_not_exists': async (error) => {
|
||||
// Directly display the error if user is trying to bind with social
|
||||
if (socialToBind) {
|
||||
setToast(error.message);
|
||||
}
|
||||
|
||||
await phoneNotExistSignInHandler();
|
||||
},
|
||||
}),
|
||||
[phoneNotExistSignInHandler, setToast, socialToBind]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSignInWithSmsErrorHandler;
|
|
@ -0,0 +1,52 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { signInWithEmail } from '@/apis/sign-in';
|
||||
import useApi, { ErrorHandlers } from '@/hooks/use-api';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
|
||||
const useRegisterWithEmailErrorHandler = (email: string) => {
|
||||
const { t } = useTranslation();
|
||||
const { show } = useConfirmModal();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { run: signInWithEmailAsync } = useApi(signInWithEmail);
|
||||
|
||||
const emailExistRegisterHandler = useCallback(async () => {
|
||||
const [confirm] = await show({
|
||||
ModalContent: t('description.create_account_id_exists', {
|
||||
confirmText: 'action.sign_in',
|
||||
type: t(`description.email`),
|
||||
value: email,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
navigate(-1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signInWithEmailAsync();
|
||||
|
||||
if (result?.redirectTo) {
|
||||
window.location.replace(result.redirectTo);
|
||||
}
|
||||
}, [navigate, show, signInWithEmailAsync, t, email]);
|
||||
|
||||
const errorHandler = useMemo<ErrorHandlers>(
|
||||
() => ({
|
||||
'user.email_exists_register': async () => {
|
||||
await emailExistRegisterHandler();
|
||||
},
|
||||
}),
|
||||
[emailExistRegisterHandler]
|
||||
);
|
||||
|
||||
return {
|
||||
errorHandler,
|
||||
};
|
||||
};
|
||||
|
||||
export default useRegisterWithEmailErrorHandler;
|
35
packages/ui/src/containers/PasscodeValidation/utils.ts
Normal file
35
packages/ui/src/containers/PasscodeValidation/utils.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { UserFlow } from '@/types';
|
||||
|
||||
import useForgotPasswordWithEmailErrorHandler from './use-forgot-password-with-email-error-handler';
|
||||
import useForgotPasswordWithSmsErrorHandler from './use-forgot-password-with-sms-error-handler';
|
||||
import useRegisterWithSmsErrorHandler from './use-register-with-sms-error-handler';
|
||||
import useSignInWithEmailErrorHandler from './use-sign-in-with-email-error-handler';
|
||||
import useSignInWithSmsErrorHandler from './use-sign-in-with-sms-error-handler';
|
||||
import useRegisterWithEmailErrorHandler from './user-register-with-email-error-handler';
|
||||
|
||||
export const getPasscodeValidationErrorHandlersByFlowAndMethod = (
|
||||
flow: UserFlow,
|
||||
method: 'email' | 'sms'
|
||||
) => {
|
||||
if (flow === 'sign-in' && method === 'email') {
|
||||
return useSignInWithEmailErrorHandler;
|
||||
}
|
||||
|
||||
if (flow === 'sign-in' && method === 'sms') {
|
||||
return useSignInWithSmsErrorHandler;
|
||||
}
|
||||
|
||||
if (flow === 'register' && method === 'email') {
|
||||
return useRegisterWithEmailErrorHandler;
|
||||
}
|
||||
|
||||
if (flow === 'register' && method === 'sms') {
|
||||
return useRegisterWithSmsErrorHandler;
|
||||
}
|
||||
|
||||
if (flow === 'forgot-password' && method === 'email') {
|
||||
return useForgotPasswordWithEmailErrorHandler;
|
||||
}
|
||||
|
||||
return useForgotPasswordWithSmsErrorHandler;
|
||||
};
|
Loading…
Add table
Reference in a new issue