diff --git a/packages/widget-playground/src/config.ts b/packages/widget-playground/src/config.ts index 5b8b47bea..105b6d2ac 100644 --- a/packages/widget-playground/src/config.ts +++ b/packages/widget-playground/src/config.ts @@ -30,6 +30,7 @@ export const widgetBaseConfig: WidgetConfig = { buildSwapUrl: true, // disabledUI: ['toAddress', 'fromAmount', 'toToken', 'fromToken'], // requiredUI: ['toAddress'], + // slippage: 0.003, sdkConfig: { // apiUrl: 'https://staging.li.quest/v1/', defaultRouteOptions: { diff --git a/packages/widget/src/AppRoutes.tsx b/packages/widget/src/AppRoutes.tsx index 77179bce0..91ab6a80d 100644 --- a/packages/widget/src/AppRoutes.tsx +++ b/packages/widget/src/AppRoutes.tsx @@ -3,6 +3,7 @@ import { NotFound } from './components/NotFound'; import { ActiveSwapsPage } from './pages/ActiveSwapsPage'; import { MainPage } from './pages/MainPage'; import { SelectChainPage } from './pages/SelectChainPage'; +import { SelectEnabledToolsPage } from './pages/SelectEnabledToolsPage'; import { SelectTokenPage } from './pages/SelectTokenPage'; import { SelectWalletPage } from './pages/SelectWalletPage'; import { SettingsPage } from './pages/SettingsPage'; @@ -22,6 +23,14 @@ export const AppRoutes = () => { path: navigationRoutes.settings, element: , }, + { + path: `${navigationRoutes.settings}/${navigationRoutes.bridges}`, + element: , + }, + { + path: `${navigationRoutes.settings}/${navigationRoutes.exchanges}`, + element: , + }, { path: navigationRoutes.fromToken, element: , diff --git a/packages/widget/src/components/Header/NavigationHeader.tsx b/packages/widget/src/components/Header/NavigationHeader.tsx index fc418617c..fbb558043 100644 --- a/packages/widget/src/components/Header/NavigationHeader.tsx +++ b/packages/widget/src/components/Header/NavigationHeader.tsx @@ -36,6 +36,10 @@ export const NavigationHeader: React.FC = () => { return t(`header.selectWallet`); case navigationRoutes.settings: return t(`header.settings`); + case navigationRoutes.bridges: + return t(`settings.enabledBridges`); + case navigationRoutes.exchanges: + return t(`settings.enabledExchanges`); case navigationRoutes.swapHistory: return t(`header.swapHistory`); case navigationRoutes.fromToken: diff --git a/packages/widget/src/components/ListItemButton.tsx b/packages/widget/src/components/ListItemButton.tsx new file mode 100644 index 000000000..0f99027a0 --- /dev/null +++ b/packages/widget/src/components/ListItemButton.tsx @@ -0,0 +1,12 @@ +import { ListItemButton as MuiListItemButton } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { getContrastAlphaColor } from '../utils'; + +export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + paddingLeft: theme.spacing(1.5), + height: 56, + '&:hover': { + backgroundColor: getContrastAlphaColor(theme, '4%'), + }, +})); diff --git a/packages/widget/src/components/ListItemText.tsx b/packages/widget/src/components/ListItemText.tsx new file mode 100644 index 000000000..61025f615 --- /dev/null +++ b/packages/widget/src/components/ListItemText.tsx @@ -0,0 +1,9 @@ +import { ListItemText as MuiListItemText } from '@mui/material'; +import { listItemTextClasses } from '@mui/material/ListItemText'; +import { styled } from '@mui/material/styles'; + +export const ListItemText = styled(MuiListItemText)(({ theme }) => ({ + [`.${listItemTextClasses.primary}`]: { + fontWeight: 400, + }, +})); diff --git a/packages/widget/src/components/Select.tsx b/packages/widget/src/components/Select.tsx index 7f0572928..2736b103b 100644 --- a/packages/widget/src/components/Select.tsx +++ b/packages/widget/src/components/Select.tsx @@ -19,6 +19,7 @@ export const Select = styled(MuiSelect, { }, [`.${selectClasses.icon}`]: { right: 10, + color: theme.palette.text.primary, }, [`.${listItemIconClasses.root}`]: { minWidth: 38, diff --git a/packages/widget/src/components/TokenList/TokenList.style.tsx b/packages/widget/src/components/TokenList/TokenList.style.tsx index ed3948d6a..14f151021 100644 --- a/packages/widget/src/components/TokenList/TokenList.style.tsx +++ b/packages/widget/src/components/TokenList/TokenList.style.tsx @@ -1,21 +1,14 @@ -import { - ListItem as MuiListItem, - ListItemButton as MuiListItemButton, -} from '@mui/material'; +import { ListItem as MuiListItem } from '@mui/material'; import { listItemSecondaryActionClasses } from '@mui/material/ListItemSecondaryAction'; import { listItemTextClasses } from '@mui/material/ListItemText'; import { styled } from '@mui/material/styles'; -import { getContrastAlphaColor } from '../../utils'; +import { ListItemButton as ListItemButtonBase } from '../ListItemButton'; -export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({ - borderRadius: theme.shape.borderRadius, +export const ListItemButton = styled(ListItemButtonBase)(({ theme }) => ({ paddingLeft: theme.spacing(1.5), paddingRight: theme.spacing(1.5), height: 64, width: '100%', - '&:hover': { - backgroundColor: getContrastAlphaColor(theme, '4%'), - }, })); export const ListItem = styled(MuiListItem)(({ theme }) => ({ diff --git a/packages/widget/src/config/env.ts b/packages/widget/src/config/env.ts index 6df81354d..42e289fdd 100644 --- a/packages/widget/src/config/env.ts +++ b/packages/widget/src/config/env.ts @@ -1,3 +1,3 @@ export const env = { - LIFI_API_URL: 'https://li.quest/v1/', // 'https://developkub.li.finance/v1/', + LIFI_API_URL: 'https://li.quest/v1/', // 'https://develop.li.quest/v1/', }; diff --git a/packages/widget/src/hooks/useTools.ts b/packages/widget/src/hooks/useTools.ts index 426c96245..8c064d42f 100644 --- a/packages/widget/src/hooks/useTools.ts +++ b/packages/widget/src/hooks/useTools.ts @@ -1,53 +1,42 @@ /* eslint-disable no-underscore-dangle */ -import type { Bridge, Exchange } from '@lifi/sdk'; +import type { ToolsResponse } from '@lifi/sdk'; import { useQuery } from '@tanstack/react-query'; -import { useMemo } from 'react'; import { isItemAllowed, useLiFi, useWidgetConfig } from '../providers'; import { useSettingsStoreContext } from '../stores'; -interface WidgetBridge extends Omit { - key: string; -} - -type FormattedTool = Record>; - export const useTools = () => { const lifi = useLiFi(); const { bridges, exchanges } = useWidgetConfig(); const settingsStoreContext = useSettingsStoreContext(); - const { data } = useQuery(['tools'], () => lifi.getTools(), { - onSuccess(data) { - const { initializeTools } = settingsStoreContext.getState(); - initializeTools( - 'Bridges', - data.bridges - .filter((bridge) => isItemAllowed(bridge.key, bridges)) - .map((bridge) => bridge.key), - ); - initializeTools( - 'Exchanges', - data.exchanges - .filter((exchange) => isItemAllowed(exchange.key, exchanges)) - .map((exchange) => exchange.key), - ); + const { data } = useQuery( + ['tools'], + async (): Promise => { + const tools = await lifi.getTools(); + return { + bridges: tools.bridges.filter((bridge) => + isItemAllowed(bridge.key, bridges), + ), + exchanges: tools.exchanges.filter((exchange) => + isItemAllowed(exchange.key, exchanges), + ), + }; + }, + { + onSuccess(data) { + const { initializeTools } = settingsStoreContext.getState(); + initializeTools( + 'Bridges', + data.bridges.map((bridge) => bridge.key), + ); + initializeTools( + 'Exchanges', + data.exchanges.map((exchange) => exchange.key), + ); + }, + refetchInterval: 180000, + staleTime: 180000, }, - refetchInterval: 180000, - staleTime: 180000, - }); - - const formattedTools = useMemo( - () => ({ - bridges: data?.bridges.reduce((bridges, bridge) => { - bridges[bridge.key] = bridge; - return bridges; - }, {} as FormattedTool), - exchanges: data?.exchanges.reduce((exchanges, exchange) => { - exchanges[exchange.key] = exchange; - return exchanges; - }, {} as FormattedTool), - }), - [data?.bridges, data?.exchanges], ); - return { tools: data, formattedTools }; + return { tools: data }; }; diff --git a/packages/widget/src/i18n/en.json b/packages/widget/src/i18n/en.json index 421d98bf5..c3fd3ea38 100644 --- a/packages/widget/src/i18n/en.json +++ b/packages/widget/src/i18n/en.json @@ -6,7 +6,6 @@ "contactSupport": "Contact support", "continue": "Continue", "copyAddress": "Copy address", - "viewOnExplorer": "View on explorer", "dark": "Dark", "delete": "Delete", "disconnect": "Disconnect", @@ -19,6 +18,8 @@ "ok": "Ok", "okay": "Okay", "removeSwap": "Remove swap", + "reset": "Reset", + "resetSettings": "Reset settings", "restartSwap": "Restart swap", "reviewGasSwap": "Review gas swap", "reviewSwap": "Review swap", @@ -27,7 +28,8 @@ "startGasSwap": "Start gas swap", "startSwap": "Start swap", "swap": "Swap", - "tryAgain": "Try again" + "tryAgain": "Try again", + "viewOnExplorer": "View on explorer" }, "format": { "currency": "{{value, currency(currency: USD)}}", @@ -201,7 +203,8 @@ "highValueLoss": "The value of the received tokens is significantly lower than the swapped tokens and transaction cost.", "insufficientFunds": "You don't have enough funds to execute the swap.", "insufficientGas": "You need to add at least:", - "rateChanged": "The exchange rate has changed. By continuing the swap, you'll accept the new rate." + "rateChanged": "The exchange rate has changed. By continuing the swap, you'll accept the new rate.", + "resetSettings": "This will reset your route priority, slippage, gas price, enabled bridges and exchanges." }, "title": { "deleteActiveSwaps": "Delete all active swaps?", @@ -209,7 +212,8 @@ "deleteSwapHistory": "Delete swap history?", "highValueLoss": "High value loss", "insufficientGas": "Insufficient gas", - "rateChanged": "Rate changed" + "rateChanged": "Rate changed", + "resetSettings": "Reset settings?" } } }, diff --git a/packages/widget/src/pages/SelectChainPage/SelectChainPage.style.tsx b/packages/widget/src/pages/SelectChainPage/SelectChainPage.style.tsx deleted file mode 100644 index 5b5fb66e6..000000000 --- a/packages/widget/src/pages/SelectChainPage/SelectChainPage.style.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { - ListItemButton as MuiListItemButton, - ListItemText as MuiListItemText, -} from '@mui/material'; -import { listItemTextClasses } from '@mui/material/ListItemText'; -import { styled } from '@mui/material/styles'; -import { getContrastAlphaColor } from '../../utils'; - -export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({ - borderRadius: theme.shape.borderRadius, - paddingLeft: theme.spacing(1.5), - height: 56, - '&:hover': { - backgroundColor: getContrastAlphaColor(theme, '4%'), - }, -})); - -export const ListItemText = styled(MuiListItemText)(({ theme }) => ({ - [`.${listItemTextClasses.primary}`]: { - fontWeight: 400, - }, -})); diff --git a/packages/widget/src/pages/SelectChainPage/SelectChainPage.tsx b/packages/widget/src/pages/SelectChainPage/SelectChainPage.tsx index b733a9f28..3e831f5e9 100644 --- a/packages/widget/src/pages/SelectChainPage/SelectChainPage.tsx +++ b/packages/widget/src/pages/SelectChainPage/SelectChainPage.tsx @@ -1,9 +1,10 @@ import type { ExtendedChain } from '@lifi/sdk'; import { Avatar, Container, List, ListItemAvatar } from '@mui/material'; import { useChainSelect } from '../../components/ChainSelect'; +import { ListItemButton } from '../../components/ListItemButton'; +import { ListItemText } from '../../components/ListItemText'; import { useTokenSelect } from '../../components/TokenList'; import { useNavigateBack } from '../../hooks'; -import { ListItemButton, ListItemText } from './SelectChainPage.style'; import type { SelectChainPageProps } from './types'; export const SelectChainPage: React.FC = ({ diff --git a/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.style.tsx b/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.style.tsx new file mode 100644 index 000000000..b928bb82c --- /dev/null +++ b/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.style.tsx @@ -0,0 +1,6 @@ +import { styled } from '@mui/material/styles'; +import { ListItemButton as ListItemButtonBase } from '../../components/ListItemButton'; + +export const ListItemButton = styled(ListItemButtonBase)(({ theme }) => ({ + paddingRight: theme.spacing(1), +})); diff --git a/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.tsx b/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.tsx new file mode 100644 index 000000000..b45e31c46 --- /dev/null +++ b/packages/widget/src/pages/SelectEnabledToolsPage/SelectEnabledToolsPage.tsx @@ -0,0 +1,105 @@ +import { + CheckBox, + CheckBoxOutlineBlankOutlined, + CheckBoxOutlined, + IndeterminateCheckBoxOutlined, +} from '@mui/icons-material'; +import { + Avatar, + Container, + IconButton, + List, + ListItemAvatar, +} from '@mui/material'; +import { useHeaderActionStore } from '../../components/Header'; + +import { useEffect } from 'react'; +import { shallow } from 'zustand/shallow'; +import { ListItemText } from '../../components/ListItemText'; +import { useTools } from '../../hooks'; +import { useSettingsStore } from '../../stores'; +import { ListItemButton } from './SelectEnabledToolsPage.style'; + +export const SelectEnabledToolsPage: React.FC<{ + type: 'Bridges' | 'Exchanges'; +}> = ({ type }) => { + const typeKey = type.toLowerCase() as 'bridges' | 'exchanges'; + const { tools } = useTools(); + const [enabledTools, setTools] = useSettingsStore( + (state) => [state[`enabled${type}`], state.setTools], + shallow, + ); + + const handleClick = (key: string) => { + if (!tools) { + return; + } + const toolKeys = tools[typeKey].map((tool) => tool.key); + if (enabledTools?.includes(key)) { + setTools( + type, + enabledTools.filter((toolKey) => toolKey !== key), + toolKeys, + ); + } else { + setTools(type, [...enabledTools, key], toolKeys); + } + }; + + useEffect(() => { + const allToolsSelected = tools?.[typeKey].length === enabledTools.length; + const toggleCheckboxes = () => { + if (!tools) { + return; + } + const toolKeys = tools[typeKey].map((tool) => tool.key); + if (allToolsSelected) { + setTools(type, [], toolKeys); + } else { + setTools(type, toolKeys, toolKeys); + } + }; + return useHeaderActionStore.getState().setAction( + + {allToolsSelected ? ( + + ) : enabledTools.length ? ( + + ) : ( + + )} + , + ); + }, [enabledTools.length, setTools, tools, type, typeKey]); + + return ( + + + {tools?.[typeKey].map((tool) => ( + handleClick(tool.key)} + disableRipple + > + + + {tool.name[0]} + + + + {enabledTools?.includes(tool.key) ? ( + + ) : ( + + )} + + ))} + + + ); +}; diff --git a/packages/widget/src/pages/SelectEnabledToolsPage/index.ts b/packages/widget/src/pages/SelectEnabledToolsPage/index.ts new file mode 100644 index 000000000..20bfe8d1b --- /dev/null +++ b/packages/widget/src/pages/SelectEnabledToolsPage/index.ts @@ -0,0 +1 @@ +export * from './SelectEnabledToolsPage'; diff --git a/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.style.tsx b/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.style.tsx deleted file mode 100644 index 5b5fb66e6..000000000 --- a/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.style.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { - ListItemButton as MuiListItemButton, - ListItemText as MuiListItemText, -} from '@mui/material'; -import { listItemTextClasses } from '@mui/material/ListItemText'; -import { styled } from '@mui/material/styles'; -import { getContrastAlphaColor } from '../../utils'; - -export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({ - borderRadius: theme.shape.borderRadius, - paddingLeft: theme.spacing(1.5), - height: 56, - '&:hover': { - backgroundColor: getContrastAlphaColor(theme, '4%'), - }, -})); - -export const ListItemText = styled(MuiListItemText)(({ theme }) => ({ - [`.${listItemTextClasses.primary}`]: { - fontWeight: 400, - }, -})); diff --git a/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.tsx b/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.tsx index 30bd6d9fb..23eed0eaa 100644 --- a/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.tsx +++ b/packages/widget/src/pages/SelectNativeTokenPage/SelectNativeTokenPage.tsx @@ -1,10 +1,11 @@ import { Container, List, ListItemAvatar } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import { ListItemButton } from '../../components/ListItemButton'; +import { ListItemText } from '../../components/ListItemText'; import { TokenAvatar } from '../../components/TokenAvatar'; import { useTokenSelect } from '../../components/TokenList'; import { useChains, useNavigateBack } from '../../hooks'; import type { SwapFormTypeProps } from '../../providers'; -import { ListItemButton, ListItemText } from './SelectNativeTokenPage.style'; export const SelectNativeTokenPage: React.FC = ({ formType, diff --git a/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.style.tsx b/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.style.tsx deleted file mode 100644 index 5b5fb66e6..000000000 --- a/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.style.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { - ListItemButton as MuiListItemButton, - ListItemText as MuiListItemText, -} from '@mui/material'; -import { listItemTextClasses } from '@mui/material/ListItemText'; -import { styled } from '@mui/material/styles'; -import { getContrastAlphaColor } from '../../utils'; - -export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({ - borderRadius: theme.shape.borderRadius, - paddingLeft: theme.spacing(1.5), - height: 56, - '&:hover': { - backgroundColor: getContrastAlphaColor(theme, '4%'), - }, -})); - -export const ListItemText = styled(MuiListItemText)(({ theme }) => ({ - [`.${listItemTextClasses.primary}`]: { - fontWeight: 400, - }, -})); diff --git a/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.tsx b/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.tsx index abd751261..f034753e0 100644 --- a/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.tsx +++ b/packages/widget/src/pages/SelectWalletPage/SelectWalletPage.tsx @@ -13,9 +13,10 @@ import { import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Dialog } from '../../components/Dialog'; +import { ListItemButton } from '../../components/ListItemButton'; +import { ListItemText } from '../../components/ListItemText'; import { useNavigateBack } from '../../hooks'; import { useWallet } from '../../providers'; -import { ListItemButton, ListItemText } from './SelectWalletPage.style'; export const SelectWalletPage = () => { const { t } = useTranslation(); diff --git a/packages/widget/src/pages/SettingsPage/AdvancedPreferences.tsx b/packages/widget/src/pages/SettingsPage/AdvancedPreferences.tsx deleted file mode 100644 index f54b32e8d..000000000 --- a/packages/widget/src/pages/SettingsPage/AdvancedPreferences.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Box, Typography } from '@mui/material'; -import type { ChangeEvent } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Switch } from '../../components/Switch'; -import { useSettings, useSettingsStore } from '../../stores'; -import { EnabledBridgesSelect } from './EnabledBridgesSelect'; -import { EnabledExchangesSelect } from './EnabledExchangesSelect'; - -export const AdvancedPreferences = () => { - const { t } = useTranslation(); - const setValue = useSettingsStore((state) => state.setValue); - const { advancedPreferences } = useSettings(['advancedPreferences']); - - const handleAdvancedPreferences = ( - _: ChangeEvent, - checked: boolean, - ) => { - setValue('advancedPreferences', checked); - }; - - return ( - - - - - {t(`settings.advancedPreferences`)} - - - - - {advancedPreferences && ( - - - - - - - )} - - ); -}; diff --git a/packages/widget/src/pages/SettingsPage/EnabledBridgesSelect.tsx b/packages/widget/src/pages/SettingsPage/EnabledBridgesSelect.tsx deleted file mode 100644 index e8f362eea..000000000 --- a/packages/widget/src/pages/SettingsPage/EnabledBridgesSelect.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { KeyboardArrowDown as KeyboardArrowDownIcon } from '@mui/icons-material'; -import { - Box, - Checkbox, - Chip, - FormControl, - MenuItem, - Skeleton, -} from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { shallow } from 'zustand/shallow'; -import { Card, CardTitle } from '../../components/Card'; -import { Select } from '../../components/Select'; -import { useTools } from '../../hooks'; -import { useSettingsStore } from '../../stores'; - -export const EnabledBridgesSelect: React.FC = () => { - const { t } = useTranslation(); - const { tools, formattedTools } = useTools(); - const [enabledBridges, setTools] = useSettingsStore( - (state) => [state.enabledBridges, state.setTools], - shallow, - ); - - return tools?.bridges.length ? ( - - {t(`settings.enabledBridges`)} - - - - - ) : ( - - ); -}; diff --git a/packages/widget/src/pages/SettingsPage/EnabledExchangesSelect.tsx b/packages/widget/src/pages/SettingsPage/EnabledExchangesSelect.tsx deleted file mode 100644 index cfaf7860a..000000000 --- a/packages/widget/src/pages/SettingsPage/EnabledExchangesSelect.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { KeyboardArrowDown as KeyboardArrowDownIcon } from '@mui/icons-material'; -import { - Box, - Checkbox, - Chip, - FormControl, - MenuItem, - Skeleton, -} from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { shallow } from 'zustand/shallow'; -import { Card, CardTitle } from '../../components/Card'; -import { Select } from '../../components/Select'; -import { useTools } from '../../hooks'; -import { useSettingsStore } from '../../stores'; - -export const EnabledExchangesSelect: React.FC = () => { - const { t } = useTranslation(); - const { tools, formattedTools } = useTools(); - const [enabledExchanges, setTools] = useSettingsStore( - (state) => [state.enabledExchanges, state.setTools], - shallow, - ); - - return tools?.exchanges.length ? ( - - {t(`settings.enabledExchanges`)} - - - - - ) : ( - - ); -}; diff --git a/packages/widget/src/pages/SettingsPage/EnabledToolsButton.style.tsx b/packages/widget/src/pages/SettingsPage/EnabledToolsButton.style.tsx new file mode 100644 index 000000000..6d3f21fcc --- /dev/null +++ b/packages/widget/src/pages/SettingsPage/EnabledToolsButton.style.tsx @@ -0,0 +1,16 @@ +import { ListItemText as MuiListItemText } from '@mui/material'; +import { listItemTextClasses } from '@mui/material/ListItemText'; +import { styled } from '@mui/material/styles'; +import { ListItemButton as ListItemButtonBase } from '../../components/ListItemButton'; + +export const ListItemButton = styled(ListItemButtonBase)(({ theme }) => ({ + height: 48, + paddingRight: theme.spacing(0.5), +})); + +export const ListItemText = styled(MuiListItemText)({ + [`.${listItemTextClasses.primary}`]: { + fontWeight: 400, + fontSize: '1rem', + }, +}); diff --git a/packages/widget/src/pages/SettingsPage/EnabledToolsButton.tsx b/packages/widget/src/pages/SettingsPage/EnabledToolsButton.tsx new file mode 100644 index 000000000..df10c7785 --- /dev/null +++ b/packages/widget/src/pages/SettingsPage/EnabledToolsButton.tsx @@ -0,0 +1,33 @@ +import { ChevronRight as ChevronRightIcon } from '@mui/icons-material'; +import { Box } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { shallow } from 'zustand/shallow'; +import { useSettingsStore } from '../../stores'; +import { navigationRoutes } from '../../utils'; +import { ListItemButton, ListItemText } from './EnabledToolsButton.style'; + +export const EnabledToolsButton: React.FC<{ + type: 'Bridges' | 'Exchanges'; +}> = ({ type }) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const [enabledTools, tools] = useSettingsStore((state) => { + const enabledTools = Object.values(state[`_enabled${type}`] ?? {}); + return [enabledTools.filter((tool) => tool).length, enabledTools.length]; + }, shallow); + + const handleClick = () => { + navigate(navigationRoutes[type.toLowerCase() as 'bridges' | 'exchanges']); + }; + + return ( + + + + + + + + ); +}; diff --git a/packages/widget/src/pages/SettingsPage/ResetSettingsButton.tsx b/packages/widget/src/pages/SettingsPage/ResetSettingsButton.tsx new file mode 100644 index 000000000..138141555 --- /dev/null +++ b/packages/widget/src/pages/SettingsPage/ResetSettingsButton.tsx @@ -0,0 +1,56 @@ +import { + Box, + Button, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Dialog } from '../../components/Dialog'; +import { useTools } from '../../hooks'; +import { useSettingsStore } from '../../stores'; + +export const ResetSettingsButton: React.FC = () => { + const { t } = useTranslation(); + const { tools } = useTools(); + const resetSettings = useSettingsStore((state) => state.reset); + const [open, setOpen] = useState(false); + + const toggleDialog = useCallback(() => { + setOpen((open) => !open); + }, []); + + const handleReset = () => { + if (tools) { + resetSettings( + tools.bridges.map((tool) => tool.key), + tools.exchanges.map((tool) => tool.key), + ); + } + toggleDialog(); + }; + + return ( + + + + {t('swap.warning.title.resetSettings')} + + + {t('swap.warning.message.resetSettings')} + + + + + + + + + ); +}; diff --git a/packages/widget/src/pages/SettingsPage/SettingsPage.tsx b/packages/widget/src/pages/SettingsPage/SettingsPage.tsx index 5e06aec65..7df36d1b5 100644 --- a/packages/widget/src/pages/SettingsPage/SettingsPage.tsx +++ b/packages/widget/src/pages/SettingsPage/SettingsPage.tsx @@ -1,8 +1,9 @@ import { Box, Container } from '@mui/material'; -import { AdvancedPreferences } from './AdvancedPreferences'; import { ColorSchemeButtonGroup } from './ColorSchemeButtonGroup'; +import { EnabledToolsButton } from './EnabledToolsButton'; import { GasPriceSelect } from './GasPriceSelect'; import { LanguageSelect } from './LanguageSelect'; +import { ResetSettingsButton } from './ResetSettingsButton'; import { RoutePrioritySelect } from './RoutePrioritySelect'; import { ShowDestinationWallet } from './ShowDestinationWallet'; import { SlippageInput } from './SlippageInput'; @@ -22,7 +23,11 @@ export const SettingsPage = () => { - + + + + + ); }; diff --git a/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx b/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx index d5be13898..5dc87a6dc 100644 --- a/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx +++ b/packages/widget/src/pages/SettingsPage/ShowDestinationWallet.tsx @@ -29,7 +29,7 @@ export const ShowDestinationWallet = () => { }; return ( - + create( persist( - (set) => ({ + (set, get) => ({ ...defaultSettings, setValue: (key, value) => set(() => ({ @@ -40,14 +41,14 @@ export const createSettingsStore = ({ namePrefix }: PersistStoreProps) => } return updatedState; }), - initializeTools: (toolType, tools) => { + initializeTools: (toolType, tools, reset) => { if (!tools.length) { return; } set((state) => { const updatedState = { ...state }; - if (updatedState[`_enabled${toolType}`]) { - // Add a new tools + if (updatedState[`_enabled${toolType}`] && !reset) { + // Add new tools const enabledTools = tools .filter( (tool) => @@ -86,11 +87,22 @@ export const createSettingsStore = ({ namePrefix }: PersistStoreProps) => setTools: (toolType, tools, availableTools) => set(() => ({ [`enabled${toolType}`]: tools, - [`_enabled${toolType}`]: availableTools.reduce((values, tool) => { - values[tool.key] = tools.includes(tool.key); - return values; - }, {} as Record), + [`_enabled${toolType}`]: availableTools.reduce( + (values, toolKey) => { + values[toolKey] = tools.includes(toolKey); + return values; + }, + {} as Record, + ), })), + reset: (bridges, exchanges) => { + set(() => ({ + ...defaultSettings, + ...defaultConfigurableSettings, + })); + get().initializeTools('Bridges', bridges, true); + get().initializeTools('Exchanges', exchanges, true); + }, }), { name: `${namePrefix || 'li.fi'}-widget-settings`, diff --git a/packages/widget/src/stores/settings/types.ts b/packages/widget/src/stores/settings/types.ts index 9c39cb162..b7e123c4a 100644 --- a/packages/widget/src/stores/settings/types.ts +++ b/packages/widget/src/stores/settings/types.ts @@ -1,4 +1,4 @@ -import type { Bridge, Exchange, Order } from '@lifi/sdk'; +import type { Order } from '@lifi/sdk'; import type { Appearance } from '../../types'; export type ValueSetter = ( @@ -14,28 +14,32 @@ export type SettingsToolType = 'Bridges' | 'Exchanges'; export const SettingsToolTypes: SettingsToolType[] = ['Bridges', 'Exchanges']; export interface SettingsProps { - advancedPreferences: boolean; appearance: Appearance; gasPrice?: string; language?: string; routePriority?: Order; showDestinationWallet: boolean; slippage?: string; - enabledBridges?: string[]; + enabledBridges: string[]; _enabledBridges?: Record; - enabledExchanges?: string[]; + enabledExchanges: string[]; _enabledExchanges?: Record; } export interface SettingsState extends SettingsProps { setValue: ValueSetter; setValues: ValuesSetter; - initializeTools(toolType: SettingsToolType, tools: string[]): void; + initializeTools( + toolType: SettingsToolType, + tools: string[], + reset?: boolean, + ): void; setTools( toolType: SettingsToolType, tools: string[], - availableTools: (Pick | Pick)[], + availableTools: string[], ): void; + reset(bridges: string[], exchanges: string[]): void; } export interface SendToWalletState { diff --git a/packages/widget/src/utils/navigationRoutes.ts b/packages/widget/src/utils/navigationRoutes.ts index 5e5750561..aeecedbec 100644 --- a/packages/widget/src/utils/navigationRoutes.ts +++ b/packages/widget/src/utils/navigationRoutes.ts @@ -1,6 +1,8 @@ export const navigationRoutes = { home: '/', activeSwaps: 'active-swaps', + bridges: 'bridges', + exchanges: 'exchanges', fromChain: 'from-chain', fromToken: 'from-token', selectWallet: 'wallet', @@ -18,6 +20,8 @@ export const navigationRoutesValues = Object.values(navigationRoutes); export const stickyHeaderRoutes = [ navigationRoutes.activeSwaps, + navigationRoutes.bridges, + navigationRoutes.exchanges, navigationRoutes.fromChain, navigationRoutes.home, navigationRoutes.selectWallet, @@ -32,6 +36,8 @@ export const stickyHeaderRoutes = [ export const backButtonRoutes = [ navigationRoutes.activeSwaps, + navigationRoutes.bridges, + navigationRoutes.exchanges, navigationRoutes.fromChain, navigationRoutes.fromToken, navigationRoutes.selectWallet,