mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
refactor(ui): replace continue api with patchProfile (#2744)
This commit is contained in:
parent
06ec86d831
commit
e02f7e5ac2
22 changed files with 110 additions and 175 deletions
|
@ -153,7 +153,7 @@ describe('verifyUserAccount', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(verifyUserAccount(interaction)).rejects.toMatchError(
|
await expect(verifyUserAccount(interaction)).rejects.toMatchError(
|
||||||
new RequestError({ code: 'user.user_not_exist', status: 404 }, { identity: 'email' })
|
new RequestError({ code: 'user.user_not_exist', status: 404 }, { identifier: 'email' })
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' });
|
expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' });
|
||||||
|
|
|
@ -26,7 +26,7 @@ const identifyUserByVerifiedEmailOrPhone = async (
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
user,
|
user,
|
||||||
new RequestError({ code: 'user.user_not_exist', status: 404 }, { identity: identifier.value })
|
new RequestError({ code: 'user.user_not_exist', status: 404 }, { identifier: identifier.value })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { id, isSuspended } = user;
|
const { id, isSuspended } = user;
|
||||||
|
|
|
@ -62,7 +62,7 @@ const errors = {
|
||||||
email_or_phone_required_in_profile:
|
email_or_phone_required_in_profile:
|
||||||
'You need to add an email address or phone number before signing-in.',
|
'You need to add an email address or phone number before signing-in.',
|
||||||
suspended: 'This account is suspended.',
|
suspended: 'This account is suspended.',
|
||||||
user_not_exist: 'User with {{ identity }} does not exist.',
|
user_not_exist: 'User with {{ identifier }} does not exist.',
|
||||||
missing_profile: 'You need to provide additional info before signing-in.',
|
missing_profile: 'You need to provide additional info before signing-in.',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
|
|
|
@ -62,7 +62,7 @@ const errors = {
|
||||||
email_or_phone_required_in_profile:
|
email_or_phone_required_in_profile:
|
||||||
'Você precisa adicionar um endereço de e-mail ou número de telefone antes de fazer login.',
|
'Você precisa adicionar um endereço de e-mail ou número de telefone antes de fazer login.',
|
||||||
suspended: 'Esta conta está suspensa.',
|
suspended: 'Esta conta está suspensa.',
|
||||||
user_not_exist: 'O usuário com {{ identity }} não existe',
|
user_not_exist: 'O usuário com {{ identifier }} não existe',
|
||||||
missing_profile: 'Você precisa fornecer informações adicionais antes de fazer login.',
|
missing_profile: 'Você precisa fornecer informações adicionais antes de fazer login.',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
|
|
|
@ -59,7 +59,7 @@ const errors = {
|
||||||
phone_exists_in_profile: '当前用户已绑定手机号,无需重复操作。',
|
phone_exists_in_profile: '当前用户已绑定手机号,无需重复操作。',
|
||||||
email_or_phone_required_in_profile: '请绑定邮箱地址或手机号码。',
|
email_or_phone_required_in_profile: '请绑定邮箱地址或手机号码。',
|
||||||
suspended: '账号已被禁用。',
|
suspended: '账号已被禁用。',
|
||||||
user_not_exist: '未找到与 {{ identity }} 相关联的用户。',
|
user_not_exist: '未找到与 {{ identifier }} 相关联的用户。',
|
||||||
missing_profile: '请于登录时提供必要的用户补充信息。',
|
missing_profile: '请于登录时提供必要的用户补充信息。',
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import ky from 'ky';
|
|
||||||
|
|
||||||
import { continueApi } from './continue';
|
|
||||||
|
|
||||||
jest.mock('ky', () => ({
|
|
||||||
extend: () => ky,
|
|
||||||
post: jest.fn(() => ({
|
|
||||||
json: jest.fn(),
|
|
||||||
})),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('continue API', () => {
|
|
||||||
const mockKyPost = ky.post as jest.Mock;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockKyPost.mockReturnValueOnce({
|
|
||||||
json: () => ({
|
|
||||||
redirectTo: '/',
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
mockKyPost.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('continue with password', async () => {
|
|
||||||
await continueApi('password', 'password');
|
|
||||||
expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/password', {
|
|
||||||
json: {
|
|
||||||
password: 'password',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('continue with username', async () => {
|
|
||||||
await continueApi('username', 'username');
|
|
||||||
expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/username', {
|
|
||||||
json: {
|
|
||||||
username: 'username',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('continue with email', async () => {
|
|
||||||
await continueApi('email', 'email');
|
|
||||||
|
|
||||||
expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/email', {
|
|
||||||
json: {
|
|
||||||
email: 'email',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('continue with phone', async () => {
|
|
||||||
await continueApi('phone', 'phone');
|
|
||||||
|
|
||||||
expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/sms', {
|
|
||||||
json: {
|
|
||||||
phone: 'phone',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,24 +0,0 @@
|
||||||
import api from './api';
|
|
||||||
import { bindSocialAccount } from './social';
|
|
||||||
|
|
||||||
type Response = {
|
|
||||||
redirectTo: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const continueApiPrefix = '/api/session/sign-in/continue';
|
|
||||||
|
|
||||||
type ContinueKey = 'password' | 'username' | 'email' | 'phone';
|
|
||||||
|
|
||||||
export const continueApi = async (key: ContinueKey, value: string, socialToBind?: string) => {
|
|
||||||
const result = await api
|
|
||||||
.post(`${continueApiPrefix}/${key === 'phone' ? 'sms' : key}`, {
|
|
||||||
json: { [key]: value },
|
|
||||||
})
|
|
||||||
.json<Response>();
|
|
||||||
|
|
||||||
if (result.redirectTo && socialToBind) {
|
|
||||||
await bindSocialAccount(socialToBind);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
|
@ -152,3 +152,16 @@ export const registerWithVerifiedIdentifier = async (payload: SendPasscodePayloa
|
||||||
|
|
||||||
return api.post(`${interactionPrefix}/submit`).json<Response>();
|
return api.post(`${interactionPrefix}/submit`).json<Response>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addProfile = async (
|
||||||
|
payload: { username: string } | { password: string },
|
||||||
|
socialToBind?: string
|
||||||
|
) => {
|
||||||
|
await api.patch(`${interactionPrefix}/profile`, { json: payload });
|
||||||
|
|
||||||
|
if (socialToBind) {
|
||||||
|
// TODO: bind social account
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.post(`${interactionPrefix}/submit`).json<Response>();
|
||||||
|
};
|
||||||
|
|
|
@ -24,7 +24,7 @@ const useContinueSetEmailPasscodeValidation = (email: string, errorCallback?: ()
|
||||||
|
|
||||||
const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo(
|
const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
'user.email_not_exist': identifierNotExistErrorHandler,
|
'user.email_already_in_use': identifierNotExistErrorHandler,
|
||||||
...requiredProfileErrorHandler,
|
...requiredProfileErrorHandler,
|
||||||
...sharedErrorHandlers,
|
...sharedErrorHandlers,
|
||||||
callback: errorCallback,
|
callback: errorCallback,
|
||||||
|
|
|
@ -16,7 +16,7 @@ const useContinueSetSmsPasscodeValidation = (phone: string, errorCallback?: () =
|
||||||
|
|
||||||
const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true);
|
const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true);
|
||||||
|
|
||||||
const identifierNotExistErrorHandler = useIdentifierErrorAlert(
|
const identifierExistErrorHandler = useIdentifierErrorAlert(
|
||||||
UserFlow.continue,
|
UserFlow.continue,
|
||||||
SignInIdentifier.Sms,
|
SignInIdentifier.Sms,
|
||||||
phone
|
phone
|
||||||
|
@ -24,17 +24,12 @@ const useContinueSetSmsPasscodeValidation = (phone: string, errorCallback?: () =
|
||||||
|
|
||||||
const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo(
|
const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
'user.phone_not_exist': identifierNotExistErrorHandler,
|
'user.phone_already_in_use': identifierExistErrorHandler,
|
||||||
...requiredProfileErrorHandler,
|
...requiredProfileErrorHandler,
|
||||||
...sharedErrorHandlers,
|
...sharedErrorHandlers,
|
||||||
callback: errorCallback,
|
callback: errorCallback,
|
||||||
}),
|
}),
|
||||||
[
|
[errorCallback, identifierExistErrorHandler, requiredProfileErrorHandler, sharedErrorHandlers]
|
||||||
errorCallback,
|
|
||||||
identifierNotExistErrorHandler,
|
|
||||||
requiredProfileErrorHandler,
|
|
||||||
sharedErrorHandlers,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { run: verifyPasscode } = useApi(
|
const { run: verifyPasscode } = useApi(
|
||||||
|
|
|
@ -22,7 +22,7 @@ const useForgotPasswordEmailPasscodeValidation = (email: string, errorCallback?:
|
||||||
|
|
||||||
const errorHandlers: ErrorHandlers = useMemo(
|
const errorHandlers: ErrorHandlers = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
'user.email_not_exist': identifierNotExistErrorHandler,
|
'user.user_not_exist': identifierNotExistErrorHandler,
|
||||||
'user.new_password_required_in_profile': () => {
|
'user.new_password_required_in_profile': () => {
|
||||||
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,7 +22,7 @@ const useForgotPasswordSmsPasscodeValidation = (phone: string, errorCallback?: (
|
||||||
|
|
||||||
const errorHandlers: ErrorHandlers = useMemo(
|
const errorHandlers: ErrorHandlers = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
'user.phone_not_exist': identifierNotExistErrorHandler,
|
'user.user_not_exist': identifierNotExistErrorHandler,
|
||||||
'user.new_password_required_in_profile': () => {
|
'user.new_password_required_in_profile': () => {
|
||||||
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true });
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,7 +20,7 @@ const useIdentifierErrorAlert = (
|
||||||
await show({
|
await show({
|
||||||
type: 'alert',
|
type: 'alert',
|
||||||
ModalContent: t(
|
ModalContent: t(
|
||||||
flow === UserFlow.register
|
flow === UserFlow.register || flow === UserFlow.continue
|
||||||
? 'description.create_account_id_exists_alert'
|
? 'description.create_account_id_exists_alert'
|
||||||
: 'description.sign_in_id_does_not_exist_alert',
|
: 'description.sign_in_id_does_not_exist_alert',
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,7 +62,7 @@ const useSignInWithEmailPasscodeValidation = (email: string, errorCallback?: ()
|
||||||
|
|
||||||
const errorHandlers = useMemo<ErrorHandlers>(
|
const errorHandlers = useMemo<ErrorHandlers>(
|
||||||
() => ({
|
() => ({
|
||||||
'user.email_not_exist':
|
'user.user_not_exist':
|
||||||
// Block user auto register if is bind social or sign-in only flow
|
// Block user auto register if is bind social or sign-in only flow
|
||||||
signInMode === SignInMode.SignIn || socialToBind
|
signInMode === SignInMode.SignIn || socialToBind
|
||||||
? identifierNotExistErrorHandler
|
? identifierNotExistErrorHandler
|
||||||
|
|
|
@ -62,7 +62,7 @@ const useSignInWithSmsPasscodeValidation = (phone: string, errorCallback?: () =>
|
||||||
|
|
||||||
const errorHandlers = useMemo<ErrorHandlers>(
|
const errorHandlers = useMemo<ErrorHandlers>(
|
||||||
() => ({
|
() => ({
|
||||||
'user.phone_not_exist':
|
'user.user_not_exist':
|
||||||
// Block user auto register if is bind social or sign-in only flow
|
// Block user auto register if is bind social or sign-in only flow
|
||||||
signInMode === SignInMode.SignIn || socialToBind
|
signInMode === SignInMode.SignIn || socialToBind
|
||||||
? identifierNotExistErrorHandler
|
? identifierNotExistErrorHandler
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { fireEvent, act, waitFor } from '@testing-library/react';
|
import { fireEvent, act, waitFor } from '@testing-library/react';
|
||||||
|
|
||||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||||
import { continueApi } from '@/apis/continue';
|
import { addProfile } from '@/apis/interaction';
|
||||||
|
|
||||||
import SetUsername from '.';
|
import SetUsername from '.';
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ jest.mock('react-router-dom', () => ({
|
||||||
useNavigate: () => mockedNavigate,
|
useNavigate: () => mockedNavigate,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/apis/continue', () => ({
|
jest.mock('@/apis/interaction', () => ({
|
||||||
continueApi: jest.fn(async () => ({})),
|
addProfile: jest.fn(async () => ({})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('<UsernameRegister />', () => {
|
describe('<UsernameRegister />', () => {
|
||||||
|
@ -37,7 +37,7 @@ describe('<UsernameRegister />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(continueApi).toBeCalledWith('username', 'username', undefined);
|
expect(addProfile).toBeCalledWith({ username: 'username' }, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, useCallback, useMemo, useEffect } from 'react';
|
import { useState, useCallback, useMemo, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { continueApi } from '@/apis/continue';
|
import { addProfile } from '@/apis/interaction';
|
||||||
import useApi from '@/hooks/use-api';
|
import useApi from '@/hooks/use-api';
|
||||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||||
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler';
|
||||||
|
@ -28,14 +28,14 @@ const useSetUsername = () => {
|
||||||
[requiredProfileErrorHandler]
|
[requiredProfileErrorHandler]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { result, run: setUsername } = useApi(continueApi, errorHandlers);
|
const { result, run: asyncAddProfile } = useApi(addProfile, errorHandlers);
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (username: string) => {
|
async (username: string) => {
|
||||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||||
await setUsername('username', username, socialToBind);
|
await asyncAddProfile({ username }, socialToBind);
|
||||||
},
|
},
|
||||||
[setUsername]
|
[asyncAddProfile]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,60 +1,63 @@
|
||||||
import { useMemo } from 'react';
|
import { MissingProfile } from '@logto/schemas';
|
||||||
|
import { useMemo, useContext } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { validate } from 'superstruct';
|
||||||
|
|
||||||
import { UserFlow } from '@/types';
|
import { UserFlow } from '@/types';
|
||||||
|
import { missingProfileErrorDataGuard } from '@/types/guard';
|
||||||
|
|
||||||
|
import type { ErrorHandlers } from './use-api';
|
||||||
|
import { PageContext } from './use-page-context';
|
||||||
|
|
||||||
const useRequiredProfileErrorHandler = (replace?: boolean) => {
|
const useRequiredProfileErrorHandler = (replace?: boolean) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { setToast } = useContext(PageContext);
|
||||||
|
|
||||||
const requiredProfileErrorHandler = useMemo(
|
const requiredProfileErrorHandler = useMemo<ErrorHandlers>(
|
||||||
() => ({
|
() => ({
|
||||||
'user.password_required_in_profile': () => {
|
'user.missing_profile': (error) => {
|
||||||
navigate(
|
const [, data] = validate(error.data, missingProfileErrorDataGuard);
|
||||||
{
|
const missingProfile = data?.missingProfile[0];
|
||||||
pathname: `/${UserFlow.continue}/password`,
|
|
||||||
search: location.search,
|
switch (missingProfile) {
|
||||||
},
|
case MissingProfile.password:
|
||||||
{ replace }
|
case MissingProfile.username:
|
||||||
);
|
case MissingProfile.email:
|
||||||
},
|
navigate(
|
||||||
'user.username_required_in_profile': () => {
|
{
|
||||||
navigate(
|
pathname: `/${UserFlow.continue}/${missingProfile}`,
|
||||||
{
|
search: location.search,
|
||||||
pathname: `/${UserFlow.continue}/username`,
|
},
|
||||||
search: location.search,
|
{ replace }
|
||||||
},
|
);
|
||||||
{ replace }
|
break;
|
||||||
);
|
case MissingProfile.phone:
|
||||||
},
|
navigate(
|
||||||
'user.email_required_in_profile': () => {
|
{
|
||||||
navigate(
|
pathname: `/${UserFlow.continue}/sms`,
|
||||||
{
|
search: location.search,
|
||||||
pathname: `/${UserFlow.continue}/email`,
|
},
|
||||||
search: location.search,
|
{ replace }
|
||||||
},
|
);
|
||||||
{ replace }
|
break;
|
||||||
);
|
case MissingProfile.emailOrPhone:
|
||||||
},
|
navigate(
|
||||||
'user.phone_required_in_profile': () => {
|
{
|
||||||
navigate(
|
pathname: `/${UserFlow.continue}/email-or-sms/email`,
|
||||||
{
|
search: location.search,
|
||||||
pathname: `/${UserFlow.continue}/sms`,
|
},
|
||||||
search: location.search,
|
{ replace }
|
||||||
},
|
);
|
||||||
{ replace }
|
break;
|
||||||
);
|
|
||||||
},
|
default: {
|
||||||
'user.email_or_phone_required_in_profile': () => {
|
setToast(error.message);
|
||||||
navigate(
|
break;
|
||||||
{
|
}
|
||||||
pathname: `/${UserFlow.continue}/email-or-sms/email`,
|
}
|
||||||
search: location.search,
|
|
||||||
},
|
|
||||||
{ replace }
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[navigate, replace]
|
[navigate, replace, setToast]
|
||||||
);
|
);
|
||||||
|
|
||||||
return requiredProfileErrorHandler;
|
return requiredProfileErrorHandler;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { act, waitFor, fireEvent } from '@testing-library/react';
|
||||||
|
|
||||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||||
import { continueApi } from '@/apis/continue';
|
import { addProfile } from '@/apis/interaction';
|
||||||
|
|
||||||
import SetPassword from '.';
|
import SetPassword from '.';
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ jest.mock('react-router-dom', () => ({
|
||||||
useNavigate: () => mockedNavigate,
|
useNavigate: () => mockedNavigate,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/apis/continue', () => ({
|
jest.mock('@/apis/interaction', () => ({
|
||||||
continueApi: jest.fn(async () => ({ redirectTo: '/' })),
|
addProfile: jest.fn(async () => ({ redirectTo: '/' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('SetPassword', () => {
|
describe('SetPassword', () => {
|
||||||
|
@ -52,7 +52,7 @@ describe('SetPassword', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(continueApi).toBeCalledWith('password', '123456', undefined);
|
expect(addProfile).toBeCalledWith({ password: '123456' }, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo, useEffect, useCallback } from 'react';
|
import { useMemo, useEffect, useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { continueApi } from '@/apis/continue';
|
import { addProfile } from '@/apis/interaction';
|
||||||
import type { ErrorHandlers } from '@/hooks/use-api';
|
import type { ErrorHandlers } from '@/hooks/use-api';
|
||||||
import useApi from '@/hooks/use-api';
|
import useApi from '@/hooks/use-api';
|
||||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||||
|
@ -26,14 +26,14 @@ const useSetPassword = () => {
|
||||||
[navigate, requiredProfileErrorHandler, show]
|
[navigate, requiredProfileErrorHandler, show]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { result, run: asyncSetPassword } = useApi(continueApi, errorHandlers);
|
const { result, run: asyncAddProfile } = useApi(addProfile, errorHandlers);
|
||||||
|
|
||||||
const setPassword = useCallback(
|
const setPassword = useCallback(
|
||||||
async (password: string) => {
|
async (password: string) => {
|
||||||
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial);
|
||||||
await asyncSetPassword('password', password, socialToBind);
|
await asyncAddProfile({ password }, socialToBind);
|
||||||
},
|
},
|
||||||
[asyncSetPassword]
|
[asyncAddProfile]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { act, waitFor, fireEvent } from '@testing-library/react';
|
||||||
|
|
||||||
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
|
||||||
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
|
||||||
import { continueApi } from '@/apis/continue';
|
import { addProfile } from '@/apis/interaction';
|
||||||
|
|
||||||
import SetUsername from '.';
|
import SetUsername from '.';
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ jest.mock('react-router-dom', () => ({
|
||||||
useNavigate: () => mockedNavigate,
|
useNavigate: () => mockedNavigate,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@/apis/continue', () => ({
|
jest.mock('@/apis/interaction', () => ({
|
||||||
continueApi: jest.fn(async () => ({ redirectTo: '/' })),
|
addProfile: jest.fn(async () => ({ redirectTo: '/' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('SetPassword', () => {
|
describe('SetPassword', () => {
|
||||||
|
@ -46,7 +46,7 @@ describe('SetPassword', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(continueApi).toBeCalledWith('username', 'username', undefined);
|
expect(addProfile).toBeCalledWith({ username: 'username' }, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SignInIdentifier } from '@logto/schemas';
|
import { SignInIdentifier, MissingProfile } from '@logto/schemas';
|
||||||
import * as s from 'superstruct';
|
import * as s from 'superstruct';
|
||||||
|
|
||||||
export const bindSocialStateGuard = s.object({
|
export const bindSocialStateGuard = s.object({
|
||||||
|
@ -38,3 +38,15 @@ export const continueMethodGuard = s.union([
|
||||||
export const usernameGuard = s.object({
|
export const usernameGuard = s.object({
|
||||||
username: s.string(),
|
username: s.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const missingProfileErrorDataGuard = s.object({
|
||||||
|
missingProfile: s.array(
|
||||||
|
s.union([
|
||||||
|
s.literal(MissingProfile.password),
|
||||||
|
s.literal(MissingProfile.email),
|
||||||
|
s.literal(MissingProfile.phone),
|
||||||
|
s.literal(MissingProfile.username),
|
||||||
|
s.literal(MissingProfile.emailOrPhone),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue