Skip to content

Commit

Permalink
feat: improved methods before merge with the other parts
Browse files Browse the repository at this point in the history
  • Loading branch information
darkoatanasovski committed Dec 20, 2022
1 parent e15bc15 commit 3c750c1
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 28 deletions.
1 change: 1 addition & 0 deletions src/components/CreditCardCVCField/CreditCardCVCField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const CreditCardCVCField: React.FC<Props> = ({ value, onChange, error, ...props
type="text"
value={value}
onChange={formatCVC}
pattern="\d*"
placeholder="cvc/cvv"
required
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ exports[`<CreditCardCVCField> > renders and matches snapshot 1`] = `
class="_input_e16c1b"
id="text-field_1235_cardCVC"
name="cardCVC"
pattern="\\\\d*"
placeholder="cvc/cvv"
required=""
type="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const CreditCardExpiryField: React.FC<Props> = ({ value, onChange, error, ...pro
helperText={error ? error : null}
onChange={formatExpirationDate}
type="text"
pattern="\d*"
placeholder="dd/mm"
required
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ exports[`<CreditCardExpiryField> > renders and matches snapshot 1`] = `
class="_input_e16c1b"
id="text-field_1235_cardExpiry"
name="cardExpiry"
pattern="\\\\d*"
placeholder="dd/mm"
required=""
type="text"
Expand Down
3 changes: 2 additions & 1 deletion src/components/Root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { cleanupQueryParams, getConfigSource } from '#src/utils/configOverride';
import { loadAndValidateConfig } from '#src/utils/configLoad';
import { initSettings } from '#src/stores/SettingsController';
import AppRoutes from '#src/containers/AppRoutes/AppRoutes';
import useNotifications from '#src/hooks/useNotifications';

const Root: FC = () => {
const { t } = useTranslation('error');

useNotifications();
const settingsQuery = useQuery('settings-init', initSettings, {
enabled: true,
retry: 1,
Expand Down
22 changes: 3 additions & 19 deletions src/containers/AccountModal/forms/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ import { useCheckoutStore } from '#src/stores/CheckoutStore';
import { adyenPayment, cardPayment, createOrder, getPaymentMethods, paymentWithoutDetails, paypalPayment, updateOrder } from '#src/stores/CheckoutController';
import { reloadActiveSubscription } from '#src/stores/AccountController';
import PaymentForm from '#src/components/PaymentForm/PaymentForm';
import { useNotificationStore } from '#src/stores/NotificationStore';
import useCheckAccess from '#src/hooks/useCheckAccess';

const Checkout = () => {
const location = useLocation();
const { cleengSandbox } = useConfigStore((state) => state.getCleengData());
const notification = useNotificationStore((state) => state);
const { t } = useTranslation('account');
const navigate = useNavigate();
const [paymentError, setPaymentError] = useState<string | undefined>(undefined);
const [updatingOrder, setUpdatingOrder] = useState(false);
const [couponFormOpen, setCouponFormOpen] = useState(false);
const [couponCodeApplied, setCouponCodeApplied] = useState(false);
const [paymentMethodId, setPaymentMethodId] = useState<number | undefined>(undefined);
const { intervalCheckAccess } = useCheckAccess();

const { order, offer, paymentMethods, setOrder } = useCheckoutStore(
({ order, offer, paymentMethods, setOrder }) => ({
Expand All @@ -53,6 +53,7 @@ const Checkout = () => {
async () => {
setUpdatingOrder(true);
await cardPayment(paymentDataForm.values);
intervalCheckAccess({ interval: 5000 });
},
object().shape({
cardNumber: string().test('card number validation', t('checkout.invalid_card_number'), (value) => {
Expand Down Expand Up @@ -90,23 +91,6 @@ const Checkout = () => {
paymentDataForm.setSubmitting(false);
};

useEffect(() => {
if (notification.type?.endsWith('.failed')) {
navigate(
addQueryParams(window.location.href, {
u: 'paypal-error',
message: (notification.resource as Error)?.message,
}),
);
} else if (notification.type === 'access.granted') {
navigate(addQueryParam(location, 'u', 'welcome'));
}

return () => {
useNotificationStore.setState({ type: null, resource: null });
};
}, [notification, navigate, location]);

useEffect(() => {
if (paymentDataForm.values.cardExpiry) {
const expiry = Payment.fns.cardExpiryVal(paymentDataForm.values.cardExpiry);
Expand Down
42 changes: 42 additions & 0 deletions src/hooks/useCheckAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useRef } from 'react';
import { useLocation, useNavigate } from 'react-router';

import { addQueryParam } from '#src/utils/location';
import { checkEntitlements, reloadActiveSubscription } from '#src/stores/AccountController';

type intervalCheckAccessPayload = {
interval?: number;
iterations?: number;
offerId?: string;
};
const useCheckAccess = () => {
const intervalRef = useRef<number>();
const navigate = useNavigate();
const location = useLocation();

const intervalCheckAccess = ({ interval = 3000, iterations = 5, offerId }: intervalCheckAccessPayload) => {
intervalRef.current = window.setInterval(async () => {
if (!offerId) {
offerId = '115047';
}
const hasAccess = await checkEntitlements(offerId);

if (hasAccess) {
await reloadActiveSubscription();
navigate(addQueryParam(location, 'u', 'welcome'));
} else if (--iterations === 0) {
window.clearInterval(intervalRef.current);
}
}, interval);
};

useEffect(() => {
return () => {
window.clearInterval(intervalRef.current);
};
}, []);

return { intervalCheckAccess };
};

export default useCheckAccess;
31 changes: 31 additions & 0 deletions src/hooks/useNotifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router';

import { useNotificationStore } from '#src/stores/NotificationStore';
import { addQueryParams } from '#src/utils/formatting';
import { addQueryParam } from '#src/utils/location';

const useNotifications = () => {
const navigate = useNavigate();
const location = useLocation();
const notification = useNotificationStore((state) => state);

useEffect(() => {
if (notification.type?.endsWith('.failed')) {
navigate(
addQueryParams(window.location.href, {
u: 'paypal-error',
message: (notification.resource as Error)?.message,
}),
);
} else if (notification.type === 'access.granted') {
navigate(addQueryParam(location, 'u', 'welcome'));
}

//eslint-disable-next-line
}, [notification]);

return notification;
};

export default useNotifications;
1 change: 0 additions & 1 deletion src/hooks/useOffers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const useOffers = () => {
accessModel,
} = useConfigStore(({ getCleengData, accessModel }) => ({ cleeng: getCleengData(), accessModel }), shallow);
const { checkoutService, integration } = useClientIntegration();

const { requestedMediaOffers } = useCheckoutStore(({ requestedMediaOffers }) => ({ requestedMediaOffers }), shallow);
const hasPremierOffer = (requestedMediaOffers || []).some((offer) => offer.premier);
const [offerType, setOfferType] = useState<OfferType>(accessModel === 'SVOD' ? 'svod' : 'tvod');
Expand Down
16 changes: 16 additions & 0 deletions src/services/inplayer.checkout.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
CardPaymentData,
CreateOrder,
CreateOrderPayload,
GetEntitlements,
GetOffers,
GetPaymentMethods,
Offer,
Expand Down Expand Up @@ -173,6 +174,21 @@ export const cardPayment = async (cardPaymentPayload: CardPaymentData, order: Or
}
};

export const getEntitlements: GetEntitlements = async ({ offerId }) => {
try {
const response = await InPlayer.Asset.checkAccessForAsset(parseInt(offerId));
return {
errors: [],
responseData: {
accessGranted: true,
expiresAt: response.data.expires_at,
},
};
} catch {
throw new Error('no access for this resource');
}
};

const processOffer = (offer: GetAccessFee): Offer => {
return {
id: offer.id,
Expand Down
25 changes: 18 additions & 7 deletions src/stores/AccountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ let refreshTimeout: number;

// actions needed when listening to InPlayer web socket notifications
const notifications: Record<NotificationsTypes, () => Promise<unknown>> = {
[NotificationsTypes.ACCESS_GRANTED]: reloadActiveSubscription,
[NotificationsTypes.ACCESS_GRANTED]: async () => {
reloadActiveSubscription({ delay: 2000 });
},
[NotificationsTypes.ACCESS_REVOKED]: reloadActiveSubscription,
[NotificationsTypes.SUBSCRIBE_SUCCESS]: reloadActiveSubscription,
[NotificationsTypes.SUBSCRIBE_SUCCESS]: async () => true,
[NotificationsTypes.SUBSCRIBE_FAILED]: async () => true,
[NotificationsTypes.PAYMENT_CARD_SUCCESS]: reloadActiveSubscription,
[NotificationsTypes.PAYMENT_CARD_FAILED]: async () => true,
Expand Down Expand Up @@ -396,15 +398,19 @@ export const updateSubscription = async (status: 'active' | 'cancelled') => {
});
};

export async function checkEntitlements(offerId: string): Promise<unknown> {
return await useAccount(async ({ auth: { jwt } }) => {
return await useService(async ({ checkoutService, sandbox }) => {
const response = await checkoutService.getEntitlements({ offerId }, sandbox, jwt);
return !!response;
});
});
}

export async function reloadActiveSubscription({ delay }: { delay: number } = { delay: 0 }): Promise<unknown> {
useAccountStore.setState({ loading: true });
return await useAccount(async ({ customerId, auth: { jwt } }) => {
return await useService(async ({ subscriptionService, sandbox, config }) => {
const [activeSubscription, transactions, activePayment] = await Promise.all([
subscriptionService.getActiveSubscription({ sandbox, customerId, jwt, config }),
subscriptionService.getAllTransactions({ sandbox, customerId, jwt }),
subscriptionService.getActivePayment({ sandbox, customerId, jwt }),
]);
// The subscription data takes a few seconds to load after it's purchased,
// so here's a delay mechanism to give it time to process
if (delay > 0) {
Expand All @@ -415,6 +421,11 @@ export async function reloadActiveSubscription({ delay }: { delay: number } = {
});
}

const [activeSubscription, transactions, activePayment] = await Promise.all([
subscriptionService.getActiveSubscription({ sandbox, customerId, jwt, config }),
subscriptionService.getAllTransactions({ sandbox, customerId, jwt }),
subscriptionService.getActivePayment({ sandbox, customerId, jwt }),
]);
// this invalidates all entitlements caches which makes the useEntitlement hook to verify the entitlements.
await queryClient.invalidateQueries('entitlements');

Expand Down

0 comments on commit 3c750c1

Please sign in to comment.