0
Fork 0
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:
simeng-li 2022-10-14 10:54:57 +08:00
parent 5f8875c688
commit 2fc4ec9410
No known key found for this signature in database
GPG key ID: 14EA7BB1541E8075
9 changed files with 352 additions and 194 deletions

View file

@ -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(

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View 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;
};