Skip to content

Commit

Permalink
feat: Add skeleton for Purchase widget (#2398)
Browse files Browse the repository at this point in the history
  • Loading branch information
luads authored Nov 28, 2024
1 parent 33ba130 commit 307be70
Show file tree
Hide file tree
Showing 23 changed files with 585 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './theme';
export * from './widget';
export * from './addTokens';
export * from './commerce';
export * from './purchase';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { WidgetConfiguration } from './widget';

export type PurchaseWidgetConfiguration = {
} & WidgetConfiguration;
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './orchestration';
export * from './onramp';
export * from './commerce';
export * from './addTokens';
export * from './purchase';
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum OrchestrationEventType {
REQUEST_BRIDGE = 'request-bridge',
REQUEST_ONRAMP = 'request-onramp',
REQUEST_ADD_TOKENS = 'request-add-tokens',
REQUEST_PURCHASE = 'request-purchase',
REQUEST_GO_BACK = 'request-go-back',
}

Expand Down
35 changes: 35 additions & 0 deletions packages/checkout/sdk/src/widgets/definitions/events/purchase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Web3Provider } from '@ethersproject/providers';
import { EIP6963ProviderInfo } from '../../../types';

export enum PurchaseEventType {
CLOSE_WIDGET = 'close-widget',
CONNECT_SUCCESS = 'connect-success',
SUCCESS = 'success',
FAILURE = 'failure',
}

/**
* Represents a successful purchase.
*/
export type PurchaseSuccess = {};

/**
* Type representing a purchase failure
* @property {string} reason
*/
export type PurchaseFailed = {
reason: string;
timestamp: number;
};

