Skip to content

Commit

Permalink
feat(entitlement): add tvod entitlement check to movie screen
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed May 18, 2022
1 parent 9337f45 commit 5c5e388
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 57 deletions.
1 change: 1 addition & 0 deletions .commitlintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module.exports = {
'menu',
'payment',
'e2e',
'entitlement',
],
],
},
Expand Down
50 changes: 50 additions & 0 deletions src/hooks/useEntitlement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useQueries } from 'react-query';
import { useMemo } from 'react';

import type { GetEntitlementsResponse } from '../../types/checkout';

import { ConfigStore } from '#src/stores/ConfigStore';
import { AccountStore } from '#src/stores/AccountStore';
import { getEntitlements } from '#src/services/checkout.service';

export type UseEntitlementResult = {
isEntitled: boolean;
isLoading: boolean;
error: unknown | null;
};

export type UseEntitlement = (offerIds?: string[], enabled?: boolean) => UseEntitlementResult;

type QueryResult = {
responseData?: GetEntitlementsResponse;
};

const useEntitlement: UseEntitlement = (offerIds = [], enabled = true) => {
const sandbox = ConfigStore.useState(({ config }) => config?.cleengSandbox);
const jwt = AccountStore.useState(({ auth }) => auth?.jwt);

const entitlementQueries = useQueries(
offerIds.map((offerId) => ({
queryKey: ['mediaOffer', offerId],
queryFn: () => getEntitlements({ offerId: offerId || '' }, sandbox, jwt || ''),
enabled: enabled && !!jwt && !!offerId,
})),
);

const evaluateEntitlement = (queryResult: QueryResult) => !!queryResult?.responseData?.accessGranted;

return useMemo(
() =>
entitlementQueries.reduce<UseEntitlementResult>(
(prev, cur) => ({
isLoading: prev.isLoading || cur.isLoading,
error: prev.error || cur.error,
isEntitled: prev.isEntitled || (cur.isSuccess && evaluateEntitlement(cur as QueryResult)),
}),
{ isEntitled: false, isLoading: false, error: null },
),
[entitlementQueries],
);
};

export default useEntitlement;
47 changes: 26 additions & 21 deletions src/screens/Movie/Movie.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,29 @@ import { useHistory } from 'react-router';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';

import { useFavorites } from '../../stores/FavoritesStore';
import useBlurImageUpdater from '../../hooks/useBlurImageUpdater';
import { cardUrl, movieURL, videoUrl } from '../../utils/formatting';
import type { PlaylistItem } from '../../../types/playlist';
import VideoComponent from '../../components/Video/Video';
import ErrorPage from '../../components/ErrorPage/ErrorPage';
import CardGrid from '../../components/CardGrid/CardGrid';
import useMedia from '../../hooks/useMedia';
import { generateMovieJSONLD } from '../../utils/structuredData';
import { copyToClipboard } from '../../utils/dom';
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import useRecommendedPlaylist from '../../hooks/useRecommendationsPlaylist';
import { watchHistoryStore } from '../../stores/WatchHistoryStore';
import { VideoProgressMinMax } from '../../config';
import { ConfigStore } from '../../stores/ConfigStore';
import { AccountStore } from '../../stores/AccountStore';
import { addQueryParam } from '../../utils/history';
import { isAllowedToWatch } from '../../utils/cleeng';
import { addConfigParamToUrl } from '../../utils/configOverride';

import styles from './Movie.module.scss';

