0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

refactor(ui): refactor social signin callback flow (#967)

* refactor(ui): refactor social signin callback flow

refactor social signin callback flow

* fix(ui): change file structure

change file structure
This commit is contained in:
simeng-li 2022-05-27 12:42:20 +08:00 committed by GitHub
parent 6b53ed5f4f
commit 7015d81378
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 46 deletions

View file

@ -15,6 +15,7 @@ import Register from './pages/Register';
import SecondarySignIn from './pages/SecondarySignIn';
import SignIn from './pages/SignIn';
import SocialRegister from './pages/SocialRegister';
import SocialSignInCallback from './pages/SocialSignInCallback';
import getSignInExperienceSettings from './utils/sign-in-experience';
import './scss/normalized.scss';
@ -54,7 +55,7 @@ const App = () => {
<Route path="/" element={<Navigate replace to="/sign-in" />} />
<Route path="/sign-in" element={<SignIn />} />
<Route path="/sign-in/consent" element={<Consent />} />
<Route path="/sign-in/callback/:connector" element={<SignIn />} />
<Route path="/sign-in/callback/:connector" element={<SocialSignInCallback />} />
<Route path="/sign-in/:method" element={<SecondarySignIn />} />
<Route path="/register" element={<Register />} />
<Route path="/register/:method" element={<Register />} />

View file

@ -1,6 +1,6 @@
import React from 'react';
import useSocialSignInListener from '@/hooks/use-social-signin-listener';
import useNativeMessageListener from '@/hooks/use-native-message-listener';
import SocialSignInList from '../SocialSignInList';
@ -11,7 +11,7 @@ type Props = {
};
const PrimarySocialSignIn = ({ className }: Props) => {
useSocialSignInListener();
useNativeMessageListener();
return <SocialSignInList className={className} />;
};

View file

@ -1,12 +1,11 @@
import { fireEvent, waitFor } from '@testing-library/react';
import React from 'react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { MemoryRouter } from 'react-router-dom';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import { socialConnectors, mockSignInExperienceSettings } from '@/__mocks__/logto';
import * as socialSignInApi from '@/apis/social';
import { generateState, storeState } from '@/hooks/utils';
import SecondarySocialSignIn, { defaultSize } from '.';
@ -17,10 +16,6 @@ describe('SecondarySocialSignIn', () => {
.spyOn(socialSignInApi, 'invokeSocialSignIn')
.mockResolvedValue({ redirectTo: `${mockOrigin}/callback` });
const signInWithSocialSpy = jest
.spyOn(socialSignInApi, 'signInWithSocial')
.mockResolvedValue({ redirectTo: `${mockOrigin}/callback` });
beforeEach(() => {
/* eslint-disable @silverhand/fp/no-mutation */
// @ts-expect-error mock global object
@ -126,34 +121,4 @@ describe('SecondarySocialSignIn', () => {
expect(logtoNativeSdk?.getPostMessage).toBeCalled();
}
});
it('callback validation and signIn with social', async () => {
const state = generateState();
storeState(state, 'github');
/* eslint-disable @silverhand/fp/no-mutating-methods */
Object.defineProperty(window, 'location', {
value: {
href: `/sign-in/callback?state=${state}&code=foo`,
search: `?state=${state}&code=foo`,
pathname: '/sign-in/callback',
assign: jest.fn(),
},
});
/* eslint-enable @silverhand/fp/no-mutating-methods */
renderWithPageContext(
<SettingsProvider>
<MemoryRouter initialEntries={['/sign-in/callback/github']}>
<Routes>
<Route path="/sign-in/callback/:connector" element={<SecondarySocialSignIn />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
await waitFor(() => {
expect(signInWithSocialSpy).toBeCalled();
});
});
});

View file

@ -1,8 +1,8 @@
import React, { useMemo, useState, useRef } from 'react';
import useNativeMessageListener from '@/hooks/use-native-message-listener';
import usePlatform from '@/hooks/use-platform';
import useSocial from '@/hooks/use-social';
import useSocialSignInListener from '@/hooks/use-social-signin-listener';
import SocialSignInDropdown from '../SocialSignInDropdown';
import SocialSignInIconList from '../SocialSignInIconList';
@ -20,7 +20,7 @@ const SecondarySocialSignIn = ({ className }: Props) => {
const moreButtonRef = useRef<HTMLButtonElement>(null);
const { isMobile } = usePlatform();
useSocialSignInListener();
useNativeMessageListener();
const isCollapsed = socialConnectors.length > defaultSize;

View file

@ -43,7 +43,10 @@ const useSocialCallbackHandler = () => {
// Web/Mobile-Web redirect to sign-in/callback page to login
if (platform === 'web') {
navigate(
new URL(`${location.origin}/sign-in/callback/${connectorId}/${window.location.search}`),
{
pathname: `/sign-in/callback/${connectorId}`,
search: window.location.search,
},
{
replace: true,
}

View file

@ -6,7 +6,6 @@ import { signInWithSocial } from '@/apis/social';
import { parseQueryParameters } from '@/utils';
import useApi, { ErrorHandlers } from './use-api';
import useNativeMessageListener from './use-native-message-listener';
import { PageContext } from './use-page-context';
import { stateValidation } from './utils';
@ -16,8 +15,6 @@ const useSocialSignInListener = () => {
const parameters = useParams();
const navigate = useNavigate();
useNativeMessageListener();
const signInWithSocialErrorHandlers: ErrorHandlers = useMemo(
() => ({
'user.identity_not_exists': (error) => {
@ -58,7 +55,7 @@ const useSocialSignInListener = () => {
// Social Sign-In Callback Handler
useEffect(() => {
if (!location.pathname.includes('/sign-in/callback') || !parameters.connector) {
if (!parameters.connector) {
return;
}

View file

@ -0,0 +1,49 @@
import { waitFor } from '@testing-library/react';
import React from 'react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import * as socialSignInApi from '@/apis/social';
import { generateState, storeState } from '@/hooks/utils';
import SocialCallback from '.';
const origin = 'http://localhost:3000';
describe('SocialCallbackPage with code', () => {
const signInWithSocialSpy = jest
.spyOn(socialSignInApi, 'signInWithSocial')
.mockResolvedValue({ redirectTo: `/sign-in` });
it('callback validation and signIn with social', async () => {
const state = generateState();
storeState(state, 'github');
/* eslint-disable @silverhand/fp/no-mutating-methods */
Object.defineProperty(window, 'location', {
value: {
origin,
href: `/sign-in/callback?state=${state}&code=foo`,
search: `?state=${state}&code=foo`,
pathname: '/sign-in/callback',
assign: jest.fn(),
},
});
/* eslint-enable @silverhand/fp/no-mutating-methods */
renderWithPageContext(
<SettingsProvider>
<MemoryRouter initialEntries={['/sign-in/callback/github']}>
<Routes>
<Route path="/sign-in/callback/:connector" element={<SocialCallback />} />
</Routes>
</MemoryRouter>
</SettingsProvider>
);
await waitFor(() => {
expect(signInWithSocialSpy).toBeCalled();
});
});
});

View file

@ -0,0 +1,13 @@
import React from 'react';
import useSocialSignInListener from '@/hooks/use-social-sign-in-listener';
import SignIn from '../SignIn';
const SocialSignInCallback = () => {
useSocialSignInListener();
return <SignIn />;
};
export default SocialSignInCallback;