Skip to content

Commit

Permalink
refactor(experience): improve identifier prefilling
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoyijun committed Aug 23, 2024
1 parent 14d25ba commit d8db185
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AgreeToTermsPolicy, ExtraParamsKey, type SignInIdentifier } from '@logto/schemas';
import { AgreeToTermsPolicy, type SignInIdentifier } from '@logto/schemas';
import classNames from 'classnames';
import { useCallback, useContext, useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';

import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import LockIcon from '@/assets/icons/lock.svg?react';
Expand All @@ -12,6 +11,7 @@ import ErrorMessage from '@/components/ErrorMessage';
import { SmartInputField } from '@/components/InputFields';
import type { IdentifierInputValue } from '@/components/InputFields/SmartInputField';
import TermsAndPrivacyCheckbox from '@/containers/TermsAndPrivacyCheckbox';
import usePrefilledIdentifier from '@/hooks/use-prefilled-identifier';
import useSingleSignOnWatch from '@/hooks/use-single-sign-on-watch';
import useTerms from '@/hooks/use-terms';
import { getGeneralIdentifierErrorMessage, validateIdentifierField } from '@/utils/form';
Expand All @@ -36,11 +36,8 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)

const { errorMessage, clearErrorMessage, onSubmit } = useOnSubmit();

const { getIdentifierInputValueByTypes, setIdentifierInputValue } =
useContext(UserInteractionContext);
const identifierInputValue = getIdentifierInputValueByTypes(signUpMethods);

const [searchParams] = useSearchParams();
const { setIdentifierInputValue } = useContext(UserInteractionContext);
const prefilledIdentifier = usePrefilledIdentifier({ enabledIdentifiers: signUpMethods });

const {
watch,
Expand All @@ -49,6 +46,7 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)
control,
} = useForm<FormState>({
reValidateMode: 'onBlur',
defaultValues: { id: prefilledIdentifier },
});

// Watch identifier field and check single sign on method availability
Expand Down Expand Up @@ -127,10 +125,8 @@ const IdentifierRegisterForm = ({ className, autoFocus, signUpMethods }: Props)
autoFocus={autoFocus}
className={styles.inputField}
{...field}
defaultValue={
identifierInputValue?.value ?? searchParams.get(ExtraParamsKey.LoginHint) ?? undefined
}
defaultType={identifierInputValue?.type}
defaultValue={field.value.value}
defaultType={field.value.type}
isDanger={!!errors.id || !!errorMessage}
errorMessage={errors.id?.message}
enabledTypes={signUpMethods}
Expand Down
21 changes: 11 additions & 10 deletions packages/experience/src/components/IdentifierSignInForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AgreeToTermsPolicy, ExtraParamsKey, type SignIn } from '@logto/schemas';
import { AgreeToTermsPolicy, type SignIn } from '@logto/schemas';
import classNames from 'classnames';
import { useCallback, useContext, useEffect, useMemo } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';

import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import LockIcon from '@/assets/icons/lock.svg?react';
Expand All @@ -12,6 +11,7 @@ import ErrorMessage from '@/components/ErrorMessage';
import { SmartInputField } from '@/components/InputFields';
import type { IdentifierInputValue } from '@/components/InputFields/SmartInputField';
import TermsAndPrivacyCheckbox from '@/containers/TermsAndPrivacyCheckbox';
import usePrefilledIdentifier from '@/hooks/use-prefilled-identifier';
import useSingleSignOnWatch from '@/hooks/use-single-sign-on-watch';
import useTerms from '@/hooks/use-terms';
import { getGeneralIdentifierErrorMessage, validateIdentifierField } from '@/utils/form';
Expand All @@ -34,16 +34,16 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
const { t } = useTranslation();
const { errorMessage, clearErrorMessage, onSubmit } = useOnSubmit(signInMethods);
const { termsValidation, agreeToTermsPolicy } = useTerms();
const { getIdentifierInputValueByTypes, setIdentifierInputValue } =
useContext(UserInteractionContext);
const [searchParams] = useSearchParams();
const { setIdentifierInputValue } = useContext(UserInteractionContext);

const enabledSignInMethods = useMemo(
() => signInMethods.map(({ identifier }) => identifier),
[signInMethods]
);

const identifierInputValue = getIdentifierInputValueByTypes(enabledSignInMethods);
const prefilledIdentifier = usePrefilledIdentifier({
enabledIdentifiers: enabledSignInMethods,
});

const {
watch,
Expand All @@ -52,6 +52,9 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
formState: { errors, isValid, isSubmitting },
} = useForm<FormState>({
reValidateMode: 'onBlur',
defaultValues: {
identifier: prefilledIdentifier,
},
});

// Watch identifier field and check single sign on method availability
Expand Down Expand Up @@ -127,10 +130,8 @@ const IdentifierSignInForm = ({ className, autoFocus, signInMethods }: Props) =>
isDanger={!!errors.identifier || !!errorMessage}
errorMessage={errors.identifier?.message}
enabledTypes={enabledSignInMethods}
defaultType={identifierInputValue?.type}
defaultValue={
identifierInputValue?.value ?? searchParams.get(ExtraParamsKey.LoginHint) ?? undefined
}
defaultType={field.value.type}
defaultValue={field.value.value}
/>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { assert } from '@silverhand/essentials';
import { useState, useCallback, useMemo } from 'react';
import type { ChangeEventHandler } from 'react';

import useUpdateEffect from '@/hooks/use-update-effect';
import { getDefaultCountryCallingCode } from '@/utils/country-code';
import { parseIdentifierValue } from '@/utils/form';

Expand Down Expand Up @@ -102,12 +101,6 @@ const useSmartInputField = ({ _defaultType, defaultValue, enabledTypes }: Props)
setCurrentType(enabledTypeSet.size === 1 ? defaultType : undefined);
}, [defaultType, enabledTypeSet.size]);

