mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
feat(ui): show notification on the sign-in profile fulfill page (#3012)
This commit is contained in:
parent
f17b69a513
commit
96df7698c5
11 changed files with 83 additions and 20 deletions
|
@ -14,6 +14,7 @@ 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 { UserFlow } from '@/types';
|
||||
import { formatPhoneNumberWithCountryCallingCode } from '@/utils/country-code';
|
||||
|
||||
import useGeneralVerificationCodeErrorHandler from './use-general-verification-code-error-handler';
|
||||
|
@ -33,7 +34,10 @@ const useSignInFlowCodeVerification = (
|
|||
|
||||
const { signInMode } = useSieMethods();
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler({ replace: true });
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler({
|
||||
replace: true,
|
||||
flow: UserFlow.signIn,
|
||||
});
|
||||
|
||||
const { run: registerWithIdentifierAsync } = useApi(
|
||||
registerWithVerifiedIdentifier,
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { PasswordSignInPayload } from '@/apis/interaction';
|
|||
import { signInWithPasswordIdentifier } from '@/apis/interaction';
|
||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { UserFlow } from '@/types';
|
||||
|
||||
import useRequiredProfileErrorHandler from './use-required-profile-error-handler';
|
||||
|
||||
|
@ -14,7 +15,7 @@ const usePasswordSignIn = () => {
|
|||
setErrorMessage('');
|
||||
}, []);
|
||||
|
||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler();
|
||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler({ flow: UserFlow.signIn });
|
||||
|
||||
const errorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -13,9 +13,10 @@ import { PageContext } from './use-page-context';
|
|||
type Options = {
|
||||
replace?: boolean;
|
||||
linkSocial?: string;
|
||||
flow?: UserFlow;
|
||||
};
|
||||
|
||||
const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) => {
|
||||
const useRequiredProfileErrorHandler = ({ replace, linkSocial, flow }: Options = {}) => {
|
||||
const navigate = useNavigate();
|
||||
const { setToast } = useContext(PageContext);
|
||||
|
||||
|
@ -37,7 +38,7 @@ const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) =
|
|||
{
|
||||
pathname: `/${UserFlow.continue}/${missingProfile}`,
|
||||
},
|
||||
{ replace }
|
||||
{ replace, state: { flow } }
|
||||
);
|
||||
break;
|
||||
case MissingProfile.email:
|
||||
|
@ -47,7 +48,7 @@ const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) =
|
|||
pathname: `/${UserFlow.continue}/${missingProfile}`,
|
||||
search: linkSocialQueryString,
|
||||
},
|
||||
{ replace, state: { registeredSocialIdentity } }
|
||||
{ replace, state: { registeredSocialIdentity, flow } }
|
||||
);
|
||||
break;
|
||||
case MissingProfile.emailOrPhone:
|
||||
|
@ -56,7 +57,7 @@ const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) =
|
|||
pathname: `/${UserFlow.continue}/email-or-phone/email`,
|
||||
search: linkSocialQueryString,
|
||||
},
|
||||
{ replace }
|
||||
{ replace, state: { registeredSocialIdentity, flow } }
|
||||
);
|
||||
break;
|
||||
|
||||
|
@ -67,7 +68,7 @@ const useRequiredProfileErrorHandler = ({ replace, linkSocial }: Options = {}) =
|
|||
}
|
||||
},
|
||||
}),
|
||||
[linkSocial, navigate, replace, setToast]
|
||||
[flow, linkSocial, navigate, replace, setToast]
|
||||
);
|
||||
|
||||
return requiredProfileErrorHandler;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom';
|
|||
import { validate } from 'superstruct';
|
||||
|
||||
import { signInWithSocial } from '@/apis/interaction';
|
||||
import { UserFlow } from '@/types';
|
||||
import { socialAccountNotExistErrorDataGuard } from '@/types/guard';
|
||||
import { parseQueryParameters } from '@/utils';
|
||||
import { stateValidation } from '@/utils/social-connectors';
|
||||
|
@ -50,7 +51,7 @@ const useSocialSignInListener = (connectorId?: string) => {
|
|||
[connectorId, navigate, registerWithSocial]
|
||||
);
|
||||
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler();
|
||||
const requiredProfileErrorHandlers = useRequiredProfileErrorHandler({ flow: UserFlow.signIn });
|
||||
|
||||
const signInWithSocialErrorHandlers: ErrorHandlers = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
import { validate } from 'superstruct';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import { EmailContinue } from '@/containers/EmailForm';
|
||||
import { PhoneContinue } from '@/containers/PhoneForm';
|
||||
import ErrorPage from '@/pages/ErrorPage';
|
||||
import { UserFlow } from '@/types';
|
||||
import { continueFlowStateGuard } from '@/types/guard';
|
||||
|
||||
type Parameters = {
|
||||
method?: string;
|
||||
|
@ -12,12 +16,19 @@ type Parameters = {
|
|||
|
||||
const EmailOrPhone = () => {
|
||||
const { method = '' } = useParams<Parameters>();
|
||||
const { state } = useLocation();
|
||||
|
||||
const [_, data] = validate(state, continueFlowStateGuard);
|
||||
const notification = conditional(
|
||||
data?.flow === UserFlow.signIn && 'description.continue_with_more_information'
|
||||
);
|
||||
|
||||
if (method === SignInIdentifier.Email) {
|
||||
return (
|
||||
<SecondaryPageWrapper
|
||||
title="description.link_email_or_phone"
|
||||
description="description.link_email_or_phone_description"
|
||||
notification={notification}
|
||||
>
|
||||
<EmailContinue autoFocus hasSwitch />
|
||||
</SecondaryPageWrapper>
|
||||
|
@ -29,6 +40,7 @@ const EmailOrPhone = () => {
|
|||
<SecondaryPageWrapper
|
||||
title="description.link_email_or_phone"
|
||||
description="description.link_email_or_phone_description"
|
||||
notification={notification}
|
||||
>
|
||||
<PhoneContinue autoFocus hasSwitch />
|
||||
</SecondaryPageWrapper>
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import type { TFuncKey } from 'react-i18next';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import { EmailContinue } from '@/containers/EmailForm';
|
||||
|
||||
const SetEmail = () => (
|
||||
type Props = {
|
||||
notification?: TFuncKey;
|
||||
};
|
||||
|
||||
const SetEmail = (props: Props) => (
|
||||
<SecondaryPageWrapper
|
||||
title="description.link_email"
|
||||
description="description.link_email_description"
|
||||
{...props}
|
||||
>
|
||||
<EmailContinue autoFocus />
|
||||
</SecondaryPageWrapper>
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import type { TFuncKey } from 'react-i18next';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import SetPasswordForm from '@/containers/SetPassword';
|
||||
|
||||
import useSetPassword from './use-set-password';
|
||||
|
||||
const SetPassword = () => {
|
||||
type Props = {
|
||||
notification?: TFuncKey;
|
||||
};
|
||||
|
||||
const SetPassword = (props: Props) => {
|
||||
const { setPassword } = useSetPassword();
|
||||
|
||||
return (
|
||||
<SecondaryPageWrapper title="description.set_password">
|
||||
<SecondaryPageWrapper title="description.set_password" {...props}>
|
||||
<SetPasswordForm autoFocus onSubmit={setPassword} />
|
||||
</SecondaryPageWrapper>
|
||||
);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import type { TFuncKey } from 'react-i18next';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import { PhoneContinue } from '@/containers/PhoneForm';
|
||||
|
||||
const SetPhone = () => {
|
||||
type Props = {
|
||||
notification?: TFuncKey;
|
||||
};
|
||||
|
||||
const SetPhone = (props: Props) => {
|
||||
return (
|
||||
<SecondaryPageWrapper
|
||||
title="description.link_phone"
|
||||
description="description.link_phone_description"
|
||||
{...props}
|
||||
>
|
||||
<PhoneContinue autoFocus />
|
||||
</SecondaryPageWrapper>
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import type { TFuncKey } from 'react-i18next';
|
||||
|
||||
import SecondaryPageWrapper from '@/components/SecondaryPageWrapper';
|
||||
import { SetUsername as SetUsernameForm } from '@/containers/UsernameForm';
|
||||
|
||||
const SetUsername = () => (
|
||||
type Props = {
|
||||
notification?: TFuncKey;
|
||||
};
|
||||
|
||||
const SetUsername = (props: Props) => (
|
||||
<SecondaryPageWrapper
|
||||
title="description.enter_username"
|
||||
description="description.enter_username_description"
|
||||
{...props}
|
||||
>
|
||||
<SetUsernameForm />
|
||||
</SecondaryPageWrapper>
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { validate } from 'superstruct';
|
||||
|
||||
import ErrorPage from '@/pages/ErrorPage';
|
||||
import { UserFlow } from '@/types';
|
||||
import { continueFlowStateGuard } from '@/types/guard';
|
||||
|
||||
import SetEmail from './SetEmail';
|
||||
import SetPassword from './SetPassword';
|
||||
|
@ -14,21 +18,28 @@ type Parameters = {
|
|||
|
||||
const Continue = () => {
|
||||
const { method = '' } = useParams<Parameters>();
|
||||
const { state } = useLocation();
|
||||
|
||||
const [_, data] = validate(state, continueFlowStateGuard);
|
||||
|
||||
const notification = conditional(
|
||||
data?.flow === UserFlow.signIn && 'description.continue_with_more_information'
|
||||
);
|
||||
|
||||
if (method === 'password') {
|
||||
return <SetPassword />;
|
||||
return <SetPassword notification={notification} />;
|
||||
}
|
||||
|
||||
if (method === SignInIdentifier.Username) {
|
||||
return <SetUsername />;
|
||||
return <SetUsername notification={notification} />;
|
||||
}
|
||||
|
||||
if (method === SignInIdentifier.Email) {
|
||||
return <SetEmail />;
|
||||
return <SetEmail notification={notification} />;
|
||||
}
|
||||
|
||||
if (method === SignInIdentifier.Phone) {
|
||||
return <SetPhone />;
|
||||
return <SetPhone notification={notification} />;
|
||||
}
|
||||
|
||||
return <ErrorPage />;
|
||||
|
|
|
@ -26,6 +26,12 @@ export const userFlowGuard = s.enums([
|
|||
UserFlow.continue,
|
||||
]);
|
||||
|
||||
export const continueFlowStateGuard = s.optional(
|
||||
s.type({
|
||||
flow: userFlowGuard,
|
||||
})
|
||||
);
|
||||
|
||||
export const continueMethodGuard = s.union([
|
||||
s.literal('password'),
|
||||
s.literal('username'),
|
||||
|
@ -57,7 +63,7 @@ export const missingProfileErrorDataGuard = s.object({
|
|||
registeredSocialIdentity,
|
||||
});
|
||||
|
||||
export const registeredSocialIdentityStateGuard = s.object({
|
||||
export const registeredSocialIdentityStateGuard = s.type({
|
||||
registeredSocialIdentity,
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue