@lifi/widget 3.8.0-beta.2 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/esm/components/GasMessage/GasRefuelMessage.js +2 -2
  3. package/dist/esm/components/GasMessage/GasRefuelMessage.js.map +1 -1
  4. package/dist/esm/components/Search/SearchInput.d.ts +2 -1
  5. package/dist/esm/components/Search/SearchInput.js +3 -3
  6. package/dist/esm/components/Search/SearchInput.js.map +1 -1
  7. package/dist/esm/components/Token/Token.js +7 -3
  8. package/dist/esm/components/Token/Token.js.map +1 -1
  9. package/dist/esm/components/TransactionDetails.js +8 -6
  10. package/dist/esm/components/TransactionDetails.js.map +1 -1
  11. package/dist/esm/config/version.d.ts +1 -1
  12. package/dist/esm/config/version.js +1 -1
  13. package/dist/esm/config/version.js.map +1 -1
  14. package/dist/esm/hooks/useLanguages.js +2 -2
  15. package/dist/esm/hooks/useLanguages.js.map +1 -1
  16. package/dist/esm/hooks/useSettingMonitor.js +3 -2
  17. package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
  18. package/dist/esm/index.d.ts +1 -0
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/pages/SelectEnabledToolsPage.js +3 -6
  22. package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
  23. package/dist/esm/pages/SettingsPage/GasPriceSettings.js +2 -2
  24. package/dist/esm/pages/SettingsPage/GasPriceSettings.js.map +1 -1
  25. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +2 -2
  26. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
  27. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +16 -10
  28. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
  29. package/dist/esm/providers/WidgetProvider/WidgetProvider.js +3 -2
  30. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  31. package/dist/esm/stores/form/types.d.ts +1 -1
  32. package/dist/esm/stores/form/useFieldActions.d.ts +10 -2
  33. package/dist/esm/stores/form/useFieldActions.js +39 -1
  34. package/dist/esm/stores/form/useFieldActions.js.map +1 -1
  35. package/dist/esm/stores/settings/types.d.ts +5 -2
  36. package/dist/esm/stores/settings/types.js.map +1 -1
  37. package/dist/esm/stores/settings/useAppearance.js +3 -2
  38. package/dist/esm/stores/settings/useAppearance.js.map +1 -1
  39. package/dist/esm/stores/settings/useSettingsActions.d.ts +9 -0
  40. package/dist/esm/stores/settings/useSettingsActions.js +77 -0
  41. package/dist/esm/stores/settings/useSettingsActions.js.map +1 -0
  42. package/dist/esm/stores/settings/useSettingsStore.d.ts +0 -2
  43. package/dist/esm/stores/settings/useSettingsStore.js +4 -26
  44. package/dist/esm/stores/settings/useSettingsStore.js.map +1 -1
  45. package/dist/esm/stores/settings/utils/getStateValues.d.ts +2 -0
  46. package/dist/esm/stores/settings/utils/getStateValues.js +15 -0
  47. package/dist/esm/stores/settings/utils/getStateValues.js.map +1 -0
  48. package/dist/esm/types/events.d.ts +21 -4
  49. package/dist/esm/types/events.js +2 -0
  50. package/dist/esm/types/events.js.map +1 -1
  51. package/dist/esm/utils/deepEqual.d.ts +2 -0
  52. package/dist/esm/utils/deepEqual.js +52 -0
  53. package/dist/esm/utils/deepEqual.js.map +1 -0
  54. package/dist/esm/utils/format.d.ts +1 -1
  55. package/dist/esm/utils/format.js +3 -3
  56. package/dist/esm/utils/getPriceImpact.d.ts +9 -0
  57. package/dist/esm/utils/getPriceImpact.js +10 -0
  58. package/dist/esm/utils/getPriceImpact.js.map +1 -0
  59. package/package.json +5 -5
  60. package/src/components/GasMessage/GasRefuelMessage.tsx +3 -2
  61. package/src/components/Search/SearchInput.tsx +4 -1
  62. package/src/components/Token/Token.tsx +7 -8
  63. package/src/components/TransactionDetails.tsx +8 -17
  64. package/src/config/version.ts +1 -1
  65. package/src/hooks/useLanguages.ts +2 -2
  66. package/src/hooks/useSettingMonitor.ts +2 -2
  67. package/src/index.ts +1 -0
  68. package/src/pages/SelectEnabledToolsPage.tsx +6 -10
  69. package/src/pages/SettingsPage/GasPriceSettings.tsx +2 -2
  70. package/src/pages/SettingsPage/RoutePrioritySettings.tsx +2 -2
  71. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +31 -21
  72. package/src/providers/WidgetProvider/WidgetProvider.tsx +5 -3
  73. package/src/stores/form/types.ts +1 -1
  74. package/src/stores/form/useFieldActions.ts +73 -2
  75. package/src/stores/settings/types.ts +7 -2
  76. package/src/stores/settings/useAppearance.ts +3 -5
  77. package/src/stores/settings/useSettingsActions.ts +152 -0
  78. package/src/stores/settings/useSettingsStore.ts +4 -34
  79. package/src/stores/settings/utils/getStateValues.ts +16 -0
  80. package/src/types/events.ts +24 -3
  81. package/src/utils/deepEqual.ts +62 -0
  82. package/src/utils/format.ts +3 -3
  83. package/src/utils/getPriceImpact.ts +26 -0
