Skip to content

Commit

Permalink
[NO CHANGELOG][Checkout Widget] Feat: Checkout widget refactor iframe…
Browse files Browse the repository at this point in the history
… params (#2168)
  • Loading branch information
jhesgodi authored Sep 12, 2024
1 parent cc98041 commit 29b0caa
Show file tree
Hide file tree
Showing 8 changed files with 481 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CheckoutContextProvider } from './context/CheckoutContextProvider';
import { CheckoutAppIframe } from './views/CheckoutAppIframe';
import { getIframeURL } from './functions/iframeParams';
import { useMount } from './hooks/useMount';
import { useAsyncMemo } from './hooks/useAsyncMemo';

export type CheckoutWidgetInputs = {
checkout: Checkout;
Expand All @@ -28,10 +29,10 @@ export default function CheckoutWidget(props: CheckoutWidgetInputs) {
config, checkout, params, provider,
} = props;

const [, iframeURL] = useMemo(() => {
if (!checkout.config.publishableKey) return ['', ''];
return getIframeURL(params, config, checkout.config);
}, [params, config, checkout.config]);
const iframeURL = useAsyncMemo(
async () => getIframeURL(params, config, checkout.config),
[params, config, checkout.config],
);

const [checkoutState, checkoutDispatch] = useReducer(
checkoutReducer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-case-declarations */
import {
CheckoutConfiguration,
CheckoutFlowType,
Expand All @@ -6,6 +7,8 @@ import {
} from '@imtbl/checkout-sdk';

import { Environment } from '@imtbl/config';

import { encodeObject } from '../utils/encode';
import { CHECKOUT_APP_URL, ENV_DEVELOPMENT } from '../../../lib/constants';

/**
Expand All @@ -29,11 +32,11 @@ const toQueryString = (params: Record<string, unknown>): string => {
/**
* Maps the flow configuration and params to the corresponding query parameters.
*/
const getIframeParams = (
const getIframeParams = async (
params: CheckoutWidgetParams,
widgetConfig: CheckoutWidgetConfiguration,
checkoutConfig: CheckoutConfiguration,
): string => {
): Promise<string> => {
const { flow } = params;
const commonConfig = {
theme: widgetConfig.theme,
Expand Down Expand Up @@ -108,6 +111,8 @@ const getIframeParams = (
toAmount: params.tokenAddress,
});
case CheckoutFlowType.SALE:
const items = await encodeObject(params.items || []);

return toQueryString({
...commonConfig,
...(widgetConfig.sale || {}),
Expand All @@ -123,7 +128,7 @@ const getIframeParams = (
environmentId: params.environmentId,
collectionName: params.collectionName,

items: params.items,
items,
preferredCurrency: params.preferredCurrency,
excludePaymentTypes: params.excludePaymentTypes,
excludeFiatCurrencies: params.excludeFiatCurrencies,
Expand All @@ -136,28 +141,34 @@ const getIframeParams = (
/**
* Returns the iframe URL for the Checkout App based on the environment.
*/
export const getIframeURL = (
export const getIframeURL = async (
params: CheckoutWidgetParams,
widgetConfig: CheckoutWidgetConfiguration,
checkoutConfig: CheckoutConfiguration,
) => {
): Promise<string> => {
const { flow } = params;
const { publishableKey } = checkoutConfig;

const language = params.language || widgetConfig.language;
if (!publishableKey) return '';

let environment: Environment = checkoutConfig.environment || Environment.SANDBOX;
if (checkoutConfig.isDevelopment) {
environment = ENV_DEVELOPMENT;
}

if (checkoutConfig.overrides?.environment) {
environment = checkoutConfig.overrides.environment;
}

const baseURL = checkoutConfig.overrides?.checkoutAppUrl as string ?? CHECKOUT_APP_URL[environment];
const queryParams = getIframeParams(params, widgetConfig, checkoutConfig);
const baseURL = (checkoutConfig.overrides?.checkoutAppUrl as string)
?? CHECKOUT_APP_URL[environment];
const queryParams = await getIframeParams(
params,
widgetConfig,
checkoutConfig,
);

const iframeURL = `${baseURL}/${publishableKey}/${language}/${flow}?${queryParams}`;
const iframeURL = `${baseURL}/${flow}?${queryParams}`;

return [baseURL, iframeURL] as const;
return iframeURL;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useState } from 'react';

/**
* Handle asynchronous operations with memoization.
* It only re-executes the async function when dependencies change.
*/
export const useAsyncMemo = <T>(
asyncFn: () => Promise<T>,
dependencies: any[],
): T => {
const [value, setValue] = useState<T>();

useEffect(() => {
let isMounted = true;

asyncFn().then((result) => {
if (isMounted) setValue(result);
});

return () => {
isMounted = false;
};
}, dependencies);

return value as T;
};
26 changes: 26 additions & 0 deletions packages/checkout/widgets-lib/src/widgets/checkout/utils/encode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Encodes a JSON object using base64 encoding.
*/
export const encodeObject = async (value: Object): Promise<string> => {
try {
const str = JSON.stringify(value);
const base64String = btoa(str);
return encodeURIComponent(base64String);
} catch (error) {
throw new Error(`Compression failed: ${(error as Error).message}`);
}
};

/**
* Decodes a string encoded using encodeObject.
*/
export const decodeObject = async (
encodedValue: string,
): Promise<Object> => {
try {
const decodedString = atob(decodeURIComponent(encodedValue));
return JSON.parse(decodedString);
} catch (error) {
throw new Error(`Decompression failed: ${(error as Error).message}`);
}
};
Loading

0 comments on commit 29b0caa

Please sign in to comment.