From 097728c92b25f71c2a0ee0de4b56953c888afc0e Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 7 Nov 2023 22:09:34 +0600 Subject: [PATCH 01/15] fix(twap): fix infinite loop in orders hook (#3348) --- .../src/modules/orders/hooks/useSWRProdOrders.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/orders/hooks/useSWRProdOrders.ts b/apps/cowswap-frontend/src/modules/orders/hooks/useSWRProdOrders.ts index 0e438f14bf..6fde864df6 100644 --- a/apps/cowswap-frontend/src/modules/orders/hooks/useSWRProdOrders.ts +++ b/apps/cowswap-frontend/src/modules/orders/hooks/useSWRProdOrders.ts @@ -12,6 +12,8 @@ import { getOrders } from 'api/gnosisProtocol' import { useApiOrders } from './useApiOrders' import { useSWROrdersRequest } from './useSWROrdersRequest' +const EMPTY_ORDERS: EnrichedOrder[] = [] + // Fetch PROD orders only when current env is not prod // We need them for TWAP, because WatchTower creates orders only on Prod export function useSWRProdOrders(): EnrichedOrder[] { @@ -19,11 +21,10 @@ export function useSWRProdOrders(): EnrichedOrder[] { const requestParams = useSWROrdersRequest() const apiOrders = useApiOrders() - const { data: loadedProdOrders = [] } = useSWR( + const { data: loadedProdOrders = EMPTY_ORDERS } = useSWR( ['prod-orders', requestParams, chainId], () => { - if (!chainId || !requestParams) return [] - if (!isBarnBackendEnv) return [] + if (!chainId || !requestParams || !isBarnBackendEnv) return EMPTY_ORDERS return getOrders(requestParams, { chainId, env: 'prod' }) }, From c9e235cdf466b80d9241099fcfd4cb7c6230ee89 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 7 Nov 2023 22:09:59 +0600 Subject: [PATCH 02/15] fix(tokens): use default value for user-added tokens migration (#3347) --- .../src/migrations/tokensLegacyStateMigration.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/tokens/src/migrations/tokensLegacyStateMigration.ts b/libs/tokens/src/migrations/tokensLegacyStateMigration.ts index 9755d842dd..bdd8ca55cd 100644 --- a/libs/tokens/src/migrations/tokensLegacyStateMigration.ts +++ b/libs/tokens/src/migrations/tokensLegacyStateMigration.ts @@ -35,7 +35,15 @@ function migrateLegacyTokensInUserState() { const userState = JSON.parse(userStateRaw) if (userState?.tokens && Object.keys(userState.tokens).length > 0) { - localStorage.setItem('userAddedTokensAtom:v1', JSON.stringify(userState.tokens)) + localStorage.setItem( + 'userAddedTokensAtom:v1', + JSON.stringify({ + [SupportedChainId.MAINNET]: {}, + [SupportedChainId.GNOSIS_CHAIN]: {}, + [SupportedChainId.GOERLI]: {}, + ...userState.tokens, + }) + ) } // Cleanup legacy redux store (UserState) From 9135805ab6b3b7b2dfcf432358eee85c2b1777de Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:31:27 +0000 Subject: [PATCH 03/15] chore: fix token image on permit sign modal (#3357) Co-authored-by: Anxo Rodriguez --- apps/cowswap-frontend/src/common/pure/IconSpinner/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/common/pure/IconSpinner/index.tsx b/apps/cowswap-frontend/src/common/pure/IconSpinner/index.tsx index e6800ca65c..90777494a3 100644 --- a/apps/cowswap-frontend/src/common/pure/IconSpinner/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/IconSpinner/index.tsx @@ -39,8 +39,8 @@ const Wrapper = styled.div<{ size: number; spinnerWidth: number; bgColor: UI }>` z-index: 1; } - > img, - > svg, + img, + svg, > span { object-fit: contain; z-index: 2; From 1a9c8b960fc95c059dbae888687246ceaf66811e Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:42:49 +0000 Subject: [PATCH 04/15] chore: fix token symbol crop image (#3358) Co-authored-by: Anxo Rodriguez --- libs/tokens/src/pure/TokenLogo/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/tokens/src/pure/TokenLogo/index.tsx b/libs/tokens/src/pure/TokenLogo/index.tsx index cc6173ecec..ed236b3718 100644 --- a/libs/tokens/src/pure/TokenLogo/index.tsx +++ b/libs/tokens/src/pure/TokenLogo/index.tsx @@ -22,8 +22,8 @@ const TokenLogoWrapper = styled.div<{ size?: number }>` overflow: hidden; img { - width: ${({ size }) => size}px; - height: ${({ size }) => size}px; + width: 100%; + height: 100%; border-radius: ${({ size }) => size}px; object-fit: contain; } @@ -62,7 +62,7 @@ export function TokenLogo({ logoURI, token, className, size = 36 }: TokenLogoPro return ( - {!currentUrl ? : } + {!currentUrl ? : token logo} ) } From aeb4e111c7d047bd9607f1c5af3c4c21305f5096 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Wed, 8 Nov 2023 20:25:42 +0600 Subject: [PATCH 05/15] feat(widget): links to landing and docs (#3359) --- .../src/app/configurator/index.tsx | 21 ++++++++++++++++++- .../src/app/configurator/styled.ts | 6 ++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/widget-configurator/src/app/configurator/index.tsx b/apps/widget-configurator/src/app/configurator/index.tsx index a91a45f18d..531d044451 100644 --- a/apps/widget-configurator/src/app/configurator/index.tsx +++ b/apps/widget-configurator/src/app/configurator/index.tsx @@ -19,7 +19,14 @@ import { TradeModesControl } from './controls/TradeModesControl' import { useProvider } from './hooks/useProvider' import { useSyncWidgetNetwork } from './hooks/useSyncWidgetNetwork' import { useWidgetParamsAndSettings } from './hooks/useWidgetParamsAndSettings' -import { ContentStyled, DrawerStyled, ShowDrawerButton, WalletConnectionWrapper, WrapperStyled } from './styled' +import { + ContentStyled, + DrawerStyled, + LinksWrapper, + ShowDrawerButton, + WalletConnectionWrapper, + WrapperStyled, +} from './styled' import { ConfiguratorState } from './types' import { ColorModeContext } from '../../theme/ColorModeContext' @@ -34,6 +41,8 @@ const DEFAULT_STATE = { buyAmount: 0, } +const UTM_PARAMS = 'utm_content=cow-widget-configurator&utm_medium=web&utm_source=widget.cow.fi' + export function Configurator({ title }: { title: string }) { const { mode } = useContext(ColorModeContext) @@ -105,6 +114,16 @@ export function Configurator({ title }: { title: string }) { {title} +
+ + Website + + {/*TODO: add link to the widget page docs*/} + + Docs + +
+ Wallet
diff --git a/apps/widget-configurator/src/app/configurator/styled.ts b/apps/widget-configurator/src/app/configurator/styled.ts index e675155a76..b7a683c4b2 100644 --- a/apps/widget-configurator/src/app/configurator/styled.ts +++ b/apps/widget-configurator/src/app/configurator/styled.ts @@ -56,3 +56,9 @@ export const ShowDrawerButton: (mode: PaletteMode) => CSSProperties = (mode: Pal fontSize: '24px', cursor: 'pointer', }) + +export const LinksWrapper = { + display: 'flex', + justifyContent: 'center', + gap: '15px', +} From b9127d93c05eaf942913065c008d839f694b2ae1 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 8 Nov 2023 17:01:42 +0000 Subject: [PATCH 06/15] feat: change appKey into appCode (#3361) --- .../src/common/hooks/useAnalyticsReporter.ts | 2 +- apps/cowswap-frontend/src/modules/appData/hooks.ts | 2 +- .../injectedWidget/state/injectedWidgetMetaDataAtom.ts | 4 ++-- .../configurator/hooks/useWidgetParamsAndSettings.ts | 2 +- apps/widget-configurator/src/app/embedDialog/const.ts | 4 ++-- libs/widget-lib/README.md | 7 ++++--- libs/widget-lib/docs/README.md | 8 ++++---- libs/widget-lib/src/cowSwapWidget.ts | 10 +++++----- libs/widget-lib/src/types.ts | 2 +- libs/widget-react/README.md | 3 +-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts b/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts index d869f2ab4f..c97e876405 100644 --- a/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts +++ b/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts @@ -76,7 +76,7 @@ export function useAnalyticsReporter() { const walletName = _walletName || getConnectionName(connection.type, isMetaMask) - const injectedWidgetAppId = injectedWidgetMetaData.appKey + const injectedWidgetAppId = injectedWidgetMetaData.appCode useEffect(() => { // Custom dimension 2 - walletname diff --git a/apps/cowswap-frontend/src/modules/appData/hooks.ts b/apps/cowswap-frontend/src/modules/appData/hooks.ts index c4fecb66cd..a63a57a373 100644 --- a/apps/cowswap-frontend/src/modules/appData/hooks.ts +++ b/apps/cowswap-frontend/src/modules/appData/hooks.ts @@ -22,7 +22,7 @@ export function useAppCode(): string | null { return useMemo(() => { if (isInjectedWidget()) { - return injectedWidgetMetaData.appKey + return injectedWidgetMetaData.appCode } if (APP_CODE) { diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts index 710ba8b1fd..6c0ae363d8 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts @@ -1,11 +1,11 @@ import { atom } from 'jotai' export interface CowSwapWidgetMetaData { - appKey: string + appCode: string } const DEFAULT_INJECTED_WIDGET_META_DATA: CowSwapWidgetMetaData = { - appKey: 'DEFAULT_INJECTED_WIDGET', + appCode: 'DEFAULT_INJECTED_WIDGET', } export const injectedWidgetMetaDataAtom = atom(DEFAULT_INJECTED_WIDGET_META_DATA) diff --git a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts index be24d55efd..530e9499f2 100644 --- a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts +++ b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts @@ -31,7 +31,7 @@ export function useWidgetParamsAndSettings( } = configuratorState const params: CowSwapWidgetProps['params'] = { - appKey: '', + appCode: '', width: '450px', height: '640px', provider, diff --git a/apps/widget-configurator/src/app/embedDialog/const.ts b/apps/widget-configurator/src/app/embedDialog/const.ts index 3a11c92eab..a880a69e44 100644 --- a/apps/widget-configurator/src/app/embedDialog/const.ts +++ b/apps/widget-configurator/src/app/embedDialog/const.ts @@ -1,9 +1,9 @@ import { CowSwapWidgetParams } from '@cowprotocol/widget-lib' -export const COMMENTS_BEFORE_PARAMS = ` Fill this form https://cowprotocol.typeform.com/to/rONXaxHV once you pick your "appKey"` +export const COMMENTS_BEFORE_PARAMS = ` Fill this form https://cowprotocol.typeform.com/to/rONXaxHV once you pick your "appCode"` export const COMMENTS_BY_PARAM_NAME: Record = { - appKey: 'Name of your app (max 50 characters, e.g. "Pig Swap")', + appCode: 'Name of your app (max 50 characters, e.g. "Pig Swap")', width: 'Width in pixels (or 100% to use all available space)', provider: 'Ethereum EIP-1193 provider. For a quick test, you can pass `window.ethereum`, but consider using something like https://web3modal.com', diff --git a/libs/widget-lib/README.md b/libs/widget-lib/README.md index 5d6f517294..f6879e5843 100644 --- a/libs/widget-lib/README.md +++ b/libs/widget-lib/README.md @@ -37,11 +37,11 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const widgetContainer = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - appKey: '', // Just an unique identifier for your app + appCode: '', // Just an unique identifier for your app sell: { asset: 'DAI' }, buy: { asset: 'USDC', amount: '0.1' }, // instantiate your own web3 provider - provider: window.ethereum + provider: window.ethereum, } const updateWidget = cowSwapWidget( @@ -57,12 +57,13 @@ updateWidget({ ...params, tradeType: 'limit' }) ## Developers #### Test + ``` nx test widget-lib ``` #### Build the library + ``` nx build widget-lib ``` - diff --git a/libs/widget-lib/docs/README.md b/libs/widget-lib/docs/README.md index f4227198dc..ad18b54470 100644 --- a/libs/widget-lib/docs/README.md +++ b/libs/widget-lib/docs/README.md @@ -27,7 +27,7 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const widgetContainer = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - appKey: 'YOUR_APP_ID', + appCode: 'YOUR_APP_ID', width: 600, height: 640, sell: { asset: 'DAI' }, @@ -39,7 +39,7 @@ cowSwapWidget(widgetContainer, params) ## App key -You must specify the `appKey` parameter when initializing the widget. This parameter is used to identify the source of +You must specify the `appCode` parameter when initializing the widget. This parameter is used to identify the source of orders. The key must be a UTF8 string of up to 50 chars. It will be a part of orders meta-data, see more in @@ -107,7 +107,7 @@ cowSwapWidget(document.getElementById('cowswap-widget'), { | --------------------- | ---------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `width` | `string` | 450px | Width in pixels (or 100% to use all available space). | | `height` | `string` | 640px | Height of the widget in css values (px, vh, etc.). | -| `appKey` | `string` | 'DEFAULT_INJECTED_WIDGET' | Name of your app (max 50 characters, e.g. "Pig Swap"). Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) after you pick yours | +| `appCode` | `string` | 'DEFAULT_INJECTED_WIDGET' | Name of your app (max 50 characters, e.g. "Pig Swap"). Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) after you pick yours | | `provider` | `EthereumProvider` | --- | Ethereum EIP-1193 provider to connect to the wallet. For a quick test, you can pass `window.ethereum`. You also might like to use https://web3modal.com | | `chainId` | `number` | 1 | The blockchain ID on which the trade will take place. Currently supported: 1 (Mainnet), 5 (Goerli), 100 (Gnosis chain) | | `tradeType` | `TradeType` | 'swap' | The type of trade. Can be `swap` or `limit` or `advanced`. | @@ -131,7 +131,7 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const container = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - appKey: 'YOUR_APP_ID', + appCode: 'YOUR_APP_ID', logoUrl: 'YOUR_LOGO_URL', } diff --git a/libs/widget-lib/src/cowSwapWidget.ts b/libs/widget-lib/src/cowSwapWidget.ts index d8d07725b9..d48fff6555 100644 --- a/libs/widget-lib/src/cowSwapWidget.ts +++ b/libs/widget-lib/src/cowSwapWidget.ts @@ -32,7 +32,7 @@ export function cowSwapWidget(container: HTMLElement, params: CowSwapWidgetParam if (!contentWindow) throw new Error('Iframe does not contain a window!') - sendAppKey(contentWindow, params.appKey) + sendAppCode(contentWindow, params.appCode) applyDynamicHeight(iframe, params.height) @@ -94,11 +94,11 @@ function updateWidget(params: CowSwapWidgetParams, contentWindow: Window) { } /** - * Sends appKey to the contentWindow of the widget. + * Sends appCode to the contentWindow of the widget. * @param contentWindow - Window object of the widget's iframe. - * @param appKey - A unique identifier for the app. + * @param appCode - A unique identifier for the app. */ -function sendAppKey(contentWindow: Window, appKey: string | undefined) { +function sendAppCode(contentWindow: Window, appCode: string | undefined) { window.addEventListener('message', (event) => { if (event.data.key !== COW_SWAP_WIDGET_EVENT_KEY || event.data.method !== 'activate') { return @@ -108,7 +108,7 @@ function sendAppKey(contentWindow: Window, appKey: string | undefined) { { key: COW_SWAP_WIDGET_EVENT_KEY, method: 'metaData', - metaData: appKey ? { appKey } : undefined, + metaData: appCode ? { appCode } : undefined, }, '*' ) diff --git a/libs/widget-lib/src/types.ts b/libs/widget-lib/src/types.ts index 7d23fd4750..ddd001e5d6 100644 --- a/libs/widget-lib/src/types.ts +++ b/libs/widget-lib/src/types.ts @@ -87,7 +87,7 @@ interface CowSwapWidgetConfig { * The unique identifier of the widget consumer. * Please fill the for to let us know a little about you: https://cowprotocol.typeform.com/to/rONXaxHV */ - appKey: string + appCode: string /** * The widget might be connected to a custom Ethereum provider. */ diff --git a/libs/widget-react/README.md b/libs/widget-react/README.md index 02229e9de9..85bc3498f9 100644 --- a/libs/widget-react/README.md +++ b/libs/widget-react/README.md @@ -10,7 +10,6 @@ Install dependency yarn add @cowprotocol/widget-react ``` - ```bash npm install @cowprotocol/widget-react ``` @@ -25,7 +24,7 @@ Prepare the config for the widget: ```ts const cowSwapWidgetParams: CowSwapWidgetParams = { - appKey: '', + appCode: '', width: '600px', height: '700px', tradeType: 'swap', From 43e22046de4ace3c85b9bb7d1a7409c2851fb395 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 8 Nov 2023 18:40:03 +0000 Subject: [PATCH 07/15] fix: fix race condition with widget (#3367) --- .../src/modules/appData/updater/AppDataInfoUpdater.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts b/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts index e9cb72f06f..ebf39e6109 100644 --- a/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts +++ b/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts @@ -1,5 +1,5 @@ import { useSetAtom } from 'jotai' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { CowEnv, SupportedChainId } from '@cowprotocol/cow-sdk' @@ -30,6 +30,8 @@ export function AppDataInfoUpdater({ chainId, slippageBips, orderClass, utm, hoo // AppCode is dynamic and based on how it's loaded (if used as a Gnosis Safe app) const appCode = useAppCode() + const updateAppDataPromiseRef = useRef(Promise.resolve()) + useEffect(() => { if (!appCode) { // reset values when there is no price estimation or network changes @@ -51,7 +53,8 @@ export function AppDataInfoUpdater({ chainId, slippageBips, orderClass, utm, hoo } } - updateAppData() + // Chain the next update to avoid race conditions + updateAppDataPromiseRef.current = updateAppDataPromiseRef.current.finally(updateAppData) }, [appCode, chainId, setAppDataInfo, slippageBips, orderClass, utm, hooks]) } function getEnvByClass(orderClass: string): CowEnv | undefined { From c18bb2edc8f4f433a4ca0ef1be78e4222747fe8a Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 8 Nov 2023 18:41:28 +0000 Subject: [PATCH 08/15] feat: improve appCode, config and readme (#3362) * chore: flatten tradeAssets * feat: show friendly appCode in snippet * docs: add fixme --- .../state/injectedWidgetMetaDataAtom.ts | 2 +- .../hooks/useWidgetParamsAndSettings.ts | 8 +-- .../src/app/embedDialog/const.ts | 10 +-- libs/widget-lib/docs/README.md | 67 ++++++++++--------- libs/widget-lib/src/types.ts | 31 +++++---- libs/widget-lib/src/urlUtils.spec.ts | 12 ++-- libs/widget-lib/src/urlUtils.ts | 31 +++++---- 7 files changed, 88 insertions(+), 73 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts index 6c0ae363d8..2f488974e3 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts @@ -5,7 +5,7 @@ export interface CowSwapWidgetMetaData { } const DEFAULT_INJECTED_WIDGET_META_DATA: CowSwapWidgetMetaData = { - appCode: 'DEFAULT_INJECTED_WIDGET', + appCode: 'DEFAULT_INJECTED_WIDGET', // FIXME: there shouldn't be a default. If it's not set, we should have a banner or something } export const injectedWidgetMetaDataAtom = atom(DEFAULT_INJECTED_WIDGET_META_DATA) diff --git a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts index 530e9499f2..e4d4dd0624 100644 --- a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts +++ b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts @@ -31,7 +31,7 @@ export function useWidgetParamsAndSettings( } = configuratorState const params: CowSwapWidgetProps['params'] = { - appCode: '', + appCode: 'CoW Widget: Configurator', width: '450px', height: '640px', provider, @@ -39,10 +39,8 @@ export function useWidgetParamsAndSettings( chainId, env: getEnv(), tradeType: currentTradeType, - tradeAssets: { - sell: { asset: sellToken, amount: sellTokenAmount ? sellTokenAmount.toString() : undefined }, - buy: { asset: buyToken, amount: buyTokenAmount?.toString() }, - }, + sell: { asset: sellToken, amount: sellTokenAmount ? sellTokenAmount.toString() : undefined }, + buy: { asset: buyToken, amount: buyTokenAmount?.toString() }, enabledTradeTypes, // palette: { // primaryColor: '#d9258e', diff --git a/apps/widget-configurator/src/app/embedDialog/const.ts b/apps/widget-configurator/src/app/embedDialog/const.ts index a880a69e44..977a00625b 100644 --- a/apps/widget-configurator/src/app/embedDialog/const.ts +++ b/apps/widget-configurator/src/app/embedDialog/const.ts @@ -3,16 +3,17 @@ import { CowSwapWidgetParams } from '@cowprotocol/widget-lib' export const COMMENTS_BEFORE_PARAMS = ` Fill this form https://cowprotocol.typeform.com/to/rONXaxHV once you pick your "appCode"` export const COMMENTS_BY_PARAM_NAME: Record = { - appCode: 'Name of your app (max 50 characters, e.g. "Pig Swap")', + appCode: 'Name of your app (max 50 characters)', width: 'Width in pixels (or 100% to use all available space)', provider: 'Ethereum EIP-1193 provider. For a quick test, you can pass `window.ethereum`, but consider using something like https://web3modal.com', chainId: '1 (Mainnet), 5 (Goerli), 100 (Gnosis)', theme: 'light or dark', tradeType: 'swap, limit or advanced', - tradeAssets: 'Selected assets and amounts (e.g. COW-USDC)', + sell: 'Sell token. Optionally add amount for sell orders', + buy: 'Buy token. Optionally add amount for buy orders', enabledTradeTypes: 'swap, limit and/or advanced', - partnerFeeBips: 'Fill the form above if you are interested', + interfaceFeeBips: 'Fill the form above if you are interested', } export const VALUES_BY_PARAM_NAME: Record = { @@ -20,8 +21,9 @@ export const VALUES_BY_PARAM_NAME: Record = { } export const SANITIZE_PARAMS = { + appCode: 'My Cool App', provider: 'EIP-1271 Provider', - partnerFeeBips: '50', + interfaceFeeBips: '50', } export const REMOVE_PARAMS: (keyof CowSwapWidgetParams)[] = ['env'] diff --git a/libs/widget-lib/docs/README.md b/libs/widget-lib/docs/README.md index ad18b54470..741e2d83e0 100644 --- a/libs/widget-lib/docs/README.md +++ b/libs/widget-lib/docs/README.md @@ -27,27 +27,31 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const widgetContainer = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - appCode: 'YOUR_APP_ID', - width: 600, + appCode: 'My Cool App', // Name of your app (max 50 characters) + width: 600, // Width in pixels (or 100% to use all available space) height: 640, - sell: { asset: 'DAI' }, - buy: { asset: 'USDC', amount: '0.1' }, + sell: { asset: 'DAI' }, // Sell token. Optionally add amount for sell orders + buy: { asset: 'USDC', amount: '0.1' }, // Buy token. Optionally add amount for buy orders } cowSwapWidget(widgetContainer, params) ``` -## App key +## App Code You must specify the `appCode` parameter when initializing the widget. This parameter is used to identify the source of -orders. -The key must be a UTF8 string of up to 50 chars. +orders. + +The key must be a UTF8 string of up to `50 chars`. + It will be a part of orders meta-data, see more in the [CoW Protocol Docs](https://docs.cow.fi/front-end/creating-app-ids/create-the-order-meta-data-file/appcode). -## Partner fee +## Interface fee + +> **Coming soon! Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) if you are interested** -If your business model involves charging a fee for trading, you can easily do this by adding just one parameter: +You can add a additional trading fee that will be displayed and applied to the quoted amounts: ```typescript import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' @@ -55,14 +59,12 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const widgetContainer = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - partnerFeeBips: '50', // 0.5% + interfaceFeeBips: '50', // 0.5% } cowSwapWidget(widgetContainer, params) ``` -> **Coming soon! Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) if you are interested** - ## Wallet provider You can pass the wallet provider from your application to seamlessly use the widget as part of your application. @@ -101,25 +103,26 @@ cowSwapWidget(document.getElementById('cowswap-widget'), { ### `CowSwapWidgetParams` -> All params are optional - -| Parameter | Type | Default | Description | -| --------------------- | ---------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `width` | `string` | 450px | Width in pixels (or 100% to use all available space). | -| `height` | `string` | 640px | Height of the widget in css values (px, vh, etc.). | -| `appCode` | `string` | 'DEFAULT_INJECTED_WIDGET' | Name of your app (max 50 characters, e.g. "Pig Swap"). Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) after you pick yours | -| `provider` | `EthereumProvider` | --- | Ethereum EIP-1193 provider to connect to the wallet. For a quick test, you can pass `window.ethereum`. You also might like to use https://web3modal.com | -| `chainId` | `number` | 1 | The blockchain ID on which the trade will take place. Currently supported: 1 (Mainnet), 5 (Goerli), 100 (Gnosis chain) | -| `tradeType` | `TradeType` | 'swap' | The type of trade. Can be `swap` or `limit` or `advanced`. | -| `env` | `CowSwapWidgetEnv` | 'prod' | The environment of the widget (`local` , `prod` , `dev` , `pr`). See [`COWSWAP_URLS`](https://github.com/cowprotocol/cowswap/blob/develop/libs/widget-lib/src/consts.ts) const value for urls. | -| `tradeAssets` | `TradeAssets` | Same as in swap.cow.fi | An object containing information about the selling and buying assets. Example: `{ asset: 'WBTC', amount: 12 }` or `{ asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }` | -| `theme` | `CowSwapTheme` | 'light' | Theme of the widget (`'dark'` for dark theme or `'light'` for light theme). | -| `logoUrl` | `string` | --- | Sets a custom logo for the widget. | -| `hideLogo` | `boolean` | false | Hides the logo in the widget. | -| `hideNetworkSelector` | `boolean` | false | Disables an opportunity to change the network from the widget UI. | -| `enabledTradeTypes` | `Array` | All are enabled | CoW Swap provides three trading widgets: `swap`, `limit` and `advanced` orders. Using this option you can narrow down the list of available trading widgets. | -| `palette` | `CowSwapWidgetPalette` | --- | Customizes the appearance of the widget. For example, you can change the main color of the background and text. | -| `partnerFeeBips` | `string` | --- | Coming soon! Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) if you are interested | +> All params except `appCode` are optional: + +| Parameter | Type | Default | Description | +| --------------------- | ---------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `appCode` | `string` | No default. Required | Name of your app (max 50 characters, e.g. "My Cool App"). Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) after you pick yours | +| `width` | `string` | 450px | Width in pixels (or 100% to use all available space). | +| `height` | `string` | 640px | Height of the widget in css values (px, vh, etc.). | +| `provider` | `EthereumProvider` | --- | Ethereum EIP-1193 provider to connect to the wallet. For a quick test, you can pass `window.ethereum`. You also might like to use https://web3modal.com | +| `chainId` | `number` | 1 | The blockchain ID on which the trade will take place. Currently supported: 1 (Mainnet), 5 (Goerli), 100 (Gnosis chain) | +| `tradeType` | `TradeType` | 'swap' | The type of trade. Can be `swap` or `limit` or `advanced`. | +| `env` | `CowSwapWidgetEnv` | 'prod' | The environment of the widget (`local` , `prod` , `dev` , `pr`). See [`COWSWAP_URLS`](https://github.com/cowprotocol/cowswap/blob/develop/libs/widget-lib/src/consts.ts) const value for urls. | +| `sell` | `TradeAsset` | undefined | Sell token and optionally the sell order amount. Example: `{ asset: 'WBTC', amount: 12 }` or `{ asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }` | +| `buy` | `TradeAsset` | undefined | Buy token and optionally the buy order amount. Example: `{ asset: 'WBTC', amount: 12 }` or `{ asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }` | +| `theme` | `CowSwapTheme` | 'light' | Theme of the widget (`'dark'` for dark theme or `'light'` for light theme). | +| `logoUrl` | `string` | --- | Sets a custom logo for the widget. | +| `hideLogo` | `boolean` | false | Hides the logo in the widget. | +| `hideNetworkSelector` | `boolean` | false | Disables an opportunity to change the network from the widget UI. | +| `enabledTradeTypes` | `Array` | All are enabled | CoW Swap provides three trading widgets: `swap`, `limit` and `advanced` orders. Using this option you can narrow down the list of available trading widgets. | +| `palette` | `CowSwapWidgetPalette` | --- | Customizes the appearance of the widget. For example, you can change the main color of the background and text. | +| `interfaceFeeBips` | `string` | --- | Coming soon! Fill [this form](https://cowprotocol.typeform.com/to/rONXaxHV) if you are interested | ## Widget updating @@ -131,7 +134,7 @@ import { cowSwapWidget, CowSwapWidgetParams } from '@cowprotocol/widget-lib' const container = document.getElementById('cowswap-widget') const params: CowSwapWidgetParams = { - appCode: 'YOUR_APP_ID', + appCode: 'My Cool App', // Name of your app (max 50 characters) logoUrl: 'YOUR_LOGO_URL', } diff --git a/libs/widget-lib/src/types.ts b/libs/widget-lib/src/types.ts index ddd001e5d6..69a99aa2e0 100644 --- a/libs/widget-lib/src/types.ts +++ b/libs/widget-lib/src/types.ts @@ -49,14 +49,6 @@ interface TradeAsset { amount?: string } -/** - * A pair of assets to trade. - */ -export interface TradeAssets { - sell: TradeAsset - buy: TradeAsset -} - export enum TradeType { SWAP = 'swap', LIMIT = 'limit', @@ -105,10 +97,17 @@ interface CowSwapWidgetConfig { * The environment of the widget. Default: prod */ env: CowSwapWidgetEnv + + /** + * Sell token, and optionally the amount. + */ + sell: TradeAsset + /** - * The assets to trade. + * Buy token, and optionally the amount. */ - tradeAssets: TradeAssets + buy: TradeAsset + /** * The theme of the widget UI. */ @@ -118,28 +117,34 @@ interface CowSwapWidgetConfig { * Allows to set a custom logo for the widget. */ logoUrl: string + /** * Option to hide the logo in the widget. */ hideLogo: boolean + /** * Option to hide the network selector in the widget. */ hideNetworkSelector: boolean + /** * Enables the ability to switch between trade types in the widget. */ enabledTradeTypes: TradeType[] + /** * Colors palette to customize the widget UI. */ palette: CowSwapWidgetPalette + /** - * The partner fee in basis points. + * The interface fee in basis points. * For example: 1.5% = 150 bips - * Please contact https://cowprotocol.typeform.com/to/rONXaxHV to enable your partner fee. + * + * Please contact https://cowprotocol.typeform.com/to/rONXaxHV */ - partnerFeeBips: string + interfaceFeeBips: string } export type CowSwapWidgetParams = Partial diff --git a/libs/widget-lib/src/urlUtils.spec.ts b/libs/widget-lib/src/urlUtils.spec.ts index 5e3d6fc425..844cb377ba 100644 --- a/libs/widget-lib/src/urlUtils.spec.ts +++ b/libs/widget-lib/src/urlUtils.spec.ts @@ -42,7 +42,8 @@ describe('buildWidgetUrl', () => { describe('trade assets', () => { it('without amounts', () => { const url = buildWidgetUrl({ - tradeAssets: { sell: { asset: 'WETH' }, buy: { asset: 'COW' } }, + sell: { asset: 'WETH' }, + buy: { asset: 'COW' }, chainId, tradeType, env: defaultEnv, @@ -52,7 +53,8 @@ describe('buildWidgetUrl', () => { it('with sell amount', () => { const url = buildWidgetUrl({ - tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC' } }, + sell: { asset: 'DAI', amount: '0.1' }, + buy: { asset: 'USDC' }, chainId, tradeType, env: defaultEnv, @@ -62,7 +64,8 @@ describe('buildWidgetUrl', () => { it('with buy amount', () => { const url = buildWidgetUrl({ - tradeAssets: { sell: { asset: 'DAI' }, buy: { asset: 'USDC', amount: '0.1' } }, + sell: { asset: 'DAI' }, + buy: { asset: 'USDC', amount: '0.1' }, chainId, tradeType, env: defaultEnv, @@ -78,7 +81,8 @@ describe('buildWidgetUrl', () => { chainId: 100, tradeType, theme: 'light', - tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC', amount: '0.1' } }, + sell: { asset: 'DAI', amount: '0.1' }, + buy: { asset: 'USDC', amount: '0.1' }, }) expect(url).toEqual('https://swap.cow.fi/#/100/widget/swap/DAI/USDC?sellAmount=0.1&buyAmount=0.1&theme=light') }) diff --git a/libs/widget-lib/src/urlUtils.ts b/libs/widget-lib/src/urlUtils.ts index 9669910eac..15a320e684 100644 --- a/libs/widget-lib/src/urlUtils.ts +++ b/libs/widget-lib/src/urlUtils.ts @@ -10,29 +10,32 @@ export function buildWidgetUrl(params: CowSwapWidgetParams): string { } export function buildWidgetPath(params: CowSwapWidgetParams): string { - const { chainId = 1, tradeAssets, tradeType = TradeType.SWAP } = params + const { chainId = 1, sell, buy, tradeType = TradeType.SWAP } = params - const assetsPath = tradeAssets - ? [tradeAssets.sell.asset, tradeAssets.buy.asset].map(encodeURIComponent).join('/') - : '' + const assets = [] + if (sell?.asset) { + assets.push(sell.asset) + } + + if (buy?.asset) { + assets.push(buy.asset) + } + + const assetsPath = assets.map(encodeURIComponent).join('/') return `/${chainId}/widget/${tradeType}/${assetsPath}` } export function buildTradeAmountsQuery(params: CowSwapWidgetParams): URLSearchParams { - const { tradeAssets, theme } = params + const { sell, buy, theme } = params const query = new URLSearchParams() - if (tradeAssets) { - const { sell, buy } = tradeAssets - - if (sell.amount) { - query.append('sellAmount', sell.amount) - } + if (sell?.amount) { + query.append('sellAmount', sell.amount) + } - if (buy.amount) { - query.append('buyAmount', buy.amount) - } + if (buy?.amount) { + query.append('buyAmount', buy.amount) } if (theme) { From 6b262ab129cbfa62342b0a17ee2e67f26149f045 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 9 Nov 2023 08:54:06 +0000 Subject: [PATCH 09/15] feat: simplify theme config (#3363) * feat: simplify theme config * chore: remove override of colors --- .../injectedWidget/hooks/useInjectedWidgetPalette.ts | 6 +++++- .../app/configurator/hooks/useWidgetParamsAndSettings.ts | 5 +++-- apps/widget-configurator/src/app/embedDialog/const.ts | 2 +- libs/widget-lib/src/types.ts | 8 ++------ libs/widget-lib/src/urlUtils.ts | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetPalette.ts b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetPalette.ts index 306a523e81..12784d40c3 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetPalette.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetPalette.ts @@ -6,5 +6,9 @@ import { useInjectedWidgetParams } from './useInjectedWidgetParams' export function useInjectedWidgetPalette(): CowSwapWidgetPalette | undefined { const state = useInjectedWidgetParams() - return state.palette + return isCowSwapWidgetPallet(state.theme) ? state.theme : undefined +} + +function isCowSwapWidgetPallet(palette: any): palette is CowSwapWidgetPalette { + return palette && typeof palette === 'object' } diff --git a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts index e4d4dd0624..d7bec78241 100644 --- a/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts +++ b/apps/widget-configurator/src/app/configurator/hooks/useWidgetParamsAndSettings.ts @@ -35,14 +35,15 @@ export function useWidgetParamsAndSettings( width: '450px', height: '640px', provider, - theme, chainId, env: getEnv(), tradeType: currentTradeType, sell: { asset: sellToken, amount: sellTokenAmount ? sellTokenAmount.toString() : undefined }, buy: { asset: buyToken, amount: buyTokenAmount?.toString() }, enabledTradeTypes, - // palette: { + theme, + // theme: { + // baseTheme: theme, // primaryColor: '#d9258e', // screenBackground: '#ee00cd', // widgetBackground: '#b900ff', diff --git a/apps/widget-configurator/src/app/embedDialog/const.ts b/apps/widget-configurator/src/app/embedDialog/const.ts index 977a00625b..3d07acb119 100644 --- a/apps/widget-configurator/src/app/embedDialog/const.ts +++ b/apps/widget-configurator/src/app/embedDialog/const.ts @@ -8,7 +8,7 @@ export const COMMENTS_BY_PARAM_NAME: Record = { provider: 'Ethereum EIP-1193 provider. For a quick test, you can pass `window.ethereum`, but consider using something like https://web3modal.com', chainId: '1 (Mainnet), 5 (Goerli), 100 (Gnosis)', - theme: 'light or dark', + theme: 'light/dark or provide your own color palette', tradeType: 'swap, limit or advanced', sell: 'Sell token. Optionally add amount for sell orders', buy: 'Buy token. Optionally add amount for buy orders', diff --git a/libs/widget-lib/src/types.ts b/libs/widget-lib/src/types.ts index 69a99aa2e0..cc993637cc 100644 --- a/libs/widget-lib/src/types.ts +++ b/libs/widget-lib/src/types.ts @@ -60,6 +60,7 @@ export enum TradeType { } export interface CowSwapWidgetPalette { + baseTheme: CowSwapTheme primaryColor: string screenBackground: string widgetBackground: string @@ -111,7 +112,7 @@ interface CowSwapWidgetConfig { /** * The theme of the widget UI. */ - theme: CowSwapTheme + theme: CowSwapTheme | CowSwapWidgetPalette /** * Allows to set a custom logo for the widget. @@ -133,11 +134,6 @@ interface CowSwapWidgetConfig { */ enabledTradeTypes: TradeType[] - /** - * Colors palette to customize the widget UI. - */ - palette: CowSwapWidgetPalette - /** * The interface fee in basis points. * For example: 1.5% = 150 bips diff --git a/libs/widget-lib/src/urlUtils.ts b/libs/widget-lib/src/urlUtils.ts index 15a320e684..f70539b26e 100644 --- a/libs/widget-lib/src/urlUtils.ts +++ b/libs/widget-lib/src/urlUtils.ts @@ -39,7 +39,7 @@ export function buildTradeAmountsQuery(params: CowSwapWidgetParams): URLSearchPa } if (theme) { - query.append('theme', theme) + query.append('theme', typeof theme === 'string' ? theme : theme.baseTheme) } return query From 51eb483643d80b1b0a39775b68b055a3ff8a8b92 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 9 Nov 2023 15:39:12 +0600 Subject: [PATCH 10/15] fix(widget-configurator): switch network only when user changed in the configurator (#3369) --- .../src/app/configurator/hooks/useSyncWidgetNetwork.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/widget-configurator/src/app/configurator/hooks/useSyncWidgetNetwork.ts b/apps/widget-configurator/src/app/configurator/hooks/useSyncWidgetNetwork.ts index bbbb9b917e..614ce7c366 100644 --- a/apps/widget-configurator/src/app/configurator/hooks/useSyncWidgetNetwork.ts +++ b/apps/widget-configurator/src/app/configurator/hooks/useSyncWidgetNetwork.ts @@ -1,4 +1,4 @@ -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import type { SupportedChainId } from '@cowprotocol/cow-sdk' @@ -14,6 +14,8 @@ export function useSyncWidgetNetwork( const network = useNetwork() const { switchNetwork } = useSwitchNetwork() const walletChainId = network.chain?.id + const walletChainIdRef = useRef(walletChainId) + walletChainIdRef.current = walletChainId // Bind network control to wallet network useEffect(() => { @@ -26,10 +28,10 @@ export function useSyncWidgetNetwork( } }, [isDisconnected, walletChainId, setNetworkControlState]) - // Send a request to switch network if wallet network is different from widget network + // Send a request to switch network when user changes network in the configurator useEffect(() => { - if (!switchNetwork || isDisconnected || walletChainId === chainId) return + if (!switchNetwork || isDisconnected || walletChainIdRef.current === chainId) return switchNetwork(chainId) - }, [chainId, isDisconnected, switchNetwork, walletChainId, setNetworkControlState]) + }, [chainId, isDisconnected, switchNetwork, setNetworkControlState]) } From f9a4d0b667c621f6f6692db926a727636b774511 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 9 Nov 2023 15:42:41 +0600 Subject: [PATCH 11/15] fix(tokens): fix token lists styles (#3360) --- .../containers/ManageTokens/index.tsx | 27 +-- .../containers/ManageTokens/styled.ts | 18 +- .../containers/TokenSearchResults/index.tsx | 155 ++++++++++-------- .../containers/TokenSearchResults/styled.ts | 5 + .../tokensList/pure/ListItem/styled.ts | 5 + .../tokens/src/hooks/tokens/useSearchToken.ts | 37 ++++- 6 files changed, 145 insertions(+), 102 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/index.tsx index 8fadd69f0b..22c887e388 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/index.tsx +++ b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/index.tsx @@ -10,6 +10,7 @@ import { ExternalLink, Trash } from 'react-feather' import * as styledEl from './styled' import { useAddTokenImportCallback } from '../../hooks/useAddTokenImportCallback' +import { CommonListContainer } from '../../pure/commonElements' import { ImportTokenItem } from '../../pure/ImportTokenItem' const tokensListToMap = (tokens: TokenWithLogo[]) => { @@ -44,19 +45,23 @@ export function ManageTokens(props: ManageTokensProps) { }, [blockchainResult, externalApiResult, inactiveListsResult]) return ( - - {activeListsResult?.map((token) => { - return - })} - {!activeListsResult?.length && - tokensToImport?.map((token) => { - return - })} + <> + {(!!activeListsResult?.length || !!tokensToImport?.length) && ( + + {activeListsResult?.map((token) => { + return + })} + {!activeListsResult?.length && + tokensToImport?.map((token) => { + return + })} + + )} {tokens.length} Custom Tokens {tokens.length > 0 && Clear all} - + {tokens.map((token) => { return ( @@ -81,8 +86,8 @@ export function ManageTokens(props: ManageTokensProps) { ) })} - + Tip: Custom tokens are stored locally in your browser - + ) } diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/styled.ts index 217ef5559c..f64cc349ff 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/styled.ts +++ b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageTokens/styled.ts @@ -11,8 +11,10 @@ const RowBox = styled.div` align-items: center; ` -export const Wrapper = styled.div` - overflow: auto; +export const SearchResults = styled.div` + margin-top: 15px; + padding-bottom: 15px; + border-bottom: 1px solid var(${UI.COLOR_GREY}); ` export const Header = styled(RowBox)` @@ -24,17 +26,6 @@ export const Title = styled.div` opacity: 0.65; ` -export const TokensWrapper = styled.div` - height: calc(100vh - 450px); - overflow: auto; - - ${({ theme }) => theme.colorScrollbar}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - height: calc(100vh - 350px); - `} -` - export const LinkButton = styled.button` ${blankButtonMixin}; @@ -65,5 +56,4 @@ export const TipText = styled.div` text-align: center; padding: 20px 0; border-top: 1px solid var(${UI.COLOR_GREY}); - margin-top: 20px; ` diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx index f7e000d567..c148393351 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx +++ b/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo } from 'react' import { useNetworkName } from '@cowprotocol/common-hooks' import { doesTokenMatchSymbolOrAddress } from '@cowprotocol/common-utils' import { useSearchToken } from '@cowprotocol/tokens' +import { Loader } from '@cowprotocol/ui' import * as styledEl from './styled' @@ -29,8 +30,9 @@ export function TokenSearchResults({ unsupportedTokens, permitCompatibleTokens, }: TokenSearchResultsProps) { - const { inactiveListsResult, blockchainResult, activeListsResult, externalApiResult, isLoading } = - useSearchToken(searchInput) + const searchResults = useSearchToken(searchInput) + + const { inactiveListsResult, blockchainResult, activeListsResult, externalApiResult, isLoading } = searchResults const updateSelectTokenWidget = useSetAtom(updateSelectTokenWidgetAtom) @@ -68,78 +70,91 @@ export function TokenSearchResults({ }) }, [onInputPressEnter, updateSelectTokenWidget]) - if (isTokenNotFound) return No tokens found in {networkName} - return ( - {/*Tokens from active lists*/} - {activeListsResult && - activeListsResult.slice(0, searchResultsLimit).map((token) => { - const addressLowerCase = token.address.toLowerCase() - + {(() => { + if (isLoading) return ( - + + + ) - })} - - {/*Tokens from blockchain*/} - {blockchainResult?.length ? ( - - {blockchainResult.slice(0, searchResultsLimit).map((token) => { - return - })} - - ) : null} - - {/*Tokens from inactive lists*/} - {inactiveListsResult?.length ? ( -
- - Expanded results from inactive Token Lists - -
- {inactiveListsResult.slice(0, searchResultsLimit).map((token) => { - return ( - - ) - })} -
-
- ) : null} - - {/*Tokens from external sources*/} - {externalApiResult?.length ? ( -
- - Additional Results from External Sources - -
- {externalApiResult.map((token) => { - return ( - - ) - })} -
-
- ) : null} + + if (isTokenNotFound) return No tokens found in {networkName} + + return ( + <> + {/*Tokens from active lists*/} + {activeListsResult && + activeListsResult.slice(0, searchResultsLimit).map((token) => { + const addressLowerCase = token.address.toLowerCase() + + return ( + + ) + })} + + {/*Tokens from blockchain*/} + {blockchainResult?.length ? ( + + {blockchainResult.slice(0, searchResultsLimit).map((token) => { + return + })} + + ) : null} + + {/*Tokens from inactive lists*/} + {inactiveListsResult?.length ? ( +
+ + Expanded results from inactive Token Lists + +
+ {inactiveListsResult.slice(0, searchResultsLimit).map((token) => { + return ( + + ) + })} +
+
+ ) : null} + + {/*Tokens from external sources*/} + {externalApiResult?.length ? ( +
+ + Additional Results from External Sources + +
+ {externalApiResult.map((token) => { + return ( + + ) + })} +
+
+ ) : null} + + ) + })()}
) } diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/styled.ts index 4eff7f9dc6..abfdc0556f 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/styled.ts +++ b/apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/styled.ts @@ -12,3 +12,8 @@ export const TokenNotFound = styled.div` export const ImportTokenWrapper = styled.div` margin: 20px 0; ` + +export const LoaderWrapper = styled.div` + text-align: center; + margin: 20px 0 10px 0; +` diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/styled.ts index 2e1fd2be47..20aefe2d36 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/styled.ts +++ b/apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/styled.ts @@ -44,6 +44,11 @@ export const SettingsAction = styled.div` padding: 5px; box-sizing: content-box; + > a { + text-decoration: none; + color: inherit; + } + &:hover { text-decoration: underline; } diff --git a/libs/tokens/src/hooks/tokens/useSearchToken.ts b/libs/tokens/src/hooks/tokens/useSearchToken.ts index 8f3ecbd805..70b9a4fa75 100644 --- a/libs/tokens/src/hooks/tokens/useSearchToken.ts +++ b/libs/tokens/src/hooks/tokens/useSearchToken.ts @@ -1,4 +1,4 @@ -import { useMemo } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useAtomValue } from 'jotai' import { activeTokensAtom, inactiveTokensAtom } from '../../state/tokens/allTokensAtom' import { useDebounce } from '@cowprotocol/common-hooks' @@ -45,9 +45,12 @@ const emptyFromListsResult: FromListsResult = { tokensFromActiveLists: [], token */ export function useSearchToken(input: string | null): TokenSearchResponse { const inputLowerCase = input?.toLowerCase() + const [isLoading, setIsLoading] = useState(false) const debouncedInputInList = useDebounce(inputLowerCase, IN_LISTS_DEBOUNCE_TIME) const debouncedInputInExternals = useDebounce(inputLowerCase, IN_EXTERNALS_DEBOUNCE_TIME) + const isInputStale = debouncedInputInExternals !== inputLowerCase + // Search in active and inactive lists const { tokensFromActiveLists, tokensFromInactiveLists } = useSearchTokensInLists(debouncedInputInList) @@ -67,6 +70,26 @@ export function useSearchToken(input: string | null): TokenSearchResponse { isTokenAlreadyFoundByAddress ) + useEffect(() => { + setIsLoading(true) + }, [input]) + + useEffect(() => { + // When there are results from toke lists, then we don't need to wait for the rest + if (tokensFromActiveLists.length || tokensFromInactiveLists.length) { + setIsLoading(false) + return + } + + // Change loading state only when input is not stale + if (isInputStale) return + + // Loading is finished when all sources are loaded + if (!apiIsLoading && !blockchainIsLoading) { + setIsLoading(false) + } + }, [isInputStale, apiIsLoading, blockchainIsLoading, tokensFromActiveLists, tokensFromInactiveLists]) + return useMemo(() => { if (!debouncedInputInList) { return emptyResponse @@ -75,7 +98,7 @@ export function useSearchToken(input: string | null): TokenSearchResponse { if (isTokenAlreadyFoundByAddress) { return { ...emptyResponse, - isLoading: apiIsLoading || blockchainIsLoading, + isLoading, activeListsResult: tokensFromActiveLists, } } @@ -88,25 +111,25 @@ export function useSearchToken(input: string | null): TokenSearchResponse { const filterFoundTokens = (token: TokenWithLogo) => !foundTokens[token.address.toLowerCase()] const inactiveListsResult = tokensFromInactiveLists.filter(filterFoundTokens) - const blockchainResult = tokenFromBlockChain ? [tokenFromBlockChain] : [] - const externalApiResult = apiResultTokens ? apiResultTokens.filter(filterFoundTokens) : [] + const blockchainResult = !isInputStale && tokenFromBlockChain ? [tokenFromBlockChain] : [] + const externalApiResult = !isInputStale && apiResultTokens ? apiResultTokens.filter(filterFoundTokens) : [] return { - isLoading: apiIsLoading || blockchainIsLoading, + isLoading, activeListsResult: tokensFromActiveLists, inactiveListsResult, blockchainResult, externalApiResult, } }, [ + isInputStale, + isLoading, debouncedInputInList, isTokenAlreadyFoundByAddress, tokensFromActiveLists, tokensFromInactiveLists, apiResultTokens, tokenFromBlockChain, - apiIsLoading, - blockchainIsLoading, ]) } From d5ef0a8898e58f8f85d2f0709a24eae513fe00b7 Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:32:23 +0000 Subject: [PATCH 12/15] feat(wallets): connect wallet rounded modal (#3368) --- apps/cowswap-frontend/src/common/pure/Modal/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/cowswap-frontend/src/common/pure/Modal/index.tsx b/apps/cowswap-frontend/src/common/pure/Modal/index.tsx index bc01d75991..3eece7dd46 100644 --- a/apps/cowswap-frontend/src/common/pure/Modal/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/Modal/index.tsx @@ -94,6 +94,8 @@ export const CowModal = styled(Modal)<{ border?: string padding?: string }>` + border-radius: var(${UI.BORDER_RADIUS_NORMAL}); + > [data-reach-dialog-content] { color: var(${UI.COLOR_TEXT1}); width: 100%; @@ -151,4 +153,4 @@ export const CowModal = styled(Modal)<{ export const NewCowModal = styled(Modal)` width: 100vw; height: 100vh; -` \ No newline at end of file +` From c36beb61b1426b20ea15eee07ff8c0300e002265 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 9 Nov 2023 18:05:17 +0000 Subject: [PATCH 13/15] Include widget metadata (#3371) * feat(widget): include widget meta-data * feat: make sure appData and widget values are correct * feat: pass the environment instead of using it from the utils * chore: update version in appCodes * fix: fix tests * chore: refactor name * docs: add documentation * fix: broken build --- apps/cowswap-frontend/.env | 14 ++--- .../src/common/hooks/useAnalyticsReporter.ts | 2 +- .../src/modules/appData/hooks.ts | 10 +--- .../src/modules/appData/types.tsx | 2 + .../appData/updater/AppDataInfoUpdater.ts | 23 +++++--- .../appData/updater/AppDataUpdater.tsx | 14 ++++- .../src/modules/appData/utils/buildAppData.ts | 16 ++++-- .../src/modules/appData/utils/fullAppData.ts | 2 +- .../hooks/useAppCodeWidgetAware.ts | 57 +++++++++++++++++++ .../hooks/useInjectedWidgetMetaData.ts | 12 +++- .../state/injectedWidgetMetaDataAtom.ts | 6 +- .../useTradeQuotePolling.test.tsx.snap | 4 +- .../hooks/useTradeQuotePolling.test.tsx | 2 +- .../createTwapOrderTxs.test.ts.snap | 6 +- libs/common-const/src/networks.ts | 2 - package.json | 2 +- yarn.lock | 8 +-- 17 files changed, 129 insertions(+), 53 deletions(-) create mode 100644 apps/cowswap-frontend/src/modules/injectedWidget/hooks/useAppCodeWidgetAware.ts diff --git a/apps/cowswap-frontend/.env b/apps/cowswap-frontend/.env index fb8bb546f2..571fbd81c6 100644 --- a/apps/cowswap-frontend/.env +++ b/apps/cowswap-frontend/.env @@ -56,13 +56,13 @@ # To set your own `AppData`, change `REACT_APP_FULL_APP_DATA_` # AppData, build yours at https://explorer.cow.fi/appdata -REACT_APP_FULL_APP_DATA_PRODUCTION='{"version":"0.10.0","appCode":"CoW Swap","environment":"production","metadata":{}}' -REACT_APP_FULL_APP_DATA_ENS='{"version":"0.10.0","appCode":"CoW Swap","environment":"ens","metadata":{}}' -REACT_APP_FULL_APP_DATA_BARN='{"version":"0.10.0","appCode":"CoW Swap","environment":"barn","metadata":{}}' -REACT_APP_FULL_APP_DATA_STAGING='{"version":"0.10.0","appCode":"CoW Swap","environment":"staging","metadata":{}}' -REACT_APP_FULL_APP_DATA_PR='{"version":"0.10.0","appCode":"CoW Swap","environment":"pr","metadata":{}}' -REACT_APP_FULL_APP_DATA_DEVELOPMENT='{"version":"0.10.0","appCode":"CoW Swap","environment":"development","metadata":{}}' -REACT_APP_FULL_APP_DATA_LOCAL='{"version":"0.10.0","appCode":"CoW Swap","environment":"local","metadata":{}}' +REACT_APP_FULL_APP_DATA_PRODUCTION='{"version":"0.11.0","appCode":"CoW Swap","environment":"production","metadata":{}}' +REACT_APP_FULL_APP_DATA_ENS='{"version":"0.11.0","appCode":"CoW Swap","environment":"ens","metadata":{}}' +REACT_APP_FULL_APP_DATA_BARN='{"version":"0.11.0","appCode":"CoW Swap","environment":"barn","metadata":{}}' +REACT_APP_FULL_APP_DATA_STAGING='{"version":"0.11.0","appCode":"CoW Swap","environment":"staging","metadata":{}}' +REACT_APP_FULL_APP_DATA_PR='{"version":"0.11.0","appCode":"CoW Swap","environment":"pr","metadata":{}}' +REACT_APP_FULL_APP_DATA_DEVELOPMENT='{"version":"0.11.0","appCode":"CoW Swap","environment":"development","metadata":{}}' +REACT_APP_FULL_APP_DATA_LOCAL='{"version":"0.11.0","appCode":"CoW Swap","environment":"local","metadata":{}}' diff --git a/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts b/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts index c97e876405..947cde1b6d 100644 --- a/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts +++ b/apps/cowswap-frontend/src/common/hooks/useAnalyticsReporter.ts @@ -76,7 +76,7 @@ export function useAnalyticsReporter() { const walletName = _walletName || getConnectionName(connection.type, isMetaMask) - const injectedWidgetAppId = injectedWidgetMetaData.appCode + const injectedWidgetAppId = injectedWidgetMetaData?.appCode useEffect(() => { // Custom dimension 2 - walletname diff --git a/apps/cowswap-frontend/src/modules/appData/hooks.ts b/apps/cowswap-frontend/src/modules/appData/hooks.ts index a63a57a373..05418a075a 100644 --- a/apps/cowswap-frontend/src/modules/appData/hooks.ts +++ b/apps/cowswap-frontend/src/modules/appData/hooks.ts @@ -2,11 +2,8 @@ import { useAtomValue, useSetAtom } from 'jotai' import { useMemo } from 'react' import { DEFAULT_APP_CODE, SAFE_APP_CODE } from '@cowprotocol/common-const' -import { isInjectedWidget } from '@cowprotocol/common-utils' import { useIsSafeApp } from '@cowprotocol/wallet' -import { useInjectedWidgetMetaData } from 'modules/injectedWidget' - import { addAppDataToUploadQueueAtom, appDataHooksAtom, appDataInfoAtom } from './state/atoms' import { AppDataInfo } from './types' @@ -17,21 +14,16 @@ export function useAppData(): AppDataInfo | null { } export function useAppCode(): string | null { - const injectedWidgetMetaData = useInjectedWidgetMetaData() const isSafeApp = useIsSafeApp() return useMemo(() => { - if (isInjectedWidget()) { - return injectedWidgetMetaData.appCode - } - if (APP_CODE) { // appCode coming from env var has priority return APP_CODE } return isSafeApp ? SAFE_APP_CODE : DEFAULT_APP_CODE - }, [isSafeApp, injectedWidgetMetaData]) + }, [isSafeApp]) } export function useUploadAppData() { diff --git a/apps/cowswap-frontend/src/modules/appData/types.tsx b/apps/cowswap-frontend/src/modules/appData/types.tsx index 521c348b62..c25a9f6592 100644 --- a/apps/cowswap-frontend/src/modules/appData/types.tsx +++ b/apps/cowswap-frontend/src/modules/appData/types.tsx @@ -38,3 +38,5 @@ export type PreHooks = latest.PreHooks export type PostHooks = latest.PostHooks export type AppDataRootSchema = latest.AppDataRootSchema + +export type AppDataWidget = latest.Widget diff --git a/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts b/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts index ebf39e6109..ad37957c69 100644 --- a/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts +++ b/apps/cowswap-frontend/src/modules/appData/updater/AppDataInfoUpdater.ts @@ -3,15 +3,16 @@ import { useEffect, useRef } from 'react' import { CowEnv, SupportedChainId } from '@cowprotocol/cow-sdk' +import { AppCodeWithWidgetMetadata } from 'modules/injectedWidget/hooks/useAppCodeWidgetAware' import { UtmParams } from 'modules/utm' -import { useAppCode } from '../hooks' import { appDataInfoAtom } from '../state/atoms' import { AppDataHooks, AppDataOrderClass } from '../types' import { buildAppData, BuildAppDataParams } from '../utils/buildAppData' import { getAppData } from '../utils/fullAppData' export type UseAppDataParams = { + appCodeWithWidgetMetadata: AppCodeWithWidgetMetadata | null chainId: SupportedChainId slippageBips: string orderClass: AppDataOrderClass @@ -23,23 +24,28 @@ export type UseAppDataParams = { * Fetches and updates appDataInfo whenever a dependency changes * The hook can be called only from an updater */ -export function AppDataInfoUpdater({ chainId, slippageBips, orderClass, utm, hooks }: UseAppDataParams): void { +export function AppDataInfoUpdater({ + appCodeWithWidgetMetadata, + chainId, + slippageBips, + orderClass, + utm, + hooks, +}: UseAppDataParams): void { // AppDataInfo, from Jotai const setAppDataInfo = useSetAtom(appDataInfoAtom) - // AppCode is dynamic and based on how it's loaded (if used as a Gnosis Safe app) - const appCode = useAppCode() - const updateAppDataPromiseRef = useRef(Promise.resolve()) useEffect(() => { - if (!appCode) { + if (!appCodeWithWidgetMetadata) { // reset values when there is no price estimation or network changes setAppDataInfo(null) return } - const params: BuildAppDataParams = { chainId, slippageBips, appCode, orderClass, utm, hooks } + const { appCode, environment, widget } = appCodeWithWidgetMetadata + const params: BuildAppDataParams = { chainId, slippageBips, appCode, environment, orderClass, utm, hooks, widget } const updateAppData = async (): Promise => { try { @@ -55,8 +61,9 @@ export function AppDataInfoUpdater({ chainId, slippageBips, orderClass, utm, hoo // Chain the next update to avoid race conditions updateAppDataPromiseRef.current = updateAppDataPromiseRef.current.finally(updateAppData) - }, [appCode, chainId, setAppDataInfo, slippageBips, orderClass, utm, hooks]) + }, [appCodeWithWidgetMetadata, chainId, setAppDataInfo, slippageBips, orderClass, utm, hooks]) } + function getEnvByClass(orderClass: string): CowEnv | undefined { if (orderClass === 'twap') { return 'prod' // Upload the appData to production always, since WatchTower will create the orders there diff --git a/apps/cowswap-frontend/src/modules/appData/updater/AppDataUpdater.tsx b/apps/cowswap-frontend/src/modules/appData/updater/AppDataUpdater.tsx index 481b8b96ae..f3d88aee19 100644 --- a/apps/cowswap-frontend/src/modules/appData/updater/AppDataUpdater.tsx +++ b/apps/cowswap-frontend/src/modules/appData/updater/AppDataUpdater.tsx @@ -4,12 +4,13 @@ import { percentToBips } from '@cowprotocol/common-utils' import { useWalletInfo } from '@cowprotocol/wallet' import { Percent } from '@uniswap/sdk-core' +import { useAppCodeWidgetAware } from 'modules/injectedWidget/hooks/useAppCodeWidgetAware' import { useUtm } from 'modules/utm' import { AppDataHooksUpdater } from './AppDataHooksUpdater' import { AppDataInfoUpdater, UseAppDataParams } from './AppDataInfoUpdater' -import { useAppDataHooks } from '../hooks' +import { useAppCode, useAppDataHooks } from '../hooks' import { AppDataOrderClass } from '../types' interface AppDataUpdaterProps { @@ -20,14 +21,23 @@ interface AppDataUpdaterProps { export const AppDataUpdater = React.memo(({ slippage, orderClass }: AppDataUpdaterProps) => { const { chainId } = useWalletInfo() + const appCode = useAppCode() const slippageBips = percentToBips(slippage) const utm = useUtm() const hooks = useAppDataHooks() + const appCodeWithWidgetMetadata = useAppCodeWidgetAware(appCode) if (!chainId) return null return ( - + ) }) diff --git a/apps/cowswap-frontend/src/modules/appData/utils/buildAppData.ts b/apps/cowswap-frontend/src/modules/appData/utils/buildAppData.ts index 0239246bd0..3326dc8c5e 100644 --- a/apps/cowswap-frontend/src/modules/appData/utils/buildAppData.ts +++ b/apps/cowswap-frontend/src/modules/appData/utils/buildAppData.ts @@ -1,5 +1,4 @@ import { stringifyDeterministic } from '@cowprotocol/app-data' -import { environmentName } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { metadataApiSDK } from 'cowSdk' @@ -7,16 +6,18 @@ import { keccak256, toUtf8Bytes } from 'ethers/lib/utils' import { UtmParams } from 'modules/utm' -import { AppDataHooks, AppDataInfo, AppDataOrderClass, AppDataRootSchema } from '../types' +import { AppDataHooks, AppDataInfo, AppDataOrderClass, AppDataRootSchema, AppDataWidget } from '../types' export type BuildAppDataParams = { appCode: string + environment?: string chainId: SupportedChainId slippageBips: string orderClass: AppDataOrderClass referrerAccount?: string utm: UtmParams | undefined hooks?: AppDataHooks + widget?: AppDataWidget } async function generateAppDataFromDoc( @@ -32,9 +33,11 @@ export async function buildAppData({ slippageBips, referrerAccount, appCode, + environment, orderClass: orderClassName, utm, hooks, + widget, }: BuildAppDataParams): Promise { const referrerParams = referrerAccount && chainId === SupportedChainId.MAINNET ? { address: referrerAccount } : undefined @@ -44,8 +47,8 @@ export async function buildAppData({ const doc = await metadataApiSDK.generateAppDataDoc({ appCode, - environment: environmentName, - metadata: { referrer: referrerParams, quote: quoteParams, orderClass, utm, hooks }, + environment, + metadata: { referrer: referrerParams, quote: quoteParams, orderClass, utm, hooks, widget }, }) const { fullAppData, appDataKeccak256 } = await generateAppDataFromDoc(doc) @@ -57,7 +60,10 @@ export function toKeccak256(fullAppData: string) { return keccak256(toUtf8Bytes(fullAppData)) } -export async function updateHooksOnAppData(appData: AppDataInfo, hooks: AppDataHooks | undefined): Promise { +export async function updateHooksOnAppData( + appData: AppDataInfo, + hooks: AppDataHooks | undefined +): Promise { const { doc } = appData const newDoc = { diff --git a/apps/cowswap-frontend/src/modules/appData/utils/fullAppData.ts b/apps/cowswap-frontend/src/modules/appData/utils/fullAppData.ts index 7ec406fb81..998a8928a4 100644 --- a/apps/cowswap-frontend/src/modules/appData/utils/fullAppData.ts +++ b/apps/cowswap-frontend/src/modules/appData/utils/fullAppData.ts @@ -3,7 +3,7 @@ import { EnvironmentName, environmentName } from '@cowprotocol/common-utils' import { AppDataInfo } from '../types' import { toKeccak256 } from '../utils/buildAppData' -const DEFAULT_FULL_APP_DATA = '{"version":"0.10.0","appCode":"CoW Swap","metadata":{}}' +const DEFAULT_FULL_APP_DATA = '{"version":"0.11.0","appCode":"CoW Swap","metadata":{}}' let appData: AppDataInfo = (() => { const fullAppData = getFullAppDataByEnv(environmentName) diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useAppCodeWidgetAware.ts b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useAppCodeWidgetAware.ts new file mode 100644 index 0000000000..821819d8a5 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useAppCodeWidgetAware.ts @@ -0,0 +1,57 @@ +import { useMemo } from 'react' + +import { environmentName } from '@cowprotocol/common-utils' + +import { AppDataWidget } from 'modules/appData/types' + +import { useInjectedWidgetMetaData } from './useInjectedWidgetMetaData' + +export interface AppCodeWithWidgetMetadata { + appCode: string + environment?: string + widget?: AppDataWidget +} + +/** + * The final "appCode" used in the appData is different depending on whether the app is being used in the widget mode or not. + * + * In order to make the official `appCode` of the app not widget aware (to depend only on the appCode of the app), we use this + * widget that will use the official `appCode` and the injected metadata from the widget to derive the 3 depending filds: + * - appCode: the official `appCode` of the app when not used in the widget mode, or the `appCode` of the host app using the widget + * - environment: the environment of the app when not used in the widget mode, or not specified in widget mode + * - widget: The widget metadata if in widget mode + * + * @param appCodeOfficial the official `appCode` of the app + * + */ +export function useAppCodeWidgetAware(appCodeOfficial: string | null): AppCodeWithWidgetMetadata | null { + const injectedWidgetMetadata = useInjectedWidgetMetaData() + + const appCodeInjectedHostApp = injectedWidgetMetadata?.appCode + + return useMemo(() => { + // appCodeOfficial is required for generating the appData, if not provided, return null + if (!appCodeOfficial) { + return null + } + + // If running in widget mode, and the host app injects us an appCode + if (appCodeInjectedHostApp) { + return { + // The main appCode will be the one of the host app that uses the widget + appCode: appCodeInjectedHostApp, + widget: { + // In the widget appCode we include the official appCode and environment (this way we report which app is backing the widget iframe) + appCode: appCodeOfficial, + environment: environmentName, + }, + } + } + + // Return the official appCode and environment as the main appCode/environment in the appData + return { + appCode: appCodeOfficial, + environment: environmentName, + } + }, [appCodeOfficial, appCodeInjectedHostApp]) +} diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetMetaData.ts b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetMetaData.ts index d17e901912..6bdeb60fac 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetMetaData.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/hooks/useInjectedWidgetMetaData.ts @@ -1,7 +1,15 @@ import { useAtomValue } from 'jotai' +import { isInjectedWidget } from '@cowprotocol/common-utils' + import { CowSwapWidgetMetaData, injectedWidgetMetaDataAtom } from '../state/injectedWidgetMetaDataAtom' -export function useInjectedWidgetMetaData(): CowSwapWidgetMetaData { - return useAtomValue(injectedWidgetMetaDataAtom) +export function useInjectedWidgetMetaData(): CowSwapWidgetMetaData | undefined { + const injectedWidgetMetaData = useAtomValue(injectedWidgetMetaDataAtom) + + if (!isInjectedWidget()) { + return undefined + } + + return injectedWidgetMetaData } diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts index 2f488974e3..27dc739f96 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/state/injectedWidgetMetaDataAtom.ts @@ -4,8 +4,4 @@ export interface CowSwapWidgetMetaData { appCode: string } -const DEFAULT_INJECTED_WIDGET_META_DATA: CowSwapWidgetMetaData = { - appCode: 'DEFAULT_INJECTED_WIDGET', // FIXME: there shouldn't be a default. If it's not set, we should have a banner or something -} - -export const injectedWidgetMetaDataAtom = atom(DEFAULT_INJECTED_WIDGET_META_DATA) +export const injectedWidgetMetaDataAtom = atom(undefined) diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/__snapshots__/useTradeQuotePolling.test.tsx.snap b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/__snapshots__/useTradeQuotePolling.test.tsx.snap index 5fbfc08a1a..4d375e5b3f 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/__snapshots__/useTradeQuotePolling.test.tsx.snap +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/__snapshots__/useTradeQuotePolling.test.tsx.snap @@ -2,7 +2,7 @@ exports[`useTradeQuotePolling() When wallet is NOT connected Then the "useAddress" field in the quote request should be 0x000...0000 1`] = ` { - "appData": "0x67e14fb94bf7d70b1d7822a861a33c0ca9d7d2db930869f1c65bf32176d95dad", + "appData": "0x93782141af03a1e83fd03bd86bd8de09f12030aa078e49abe02ad0b15c565d1b", "appDataHash": undefined, "buyToken": "0x91056D4A53E1faa1A84306D4deAEc71085394bC8", "from": "0x0000000000000000000000000000000000000000", @@ -18,7 +18,7 @@ exports[`useTradeQuotePolling() When wallet is NOT connected Then the "useAddres exports[`useTradeQuotePolling() When wallet is connected Then should put account address into "useAddress" field in the quote request 1`] = ` { - "appData": "0x67e14fb94bf7d70b1d7822a861a33c0ca9d7d2db930869f1c65bf32176d95dad", + "appData": "0x93782141af03a1e83fd03bd86bd8de09f12030aa078e49abe02ad0b15c565d1b", "appDataHash": undefined, "buyToken": "0x91056D4A53E1faa1A84306D4deAEc71085394bC8", "from": "0x333333f332a06ecb5d20d35da44ba07986d6e203", diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.test.tsx b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.test.tsx index 4961fa1015..9cde44db4c 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.test.tsx +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.test.tsx @@ -60,7 +60,7 @@ describe('useTradeQuotePolling()', () => { jest.clearAllMocks() getQuoteMock.mockImplementation(() => new Promise(() => void 0)) - useEnoughBalanceAndAllowanceMock.mockReturnValue(true) + useEnoughBalanceAndAllowanceMock.mockReturnValue({ enoughBalance: true, enoughAllowance: true }) }) describe('When wallet is connected', () => { diff --git a/apps/cowswap-frontend/src/modules/twap/services/__snapshots__/createTwapOrderTxs.test.ts.snap b/apps/cowswap-frontend/src/modules/twap/services/__snapshots__/createTwapOrderTxs.test.ts.snap index 729e4535ec..1e24111764 100644 --- a/apps/cowswap-frontend/src/modules/twap/services/__snapshots__/createTwapOrderTxs.test.ts.snap +++ b/apps/cowswap-frontend/src/modules/twap/services/__snapshots__/createTwapOrderTxs.test.ts.snap @@ -7,7 +7,7 @@ exports[`Create TWAP order When sell token is NOT approved AND token needs zero { "handler": "0x6cF1e9cA41f7611dEf408122793c358a3d11E5a5", "salt": "0x00000000000000000000000000000000000000000000000000000015c90b9b2a", - "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000067e14fb94bf7d70b1d7822a861a33c0ca9d7d2db930869f1c65bf32176d95dad", + "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000093782141af03a1e83fd03bd86bd8de09f12030aa078e49abe02ad0b15c565d1b", }, "0x52eD56Da04309Aca4c3FECC595298d80C2f16BAc", "0x", @@ -66,7 +66,7 @@ exports[`Create TWAP order When sell token is NOT approved, then should generate { "handler": "0x6cF1e9cA41f7611dEf408122793c358a3d11E5a5", "salt": "0x00000000000000000000000000000000000000000000000000000015c90b9b2a", - "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000067e14fb94bf7d70b1d7822a861a33c0ca9d7d2db930869f1c65bf32176d95dad", + "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000093782141af03a1e83fd03bd86bd8de09f12030aa078e49abe02ad0b15c565d1b", }, "0x52eD56Da04309Aca4c3FECC595298d80C2f16BAc", "0x", @@ -109,7 +109,7 @@ exports[`Create TWAP order When sell token is approved, then should generate onl { "handler": "0x6cF1e9cA41f7611dEf408122793c358a3d11E5a5", "salt": "0x00000000000000000000000000000000000000000000000000000015c90b9b2a", - "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000067e14fb94bf7d70b1d7822a861a33c0ca9d7d2db930869f1c65bf32176d95dad", + "staticInput": "0x00000000000000000000000091056d4a53e1faa1a84306d4deaec71085394bc8000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d600000000000000000000000000000000000000000000000000000007c2d24d55000000000000000000000000000000000000000000000000000000000001046a00000000000000000000000000000000000000000000000000000000646b782c00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000093782141af03a1e83fd03bd86bd8de09f12030aa078e49abe02ad0b15c565d1b", }, "0x52eD56Da04309Aca4c3FECC595298d80C2f16BAc", "0x", diff --git a/libs/common-const/src/networks.ts b/libs/common-const/src/networks.ts index d3a0cc8189..fd588f6a30 100644 --- a/libs/common-const/src/networks.ts +++ b/libs/common-const/src/networks.ts @@ -41,5 +41,3 @@ function getRpcUrl(chainId: SupportedChainId): string { return defaultRpc.url } - -console.log('RPC_URLS', RPC_URLS) diff --git a/package.json b/package.json index 8f7617f3a4..264f9c68f1 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@1inch/permit-signed-approvals-utils": "^1.4.10", "@babel/runtime": "^7.17.0", "@coinbase/wallet-sdk": "^3.3.0", - "@cowprotocol/app-data": "^1.1.0", + "@cowprotocol/app-data": "^1.2.0", "@cowprotocol/contracts": "^1.3.1", "@cowprotocol/cow-runner-game": "^0.2.9", "@cowprotocol/cow-sdk": "^3.0.0-rc.0", diff --git a/yarn.lock b/yarn.lock index 08649d4fe2..0938ec129d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1444,10 +1444,10 @@ dependencies: chalk "^4.1.0" -"@cowprotocol/app-data@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-1.1.0.tgz#38d91a79388220bc1ff99d2b6d0d3e3cce38e8f9" - integrity sha512-r55wyVrVnyq32KcswGN+1q5jTIfkLq4NlhibLWpe8px05lqvHA/RXRLO8lTkK7WFtlbO4iSnQqkIs4wqLc80kg== +"@cowprotocol/app-data@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-1.2.0.tgz#ca181a8aaa336eb5411aa0e40720f1b4c42955f1" + integrity sha512-gXuKoB5o00SLF6CEfERnspusKJWx7wKKMQtYbZB9r61n/I8o+bEIuYgKO+oXLqBQwheOjZzPW/oAjuVrDpBkkg== dependencies: ajv "^8.11.0" cross-fetch "^3.1.5" From c4ba4bb2014291454bcae7d0f2a3f4d99b74a3a5 Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:11:18 +0000 Subject: [PATCH 14/15] feat: iterate on configurator (#3364) * feat: iterate on configurator * feat: move embed code button (#3365) * feat: move embed code button * feat: add snackbar click to copy (#3366) * feat: add snackbar click to copy * feat: snackbar set timeout to 3s * feat: revert click away and font fix * feat: embed code button styling * feat: embed code button refactor * feat: remove comments --- apps/widget-configurator/src/app/analytics.ts | 6 +- .../configurator/hooks/useEmbedDialogState.ts | 14 +++ .../src/app/configurator/index.tsx | 112 ++++++++++++------ .../src/app/configurator/styled.ts | 25 +--- .../src/app/embedDialog/index.tsx | 54 +++++---- apps/widget-configurator/src/main.tsx | 23 ++++ .../src/theme/commonTypography.ts | 1 + 7 files changed, 153 insertions(+), 82 deletions(-) create mode 100644 apps/widget-configurator/src/app/configurator/hooks/useEmbedDialogState.ts diff --git a/apps/widget-configurator/src/app/analytics.ts b/apps/widget-configurator/src/app/analytics.ts index 6504d10783..990d6393c1 100644 --- a/apps/widget-configurator/src/app/analytics.ts +++ b/apps/widget-configurator/src/app/analytics.ts @@ -1,20 +1,20 @@ import { sendEvent, Category } from '@cowprotocol/analytics' -export function connectWalletToConfigurator() { +export function connectWalletToConfiguratorGA() { sendEvent({ category: Category.WIDGET_CONFIGURATOR, action: 'Connect wallet', }) } -export function viewEmbedCode() { +export function viewEmbedCodeGA() { sendEvent({ category: Category.WIDGET_CONFIGURATOR, action: 'View code', }) } -export function copyEmbedCode() { +export function copyEmbedCodeGA() { sendEvent({ category: Category.WIDGET_CONFIGURATOR, action: 'Copy code', diff --git a/apps/widget-configurator/src/app/configurator/hooks/useEmbedDialogState.ts b/apps/widget-configurator/src/app/configurator/hooks/useEmbedDialogState.ts new file mode 100644 index 0000000000..9c2b18c0af --- /dev/null +++ b/apps/widget-configurator/src/app/configurator/hooks/useEmbedDialogState.ts @@ -0,0 +1,14 @@ +import { useState } from 'react' + +export function useEmbedDialogState(initialOpen = false) { + const [open, setOpen] = useState(initialOpen) + + const handleOpen = () => setOpen(true) + const handleClose = () => setOpen(false) + + return { + dialogOpen: open, + handleDialogClose: handleClose, + handleDialogOpen: handleOpen, + } +} diff --git a/apps/widget-configurator/src/app/configurator/index.tsx b/apps/widget-configurator/src/app/configurator/index.tsx index 531d044451..476cbf28ad 100644 --- a/apps/widget-configurator/src/app/configurator/index.tsx +++ b/apps/widget-configurator/src/app/configurator/index.tsx @@ -3,10 +3,19 @@ import { useContext, useEffect, useState } from 'react' import { TradeType } from '@cowprotocol/widget-lib' import { CowSwapWidget } from '@cowprotocol/widget-react' +import ChromeReaderModeIcon from '@mui/icons-material/ChromeReaderMode' +import CodeIcon from '@mui/icons-material/Code' +import EditIcon from '@mui/icons-material/Edit' +import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft' +import LanguageIcon from '@mui/icons-material/Language' import Box from '@mui/material/Box' import Divider from '@mui/material/Divider' import Drawer from '@mui/material/Drawer' -import Link from '@mui/material/Link' +import Fab from '@mui/material/Fab' +import List from '@mui/material/List' +import ListItemButton from '@mui/material/ListItemButton' +import ListItemIcon from '@mui/material/ListItemIcon' +import ListItemText from '@mui/material/ListItemText' import Typography from '@mui/material/Typography' import { useAccount, useNetwork } from 'wagmi' @@ -16,22 +25,16 @@ import { CurrentTradeTypeControl } from './controls/CurrentTradeTypeControl' import { NetworkControl, NetworkOption, NetworkOptions } from './controls/NetworkControl' import { ThemeControl } from './controls/ThemeControl' import { TradeModesControl } from './controls/TradeModesControl' +import { useEmbedDialogState } from './hooks/useEmbedDialogState' import { useProvider } from './hooks/useProvider' import { useSyncWidgetNetwork } from './hooks/useSyncWidgetNetwork' import { useWidgetParamsAndSettings } from './hooks/useWidgetParamsAndSettings' -import { - ContentStyled, - DrawerStyled, - LinksWrapper, - ShowDrawerButton, - WalletConnectionWrapper, - WrapperStyled, -} from './styled' +import { ContentStyled, DrawerStyled, WalletConnectionWrapper, WrapperStyled } from './styled' import { ConfiguratorState } from './types' import { ColorModeContext } from '../../theme/ColorModeContext' import { web3Modal } from '../../wagmiConfig' -import { connectWalletToConfigurator } from '../analytics' +import { connectWalletToConfiguratorGA } from '../analytics' import { EmbedDialog } from '../embedDialog' const DEFAULT_STATE = { @@ -67,6 +70,14 @@ export function Configurator({ title }: { title: string }) { const [buyToken] = buyTokenState const [buyTokenAmount] = buyTokenAmountState + const { dialogOpen, handleDialogClose, handleDialogOpen } = useEmbedDialogState() + + const LINKS = [ + { icon: , label: 'View embed code', onClick: () => handleDialogOpen() }, + { icon: , label: 'Widget web', url: `https://cow.fi/widget/?${UTM_PARAMS}` }, + { icon: , label: 'Developer docs', url: `https://docs.cow.fi/?${UTM_PARAMS}` }, + ] + const { isDisconnected, isConnected } = useAccount() const network = useNetwork() @@ -98,40 +109,36 @@ export function Configurator({ title }: { title: string }) { // Fire an event to GA when user connect a wallet useEffect(() => { if (isConnected) { - connectWalletToConfigurator() + connectWalletToConfiguratorGA() } }, [isConnected]) return ( {!isDrawerOpen && ( - + { + e.stopPropagation() + setIsDrawerOpen(true) + }} + style={{ position: 'fixed', bottom: '1.6rem', left: '1.6rem' }} + > + + )} + - + {title} -
- - Website - - {/*TODO: add link to the widget page docs*/} - - Docs - -
- - Wallet -
- General - @@ -150,22 +157,59 @@ export function Configurator({ title }: { title: string }) { - + {isDrawerOpen && ( + setIsDrawerOpen(false)} + style={{ position: 'fixed', top: '1.3rem', left: '26.7rem' }} + > + + + )} - setIsDrawerOpen(false)}> - Hide drawer - + + {LINKS.map(({ label, icon, url, onClick }) => ( + + {icon} + + + ))} +
{params && ( <> - +
)}
+ + handleDialogOpen()} + > + + View Embed Code +
) } diff --git a/apps/widget-configurator/src/app/configurator/styled.ts b/apps/widget-configurator/src/app/configurator/styled.ts index b7a683c4b2..92321ff78a 100644 --- a/apps/widget-configurator/src/app/configurator/styled.ts +++ b/apps/widget-configurator/src/app/configurator/styled.ts @@ -1,14 +1,11 @@ -import { CSSProperties } from 'react' - import { Theme } from '@mui/material/styles' -import type { PaletteMode } from '@mui/material' - export const WrapperStyled = { display: 'flex', flexFlow: 'column wrap', width: '100%' } export const DrawerStyled = (theme: Theme) => ({ width: '29rem', flexShrink: 0, + '& .MuiDrawer-paper': { width: '29rem', boxSizing: 'border-box', @@ -42,23 +39,5 @@ export const ContentStyled = { export const WalletConnectionWrapper = { display: 'flex', justifyContent: 'center', -} - -export const ShowDrawerButton: (mode: PaletteMode) => CSSProperties = (mode: PaletteMode) => ({ - borderRadius: '50%', - width: '60px', - height: '60px', - position: 'fixed', - left: '20px', - bottom: '20px', - background: mode === 'dark' ? 'rgb(63 162 255 / 71%)' : '#fff', - border: 0, - fontSize: '24px', - cursor: 'pointer', -}) - -export const LinksWrapper = { - display: 'flex', - justifyContent: 'center', - gap: '15px', + margin: '0 auto 1rem', } diff --git a/apps/widget-configurator/src/app/embedDialog/index.tsx b/apps/widget-configurator/src/app/embedDialog/index.tsx index af861a87bd..8d99ce1921 100644 --- a/apps/widget-configurator/src/app/embedDialog/index.tsx +++ b/apps/widget-configurator/src/app/embedDialog/index.tsx @@ -1,13 +1,15 @@ -import { SyntheticEvent, useEffect, useMemo, useRef, useState } from 'react' +import React, { SyntheticEvent, useEffect, useMemo, useRef, useState } from 'react' import { CowSwapWidgetProps } from '@cowprotocol/widget-react' +import MuiAlert, { AlertProps } from '@mui/material/Alert' import Box from '@mui/material/Box' import Button from '@mui/material/Button' import Dialog, { DialogProps } from '@mui/material/Dialog' import DialogActions from '@mui/material/DialogActions' import DialogContent from '@mui/material/DialogContent' import DialogTitle from '@mui/material/DialogTitle' +import Snackbar from '@mui/material/Snackbar' import Tab from '@mui/material/Tab' import Tabs from '@mui/material/Tabs' import SyntaxHighlighter from 'react-syntax-highlighter' @@ -18,7 +20,11 @@ import { reactExample } from './utils/reactExample' import { vanilaNpmExample } from './utils/vanilaNpmExample' import { vanillaNoDepsExample } from './utils/vanillaNoDepsExample' -import { copyEmbedCode, viewEmbedCode } from '../analytics' +import { copyEmbedCodeGA, viewEmbedCodeGA } from '../analytics' + +const Alert = React.forwardRef(function Alert(props, ref) { + return +}) function a11yProps(index: number) { return { @@ -29,28 +35,33 @@ function a11yProps(index: number) { export interface EmbedDialogProps { params: CowSwapWidgetProps['params'] + open: boolean + handleClose: () => void } -export function EmbedDialog({ params }: EmbedDialogProps) { - const [open, setOpen] = useState(false) +export function EmbedDialog({ params, open, handleClose }: EmbedDialogProps) { const [scroll, setScroll] = useState('paper') - const [currentTab, setCurrentTab] = useState(0) + const descriptionElementRef = useRef(null) - const handleClickOpen = (scrollType: DialogProps['scroll']) => () => { - setOpen(true) - setScroll(scrollType) - viewEmbedCode() + const [snackbarOpen, setSnackbarOpen] = useState(false) + const handleCopy = () => { + navigator.clipboard.writeText(code) + copyEmbedCodeGA() + setSnackbarOpen(true) } - const handleClose = () => { - setOpen(false) + const handleSnackbarClose = (event?: React.SyntheticEvent | Event, reason?: string) => { + if (reason === 'clickaway') { + return + } + setSnackbarOpen(false) } - const descriptionElementRef = useRef(null) - useEffect(() => { if (open) { + setScroll('paper') + viewEmbedCodeGA() const { current: descriptionElement } = descriptionElementRef if (descriptionElement !== null) { descriptionElement.focus() @@ -66,23 +77,16 @@ export function EmbedDialog({ params }: EmbedDialogProps) { return '' }, [currentTab, params]) - const handleCopy = () => { - navigator.clipboard.writeText(code) - copyEmbedCode() - } - return (
- - {/* */} Snippet for CoW Widget @@ -114,6 +118,12 @@ export function EmbedDialog({ params }: EmbedDialogProps) { + + + + Successfully copied to clipboard! + +
) } diff --git a/apps/widget-configurator/src/main.tsx b/apps/widget-configurator/src/main.tsx index cba050ffbe..564fe8c4cf 100644 --- a/apps/widget-configurator/src/main.tsx +++ b/apps/widget-configurator/src/main.tsx @@ -1,6 +1,7 @@ import { StrictMode, useMemo } from 'react' import { CssBaseline, GlobalStyles } from '@mui/material' +import 'inter-ui' import Box from '@mui/material/Box' import { createTheme, PaletteOptions, ThemeProvider } from '@mui/material/styles' import { createRoot } from 'react-dom/client' @@ -30,6 +31,28 @@ function Root() { return createTheme({ palette, typography: commonTypography, + components: { + MuiCssBaseline: { + styleOverrides: { + '@global': { + html: { + fontFamily: '"Inter var", "Inter", sans-serif', + }, + body: { + fontFamily: '"Inter var", "Inter", sans-serif', + }, + '@supports (font-variation-settings: normal)': { + html: { + fontFamily: '"Inter var", "Inter", sans-serif', + }, + body: { + fontFamily: '"Inter var", "Inter", sans-serif', + }, + }, + }, + }, + }, + }, }) }, [mode]) diff --git a/apps/widget-configurator/src/theme/commonTypography.ts b/apps/widget-configurator/src/theme/commonTypography.ts index c513bc4796..7678625399 100644 --- a/apps/widget-configurator/src/theme/commonTypography.ts +++ b/apps/widget-configurator/src/theme/commonTypography.ts @@ -1,6 +1,7 @@ import { TypographyOptions } from '@mui/material/styles/createTypography' export const commonTypography: TypographyOptions = { + fontFamily: '"Inter var", "Inter", sans-serif', htmlFontSize: 10, button: { textTransform: 'none' as const, From c202eb05243258810663c63421dd3c7b4929dd75 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Fri, 10 Nov 2023 00:30:59 +0600 Subject: [PATCH 15/15] chore(main): release 1.49.0 (#3373) --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2162f33d8b..ff91c1283a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # Changelog +## [1.49.0](https://github.com/cowprotocol/cowswap/compare/v1.48.17...v1.49.0) (2023-11-09) + + +### Features + +* add comment to config ([#3345](https://github.com/cowprotocol/cowswap/issues/3345)) ([1534611](https://github.com/cowprotocol/cowswap/commit/153461103342fbbaa84d340b4eb48ae94cc5b2d3)) +* change appKey into appCode ([#3361](https://github.com/cowprotocol/cowswap/issues/3361)) ([b9127d9](https://github.com/cowprotocol/cowswap/commit/b9127d93c05eaf942913065c008d839f694b2ae1)) +* change from node v16 to v18 ([#3350](https://github.com/cowprotocol/cowswap/issues/3350)) ([edc7de3](https://github.com/cowprotocol/cowswap/commit/edc7de3d5bb0e17088a58c266641022e79f0e346)) +* hide nose picker loader ([#3338](https://github.com/cowprotocol/cowswap/issues/3338)) ([4cc2f1a](https://github.com/cowprotocol/cowswap/commit/4cc2f1abdc8a2429f2772c2992d76036c8d9ac9e)) +* improve appCode, config and readme ([#3362](https://github.com/cowprotocol/cowswap/issues/3362)) ([c18bb2e](https://github.com/cowprotocol/cowswap/commit/c18bb2edc8f4f433a4ca0ef1be78e4222747fe8a)) +* iterate on configurator ([#3364](https://github.com/cowprotocol/cowswap/issues/3364)) ([c4ba4bb](https://github.com/cowprotocol/cowswap/commit/c4ba4bb2014291454bcae7d0f2a3f4d99b74a3a5)) +* **permit:** don't show gas-free flags when permit is not supported ([#3346](https://github.com/cowprotocol/cowswap/issues/3346)) ([1a56029](https://github.com/cowprotocol/cowswap/commit/1a560290b9fc82f3ba17ac1fd9ca9950da796661)) +* **permit:** load pre generated permit info ([#3316](https://github.com/cowprotocol/cowswap/issues/3316)) ([46943ad](https://github.com/cowprotocol/cowswap/commit/46943ad45eb58ec5819dc0d8932017144928c144)) +* **permit:** remove permit related feature flags ([#3320](https://github.com/cowprotocol/cowswap/issues/3320)) ([372b1a6](https://github.com/cowprotocol/cowswap/commit/372b1a67de71941f1deca46c0459cef2a32c536b)) +* **permit:** update gas-free flag tooltip ([#3332](https://github.com/cowprotocol/cowswap/issues/3332)) ([4caf929](https://github.com/cowprotocol/cowswap/commit/4caf9299c697ab838970cd23e4445397b091cf21)) +* simplify theme config ([#3363](https://github.com/cowprotocol/cowswap/issues/3363)) ([6b262ab](https://github.com/cowprotocol/cowswap/commit/6b262ab129cbfa62342b0a17ee2e67f26149f045)) +* **wallets:** connect wallet rounded modal ([#3368](https://github.com/cowprotocol/cowswap/issues/3368)) ([d5ef0a8](https://github.com/cowprotocol/cowswap/commit/d5ef0a8898e58f8f85d2f0709a24eae513fe00b7)) +* **widget-configurator:** button to show drawer ([#3324](https://github.com/cowprotocol/cowswap/issues/3324)) ([cbc1521](https://github.com/cowprotocol/cowswap/commit/cbc152108910f3d954aca10b5815dcf519aa78fd)) +* **widget-configurator:** google analytics events ([#3335](https://github.com/cowprotocol/cowswap/issues/3335)) ([9b1074e](https://github.com/cowprotocol/cowswap/commit/9b1074e021399d25e7f105c3228a26113dc2edf1)) +* **widget:** links to landing and docs ([#3359](https://github.com/cowprotocol/cowswap/issues/3359)) ([aeb4e11](https://github.com/cowprotocol/cowswap/commit/aeb4e111c7d047bd9607f1c5af3c4c21305f5096)) + + +### Bug Fixes + +* fix issue with RPC env ([#3353](https://github.com/cowprotocol/cowswap/issues/3353)) ([ede338d](https://github.com/cowprotocol/cowswap/commit/ede338d29279654873f0f8677d6e09ca2e41686f)) +* fix race condition with widget ([#3367](https://github.com/cowprotocol/cowswap/issues/3367)) ([43e2204](https://github.com/cowprotocol/cowswap/commit/43e22046de4ace3c85b9bb7d1a7409c2851fb395)) +* **tokens:** fix token lists styles ([#3360](https://github.com/cowprotocol/cowswap/issues/3360)) ([f9a4d0b](https://github.com/cowprotocol/cowswap/commit/f9a4d0b667c621f6f6692db926a727636b774511)) +* **tokens:** use default value for user-added tokens migration ([#3347](https://github.com/cowprotocol/cowswap/issues/3347)) ([c9e235c](https://github.com/cowprotocol/cowswap/commit/c9e235cdf466b80d9241099fcfd4cb7c6230ee89)) +* **twap:** fix infinite loop in orders hook ([#3348](https://github.com/cowprotocol/cowswap/issues/3348)) ([097728c](https://github.com/cowprotocol/cowswap/commit/097728c92b25f71c2a0ee0de4b56953c888afc0e)) +* update chainId in trade state ([#3340](https://github.com/cowprotocol/cowswap/issues/3340)) ([60a16ec](https://github.com/cowprotocol/cowswap/commit/60a16ec96987ed3e9c6115664dbe0eb164c4ddb7)) +* update connect wallet modal styles ([#3341](https://github.com/cowprotocol/cowswap/issues/3341)) ([e577b76](https://github.com/cowprotocol/cowswap/commit/e577b766a3e876e8caf167f3a19206a034b7be06)) +* **widget-configurator:** switch network only when user changed in the configurator ([#3369](https://github.com/cowprotocol/cowswap/issues/3369)) ([51eb483](https://github.com/cowprotocol/cowswap/commit/51eb483643d80b1b0a39775b68b055a3ff8a8b92)) +* **widget-lib:** adjust code for server-side rendering ([#3339](https://github.com/cowprotocol/cowswap/issues/3339)) ([db8743d](https://github.com/cowprotocol/cowswap/commit/db8743d586a58dc8f38ea7b0167e1369c5bb8f06)) + ## [1.48.17](https://github.com/cowprotocol/cowswap/compare/v1.48.16...v1.48.17) (2023-11-03) diff --git a/package.json b/package.json index 264f9c68f1..9fa965d81e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cowswap", - "version": "1.48.17", + "version": "1.49.0", "description": "CoW Swap", "main": "index.js", "author": "",