Skip to content

Commit

Permalink
Add Direct NFT Purchase flow to Commerce widget
Browse files Browse the repository at this point in the history
  • Loading branch information
luads committed Jan 14, 2025
1 parent dbb0606 commit e89cb0b
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SwapWidgetParams } from './swap';
import { OnRampWidgetParams } from './onramp';
import { SaleWidgetParams } from './sale';
import { AddTokensWidgetParams } from './addTokens';
import { PurchaseWidgetParams } from './purchase';

export enum CommerceFlowType {
CONNECT = 'CONNECT',
Expand All @@ -16,6 +17,7 @@ export enum CommerceFlowType {
BRIDGE = 'BRIDGE',
ONRAMP = 'ONRAMP',
ADD_TOKENS = 'ADD_TOKENS',
PURCHASE = 'PURCHASE',
}

export type CommerceWidgetConnectFlowParams = {
Expand Down Expand Up @@ -46,14 +48,19 @@ export type CommerceWidgetAddTokensFlowParams = {
flow: CommerceFlowType.ADD_TOKENS;
} & AddTokensWidgetParams;

export type CommerceWidgetPurchaseFlowParams = {
flow: CommerceFlowType.PURCHASE;
} & PurchaseWidgetParams;

export type CommerceWidgetFlowParams =
| CommerceWidgetConnectFlowParams
| CommerceWidgetWalletFlowParams
| CommerceWidgetSwapFlowParams
| CommerceWidgetBridgeFlowParams
| CommerceWidgetOnRampFlowParams
| CommerceWidgetSaleFlowParams
| CommerceWidgetAddTokensFlowParams;
| CommerceWidgetAddTokensFlowParams
| CommerceWidgetPurchaseFlowParams;

export type CommerceWidgetParams = {
/** The language to use for the Commerce Widget */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
ConnectWidgetConfiguration,
WalletWidgetParams,
WalletWidgetConfiguration,
PurchaseWidgetConfiguration,
PurchaseWidgetParams,
} from '@imtbl/checkout-sdk';
import { ViewType } from './ViewType';

Expand All @@ -24,7 +26,8 @@ export type CheckoutWidgetView =
| SaleView
| SwapView
| OnRampView
| BrdigeView;
| BridgeView
| PurchaseView;

interface ConnectView extends ViewType {
type: CommerceFlowType.CONNECT;
Expand All @@ -50,6 +53,14 @@ interface AddTokensView extends ViewType {
};
}

interface PurchaseView extends ViewType {
type: CommerceFlowType.PURCHASE;
data: {
params: PurchaseWidgetParams;
config: PurchaseWidgetConfiguration;
};
}

interface SaleView extends ViewType {
type: CommerceFlowType.SALE;
data: {
Expand All @@ -74,7 +85,7 @@ interface OnRampView extends ViewType {
};
}

interface BrdigeView extends ViewType {
interface BridgeView extends ViewType {
type: CommerceFlowType.BRIDGE;
data: {
params: BridgeWidgetParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
commerceReducer,
initialCommerceState,
} from './context/CommerceContext';
import PurchaseWidget from '../purchase/PurchaseWidget';

export type CommerceWidgetInputs = {
checkout: Checkout;
Expand Down Expand Up @@ -82,7 +83,10 @@ export default function CommerceWidget(props: CommerceWidgetInputs) {
[view, checkout, provider, web3Provider],
);

const connectLoaderSuccessEvent = flowParams.flow === CommerceFlowType.ADD_TOKENS ? () => {} : undefined;
const connectLoaderSuccessEvent = (
flowParams.flow === CommerceFlowType.ADD_TOKENS
|| flowParams.flow === CommerceFlowType.PURCHASE
) ? () => {} : undefined;

const goToPreviousView = useCallback(() => {
const sharedViews = [
Expand Down Expand Up @@ -255,6 +259,16 @@ export default function CommerceWidget(props: CommerceWidgetInputs) {
/>
</ProvidersContextProvider>
)}
{shouldWrapWithProvidersContext && view.type === CommerceFlowType.PURCHASE && (
<ProvidersContextProvider initialState={{ checkout }}>
<PurchaseWidget
config={widgetsConfig}
{...(view.data.params || {})}
{...(view.data.config || {})}
showBackButton={showBackButton}
/>
</ProvidersContextProvider>
)}
{/* --- Widgets that require connect loader --- */}
{shouldWrapWithConnectLoader && (
<ConnectLoader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CommerceWidgetOnRampFlowParams,
CommerceWidgetSaleFlowParams,
CommerceFlowType,
CommerceWidgetPurchaseFlowParams,
} from '@imtbl/checkout-sdk';
import React, { Suspense } from 'react';
import { ThemeProvider } from '../../components/ThemeProvider/ThemeProvider';
Expand Down Expand Up @@ -148,6 +149,27 @@ export class CommerceWidgetRoot extends Base<WidgetType.IMMUTABLE_COMMERCE> {
return validatedParams;
}

protected getValidPurchaseFlowParams(params: CommerceWidgetPurchaseFlowParams) {
const validatedParams = { ...params };

if (!Array.isArray(validatedParams.items)) {
// eslint-disable-next-line no-console
console.warn('[IMTBL]: invalid "items" widget input.');
validatedParams.items = [];
}

if (!params.environmentId) {
// eslint-disable-next-line no-console
console.warn('[IMTBL]: invalid "environmentId" widget input');
validatedParams.environmentId = '';
}

return {
...validatedParams,
items: deduplicateSaleItemsArray(params.items),
};
}

protected getValidSwapFlowParams(params: CommerceWidgetSwapFlowParams) {
const validatedParams = { ...params };

Expand Down Expand Up @@ -238,6 +260,8 @@ export class CommerceWidgetRoot extends Base<WidgetType.IMMUTABLE_COMMERCE> {
return this.getValidOnRampFlowParams(params);
case CommerceFlowType.ADD_TOKENS:
return this.getValidAddTokensFlowParams(params);
case CommerceFlowType.PURCHASE:
return this.getValidPurchaseFlowParams(params);
default:
// eslint-disable-next-line no-console
console.warn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IMTBLWidgetEvents,
OnRampEventType,
ProviderEventType,
PurchaseEventType,
SaleEventType,
SwapEventType,
WalletEventType,
Expand Down Expand Up @@ -315,6 +316,38 @@ function mapSaleWidgetEvent(
}
}

function mapPurchaseWidgetEvent(
event: CustomEvent<{ type: PurchaseEventType; data: Record<string, unknown> }>,
): CommerceEventDetail {
const { type, data } = event.detail;

switch (type) {
case PurchaseEventType.SUCCESS:
return {
type: CommerceEventType.SUCCESS,
data: {
type: CommerceSuccessEventType.SALE_SUCCESS,
data,
},
};
case PurchaseEventType.FAILURE:
return {
type: CommerceEventType.FAILURE,
data: {
type: CommerceFailureEventType.SALE_FAILED,
data,
},
};
case PurchaseEventType.CLOSE_WIDGET:
return {
type: CommerceEventType.CLOSE,
data: {},
};
default:
throw new Error(`Unknown purchase event type "${event.detail.type}"`);
}
}

/**
* Map widget events to commerce widget event detail
*/
Expand Down Expand Up @@ -343,6 +376,8 @@ export function getCommerceWidgetEvent(
return mapOnrampWidgetEvent(event);
case IMTBLWidgetEvents.IMTBL_SALE_WIDGET_EVENT:
return mapSaleWidgetEvent(event);
case IMTBLWidgetEvents.IMTBL_PURCHASE_WIDGET_EVENT:
return mapPurchaseWidgetEvent(event);
default:
throw new Error(`Unknown widget event type "${event.type}"`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const connectLoaderFlows = [
*/
const providersContextFlows = [
CommerceFlowType.ADD_TOKENS,
CommerceFlowType.PURCHASE,
] as unknown[];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export function getViewFromOrchestrationEventType(
return CommerceFlowType.ONRAMP;
case OrchestrationEventType.REQUEST_ADD_TOKENS:
return CommerceFlowType.ADD_TOKENS;
case OrchestrationEventType.REQUEST_PURCHASE:
return CommerceFlowType.PURCHASE;
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const orchestrationEvents = [
OrchestrationEventType.REQUEST_BRIDGE,
OrchestrationEventType.REQUEST_ONRAMP,
OrchestrationEventType.REQUEST_ADD_TOKENS,
OrchestrationEventType.REQUEST_PURCHASE,
OrchestrationEventType.REQUEST_GO_BACK,
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const commerceFlows = [
CommerceFlowType.BRIDGE,
CommerceFlowType.ONRAMP,
CommerceFlowType.ADD_TOKENS,
CommerceFlowType.PURCHASE,
];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const widgetEvents = [
IMTBLWidgetEvents.IMTBL_ONRAMP_WIDGET_EVENT,
IMTBLWidgetEvents.IMTBL_SALE_WIDGET_EVENT,
IMTBLWidgetEvents.IMTBL_ADD_TOKENS_WIDGET_EVENT,
IMTBLWidgetEvents.IMTBL_PURCHASE_WIDGET_EVENT,
];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const flows: Array<CommerceFlowType> = [
CommerceFlowType.BRIDGE,
CommerceFlowType.SALE,
CommerceFlowType.ADD_TOKENS,
CommerceFlowType.PURCHASE,
];

function CheckoutUI() {
Expand Down Expand Up @@ -206,6 +207,11 @@ function CheckoutUI() {
toAmount: "1",
toTokenAddress: "native",
},
PURCHASE: {
flow: CommerceFlowType.PURCHASE,
items: itemsMock.slice(0, 1),
environmentId: "82a81049-8c41-4ae3-91ca-0bd82a283abc",
},
});

// set a state to keep widget event results
Expand Down Expand Up @@ -474,6 +480,26 @@ function CheckoutUI() {
>
<MenuItem.Label>Primary Sale</MenuItem.Label>
</MenuItem>
<MenuItem
onClick={() => {
setParams({
flow: CommerceFlowType.PURCHASE,
items: [
{
productId: "kangaroo",
qty: 1,
name: "Kangaroo",
image:
"https://iguanas.mystagingwebsite.com/wp-content/uploads/2024/05/character-image-10-1.png",
description: "Pixel Art Kangaroo",
},
],
environmentId: "82a81049-8c41-4ae3-91ca-0bd82a283abc",
});
}}
>
<MenuItem.Label>Direct NFT Purchase</MenuItem.Label>
</MenuItem>
</AppHeaderBar.OverflowPopoverMenu>
<AppHeaderBar.RightSlot gap="base.spacing.x4">
<Box
Expand Down

0 comments on commit e89cb0b

Please sign in to comment.