From 4c4c89113cc87473218720da344ebe653eda00e4 Mon Sep 17 00:00:00 2001 From: Jhonatan Gonzalez Date: Thu, 5 Sep 2024 13:28:31 +1000 Subject: [PATCH] [NO CHANGELOG][Checkout Widget] Notify iframe if widget was initialised with a provider (#2147) --- .../src/widgets/checkout/CheckoutWidget.tsx | 30 ++++++++++++++----- .../src/widgets/checkout/hooks/useMount.ts | 23 ++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 packages/checkout/widgets-lib/src/widgets/checkout/hooks/useMount.ts diff --git a/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx index 26cea44900..259636f78f 100644 --- a/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx +++ b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx @@ -3,6 +3,7 @@ import { Checkout, CheckoutWidgetConfiguration, CheckoutWidgetParams, + PostMessageHandlerEventType, } from '@imtbl/checkout-sdk'; import { Web3Provider } from '@ethersproject/providers'; import { @@ -13,12 +14,13 @@ import { import { CheckoutContextProvider } from './context/CheckoutContextProvider'; import { CheckoutAppIframe } from './views/CheckoutAppIframe'; import { getIframeURL } from './functions/iframeParams'; +import { useMount } from './hooks/useMount'; export type CheckoutWidgetInputs = { checkout: Checkout; params: CheckoutWidgetParams; config: CheckoutWidgetConfiguration; - provider?: Web3Provider + provider?: Web3Provider; }; export default function CheckoutWidget(props: CheckoutWidgetInputs) { @@ -37,20 +39,32 @@ export default function CheckoutWidget(props: CheckoutWidgetInputs) { ); const checkoutReducerValues = useMemo( () => ({ - checkoutState: { - ...checkoutState, iframeURL, checkout, - }, + checkoutState: { ...checkoutState, iframeURL, checkout }, checkoutDispatch, }), [checkoutState, checkoutDispatch, iframeURL, checkout], ); + // If the widget was initialized with a provider, + // notify iframe via postMessage + const { postMessageHandler } = checkoutState; + useMount( + () => { + if (!provider) return; + + postMessageHandler?.send(PostMessageHandlerEventType.PROVIDER_UPDATED, { + isMetamask: provider.provider.isMetaMask, + isPassport: (provider.provider as any)?.isPassport, + }); + }, + [postMessageHandler, provider] as const, + ([_postMessageHandler]) => _postMessageHandler !== undefined, + ); + + // keep the provider updated in the state useEffect(() => { checkoutDispatch({ - payload: { - type: CheckoutActions.SET_PROVIDER, - provider, - }, + payload: { type: CheckoutActions.SET_PROVIDER, provider }, }); }, [provider]); diff --git a/packages/checkout/widgets-lib/src/widgets/checkout/hooks/useMount.ts b/packages/checkout/widgets-lib/src/widgets/checkout/hooks/useMount.ts new file mode 100644 index 0000000000..4dc73ec2f0 --- /dev/null +++ b/packages/checkout/widgets-lib/src/widgets/checkout/hooks/useMount.ts @@ -0,0 +1,23 @@ +import { useEffect, useRef } from 'react'; + +/** + * This hook is used to run an effect only once when after the component is mounted. + * and all dependencies are either by default not undefined or the compare function returns true. + */ +export const useMount = ( + effect: () => void, + deps: D, + compare?: (deps: D) => boolean, +) => { + const hasRunRef = useRef(false); + + useEffect(() => { + const allDepsSatisfy = compare + ? compare(deps) + : deps.every((dep) => dep !== undefined); + if (!hasRunRef.current && allDepsSatisfy) { + hasRunRef.current = true; + effect(); + } + }, [deps, effect, compare]); +};