Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: move account modals to separate file #202

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 29 additions & 92 deletions packages/ui-react/src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import React, { useEffect, useMemo, useRef } from 'react';
import React, { Suspense, useEffect, useMemo, useRef } from 'react';
import { useLocation, useNavigate } from 'react-router';
import { shallow } from '@jwp/ott-common/src/utils/compare';
import { useConfigStore } from '@jwp/ott-common/src/stores/ConfigStore';
import { useAccountStore } from '@jwp/ott-common/src/stores/AccountStore';
import { modalURLFromLocation, createURLFromLocation } from '@jwp/ott-ui-react/src/utils/location';
import { createURLFromLocation, modalURLFromLocation } from '@jwp/ott-ui-react/src/utils/location';
import useEventCallback from '@jwp/ott-hooks-react/src/useEventCallback';
import useQueryParam from '@jwp/ott-ui-react/src/hooks/useQueryParam';

import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import Welcome from '../../components/Welcome/Welcome';
import PaymentFailed from '../../components/PaymentFailed/PaymentFailed';
import Dialog from '../../components/Dialog/Dialog';
import DeleteAccountModal from '../../components/DeleteAccountModal/DeleteAccountModal';
import FinalizePayment from '../../components/FinalizePayment/FinalizePayment';
import WaitingForPayment from '../../components/WaitingForPayment/WaitingForPayment';
import UpgradeSubscription from '../../components/UpgradeSubscription/UpgradeSubscription';
import DeleteAccountPasswordWarning from '../../components/DeleteAccountPasswordWarning/DeleteAccountPasswordWarning';
import UpdatePaymentMethod from '../UpdatePaymentMethod/UpdatePaymentMethod';

import EditCardDetails from './forms/EditCardDetails';
import EditPassword from './forms/EditPassword';
import RenewSubscription from './forms/RenewSubscription';
import CancelSubscription from './forms/CancelSubscription';
import ResetPassword from './forms/ResetPassword';
import Checkout from './forms/Checkout';
import ChooseOffer from './forms/ChooseOffer';
import PersonalDetails from './forms/PersonalDetails';
import Registration from './forms/Registration';
import Login from './forms/Login';
import styles from './AccountModal.module.scss';

// @todo: connect with route typings
const PUBLIC_VIEWS = ['login', 'create-account', 'forgot-password', 'reset-password', 'send-confirmation', 'edit-password'];
import styles from './AccountModal.module.scss';

