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:
parent
d7ce13d260
commit
9859213e19
14 changed files with 72 additions and 49 deletions
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
28
packages/ui/src/containers/TermsOfUsePromiseModal/index.tsx
Normal file
28
packages/ui/src/containers/TermsOfUsePromiseModal/index.tsx
Normal 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);
|
|
@ -61,7 +61,7 @@ const UsernameSignin = ({ className }: Props) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!termsValidation()) {
|
if (!(await termsValidation())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 false;
|
return true;
|
||||||
}, [experienceSettings, termsAgreement, setShowTermsModal]);
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [termsAgreement, termsOfUse]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
termsSettings: experienceSettings?.termsOfUse,
|
termsSettings: termsOfUse,
|
||||||
termsAgreement,
|
termsAgreement,
|
||||||
showTermsModal,
|
|
||||||
termsValidation,
|
termsValidation,
|
||||||
setTermsAgreement,
|
setTermsAgreement,
|
||||||
setShowTermsModal,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,6 @@ describe('<SignIn />', () => {
|
||||||
</SettingsProvider>
|
</SettingsProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(container.querySelectorAll('button')).toHaveLength(3);
|
expect(container.querySelectorAll('button')).toHaveLength(4); // Plus Expand Button
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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'}
|
||||||
|
|
Loading…
Reference in a new issue