import { useFavorites } from '#src/stores/FavoritesStore';
import useBlurImageUpdater from '#src/hooks/useBlurImageUpdater';
import { cardUrl, movieURL, videoUrl } from '#src/utils/formatting';
import type { PlaylistItem } from '#src/../types/playlist';
import VideoComponent from '#src/components/Video/Video';
import ErrorPage from '#src/components/ErrorPage/ErrorPage';
import CardGrid from '#src/components/CardGrid/CardGrid';
import useMedia from '#src/hooks/useMedia';
import { generateMovieJSONLD } from '#src/utils/structuredData';
import { copyToClipboard } from '#src/utils/dom';
import LoadingOverlay from '#src/components/LoadingOverlay/LoadingOverlay';
import useRecommendedPlaylist from '#src/hooks/useRecommendationsPlaylist';
import { watchHistoryStore } from '#src/stores/WatchHistoryStore';
import { VideoProgressMinMax } from '#src/config';
import { ConfigStore } from '#src/stores/ConfigStore';
import { AccountStore } from '#src/stores/AccountStore';
import { addQueryParam } from '#src/utils/history';
import { filterCleengMediaOffers, isAllowedToWatch } from '#src/utils/cleeng';
import { addConfigParamToUrl } from '#src/utils/configOverride';
import useEntitlement from '#src/hooks/useEntitlement';

type MovieRouteParams = {
id: string;
};
Expand All @@ -48,11 +49,15 @@ const Movie = ({ match, location }: RouteComponentProps<MovieRouteParams>): JSX.

// Media
const { isLoading, error, data: item } = useMedia(id);
const itemRequiresSubscription = item?.requiresSubscription !== 'false';
const itemRequiresSubscription = item?.requiresSubscription !== 'false'; // || item.free ?

This comment has been minimized.

Copy link
@marcovandeveen

marcovandeveen Jun 22, 2022

Member

|| item.free awesome @royschut! You picked up this comment! And it also works with the entitlement service

useBlurImageUpdater(item);
const { data: trailerItem } = useMedia(item?.trailerId || '');
const { data: playlist } = useRecommendedPlaylist(recommendationsPlaylist || '', item);

// AccessModel & entitlement
const mediaOffer = useMemo(() => (item?.productIds && filterCleengMediaOffers(item.productIds)) || undefined, [item]);
useEntitlement(mediaOffer);

const { hasItem, saveItem, removeItem } = useFavorites();

const watchHistory = watchHistoryStore.useState((s) => s.watchHistory);
Expand Down
19 changes: 16 additions & 3 deletions src/services/checkout.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import type { CreateOrder, GetOffer, GetPaymentMethods, PaymentWithAdyen, PaymentWithoutDetails, PaymentWithPayPal, UpdateOrder } from '../../types/checkout';
import { getOverrideIP, IS_DEV_BUILD } from '../utils/common';

import { get, post, patch } from './cleeng.service';

import type {
CreateOrder,
GetEntitlements,
GetOffer,
GetPaymentMethods,
PaymentWithAdyen,
PaymentWithoutDetails,
PaymentWithPayPal,
UpdateOrder,
} from '#types/checkout';
import { getOverrideIP, IS_DEV_BUILD } from '#src/utils/common';

export const getOffer: GetOffer = async (payload, sandbox) => {
// @ts-ignore
return get(sandbox, `/offers/${payload.offerId}${IS_DEV_BUILD && getOverrideIP() ? '?customerIP=' + getOverrideIP() : ''}`);
Expand Down Expand Up @@ -35,3 +44,7 @@ export const paymentWithAdyen: PaymentWithAdyen = async (payload, sandbox, jwt)
export const paymentWithPayPal: PaymentWithPayPal = async (payload, sandbox, jwt) => {
return post(sandbox, '/connectors/paypal/v1/tokens', JSON.stringify(payload), jwt);
};

export const getEntitlements: GetEntitlements = async (payload, sandbox, jwt = '') => {
return get(sandbox, `/entitlements/${payload.offerId}`, jwt);
};
13 changes: 7 additions & 6 deletions src/utils/cleeng.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { AccessModel } from '../../types/Config';

export const isAllowedToWatch = (
accessModel: AccessModel,
isLoggedIn: boolean,
itemRequiresSubscription: boolean,
hasSubscription: boolean,
): boolean =>
export const isAllowedToWatch = (accessModel: AccessModel, isLoggedIn: boolean, itemRequiresSubscription: boolean, hasSubscription: boolean): boolean =>
accessModel === 'AVOD' ||
(accessModel === 'AUTHVOD' && (isLoggedIn || !itemRequiresSubscription)) ||
(accessModel === 'SVOD' && (hasSubscription || !itemRequiresSubscription));

export const filterCleengMediaOffers = (productIds: string): string[] => {
return productIds
.split(',')
.reduce<string[]>((ids, productId) => (productId?.indexOf('cleeng:') === 0 ? [...ids, productId.replace('cleeng:', '')] : ids), []);
};
64 changes: 37 additions & 27 deletions types/checkout.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@ export type Offer = {
videoId: string | null;
contentExternalId: string | null;
contentExternalData: string | null;
contentAgeRestriction: string | null
}
contentAgeRestriction: string | null;
};

