feat: integrate widget settings/value (#4499)

* widgets: v2.2.0

* refactor: wrap Widget

* feat: integrate widget settings

* refactor: import from widgets

* fix: better settings integration

* fix: include types

* chore: formatting

* build: bump widgets version

* feat: integrate widget value

* feat: integrate widget token selection

* feat: clean up widget integration

* build: bump widgets version

* refactor: mv widget wrapper to components

* refactor: split widget integrations

* refactor: value -> inputs

* fix: consolidate slippage hooks

* fix: memoize currency search modal

* refactor: clarify widget code

* fix: allow loading token in widget

* fix: add TradeType helpers
This commit is contained in:
Zach Pomerantz 2022-08-26 11:19:51 -07:00 committed by GitHub
parent cbf165dc40
commit 723db9d0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 292 additions and 78 deletions

@ -145,7 +145,7 @@
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.1.1",
"@uniswap/widgets": "^2.3.1",
"@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2",

@ -3,7 +3,7 @@ import { TokenList } from '@uniswap/token-lists'
import TokenSafety from 'components/TokenSafety'
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useState } from 'react'
import { memo, useCallback, useEffect, useState } from 'react'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import { useUserAddedTokens } from 'state/user/hooks'
@ -33,7 +33,7 @@ export enum CurrencyModalView {
tokenSafety,
}
export default function CurrencySearchModal({
export default memo(function CurrencySearchModal({
isOpen,
onDismiss,
onCurrencySelect,
@ -170,4 +170,4 @@ export default function CurrencySearchModal({
{content}
</Modal>
)
}
})

@ -7,7 +7,7 @@ import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import ms from 'ms.macro'
import { darken } from 'polished'
import { useState } from 'react'
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme'
@ -112,8 +112,7 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
const redesignFlag = useRedesignFlag()
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
const userSlippageTolerance = useUserSlippageTolerance()
const setUserSlippageTolerance = useSetUserSlippageTolerance()
const [userSlippageTolerance, setUserSlippageTolerance] = useUserSlippageTolerance()
const [deadline, setDeadline] = useUserTransactionTTL()

@ -0,0 +1,49 @@
import { Currency, SwapWidget } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import { RPC_URLS } from 'constants/networks'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { useMemo } from 'react'
import { useIsDarkMode } from 'state/user/hooks'
import { DARK_THEME, LIGHT_THEME } from 'theme/widget'
import { useSyncWidgetInputs } from './inputs'
import { useSyncWidgetSettings } from './settings'
import { useSyncWidgetTransactions } from './transactions'
export const WIDGET_WIDTH = 320
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
export interface WidgetProps {
defaultToken?: Currency
}
export default function Widget({ defaultToken }: WidgetProps) {
const locale = useActiveLocale()
const darkMode = useIsDarkMode()
const theme = useMemo(() => (darkMode ? DARK_THEME : LIGHT_THEME), [darkMode])
const { provider } = useWeb3React()
const { inputs, tokenSelector } = useSyncWidgetInputs(defaultToken)
const { settings } = useSyncWidgetSettings()
const { transactions } = useSyncWidgetTransactions()
return (
<>
<SwapWidget
hideConnectionUI
jsonRpcUrlMap={RPC_URLS}
routerUrl={WIDGET_ROUTER_URL}
width={WIDGET_WIDTH}
locale={locale}
theme={theme}
// defaultChainId is excluded - it is always inferred from the passed provider
provider={provider}
{...inputs}
{...settings}
{...transactions}
/>
{tokenSelector}
</>
)
}

@ -0,0 +1,88 @@
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { useCallback, useMemo, useState } from 'react'
/**
* Integrates the Widget's inputs.
* Treats the Widget as a controlled component, using the app's own token selector for selection.
*/
export function useSyncWidgetInputs(defaultToken?: Currency) {
const [type, setType] = useState(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<string>()
const onAmountChange = useCallback((field: Field, amount: string) => {
setType(toTradeType(field))
setAmount(amount)
}, [])
const [tokens, setTokens] = useState<{ [Field.INPUT]?: Currency; [Field.OUTPUT]?: Currency }>({
[Field.OUTPUT]: defaultToken,
})
const onSwitchTokens = useCallback(() => {
setType((type) => invertTradeType(type))
setTokens((tokens) => ({
[Field.INPUT]: tokens[Field.OUTPUT],
[Field.OUTPUT]: tokens[Field.INPUT],
}))
}, [])
const [selectingField, setSelectingField] = useState<Field>()
const otherField = useMemo(() => (selectingField === Field.INPUT ? Field.OUTPUT : Field.INPUT), [selectingField])
const [selectingToken, otherToken] = useMemo(() => {
if (selectingField === undefined) return [undefined, undefined]
return [tokens[selectingField], tokens[otherField]]
}, [otherField, selectingField, tokens])
const onTokenSelectorClick = useCallback((field: Field) => {
setSelectingField(field)
return false
}, [])
const onTokenSelect = useCallback(
(token: Currency) => {
if (selectingField === undefined) return
setType(TradeType.EXACT_INPUT)
setTokens(() => {
return {
[otherField]: token === otherToken ? selectingToken : otherToken,
[selectingField]: token,
}
})
},
[otherField, otherToken, selectingField, selectingToken]
)
const tokenSelector = (
<CurrencySearchModal
isOpen={selectingField !== undefined}
onDismiss={() => setSelectingField(undefined)}
selectedCurrency={selectingToken}
otherSelectedCurrency={otherToken}
onCurrencySelect={onTokenSelect}
/>
)
const value: SwapController = useMemo(() => ({ type, amount, ...tokens }), [amount, tokens, type])
const valueHandlers: SwapEventHandlers = useMemo(
() => ({ onAmountChange, onSwitchTokens, onTokenSelectorClick }),
[onAmountChange, onSwitchTokens, onTokenSelectorClick]
)
return { inputs: { value, ...valueHandlers }, tokenSelector }
}
// TODO(zzmp): Move to @uniswap/widgets.
function toTradeType(modifiedField: Field) {
switch (modifiedField) {
case Field.INPUT:
return TradeType.EXACT_INPUT
case Field.OUTPUT:
return TradeType.EXACT_OUTPUT
}
}
// TODO(zzmp): Include in @uniswap/sdk-core (on TradeType, if possible).
function invertTradeType(tradeType: TradeType) {
switch (tradeType) {
case TradeType.EXACT_INPUT:
return TradeType.EXACT_OUTPUT
case TradeType.EXACT_OUTPUT:
return TradeType.EXACT_INPUT
}
}

@ -0,0 +1,57 @@
import { Percent } from '@uniswap/sdk-core'
import { Slippage, SwapEventHandlers, SwapSettingsController } from '@uniswap/widgets'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { useCallback, useMemo, useState } from 'react'
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
/**
* Integrates the Widget's settings, keeping the widget and app settings in sync.
* NB: This acts as an integration layer, so certain values are duplicated in order to translate
* between app and widget representations.
*/
export function useSyncWidgetSettings() {
const [appTtl, setAppTtl] = useUserTransactionTTL()
const [widgetTtl, setWidgetTtl] = useState<number | undefined>(appTtl / 60)
const onTransactionDeadlineChange = useCallback(
(widgetTtl: number | undefined) => {
setWidgetTtl(widgetTtl)
const appTtl = widgetTtl === undefined ? widgetTtl : widgetTtl * 60
setAppTtl(appTtl ?? DEFAULT_DEADLINE_FROM_NOW)
},
[setAppTtl]
)
const [appSlippage, setAppSlippage] = useUserSlippageTolerance()
const [widgetSlippage, setWidgetSlippage] = useState<string | undefined>(
appSlippage === 'auto' ? undefined : appSlippage.toFixed(2)
)
const onSlippageChange = useCallback(
(widgetSlippage: Slippage) => {
setWidgetSlippage(widgetSlippage.max)
if (widgetSlippage.auto || !widgetSlippage.max) {
setAppSlippage('auto')
} else {
setAppSlippage(new Percent(Math.floor(Number(widgetSlippage.max) * 100), 10_000))
}
},
[setAppSlippage]
)
const onSettingsReset = useCallback(() => {
setWidgetTtl(undefined)
setAppTtl(DEFAULT_DEADLINE_FROM_NOW)
setWidgetSlippage(undefined)
setAppSlippage('auto')
}, [setAppSlippage, setAppTtl])
const settings: SwapSettingsController = useMemo(() => {
const auto = appSlippage === 'auto'
return { slippage: { auto, max: widgetSlippage }, transactionTtl: widgetTtl }
}, [widgetSlippage, widgetTtl, appSlippage])
const settingsHandlers: SwapEventHandlers = useMemo(
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange }),
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange]
)
return { settings: { settings, ...settingsHandlers } }
}

@ -0,0 +1,18 @@
import { TransactionReceipt } from '@ethersproject/abstract-provider'
import { TransactionEventHandlers } from '@uniswap/widgets'
import { useMemo } from 'react'
/** Integrates the Widget's transactions, showing the widget's transactions in the app. */
export function useSyncWidgetTransactions() {
// TODO(jfrankfurt): Integrate widget transactions with app transaction tracking.
const txHandlers: TransactionEventHandlers = useMemo(
() => ({
onTxSubmit: (hash: string, tx: unknown) => console.log('onTxSubmit'),
onTxSuccess: (hash: string, receipt: TransactionReceipt) => console.log('onTxSuccess'),
onTxFail: (hash: string, receipt: TransactionReceipt) => console.log('onTxFail'),
}),
[]
)
return { transactions: { ...txHandlers } }
}

@ -119,7 +119,7 @@ export default function SwapModalFooter({
swapQuoteReceivedDate: Date | undefined
}) {
const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch
const isAutoSlippage = useUserSlippageTolerance() === 'auto'
const isAutoSlippage = useUserSlippageTolerance()[0] === 'auto'
const [clientSideRouter] = useClientSideRouter()
const tokenInAmountUsd = useStablecoinValue(trade.inputAmount)?.toFixed(2)
const tokenOutAmountUsd = useStablecoinValue(trade.outputAmount)?.toFixed(2)

@ -1,4 +1,3 @@
import { SwapWidget } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import {
LARGE_MEDIA_BREAKPOINT,
@ -12,20 +11,16 @@ import LoadingTokenDetail from 'components/Tokens/TokenDetails/LoadingTokenDetai
import NetworkBalance from 'components/Tokens/TokenDetails/NetworkBalance'
import TokenDetail from 'components/Tokens/TokenDetails/TokenDetail'
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import Widget, { WIDGET_WIDTH } from 'components/Widget'
import { getChainInfo } from 'constants/chainInfo'
import { L1_CHAIN_IDS, L2_CHAIN_IDS, SupportedChainId, TESTNET_CHAIN_IDS } from 'constants/chains'
import { checkWarning } from 'constants/tokenSafety'
import { useToken } from 'hooks/Tokens'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { useNetworkTokenBalances } from 'hooks/useNetworkTokenBalances'
import { useCallback, useMemo } from 'react'
import { useMemo } from 'react'
import { useParams } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { DARK_THEME, LIGHT_THEME } from 'theme/token-details-widget-theme'
import { ROUTER_URL, RPC_URL_MAP } from 'utils/token-details-widget-config'
const WIDGET_WIDTH = 320
const Footer = styled.div`
display: none;
@media only screen and (max-width: ${LARGE_MEDIA_BREAKPOINT}) {
@ -78,26 +73,13 @@ function NetworkBalances(tokenAddress: string) {
export default function TokenDetails() {
const { tokenAddress } = useParams<{ tokenAddress?: string }>()
const tokenSymbol = useToken(tokenAddress)?.symbol
const darkMode = useIsDarkMode()
const widgetTheme = useMemo(() => (darkMode ? DARK_THEME : LIGHT_THEME), [darkMode])
const locale = useActiveLocale()
const onTxSubmit = useCallback(() => {
console.log('onTxSubmit')
}, [])
const onTxSuccess = useCallback(() => {
console.log('onTxSuccess')
}, [])
const onTxFail = useCallback(() => {
console.log('onTxFail')
}, [])
const token = useToken(tokenAddress)
const tokenWarning = tokenAddress ? checkWarning(tokenAddress) : null
/* network balance handling */
const { data: networkData } = tokenAddress ? NetworkBalances(tokenAddress) : { data: null }
const { chainId: connectedChainId, provider } = useWeb3React()
const { chainId: connectedChainId } = useWeb3React()
const totalBalance = 4.3 // dummy data
const chainsToList = useMemo(() => {
@ -113,7 +95,7 @@ export default function TokenDetails() {
? chainsToList.map((chainId) => {
const amount = networkData[chainId]
const fiatValue = amount // for testing purposes
if (!fiatValue || !tokenSymbol) return null
if (!fiatValue || !token?.symbol) return null
const chainInfo = getChainInfo(chainId)
const networkColor = chainInfo.color
if (!chainInfo) return null
@ -122,7 +104,7 @@ export default function TokenDetails() {
key={chainId}
logoUrl={chainInfo.logoUrl}
balance={'1'}
tokenSymbol={tokenSymbol}
tokenSymbol={token?.symbol}
fiatValue={fiatValue.toSignificant(2)}
label={chainInfo.label}
networkColor={networkColor}
@ -137,22 +119,7 @@ export default function TokenDetails() {
<>
<TokenDetail address={tokenAddress} />
<RightPanel>
<SwapWidget
defaultChainId={connectedChainId}
defaultInputTokenAddress={'NATIVE'}
defaultOutputTokenAddress={tokenAddress}
hideConnectionUI
jsonRpcUrlMap={RPC_URL_MAP}
locale={locale}
onTxSubmit={onTxSubmit}
onTxSuccess={onTxSuccess}
onTxFail={onTxFail}
provider={provider}
routerUrl={ROUTER_URL}
theme={widgetTheme}
// tokenList={[]}
width={WIDGET_WIDTH}
/>
<Widget defaultToken={token ?? undefined} />
{tokenWarning && <TokenSafetyMessage tokenAddress={tokenAddress} warning={tokenWarning} />}
<BalanceSummary address={tokenAddress} totalBalance={totalBalance} networkBalances={balancesByNetwork} />
</RightPanel>

@ -144,10 +144,20 @@ export function useClientSideRouter(): [boolean, (userClientSideRouter: boolean)
return [clientSideRouter, setClientSideRouter]
}
export function useSetUserSlippageTolerance(): (slippageTolerance: Percent | 'auto') => void {
const dispatch = useAppDispatch()
/**
* Return the user's slippage tolerance, from the redux store, and a function to update the slippage tolerance
*/
export function useUserSlippageTolerance(): [Percent | 'auto', (slippageTolerance: Percent | 'auto') => void] {
const userSlippageToleranceRaw = useAppSelector((state) => {
return state.user.userSlippageTolerance
})
const userSlippageTolerance = useMemo(
() => (userSlippageToleranceRaw === 'auto' ? 'auto' : new Percent(userSlippageToleranceRaw, 10_000)),
[userSlippageToleranceRaw]
)
return useCallback(
const dispatch = useAppDispatch()
const setUserSlippageTolerance = useCallback(
(userSlippageTolerance: Percent | 'auto') => {
let value: 'auto' | number
try {
@ -164,19 +174,10 @@ export function useSetUserSlippageTolerance(): (slippageTolerance: Percent | 'au
},
[dispatch]
)
}
/**
* Return the user's slippage tolerance, from the redux store, and a function to update the slippage tolerance
*/
export function useUserSlippageTolerance(): Percent | 'auto' {
const userSlippageTolerance = useAppSelector((state) => {
return state.user.userSlippageTolerance
})
return useMemo(
() => (userSlippageTolerance === 'auto' ? 'auto' : new Percent(userSlippageTolerance, 10_000)),
[userSlippageTolerance]
() => [userSlippageTolerance, setUserSlippageTolerance],
[setUserSlippageTolerance, userSlippageTolerance]
)
}
@ -200,7 +201,7 @@ export function useUserHideClosedPositions(): [boolean, (newHideClosedPositions:
* @param defaultSlippageTolerance the default value to replace auto with
*/
export function useUserSlippageToleranceWithDefault(defaultSlippageTolerance: Percent): Percent {
const allowedSlippage = useUserSlippageTolerance()
const allowedSlippage = useUserSlippageTolerance()[0]
return useMemo(
() => (allowedSlippage === 'auto' ? defaultSlippageTolerance : allowedSlippage),
[allowedSlippage, defaultSlippageTolerance]

@ -17,6 +17,7 @@ export const LIGHT_THEME = {
warning: colorsLight.accentWarning,
error: colorsLight.accentCritical,
}
export const DARK_THEME = {
// surface
container: colorsDark.backgroundSurface,

@ -1,8 +0,0 @@
import { SupportedChainId } from 'constants/chains'
import { RPC_URLS } from 'constants/networks'
export const ROUTER_URL = 'https://api.uniswap.org/v1/'
export const RPC_URL_MAP = Object.keys(RPC_URLS).reduce(
(acc, cur) => ({ ...acc, [cur]: [RPC_URLS[cur as unknown as SupportedChainId]] }),
{}
)

@ -1074,7 +1074,7 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
@ -3678,7 +3678,7 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.24":
"@types/react-redux@^7.1.20", "@types/react-redux@^7.1.24":
version "7.1.24"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==
@ -4212,10 +4212,10 @@
"@uniswap/v3-core" "1.0.0"
"@uniswap/v3-periphery" "^1.0.1"
"@uniswap/widgets@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.1.1.tgz#13aea0255b7516d86de6838d4de2507a9f84e0df"
integrity sha512-ZtJhZni1t1tM0ZsAu5INFDMa588RmRkXby8DAOou/RXz3dzh7jTKm7+wEph6QE9NE5eTdVvVgqbwH6QPLR3nuQ==
"@uniswap/widgets@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.3.1.tgz#e9973cb34d19534ccf7a7dbe79de63bc5fe7e1e9"
integrity sha512-b6eL/fIjAkj52dj0TVcVF5FeQ1r3dAtPNoO4Go4zABVHlj6c4m6dariY3VQBkNnd2vwt40XBELtiuYXZn3DGjA==
dependencies:
"@babel/runtime" "^7.17.0"
"@fontsource/ibm-plex-mono" "^4.5.1"
@ -4251,12 +4251,17 @@
polished "^3.3.2"
popper-max-size-modifier "^0.2.0"
qrcode "^1.5.0"
react "^17.0.1"
react-dom "^17.0.1"
react-feather "^2.0.8"
react-popper "^2.2.3"
react-redux "^7.2.2"
react-virtualized-auto-sizer "^1.0.2"
react-window "^1.8.5"
rebass "^4.0.7"
redux "^4.1.2"
setimmediate "^1.0.5"
styled-components "^5.3.0"
tiny-invariant "^1.2.0"
wcag-contrast "^3.0.0"
wicg-inert "^3.1.1"
@ -14663,6 +14668,15 @@ react-dev-utils@^11.0.3:
strip-ansi "6.0.0"
text-table "0.2.0"
react-dom@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@ -14758,6 +14772,18 @@ react-query@^3.39.1:
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-redux@^7.2.2:
version "7.2.8"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
integrity sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/react-redux" "^7.1.20"
hoist-non-react-statics "^3.3.2"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^17.0.2"
react-redux@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad"
@ -14926,6 +14952,14 @@ react-window@^1.8.5:
"@babel/runtime" "^7.0.0"
memoize-one ">=3.1.1 <6"
react@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@ -15621,6 +15655,14 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
@ -16426,7 +16468,7 @@ style-loader@1.3.0:
loader-utils "^2.0.0"
schema-utils "^2.7.0"
styled-components@^5.3.5:
styled-components@^5.3.0, styled-components@^5.3.5:
version "5.3.5"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4"
integrity sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==