export type AccountModals = {
login: 'login';
Expand Down Expand Up @@ -63,19 +42,24 @@ export type AccountModals = {
'finalize-payment': 'finalize-payment';
};

const AccountModal = () => {
type ModalDef = {
key: string;
component: React.ReactNode;
public?: boolean;
hideBanner?: boolean;
size?: 'large' | 'small';
};

const AccountModal = ({ modals }: { modals: ModalDef[] }) => {
const navigate = useNavigate();
const location = useLocation();
const viewParam = useQueryParam('u');
const viewParamRef = useRef(viewParam);
const message = useQueryParam('message');
const { loading, user } = useAccountStore(({ loading, user }) => ({ loading, user }), shallow);
const config = useConfigStore((s) => s.config);
const {
assets: { banner },
siteName,
} = config;
const isPublicView = viewParam && PUBLIC_VIEWS.includes(viewParam);

const toLogin = useEventCallback(() => {
navigate(modalURLFromLocation(location, 'login'));
Expand All @@ -87,6 +71,11 @@ const AccountModal = () => {
return viewParamRef.current;
}, [viewParam]);

const currentModal = useMemo(() => modals.find(({ key }) => key === view), [view, modals]);
const isPublicView = !!currentModal?.public;
const shouldShowBanner = !!currentModal?.hideBanner;
const dialogSize = currentModal?.size || 'small';

useEffect(() => {
if (!!viewParam && !loading && !user && !isPublicView) {
toLogin();
Expand All @@ -97,82 +86,30 @@ const AccountModal = () => {
navigate(createURLFromLocation(location, { u: null, message: null }));
});

const fallback = (
<div style={{ height: 300 }}>
<LoadingOverlay inline />
</div>
);

const renderForm = () => {
if (!user && loading && !isPublicView) {
return (
<div style={{ height: 300 }}>
<LoadingOverlay inline />
</div>
);
return fallback;
}

switch (view) {
case 'login':
return <Login />;
case 'create-account':
return <Registration />;
case 'personal-details':
return <PersonalDetails />;
case 'choose-offer':
return <ChooseOffer />;
case 'edit-card':
return <EditCardDetails />;
case 'upgrade-subscription':
return <ChooseOffer />;
case 'upgrade-subscription-error':
return <UpgradeSubscription type="error" onCloseButtonClick={closeHandler} />;
case 'upgrade-subscription-success':
return <UpgradeSubscription type="success" onCloseButtonClick={closeHandler} />;
case 'upgrade-subscription-pending':
return <UpgradeSubscription type="pending" onCloseButtonClick={closeHandler} />;
case 'checkout':
return <Checkout />;
case 'payment-error':
return <PaymentFailed type="error" message={message} onCloseButtonClick={closeHandler} />;
case 'payment-cancelled':
return <PaymentFailed type="cancelled" message={message} onCloseButtonClick={closeHandler} />;
case 'welcome':
return <Welcome onCloseButtonClick={closeHandler} onCountdownCompleted={closeHandler} siteName={siteName} />;
case 'reset-password':
return <ResetPassword type="reset" />;
case 'forgot-password':
return <ResetPassword type="forgot" />;
case 'add-password':
return <EditPassword type="add" />;
case 'delete-account':
case 'delete-account-confirmation':
return <DeleteAccountModal />;
case 'warning-account-deletion':
return <DeleteAccountPasswordWarning />;
case 'send-confirmation':
return <ResetPassword type="confirmation" />;
case 'edit-password':
return <EditPassword />;
case 'unsubscribe':
return <CancelSubscription />;
case 'renew-subscription':
return <RenewSubscription />;
case 'payment-method':
case 'payment-method-success':
return <UpdatePaymentMethod onCloseButtonClick={closeHandler} />;
case 'waiting-for-payment':
return <WaitingForPayment />;
case 'finalize-payment':
return <FinalizePayment />;
}
};
if (!currentModal) return null;

const shouldShowBanner = !['delete-account', 'delete-account-confirmation', 'edit-card', 'warning-account-deletion'].includes(view ?? '');
const dialogSize = ['delete-account-confirmation'].includes(view ?? '') ? 'large' : 'small';
return currentModal.component;
};

return (
<Dialog size={dialogSize} open={!!viewParam} onClose={closeHandler}>
{shouldShowBanner && banner && (
{!shouldShowBanner && banner && (
<div className={styles.banner}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this shouldShowBanner condition reversed?

<img src={banner} alt="" />
</div>
)}
{renderForm()}
<Suspense fallback={fallback}>{renderForm()}</Suspense>
</Dialog>
Copy link

@royschut royschut Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: we don't need a function anymore. Maybe just a boolean for the !user etc fallback, or render currentModal.component.

);
};
Expand Down
113 changes: 113 additions & 0 deletions platforms/web/src/accountModals.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from 'react';

type ModalDef = {
key: string;
component: React.ReactNode;
public?: boolean;
hideBanner?: boolean;
size?: 'large' | 'small';
};

const Login = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/Login'));
const Registration = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/Registration'));
const PersonalDetails = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/PersonalDetails'));
const EditCard = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/EditCardDetails'));
const Checkout = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/Checkout'));
const Welcome = React.lazy(() => import('@jwp/ott-ui-react/src/components/Welcome/Welcome'));

const ChooseOffer = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/ChooseOffer'));
const ResetPassword = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/ResetPassword'));
const EditPassword = React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/EditPassword'));
const UpgradeSubscription = React.lazy(() => import('@jwp/ott-ui-react/src/components/UpgradeSubscription/UpgradeSubscription'));
const PaymentFailed = React.lazy(() => import('@jwp/ott-ui-react/src/components/PaymentFailed/PaymentFailed'));
const DeleteAccountModal = React.lazy(() => import('@jwp/ott-ui-react/src/components/DeleteAccountModal/DeleteAccountModal'));
const DeleteAccountPasswordWarning = React.lazy(() => import('@jwp/ott-ui-react/src/components/DeleteAccountPasswordWarning/DeleteAccountPasswordWarning'));
const UpdatePaymentMethod = React.lazy(() => import('@jwp/ott-ui-react/src/containers/UpdatePaymentMethod/UpdatePaymentMethod'));

const closeModal = () => {
// todo
console.warn('Close modal should be moved to component...');
};

const accountModals: ModalDef[] = [
{
key: 'login',
public: true,
component: <Login />,
},
{
key: 'create-account',
public: true,
component: <Registration />,
},
{
key: 'personal-details',
component: <PersonalDetails />,
},
{ key: 'choose-offer', component: <ChooseOffer /> },
{
key: 'edit-card',
hideBanner: true,
component: <EditCard />,
},
{
key: 'checkout',
component: <Checkout />,
},
{ key: 'welcome', component: <Welcome /> },

{ key: 'upgrade-subscription', component: <ChooseOffer /> },
{
key: 'upgrade-subscription-error',
component: <UpgradeSubscription type="error" onCloseButtonClick={closeModal} />,
},
{
key: 'upgrade-subscription-success',
component: <UpgradeSubscription type="success" onCloseButtonClick={closeModal} />,
},
{
key: 'upgrade-subscription-pending',
component: <UpgradeSubscription type="pending" onCloseButtonClick={closeModal} />,
},

{
key: 'payment-error',
component: <PaymentFailed type="error" message="asd" onCloseButtonClick={closeModal} />,
},
{
key: 'payment-cancelled',
component: <PaymentFailed type="cancelled" message="asd" onCloseButtonClick={closeModal} />,
},

{ key: 'reset-password', public: true, component: <ResetPassword type="reset" /> },
{ key: 'forgot-password', public: true, component: <ResetPassword type="forgot" /> },
{ key: 'edit-password', public: true, component: <EditPassword /> },
{ key: 'send-confirmation', public: true, component: <ResetPassword type="confirmation" /> },
{ key: 'add-password', component: <EditPassword type="add" /> },

{ key: 'delete-account', hideBanner: true, component: <DeleteAccountModal /> },
{ key: 'delete-account-confirmation', hideBanner: true, size: 'large', component: <DeleteAccountModal /> },
{ key: 'warning-account-deletion', hideBanner: true, component: <DeleteAccountPasswordWarning /> },

// {
// key: 'unsubscribe',
// component: React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/CancelSubscription')),
// },
// {
// key: 'renew-subscription',
// component: React.lazy(() => import('@jwp/ott-ui-react/src/containers/AccountModal/forms/RenewSubscription')),
// },

{ key: 'payment-method', component: <UpdatePaymentMethod onCloseButtonClick={closeModal} /> },
{ key: 'payment-method-success', component: <UpdatePaymentMethod onCloseButtonClick={closeModal} /> },
// {
// key: 'waiting-for-payment',
// component: React.lazy(() => import('@jwp/ott-ui-react/src/components/WaitingForPayment/WaitingForPayment')),
// },
// {
// key: 'finalize-payment',
// component: React.lazy(() => import('@jwp/ott-ui-react/src/components/FinalizePayment/FinalizePayment')),
// },
];

export default accountModals;
3 changes: 2 additions & 1 deletion platforms/web/src/containers/Root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import AppRoutes from '../AppRoutes/AppRoutes';

import registerCustomScreens from '#src/screenMapping';
import { useTrackConfigKeyChange } from '#src/hooks/useTrackConfigKeyChange';
import accountModals from '#src/accountModals';

const IS_DEMO_OR_PREVIEW = IS_DEMO_MODE || IS_PREVIEW_MODE;

Expand Down Expand Up @@ -93,7 +94,7 @@ const Root: FC = () => {
return (
<>
{isReady && <AppRoutes />}
{isReady && <AccountModal />}
{isReady && <AccountModal modals={accountModals} />}
{/*This is moved to a separate, parallel component to reduce rerenders */}
<RootLoader onReady={onReadyCallback} />
</>
Expand Down
Loading