// CAUTION: For preview use only, enabledTypes and defaultType should not be changed after component mounted
useUpdateEffect(() => {
setInputValue('');
setCurrentType(defaultType);
}, [defaultType]);

return {
countryCode,
onCountryCodeChange,
Expand Down
11 changes: 6 additions & 5 deletions packages/experience/src/components/PasswordSignInForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AgreeToTermsPolicy, ExtraParamsKey, type SignInIdentifier } from '@logto/schemas';
import { AgreeToTermsPolicy, type SignInIdentifier } from '@logto/schemas';
import classNames from 'classnames';
import { useCallback, useContext, useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'react-router-dom';

import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import LockIcon from '@/assets/icons/lock.svg?react';
Expand All @@ -14,6 +13,7 @@ import type { IdentifierInputValue } from '@/components/InputFields/SmartInputFi
import ForgotPasswordLink from '@/containers/ForgotPasswordLink';
import TermsAndPrivacyCheckbox from '@/containers/TermsAndPrivacyCheckbox';
import usePasswordSignIn from '@/hooks/use-password-sign-in';
import usePrefilledIdentifier from '@/hooks/use-prefilled-identifier';
import { useForgotPasswordSettings } from '@/hooks/use-sie';
import useSingleSignOnWatch from '@/hooks/use-single-sign-on-watch';
import useTerms from '@/hooks/use-terms';
Expand All @@ -40,7 +40,7 @@ const PasswordSignInForm = ({ className, autoFocus, signInMethods }: Props) => {
const { isForgotPasswordEnabled } = useForgotPasswordSettings();
const { termsValidation, agreeToTermsPolicy } = useTerms();
const { setIdentifierInputValue } = useContext(UserInteractionContext);
const [searchParams] = useSearchParams();
const prefilledIdentifier = usePrefilledIdentifier({ enabledIdentifiers: signInMethods });

const {
watch,
Expand All @@ -51,7 +51,7 @@ const PasswordSignInForm = ({ className, autoFocus, signInMethods }: Props) => {
} = useForm<FormState>({
reValidateMode: 'onBlur',
defaultValues: {
identifier: {},
identifier: prefilledIdentifier,
password: '',
},
});
Expand Down Expand Up @@ -129,7 +129,8 @@ const PasswordSignInForm = ({ className, autoFocus, signInMethods }: Props) => {
isDanger={!!errors.identifier}
errorMessage={errors.identifier?.message}
enabledTypes={signInMethods}
defaultValue={searchParams.get(ExtraParamsKey.LoginHint) ?? undefined}
defaultValue={field.value.value}
defaultType={field.value.type}
/>
)}
/>
Expand Down
10 changes: 10 additions & 0 deletions packages/experience/src/hooks/use-login-hint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ExtraParamsKey } from '@logto/schemas';
import { useSearchParams } from 'react-router-dom';

const useLoginHint = () => {
const [searchParams] = useSearchParams();

return searchParams.get(ExtraParamsKey.LoginHint) ?? undefined;
};

export default useLoginHint;
35 changes: 35 additions & 0 deletions packages/experience/src/hooks/use-prefilled-identifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { type SignInIdentifier } from '@logto/schemas';
import { useContext, useMemo } from 'react';

import UserInteractionContext from '@/Providers/UserInteractionContextProvider/UserInteractionContext';
import { type IdentifierInputValue } from '@/components/InputFields/SmartInputField';

import useLoginHint from './use-login-hint';

type Options = {
enabledIdentifiers?: SignInIdentifier[];
};

const usePrefilledIdentifier = ({ enabledIdentifiers }: Options = {}) => {
const { identifierInputValue, getIdentifierInputValueByTypes } =
useContext(UserInteractionContext);

const loginHint = useLoginHint();

const cachedInputIdentifier = useMemo(() => {
return enabledIdentifiers
? getIdentifierInputValueByTypes(enabledIdentifiers)
: identifierInputValue;
}, [enabledIdentifiers, getIdentifierInputValueByTypes, identifierInputValue]);

return useMemo<IdentifierInputValue>(() => {
/**
* First, check if there's a cached input identifier
* If there's no cached input identifier, check if there's a valid login hint
* If there's neither, return empty
*/
return cachedInputIdentifier ?? { value: loginHint ?? '' };
}, [cachedInputIdentifier, loginHint]);
};

export default usePrefilledIdentifier;
19 changes: 0 additions & 19 deletions packages/experience/src/hooks/use-update-effect.ts

This file was deleted.

8 changes: 4 additions & 4 deletions packages/experience/src/pages/ResetPasswordLanding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { experience, ExtraParamsKey } from '@logto/schemas';
import { experience } from '@logto/schemas';
import { useTranslation } from 'react-i18next';
import { Navigate, useSearchParams } from 'react-router-dom';
import { Navigate } from 'react-router-dom';

import FocusedAuthPageLayout from '@/Layout/FocusedAuthPageLayout';
import useLoginHint from '@/hooks/use-login-hint';
import { identifierInputDescriptionMap } from '@/utils/form';

import ForgotPasswordForm from '../ForgotPassword/ForgotPasswordForm';
Expand Down Expand Up @@ -32,8 +33,7 @@ import { useResetPasswordMethods } from './use-reset-password-methods';
const ResetPasswordLanding = () => {
const { t } = useTranslation();
const enabledMethods = useResetPasswordMethods();
const [searchParams] = useSearchParams();
const loginHint = searchParams.get(ExtraParamsKey.LoginHint) ?? undefined;
const loginHint = useLoginHint();

// Fallback to sign-in page
if (enabledMethods.length === 0) {
Expand Down

0 comments on commit d8db185

Please sign in to comment.