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

Feat/zustand refactor #64

Merged
merged 20 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ed9968e
feat(project): add Zustand dependency
ChristiaanScheermeijer May 16, 2022
fc9c099
refactor(project): refactor AccountStore with Zustand
ChristiaanScheermeijer May 16, 2022
8bdafe4
refactor(project): refactor ConfigStore with Zustand
ChristiaanScheermeijer May 17, 2022
742ed88
refactor(project): rename UseConfigStore to ConfigStore
ChristiaanScheermeijer May 17, 2022
392fab1
refactor(payment): refactor zustand into checkoutstore
royschut May 17, 2022
ef2db82
chore(project): include Zustand devtools only for dev env
ChristiaanScheermeijer May 17, 2022
8a4810e
refactor(project): combine and optimize Zustand selectors
ChristiaanScheermeijer May 17, 2022
d2cbdf7
refactor(payment): destructure using objects
royschut May 17, 2022
e8ae6e8
refactor(project): combine and optimize Zustand selectors
ChristiaanScheermeijer May 17, 2022
7ca5ee8
Merge branch 'feat/add-zustand-and-refactor-account-store' into feat/…
royschut May 17, 2022
04e496e
Merge branch 'feat/add-zustand-and-refactor-account-store' into feat/…
royschut May 17, 2022
4f6eca8
refactor(watchhistory): refactor WatchHistoryStore with Zustand
ChristiaanScheermeijer May 17, 2022
c98f8ae
Merge pull request #55 from jwplayer/feat/add-zustand-and-refactor-ac…
ChristiaanScheermeijer May 17, 2022
0760b4b
refactor(favorites): refactor FavoritesStore with Zustand
ChristiaanScheermeijer May 17, 2022
905da68
Merge pull request #61 from jwplayer/feat/refactor-watch-history-store
ChristiaanScheermeijer May 17, 2022
f9b1d95
refactor(project): refactor UIStore with Zustand
ChristiaanScheermeijer May 17, 2022
6c66b53
Merge pull request #60 from jwplayer/feat/refactor-checkout-store
ChristiaanScheermeijer May 17, 2022
257f28e
Merge pull request #62 from jwplayer/feat/refactor-favorites-store
ChristiaanScheermeijer May 17, 2022
04e958f
Merge pull request #63 from jwplayer/feat/refactor-ui-store
ChristiaanScheermeijer May 18, 2022
707baec
chore(project): remove pullstate dependency
ChristiaanScheermeijer May 18, 2022
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"jwt-decode": "^3.1.2",
"lodash.merge": "^4.6.2",
"memoize-one": "^5.2.1",
"pullstate": "^1.22.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet": "^6.1.0",
Expand All @@ -39,7 +38,8 @@
"react-router-dom": "^5.2.0",
"react-virtualized": "^9.22.3",
"wicg-inert": "^3.1.1",
"yup": "^0.32.9"
"yup": "^0.32.9",
"zustand": "^3.6.9"
},
"devDependencies": {
"@codeceptjs/configure": "^0.8.0",
Expand Down
6 changes: 3 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import Router from '#src/components/Router/Router';
import Root from '#src/components/Root/Root';
import ConfigProvider from '#src/providers/ConfigProvider';
import QueryProvider from '#src/providers/QueryProvider';
import { restoreWatchHistory } from '#src/stores/WatchHistoryStore';
import { initializeFavorites } from '#src/stores/FavoritesStore';
import { initializeAccount } from '#src/stores/AccountStore';
import { restoreWatchHistory } from '#src/stores/WatchHistoryController';
import { initializeAccount } from '#src/stores/AccountController';
import { initializeFavorites } from '#src/stores/FavoritesController';
import '#src/i18n/config';
import '#src/styles/main.scss';

Expand Down
8 changes: 4 additions & 4 deletions src/components/Account/Account.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import React from 'react';
import { render } from '@testing-library/react';

import customer from '../../fixtures/customer.json';
import { AccountStore } from '../../stores/AccountStore';
import { useAccountStore } from '../../stores/AccountStore';

import Account from './Account';

import type { Consent } from '#types/account';

describe('<Account>', () => {
test('renders and matches snapshot', () => {
AccountStore.update((s) => {
s.user = customer;
s.publisherConsents = Array.of({ name: 'marketing', label: 'Receive Marketing Emails' } as Consent);
useAccountStore.setState({
user: customer,
publisherConsents: Array.of({ name: 'marketing', label: 'Receive Marketing Emails' } as Consent),
});

const { container } = render(<Account panelClassName={'panel-class'} panelHeaderClassName={'header-class'} />);
Expand Down
37 changes: 29 additions & 8 deletions src/components/Account/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import shallow from 'zustand/shallow';

import { formatConsentsFromValues, formatConsentValues } from '../../utils/collection';
import type { FormSectionContentArgs, FormSectionProps } from '../Form/FormSection';
import Visibility from '../../icons/Visibility';
import VisibilityOff from '../../icons/VisibilityOff';
import useToggle from '../../hooks/useToggle';
Expand All @@ -11,11 +12,13 @@ import Form from '../Form/Form';
import IconButton from '../IconButton/IconButton';
import TextField from '../TextField/TextField';
import Checkbox from '../Checkbox/Checkbox';
import { addQueryParam } from '../../utils/history';
import { AccountStore, updateConsents, updateUser } from '../../stores/AccountStore';
import HelperText from '../HelperText/HelperText';
import type { FormSectionContentArgs, FormSectionProps } from '../Form/FormSection';
import { logDev } from '../../utils/common';

import { formatConsentsFromValues, formatConsentValues } from '#src/utils/collection';
import { addQueryParam } from '#src/utils/history';
import { useAccountStore } from '#src/stores/AccountStore';
import { logDev } from '#src/utils/common';
import { updateConsents, updateUser } from '#src/stores/AccountController';

type Props = {
panelClassName?: string;
Expand All @@ -35,10 +38,24 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =
const history = useHistory();
const [viewPassword, toggleViewPassword] = useToggle();

const { user: customer, customerConsents, publisherConsents } = AccountStore.useState((state) => state);
const { customer, customerConsents, publisherConsents } = useAccountStore(
({ user, customerConsents, publisherConsents }) => ({
customer: user,
customerConsents,
publisherConsents,
}),
shallow,
);

const consentValues = useMemo(() => formatConsentValues(publisherConsents, customerConsents), [publisherConsents, customerConsents]);
const initialValues = useMemo(() => ({ ...customer, consents: consentValues, confirmationPassword: '' }), [customer, consentValues]);
const initialValues = useMemo(
() => ({
...customer,
consents: consentValues,
confirmationPassword: '',
}),
[customer, consentValues],
);

const formatConsentLabel = (label: string): string | JSX.Element => {
// @todo sanitize consent label to prevent XSS
Expand Down Expand Up @@ -123,7 +140,11 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =
{[
formSection({
label: t('account.email'),
onSubmit: (values) => updateUser({ email: values.email || '', confirmationPassword: values.confirmationPassword }),
onSubmit: (values) =>
updateUser({
email: values.email || '',
confirmationPassword: values.confirmationPassword,
}),
canSave: (values) => !!(values.email && values.confirmationPassword),
editButton: t('account.edit_account'),
content: (section) => (
Expand Down
2 changes: 1 addition & 1 deletion src/components/UserMenu/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import Favorite from '../../icons/Favorite';
import BalanceWallet from '../../icons/BalanceWallet';
import Exit from '../../icons/Exit';
import MenuButton from '../MenuButton/MenuButton';
import { logout } from '../../stores/AccountStore';

import styles from './UserMenu.module.scss';
import { logout } from '#src/stores/AccountController';

type Props = {
inPopover?: boolean;
Expand Down
9 changes: 5 additions & 4 deletions src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import shallow from 'zustand/shallow';

import Dialog from '../../components/Dialog/Dialog';
import useQueryParam from '../../hooks/useQueryParam';
import { addQueryParam, removeQueryParam } from '../../utils/history';
import PaymentFailed from '../../components/PaymentFailed/PaymentFailed';
import Welcome from '../../components/Welcome/Welcome';
import { AccountStore } from '../../stores/AccountStore';
import { useAccountStore } from '../../stores/AccountStore';
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import { ConfigStore } from '../../stores/ConfigStore';
import { useConfigStore } from '../../stores/ConfigStore';

import styles from './AccountModal.module.scss';
import Login from './forms/Login';
Expand All @@ -28,8 +29,8 @@ const AccountModal = () => {
const viewParam = useQueryParam('u');
const [view, setView] = useState(viewParam);
const message = useQueryParam('message');
const { loading, auth } = AccountStore.useState((s) => s);
const config = ConfigStore.useState((s) => s.config);
const { loading, auth } = useAccountStore(({ loading, auth }) => ({ loading, auth }), shallow);
const config = useConfigStore((s) => s.config);
const {
assets: { banner },
siteName,
Expand Down
6 changes: 4 additions & 2 deletions src/containers/AccountModal/forms/CancelSubscription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import { useHistory } from 'react-router';
import CancelSubscriptionForm from '../../../components/CancelSubscriptionForm/CancelSubscriptionForm';
import { removeQueryParam } from '../../../utils/history';
import LoadingOverlay from '../../../components/LoadingOverlay/LoadingOverlay';
import { AccountStore, updateSubscription } from '../../../stores/AccountStore';
import { useAccountStore } from '../../../stores/AccountStore';
import SubscriptionCancelled from '../../../components/SubscriptionCancelled/SubscriptionCancelled';
import { formatDate } from '../../../utils/formatting';

import { updateSubscription } from '#src/stores/AccountController';

const CancelSubscription = () => {
const { t } = useTranslation('account');
const history = useHistory();
const subscription = AccountStore.useState((s) => s.subscription);
const subscription = useAccountStore((s) => s.subscription);
const [cancelled, setCancelled] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
Expand Down
30 changes: 16 additions & 14 deletions src/containers/AccountModal/forms/Checkout.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import { useTranslation } from 'react-i18next';

import CheckoutForm from '../../../components/CheckoutForm/CheckoutForm';
import { CheckoutStore, createOrder, updateOrder, getPaymentMethods, paymentWithoutDetails, adyenPayment, paypalPayment } from '../../../stores/CheckoutStore';
import { addQueryParam } from '../../../utils/history';
import useForm from '../../../hooks/useForm';
import LoadingOverlay from '../../../components/LoadingOverlay/LoadingOverlay';
import Adyen from '../../../components/Adyen/Adyen';
import PayPal from '../../../components/PayPal/PayPal';
import NoPaymentRequired from '../../../components/NoPaymentRequired/NoPaymentRequired';
import { addQueryParams } from '../../../utils/formatting';
import { reloadActiveSubscription } from '../../../stores/AccountStore';
import { ConfigStore } from '../../../stores/ConfigStore';
import shallow from 'zustand/shallow';

import CheckoutForm from '#src/components/CheckoutForm/CheckoutForm';
import { addQueryParam } from '#src/utils/history';
import useForm from '#src/hooks/useForm';
import LoadingOverlay from '#src/components/LoadingOverlay/LoadingOverlay';
import Adyen from '#src/components/Adyen/Adyen';
import PayPal from '#src/components/PayPal/PayPal';
import NoPaymentRequired from '#src/components/NoPaymentRequired/NoPaymentRequired';
import { addQueryParams } from '#src/utils/formatting';
import { useConfigStore } from '#src/stores/ConfigStore';
import { useCheckoutStore } from '#src/stores/CheckoutStore';
import { adyenPayment, createOrder, getPaymentMethods, paymentWithoutDetails, paypalPayment, updateOrder } from '#src/stores/CheckoutController';
import { reloadActiveSubscription } from '#src/stores/AccountController';

const Checkout = () => {
const { cleengSandbox } = ConfigStore.useState((s) => s.config);
const { cleengSandbox } = useConfigStore((s) => s.config);
const { t } = useTranslation('account');
const history = useHistory();
const [paymentError, setPaymentError] = useState<string | undefined>(undefined);
Expand All @@ -24,7 +26,7 @@ const Checkout = () => {
const [couponCodeApplied, setCouponCodeApplied] = useState(false);
const [paymentMethodId, setPaymentMethodId] = useState<number | undefined>(undefined);

const { order, offer, paymentMethods } = CheckoutStore.useState((s) => s);
const { order, offer, paymentMethods } = useCheckoutStore(({ order, offer, paymentMethods }) => ({ order, offer, paymentMethods }), shallow);

const couponCodeForm = useForm({ couponCode: '' }, async (values, { setSubmitting, setErrors }) => {
setUpdatingOrder(true);
Expand Down
27 changes: 12 additions & 15 deletions src/containers/AccountModal/forms/ChooseOffer.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import React, { useEffect } from 'react';
import { object, SchemaOf, mixed } from 'yup';
import { mixed, object, SchemaOf } from 'yup';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
import { useHistory } from 'react-router';
import shallow from 'zustand/shallow';

import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import ChooseOfferForm from '../../../components/ChooseOfferForm/ChooseOfferForm';
import { getOffer } from '../../../services/checkout.service';
import LoadingOverlay from '../../../components/LoadingOverlay/LoadingOverlay';
import { CheckoutStore } from '../../../stores/CheckoutStore';
import { addQueryParam, removeQueryParam } from '../../../utils/history';
import { ConfigStore } from '../../../stores/ConfigStore';

import { addQueryParam, removeQueryParam } from '#src/utils/history';
import { useConfigStore } from '#src/stores/ConfigStore';
import { useCheckoutStore } from '#src/stores/CheckoutStore';
import { getOffer } from '#src/services/checkout.service';
import LoadingOverlay from '#src/components/LoadingOverlay/LoadingOverlay';
import ChooseOfferForm from '#src/components/ChooseOfferForm/ChooseOfferForm';
import useForm, { UseFormOnSubmitHandler } from '#src/hooks/useForm';
import type { ChooseOfferFormData, OfferPeriodicity } from '#types/account';

const ChooseOffer = () => {
const history = useHistory();
const { t } = useTranslation('account');
const config = ConfigStore.useState((s) => s.config);
const { config, accessModel } = useConfigStore(({ config, accessModel }) => ({ config, accessModel }), shallow);
const { cleengSandbox, json } = config;
const accessModel = ConfigStore.useState((s) => s.accessModel);
const hasOffer = accessModel === 'SVOD';
const offer = CheckoutStore.useState((s) => s.offer);
const { offer, setOffer } = useCheckoutStore(({ offer, setOffer }) => ({ offer, setOffer }), shallow);

const cleengMonthlyOffer = json?.cleengMonthlyOffer as string;
const cleengYearlyOffer = json?.cleengYearlyOffer as string;
Expand All @@ -42,9 +41,7 @@ const ChooseOffer = () => {
return setErrors({ form: t('choose_offer.offer_not_found') });
}

CheckoutStore.update((s) => {
s.offer = offer;
});
setOffer(offer);

history.push(addQueryParam(history, 'u', 'checkout'));

Expand Down
12 changes: 6 additions & 6 deletions src/containers/AccountModal/forms/EditPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { useHistory } from 'react-router-dom';
import { object, string } from 'yup';
import { useTranslation } from 'react-i18next';

import { changePassword } from '../../../stores/AccountStore';
import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import type { EditPasswordFormData } from '../../../../types/account';
import EditPasswordForm from '../../../components/EditPasswordForm/EditPasswordForm';
import useQueryParam from '../../../hooks/useQueryParam';
import { addQueryParams } from '../../../utils/formatting';
import type { EditPasswordFormData } from '#types/account';
import EditPasswordForm from '#src/components/EditPasswordForm/EditPasswordForm';
import { changePassword } from '#src/stores/AccountController';
import useQueryParam from '#src/hooks/useQueryParam';
import useForm, { UseFormOnSubmitHandler } from '#src/hooks/useForm';
import { addQueryParams } from '#src/utils/formatting';

const ResetPassword: React.FC = () => {
const { t } = useTranslation('account');
Expand Down
8 changes: 4 additions & 4 deletions src/containers/AccountModal/forms/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import React from 'react';
import { object, string, SchemaOf } from 'yup';
import { object, SchemaOf, string } from 'yup';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router';

import LoginForm from '../../../components/LoginForm/LoginForm';
import { login } from '../../../stores/AccountStore';
import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import { removeQueryParam } from '../../../utils/history';
import { ConfigStore } from '../../../stores/ConfigStore';
import { useConfigStore } from '../../../stores/ConfigStore';

import type { LoginFormData } from '#types/account';
import { login } from '#src/stores/AccountController';

const Login = () => {
const { siteName } = ConfigStore.useState((s) => s.config);
const { siteName } = useConfigStore((s) => s.config);
const history = useHistory();
const { t } = useTranslation('account');
const loginSubmitHandler: UseFormOnSubmitHandler<LoginFormData> = async (formData, { setErrors, setSubmitting, setValue }) => {
Expand Down
6 changes: 3 additions & 3 deletions src/containers/AccountModal/forms/PersonalDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { useQuery } from 'react-query';
import PersonalDetailsForm from '../../../components/PersonalDetailsForm/PersonalDetailsForm';
import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import { addQueryParam } from '../../../utils/history';
import { getCaptureStatus, updateCaptureAnswers } from '../../../stores/AccountStore';
import LoadingOverlay from '../../../components/LoadingOverlay/LoadingOverlay';
import { ConfigStore } from '../../../stores/ConfigStore';
import { useConfigStore } from '../../../stores/ConfigStore';

import type { CaptureCustomAnswer, CleengCaptureQuestionField, PersonalDetailsFormData } from '#types/account';
import { getCaptureStatus, updateCaptureAnswers } from '#src/stores/AccountController';

const yupConditional = (required: boolean, message: string) => {
return required ? string().required(message) : mixed().notRequired();
Expand All @@ -20,7 +20,7 @@ const yupConditional = (required: boolean, message: string) => {
const PersonalDetails = () => {
const history = useHistory();
const { t } = useTranslation('account');
const accessModel = ConfigStore.useState((s) => s.accessModel);
const accessModel = useConfigStore((s) => s.accessModel);
const { data, isLoading } = useQuery('captureStatus', () => getCaptureStatus());
const [questionValues, setQuestionValues] = useState<Record<string, string>>({});
const [questionErrors, setQuestionErrors] = useState<Record<string, string>>({});
Expand Down
2 changes: 1 addition & 1 deletion src/containers/AccountModal/forms/Registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import RegistrationForm from '../../../components/RegistrationForm/RegistrationF
import useForm, { UseFormOnSubmitHandler } from '../../../hooks/useForm';
import { addQueryParam } from '../../../utils/history';
import { extractConsentValues, checkConsentsFromValues } from '../../../utils/collection';
import { getPublisherConsents, register, updateConsents } from '../../../stores/AccountStore';

import type { RegistrationFormData } from '#types/account';
import { getPublisherConsents, register, updateConsents } from '#src/stores/AccountController';

const Registration = () => {
const history = useHistory();
Expand Down
7 changes: 5 additions & 2 deletions src/containers/AccountModal/forms/RenewSubscription.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router';
import shallow from 'zustand/shallow';

import { removeQueryParam } from '../../../utils/history';
import LoadingOverlay from '../../../components/LoadingOverlay/LoadingOverlay';
import { AccountStore, updateSubscription } from '../../../stores/AccountStore';
import { useAccountStore } from '../../../stores/AccountStore';
import RenewSubscriptionForm from '../../../components/RenewSubscriptionForm/RenewSubscriptionForm';
import SubscriptionRenewed from '../../../components/SubscriptionRenewed/SubscriptionRenewed';

import { updateSubscription } from '#src/stores/AccountController';

const RenewSubscription = () => {
const { t } = useTranslation('account');
const history = useHistory();
const { subscription, user } = AccountStore.useState((s) => s);
const { subscription, user } = useAccountStore(({ subscription, user }) => ({ subscription, user }), shallow);
const [renewed, setRenewed] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
Expand Down
Loading