From 0d8509464df907e816c53a108e607edabc549161 Mon Sep 17 00:00:00 2001 From: "haoyang.feng" Date: Thu, 26 Sep 2024 19:46:34 +0800 Subject: [PATCH] chore: add new config --- packages/dex-widget/src/OkxSwapWidget.ts | 3 +- packages/dex-widget/src/types.ts | 2 + packages/dex-widget/src/widgetHelp.ts | 310 +++++++++--------- .../configurator/controls/BaseUrlControl.tsx | 105 ++++-- .../controls/CommonJsonControl.tsx | 46 +++ .../hooks/useWidgetParamsAndSettings.ts | 9 +- .../src/app/configurator/index.tsx | 25 +- .../src/app/configurator/types.ts | 1 + .../src/app/embedDialog/const.ts | 2 + 9 files changed, 312 insertions(+), 191 deletions(-) create mode 100644 packages/widget-configurator/src/app/configurator/controls/CommonJsonControl.tsx diff --git a/packages/dex-widget/src/OkxSwapWidget.ts b/packages/dex-widget/src/OkxSwapWidget.ts index c9ba75e..ce48380 100644 --- a/packages/dex-widget/src/OkxSwapWidget.ts +++ b/packages/dex-widget/src/OkxSwapWidget.ts @@ -105,7 +105,7 @@ export function createOkxSwapWidget( return { updateParams: (newParams: IWidgetParams) => { // width, lang, theme - const { width, lang, theme } = newParams; + const { width, lang, theme, extraParams } = newParams; updateIframeStyle(iframe, { width }); @@ -113,6 +113,7 @@ export function createOkxSwapWidget( ...params, lang, theme, + extraParams, }; currentParams = createWidgetParams(nextParams).data; diff --git a/packages/dex-widget/src/types.ts b/packages/dex-widget/src/types.ts index 2e98acd..8a9942e 100644 --- a/packages/dex-widget/src/types.ts +++ b/packages/dex-widget/src/types.ts @@ -423,6 +423,8 @@ export interface IWidgetParams { lang?: string; chainIds?: string[]; + + extraParams?: any; } export interface IWidgetConfig { params: IWidgetParams; diff --git a/packages/dex-widget/src/widgetHelp.ts b/packages/dex-widget/src/widgetHelp.ts index adfe8af..b9806a3 100644 --- a/packages/dex-widget/src/widgetHelp.ts +++ b/packages/dex-widget/src/widgetHelp.ts @@ -1,190 +1,202 @@ import { - TradeType, - IWidgetParams, - ITokenPair, - TWalletTypeRecord, - ProviderType, - IFormattedTokenPair, - IFormattedWidgetProps, + TradeType, + IWidgetParams, + ITokenPair, + TWalletTypeRecord, + ProviderType, + IFormattedTokenPair, + IFormattedWidgetProps, } from './types'; -import { isSameChain, verifyWidgetParams } from './verifyParamsUtils'; +import { verifyWidgetParams } from './verifyParamsUtils'; const DEFAULT_BASE_URL = 'https://www.okx.com'; export const WIDGET_ROUTE_CONSTANTS = { - SWAP: 'web3/dex-widget', - BRIDGE: 'web3/dex-widget/bridge', + SWAP: 'web3/dex-widget', + BRIDGE: 'web3/dex-widget/bridge', }; export const WALLET_TYPE: TWalletTypeRecord = { - [ProviderType.EVM]: 'metamask', - [ProviderType.SOLANA]: 'phantom', - [ProviderType.WALLET_CONNECT]: 'walletconnect', + [ProviderType.EVM]: 'metamask', + [ProviderType.SOLANA]: 'phantom', + [ProviderType.WALLET_CONNECT]: 'walletconnect', }; export const SOLANA_CHAIN_ID = 501; export const formatTokenPair = (tokenPair?: ITokenPair): IFormattedTokenPair => { - return tokenPair - ? { - inputChain: tokenPair.fromChain, - outputChain: tokenPair.toChain, - inputCurrency: tokenPair.fromToken, - outputCurrency: tokenPair.toToken, - } - : null; + return tokenPair + ? { + inputChain: tokenPair.fromChain, + outputChain: tokenPair.toChain, + inputCurrency: tokenPair.fromToken, + outputCurrency: tokenPair.toToken, + } + : null; }; // this function is designed to determine the supported trade types and the appropriate route based on the provided trade type and token pairs. // It returns an object containing the supported trade types, the route, and formatted token pairs. export function formatDefaultConfig( - tradeType: TradeType, - tokenPair?: ITokenPair, - bridgeTokenPair?: ITokenPair, + tradeType: TradeType, + tokenPair?: ITokenPair, + bridgeTokenPair?: ITokenPair, ): { - supportTradeType: TradeType[]; - route: string; - defaultTokenPair?: IFormattedTokenPair, - formattedTokenPair?: IFormattedTokenPair, - formattedBridgeTokenPair?: IFormattedTokenPair + supportTradeType: TradeType[]; + route: string; + defaultTokenPair?: IFormattedTokenPair, + formattedTokenPair?: IFormattedTokenPair, + formattedBridgeTokenPair?: IFormattedTokenPair } { - const formattedTokenPair = formatTokenPair(tokenPair); - const formattedBridgeTokenPair = formatTokenPair(bridgeTokenPair); - - if (tradeType === TradeType.SWAP) { - return { - supportTradeType: [TradeType.SWAP], - route: WIDGET_ROUTE_CONSTANTS.SWAP, - defaultTokenPair: formattedTokenPair, - formattedTokenPair, - formattedBridgeTokenPair: null, - }; - } + const formattedTokenPair = formatTokenPair(tokenPair); + const formattedBridgeTokenPair = formatTokenPair(bridgeTokenPair); - if (tradeType === TradeType.BRIDGE) { - return { - supportTradeType: [TradeType.BRIDGE], - route: WIDGET_ROUTE_CONSTANTS.BRIDGE, - defaultTokenPair: formattedBridgeTokenPair, - formattedTokenPair: null, - formattedBridgeTokenPair, - }; - } - - const defaultIsBridge = !formattedTokenPair && formattedBridgeTokenPair; - const route = defaultIsBridge - ? WIDGET_ROUTE_CONSTANTS.BRIDGE - : WIDGET_ROUTE_CONSTANTS.SWAP; - const defaultTokenPair = defaultIsBridge ? formattedBridgeTokenPair : formattedTokenPair; + if (tradeType === TradeType.SWAP) { + return { + supportTradeType: [TradeType.SWAP], + route: WIDGET_ROUTE_CONSTANTS.SWAP, + defaultTokenPair: formattedTokenPair, + formattedTokenPair, + formattedBridgeTokenPair: null, + }; + } + if (tradeType === TradeType.BRIDGE) { return { - supportTradeType: [TradeType.SWAP, TradeType.BRIDGE], - route, - defaultTokenPair, - formattedTokenPair, - formattedBridgeTokenPair, + supportTradeType: [TradeType.BRIDGE], + route: WIDGET_ROUTE_CONSTANTS.BRIDGE, + defaultTokenPair: formattedBridgeTokenPair, + formattedTokenPair: null, + formattedBridgeTokenPair, }; + } + + const defaultIsBridge = !formattedTokenPair && formattedBridgeTokenPair; + const route = defaultIsBridge + ? WIDGET_ROUTE_CONSTANTS.BRIDGE + : WIDGET_ROUTE_CONSTANTS.SWAP; + const defaultTokenPair = defaultIsBridge ? formattedBridgeTokenPair : formattedTokenPair; + + return { + supportTradeType: [TradeType.SWAP, TradeType.BRIDGE], + route, + defaultTokenPair, + formattedTokenPair, + formattedBridgeTokenPair, + }; } export const createWidgetParams = (widgetParams: IWidgetParams): IFormattedWidgetProps => { - const { baseUrl, feeConfig, tokenPair, bridgeTokenPair, providerType, tradeType, theme, lang, chainIds } = - widgetParams; - - const widgetVersion = process.env.WIDGET_VERSION; - // verify widget params, if invalid, throw error - verifyWidgetParams({ - widgetVersion, - feeConfig, - tokenPair, - bridgeTokenPair, - providerType, - }); - - // get trade type config, route, default token pair and formatted tokenPair/bridgeTokenPair config - const { - supportTradeType, - route, - defaultTokenPair, - formattedTokenPair, - formattedBridgeTokenPair, - } = formatDefaultConfig(tradeType, tokenPair, bridgeTokenPair); - - // define initial params - const initParams = { - tradeType: supportTradeType, - theme, - lang, - walletType: WALLET_TYPE[providerType], - widgetVersion, - chainIds, - }; - - // add token info to url params - const urlParams = { - ...initParams, - ...defaultTokenPair, - }; - const params = new URLSearchParams(); - // Append non-empty key-value pairs to URLSearchParams - for (const key in urlParams) { - if (urlParams.hasOwnProperty(key)) { - const value = urlParams[key]; - if (value !== '' && value !== null && value !== undefined) { - params.append(key, value); - } - } + const { + baseUrl, + feeConfig, + tokenPair, + bridgeTokenPair, + providerType, + tradeType, + theme, + lang, + chainIds, + extraParams, + } = + widgetParams; + + const widgetVersion = process.env.WIDGET_VERSION; + // verify widget params, if invalid, throw error + verifyWidgetParams({ + widgetVersion, + feeConfig, + tokenPair, + bridgeTokenPair, + providerType, + }); + + // get trade type config, route, default token pair and formatted tokenPair/bridgeTokenPair config + const { + supportTradeType, + route, + defaultTokenPair, + formattedTokenPair, + formattedBridgeTokenPair, + } = formatDefaultConfig(tradeType, tokenPair, bridgeTokenPair); + + // define initial params + const initParams = { + tradeType: supportTradeType, + theme, + lang, + walletType: WALLET_TYPE[providerType], + widgetVersion, + chainIds, + }; + + // add token info to url params + const urlParams = { + ...initParams, + ...defaultTokenPair, + }; + const params = new URLSearchParams(); + // Append non-empty key-value pairs to URLSearchParams + for (const key in urlParams) { + if (urlParams.hasOwnProperty(key)) { + const value = urlParams[key]; + if (value !== '' && value !== null && value !== undefined) { + params.append(key, value); + } } - // get query - const queryString = params.toString(); - // generate url - const host = typeof baseUrl === 'string' ? baseUrl : DEFAULT_BASE_URL; - const url = `${host}/${route}?${queryString}`; - - // add tokenPair, feeConfig, providerType to generate data - const data = { - ...initParams, - tokenPair: formattedTokenPair, - bridgeTokenPair: formattedBridgeTokenPair, - feeConfig, - providerType, - }; - - return { - url, - data, - }; + } + // get query + const queryString = params.toString(); + // generate url + const host = typeof baseUrl === 'string' ? baseUrl : DEFAULT_BASE_URL; + const url = `${host}/${route}?${queryString}`; + + // add tokenPair, feeConfig, providerType to generate data + const data = { + ...initParams, + tokenPair: formattedTokenPair, + bridgeTokenPair: formattedBridgeTokenPair, + feeConfig, + providerType, + extraParams, + }; + + return { + url, + data, + }; }; export const getChainId = (provider: any, providerType: ProviderType) => { - let chainId = null; + let chainId = null; - if (providerType === ProviderType.EVM && provider?.chainId) { - chainId = parseInt(provider.chainId, 16); - } + if (providerType === ProviderType.EVM && provider?.chainId) { + chainId = parseInt(provider.chainId, 16); + } - if (providerType === ProviderType.WALLET_CONNECT && provider?.chainId) { - chainId = provider.chainId; - } + if (providerType === ProviderType.WALLET_CONNECT && provider?.chainId) { + chainId = provider.chainId; + } - if (providerType === ProviderType.SOLANA) { - chainId = SOLANA_CHAIN_ID; - } + if (providerType === ProviderType.SOLANA) { + chainId = SOLANA_CHAIN_ID; + } - return chainId; + return chainId; }; export const getAddress = (provider: any, providerType: ProviderType) => { - if ( - (providerType === ProviderType.EVM || providerType === ProviderType.WALLET_CONNECT) && - provider?.chainId - ) { - const accounts = - providerType === ProviderType.EVM ? provider.selectedAddress : provider.accounts[0]; - return accounts; - } - if (providerType === ProviderType.SOLANA) { - return provider?.publicKey?.toBase58(); - } - return null; + if ( + (providerType === ProviderType.EVM || providerType === ProviderType.WALLET_CONNECT) && + provider?.chainId + ) { + const accounts = + providerType === ProviderType.EVM ? provider.selectedAddress : provider.accounts[0]; + return accounts; + } + if (providerType === ProviderType.SOLANA) { + return provider?.publicKey?.toBase58(); + } + return null; }; diff --git a/packages/widget-configurator/src/app/configurator/controls/BaseUrlControl.tsx b/packages/widget-configurator/src/app/configurator/controls/BaseUrlControl.tsx index c22d1b1..3f2770f 100644 --- a/packages/widget-configurator/src/app/configurator/controls/BaseUrlControl.tsx +++ b/packages/widget-configurator/src/app/configurator/controls/BaseUrlControl.tsx @@ -1,32 +1,85 @@ -import { Dispatch, SetStateAction } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; import Select, { SelectChangeEvent } from '@mui/material/Select'; import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; import MenuItem from '@mui/material/MenuItem'; +import TextField from '@mui/material/TextField'; -export const BaseUrlControl = ({ state, widgetHandler, params }: { state: [string, Dispatch>], params: any, widgetHandler: any }) => { - const [baseUrl, setBaseUrl] = state; - const handleBaseUrlChange = (event: SelectChangeEvent) => { - const url = event.target.value - setBaseUrl(url); - setTimeout(() => { - widgetHandler.current?.reload({ ...params, baseUrl: url }) - }) +const local_urls = 'local_urls'; + +export const BaseUrlControl = ({ state, widgetHandler, params }: { + state: [string, Dispatch>], + params: any, + widgetHandler: any +}) => { + const [baseUrl, setBaseUrl] = state; + const handleBaseUrlChange = (event: SelectChangeEvent) => { + const url = event.target.value; + setBaseUrl(url); + setTimeout(() => { + widgetHandler.current?.reload({ ...params, baseUrl: url }); + }); + }; + + const [customUrl, setCustomUrl] = useState(''); + + const [urlOptions, setUrlOptions] = useState([]); + useEffect(() => { + const localUrls = localStorage.getItem(local_urls); + try { + if (localUrls) { + setUrlOptions(JSON.parse(localUrls)); + } + } catch (e) { + console.error(e); } - return ( - - Base url - - - ); -} + }, []); + + const addCustomUrl = () => { + if (!customUrl) return; + const preUrls = localStorage.getItem(local_urls); + try { + const urls = preUrls ? JSON.parse(preUrls) : []; + urls.push(customUrl); + localStorage.setItem(local_urls, JSON.stringify(urls)); + setUrlOptions(urls); + } catch (e) { + console.error(e); + } + }; + + return ( + <> + + Base url + + + { + const url = event.target.value; + setCustomUrl(url); + }} + onBlur={addCustomUrl} + /> + + ); +}; diff --git a/packages/widget-configurator/src/app/configurator/controls/CommonJsonControl.tsx b/packages/widget-configurator/src/app/configurator/controls/CommonJsonControl.tsx new file mode 100644 index 0000000..c002619 --- /dev/null +++ b/packages/widget-configurator/src/app/configurator/controls/CommonJsonControl.tsx @@ -0,0 +1,46 @@ +import TextField from '@mui/material/TextField'; +import FormControl from '@mui/material/FormControl'; +import { Dispatch, SetStateAction } from 'react'; +import debounce from '@mui/material/utils/debounce'; + +const CommonJsonControl = ({ state, widgetHandler, params, configKey }: { + state: [string, Dispatch>], + params: any, + widgetHandler: any, + configKey: string +}) => { + const [config, setConfig] = state; + const updateConfig = debounce((value) => { + if (!value) { + widgetHandler.current?.reload({ ...params, [configKey]: null }); + return; + } + try { + const tokenPairObj = JSON.parse(value); + widgetHandler.current?.reload({ ...params, [configKey]: tokenPairObj }); + } catch (error) { + console.log(error); + } + }, 500); + + const handleChange = (event: React.ChangeEvent) => { + const value = event.target.value; + setConfig(value); + updateConfig(value); + }; + return ( + + + + ); +}; + +export default CommonJsonControl; diff --git a/packages/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts b/packages/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts index 9e64e1a..49c6cfa 100644 --- a/packages/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts +++ b/packages/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts @@ -16,6 +16,7 @@ export function useWidgetParams(configuratorState: ConfiguratorState) { provider, baseUrl, width, + extraParams, } = configuratorState; const params: any = { chainIds: chainIds ? chainIds.split(',') : [], @@ -26,13 +27,13 @@ export function useWidgetParams(configuratorState: ConfiguratorState) { provider, baseUrl, width, - bridgeTokenPair, }; - let parseTokenPair, parseBridgeTokenPair, parseFeeConfig; + let parseTokenPair, parseBridgeTokenPair, parseFeeConfig, parseExtraParams; try { parseTokenPair = tokenPair ? JSON.parse(tokenPair) : null; parseBridgeTokenPair = bridgeTokenPair ? JSON.parse(bridgeTokenPair) : null; + parseExtraParams = extraParams ? JSON.parse(extraParams) : null; // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { parseTokenPair = null; @@ -55,6 +56,10 @@ export function useWidgetParams(configuratorState: ConfiguratorState) { params.feeConfig = parseFeeConfig; } + if (parseExtraParams) { + params.extraParams = parseExtraParams; + } + return params; }, [configuratorState]); } diff --git a/packages/widget-configurator/src/app/configurator/index.tsx b/packages/widget-configurator/src/app/configurator/index.tsx index 252037d..b62ebb1 100644 --- a/packages/widget-configurator/src/app/configurator/index.tsx +++ b/packages/widget-configurator/src/app/configurator/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useRef, useState } from 'react'; +import React, { useContext, useRef, useState } from 'react'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import CodeIcon from '@mui/icons-material/Code'; import EditIcon from '@mui/icons-material/Edit'; @@ -25,6 +25,7 @@ import { ContentStyled, DrawerStyled, WrapperStyled } from './styled'; import { ConfiguratorState } from './types'; import TokenPairControl from './controls/TokenPairControl'; import CommissionControl from './controls/CommissionControl'; +import CommonJsonControl from './controls/CommonJsonControl'; import ProviderTypeControl from './controls/ProviderTypeControl'; import ChainIdsControl from './controls/ChainConfigControl'; import { DexWidget } from './DexWidget'; @@ -54,30 +55,24 @@ export function Configurator({ title }: { title: string }) { const customLanguagesState = useState('unknown'); const [lang] = customLanguagesState; - const tokenPairState = useState(JSON.stringify({ - fromChain: 1, - toChain: 1, - fromToken: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toToken: '0xec21fbf8ca053699b5059ae81f72aa2293434c86', - })); + const tokenPairState = useState(''); const [tokenPair] = tokenPairState; - const bridgeTokenPairState = useState(JSON.stringify({ - fromChain: 1, - toChain: 501, - fromToken: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toToken: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', - })); + const bridgeTokenPairState = useState(''); const [bridgeTokenPair] = bridgeTokenPairState; const feeConfigState = useState(''); const [feeConfig] = feeConfigState; + const baseUrlState = useState(import.meta.env.VITE_APP_DEFAUL_BASE_URL as string || 'https://www.okx.com'); const [baseUrl] = baseUrlState; const widthState = useState(''); const [width] = widthState; + const extraParamsState = useState(''); + const [extraParams] = extraParamsState; + const widgetHandler = useRef>(); const { dialogOpen, handleDialogClose, handleDialogOpen } = useEmbedDialogState(); @@ -94,6 +89,7 @@ export function Configurator({ title }: { title: string }) { provider, baseUrl, width, + extraParams, }; const params = useWidgetParams(state); @@ -165,6 +161,9 @@ export function Configurator({ title }: { title: string }) { More + + = { tradeType: 'The type of transaction. It can be “swap”, “bridge”, or “auto”.', providerType: 'ProviderType represents the type of the provider and corresponds to it one-to-one. For example, if the provider is Solana, then the providerType would be SOLANA.', + tokenPair: 'Set default swap tab token pair', + bridgeTokenPair: 'Set default bridge tab token pair', }; export const COMMENTS_BY_PARAM_NAME_TYPESCRIPT: Record = {