From e31da8f7498b4c4174fc79b548e0cd68e3fba40b Mon Sep 17 00:00:00 2001 From: Eugene Chybisov Date: Wed, 15 Feb 2023 14:10:42 +0700 Subject: [PATCH] fix: add provider for recommended route store --- packages/widget/src/hooks/useTools.ts | 5 +- .../WidgetProvider/WidgetProvider.tsx | 13 +- packages/widget/src/stores/StoreProvider.tsx | 18 +-- .../src/stores/chains/ChainOrderStore.tsx | 50 ------ .../stores/chains/createChainOrderStore.ts | 67 -------- packages/widget/src/stores/chains/index.ts | 3 +- .../widget/src/stores/chains/useChainOrder.ts | 2 +- .../src/stores/chains/useChainOrderStore.ts | 65 ++++++++ packages/widget/src/stores/routes/types.ts | 2 +- .../stores/routes/useRecommendedRouteStore.ts | 13 -- .../routes/useRecommendedRouteStore.tsx | 60 +++++++ .../src/stores/settings/SettingsStore.tsx | 76 --------- .../stores/settings/createSettingsStore.ts | 137 ---------------- packages/widget/src/stores/settings/index.ts | 2 +- .../src/stores/settings/useAppearance.ts | 2 +- .../widget/src/stores/settings/useSettings.ts | 2 +- .../src/stores/settings/useSettingsStore.ts | 153 ++++++++++++++++++ 17 files changed, 296 insertions(+), 374 deletions(-) delete mode 100644 packages/widget/src/stores/chains/ChainOrderStore.tsx delete mode 100644 packages/widget/src/stores/chains/createChainOrderStore.ts create mode 100644 packages/widget/src/stores/chains/useChainOrderStore.ts delete mode 100644 packages/widget/src/stores/routes/useRecommendedRouteStore.ts create mode 100644 packages/widget/src/stores/routes/useRecommendedRouteStore.tsx delete mode 100644 packages/widget/src/stores/settings/SettingsStore.tsx delete mode 100644 packages/widget/src/stores/settings/createSettingsStore.ts create mode 100644 packages/widget/src/stores/settings/useSettingsStore.ts diff --git a/packages/widget/src/hooks/useTools.ts b/packages/widget/src/hooks/useTools.ts index 8c064d42f..516c36bd2 100644 --- a/packages/widget/src/hooks/useTools.ts +++ b/packages/widget/src/hooks/useTools.ts @@ -2,12 +2,11 @@ import type { ToolsResponse } from '@lifi/sdk'; import { useQuery } from '@tanstack/react-query'; import { isItemAllowed, useLiFi, useWidgetConfig } from '../providers'; -import { useSettingsStoreContext } from '../stores'; +import { useSettingsStore } from '../stores'; export const useTools = () => { const lifi = useLiFi(); const { bridges, exchanges } = useWidgetConfig(); - const settingsStoreContext = useSettingsStoreContext(); const { data } = useQuery( ['tools'], async (): Promise => { @@ -23,7 +22,7 @@ export const useTools = () => { }, { onSuccess(data) { - const { initializeTools } = settingsStoreContext.getState(); + const { initializeTools } = useSettingsStore.getState(); initializeTools( 'Bridges', data.bridges.map((bridge) => bridge.key), diff --git a/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx b/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx index daf4d2b1c..559a61aa4 100644 --- a/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx +++ b/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx @@ -1,7 +1,7 @@ import type { ChainKey } from '@lifi/sdk'; import { getChainByKey } from '@lifi/sdk'; -import { createContext, useContext, useEffect, useId, useMemo } from 'react'; -import { setDefaultSettings, useSettingsStoreContext } from '../../stores'; +import { createContext, useContext, useId, useMemo } from 'react'; +import { setDefaultSettings } from '../../stores'; import { formatAmount } from '../../utils'; import type { WidgetContextProps, WidgetProviderProps } from './types'; @@ -28,7 +28,6 @@ export const WidgetProvider: React.FC< ...config } = {}, }) => { - const settingsStoreContext = useSettingsStoreContext(); const elementId = useId(); const value = useMemo((): WidgetContextProps => { try { @@ -41,7 +40,7 @@ export const WidgetProvider: React.FC< delete searchParams[`${key}Token`]; } }); - return { + const value = { ...config, fromChain: (searchParams.fromChain && @@ -80,16 +79,14 @@ export const WidgetProvider: React.FC< : fromAmount, elementId, } as WidgetContextProps; + setDefaultSettings(value); + return value; } catch (e) { console.warn(e); return { ...config, elementId }; } }, [config, elementId, fromAmount, fromChain, fromToken, toChain, toToken]); - useEffect(() => { - setDefaultSettings(settingsStoreContext, value); - }, [settingsStoreContext, value]); - return ( {children} ); diff --git a/packages/widget/src/stores/StoreProvider.tsx b/packages/widget/src/stores/StoreProvider.tsx index ce8c76fae..5a57e74a5 100644 --- a/packages/widget/src/stores/StoreProvider.tsx +++ b/packages/widget/src/stores/StoreProvider.tsx @@ -1,7 +1,8 @@ import type { PropsWithChildren } from 'react'; -import { ChainOrderStoreProvider } from './chains'; -import { RouteExecutionStoreProvider } from './routes'; -import { SettingsStoreProvider } from './settings'; +import { + RecommendedRouteStoreProvider, + RouteExecutionStoreProvider, +} from './routes'; import type { PersistStoreProviderProps } from './types'; export const StoreProvider: React.FC< @@ -9,16 +10,7 @@ export const StoreProvider: React.FC< > = ({ children, namePrefix }) => { return ( - {/* We don't want separate settings in each widget instance for now. */} - - - {children} - - + {children} ); }; diff --git a/packages/widget/src/stores/chains/ChainOrderStore.tsx b/packages/widget/src/stores/chains/ChainOrderStore.tsx deleted file mode 100644 index 9d63f5a5d..000000000 --- a/packages/widget/src/stores/chains/ChainOrderStore.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import { createContext, useContext, useRef } from 'react'; -import type { StoreApi, UseBoundStore } from 'zustand'; -import type { PersistStoreProviderProps } from '../types'; -import { createChainOrderStore } from './createChainOrderStore'; -import type { ChainOrderState } from './types'; - -export type ChainOrderStore = UseBoundStore>; - -export const ChainOrderStoreContext = createContext( - null, -); - -export function ChainOrderStoreProvider({ - children, - ...props -}: PersistStoreProviderProps) { - const storeRef = useRef(); - if (!storeRef.current) { - storeRef.current = createChainOrderStore(props); - } - return ( - - {children} - - ); -} - -export function useChainOrderStore( - selector: (state: ChainOrderState) => T, - equalityFn?: (left: T, right: T) => boolean, -): T { - const useStore = useContext(ChainOrderStoreContext); - if (!useStore) { - throw new Error( - `You forgot to wrap your component in <${ChainOrderStoreProvider.name}>.`, - ); - } - return useStore(selector, equalityFn); -} - -export function useChainOrderStoreContext() { - const useStore = useContext(ChainOrderStoreContext); - if (!useStore) { - throw new Error( - `You forgot to wrap your component in <${ChainOrderStoreProvider.name}>.`, - ); - } - return useStore; -} diff --git a/packages/widget/src/stores/chains/createChainOrderStore.ts b/packages/widget/src/stores/chains/createChainOrderStore.ts deleted file mode 100644 index efedcd8db..000000000 --- a/packages/widget/src/stores/chains/createChainOrderStore.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import type { StateCreator } from 'zustand'; -import { create } from 'zustand'; -import { persist } from 'zustand/middleware'; -import type { PersistStoreProps } from '../types'; -import type { ChainOrderState } from './types'; - -export const maxChainToOrder = 9; - -export const createChainOrderStore = ({ namePrefix }: PersistStoreProps) => - create( - persist( - (set, get) => ({ - chainOrder: [], - availableChains: [], - initializeChains: (chainIds: number[]) => { - set((state: ChainOrderState) => { - const chainOrder = state.chainOrder.filter((chainId) => - chainIds.includes(chainId), - ); - const chainsToAdd = chainIds.filter( - (chainId) => !chainOrder.includes(chainId), - ); - if (chainOrder.length === maxChainToOrder || !chainsToAdd.length) { - return { - availableChains: chainIds, - chainOrder, - }; - } - const chainsToAddLength = maxChainToOrder - chainOrder.length; - for (let index = 0; index < chainsToAddLength; index++) { - chainOrder.push(chainsToAdd[index]); - } - return { - availableChains: chainIds, - chainOrder, - }; - }); - return get().chainOrder; - }, - setChain: (chainId: number) => { - const state = get(); - if ( - state.chainOrder.includes(chainId) || - !state.availableChains.includes(chainId) - ) { - return; - } - set((state: ChainOrderState) => { - const chainOrder = state.chainOrder.slice(); - chainOrder.unshift(chainId); - if (chainOrder.length > maxChainToOrder) { - chainOrder.pop(); - } - return { - chainOrder, - }; - }); - }, - }), - { - name: `${namePrefix || 'li.fi'}-widget-chains-order`, - version: 0, - partialize: (state) => ({ chainOrder: state.chainOrder }), - }, - ) as StateCreator, - ); diff --git a/packages/widget/src/stores/chains/index.ts b/packages/widget/src/stores/chains/index.ts index b57473872..1c9d32297 100644 --- a/packages/widget/src/stores/chains/index.ts +++ b/packages/widget/src/stores/chains/index.ts @@ -1,4 +1,3 @@ -export * from './ChainOrderStore'; -export * from './createChainOrderStore'; export * from './types'; export * from './useChainOrder'; +export * from './useChainOrderStore'; diff --git a/packages/widget/src/stores/chains/useChainOrder.ts b/packages/widget/src/stores/chains/useChainOrder.ts index c0ef2d7e1..9400d6791 100644 --- a/packages/widget/src/stores/chains/useChainOrder.ts +++ b/packages/widget/src/stores/chains/useChainOrder.ts @@ -1,5 +1,5 @@ import { shallow } from 'zustand/shallow'; -import { useChainOrderStore } from './ChainOrderStore'; +import { useChainOrderStore } from '.'; export const useChainOrder = (): [number[], (chainId: number) => void] => { return useChainOrderStore( diff --git a/packages/widget/src/stores/chains/useChainOrderStore.ts b/packages/widget/src/stores/chains/useChainOrderStore.ts new file mode 100644 index 000000000..7ef801262 --- /dev/null +++ b/packages/widget/src/stores/chains/useChainOrderStore.ts @@ -0,0 +1,65 @@ +/* eslint-disable no-underscore-dangle */ +import type { StateCreator } from 'zustand'; +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import type { ChainOrderState } from './types'; + +export const maxChainToOrder = 9; + +export const useChainOrderStore = create( + persist( + (set, get) => ({ + chainOrder: [], + availableChains: [], + initializeChains: (chainIds: number[]) => { + set((state: ChainOrderState) => { + const chainOrder = state.chainOrder.filter((chainId) => + chainIds.includes(chainId), + ); + const chainsToAdd = chainIds.filter( + (chainId) => !chainOrder.includes(chainId), + ); + if (chainOrder.length === maxChainToOrder || !chainsToAdd.length) { + return { + availableChains: chainIds, + chainOrder, + }; + } + const chainsToAddLength = maxChainToOrder - chainOrder.length; + for (let index = 0; index < chainsToAddLength; index++) { + chainOrder.push(chainsToAdd[index]); + } + return { + availableChains: chainIds, + chainOrder, + }; + }); + return get().chainOrder; + }, + setChain: (chainId: number) => { + const state = get(); + if ( + state.chainOrder.includes(chainId) || + !state.availableChains.includes(chainId) + ) { + return; + } + set((state: ChainOrderState) => { + const chainOrder = state.chainOrder.slice(); + chainOrder.unshift(chainId); + if (chainOrder.length > maxChainToOrder) { + chainOrder.pop(); + } + return { + chainOrder, + }; + }); + }, + }), + { + name: `li.fi-widget-chains-order`, + version: 0, + partialize: (state) => ({ chainOrder: state.chainOrder }), + }, + ) as StateCreator, +); diff --git a/packages/widget/src/stores/routes/types.ts b/packages/widget/src/stores/routes/types.ts index 0ed397dac..1aff53b6d 100644 --- a/packages/widget/src/stores/routes/types.ts +++ b/packages/widget/src/stores/routes/types.ts @@ -23,7 +23,7 @@ export enum RouteExecutionStatus { Refunded = 1 << 4, } -export interface RecommendedRouteStore { +export interface RecommendedRouteState { recommendedRoute?: Route; setRecommendedRoute: (route?: Route) => void; } diff --git a/packages/widget/src/stores/routes/useRecommendedRouteStore.ts b/packages/widget/src/stores/routes/useRecommendedRouteStore.ts deleted file mode 100644 index 15108361b..000000000 --- a/packages/widget/src/stores/routes/useRecommendedRouteStore.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from '@lifi/sdk'; -import { create } from 'zustand'; -import type { RecommendedRouteStore } from './types'; - -export const useRecommendedRouteStore = create( - (set) => ({ - setRecommendedRoute: (recommendedRoute?: Route) => { - set(() => ({ - recommendedRoute, - })); - }, - }), -); diff --git a/packages/widget/src/stores/routes/useRecommendedRouteStore.tsx b/packages/widget/src/stores/routes/useRecommendedRouteStore.tsx new file mode 100644 index 000000000..e358e251e --- /dev/null +++ b/packages/widget/src/stores/routes/useRecommendedRouteStore.tsx @@ -0,0 +1,60 @@ +import type { Route } from '@lifi/sdk'; +import { createContext, useContext, useRef } from 'react'; +import type { StoreApi, UseBoundStore } from 'zustand'; +import { create } from 'zustand'; +import type { PersistStoreProviderProps } from '../types'; +import type { RecommendedRouteState } from './types'; + +export const createRecommendedRouteStore = () => + create((set) => ({ + setRecommendedRoute: (recommendedRoute?: Route) => { + set(() => ({ + recommendedRoute, + })); + }, + })); + +export type RecommendedRouteStore = UseBoundStore< + StoreApi +>; + +export const RecommendedRouteStoreContext = + createContext(null); + +export function RecommendedRouteStoreProvider({ + children, + ...props +}: PersistStoreProviderProps) { + const storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createRecommendedRouteStore(); + } + return ( + + {children} + + ); +} + +export function useRecommendedRouteStore( + selector: (state: RecommendedRouteState) => T, + equalityFn?: (left: T, right: T) => boolean, +): T { + const useStore = useContext(RecommendedRouteStoreContext); + if (!useStore) { + throw new Error( + `You forgot to wrap your component in <${RecommendedRouteStoreProvider.name}>.`, + ); + } + return useStore(selector, equalityFn); +} + +export function useRecommendedRouteStoreContext() { + const useStore = useContext(RecommendedRouteStoreContext); + if (!useStore) { + throw new Error( + `You forgot to wrap your component in <${RecommendedRouteStoreProvider.name}>.`, + ); + } + return useStore; +} diff --git a/packages/widget/src/stores/settings/SettingsStore.tsx b/packages/widget/src/stores/settings/SettingsStore.tsx deleted file mode 100644 index 4c3c84518..000000000 --- a/packages/widget/src/stores/settings/SettingsStore.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import type { WidgetConfig } from '@lifi/widget'; -import { createContext, useContext, useRef } from 'react'; -import type { StoreApi, UseBoundStore } from 'zustand'; -import type { PersistStoreProviderProps } from '../types'; -import { - createSettingsStore, - defaultConfigurableSettings, -} from './createSettingsStore'; -import type { SettingsState } from './types'; - -export type SettingsStore = UseBoundStore>; - -export const SettingsStoreContext = createContext(null); - -export function SettingsStoreProvider({ - children, - ...props -}: PersistStoreProviderProps) { - const storeRef = useRef(); - if (!storeRef.current) { - storeRef.current = createSettingsStore(props); - } - return ( - - {children} - - ); -} - -export function useSettingsStore( - selector: (state: SettingsState) => T, - equalityFn?: (left: T, right: T) => boolean, -): T { - const useStore = useContext(SettingsStoreContext); - if (!useStore) { - throw new Error( - `You forgot to wrap your component in <${SettingsStoreProvider.name}>.`, - ); - } - return useStore(selector, equalityFn); -} - -export function useSettingsStoreContext() { - const useStore = useContext(SettingsStoreContext); - if (!useStore) { - throw new Error( - `You forgot to wrap your component in <${SettingsStoreProvider.name}>.`, - ); - } - return useStore; -} - -export const setDefaultSettings = ( - useSettingsStore: SettingsStore, - config?: WidgetConfig, -) => { - const { slippage, routePriority, setValue } = useSettingsStore.getState(); - const defaultSlippage = - (config?.slippage || - config?.sdkConfig?.defaultRouteOptions?.slippage || - 0) * 100; - const defaultRoutePriority = - config?.routePriority || config?.sdkConfig?.defaultRouteOptions?.order; - defaultConfigurableSettings.slippage = ( - defaultSlippage || defaultConfigurableSettings.slippage - )?.toString(); - defaultConfigurableSettings.routePriority = - defaultRoutePriority || defaultConfigurableSettings.routePriority; - if (!slippage) { - setValue('slippage', defaultConfigurableSettings.slippage); - } - if (!routePriority) { - setValue('routePriority', defaultConfigurableSettings.routePriority); - } -}; diff --git a/packages/widget/src/stores/settings/createSettingsStore.ts b/packages/widget/src/stores/settings/createSettingsStore.ts deleted file mode 100644 index 8e1f1f545..000000000 --- a/packages/widget/src/stores/settings/createSettingsStore.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -import type { StateCreator } from 'zustand'; -import { create } from 'zustand'; -import { persist } from 'zustand/middleware'; -import type { PersistStoreProps } from '../types'; -import type { SettingsProps, SettingsState } from './types'; -import { SettingsToolTypes } from './types'; - -export const defaultConfigurableSettings: Pick< - SettingsState, - 'routePriority' | 'slippage' -> = { - routePriority: 'RECOMMENDED', - slippage: '0.5', -}; - -export const defaultSettings: SettingsProps = { - appearance: 'auto', - gasPrice: 'normal', - showDestinationWallet: true, - enabledBridges: [], - enabledExchanges: [], -}; - -export const createSettingsStore = ({ namePrefix }: PersistStoreProps) => - create( - persist( - (set, get) => ({ - ...defaultSettings, - setValue: (key, value) => - set(() => ({ - [key]: value, - })), - setValues: (values) => - set((state) => { - const updatedState: SettingsProps = { ...state }; - for (const key in values) { - if (Object.hasOwn(state, key)) { - updatedState[key] = values[key]; - } - } - return updatedState; - }), - initializeTools: (toolType, tools, reset) => { - if (!tools.length) { - return; - } - set((state) => { - const updatedState = { ...state }; - if (updatedState[`_enabled${toolType}`] && !reset) { - // Add new tools - const enabledTools = tools - .filter( - (tool) => - !Object.hasOwn( - updatedState[`_enabled${toolType}`] as object, - tool, - ), - ) - .reduce((values, tool) => { - values[tool] = true; - return values; - }, updatedState[`_enabled${toolType}`] as Record); - // Filter tools we no longer have - updatedState[`_enabled${toolType}`] = Object.fromEntries( - Object.entries(enabledTools).filter(([key]) => - tools.includes(key), - ), - ); - } else { - updatedState[`_enabled${toolType}`] = tools.reduce( - (values, tool) => { - values[tool] = true; - return values; - }, - {} as Record, - ); - } - updatedState[`enabled${toolType}`] = Object.entries( - updatedState[`_enabled${toolType}`]!, - ) - .filter(([_, value]) => value) - .map(([key]) => key); - return updatedState; - }); - }, - setTools: (toolType, tools, availableTools) => - set(() => ({ - [`enabled${toolType}`]: tools, - [`_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`, - version: 2, - partialize: (state) => { - const { enabledBridges, enabledExchanges, ...partializedState } = - state; - return partializedState; - }, - merge: (persistedState: any, currentState: SettingsState) => { - const state = { ...currentState, ...persistedState }; - SettingsToolTypes.forEach((toolType) => { - state[`enabled${toolType}`] = Object.entries( - persistedState[`_enabled${toolType}`], - ) - .filter(([_, value]) => value) - .map(([key]) => key); - }); - return state; - }, - migrate: (persistedState: any, version) => { - if (version === 0 && persistedState.appearance === 'system') { - persistedState.appearance = defaultSettings.appearance; - } - if (version === 1) { - persistedState.slippage = defaultConfigurableSettings.slippage; - } - return persistedState as SettingsState; - }, - }, - ) as StateCreator, - ); diff --git a/packages/widget/src/stores/settings/index.ts b/packages/widget/src/stores/settings/index.ts index 6ae4b8203..6704ed0ce 100644 --- a/packages/widget/src/stores/settings/index.ts +++ b/packages/widget/src/stores/settings/index.ts @@ -1,5 +1,5 @@ -export * from './SettingsStore'; export * from './types'; export * from './useAppearance'; export * from './useSendToWalletStore'; export * from './useSettings'; +export * from './useSettingsStore'; diff --git a/packages/widget/src/stores/settings/useAppearance.ts b/packages/widget/src/stores/settings/useAppearance.ts index 2804fb75d..49fe9143d 100644 --- a/packages/widget/src/stores/settings/useAppearance.ts +++ b/packages/widget/src/stores/settings/useAppearance.ts @@ -1,6 +1,6 @@ import { shallow } from 'zustand/shallow'; import type { Appearance } from '../../types'; -import { useSettingsStore } from './SettingsStore'; +import { useSettingsStore } from './useSettingsStore'; export const useAppearance = (): [ Appearance, diff --git a/packages/widget/src/stores/settings/useSettings.ts b/packages/widget/src/stores/settings/useSettings.ts index 2db704c2b..4e49c022e 100644 --- a/packages/widget/src/stores/settings/useSettings.ts +++ b/packages/widget/src/stores/settings/useSettings.ts @@ -1,6 +1,6 @@ import { shallow } from 'zustand/shallow'; import type { SettingsState } from '.'; -import { useSettingsStore } from './SettingsStore'; +import { useSettingsStore } from './useSettingsStore'; export const useSettings = ( keys: Array, diff --git a/packages/widget/src/stores/settings/useSettingsStore.ts b/packages/widget/src/stores/settings/useSettingsStore.ts new file mode 100644 index 000000000..bd46f871a --- /dev/null +++ b/packages/widget/src/stores/settings/useSettingsStore.ts @@ -0,0 +1,153 @@ +/* eslint-disable no-underscore-dangle */ +import type { StateCreator } from 'zustand'; +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import type { WidgetConfig } from '../../types'; +import type { SettingsProps, SettingsState } from './types'; +import { SettingsToolTypes } from './types'; + +export const defaultConfigurableSettings: Pick< + SettingsState, + 'routePriority' | 'slippage' +> = { + routePriority: 'RECOMMENDED', + slippage: '0.5', +}; + +export const defaultSettings: SettingsProps = { + appearance: 'auto', + gasPrice: 'normal', + showDestinationWallet: true, + enabledBridges: [], + enabledExchanges: [], +}; + +export const useSettingsStore = create( + persist( + (set, get) => ({ + ...defaultSettings, + setValue: (key, value) => + set(() => ({ + [key]: value, + })), + setValues: (values) => + set((state) => { + const updatedState: SettingsProps = { ...state }; + for (const key in values) { + if (Object.hasOwn(state, key)) { + updatedState[key] = values[key]; + } + } + return updatedState; + }), + initializeTools: (toolType, tools, reset) => { + if (!tools.length) { + return; + } + set((state) => { + const updatedState = { ...state }; + if (updatedState[`_enabled${toolType}`] && !reset) { + // Add new tools + const enabledTools = tools + .filter( + (tool) => + !Object.hasOwn( + updatedState[`_enabled${toolType}`] as object, + tool, + ), + ) + .reduce((values, tool) => { + values[tool] = true; + return values; + }, updatedState[`_enabled${toolType}`] as Record); + // Filter tools we no longer have + updatedState[`_enabled${toolType}`] = Object.fromEntries( + Object.entries(enabledTools).filter(([key]) => + tools.includes(key), + ), + ); + } else { + updatedState[`_enabled${toolType}`] = tools.reduce( + (values, tool) => { + values[tool] = true; + return values; + }, + {} as Record, + ); + } + updatedState[`enabled${toolType}`] = Object.entries( + updatedState[`_enabled${toolType}`]!, + ) + .filter(([_, value]) => value) + .map(([key]) => key); + return updatedState; + }); + }, + setTools: (toolType, tools, availableTools) => + set(() => ({ + [`enabled${toolType}`]: tools, + [`_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: `li.fi-widget-settings`, + version: 2, + partialize: (state) => { + const { enabledBridges, enabledExchanges, ...partializedState } = state; + return partializedState; + }, + merge: (persistedState: any, currentState: SettingsState) => { + const state = { ...currentState, ...persistedState }; + SettingsToolTypes.forEach((toolType) => { + state[`enabled${toolType}`] = Object.entries( + persistedState[`_enabled${toolType}`], + ) + .filter(([_, value]) => value) + .map(([key]) => key); + }); + return state; + }, + migrate: (persistedState: any, version) => { + if (version === 0 && persistedState.appearance === 'system') { + persistedState.appearance = defaultSettings.appearance; + } + if (version === 1) { + persistedState.slippage = defaultConfigurableSettings.slippage; + } + return persistedState as SettingsState; + }, + }, + ) as StateCreator, +); + +export const setDefaultSettings = (config?: WidgetConfig) => { + const { slippage, routePriority, setValue } = useSettingsStore.getState(); + const defaultSlippage = + (config?.slippage || + config?.sdkConfig?.defaultRouteOptions?.slippage || + 0) * 100; + const defaultRoutePriority = + config?.routePriority || config?.sdkConfig?.defaultRouteOptions?.order; + defaultConfigurableSettings.slippage = ( + defaultSlippage || defaultConfigurableSettings.slippage + )?.toString(); + defaultConfigurableSettings.routePriority = + defaultRoutePriority || defaultConfigurableSettings.routePriority; + if (!slippage) { + setValue('slippage', defaultConfigurableSettings.slippage); + } + if (!routePriority) { + setValue('routePriority', defaultConfigurableSettings.routePriority); + } +};