0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(ui): refactor terms modal as a promisified function (#897)

refactor terms modal as a promisified function
This commit is contained in:
simeng-li 2022-05-20 09:59:40 +08:00 committed by GitHub
parent d7ce13d260
commit 9859213e19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 72 additions and 49 deletions

View file

@ -54,6 +54,7 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-i18next": "^11.15.4", "react-i18next": "^11.15.4",
"react-modal": "^3.14.4", "react-modal": "^3.14.4",
"react-modal-promise": "^1.0.2",
"react-phone-number-input": "^3.1.46", "react-phone-number-input": "^3.1.46",
"react-router-dom": "^6.2.2", "react-router-dom": "^6.2.2",
"react-string-replace": "^1.0.0", "react-string-replace": "^1.0.0",

View file

@ -58,12 +58,12 @@ const CreateAccount = ({ className }: Props) => {
const { result, run: asyncRegister } = useApi(register, registerErrorHandlers); const { result, run: asyncRegister } = useApi(register, registerErrorHandlers);
const onSubmitHandler = useCallback(() => { const onSubmitHandler = useCallback(async () => {
if (!validateForm()) { if (!validateForm()) {
return; return;
} }
if (!termsValidation()) { if (!(await termsValidation())) {
return; return;
} }

View file

@ -53,12 +53,12 @@ const EmailPasswordless = ({ type, className }: Props) => {
const sendPasscode = getSendPasscodeApi(type, 'email'); const sendPasscode = getSendPasscodeApi(type, 'email');
const { result, run: asyncSendPasscode } = useApi(sendPasscode, errorHandlers); const { result, run: asyncSendPasscode } = useApi(sendPasscode, errorHandlers);
const onSubmitHandler = useCallback(() => { const onSubmitHandler = useCallback(async () => {
if (!validateForm()) { if (!validateForm()) {
return; return;
} }
if (!termsValidation()) { if (!(await termsValidation())) {
return; return;
} }

View file

@ -63,12 +63,12 @@ const PhonePasswordless = ({ type, className }: Props) => {
[isValidPhoneNumber] [isValidPhoneNumber]
); );
const onSubmitHandler = useCallback(() => { const onSubmitHandler = useCallback(async () => {
if (!validateForm()) { if (!validateForm()) {
return; return;
} }
if (!termsValidation()) { if (!(await termsValidation())) {
return; return;
} }

View file

@ -34,7 +34,7 @@ describe('SecondarySocialSignIn', () => {
</SettingsProvider> </SettingsProvider>
); );
expect(container.querySelectorAll('button')).toHaveLength(defaultSize); expect(container.querySelectorAll('button')).toHaveLength(defaultSize + 1); // Expand button
const expandButton = container.querySelector('svg'); const expandButton = container.querySelector('svg');
@ -42,6 +42,6 @@ describe('SecondarySocialSignIn', () => {
fireEvent.click(expandButton); fireEvent.click(expandButton);
} }
expect(container.querySelectorAll('button')).toHaveLength(socialConnectors.length); expect(container.querySelectorAll('button')).toHaveLength(socialConnectors.length + 1); // Expand button
}); });
}); });

View file

@ -61,7 +61,7 @@ describe('SecondarySocialSignIn', () => {
</MemoryRouter> </MemoryRouter>
</SettingsProvider> </SettingsProvider>
); );
expect(container.querySelectorAll('button')).toHaveLength(defaultSize - 1); expect(container.querySelectorAll('button')).toHaveLength(defaultSize); // Plus Expand button
expect(container.querySelector('svg')).not.toBeNull(); expect(container.querySelector('svg')).not.toBeNull();
}); });

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ModalContainer from 'react-modal-promise';
import PureTermsOfUse from '@/components/TermsOfUse'; import PureTermsOfUse from '@/components/TermsOfUse';
import TermsOfUseModal from '@/components/TermsOfUseModal';
import useTerms from '@/hooks/use-terms'; import useTerms from '@/hooks/use-terms';
type Props = { type Props = {
@ -9,8 +9,7 @@ type Props = {
}; };
const TermsOfUse = ({ className }: Props) => { const TermsOfUse = ({ className }: Props) => {
const { termsAgreement, setTermsAgreement, termsSettings, showTermsModal, setShowTermsModal } = const { termsAgreement, setTermsAgreement, termsSettings } = useTerms();
useTerms();
if (!termsSettings?.enabled || !termsSettings.contentUrl) { if (!termsSettings?.enabled || !termsSettings.contentUrl) {
return null; return null;
@ -27,17 +26,7 @@ const TermsOfUse = ({ className }: Props) => {
setTermsAgreement(checked); setTermsAgreement(checked);
}} }}
/> />
<TermsOfUseModal <ModalContainer />
isOpen={showTermsModal}
termsUrl={termsSettings.contentUrl}
onConfirm={() => {
setTermsAgreement(true);
setShowTermsModal(false);
}}
onClose={() => {
setShowTermsModal(false);
}}
/>
</> </>
); );
}; };

