diff --git a/components/Layout.tsx b/components/Layout.tsx index 06040d19..67b30e12 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -75,12 +75,7 @@ const Layout = ({ children }: { children: ReactNode }) => { isCollapsed ? 'md:pl-[64px]' : 'md:pl-44 lg:pl-48 xl:pl-52' }`} > -
- next +
{children} diff --git a/components/TokenList.tsx b/components/TokenList.tsx index e0456a1f..9205549c 100644 --- a/components/TokenList.tsx +++ b/components/TokenList.tsx @@ -2,6 +2,7 @@ import { Bank, MangoAccount } from '@blockworks-foundation/mango-v4' import { Transition } from '@headlessui/react' import { ChevronDownIcon, + ChevronRightIcon, EllipsisHorizontalIcon, QuestionMarkCircleIcon, } from '@heroicons/react/20/solid' @@ -11,7 +12,6 @@ import Image from "next/legacy/image"; import { useRouter } from 'next/router' import { Fragment, useCallback, useEffect, useMemo, useState } from 'react' import { useViewport } from '../hooks/useViewport' - import mangoStore from '@store/mangoStore' import { formatDecimal, formatFixedDecimals } from '../utils/numbers' import { breakpoints } from '../utils/theme' @@ -38,6 +38,7 @@ const TokenList = () => { ) const { width } = useViewport() const showTableView = width ? width > breakpoints.md : false + const router = useRouter() const banks = useMemo(() => { if (group) { @@ -74,6 +75,10 @@ const TokenList = () => { } }, [connected]) + const goToTokenPage = (bank: Bank) => { + router.push(`/token/${bank.name}`, undefined, { shallow: true }) + } + return (
@@ -226,6 +231,9 @@ const TokenList = () => { id={i === 0 ? 'account-step-ten' : ''} > + goToTokenPage(bank)}> + +
@@ -247,7 +255,7 @@ const TokenList = () => { export default TokenList const MobileTokenListItem = ({ bank }: { bank: Bank }) => { - const { t } = useTranslation('common') + const { t } = useTranslation(['common', 'token']) const [showTokenDetails, setShowTokenDetails] = useState(false) const jupiterTokens = mangoStore((s) => s.jupiterTokens) const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances) @@ -257,6 +265,7 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => { ) const symbol = bank.name const oraclePrice = bank.uiPrice + const router = useRouter() let logoURI if (jupiterTokens.length) { @@ -282,6 +291,10 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => { const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0.0 + const goToTokenPage = (bank: Bank) => { + router.push(`/token/${bank.name}`, undefined, { shallow: true }) + } + return (
@@ -367,6 +380,15 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {

+
+ goToTokenPage(bank)} + > + {t('token:token-details')} + + +
@@ -386,7 +408,7 @@ const ActionsMenu = ({ const [showBorrowModal, setShowBorrowModal] = useState(false) const [selectedToken, setSelectedToken] = useState('') // const set = mangoStore.getState().set - const router = useRouter() + // const router = useRouter() // const { asPath } = router const jupiterTokens = mangoStore((s) => s.jupiterTokens) diff --git a/components/TopBar.tsx b/components/TopBar.tsx index 1d4369b3..a45f3f84 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -1,11 +1,10 @@ import { useCallback, useState } from 'react' -import { ArrowRightIcon } from '@heroicons/react/20/solid' +import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/20/solid' import { useTranslation } from 'next-i18next' import mangoStore from '@store/mangoStore' import WalletIcon from './icons/WalletIcon' -import MangoAccountsList from './MangoAccountsList' -import { LinkButton } from './shared/Button' +import { IconButton, LinkButton } from './shared/Button' import ConnectedMenu from './wallet/ConnectedMenu' import { ConnectWalletButton } from './wallet/ConnectWalletButton' import { IS_ONBOARDED_KEY } from '../utils/constants' @@ -13,6 +12,7 @@ import useLocalStorageState from '../hooks/useLocalStorageState' import UserSetupModal from './modals/UserSetupModal' import CreateAccountModal from './modals/CreateAccountModal' import MangoAccountsListModal from './modals/MangoAccountsListModal' +import { useRouter } from 'next/router' const TopBar = () => { const { t } = useTranslation('common') @@ -22,6 +22,8 @@ const TopBar = () => { const [showUserSetupModal, setShowUserSetupModal] = useState(false) const [showCreateAccountModal, setShowCreateAccountModal] = useState(false) const [showMangoAccountsModal, setShowMangoAccountsModal] = useState(false) + const router = useRouter() + const { query } = router const handleCloseModal = useCallback(() => { setShowUserSetupModal(false) @@ -34,7 +36,23 @@ const TopBar = () => { return ( <>
- + + {query.token ? ( +
+ router.back()} hideBg size="small"> + + +
+ ) : null} + next {!connected ? ( @@ -52,9 +70,8 @@ const TopBar = () => { {connected ? (
- {/* */}
@@ -326,4 +350,4 @@ const TokenList = () => { ) } -export default TokenList +export default TokenStats diff --git a/components/token/PriceChart.tsx b/components/token/PriceChart.tsx new file mode 100644 index 00000000..e3105d4e --- /dev/null +++ b/components/token/PriceChart.tsx @@ -0,0 +1,88 @@ +import { formatDateAxis } from '@components/shared/DetailedAreaChart' +import { useTheme } from 'next-themes' +import { useMemo } from 'react' +import { Area, AreaChart, ResponsiveContainer, XAxis, YAxis } from 'recharts' +import { COLORS } from 'styles/colors' + +const PriceChart = ({ + prices, + daysToShow, +}: { + prices: number[][] + daysToShow: number +}) => { + const { theme } = useTheme() + + const change = useMemo(() => { + return prices[prices.length - 1][1] - prices[0][1] + }, [prices]) + + return ( +
+
+ + + + + = 0 ? COLORS.GREEN[theme] : COLORS.RED[theme] + } + stopOpacity={0.15} + /> + = 0 ? COLORS.GREEN[theme] : COLORS.RED[theme] + } + stopOpacity={0} + /> + + + = 0 ? COLORS.GREEN[theme] : COLORS.RED[theme]} + strokeWidth={1.5} + fill="url(#gradientArea)" + /> + formatDateAxis(d, daysToShow)} + /> + `$${x.toFixed(2)}`} + tickLine={false} + /> + + +
+
+ ) +} + +export default PriceChart diff --git a/components/wallet/ConnectedMenu.tsx b/components/wallet/ConnectedMenu.tsx index 60bf11b5..bec9f783 100644 --- a/components/wallet/ConnectedMenu.tsx +++ b/components/wallet/ConnectedMenu.tsx @@ -1,6 +1,7 @@ import { Menu, Transition } from '@headlessui/react' import { ArrowRightOnRectangleIcon, + CurrencyDollarIcon, UserCircleIcon, } from '@heroicons/react/20/solid' import { useWallet } from '@solana/wallet-adapter-react' @@ -14,10 +15,12 @@ import { PublicKey } from '@solana/web3.js' import { useViewport } from 'hooks/useViewport' import { breakpoints } from '../../utils/theme' import EditProfileModal from '@components/modals/EditProfileModal' +import MangoAccountsListModal from '@components/modals/MangoAccountsListModal' const ConnectedMenu = () => { const { t } = useTranslation('common') const [showEditProfileModal, setShowEditProfileModal] = useState(false) + const [showMangoAccountsModal, setShowMangoAccountsModal] = useState(false) const set = mangoStore((s) => s.set) const { publicKey, disconnect, wallet } = useWallet() const actions = mangoStore((s) => s.actions) @@ -106,15 +109,17 @@ const ConnectedMenu = () => { - {/* + {isMobile ? ( + - */} + + ) : null} {/* + + + + + +
+
+

{t('token:lending')}

+
+

{t('total-deposits')}

+

+ {formatFixedDecimals(bank.uiDeposits())} +

+
+
+

{t('token:total-value')}

+

+ {formatFixedDecimals(bank.uiDeposits() * bank.uiPrice, true)} +

+
+
+

{t('deposit-rate')}

+

+ {formatDecimal(bank.getDepositRateUi(), 2, { + fixed: true, + })} + % +

+
+
+
+

{t('token:borrowing')}

+
+

{t('total-borrows')}

+

+ {formatFixedDecimals(bank.uiBorrows())} +

+
+
+

{t('token:total-value')}

+

+ {formatFixedDecimals(bank.uiBorrows() * bank.uiPrice, true)} +

+
+
+

{t('borrow-rate')}

+

+ {formatDecimal(bank.getBorrowRateUi(), 2, { + fixed: true, + })} + % +

+
+
+
+
+ +

{t('utilization')}:

+
+ + {bank.uiDeposits() > 0 + ? formatDecimal( + (bank.uiBorrows() / bank.uiDeposits()) * 100, + 1, + { fixed: true } + ) + : '0.0'} + % + +
+
+

About {bank.name}

+
+

+ {parse(coingeckoData.description.en)} +

+ setShowFullDesc(!showFullDesc)} + > + {showFullDesc ? 'Less' : 'More'} + + +
+
+ {!loadingChart ? ( + coingeckoTokenPrices.length ? ( + <> +
+

{bank.name} Price Chart

+ handleDaysToShow(v)} + /> +
+ + + ) : bank?.name === 'USDC' || bank?.name === 'USDT' ? null : ( +

{t('unavailable')}

+ ) + ) : ( +
+ )} +
+
+

{bank.name} Stats

+
+
+
+

{t('token:market-cap')}

+

+ {formatFixedDecimals(market_cap.usd, true)}{' '} + + #{coingeckoData.market_cap_rank} + +

+
+
+

{t('token:volume')}

+

+ {formatFixedDecimals(total_volume.usd, true)} +

+
+
+

{t('token:all-time-high')}

+
+
+ + {formatFixedDecimals(ath.usd, true)} + + +
+

+ {dayjs(ath_date.usd).format('MMM, D, YYYY')} ( + {dayjs(ath_date.usd).fromNow()}) +

+
+
+
+

{t('token:all-time-low')}

+
+
+ + {formatFixedDecimals(atl.usd, true)} + + +
+

+ {dayjs(atl_date.usd).format('MMM, D, YYYY')} ( + {dayjs(atl_date.usd).fromNow()}) +

+
+
+
+
+ {fully_diluted_valuation.usd ? ( +
+

{t('token:fdv')}

+

+ {formatFixedDecimals(fully_diluted_valuation.usd, true)} +

+
+ ) : null} +
+

{t('token:circulating-supply')}

+

+ {formatFixedDecimals(circulating_supply)} +

+
+
+

{t('token:total-supply')}

+

+ {formatFixedDecimals(total_supply)} +

+
+ {max_supply ? ( +
+

{t('token:max-supply')}

+

+ {formatFixedDecimals(max_supply)} +

+
+ ) : null} +
+
+ + ) : loading ? ( +
+ +
+ + +
+ +
+ ) : ( +
+

😔

+

{t('token:token-not-found')}

+

+ {t('token:token-not-found-desc', { token: token })} +

+ + {t('token:go-to-account')} + +
+ )} + {showDepositModal ? ( + setShowDepositModal(false)} + token={bank!.name} + /> + ) : null} + {showBorrowModal ? ( + setShowBorrowModal(false)} + token={bank!.name} + /> + ) : null} +
+ ) +} + +export default Token diff --git a/public/locales/en/token.json b/public/locales/en/token.json new file mode 100644 index 00000000..a3a4f7ae --- /dev/null +++ b/public/locales/en/token.json @@ -0,0 +1,17 @@ +{ + "all-time-high": "All-time High", + "all-time-low": "All-time Low", + "borrowing": "Borrowing", + "circulating-supply": "Circulating Supply", + "fdv": "Fully Diluted Value", + "go-to-account": "Go To Account", + "lending": "Lending", + "market-cap": "Market Cap", + "max-supply": "Max Supply", + "token-details": "Token Details", + "token-not-found": "Token Not Found", + "token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.", + "total-supply": "Total Supply", + "total-value": "Total Value", + "volume": "24h Volume" +} \ No newline at end of file diff --git a/public/locales/es/token.json b/public/locales/es/token.json new file mode 100644 index 00000000..a3a4f7ae --- /dev/null +++ b/public/locales/es/token.json @@ -0,0 +1,17 @@ +{ + "all-time-high": "All-time High", + "all-time-low": "All-time Low", + "borrowing": "Borrowing", + "circulating-supply": "Circulating Supply", + "fdv": "Fully Diluted Value", + "go-to-account": "Go To Account", + "lending": "Lending", + "market-cap": "Market Cap", + "max-supply": "Max Supply", + "token-details": "Token Details", + "token-not-found": "Token Not Found", + "token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.", + "total-supply": "Total Supply", + "total-value": "Total Value", + "volume": "24h Volume" +} \ No newline at end of file diff --git a/public/locales/ru/token.json b/public/locales/ru/token.json new file mode 100644 index 00000000..a3a4f7ae --- /dev/null +++ b/public/locales/ru/token.json @@ -0,0 +1,17 @@ +{ + "all-time-high": "All-time High", + "all-time-low": "All-time Low", + "borrowing": "Borrowing", + "circulating-supply": "Circulating Supply", + "fdv": "Fully Diluted Value", + "go-to-account": "Go To Account", + "lending": "Lending", + "market-cap": "Market Cap", + "max-supply": "Max Supply", + "token-details": "Token Details", + "token-not-found": "Token Not Found", + "token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.", + "total-supply": "Total Supply", + "total-value": "Total Value", + "volume": "24h Volume" +} \ No newline at end of file diff --git a/public/locales/zh/token.json b/public/locales/zh/token.json new file mode 100644 index 00000000..a3a4f7ae --- /dev/null +++ b/public/locales/zh/token.json @@ -0,0 +1,17 @@ +{ + "all-time-high": "All-time High", + "all-time-low": "All-time Low", + "borrowing": "Borrowing", + "circulating-supply": "Circulating Supply", + "fdv": "Fully Diluted Value", + "go-to-account": "Go To Account", + "lending": "Lending", + "market-cap": "Market Cap", + "max-supply": "Max Supply", + "token-details": "Token Details", + "token-not-found": "Token Not Found", + "token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.", + "total-supply": "Total Supply", + "total-value": "Total Value", + "volume": "24h Volume" +} \ No newline at end of file diff --git a/public/locales/zh_tw/token.json b/public/locales/zh_tw/token.json new file mode 100644 index 00000000..a3a4f7ae --- /dev/null +++ b/public/locales/zh_tw/token.json @@ -0,0 +1,17 @@ +{ + "all-time-high": "All-time High", + "all-time-low": "All-time Low", + "borrowing": "Borrowing", + "circulating-supply": "Circulating Supply", + "fdv": "Fully Diluted Value", + "go-to-account": "Go To Account", + "lending": "Lending", + "market-cap": "Market Cap", + "max-supply": "Max Supply", + "token-details": "Token Details", + "token-not-found": "Token Not Found", + "token-not-found-desc": "'{{token}}' is either not listed or we're having issues loading the data.", + "total-supply": "Total Supply", + "total-value": "Total Value", + "volume": "24h Volume" +} \ No newline at end of file diff --git a/utils/constants.ts b/utils/constants.ts index ffe01e6e..86319344 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -44,7 +44,8 @@ export const COINGECKO_IDS = [ // { id: 'cope', symbol: 'COPE' }, // { id: 'cardano', symbol: 'ADA' }, { id: 'msol', symbol: 'MSOL' }, - // { id: 'tether', symbol: 'USDT' }, + { id: 'usd-coin', symbol: 'USDC' }, + { id: 'tether', symbol: 'USDT' }, // { id: 'stepn', symbol: 'GMT' }, ] diff --git a/utils/tokens.ts b/utils/tokens.ts index 24205483..fbaa2bb0 100644 --- a/utils/tokens.ts +++ b/utils/tokens.ts @@ -105,3 +105,13 @@ export const fetchNftsFromHolaplexIndexer = async (owner: PublicKey) => { export const formatTokenSymbol = (symbol: string) => symbol === 'MSOL' ? 'mSOL' : symbol === 'SOETH' ? 'soETH' : symbol + +export const LISTED_TOKENS: string[] = [ + 'BTC', + 'ETH', + 'soETH', + 'SOL', + 'MSOL', + 'USDC', + 'USDT', +] diff --git a/yarn.lock b/yarn.lock index 5a829268..6502c7e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3243,6 +3243,36 @@ dom-helpers@^3.4.0: dependencies: "@babel/runtime" "^7.1.2" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@5.0.3, domhandler@^5.0.1, domhandler@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -3333,6 +3363,11 @@ engine.io-parser@~5.0.3: resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== +entities@^4.2.0, entities@^4.3.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -4191,6 +4226,14 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +html-dom-parser@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-3.1.2.tgz#c137c42df80e17d185ff35a806925d96cc73f408" + integrity sha512-mLTtl3pVn3HnqZSZzW3xVs/mJAKrG1yIw3wlp+9bdoZHHLaBRvELdpfShiPVLyjPypq1Fugv2KMDoGHW4lVXnw== + dependencies: + domhandler "5.0.3" + htmlparser2 "8.0.1" + html-parse-stringify@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" @@ -4198,6 +4241,26 @@ html-parse-stringify@^3.0.1: dependencies: void-elements "3.1.0" +html-react-parser@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-3.0.4.tgz#6a6a115a011dfdadd901ca9d2ed80fa5390647e5" + integrity sha512-va68PSmC7uA6PbOEc9yuw5Mu3OHPXmFKUpkLGvUPdTuNrZ0CJZk1s/8X/FaHjswK/6uZghu2U02tJjussT8+uw== + dependencies: + domhandler "5.0.3" + html-dom-parser "3.1.2" + react-property "2.0.0" + style-to-js "1.1.1" + +htmlparser2@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + domutils "^3.0.1" + entities "^4.3.0" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -4261,6 +4324,11 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -5681,6 +5749,11 @@ react-number-format@^4.9.2: dependencies: prop-types "^15.7.2" +react-property@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.0.tgz#2156ba9d85fa4741faf1918b38efc1eae3c6a136" + integrity sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw== + react-qr-reader@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.2.1.tgz#dc89046d1c1a1da837a683dd970de5926817d55b"