Skip to content

Commit

Permalink
refactor(experience): rename SingleSignOnContext to `UserInteractio…
Browse files Browse the repository at this point in the history
…nContext` (#6163)
  • Loading branch information
xiaoyijun authored Jul 4, 2024
1 parent 6178106 commit 8635f1b
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 52 deletions.
6 changes: 3 additions & 3 deletions packages/experience/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import AppBoundary from './Providers/AppBoundary';
import LoadingLayerProvider from './Providers/LoadingLayerProvider';
import PageContextProvider from './Providers/PageContextProvider';
import SettingsProvider from './Providers/SettingsProvider';
import SingleSignOnContextProvider from './Providers/SingleSignOnContextProvider';
import UserInteractionContextProvider from './Providers/UserInteractionContextProvider';
import Callback from './pages/Callback';
import Consent from './pages/Consent';
import Continue from './pages/Continue';
Expand Down Expand Up @@ -45,7 +45,7 @@ const App = () => {
<BrowserRouter>
<PageContextProvider>
<SettingsProvider>
<SingleSignOnContextProvider>
<UserInteractionContextProvider>
<AppBoundary>
<Routes>
<Route element={<LoadingLayerProvider />}>
Expand Down Expand Up @@ -125,7 +125,7 @@ const App = () => {
</Route>
</Routes>
</AppBoundary>
</SingleSignOnContextProvider>
</UserInteractionContextProvider>
</SettingsProvider>
</PageContextProvider>
</BrowserRouter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { type SsoConnectorMetadata } from '@logto/schemas';
import { noop } from '@silverhand/essentials';
import { createContext } from 'react';

export type SingleSignOnContextType = {
export type UserInteractionContextType = {
// All the enabled sso connectors
availableSsoConnectorsMap: Map<string, SsoConnectorMetadata>;
email?: string;
setEmail: React.Dispatch<React.SetStateAction<string | undefined>>;
ssoEmail?: string;
setSsoEmail: React.Dispatch<React.SetStateAction<string | undefined>>;
// The sso connectors that are enabled for the current domain
ssoConnectors: SsoConnectorMetadata[];
setSsoConnectors: React.Dispatch<React.SetStateAction<SsoConnectorMetadata[]>>;
};

export default createContext<SingleSignOnContextType>({
email: undefined,
export default createContext<UserInteractionContextType>({
ssoEmail: undefined,
availableSsoConnectorsMap: new Map(),
ssoConnectors: [],
setEmail: noop,
setSsoEmail: noop,
setSsoConnectors: noop,
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,37 @@ import { type ReactNode, useEffect, useMemo, useState } from 'react';
import useSessionStorage, { StorageKeys } from '@/hooks/use-session-storages';
import { useSieMethods } from '@/hooks/use-sie';

import SingleSignOnContext, { type SingleSignOnContextType } from './SingleSignOnContext';
import UserInteractionContext, { type UserInteractionContextType } from './UserInteractionContext';

type Props = {
readonly children: ReactNode;
};

const SingleSignOnContextProvider = ({ children }: Props) => {
/**
* UserInteractionContextProvider
*
* This component manages user interaction data during the sign-in process,
* combining React's Context API with session storage to enable cross-page
* data persistence and access.
*
* The cached data provided by this provider primarily helps improve the sign-in experience for end users.
*/
const UserInteractionContextProvider = ({ children }: Props) => {
const { ssoConnectors } = useSieMethods();
const { get, set, remove } = useSessionStorage();
const [email, setEmail] = useState<string | undefined>(get(StorageKeys.SsoEmail));
const [ssoEmail, setSsoEmail] = useState<string | undefined>(get(StorageKeys.SsoEmail));
const [domainFilteredConnectors, setDomainFilteredConnectors] = useState<SsoConnectorMetadata[]>(
get(StorageKeys.SsoConnectors) ?? []
);

useEffect(() => {
if (!email) {
if (!ssoEmail) {
remove(StorageKeys.SsoEmail);
return;
}

set(StorageKeys.SsoEmail, email);
}, [email, remove, set]);
set(StorageKeys.SsoEmail, ssoEmail);
}, [ssoEmail, remove, set]);

useEffect(() => {
if (domainFilteredConnectors.length === 0) {
Expand All @@ -41,22 +50,22 @@ const SingleSignOnContextProvider = ({ children }: Props) => {
[ssoConnectors]
);

const singleSignOnContext = useMemo<SingleSignOnContextType>(
const userInteractionContext = useMemo<UserInteractionContextType>(
() => ({
email,
setEmail,
ssoEmail,
setSsoEmail,
availableSsoConnectorsMap: ssoConnectorsMap,
ssoConnectors: domainFilteredConnectors,
setSsoConnectors: setDomainFilteredConnectors,
}),
[domainFilteredConnectors, email, ssoConnectorsMap]
[ssoEmail, ssoConnectorsMap, domainFilteredConnectors]
);

return (
<SingleSignOnContext.Provider value={singleSignOnContext}>
<UserInteractionContext.Provider value={userInteractionContext}>
{children}
</SingleSignOnContext.Provider>
</UserInteractionContext.Provider>
);
};

export default SingleSignOnContextProvider;
export default UserInteractionContextProvider;
13 changes: 7 additions & 6 deletions packages/experience/src/hooks/use-check-single-sign-on.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useState, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

import SingleSignOnContext from '@/Providers/SingleSignOnContextProvider/SingleSignOnContext';
import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import { getSingleSignOnConnectors } from '@/apis/single-sign-on';
import useApi from '@/hooks/use-api';
import useErrorHandler from '@/hooks/use-error-handler';
Expand All @@ -15,7 +15,8 @@ const useCheckSingleSignOn = () => {
const navigate = useNavigate();
const request = useApi(getSingleSignOnConnectors);
const [errorMessage, setErrorMessage] = useState<string | undefined>();
const { setEmail, setSsoConnectors, availableSsoConnectorsMap } = useContext(SingleSignOnContext);
const { setSsoEmail, setSsoConnectors, availableSsoConnectorsMap } =
useContext(UserInteractionContext);
const singleSignOn = useSingleSignOn();

const handleError = useErrorHandler();
Expand All @@ -26,9 +27,9 @@ const useCheckSingleSignOn = () => {

// Should clear the context and storage if the user trying to resubmit the form
const clearContext = useCallback(() => {
setEmail(undefined);
setSsoEmail(undefined);
setSsoConnectors([]);
}, [setEmail, setSsoConnectors]);
}, [setSsoEmail, setSsoConnectors]);

/**
* Check if the email is registered with any SSO connectors
Expand Down Expand Up @@ -66,7 +67,7 @@ const useCheckSingleSignOn = () => {
}

setSsoConnectors(connectors);
setEmail(email);
setSsoEmail(email);

if (!continueSignIn) {
return true;
Expand All @@ -87,7 +88,7 @@ const useCheckSingleSignOn = () => {
handleError,
navigate,
request,
setEmail,
setSsoEmail,
setSsoConnectors,
singleSignOn,
t,
Expand Down
14 changes: 7 additions & 7 deletions packages/experience/src/hooks/use-single-sign-on-watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
import { useEffect, useCallback, useContext } from 'react';
import { useNavigate } from 'react-router-dom';

import SingleSignOnContext from '@/Providers/SingleSignOnContextProvider/SingleSignOnContext';
import SingleSignOnFormModeContext from '@/Providers/SingleSignOnFormModeContextProvider/SingleSignOnFormModeContext';
import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import { getSingleSignOnConnectors } from '@/apis/single-sign-on';
import type { IdentifierInputValue } from '@/components/InputFields/SmartInputField';
import useApi from '@/hooks/use-api';
Expand All @@ -23,8 +23,8 @@ const useSingleSignOnWatch = (identifierInput?: IdentifierInputValue) => {

const { singleSignOnEnabled } = useSieMethods();

const { setEmail, setSsoConnectors, ssoConnectors, availableSsoConnectorsMap } =
useContext(SingleSignOnContext);
const { setSsoEmail, setSsoConnectors, ssoConnectors, availableSsoConnectorsMap } =
useContext(UserInteractionContext);

const { showSingleSignOnForm, setShowSingleSignOnForm } = useContext(SingleSignOnFormModeContext);

Expand Down Expand Up @@ -53,20 +53,20 @@ const useSingleSignOnWatch = (identifierInput?: IdentifierInputValue) => {
}

setSsoConnectors(connectors);
setEmail(email);
setSsoEmail(email);
return true;
},
[availableSsoConnectorsMap, request, setEmail, setSsoConnectors]
[availableSsoConnectorsMap, request, setSsoEmail, setSsoConnectors]
);

// Reset the ssoContext
useEffect(() => {
if (!showSingleSignOnForm) {
setSsoConnectors([]);

setEmail(undefined);
setSsoEmail(undefined);
}
}, [setEmail, setSsoConnectors, showSingleSignOnForm]);
}, [setSsoEmail, setSsoConnectors, showSingleSignOnForm]);

const navigateToSingleSignOn = useCallback(async () => {
if (!showSingleSignOnForm) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { assert } from '@silverhand/essentials';
import { fireEvent, act, waitFor } from '@testing-library/react';

import ConfirmModalProvider from '@/Providers/ConfirmModalProvider';
import SingleSignOnContextProvider from '@/Providers/SingleSignOnContextProvider';
import SingleSignOnFormModeContextProvider from '@/Providers/SingleSignOnFormModeContextProvider';
import UserInteractionContextProvider from '@/Providers/UserInteractionContextProvider';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import { mockSignInExperienceSettings, mockSsoConnectors } from '@/__mocks__/logto';
Expand Down Expand Up @@ -53,11 +53,11 @@ const renderForm = (
}}
>
<ConfirmModalProvider>
<SingleSignOnContextProvider>
<UserInteractionContextProvider>
<SingleSignOnFormModeContextProvider>
<IdentifierRegisterForm signUpMethods={signUpMethods} />
</SingleSignOnFormModeContextProvider>
</SingleSignOnContextProvider>
</UserInteractionContextProvider>
</ConfirmModalProvider>
</SettingsProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { SignInIdentifier, experience } from '@logto/schemas';
import { assert } from '@silverhand/essentials';
import { fireEvent, act, waitFor } from '@testing-library/react';

import SingleSignOnContextProvider from '@/Providers/SingleSignOnContextProvider';
import SingleSignOnFormModeContextProvider from '@/Providers/SingleSignOnFormModeContextProvider';
import UserInteractionContextProvider from '@/Providers/UserInteractionContextProvider';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import {
Expand Down Expand Up @@ -52,11 +52,11 @@ const renderForm = (signInMethods: SignIn['methods'], ssoConnectors: SsoConnecto
ssoConnectors,
}}
>
<SingleSignOnContextProvider>
<UserInteractionContextProvider>
<SingleSignOnFormModeContextProvider>
<IdentifierSignInForm signInMethods={signInMethods} />
</SingleSignOnFormModeContextProvider>
</SingleSignOnContextProvider>
</UserInteractionContextProvider>
</SettingsProvider>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { assert } from '@silverhand/essentials';
import { fireEvent, waitFor } from '@testing-library/react';
import { act } from 'react-dom/test-utils';

import SingleSignOnContextProvider from '@/Providers/SingleSignOnContextProvider';
import SingleSignOnFormModeContextProvider from '@/Providers/SingleSignOnFormModeContextProvider';
import UserInteractionContextProvider from '@/Providers/UserInteractionContextProvider';
import renderWithPageContext from '@/__mocks__/RenderWithPageContext';
import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider';
import { mockSignInExperienceSettings, mockSsoConnectors } from '@/__mocks__/logto';
Expand Down Expand Up @@ -50,11 +50,11 @@ describe('UsernamePasswordSignInForm', () => {
) =>
renderWithPageContext(
<SettingsProvider settings={{ ...mockSignInExperienceSettings, ...settings }}>
<SingleSignOnContextProvider>
<UserInteractionContextProvider>
<SingleSignOnFormModeContextProvider>
<PasswordSignInForm signInMethods={signInMethods} />
</SingleSignOnFormModeContextProvider>
</SingleSignOnContextProvider>
</UserInteractionContextProvider>
</SettingsProvider>
);

Expand Down
10 changes: 5 additions & 5 deletions packages/experience/src/pages/SingleSignOnConnectors/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';

import SecondaryPageLayout from '@/Layout/SecondaryPageLayout';
import PageContext from '@/Providers/PageContextProvider/PageContext';
import SingleSignOnContext from '@/Providers/SingleSignOnContextProvider/SingleSignOnContext';
import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import SocialLinkButton from '@/components/Button/SocialLinkButton';
import useNativeMessageListener from '@/hooks/use-native-message-listener';
import useSingleSignOn from '@/hooks/use-single-sign-on';
Expand All @@ -13,7 +13,7 @@ import * as styles from './index.module.scss';

const SingleSignOnConnectors = () => {
const { theme } = useContext(PageContext);
const { email, ssoConnectors } = useContext(SingleSignOnContext);
const { ssoEmail, ssoConnectors } = useContext(UserInteractionContext);
const navigate = useNavigate();
const onSubmit = useSingleSignOn();

Expand All @@ -22,18 +22,18 @@ const SingleSignOnConnectors = () => {

useEffect(() => {
// Return to the previous page if no email and no connectors are available in the context
if (!email || ssoConnectors.length === 0) {
if (!ssoEmail || ssoConnectors.length === 0) {
navigate('../email', {
replace: true,
});
}
}, [email, navigate, ssoConnectors.length]);
}, [ssoEmail, navigate, ssoConnectors.length]);

return (
<SecondaryPageLayout
title="action.single_sign_on"
description="description.single_sign_on_connectors_list"
descriptionProps={{ email }}
descriptionProps={{ email: ssoEmail }}
>
<div className={styles.ssoLinkList}>
{ssoConnectors.map((connector) => {
Expand Down
6 changes: 3 additions & 3 deletions packages/experience/src/pages/SingleSignOnEmail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';

import SecondaryPageLayout from '@/Layout/SecondaryPageLayout';
import SingleSignOnContext from '@/Providers/SingleSignOnContextProvider/SingleSignOnContext';
import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import LockIcon from '@/assets/icons/lock.svg';
import Button from '@/components/Button';
import ErrorMessage from '@/components/ErrorMessage';
Expand All @@ -21,7 +21,7 @@ type FormState = {

const SingleSignOnEmail = () => {
const { errorMessage, clearErrorMessage, onSubmit } = useOnSubmit();
const { email } = useContext(SingleSignOnContext);
const { ssoEmail } = useContext(UserInteractionContext);

const {
handleSubmit,
Expand Down Expand Up @@ -73,7 +73,7 @@ const SingleSignOnEmail = () => {
className={styles.inputField}
{...field}
isDanger={!!errors.identifier}
defaultValue={email}
defaultValue={ssoEmail}
errorMessage={errors.identifier?.message}
enabledTypes={[SignInIdentifier.Email]}
/>
Expand Down

0 comments on commit 8635f1b

Please sign in to comment.