View file

@ -0,0 +1,28 @@
import React, { useContext } from 'react';
import { create, InstanceProps } from 'react-modal-promise';
import TermsOfUseModal from '@/components/TermsOfUseModal';
import { PageContext } from '@/hooks/use-page-context';
const TermsOfUsePromiseModal = ({ isOpen, onResolve, onReject }: InstanceProps<boolean>) => {
const { setTermsAgreement, experienceSettings } = useContext(PageContext);
const { termsOfUse } = experienceSettings ?? {};
return (
<TermsOfUseModal
isOpen={isOpen}
termsUrl={termsOfUse?.contentUrl ?? ''}
onConfirm={() => {
setTermsAgreement(true);
onResolve(true);
}}
onClose={() => {
onReject(false);
}}
/>
);
};
export default TermsOfUsePromiseModal;
export const termsOfUseModalPromise = create(TermsOfUsePromiseModal);

View file

@ -61,7 +61,7 @@ const UsernameSignin = ({ className }: Props) => {
return; return;
} }
if (!termsValidation()) { if (!(await termsValidation())) {
return; return;
} }

View file

@ -9,14 +9,12 @@ type Context = {
loading: boolean; loading: boolean;
platform: Platform; platform: Platform;
termsAgreement: boolean; termsAgreement: boolean;
showTermsModal: boolean;
experienceSettings: SignInExperienceSettings | undefined; experienceSettings: SignInExperienceSettings | undefined;
setTheme: (theme: Theme) => void; setTheme: (theme: Theme) => void;
setToast: (message: string) => void; setToast: (message: string) => void;
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
setPlatform: (platform: Platform) => void; setPlatform: (platform: Platform) => void;
setTermsAgreement: (termsAgreement: boolean) => void; setTermsAgreement: (termsAgreement: boolean) => void;
setShowTermsModal: (showTermsModal: boolean) => void;
setExperienceSettings: (settings: SignInExperienceSettings) => void; setExperienceSettings: (settings: SignInExperienceSettings) => void;
}; };
@ -30,14 +28,12 @@ export const PageContext = createContext<Context>({
loading: false, loading: false,
platform: isMobile ? 'mobile' : 'web', platform: isMobile ? 'mobile' : 'web',
termsAgreement: false, termsAgreement: false,
showTermsModal: false,
experienceSettings: undefined, experienceSettings: undefined,
setTheme: noop, setTheme: noop,
setToast: noop, setToast: noop,
setLoading: noop, setLoading: noop,
setPlatform: noop, setPlatform: noop,
setTermsAgreement: noop, setTermsAgreement: noop,
setShowTermsModal: noop,
setExperienceSettings: noop, setExperienceSettings: noop,
}); });
@ -48,7 +44,6 @@ const usePageContext = () => {
const [platform, setPlatform] = useState<Platform>(isMobile ? 'mobile' : 'web'); const [platform, setPlatform] = useState<Platform>(isMobile ? 'mobile' : 'web');
const [experienceSettings, setExperienceSettings] = useState<SignInExperienceSettings>(); const [experienceSettings, setExperienceSettings] = useState<SignInExperienceSettings>();
const [termsAgreement, setTermsAgreement] = useState(false); const [termsAgreement, setTermsAgreement] = useState(false);
const [showTermsModal, setShowTermsModal] = useState(false);
const context = useMemo( const context = useMemo(
() => ({ () => ({
@ -57,17 +52,15 @@ const usePageContext = () => {
loading, loading,
platform, platform,
termsAgreement, termsAgreement,
showTermsModal,
experienceSettings, experienceSettings,
setTheme, setTheme,
setLoading, setLoading,
setToast, setToast,
setPlatform, setPlatform,
setTermsAgreement, setTermsAgreement,
setShowTermsModal,
setExperienceSettings, setExperienceSettings,
}), }),
[experienceSettings, loading, platform, showTermsModal, termsAgreement, theme, toast] [experienceSettings, loading, platform, termsAgreement, theme, toast]
); );
return { return {

View file

@ -51,7 +51,7 @@ const useSocial = () => {
const invokeSocialSignInHandler = useCallback( const invokeSocialSignInHandler = useCallback(
async (connectorId: string, callback?: () => void) => { async (connectorId: string, callback?: () => void) => {
if (!termsValidation()) { if (!(await termsValidation())) {
return; return;
} }

View file

@ -1,33 +1,33 @@
import { useContext, useCallback } from 'react'; import { useContext, useCallback } from 'react';
import { termsOfUseModalPromise } from '@/containers/TermsOfUsePromiseModal';
import { PageContext } from './use-page-context'; import { PageContext } from './use-page-context';
const useTerms = () => { const useTerms = () => {
const { const { termsAgreement, setTermsAgreement, experienceSettings } = useContext(PageContext);
termsAgreement,
setTermsAgreement,
showTermsModal,
setShowTermsModal,
experienceSettings,
} = useContext(PageContext);
const termsValidation = useCallback(() => { const { termsOfUse } = experienceSettings ?? {};
if (termsAgreement || !experienceSettings?.termsOfUse.enabled) {
const termsValidation = useCallback(async () => {
if (termsAgreement || !termsOfUse?.enabled || !termsOfUse.contentUrl) {
return true; return true;
} }
setShowTermsModal(true); try {
await termsOfUseModalPromise();
return true;
} catch {
return false; return false;
}, [experienceSettings, termsAgreement, setShowTermsModal]); }
}, [termsAgreement, termsOfUse]);
return { return {
termsSettings: experienceSettings?.termsOfUse, termsSettings: termsOfUse,
termsAgreement, termsAgreement,
showTermsModal,
termsValidation, termsValidation,
setTermsAgreement, setTermsAgreement,
setShowTermsModal,
}; };
}; };

View file

@ -56,6 +56,6 @@ describe('<SignIn />', () => {
</SettingsProvider> </SettingsProvider>
); );
expect(container.querySelectorAll('button')).toHaveLength(3); expect(container.querySelectorAll('button')).toHaveLength(4); // Plus Expand Button
}); });
}); });

View file

@ -964,6 +964,7 @@ importers:
react-dom: ^17.0.2 react-dom: ^17.0.2
react-i18next: ^11.15.4 react-i18next: ^11.15.4
react-modal: ^3.14.4 react-modal: ^3.14.4
react-modal-promise: ^1.0.2
react-phone-number-input: ^3.1.46 react-phone-number-input: ^3.1.46
react-router-dom: ^6.2.2 react-router-dom: ^6.2.2
react-string-replace: ^1.0.0 react-string-replace: ^1.0.0
@ -1010,6 +1011,7 @@ importers:
react-dom: 17.0.2_react@17.0.2 react-dom: 17.0.2_react@17.0.2
react-i18next: 11.15.4_fq32mavcto3l2u7t3zyhvdh4yu react-i18next: 11.15.4_fq32mavcto3l2u7t3zyhvdh4yu
react-modal: 3.14.4_sfoxds7t5ydpegc3knd667wn6m react-modal: 3.14.4_sfoxds7t5ydpegc3knd667wn6m
react-modal-promise: 1.0.2_sfoxds7t5ydpegc3knd667wn6m
react-phone-number-input: 3.1.46_sfoxds7t5ydpegc3knd667wn6m react-phone-number-input: 3.1.46_sfoxds7t5ydpegc3knd667wn6m
react-router-dom: 6.2.2_sfoxds7t5ydpegc3knd667wn6m react-router-dom: 6.2.2_sfoxds7t5ydpegc3knd667wn6m
react-string-replace: 1.0.0 react-string-replace: 1.0.0
@ -16623,6 +16625,16 @@ packages:
- supports-color - supports-color
dev: true dev: true
/react-modal-promise/1.0.2_sfoxds7t5ydpegc3knd667wn6m:
resolution: {integrity: sha512-dqT618ROhG8qh1+O6EZkia5ELw3zaZWGpMX2YfEH4bgwYENPuFonqKw1W70LFx3K/SCZvVBcD6UYEI12yzYXzg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
dev: true
/react-modal/3.14.4_sfoxds7t5ydpegc3knd667wn6m: /react-modal/3.14.4_sfoxds7t5ydpegc3knd667wn6m:
resolution: {integrity: sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==} resolution: {integrity: sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==}
engines: {node: '>=8'} engines: {node: '>=8'}