@@ -12,7 +12,8 @@ import { formatUnits } from 'viem'
12
12
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
13
13
  import { isRouteDone } from '../stores/routes/utils.js'
14
14
  import { getAccumulatedFeeCostsBreakdown } from '../utils/fees.js'
15
- import { formatTokenAmount, formatTokenPrice } from '../utils/format.js'
15
+ import { formatTokenAmount } from '../utils/format.js'
16
+ import { getPriceImpact } from '../utils/getPriceImpact.js'
16
17
  import { Card } from './Card/Card.js'
17
18
  import { CardIconButton } from './Card/CardIconButton.js'
18
19
  import { FeeBreakdownTooltip } from './FeeBreakdownTooltip.js'
@@ -37,22 +38,12 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
37
38
  const { gasCosts, feeCosts, gasCostUSD, feeCostUSD, combinedFeesUSD } =
38
39
  getAccumulatedFeeCostsBreakdown(route)
39
40
 
40
- const fromTokenAmount = formatTokenAmount(
41
- BigInt(route.fromAmount),
42
- route.fromToken.decimals
43
- )
44
- const fromTokenPrice = formatTokenPrice(
45
- fromTokenAmount,
46
- route.fromToken.priceUSD
47
- )
48
- const toTokenAmount = formatTokenAmount(
49
- BigInt(route.toAmount),
50
- route.toToken.decimals
51
- )
52
- const toTokenPrice =
53
- formatTokenPrice(toTokenAmount, route.toToken.priceUSD) || 0.01
54
-
55
- const priceImpact = toTokenPrice / fromTokenPrice - 1
41
+ const priceImpact = getPriceImpact({
42
+ fromAmount: BigInt(route.fromAmount),
43
+ toAmount: BigInt(route.toAmount),
44
+ fromToken: route.fromToken,
45
+ toToken: route.toToken,
46
+ })
56
47
 
