Skip to content

Commit

Permalink
feat: [NO CHANGELOG][Checkout Widget] Forward configuration to checko…
Browse files Browse the repository at this point in the history
…ut app thru iframe query params (#2013)
  • Loading branch information
jiyounglee authored Jul 25, 2024
1 parent c987a75 commit a7f2d8c
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable max-len */
import { WidgetLanguage } from '../configurations';
import { WidgetLanguage, WidgetTheme } from '../configurations';
import { ConnectWidgetParams } from './connect';
import { BridgeWidgetParams } from './bridge';
import { WalletWidgetParams } from './wallet';
Expand Down Expand Up @@ -51,4 +51,5 @@ export type CheckoutWidgetFlowParams =
export type CheckoutWidgetParams = {
/** The language to use for the checkout widget */
language?: WidgetLanguage;
theme?: WidgetTheme;
} & CheckoutWidgetFlowParams;
58 changes: 43 additions & 15 deletions packages/checkout/widgets-lib/src/widgets/BaseWidgetRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ import {
} from '@imtbl/checkout-sdk';
import { Web3Provider } from '@ethersproject/providers';
import i18next from 'i18next';
import { StrongCheckoutWidgetsConfig, withDefaultWidgetConfigs } from '../lib/withDefaultWidgetConfig';
import { addProviderListenersForWidgetRoot, baseWidgetProviderEvent } from '../lib';
import {
StrongCheckoutWidgetsConfig,
withDefaultWidgetConfigs,
} from '../lib/withDefaultWidgetConfig';
import {
addProviderListenersForWidgetRoot,
baseWidgetProviderEvent,
} from '../lib';

export abstract class Base<T extends WidgetType> implements Widget<T> {
protected checkout: Checkout;
Expand All @@ -32,15 +38,18 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {

protected web3Provider: Web3Provider | undefined;

protected eventHandlers: Map<keyof WidgetEventData[T], Function> = new Map<keyof WidgetEventData[T], Function>();
protected eventHandlers: Map<keyof WidgetEventData[T], Function> = new Map<
keyof WidgetEventData[T],
Function
>();

protected eventHandlersFunction?: (event: any) => void;

protected eventTopic: string = '';

constructor(sdk: Checkout, props: WidgetProperties<T>) {
const validatedProps = this.getValidatedProperties(props);
this.parameters = { language: 'en' } as WidgetParameters[T];
this.parameters = {} as WidgetParameters[T];

this.checkout = sdk;
this.properties = validatedProps;
Expand All @@ -64,9 +73,7 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {
unmount() {
// We want to keep the properties (config and provider) across mounts
// Clear the parameters on unmount as we don't want to keep them across mounts
this.parameters = this.getValidatedParameters(
{ language: 'en' } as WidgetParameters[T],
);
this.parameters = this.getValidatedParameters({} as WidgetParameters[T]);

this.reactRoot?.unmount();
document.getElementById(this.targetId as string)?.replaceChildren();
Expand Down Expand Up @@ -112,7 +119,9 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {

if (props.provider) {
// eslint-disable-next-line no-console
console.warn('Updating a widget provider through the update() method is not supported yet');
console.warn(
'Updating a widget provider through the update() method is not supported yet',
);
}

const language = props.config?.language;
Expand All @@ -126,12 +135,18 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {
}

// eslint-disable-next-line max-len
addListener<KEventName extends keyof WidgetEventData[T]>(type: KEventName, callback: (data: WidgetEventData[T][KEventName]) => void): void {
addListener<KEventName extends keyof WidgetEventData[T]>(
type: KEventName,
callback: (data: WidgetEventData[T][KEventName]) => void,
): void {
this.eventHandlers.set(type, callback);

if (this.eventHandlersFunction) {
window.removeEventListener(this.eventTopic, this.eventHandlersFunction);
window.removeEventListener(IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER, this.eventHandlersFunction);
window.removeEventListener(
IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER,
this.eventHandlersFunction,
);
}

this.eventHandlersFunction = (event: any) => {
Expand All @@ -140,15 +155,23 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {
};

window.addEventListener(this.eventTopic, this.eventHandlersFunction);
window.addEventListener(IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER, this.eventHandlersFunction);
window.addEventListener(
IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER,
this.eventHandlersFunction,
);
}

removeListener<KEventName extends keyof WidgetEventData[T]>(type: KEventName): void {
removeListener<KEventName extends keyof WidgetEventData[T]>(
type: KEventName,
): void {
this.eventHandlers.delete(type);

if (this.eventHandlersFunction) {
window.removeEventListener(this.eventTopic, this.eventHandlersFunction);
window.removeEventListener(IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER, this.eventHandlersFunction);
window.removeEventListener(
IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER,
this.eventHandlersFunction,
);
}

if (this.eventHandlers.size <= 0) return;
Expand All @@ -159,7 +182,10 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {
};

window.addEventListener(this.eventTopic, this.eventHandlersFunction);
window.addEventListener(IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER, this.eventHandlersFunction);
window.addEventListener(
IMTBLWidgetEvents.IMTBL_WIDGETS_PROVIDER,
this.eventHandlersFunction,
);
}

protected strongConfig(): StrongCheckoutWidgetsConfig {
Expand Down Expand Up @@ -201,7 +227,9 @@ export abstract class Base<T extends WidgetType> implements Widget<T> {
private handleEIP1193ProviderEvents(widgetRoot: Base<T>) {
if (widgetRoot.web3Provider) {
// eslint-disable-next-line no-param-reassign
widgetRoot.web3Provider = new Web3Provider(widgetRoot.web3Provider!.provider);
widgetRoot.web3Provider = new Web3Provider(
widgetRoot.web3Provider!.provider,
);
}
widgetRoot.render();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import { useEffect, useState } from 'react';
import { Box } from '@biom3/react';
import { CheckoutWidgetParams, Checkout } from '@imtbl/checkout-sdk';
import { CheckoutWidgetParams, Checkout, CheckoutWidgetConfiguration } from '@imtbl/checkout-sdk';

import { StrongCheckoutWidgetsConfig } from '../../lib/withDefaultWidgetConfig';
import { getIframeURL } from './functions/iframeParams';

export type CheckoutWidgetInputs = {
checkout: Checkout;
config: StrongCheckoutWidgetsConfig;
params: CheckoutWidgetParams;
config: CheckoutWidgetConfiguration;
};

export default function CheckoutWidget(props: CheckoutWidgetInputs) {
const { config, checkout, params } = props;
const { environment } = config;
const { publishableKey } = checkout.config;
const { environment, publishableKey } = checkout.config;

const [iframeURL, setIframeURL] = useState<string>();

useEffect(() => {
if (!publishableKey || !params.language) return;

const url = getIframeURL(params, environment, publishableKey);
if (!publishableKey) return;

const url = getIframeURL(params, config, environment, publishableKey);
setIframeURL(url);
}, [publishableKey, params]);
}, [params, config, environment, publishableKey]);

// TODO:
// on iframe load error, go to error view 500
Expand All @@ -36,7 +33,7 @@ export default function CheckoutWidget(props: CheckoutWidgetInputs) {

return (
<Box
rc={<iframe src={iframeURL} title="checkout" />}
rc={<iframe id="checkout-app" src={iframeURL} title="checkout" />}
sx={{
w: '100%',
h: '100%',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class CheckoutWidgetRoot extends Base<WidgetType.CHECKOUT> {
protected render() {
if (!this.reactRoot) return;
const { t } = i18n;
const config = this.strongConfig();
const config = this.properties.config || {};

this.reactRoot.render(
<CustomAnalyticsProvider checkout={this.checkout}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { CheckoutFlowType, CheckoutWidgetParams } from '@imtbl/checkout-sdk';
import {
CheckoutFlowType,
CheckoutWidgetConfiguration,
CheckoutWidgetParams,
} from '@imtbl/checkout-sdk';
import { Environment } from '@imtbl/config';

import { CHECKOUT_APP_URL } from '../../../lib/constants';
Expand All @@ -12,18 +16,28 @@ const toQueryString = (params: Record<string, unknown>): string => {
};

// TODO: Can be removed after updating params across widgets
const getIframeParams = (params: CheckoutWidgetParams): string => {
const { flow, language, ...restParams } = params;
const getIframeParams = (
params: CheckoutWidgetParams,
config: CheckoutWidgetConfiguration,
): string => {
const { flow } = params;
const commonConfig = {
theme: config.theme,
};

switch (flow) {
case CheckoutFlowType.CONNECT:
return toQueryString({
...commonConfig,
...(config.connect || {}),
chainId: params.targetChainId,
walletRdns: params.targetWalletRdns,
blocklistWalletRdns: params.blocklistWalletRdns,
});
case CheckoutFlowType.WALLET:
return toQueryString({
...commonConfig,
...(config.wallet || {}),
// FIMXE: Add connection params
// chainId:
// walletRdns:
Expand All @@ -34,6 +48,8 @@ const getIframeParams = (params: CheckoutWidgetParams): string => {
});
case CheckoutFlowType.SWAP:
return toQueryString({
...commonConfig,
...(config.swap || {}),
// FIMXE: Add connection params
// chainId:
// walletRdns:
Expand All @@ -47,6 +63,8 @@ const getIframeParams = (params: CheckoutWidgetParams): string => {
});
case CheckoutFlowType.BRIDGE:
return toQueryString({
...commonConfig,
...(config.bridge || {}),
// FIMXE: Add bridge params
// fromChainId:
// toChainId:
Expand All @@ -60,6 +78,8 @@ const getIframeParams = (params: CheckoutWidgetParams): string => {
});
case CheckoutFlowType.ONRAMP:
return toQueryString({
...commonConfig,
...(config.onRamp || {}),
// FIMXE: Add connection params
// chainId:
// walletRdns:
Expand All @@ -72,6 +92,8 @@ const getIframeParams = (params: CheckoutWidgetParams): string => {
});
case CheckoutFlowType.SALE:
return toQueryString({
...commonConfig,
...(config.sale || {}),
// FIMXE: Add connection params
// chainId:
// walletRdns:
Expand All @@ -90,19 +112,22 @@ const getIframeParams = (params: CheckoutWidgetParams): string => {
excludeFiatCurrencies: params.excludeFiatCurrencies,
});
default:
return toQueryString(restParams);
return '';
}
};

export const getIframeURL = (
params: CheckoutWidgetParams,
config: CheckoutWidgetConfiguration,
environment: Environment,
publishableKey: string,
): string => {
const { language, flow } = params;
const { flow } = params;

const language = params.language || config.language;

const baseUrl = CHECKOUT_APP_URL[environment];
const queryParams = getIframeParams(params);
const queryParams = getIframeParams(params, config);

return `${baseUrl}/${publishableKey}/${language}/${flow}?${queryParams}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ function CheckoutUI() {
const checkoutWidget = useMemo(
() =>
factory.create(WidgetType.CHECKOUT, {
config: { wallet: { showNetworkMenu: false } },
config: {
theme: WidgetTheme.LIGHT,
wallet: { showNetworkMenu: false },
sale: {
hideExcludedPaymentTypes: true,
},
},
}),
[checkout]
);
Expand Down Expand Up @@ -134,11 +140,12 @@ function CheckoutUI() {
<button onClick={() => update(WidgetTheme.LIGHT)}>Light theme</button>
<button onClick={() => update(WidgetTheme.DARK)}>Dark theme</button>
<select
onChange={(e) =>
onChange={(e) => {
console.log("change language");
checkoutWidget.update({
config: { language: e.target.value as WidgetLanguage },
})
}
});
}}
>
<option value="en">EN</option>
<option value="ja">JA</option>
Expand Down

0 comments on commit a7f2d8c

Please sign in to comment.