diff --git a/packages/widget/src/components/Routes/RoutesExpanded.tsx b/packages/widget/src/components/Routes/RoutesExpanded.tsx index 88c1bf307..275a59f5e 100644 --- a/packages/widget/src/components/Routes/RoutesExpanded.tsx +++ b/packages/widget/src/components/Routes/RoutesExpanded.tsx @@ -88,6 +88,7 @@ export const RoutesExpandedElement = () => { navigate(navigationRoutes.transactionExecution, { state: { routeId: route.id }, }) + emitter.emit(WidgetEvent.RouteSelected, { route, routes: routes! }) } const onExit = () => { diff --git a/packages/widget/src/components/TokenList/TokenList.tsx b/packages/widget/src/components/TokenList/TokenList.tsx index c9a4411f5..fe91e3193 100644 --- a/packages/widget/src/components/TokenList/TokenList.tsx +++ b/packages/widget/src/components/TokenList/TokenList.tsx @@ -1,12 +1,14 @@ import { useAccount } from '@lifi/wallet-management' import { Box } from '@mui/material' -import type { FC } from 'react' +import { type FC, useEffect } from 'react' import { useChain } from '../../hooks/useChain.js' import { useDebouncedWatch } from '../../hooks/useDebouncedWatch.js' import { useTokenBalances } from '../../hooks/useTokenBalances.js' import { useTokenSearch } from '../../hooks/useTokenSearch.js' +import { useWidgetEvents } from '../../hooks/useWidgetEvents.js' import { FormKeyHelper } from '../../stores/form/types.js' import { useFieldValues } from '../../stores/form/useFieldValues.js' +import { WidgetEvent } from '../../types/events.js' import type { TokenAmount } from '../../types/token.js' import { TokenNotFound } from './TokenNotFound.js' import { VirtualizedTokenList } from './VirtualizedTokenList.js' @@ -20,6 +22,7 @@ export const TokenList: FC = ({ height, onClick, }) => { + const emitter = useWidgetEvents() const [selectedChainId] = useFieldValues(FormKeyHelper.getChainKey(formType)) const [tokenSearchFilter]: string[] = useDebouncedWatch( 320, @@ -81,6 +84,16 @@ export const TokenList: FC = ({ Boolean(featuredTokens?.length || popularTokens?.length) && !tokenSearchFilter + // biome-ignore lint/correctness/useExhaustiveDependencies: Should fire only when search filter changes + useEffect(() => { + if (normalizedSearchFilter) { + emitter.emit(WidgetEvent.TokenSearch, { + value: normalizedSearchFilter, + tokens, + }) + } + }, [normalizedSearchFilter, emitter]) + return ( {!tokens.length && !isLoading ? ( diff --git a/packages/widget/src/index.ts b/packages/widget/src/index.ts index 8705eb689..31d7275d0 100644 --- a/packages/widget/src/index.ts +++ b/packages/widget/src/index.ts @@ -24,3 +24,4 @@ export * from './types/events.js' export type { TokenAmount } from './types/token.js' export * from './types/widget.js' export { getPriceImpact } from './utils/getPriceImpact.js' +export { navigationRoutes } from './utils/navigationRoutes.js' diff --git a/packages/widget/src/pages/MainPage/ReviewButton.tsx b/packages/widget/src/pages/MainPage/ReviewButton.tsx index 43891524d..afd3f3b1d 100644 --- a/packages/widget/src/pages/MainPage/ReviewButton.tsx +++ b/packages/widget/src/pages/MainPage/ReviewButton.tsx @@ -3,14 +3,17 @@ import { useNavigate } from 'react-router-dom' import { BaseTransactionButton } from '../../components/BaseTransactionButton/BaseTransactionButton.js' import { useRoutes } from '../../hooks/useRoutes.js' import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js' +import { useWidgetEvents } from '../../hooks/useWidgetEvents.js' import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js' import { useFieldValues } from '../../stores/form/useFieldValues.js' import { useSplitSubvariantStore } from '../../stores/settings/useSplitSubvariantStore.js' +import { WidgetEvent } from '../../types/events.js' import { navigationRoutes } from '../../utils/navigationRoutes.js' export const ReviewButton: React.FC = () => { const { t } = useTranslation() const navigate = useNavigate() + const emitter = useWidgetEvents() const { subvariant, subvariantOptions } = useWidgetConfig() const splitState = useSplitSubvariantStore((state) => state.state) const [toAddress] = useFieldValues('toAddress') @@ -25,6 +28,10 @@ export const ReviewButton: React.FC = () => { navigate(navigationRoutes.transactionExecution, { state: { routeId: currentRoute.id }, }) + emitter.emit(WidgetEvent.RouteSelected, { + route: currentRoute, + routes: routes!, + }) } } diff --git a/packages/widget/src/pages/RoutesPage/RoutesPage.tsx b/packages/widget/src/pages/RoutesPage/RoutesPage.tsx index a8d6416c5..d08f7de51 100644 --- a/packages/widget/src/pages/RoutesPage/RoutesPage.tsx +++ b/packages/widget/src/pages/RoutesPage/RoutesPage.tsx @@ -11,12 +11,15 @@ import { useHeader } from '../../hooks/useHeader.js' import { useNavigateBack } from '../../hooks/useNavigateBack.js' import { useRoutes } from '../../hooks/useRoutes.js' import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js' +import { useWidgetEvents } from '../../hooks/useWidgetEvents.js' import { useFieldValues } from '../../stores/form/useFieldValues.js' +import { WidgetEvent } from '../../types/events.js' import { navigationRoutes } from '../../utils/navigationRoutes.js' import { Stack } from './RoutesPage.style.js' export const RoutesPage: React.FC = () => { const { navigate } = useNavigateBack() + const emitter = useWidgetEvents() const { routes, isLoading, @@ -54,6 +57,10 @@ export const RoutesPage: React.FC = () => { navigate(navigationRoutes.transactionExecution, { state: { routeId: route.id }, }) + emitter.emit(WidgetEvent.RouteSelected, { + route, + routes: routes!, + }) } const routeNotFound = !routes?.length && !isLoading && !isFetching diff --git a/packages/widget/src/types/events.ts b/packages/widget/src/types/events.ts index b4ca35833..b4b035553 100644 --- a/packages/widget/src/types/events.ts +++ b/packages/widget/src/types/events.ts @@ -2,6 +2,7 @@ import type { ChainId, ChainType, Process, Route } from '@lifi/sdk' import type { DefaultValues } from '../stores/form/types.js' import type { SettingsProps } from '../stores/settings/types.js' import type { NavigationRouteType } from '../utils/navigationRoutes.js' +import type { TokenAmount } from './token.js' export enum WidgetEvent { RouteExecutionStarted = 'routeExecutionStarted', @@ -9,6 +10,7 @@ export enum WidgetEvent { RouteExecutionCompleted = 'routeExecutionCompleted', RouteExecutionFailed = 'routeExecutionFailed', RouteHighValueLoss = 'routeHighValueLoss', + RouteSelected = 'routeSelected', AvailableRoutes = 'availableRoutes', ContactSupport = 'contactSupport', SourceChainTokenSelected = 'sourceChainTokenSelected', @@ -26,6 +28,7 @@ export enum WidgetEvent { PageEntered = 'pageEntered', FormFieldChanged = 'formFieldChanged', SettingUpdated = 'settingUpdated', + TokenSearch = 'tokenSearch', } export type WidgetEvents = { @@ -34,6 +37,7 @@ export type WidgetEvents = { routeExecutionCompleted: Route routeExecutionFailed: RouteExecutionUpdate routeHighValueLoss: RouteHighValueLossUpdate + routeSelected: RouteSelected availableRoutes: Route[] contactSupport: ContactSupport sourceChainTokenSelected: ChainTokenSelected @@ -45,13 +49,14 @@ export type WidgetEvents = { widgetExpanded: boolean pageEntered: NavigationRouteType settingUpdated: SettingUpdated + tokenSearch: TokenSearch } -export interface ContactSupport { +export type ContactSupport = { supportId?: string } -export interface RouteHighValueLossUpdate { +export type RouteHighValueLossUpdate = { fromAmountUSD: number toAmountUSD: number gasCostUSD?: number @@ -59,17 +64,27 @@ export interface RouteHighValueLossUpdate { valueLoss: number } -export interface RouteExecutionUpdate { +export type RouteExecutionUpdate = { route: Route process: Process } -export interface ChainTokenSelected { +export type RouteSelected = { + route: Route + routes: Route[] +} + +export type TokenSearch = { + value: string + tokens: TokenAmount[] +} + +export type ChainTokenSelected = { chainId: ChainId tokenAddress: string } -export interface WalletConnected { +export type WalletConnected = { address?: string chainId?: number chainType?: ChainType