diff --git a/packages/widget/src/App.tsx b/packages/widget/src/App.tsx index 42014e146..7f78ba3e9 100644 --- a/packages/widget/src/App.tsx +++ b/packages/widget/src/App.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from 'react'; +import { forwardRef, useMemo } from 'react'; import type { WidgetDrawer } from './AppDrawer'; import { AppDrawer } from './AppDrawer'; import { AppProvider } from './AppProvider'; @@ -16,7 +16,8 @@ import { useExpandableVariant } from './hooks'; import type { WidgetProps } from './types'; export const App: React.FC = forwardRef( - ({ elementRef, open, config }, ref) => { + ({ elementRef, open, ...other }, ref) => { + const config = useMemo(() => ({ ...other, ...other.config }), [other]); return config?.variant !== 'drawer' ? ( diff --git a/packages/widget/src/components/ActiveSwaps/ActiveSwapItem.tsx b/packages/widget/src/components/ActiveSwaps/ActiveSwapItem.tsx index 6e108ceb1..7b632408a 100644 --- a/packages/widget/src/components/ActiveSwaps/ActiveSwapItem.tsx +++ b/packages/widget/src/components/ActiveSwaps/ActiveSwapItem.tsx @@ -1,5 +1,7 @@ import { - ArrowForward as ArrowForwardIcon, ErrorRounded as ErrorIcon, InfoRounded as InfoIcon + ArrowForward as ArrowForwardIcon, + ErrorRounded as ErrorIcon, + InfoRounded as InfoIcon, } from '@mui/icons-material'; import { ListItemAvatar, ListItemText, Typography } from '@mui/material'; import { useNavigate } from 'react-router-dom'; diff --git a/packages/widget/src/components/PoweredBy/PoweredBy.tsx b/packages/widget/src/components/PoweredBy/PoweredBy.tsx index 27ade611e..85627ced4 100644 --- a/packages/widget/src/components/PoweredBy/PoweredBy.tsx +++ b/packages/widget/src/components/PoweredBy/PoweredBy.tsx @@ -1,11 +1,14 @@ import { Box, Tooltip, Typography } from '@mui/material'; import { useLocation } from 'react-router-dom'; import { version } from '../../config/version'; +import { useWidgetConfig } from '../../providers'; +import { HiddenUI } from '../../types'; import { navigationRoutes } from '../../utils'; import { LiFiLogo } from '../LiFiLogo'; import { Link } from './PoweredBy.style'; export const PoweredBy: React.FC = () => { + const { hiddenUI } = useWidgetConfig(); const { pathname } = useLocation(); if ( pathname.includes(navigationRoutes.fromToken) || @@ -16,27 +19,28 @@ export const PoweredBy: React.FC = () => { return ( - - - - Powered by - - - - + {!hiddenUI?.includes(HiddenUI.PoweredBy) ? ( + + + + Powered by + + + + + ) : null} ); }; diff --git a/packages/widget/src/components/SendToWallet/SendToWallet.style.tsx b/packages/widget/src/components/SendToWallet/SendToWallet.style.tsx index fe26143d9..16de0cac5 100644 --- a/packages/widget/src/components/SendToWallet/SendToWallet.style.tsx +++ b/packages/widget/src/components/SendToWallet/SendToWallet.style.tsx @@ -11,4 +11,10 @@ export const Input = styled(InputBase)(({ theme }) => ({ height: 32, padding: theme.spacing(0, 0, 0, 2), }, + [`&.${inputBaseClasses.disabled}`]: { + color: 'inherit', + }, + [`.${inputBaseClasses.input}.${inputBaseClasses.disabled}`]: { + WebkitTextFillColor: 'unset', + }, })); diff --git a/packages/widget/src/components/SendToWallet/SendToWallet.tsx b/packages/widget/src/components/SendToWallet/SendToWallet.tsx index 655febeed..9bdee4d47 100644 --- a/packages/widget/src/components/SendToWallet/SendToWallet.tsx +++ b/packages/widget/src/components/SendToWallet/SendToWallet.tsx @@ -5,25 +5,43 @@ import { forwardRef, useEffect } from 'react'; import { useFormContext, useFormState } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { SwapFormKey, useWallet, useWidgetConfig } from '../../providers'; -import { DisabledUI } from '../../types'; +import { useSendToWalletStore, useSettings } from '../../stores'; +import { DisabledUI, HiddenUI } from '../../types'; import { Card, CardTitle } from '../Card'; import { FormControl, Input } from './SendToWallet.style'; -import { useSendToWalletStore } from './store'; export const SendToWallet: React.FC = forwardRef((props, ref) => { const { t } = useTranslation(); - const { disabledUI } = useWidgetConfig(); - const showSendToWallet = useSendToWalletStore( - (state) => state.showSendToWallet, - ); - const { account, provider } = useWallet(); const { register, trigger } = useFormContext(); + const { account, provider } = useWallet(); + const { disabledUI, hiddenUI, toAddress } = useWidgetConfig(); + const { showSendToWallet, showSendToWalletDirty, setSendToWallet } = + useSendToWalletStore(); + const { showDestinationWallet } = useSettings(['showDestinationWallet']); useEffect(() => { trigger(SwapFormKey.ToAddress); }, [account.chainId, trigger]); - if (disabledUI?.includes(DisabledUI.ToAddress)) { + const hiddenToAddress = hiddenUI?.includes(HiddenUI.ToAddress); + const disabledToAddress = disabledUI?.includes(DisabledUI.ToAddress); + + // We want to show toAddress field if it is set via widget configuration, disabled for changes, but not hidden + const showInstantly = Boolean( + !showSendToWalletDirty && + toAddress && + disabledToAddress && + showDestinationWallet && + !hiddenToAddress, + ); + + useEffect(() => { + if (showInstantly) { + setSendToWallet(true); + } + }, [showInstantly, setSendToWallet]); + + if (hiddenToAddress) { return null; } @@ -51,7 +69,12 @@ export const SendToWallet: React.FC = forwardRef((props, ref) => { }); return ( - + {t('swap.sendToWallet')} @@ -66,6 +89,7 @@ export const SendToWallet: React.FC = forwardRef((props, ref) => { onBlur={onBlur} name={name} placeholder={t('swap.walletAddressOrEns') as string} + disabled={Boolean(toAddress && disabledToAddress)} /> diff --git a/packages/widget/src/components/SendToWallet/SendToWalletButton.tsx b/packages/widget/src/components/SendToWallet/SendToWalletButton.tsx index 97c8608bc..250c0730e 100644 --- a/packages/widget/src/components/SendToWallet/SendToWalletButton.tsx +++ b/packages/widget/src/components/SendToWallet/SendToWalletButton.tsx @@ -3,28 +3,27 @@ import { Button, Tooltip } from '@mui/material'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { SwapFormKey, useWallet, useWidgetConfig } from '../../providers'; -import { useSettings } from '../../stores'; -import { DisabledUI } from '../../types'; -import { useSendToWalletStore } from './store'; +import { useSendToWalletStore, useSettings } from '../../stores'; +import { DisabledUI, HiddenUI } from '../../types'; export const SendToWalletButton: React.FC = () => { const { t } = useTranslation(); - const { disabledUI } = useWidgetConfig(); - const { account } = useWallet(); const { setValue } = useFormContext(); - const { showDestinationWallet } = useSettings(['showDestinationWallet']); + const { account } = useWallet(); + const { disabledUI, hiddenUI } = useWidgetConfig(); const { showSendToWallet, toggleSendToWallet } = useSendToWalletStore(); + const { showDestinationWallet } = useSettings(['showDestinationWallet']); if ( !showDestinationWallet || !account.isActive || - disabledUI?.includes(DisabledUI.ToAddress) + hiddenUI?.includes(HiddenUI.ToAddress) ) { return null; } const handleClick = () => { - if (showSendToWallet) { + if (showSendToWallet && !disabledUI?.includes(DisabledUI.ToAddress)) { setValue(SwapFormKey.ToAddress, '', { shouldTouch: true }); } toggleSendToWallet(); diff --git a/packages/widget/src/components/SendToWallet/types.ts b/packages/widget/src/components/SendToWallet/types.ts deleted file mode 100644 index b1267429a..000000000 --- a/packages/widget/src/components/SendToWallet/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface SendToWalletState { - showSendToWallet: boolean; -} - -export interface SendToWalletStore extends SendToWalletState { - toggleSendToWallet(): void; -} diff --git a/packages/widget/src/pages/SettingsPage/ColorSchemeButtonGroup.tsx b/packages/widget/src/pages/SettingsPage/ColorSchemeButtonGroup.tsx index 505c1b9be..f04d4b000 100644 --- a/packages/widget/src/pages/SettingsPage/ColorSchemeButtonGroup.tsx +++ b/packages/widget/src/pages/SettingsPage/ColorSchemeButtonGroup.tsx @@ -7,14 +7,15 @@ import { Box, ToggleButtonGroup } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { useWidgetConfig } from '../../providers'; import { useAppearance } from '../../stores'; +import { HiddenUI } from '../../types'; import { ToggleButton } from './ColorSchemeButtonGroup.style'; export const ColorSchemeButtonGroup: React.FC = () => { const { t } = useTranslation(); - const { disableAppearance } = useWidgetConfig(); + const { disableAppearance, hiddenUI } = useWidgetConfig(); const [appearance, setAppearance] = useAppearance(); - if (disableAppearance) { + if (disableAppearance || hiddenUI?.includes(HiddenUI.Appearance)) { return null; } diff --git a/packages/widget/src/pages/SettingsPage/LanguageSelect.tsx b/packages/widget/src/pages/SettingsPage/LanguageSelect.tsx index 1b14b1243..77ec2996d 100644 --- a/packages/widget/src/pages/SettingsPage/LanguageSelect.tsx +++ b/packages/widget/src/pages/SettingsPage/LanguageSelect.tsx @@ -6,15 +6,16 @@ import { Card, CardTitle } from '../../components/Card'; import { Select } from '../../components/Select'; import { useWidgetConfig } from '../../providers'; import { useSettings, useSettingsStore } from '../../stores'; +import { HiddenUI } from '../../types'; export const LanguageSelect: React.FC = () => { const { t } = useTranslation(); - const { languages, disableI18n } = useWidgetConfig(); + const { languages, hiddenUI } = useWidgetConfig(); const { i18n } = useTranslation(); const setValue = useSettingsStore((state) => state.setValue); const { language } = useSettings(['language']); - if (disableI18n) { + if (hiddenUI?.includes(HiddenUI.Language)) { return null; } diff --git a/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx b/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx index 39a660150..d5be13898 100644 --- a/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx +++ b/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx @@ -3,21 +3,29 @@ import type { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { Switch } from '../../components/Switch'; import { useWidgetConfig } from '../../providers'; -import { useSettings, useSettingsStore } from '../../stores'; -import { DisabledUI } from '../../types'; +import { + useSendToWalletStore, + useSettings, + useSettingsStore, +} from '../../stores'; +import { HiddenUI } from '../../types'; export const ShowDestinationWallet = () => { const { t } = useTranslation(); - const { disabledUI } = useWidgetConfig(); + const { hiddenUI } = useWidgetConfig(); + const setSendToWallet = useSendToWalletStore( + (state) => state.setSendToWallet, + ); const setValue = useSettingsStore((state) => state.setValue); const { showDestinationWallet } = useSettings(['showDestinationWallet']); - if (disabledUI?.includes(DisabledUI.ToAddress)) { + if (hiddenUI?.includes(HiddenUI.ToAddress)) { return null; } const onChange = (_: ChangeEvent, checked: boolean) => { setValue('showDestinationWallet', checked); + setSendToWallet(false); }; return ( diff --git a/packages/widget/src/stores/settings/index.ts b/packages/widget/src/stores/settings/index.ts index b6f777f1e..6704ed0ce 100644 --- a/packages/widget/src/stores/settings/index.ts +++ b/packages/widget/src/stores/settings/index.ts @@ -1,4 +1,5 @@ export * from './types'; export * from './useAppearance'; +export * from './useSendToWalletStore'; export * from './useSettings'; export * from './useSettingsStore'; diff --git a/packages/widget/src/stores/settings/types.ts b/packages/widget/src/stores/settings/types.ts index 76a73458e..d680f99fd 100644 --- a/packages/widget/src/stores/settings/types.ts +++ b/packages/widget/src/stores/settings/types.ts @@ -37,3 +37,13 @@ export interface SettingsStore extends SettingsState { availableTools: (Pick | Pick)[], ): void; } + +export interface SendToWalletState { + showSendToWallet: boolean; + showSendToWalletDirty: boolean; +} + +export interface SendToWalletStore extends SendToWalletState { + toggleSendToWallet(): void; + setSendToWallet(value: boolean): void; +} diff --git a/packages/widget/src/components/SendToWallet/store.ts b/packages/widget/src/stores/settings/useSendToWalletStore.ts similarity index 54% rename from packages/widget/src/components/SendToWallet/store.ts rename to packages/widget/src/stores/settings/useSendToWalletStore.ts index af2edcf98..1ddab89c3 100644 --- a/packages/widget/src/components/SendToWallet/store.ts +++ b/packages/widget/src/stores/settings/useSendToWalletStore.ts @@ -3,9 +3,15 @@ import type { SendToWalletStore } from './types'; export const useSendToWalletStore = create((set) => ({ showSendToWallet: false, - toggleSendToWallet: () => { + showSendToWalletDirty: false, + toggleSendToWallet: () => set((state) => ({ showSendToWallet: !state.showSendToWallet, - })); - }, + showSendToWalletDirty: true, + })), + setSendToWallet: (value) => + set({ + showSendToWallet: value, + showSendToWalletDirty: true, + }), })); diff --git a/packages/widget/src/types/widget.ts b/packages/widget/src/types/widget.ts index 0de74a556..c85046cbf 100644 --- a/packages/widget/src/types/widget.ts +++ b/packages/widget/src/types/widget.ts @@ -8,19 +8,27 @@ import type { } from '@lifi/sdk'; import type { PaletteMode, PaletteOptions, Shape } from '@mui/material'; import type { TypographyOptions } from '@mui/material/styles/createTypography'; -import type { CSSProperties, RefObject } from 'react'; +import type { CSSProperties, ReactNode, RefObject } from 'react'; import type { LanguageKey, LanguageResources } from '../providers'; export type WidgetVariant = 'default' | 'expandable' | 'drawer' | 'refuel'; export enum DisabledUI { - FromToken = 'fromToken', - ToToken = 'toToken', FromAmount = 'fromAmount', + FromToken = 'fromToken', ToAddress = 'toAddress', + ToToken = 'toToken', } export type DisabledUIType = `${DisabledUI}`; +export enum HiddenUI { + Appearance = 'appearance', + Language = 'language', + PoweredBy = 'poweredBy', + ToAddress = 'toAddress', +} +export type HiddenUIType = `${HiddenUI}`; + export type Appearance = PaletteMode | 'auto'; export type ThemeConfig = { palette?: Pick; @@ -45,6 +53,15 @@ export interface SDKConfig defaultRouteOptions?: Omit; } +export interface WidgetContract { + address: string; + callData: string; + gasLimit: string; + approvalAddress?: string; + outputToken?: string; + fallbackAddress?: string; +} + export interface WidgetConfig { fromChain?: `${ChainKey}` | number; toChain?: `${ChainKey}` | number; @@ -52,6 +69,11 @@ export interface WidgetConfig { toToken?: string; toAddress?: string; fromAmount?: number | string; + toAmount?: number | string; + + contract?: WidgetContract; + contractComponent?: ReactNode; + contractCompactComponent?: ReactNode; fee?: number; integrator?: string; @@ -66,9 +88,9 @@ export interface WidgetConfig { theme?: ThemeConfig; containerStyle?: CSSProperties; - disableAppearance?: boolean; disableTelemetry?: boolean; disabledUI?: DisabledUIType[]; + hiddenUI?: HiddenUIType[]; useRecommendedRoute?: boolean; walletManagement?: WidgetWalletManagement; @@ -101,14 +123,20 @@ export interface WidgetConfig { languageResources?: LanguageResources; disableI18n?: boolean; + /** @deprecated Use hiddenUI: ['appearance'] instead */ + disableAppearance?: boolean; /** @deprecated Use chains.deny instead */ disabledChains?: number[]; /** @deprecated Use tokens.featured instead */ featuredTokens?: Token[]; } -export type WidgetProps = { +export type WidgetDrawerProps = { elementRef?: RefObject; - config?: WidgetConfig; open?: boolean; }; + +export type WidgetProps = WidgetDrawerProps & + WidgetConfig & { + config?: WidgetConfig; + };