/**
* Type representing a successful provider connection
* @property {Web3Provider} provider
* @property {EIP6963ProviderInfo} providerInfo
* @property {'from' | 'to'} providerType
*/
export type PurchaseConnectSuccess = {
provider: Web3Provider;
providerInfo: EIP6963ProviderInfo;
providerType: 'from' | 'to';
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum IMTBLWidgetEvents {
IMTBL_SALE_WIDGET_EVENT = 'imtbl-sale-widget',
IMTBL_COMMERCE_WIDGET_EVENT = 'imtbl-commerce-widget',
IMTBL_ADD_TOKENS_WIDGET_EVENT = 'imtbl-add-tokens-widget',
IMTBL_PURCHASE_WIDGET_EVENT = 'imtbl-purchase-widget',
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './swap';
export * from './onramp';
export * from './sale';
export * from './commerce';
export * from './purchase';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { WidgetLanguage } from '../configurations';

export type PurchaseWidgetParams = {
/** The language to use for the Purchase widget */
language?: WidgetLanguage;

/** Environment id from Immutable Hub */
environmentId?: string;

/** Whether to show a back button on the first screen, on click triggers REQUEST_GO_BACK event */
showBackButton?: boolean;

/** The list of products to be purchased */
items?: PurchaseItem[];
};

export type PurchaseItem = {
productId: string;
qty: number;
name: string;
image: string;
description: string;
};
26 changes: 22 additions & 4 deletions packages/checkout/sdk/src/widgets/definitions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ import {
AddTokensConnectSuccess,
AddTokensSuccess,
AddTokensFailed,
PurchaseConnectSuccess,
PurchaseEventType,
PurchaseFailed,
PurchaseSuccess,
} from './events';
import {
BridgeWidgetParams,
Expand All @@ -54,8 +58,9 @@ import {
OnRampWidgetParams,
CommerceWidgetParams,
AddTokensWidgetParams,
SaleWidgetParams,
PurchaseWidgetParams,
} from './parameters';
import { SaleWidgetParams } from './parameters/sale';
import {
BridgeWidgetConfiguration,
ConnectWidgetConfiguration,
Expand All @@ -65,8 +70,9 @@ import {
WalletWidgetConfiguration,
CommerceWidgetConfiguration,
AddTokensWidgetConfiguration,
WidgetTheme,
PurchaseWidgetConfiguration,
} from './configurations';
import { WidgetTheme } from './configurations/theme';

/**
* Enum representing the list of widget types.
Expand All @@ -80,6 +86,7 @@ export enum WidgetType {
SALE = 'sale',
IMMUTABLE_COMMERCE = 'immutableCommerce',
ADD_TOKENS = 'addTokens',
PURCHASE = 'purchase',
}

/**
Expand All @@ -98,6 +105,7 @@ export type WidgetConfigurations = {
[WidgetType.ONRAMP]: OnrampWidgetConfiguration;
[WidgetType.SALE]: SaleWidgetConfiguration;
[WidgetType.ADD_TOKENS]: AddTokensWidgetConfiguration;
[WidgetType.PURCHASE]: PurchaseWidgetConfiguration;
[WidgetType.IMMUTABLE_COMMERCE]: CommerceWidgetConfiguration;
};

Expand All @@ -110,6 +118,7 @@ export type WidgetParameters = {
[WidgetType.ONRAMP]: OnRampWidgetParams;
[WidgetType.SALE]: SaleWidgetParams;
[WidgetType.ADD_TOKENS]: AddTokensWidgetParams;
[WidgetType.PURCHASE]: PurchaseWidgetParams;
[WidgetType.IMMUTABLE_COMMERCE]: CommerceWidgetParams;
};

Expand All @@ -125,6 +134,7 @@ export type WidgetEventTypes = {
[WidgetType.SALE]: SaleEventType | OrchestrationEventType;
[WidgetType.IMMUTABLE_COMMERCE]: CommerceEventType | OrchestrationEventType;
[WidgetType.ADD_TOKENS]: AddTokensEventType | OrchestrationEventType;
[WidgetType.PURCHASE]: PurchaseEventType | OrchestrationEventType;
};

// Mapping of Orchestration events to their payloads
Expand All @@ -143,9 +153,9 @@ type ProviderEventMapping = {
};

/**
* Mapping of widget type, to each of it's events and then each event's payload
* Mapping of widget type, to each of its events and then each event's payload
* Update this whenever a new event is created and used by a widget
* Each widget also has all of the orchestration events
* Each widget also has all the orchestration events
*/
export type WidgetEventData = {
[WidgetType.CONNECT]: {
Expand Down Expand Up @@ -218,6 +228,14 @@ export type WidgetEventData = {
[AddTokensEventType.FAILURE]: AddTokensFailed;
} & OrchestrationMapping &
ProviderEventMapping;

[WidgetType.PURCHASE]: {
[PurchaseEventType.CLOSE_WIDGET]: {};
[PurchaseEventType.CONNECT_SUCCESS]: PurchaseConnectSuccess;
[PurchaseEventType.SUCCESS]: PurchaseSuccess;
[PurchaseEventType.FAILURE]: PurchaseFailed;
} & OrchestrationMapping &
ProviderEventMapping;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum UserJourney {
BRIDGE = 'Bridge',
SALE = 'PrimarySale',
ADD_TOKENS = 'AddTokens',
PURCHASE = 'Purchase',
}

export type AnalyticsControlTypes =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ViewType } from './ViewType';

export enum PurchaseWidgetViews {
PURCHASE = 'PURCHASE',
REVIEW = 'REVIEW',
GEO_BLOCK_ERROR = 'GEO_BLOCK_ERROR',
}

export type PurchaseWidgetView = PurchaseView | PurchaseReview | GeoBlockErrorView;

interface PurchaseView extends ViewType {
type: PurchaseWidgetViews.PURCHASE;
}

interface PurchaseReview extends ViewType {
type: PurchaseWidgetViews.REVIEW;
data: PurchaseReviewData;
}

interface GeoBlockErrorView extends ViewType {
type: PurchaseWidgetViews.GEO_BLOCK_ERROR;
}

export interface PurchaseReviewData {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ViewType } from './ViewType';
import { OnRampWidgetView } from './OnRampViewContextTypes';
import { AddTokensWidgetView } from './AddTokensViewContextTypes';
import { CheckoutWidgetView } from './CheckoutWidgetViewContextTypes';
import { PurchaseWidgetView } from './PurchaseViewContextTypes';

export enum SharedViews {
LOADING_VIEW = 'LOADING_VIEW',
Expand Down Expand Up @@ -54,7 +55,8 @@ export type View =
| SaleWidgetView
| BridgeWidgetView
| AddTokensWidgetView
| CheckoutWidgetView;
| CheckoutWidgetView
| PurchaseWidgetView;

export interface ViewState {
view: View;
Expand Down
7 changes: 7 additions & 0 deletions packages/checkout/widgets-lib/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { OnRamp } from './widgets/on-ramp/OnRampWidgetRoot';
import { Sale } from './widgets/sale/SaleWidgetRoot';
import { Swap } from './widgets/swap/SwapWidgetRoot';
import { Wallet } from './widgets/wallet/WalletWidgetRoot';
import { Purchase } from './widgets/purchase/PurchaseWidgetRoot';

export class WidgetsFactory implements IWidgetsFactory {
private sdk: Checkout;
Expand Down Expand Up @@ -107,6 +108,12 @@ export class WidgetsFactory implements IWidgetsFactory {
provider,
}) as Widget<WidgetType.ADD_TOKENS> as Widget<T>;
}
case WidgetType.PURCHASE: {
return new Purchase(this.sdk, {
config: { ...this.widgetConfig, ...(config) },
provider,
}) as Widget<WidgetType.PURCHASE> as Widget<T>;
}
default:
throw new Error('widget type not supported');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { IMTBLWidgetEvents, PurchaseWidgetParams } from '@imtbl/checkout-sdk';
import { CloudImage, Stack, useTheme } from '@biom3/react';
import { useContext, useMemo, useReducer } from 'react';
import { StrongCheckoutWidgetsConfig } from '../../lib/withDefaultWidgetConfig';
import { initialPurchaseState, PurchaseContext, purchaseReducer } from './context/PurchaseContext';
import { PurchaseWidgetViews } from '../../context/view-context/PurchaseViewContextTypes';
import {
initialViewState,
ViewContext,
viewReducer,
} from '../../context/view-context/ViewContext';
import { Purchase } from './views/Purchase';
import { sendPurchaseCloseEvent } from './PurchaseWidgetEvents';
import { orchestrationEvents } from '../../lib/orchestrationEvents';
import { EventTargetContext } from '../../context/event-target-context/EventTargetContext';
import { getRemoteImage } from '../../lib/utils';

export type PurchaseWidgetInputs = PurchaseWidgetParams & {
config: StrongCheckoutWidgetsConfig;
};

export default function PurchaseWidget({
config,
showBackButton,
}: PurchaseWidgetInputs) {
const { base: { colorMode } } = useTheme();

const [viewState, viewDispatch] = useReducer(viewReducer, {
...initialViewState,
view: { type: PurchaseWidgetViews.PURCHASE },
history: [{ type: PurchaseWidgetViews.PURCHASE }],
});

const viewReducerValues = useMemo(
() => ({
viewState,
viewDispatch,
}),
[viewState, viewReducer],
);

const [purchaseState, purchaseDispatch] = useReducer(
purchaseReducer,
initialPurchaseState,
);

const purchaseReducerValues = useMemo(
() => ({
purchaseState,
purchaseDispatch,
}),
[purchaseState, purchaseReducer],
);

const {
eventTargetState: { eventTarget },
} = useContext(EventTargetContext);

return (
<ViewContext.Provider value={viewReducerValues}>
<PurchaseContext.Provider value={purchaseReducerValues}>
<Stack sx={{ pos: 'relative' }}>
<CloudImage
use={(
<img
src={getRemoteImage(
config.environment,
`/add-tokens-bg-texture-${colorMode}.webp`,
)}
alt="background texture"
/>
)}
sx={{
pos: 'absolute',
h: '100%',
w: '100%',
objectFit: 'cover',
objectPosition: 'center',
}}
/>
{viewState.view.type === PurchaseWidgetViews.PURCHASE && (
<Purchase
showBackButton={showBackButton}
onCloseButtonClick={() => sendPurchaseCloseEvent(eventTarget)}
onBackButtonClick={() => {
orchestrationEvents.sendRequestGoBackEvent(
eventTarget,
IMTBLWidgetEvents.IMTBL_PURCHASE_WIDGET_EVENT,
{},
);
}}
/>
)}
</Stack>
</PurchaseContext.Provider>
</ViewContext.Provider>
);
}
Loading

0 comments on commit 307be70

Please sign in to comment.