57
48
  const feeCollectionStep = route.steps[0].includedSteps.find(
58
49
  (includedStep) => includedStep.tool === 'feeCollection'
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.8.0-beta.2'
2
+ export const version = '3.8.1'
@@ -1,13 +1,13 @@
1
1
  import { useTranslation } from 'react-i18next'
2
2
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
3
3
  import { useSettings } from '../stores/settings/useSettings.js'
4
- import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
4
+ import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
5
5
 
6
6
  export const useLanguages = () => {
7
7
  const { t, i18n } = useTranslation()
8
8
  const { languages } = useWidgetConfig()
9
9
  const { language } = useSettings(['language'])
10
- const setValue = useSettingsStore((state) => state.setValue)
10
+ const { setValue } = useSettingsActions()
11
11
 
12
12
  const sortedLanguages = Object.keys(i18n.store.data).sort()
13
13
 
@@ -1,8 +1,8 @@
1
1
  import { shallow } from 'zustand/shallow'
2
2
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
3
+ import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
3
4
  import {
4
5
  defaultConfigurableSettings,
5
- setDefaultSettings,
6
6
  useSettingsStore,
7
7
  } from '../stores/settings/useSettingsStore.js'
8
8
  import { useTools } from './useTools.js'
@@ -25,8 +25,8 @@ export const useSettingMonitor = () => {
25
25
  shallow
26
26
  )
27
27
  const { tools } = useTools()
28
- const resetSettings = useSettingsStore((state) => state.reset)
29
28
  const config = useWidgetConfig()
29
+ const { setDefaultSettings, resetSettings } = useSettingsActions()
30
30
 
31
31
  const isSlippageChanged = config.slippage
32
32
  ? Number(slippage) !== config.slippage * 100
package/src/index.ts CHANGED
@@ -23,3 +23,4 @@ export { windows95Theme } from './themes/windows95.js'
23
23
  export * from './types/events.js'
24
24
  export type { TokenAmount } from './types/token.js'
25
25
  export * from './types/widget.js'
26
+ export { getPriceImpact } from './utils/getPriceImpact.js'
@@ -27,6 +27,7 @@ import { useDefaultElementId } from '../hooks/useDefaultElementId.js'
27
27
  import { useHeader } from '../hooks/useHeader.js'
28
28
  import { useScrollableContainer } from '../hooks/useScrollableContainer.js'
29
29
  import { useTools } from '../hooks/useTools.js'
30
+ import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
30
31
  import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
31
32
 
32
33
  interface SelectAllCheckboxProps {
@@ -76,16 +77,11 @@ export const SelectEnabledToolsPage: React.FC<{
76
77
  }> = ({ type }) => {
77
78
  const typeKey = type.toLowerCase() as 'bridges' | 'exchanges'
78
79
  const { tools } = useTools()
79
- const [enabledTools, disabledTools, setToolValue, toggleToolKeys] =
80
- useSettingsStore(
81
- (state) => [
82
- state[`_enabled${type}`],
83
- state[`disabled${type}`],
84
- state.setToolValue,
85
- state.toggleToolKeys,
86
- ],
87
- shallow
88
- )
80
+ const { setToolValue, toggleToolKeys } = useSettingsActions()
81
+ const [enabledTools, disabledTools] = useSettingsStore(
82
+ (state) => [state[`_enabled${type}`], state[`disabled${type}`]],
83
+ shallow
84
+ )
89
85
 
90
86
  const { t } = useTranslation()
91
87
  const elementId = useDefaultElementId()
@@ -3,13 +3,13 @@ import { useTranslation } from 'react-i18next'
3
3
  import { CardTabs, Tab } from '../../components/Tabs/Tabs.style.js'
4
4
  import { useSettingMonitor } from '../../hooks/useSettingMonitor.js'
5
5
  import { useSettings } from '../../stores/settings/useSettings.js'
6
- import { useSettingsStore } from '../../stores/settings/useSettingsStore.js'
6
+ import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
7
7
  import { BadgedValue } from './SettingsCard/BadgedValue.js'
8
8
  import { SettingCardExpandable } from './SettingsCard/SettingCardExpandable.js'
9
9
 
10
10
  export const GasPriceSettings: React.FC = () => {
11
11
  const { t } = useTranslation()
12
- const setValue = useSettingsStore((state) => state.setValue)
12
+ const { setValue } = useSettingsActions()
13
13
  const { isGasPriceChanged } = useSettingMonitor()
14
14
  const { gasPrice } = useSettings(['gasPrice'])
15
15
 
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
4
4
  import { CardTabs, Tab } from '../../components/Tabs/Tabs.style.js'
5
5
  import { useSettingMonitor } from '../../hooks/useSettingMonitor.js'
6
6
  import { useSettings } from '../../stores/settings/useSettings.js'
7
- import { useSettingsStore } from '../../stores/settings/useSettingsStore.js'
7
+ import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
8
8
  import { BadgedValue } from './SettingsCard/BadgedValue.js'
9
9
  import { SettingCardExpandable } from './SettingsCard/SettingCardExpandable.js'
10
10
 
@@ -12,7 +12,7 @@ const Priorities: Order[] = ['CHEAPEST', 'FASTEST']
12
12
 
13
13
  export const RoutePrioritySettings: React.FC = () => {
14
14
  const { t } = useTranslation()
15
- const setValue = useSettingsStore((state) => state.setValue)
15
+ const { setValue } = useSettingsActions()
16
16
  const { isRoutePriorityChanged } = useSettingMonitor()
17
17
  const { routePriority } = useSettings(['routePriority'])
18
18
  const currentRoutePriority = routePriority ?? ''
@@ -1,14 +1,12 @@
1
1
  import { Percent, WarningRounded } from '@mui/icons-material'
2
- import { Box, Typography } from '@mui/material'
2
+ import { Box, Typography, debounce } from '@mui/material'
3
3
  import type { ChangeEventHandler, FocusEventHandler } from 'react'
4
- import { useRef, useState } from 'react'
4
+ import { useCallback, useMemo, useRef, useState } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import { useSettingMonitor } from '../../../hooks/useSettingMonitor.js'
7
7
  import { useSettings } from '../../../stores/settings/useSettings.js'
8
- import {
9
- defaultSlippage,
10
- useSettingsStore,
11
- } from '../../../stores/settings/useSettingsStore.js'
8
+ import { useSettingsActions } from '../../../stores/settings/useSettingsActions.js'
9
+ import { defaultSlippage } from '../../../stores/settings/useSettingsStore.js'
12
10
  import { formatSlippage } from '../../../utils/format.js'
13
11
  import { BadgedValue } from '../SettingsCard/BadgedValue.js'
14
12
  import { SettingCardExpandable } from '../SettingsCard/SettingCardExpandable.js'
@@ -24,36 +22,48 @@ export const SlippageSettings: React.FC = () => {
24
22
  const { isSlippageOutsideRecommendedLimits, isSlippageChanged } =
25
23
  useSettingMonitor()
26
24
  const { slippage } = useSettings(['slippage'])
27
- const setValue = useSettingsStore((state) => state.setValue)
25
+ const { setValue } = useSettingsActions()
28
26
  const defaultValue = useRef(slippage)
29
27
  const [focused, setFocused] = useState<'input' | 'button'>()
30
28
 
29
+ const customInputValue =
30
+ !slippage || slippage === defaultSlippage ? '' : slippage
31
+
32
+ const [inputValue, setInputValue] = useState(customInputValue)
33
+
31
34
  const handleDefaultClick = () => {
32
35
  setValue('slippage', formatSlippage(defaultSlippage, defaultValue.current))
33
36
  }
34
37
 
35
- const handleInputUpdate: ChangeEventHandler<HTMLInputElement> = (event) => {
36
- const { value } = event.target
38
+ const debouncedSetValue = useMemo(() => debounce(setValue, 500), [setValue])
37
39
 
38
- setValue(
39
- 'slippage',
40
- formatSlippage(value || defaultSlippage, defaultValue.current, true)
41
- )
42
- }
40
+ const handleInputUpdate: ChangeEventHandler<HTMLInputElement> = useCallback(
41
+ (event) => {
42
+ const { value } = event.target
43
+
44
+ setInputValue(formatSlippage(value, defaultValue.current, true))
45
+
46
+ debouncedSetValue(
47
+ 'slippage',
48
+ formatSlippage(value || defaultSlippage, defaultValue.current, true)
49
+ )
50
+ },
51
+ [debouncedSetValue]
52
+ )
43
53
 
44
54
  const handleInputBlur: FocusEventHandler<HTMLInputElement> = (event) => {
45
55
  setFocused(undefined)
46
56
 
47
57
  const { value } = event.target
48
58
 
49
- setValue(
50
- 'slippage',
51
- formatSlippage(value || defaultSlippage, defaultValue.current)
59
+ const formattedValue = formatSlippage(
60
+ value || defaultSlippage,
61
+ defaultValue.current
52
62
  )
53
- }
63
+ setInputValue(formattedValue === defaultSlippage ? '' : formattedValue)
54
64
 
55
- const customInputValue =
56
- !slippage || slippage === defaultSlippage ? '' : slippage
65
+ setValue('slippage', formattedValue)
66
+ }
57
67
 
58
68
  const badgeColor = isSlippageOutsideRecommendedLimits
59
69
  ? 'warning'
@@ -98,7 +108,7 @@ export const SlippageSettings: React.FC = () => {
98
108
  setFocused('input')
99
109
  }}
100
110
  onBlur={handleInputBlur}
101
- value={customInputValue}
111
+ value={inputValue}
102
112
  autoComplete="off"
103
113
  />
104
114
  </SettingsFieldSet>
@@ -1,7 +1,8 @@
1
- import { type SDKConfig, config, createConfig } from '@lifi/sdk'
1
+ import type { SDKConfig } from '@lifi/sdk'
2
+ import { config, createConfig } from '@lifi/sdk'
2
3
  import { createContext, useContext, useId, useMemo } from 'react'
3
4
  import { version } from '../../config/version.js'
4
- import { setDefaultSettings } from '../../stores/settings/useSettingsStore.js'
5
+ import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
5
6
  import type { WidgetContextProps, WidgetProviderProps } from './types.js'
6
7
 
7
8
  const initialContext: WidgetContextProps = {
@@ -20,6 +21,7 @@ export const WidgetProvider: React.FC<
20
21
  React.PropsWithChildren<WidgetProviderProps>
21
22
  > = ({ children, config: widgetConfig }) => {
22
23
  const elementId = useId()
24
+ const { setDefaultSettings } = useSettingsActions()
23
25
 
24
26
  if (!widgetConfig?.integrator) {
25
27
  throw new Error('Required property "integrator" is missing.')
@@ -69,7 +71,7 @@ export const WidgetProvider: React.FC<
69
71
  integrator: widgetConfig.integrator,
70
72
  }
71
73
  }
72
- }, [elementId, widgetConfig])
74
+ }, [elementId, widgetConfig, setDefaultSettings])
73
75
  return (
74
76
  <WidgetContext.Provider value={value}>{children}</WidgetContext.Provider>
75
77
  )
@@ -62,7 +62,7 @@ export interface FormProps {
62
62
  touchedFields: { [K in FormFieldNames]?: boolean }
63
63
  }
64
64
 
65
- interface ResetOptions {
65
+ export interface ResetOptions {
66
66
  defaultValue?: GenericFormValue
67
67
  }
68
68
 
@@ -1,8 +1,19 @@
1
+ import { useCallback } from 'react'
1
2
  import { shallow } from 'zustand/shallow'
2
- import type { FormActions } from './types.js'
3
+ import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
4
+ import type { FormFieldChanged } from '../../types/events.js'
5
+ import { WidgetEvent } from '../../types/events.js'
6
+ import type {
7
+ DefaultValues,
8
+ FormActions,
9
+ FormFieldNames,
10
+ GenericFormValue,
11
+ SetOptions,
12
+ } from './types.js'
3
13
  import { useFormStore } from './useFormStore.js'
4
14
 
5
15
  export const useFieldActions = () => {
16
+ const emitter = useWidgetEvents()
6
17
  const actions = useFormStore<FormActions>(
7
18
  (store) => ({
8
19
  getFieldValues: store.getFieldValues,
@@ -16,5 +27,65 @@ export const useFieldActions = () => {
16
27
  shallow
17
28
  )
18
29
 
19
- return actions
30
+ const setFieldValueWithEmittedEvents = useCallback(
31
+ (
32
+ fieldName: FormFieldNames,
33
+ newValue: GenericFormValue,
34
+ options?: SetOptions
35
+ ) => {
36
+ const oldValue = actions.getFieldValues(fieldName)[0]
37
+
38
+ actions.setFieldValue(fieldName, newValue, options)
39
+
40
+ if (newValue !== oldValue) {
41
+ emitter.emit(WidgetEvent.FormFieldChanged, {
42
+ fieldName,
43
+ newValue,
44
+ oldValue,
45
+ } as FormFieldChanged)
46
+ }
47
+ },
48
+ [actions, emitter]
49
+ )
50
+
51
+ const setUserAndDefaultValuesWithEmittedEvents = useCallback(
52
+ (formValues: Partial<DefaultValues>) => {
53
+ const formValuesKeys = Object.keys(formValues) as FormFieldNames[]
54
+
55
+ const changedValues = formValuesKeys.reduce(
56
+ (accum, fieldName) => {
57
+ const oldValue = actions.getFieldValues(fieldName)[0]
58
+ const newValue = formValues[fieldName]
59
+
60
+ if (newValue !== oldValue) {
61
+ accum.push({ fieldName, newValue, oldValue })
62
+ }
63
+
64
+ return accum
65
+ },
66
+ [] as {
67
+ fieldName: FormFieldNames
68
+ newValue: GenericFormValue
69
+ oldValue: GenericFormValue
70
+ }[]
71
+ )
72
+
73
+ actions.setUserAndDefaultValues(formValues)
74
+
75
+ changedValues.forEach(({ fieldName, newValue, oldValue }) => {
76
+ emitter.emit(WidgetEvent.FormFieldChanged, {
77
+ fieldName,
78
+ newValue,
79
+ oldValue,
80
+ } as FormFieldChanged)
81
+ })
82
+ },
83
+ [actions, emitter]
84
+ )
85
+
86
+ return {
87
+ ...actions,
88
+ setFieldValue: setFieldValueWithEmittedEvents,
89
+ setUserAndDefaultValues: setUserAndDefaultValuesWithEmittedEvents,
90
+ }
20
91
  }
@@ -9,6 +9,8 @@ export type ValueSetter<S> = <K extends keyof S>(
9
9
  value: S[Extract<K, string>]
10
10
  ) => void
11
11
 
12
+ export type ValueGetter<S> = <K extends keyof S>(key: K) => S[K]
13
+
12
14
  export type ValuesSetter<S> = <K extends keyof S>(
13
15
  values: Record<K, S[Extract<K, string>]>
14
16
  ) => void
@@ -31,9 +33,10 @@ export interface SettingsProps {
31
33
  _enabledExchanges: Record<string, boolean>
32
34
  }
33
35
 
34
- export interface SettingsState extends SettingsProps {
36
+ export interface SettingsActions {
35
37
  setValue: ValueSetter<SettingsProps>
36
- setValues: ValuesSetter<SettingsProps>
38
+ getValue: ValueGetter<SettingsProps>
39
+ getSettings: () => SettingsProps
37
40
  initializeTools(
38
41
  toolType: SettingsToolType,
39
42
  tools: string[],
@@ -44,6 +47,8 @@ export interface SettingsState extends SettingsProps {
44
47
  reset(bridges: string[], exchanges: string[]): void
45
48
  }
46
49
 
50
+ export type SettingsState = SettingsProps & SettingsActions
51
+
47
52
  export interface SendToWalletState {
48
53
  showSendToWallet: boolean
49
54
  }
@@ -1,4 +1,4 @@
1
- import { shallow } from 'zustand/shallow'
1
+ import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
2
2
  import type { Appearance } from '../../types/widget.js'
3
3
  import { useSettingsStore } from './useSettingsStore.js'
4
4
 
@@ -6,10 +6,8 @@ export const useAppearance = (): [
6
6
  Appearance,
7
7
  (appearance: Appearance) => void,
8
8
  ] => {
9
- const [appearance, setValue] = useSettingsStore(
10
- (state) => [state.appearance, state.setValue],
11
- shallow
12
- )
9
+ const { setValue } = useSettingsActions()
10
+ const appearance = useSettingsStore((state) => state.appearance)
13
11
  const setAppearance = (appearance: Appearance) => {
14
12
  setValue('appearance', appearance)
15
13
  }
@@ -0,0 +1,152 @@
1
+ import { useCallback } from 'react'
2
+ import { shallow } from 'zustand/shallow'
3
+ import type { widgetEvents } from '../../hooks/useWidgetEvents.js'
4
+ import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
5
+ import { WidgetEvent } from '../../types/events.js'
6
+ import type { WidgetConfig } from '../../types/widget.js'
7
+ import { deepEqual } from '../../utils/deepEqual.js'
8
+ import type {
9
+ SettingsActions,
10
+ SettingsProps,
11
+ SettingsToolType,
12
+ ValueSetter,
13
+ } from './types.js'
14
+ import {
15
+ defaultConfigurableSettings,
16
+ useSettingsStore,
17
+ } from './useSettingsStore.js'
18
+
19
+ const emitEventOnChange = <T extends (...args: any[]) => any>(
20
+ emitter: typeof widgetEvents,
21
+ actions: Omit<SettingsActions, 'initializeTools'>,
22
+ settingFunction: T,
23
+ ...args: Parameters<T>
24
+ ) => {
25
+ const oldSettings = actions.getSettings()
26
+
27
+ settingFunction(...args)
28
+
29
+ const newSettings = actions.getSettings()
30
+
31
+ if (!deepEqual(oldSettings, newSettings)) {
32
+ ;(Object.keys(oldSettings) as (keyof SettingsProps)[]).forEach(
33
+ (toolKey) => {
34
+ if (!deepEqual(oldSettings[toolKey], newSettings[toolKey])) {
35
+ emitter.emit(WidgetEvent.SettingUpdated, {
36
+ setting: toolKey,
37
+ newValue: newSettings[toolKey],
38
+ oldValue: oldSettings[toolKey],
39
+ newSettings: newSettings,
40
+ oldSettings: oldSettings,
41
+ })
42
+ }
43
+ }
44
+ )
45
+ }
46
+ }
47
+
48
+ export const useSettingsActions = () => {
49
+ const emitter = useWidgetEvents()
50
+ const actions = useSettingsStore(
51
+ (state) => ({
52
+ setValue: state.setValue,
53
+ getValue: state.getValue,
54
+ getSettings: state.getSettings,
55
+ reset: state.reset,
56
+ setToolValue: state.setToolValue,
57
+ toggleToolKeys: state.toggleToolKeys,
58
+ }),
59
+ shallow
60
+ )
61
+
62
+ const setValueWithEmittedEvent = useCallback<ValueSetter<SettingsProps>>(
63
+ (value, newValue) => {
64
+ const setting = value as keyof SettingsProps
65
+ emitEventOnChange(emitter, actions, actions.setValue, setting, newValue)
66
+ },
67
+ [emitter, actions]
68
+ )
69
+
70
+ const setDefaultSettingsWithEmittedEvents = useCallback(
71
+ (config?: WidgetConfig) => {
72
+ const slippage = actions.getValue('slippage')
73
+ const routePriority = actions.getValue('routePriority')
74
+ const gasPrice = actions.getValue('gasPrice')
75
+
76
+ const defaultSlippage =
77
+ (config?.slippage || config?.sdkConfig?.routeOptions?.slippage || 0) *
78
+ 100
79
+ const defaultRoutePriority =
80
+ config?.routePriority || config?.sdkConfig?.routeOptions?.order
81
+
82
+ defaultConfigurableSettings.slippage = (
83
+ defaultSlippage || defaultConfigurableSettings.slippage
84
+ )?.toString()
85
+
86
+ defaultConfigurableSettings.routePriority =
87
+ defaultRoutePriority || defaultConfigurableSettings.routePriority
88
+
89
+ if (!slippage) {
90
+ setValueWithEmittedEvent(
91
+ 'slippage',
92
+ defaultConfigurableSettings.slippage
93
+ )
94
+ }
95
+ if (!routePriority) {
96
+ setValueWithEmittedEvent(
97
+ 'routePriority',
98
+ defaultConfigurableSettings.routePriority
99
+ )
100
+ }
101
+ if (!gasPrice) {
102
+ setValueWithEmittedEvent(
103
+ 'gasPrice',
104
+ defaultConfigurableSettings.gasPrice
105
+ )
106
+ }
107
+ },
108
+ [actions, setValueWithEmittedEvent]
109
+ )
110
+
111
+ const resetWithEmittedEvents = useCallback(
112
+ (bridges: string[], exchanges: string[]) => {
113
+ emitEventOnChange(emitter, actions, actions.reset, bridges, exchanges)
114
+ },
115
+ [emitter, actions]
116
+ )
117
+
118
+ const setToolValueWithEmittedEvents = useCallback(
119
+ (toolType: SettingsToolType, tool: string, value: boolean) => {
120
+ emitEventOnChange(
121
+ emitter,
122
+ actions,
123
+ actions.setToolValue,
124
+ toolType,
125
+ tool,
126
+ value
127
+ )
128
+ },
129
+ [emitter, actions]
130
+ )
131
+
132
+ const toggleToolKeysWithEmittedEvents = useCallback(
133
+ (toolType: SettingsToolType, toolKeys: string[]) => {
134
+ emitEventOnChange(
135
+ emitter,
136
+ actions,
137
+ actions.toggleToolKeys,
138
+ toolType,
139
+ toolKeys
140
+ )
141
+ },
142
+ [emitter, actions]
143
+ )
144
+
145
+ return {
146
+ setValue: setValueWithEmittedEvent,
147
+ setDefaultSettings: setDefaultSettingsWithEmittedEvents,
148
+ resetSettings: resetWithEmittedEvents,
149
+ setToolValue: setToolValueWithEmittedEvents,
150
+ toggleToolKeys: toggleToolKeysWithEmittedEvents,
151
+ }
152
+ }
@@ -1,9 +1,9 @@
1
1
  import type { StateCreator } from 'zustand'
2
2
  import { persist } from 'zustand/middleware'
3
3
  import { createWithEqualityFn } from 'zustand/traditional'
4
- import type { WidgetConfig } from '../../types/widget.js'
5
4
  import type { SettingsProps, SettingsState } from './types.js'
6
5
  import { SettingsToolTypes } from './types.js'
6
+ import { getStateValues } from './utils/getStateValues.js'
7
7
 
8
8
  export const defaultSlippage = '0.5'
9
9
 
@@ -36,16 +36,8 @@ export const useSettingsStore = createWithEqualityFn<SettingsState>(
36
36
  set(() => ({
37
37
  [key]: value,
38
38
  })),
39
- setValues: (values) =>
40
- set((state) => {
41
- const updatedState: SettingsProps = { ...state }
42
- for (const key in values) {
43
- if (Object.hasOwn(state, key)) {
44
- updatedState[key] = values[key]
45
- }
46
- }
47
- return updatedState
48
- }),
39
+ getSettings: () => getStateValues(get()),
40
+ getValue: (key) => get()[key],
49
41
  initializeTools: (toolType, tools, reset) => {
50
42
  if (!tools.length) {
51
43
  return
@@ -141,6 +133,7 @@ export const useSettingsStore = createWithEqualityFn<SettingsState>(
141
133
  }))
142
134
  get().initializeTools('Bridges', bridges, true)
143
135
  get().initializeTools('Exchanges', exchanges, true)
136
+ return { ...get() }
144
137
  },
145
138
  }),
146
139
  {
@@ -187,26 +180,3 @@ export const useSettingsStore = createWithEqualityFn<SettingsState>(
187
180
  ) as StateCreator<SettingsState, [], [], SettingsState>,
188
181
  Object.is
189
182
  )
190
-
191
- export const setDefaultSettings = (config?: WidgetConfig) => {
192
- const { slippage, routePriority, setValue, gasPrice } =
193
- useSettingsStore.getState()
194
- const defaultSlippage =
195
- (config?.slippage || config?.sdkConfig?.routeOptions?.slippage || 0) * 100
196
- const defaultRoutePriority =
197
- config?.routePriority || config?.sdkConfig?.routeOptions?.order
198
- defaultConfigurableSettings.slippage = (
199
- defaultSlippage || defaultConfigurableSettings.slippage
200
- )?.toString()
201
- defaultConfigurableSettings.routePriority =
202
- defaultRoutePriority || defaultConfigurableSettings.routePriority
203
- if (!slippage) {
204
- setValue('slippage', defaultConfigurableSettings.slippage)
205
- }
206
- if (!routePriority) {
207
- setValue('routePriority', defaultConfigurableSettings.routePriority)
208
- }
209
- if (!gasPrice) {
210
- setValue('gasPrice', defaultConfigurableSettings.gasPrice)
211
- }
212
- }
@@ -0,0 +1,16 @@
1
+ import type { SettingsProps, SettingsState } from '../types.js'
2
+
3
+ export const getStateValues = (state: SettingsState): SettingsProps => ({
4
+ appearance: state.appearance,
5
+ gasPrice: state.gasPrice,
6
+ language: state.language,
7
+ routePriority: state.routePriority,
8
+ enabledAutoRefuel: state.enabledAutoRefuel,
9
+ slippage: state.slippage,
10
+ disabledBridges: [...state.disabledBridges],
11
+ enabledBridges: [...state.enabledBridges],
12
+ _enabledBridges: { ...state._enabledBridges },
13
+ disabledExchanges: [...state.disabledExchanges],
14
+ enabledExchanges: [...state.enabledExchanges],
15
+ _enabledExchanges: { ...state._enabledExchanges },
16
+ })