export type OrderOffer = {
title: string;
description: string | null;
price: number;
currency: string;
}
};

export type Order = {
id: number;
customerId: string;
customer: {
locale: string;
email: string
email: string;
};
publisherId: number;
offerId: string;
Expand All @@ -60,7 +60,7 @@ export type Order = {
discountedPrice: number;
taxValue: number;
customerServiceFee: number;
paymentMethodFee: number
paymentMethodFee: number;
};
taxRate: number;
taxBreakdown: string | null;
Expand All @@ -73,9 +73,9 @@ export type Order = {
discount: {
applied: boolean;
type: string;
periods: number
periods: number;
};
requiredPaymentDetails: boolean
requiredPaymentDetails: boolean;
};

export type PaymentMethod = {
Expand All @@ -91,26 +91,26 @@ export type PaymentMethodResponse = {
};

export type Payment = {
id: number,
orderId: number,
status: string,
totalAmount: number,
currency: string,
customerId: string,
paymentGateway: string,
paymentMethod: string,
externalPaymentId: string|number,
couponId: number | null,
amount: number,
country: string,
offerType: "subscription",
taxValue: number,
paymentMethodFee: number,
customerServiceFee: number,
rejectedReason: string | null,
refundedReason: string | null,
paymentDetailsId: number | null,
paymentOperation: string
id: number;
orderId: number;
status: string;
totalAmount: number;
currency: string;
customerId: string;
paymentGateway: string;
paymentMethod: string;
externalPaymentId: string | number;
couponId: number | null;
amount: number;
country: string;
offerType: 'subscription';
taxValue: number;
paymentMethodFee: number;
customerServiceFee: number;
rejectedReason: string | null;
refundedReason: string | null;
paymentDetailsId: number | null;
paymentOperation: string;
};

export type GetOfferPayload = {
Expand Down Expand Up @@ -165,10 +165,20 @@ export type PaymentWithPayPalResponse = {
redirectUrl: string;
};

export type GetEntitlementsPayload = {
offerId: string;
};

export type GetEntitlementsResponse = {
accessGranted: boolean;
expiresAt: number;
};

export type GetOffer = CleengRequest<GetOfferPayload, Offer>;
export type CreateOrder = CleengAuthRequest<CreateOrderPayload, CreateOrderResponse>;
export type UpdateOrder = CleengAuthRequest<UpdateOrderPayload, UpdateOrderResponse>;
export type GetPaymentMethods = CleengEmptyAuthRequest<PaymentMethodResponse>;
export type PaymentWithoutDetails = CleengAuthRequest<PaymentWithoutDetailsPayload, Payment>;
export type PaymentWithAdyen = CleengAuthRequest<PaymentWithAdyenPayload, Payment>;
export type PaymentWithPayPal = CleengAuthRequest<PaymentWithPayPalPayload, PaymentWithPayPalResponse>;
export type GetEntitlements = CleengAuthRequest<GetEntitlementsPayload, GetEntitlementsResponse>;
1 change: 1 addition & 0 deletions types/playlist.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type PlaylistItem = {
title: string;
tracks: Track[];
variations?: Record<string, unknown>;
productIds?: string;
};

export type Link = {
Expand Down

0 comments on commit 5c5e388

Please sign in to comment.