Remove payments API routing initialization
[ProtonMail-WebClient.git] / packages / components / containers / onboarding / OnboardingModal.tsx
blob77111f87fc3db3ca8a17e706a0ebfdd2c6ef2d69
1 import { isValidElement, useState } from 'react';
3 import { useWelcomeFlags } from '@proton/account';
4 import { useUserSettings } from '@proton/account/userSettings/hooks';
5 import type { ModalSize } from '@proton/components/components/modalTwo/Modal';
6 import ModalTwo from '@proton/components/components/modalTwo/Modal';
7 import ModalTwoContent from '@proton/components/components/modalTwo/ModalContent';
8 import StepDot from '@proton/components/components/stepDot/StepDot';
9 import StepDots from '@proton/components/components/stepDots/StepDots';
10 import useApi from '@proton/components/hooks/useApi';
11 import { updateFlags, updateWelcomeFlags } from '@proton/shared/lib/api/settings';
12 import clsx from '@proton/utils/clsx';
13 import noop from '@proton/utils/noop';
14 import range from '@proton/utils/range';
16 import type { OnboardingStepComponent, OnboardingStepProps } from './interface';
17 import useGenericSteps from './useGenericSteps';
19 import './OnboardingModal.scss';
21 interface Props {
22     children?: OnboardingStepComponent[];
23     className?: string;
24     extraProductStep?: OnboardingStepComponent[];
25     genericSteps?: Partial<Record<'discoverAppsStep' | 'setupThemeStep' | 'organizationStep', OnboardingStepComponent>>;
26     hideDiscoverApps?: boolean;
27     /** Some onboarding modals need custom dimension / margins */
28     hideOrganizationSetup?: boolean;
29     modalClassname?: string;
30     modalContentClassname?: string;
31     onClose?: () => void;
32     onDone?: () => void;
33     onExit?: () => void;
34     showGenericSteps?: boolean;
35     size?: ModalSize;
36     stepDotClassName?: string;
39 const OnboardingModal = ({
40     children,
41     extraProductStep,
42     genericSteps,
43     hideDiscoverApps = false,
44     hideOrganizationSetup = false,
45     modalClassname,
46     modalContentClassname = 'm-8',
47     onDone,
48     showGenericSteps,
49     size = 'small',
50     stepDotClassName,
51     ...rest
52 }: Props) => {
53     const [userSettings] = useUserSettings();
54     const api = useApi();
55     const { welcomeFlags } = useWelcomeFlags();
56     // Using useState so that isReplay is only updated when the modal closes, not when welcomeFlags change.
57     const [isReplay] = useState(welcomeFlags.isReplay);
58     let isLastStep = false;
60     const [step, setStep] = useState(0);
62     const handleNext = () => {
63         if (isLastStep) {
64             if (welcomeFlags.isWelcomeFlow) {
65                 // Set generic welcome to true
66                 api(updateFlags({ Welcomed: 1 })).catch(noop);
67             }
68             if (!userSettings.WelcomeFlag) {
69                 // Set product specific welcome to true
70                 api(updateWelcomeFlags()).catch(noop);
71             }
72             onDone?.();
73             rest?.onClose?.();
74             return;
75         }
76         setStep((step) => step + 1);
77     };
79     const handleBack = () => {
80         setStep((step) => step - 1);
81     };
83     const displayGenericSteps = welcomeFlags.hasGenericWelcomeStep || isReplay || showGenericSteps;
84     const genericStepsComponents = useGenericSteps({
85         onNext: handleNext,
86         onBack: handleBack,
87         hideDiscoverApps,
88         hideOrganizationSetup,
89         genericSteps,
90     });
92     const productSteps = children
93         ? (Array.isArray(children) ? children : [children]).map(
94               (renderCallback) =>
95                   renderCallback?.({
96                       onNext: handleNext,
97                       onBack: handleBack,
98                       displayGenericSteps,
99                   }) ?? null
100           )
101         : [];
103     const extraSteps = extraProductStep
104         ? (Array.isArray(extraProductStep) ? extraProductStep : [extraProductStep]).map(
105               (renderCallback) =>
106                   renderCallback?.({
107                       onNext: handleNext,
108                       onBack: handleBack,
109                       displayGenericSteps,
110                   }) ?? null
111           )
112         : [];
114     const steps = [...productSteps, ...(displayGenericSteps ? genericStepsComponents : []), ...extraSteps].filter(
115         Boolean
116     );
117     isLastStep = steps.length - 1 === step;
118     const childStep = steps[step];
119     const displayDots = steps.length > 1 && step < steps.length;
121     if (!steps.length) {
122         rest?.onClose?.();
123     }
125     if (!isValidElement<OnboardingStepProps>(childStep)) {
126         throw new Error('Missing step');
127     }
129     return (
130         <ModalTwo {...rest} size={size} className={clsx('onboarding-modal', modalClassname)}>
131             <ModalTwoContent className={modalContentClassname}>
132                 {childStep}
133                 {displayDots ? (
134                     <div className="text-center">
135                         <StepDots value={step} ulClassName={clsx('mb-0', stepDotClassName)}>
136                             {range(0, steps.length).map((index) => (
137                                 <StepDot
138                                     active={index === step}
139                                     key={index}
140                                     index={index}
141                                     aria-controls={`onboarding-${index}`}
142                                     onClick={() => {
143                                         setStep(index);
144                                     }}
145                                 />
146                             ))}
147                         </StepDots>
148                     </div>
149                 ) : null}
150             </ModalTwoContent>
151         </ModalTwo>
152     );
155 export default OnboardingModal;