Skip to content

Commit

Permalink
refactor: move controllers and refactor form logic
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut authored and ChristiaanScheermeijer committed Feb 6, 2024
1 parent a2885eb commit dbecda4
Show file tree
Hide file tree
Showing 72 changed files with 818 additions and 607 deletions.
11 changes: 11 additions & 0 deletions packages/common/src/FormValidationError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type FormValidationErrors = Record<string, string[]>;

export class FormValidationError extends Error {
public errors: FormValidationErrors;

constructor(errors: FormValidationErrors) {
super(Object.values(errors)[0][0]);

this.errors = errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import type {
import { assertFeature, assertModuleMethod, getNamedModule } from '../modules/container';
import { INTEGRATION_TYPE } from '../modules/types';
import type { ServiceResponse } from '../../types/service';
import { useAccountStore } from '../stores/AccountStore';
import { useConfigStore } from '../stores/ConfigStore';
import { useProfileStore } from '../stores/ProfileStore';
import { FormValidationError } from '../FormValidationError';

import { useAccountStore } from './AccountStore';
import { useConfigStore } from './ConfigStore';
import { useProfileStore } from './ProfileStore';
import ProfileController from './ProfileController';
import WatchHistoryController from './WatchHistoryController';
import ProfileController from './ProfileController';
import FavoritesController from './FavoritesController';

@injectable()
Expand Down Expand Up @@ -167,13 +168,25 @@ export default class AccountController {
login = async (email: string, password: string, referrer: string) => {
useAccountStore.setState({ loading: true });

const response = await this.accountService.login({ email, password, referrer });
try {
const response = await this.accountService.login({ email, password, referrer });

if (!response) throw new Error("Couldn't login");

if (response) {
await this.afterLogin(response.user, response.customerConsents);
await this.favoritesController?.restoreFavorites().catch(logDev);
await this.watchHistoryController?.restoreWatchHistory().catch(logDev);
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message.toLowerCase().includes('invalid param email')) {
throw new FormValidationError({ email: [i18next.t('account:login.wrong_email')] });
} else {
throw new FormValidationError({ email: [i18next.t('account:login.wrong_combination')] });
}
}
}

useAccountStore.setState({ loading: false });
return useAccountStore.setState({ loading: false });
};

logout = async () => {
Expand All @@ -184,18 +197,30 @@ export default class AccountController {
await this.refreshEntitlements?.();
};

register = async (email: string, password: string, referrer: string, consents: CustomerConsent[]) => {
useAccountStore.setState({ loading: true });
const response = await this.accountService.register({ email, password, consents, referrer });
register = async (email: string, password: string, referrer: string, consentsValues: CustomerConsent[]) => {
try {
const response = await this.accountService.register({ email, password, consents: consentsValues, referrer });

if (response) {
const { user, customerConsents } = response;
await this.afterLogin(user, customerConsents);
if (response) {
const { user, customerConsents } = response;
await this.afterLogin(user, customerConsents);
}
} catch (error: unknown) {
if (error instanceof Error) {
const errorMessage = error.message.toLowerCase();

if (errorMessage.includes('customer already exists') || errorMessage.includes('account already exists')) {
throw new FormValidationError({ form: [i18next.t('account:registration.user_exists')] });
} else if (errorMessage.includes('invalid param password')) {
throw new FormValidationError({ password: [i18next.t('account:registration.invalid_password')] });
} else {
// in case the endpoint fails
throw new FormValidationError({ password: [i18next.t('account:registration.failed_to_create')] });
}
}
}

// this stores the locally stored favorites and watch history into the users account
await this.favoritesController.persistFavorites();
await this.watchHistoryController.persistWatchHistory();
return;
};

updateConsents = async (customerConsents: CustomerConsent[]): Promise<ServiceResponse<CustomerConsent[]>> => {
Expand Down Expand Up @@ -244,11 +269,10 @@ export default class AccountController {
getPublisherConsents = async () => {
const { config } = useConfigStore.getState();

useAccountStore.setState({ loading: true });
const consents = await this.accountService.getPublisherConsents(config);

useAccountStore.setState({ publisherConsents: consents });

return consents;
useAccountStore.setState({ publisherConsents: consents, loading: false });
};

getCaptureStatus = async (): Promise<GetCaptureStatusResponse> => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import StorageService from '../services/StorageService';
import type { Config } from '../../types/config';
import type { CalculateIntegrationType } from '../../types/calculate-integration-type';
import { DETERMINE_INTEGRATION_TYPE } from '../modules/types';
import { useConfigStore } from '../stores/ConfigStore';

import AccountController from './AccountController';
import WatchHistoryController from './WatchHistoryController';
import FavoritesController from './FavoritesController';
import { useConfigStore } from './ConfigStore';
import AccountController from './AccountController';

@injectable()
export default class AppController {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { inject, injectable } from 'inversify';
import i18next from 'i18next';

import type {
AddAdyenPaymentDetailsResponse,
Expand All @@ -12,8 +13,8 @@ import type {
InitialAdyenPayment,
Offer,
Order,
Payment,
PaymentMethod,
PaymentWithPayPalResponse,
SwitchOffer,
UpdateOrderPayload,
} from '../../types/checkout';
Expand All @@ -24,9 +25,9 @@ import { assertModuleMethod, getNamedModule } from '../modules/container';
import { GET_CUSTOMER_IP, INTEGRATION_TYPE } from '../modules/types';
import type { GetCustomerIP } from '../../types/get-customer-ip';
import AccountService from '../services/integrations/AccountService';

import { useCheckoutStore } from './CheckoutStore';
import { useAccountStore } from './AccountStore';
import { useCheckoutStore } from '../stores/CheckoutStore';
import { useAccountStore } from '../stores/AccountStore';
import { FormValidationError } from '../FormValidationError';

@injectable()
export default class CheckoutController {
Expand All @@ -46,10 +47,13 @@ export default class CheckoutController {
return this.accountService.svodOfferIds;
};

createOrder = async (offer: Offer, paymentMethodId?: number): Promise<void> => {
createOrder = async (offer: Offer): Promise<void> => {
const { getAccountInfo } = useAccountStore.getState();
const { customer } = getAccountInfo();

const paymentMethods = await this.getPaymentMethods();
const paymentMethodId = paymentMethods[0]?.id;

const createOrderArgs: CreateOrderArgs = {
offer,
customerId: customer.id,
Expand All @@ -75,19 +79,30 @@ export default class CheckoutController {
couponCode,
};

const response = await this.checkoutService.updateOrder(updateOrderPayload);
if (response.errors.length > 0) {
// clear the order when the order doesn't exist on the server
if (response.errors[0].includes(`Order with ${order.id} not found`)) {
useCheckoutStore.getState().setOrder(null);
try {
const response = await this.checkoutService.updateOrder(updateOrderPayload);

if (response.errors.length > 0) {
// clear the order when the order doesn't exist on the server
if (response.errors[0].includes(`Order with ${order.id} not found`)) {
useCheckoutStore.getState().setOrder(null);
}

throw new FormValidationError({ order: [response.errors[0]] });
}

throw new Error(response.errors[0]);
}
if (response.responseData?.order) {
useCheckoutStore.getState().setOrder(response.responseData?.order);
}
} catch (error: unknown) {
if (error instanceof FormValidationError) {
throw error;
}

if (response.responseData?.order) {
useCheckoutStore.getState().setOrder(response.responseData?.order);
throw new FormValidationError({ couponCode: [i18next.t('account:checkout.coupon_not_valid')] });
}

return;
};

getPaymentMethods = async (): Promise<PaymentMethod[]> => {
Expand All @@ -104,7 +119,8 @@ export default class CheckoutController {
return response.responseData?.paymentMethods;
};

paymentWithoutDetails = async (): Promise<unknown> => {
//
paymentWithoutDetails = async (): Promise<Payment> => {
const { order } = useCheckoutStore.getState();

if (!order) throw new Error('No order created');
Expand All @@ -117,7 +133,7 @@ export default class CheckoutController {
return response.responseData;
};

directPostCardPayment = async (cardPaymentPayload: CardPaymentData, referrer: string, returnUrl: string): Promise<unknown> => {
directPostCardPayment = async ({ cardPaymentPayload, referrer, returnUrl }: { cardPaymentPayload: CardPaymentData; referrer: string; returnUrl: string }) => {
const { order } = useCheckoutStore.getState();

if (!order) throw new Error('No order created');
Expand All @@ -138,7 +154,9 @@ export default class CheckoutController {
returnUrl: returnUrl,
});

if (response.errors.length > 0) throw new Error(response.errors[0]);
if (response.errors.length > 0) {
throw new Error(response.errors[0]);
}

return response.responseData;
};
Expand Down Expand Up @@ -173,18 +191,26 @@ export default class CheckoutController {
paymentData,
});

if (response.errors.length > 0) throw new Error(response.errors[0]);
if (response.errors.length > 0) {
throw new Error(response.errors[0]);
}

return response.responseData;
};

paypalPayment = async (
successUrl: string,
waitingUrl: string,
cancelUrl: string,
errorUrl: string,
couponCode: string = '',
): Promise<PaymentWithPayPalResponse> => {
paypalPayment = async ({
successUrl,
waitingUrl,
cancelUrl,
errorUrl,
couponCode = '',
}: {
successUrl: string;
waitingUrl: string;
cancelUrl: string;
errorUrl: string;
couponCode: string;
}): Promise<string> => {
const { order } = useCheckoutStore.getState();

if (!order) throw new Error('No order created');
Expand All @@ -200,7 +226,7 @@ export default class CheckoutController {

if (response.errors.length > 0) throw new Error(response.errors[0]);

return response.responseData;
return response.responseData.redirectUrl;
};

getSubscriptionSwitches = async (): Promise<unknown> => {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { injectable } from 'inversify';

import FavoriteService from '../services/FavoriteService';
import type { PlaylistItem } from '../../types/playlist';

import { useAccountStore } from './AccountStore';
import { useFavoritesStore } from './FavoritesStore';
import { useConfigStore } from './ConfigStore';
import { useAccountStore } from '../stores/AccountStore';
import { useFavoritesStore } from '../stores/FavoritesStore';
import { useConfigStore } from '../stores/ConfigStore';

@injectable()
export default class FavoritesController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { assertModuleMethod, getNamedModule } from '../modules/container';
import StorageService from '../services/StorageService';
import { INTEGRATION_TYPE } from '../modules/types';
import type { EnterProfilePayload, ProfileDetailsPayload, ProfilePayload } from '../../types/profiles';

import { useProfileStore } from './ProfileStore';
import { useProfileStore } from '../stores/ProfileStore';

const PERSIST_PROFILE = 'profile';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { injectable } from 'inversify';
import WatchHistoryService from '../services/WatchHistoryService';
import type { PlaylistItem } from '../../types/playlist';
import type { WatchHistoryItem } from '../../types/watchHistory';

import { useAccountStore } from './AccountStore';
import { useConfigStore } from './ConfigStore';
import { useWatchHistoryStore } from './WatchHistoryStore';
import { useAccountStore } from '../stores/AccountStore';
import { useConfigStore } from '../stores/ConfigStore';
import { useWatchHistoryStore } from '../stores/WatchHistoryStore';

@injectable()
export default class WatchHistoryController {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { interfaces } from 'inversify';

import AppController from '../../stores/AppController';
import AppController from '../../controllers/AppController';

/**
* This function is used to get the integration type from the AppController and is mainly used for getting named
Expand Down
14 changes: 7 additions & 7 deletions packages/common/src/modules/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import FavoriteService from '../services/FavoriteService';
import ConfigService from '../services/ConfigService';
import SettingsService from '../services/SettingsService';

import WatchHistoryController from '../stores/WatchHistoryController';
import CheckoutController from '../stores/CheckoutController';
import AccountController from '../stores/AccountController';
import ProfileController from '../stores/ProfileController';
import FavoritesController from '../stores/FavoritesController';
import AppController from '../stores/AppController';
import EpgController from '../stores/EpgController';
import WatchHistoryController from '../controllers/WatchHistoryController';
import CheckoutController from '../controllers/CheckoutController';
import AccountController from '../controllers/AccountController';
import ProfileController from '../controllers/ProfileController';
import FavoritesController from '../controllers/FavoritesController';
import AppController from '../controllers/AppController';
import EpgController from '../controllers/EpgController';

// Epg services
import EpgService from '../services/EpgService';
Expand Down
6 changes: 0 additions & 6 deletions packages/common/types/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,6 @@ export type EditPasswordFormData = {
resetPasswordToken?: string;
};

export type OfferType = 'svod' | 'tvod';

export type ChooseOfferFormData = {
offerId?: string;
};

export type GetUserArgs = {
config: Config;
};
Expand Down
7 changes: 7 additions & 0 deletions packages/common/types/checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export type Offer = {
planSwitchEnabled?: boolean;
};

export type OfferType = 'svod' | 'tvod';

export type ChooseOfferFormData = {
offerId?: string;
};

export type OrderOffer = {
title: string;
description: string | null;
Expand Down Expand Up @@ -90,6 +96,7 @@ export type PaymentMethod = {
id: number;
methodName: 'card' | 'paypal';
provider?: 'stripe' | 'adyen';
paymentGateway?: 'adyen' | 'paypal'; // @todo: merge with provider
logoUrl: string;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/hooks-react/src/useBootstrapApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useQuery, useQueryClient } from 'react-query';
import type { Config } from '@jwp/ott-common/types/config';
import type { Settings } from '@jwp/ott-common/types/settings';
import { getModule } from '@jwp/ott-common/src/modules/container';
import AppController from '@jwp/ott-common/src/stores/AppController';
import AppController from '@jwp/ott-common/src/controllers/AppController';
import type { AppError } from '@jwp/ott-common/src/utils/error';
import { CACHE_TIME, STALE_TIME } from '@jwp/ott-common/src/constants';

Expand Down
Loading

0 comments on commit dbecda4

Please sign in to comment.