diff --git a/.changelog/1246.feature.md b/.changelog/1246.feature.md new file mode 100644 index 000000000..fba6defd8 --- /dev/null +++ b/.changelog/1246.feature.md @@ -0,0 +1 @@ +Initial support for displaying account names diff --git a/.changelog/1290.trivial.md b/.changelog/1290.trivial.md new file mode 100644 index 000000000..4182da1ae --- /dev/null +++ b/.changelog/1290.trivial.md @@ -0,0 +1 @@ +Update how links and hashes are displayed on mobile diff --git a/.env b/.env index fe601435e..955c28894 100644 --- a/.env +++ b/.env @@ -6,23 +6,24 @@ REACT_APP_BUILD_VERSION= # REACT_APP_TESTNET_API=https://testnet.nexus.stg.oasis.io/v1/ REACT_APP_API=https://nexus.oasis.io/v1/ REACT_APP_TESTNET_API=https://testnet.nexus.oasis.io/v1/ -REACT_APP_META_TITLE=Oasis Explorer -REACT_APP_META_IMAGE=oasis-og-image.jpg -REACT_APP_META_MANIFEST=app.webmanifest -REACT_APP_META_FAVICON=favicon.ico -REACT_APP_META_APPLE_ICON=logo192.png -REACT_APP_META_DESC=Official explorer for the Oasis Network. -REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity -REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol -REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord -REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ -REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/ -# REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/oasisprotocol -# REACT_APP_SOCIAL_DOCS=https://oasisprotocol.org/developers#overview -# REACT_APP_SOCIAL_HOME=https://oasisprotocol.org/ -REACT_APP_PRODUCTION_URLS=https://explorer.oasis.io, https://explorer.prd.oasis.io -REACT_APP_STAGING_URLS=https://explorer.stg.oasis.io -REACT_APP_SHOW_BUILD_BANNERS=true +REACT_APP_META_TITLE=Pontus-X Explorer +REACT_APP_META_IMAGE=pontusx-og-image.jpg +REACT_APP_META_MANIFEST=pontusx.webmanifest +REACT_APP_META_FAVICON=pontusx/favicon.ico +REACT_APP_META_APPLE_ICON=pontusx/logo192.png +REACT_APP_META_DESC=Official explorer for the Pontus-X Network. +# REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity +# REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol +# REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord +# REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ +# REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/ +REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/76729586 +REACT_APP_SOCIAL_DOCS=https://docs.pontus-x.eu/ +REACT_APP_SOCIAL_HOME=https://www.pontus-x.eu/ +#REACT_APP_PRODUCTION_URLS=https://explorer.oasis.io, https://explorer.prd.oasis.io +#REACT_APP_STAGING_URLS=https://explorer.stg.oasis.io +REACT_APP_SHOW_BUILD_BANNERS=false # REACT_APP_FIXED_NETWORK=testnet -# REACT_APP_FIXED_LAYER=sapphire -REACT_APP_SHOW_FIAT_VALUES=true +REACT_APP_FIXED_LAYER=pontusx +REACT_APP_SHOW_FIAT_VALUES=false +REACT_APP_SHOW_PRIVACY_POLICY=true diff --git a/.env.production b/.env.production index 160a2da18..596fcd2d9 100644 --- a/.env.production +++ b/.env.production @@ -1,23 +1,24 @@ REACT_APP_BUILD_DATETIME= REACT_APP_BUILD_SHA= REACT_APP_BUILD_VERSION= -REACT_APP_META_TITLE=Oasis Explorer -REACT_APP_META_IMAGE=oasis-og-image.jpg -REACT_APP_META_MANIFEST=app.webmanifest -REACT_APP_META_FAVICON=favicon.ico -REACT_APP_META_APPLE_ICON=logo192.png -REACT_APP_META_DESC=Official explorer for the Oasis Network. -REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity -REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol -REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord -REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ -REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/ -# REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/oasisprotocol -# REACT_APP_SOCIAL_DOCS=https://oasisprotocol.org/developers#overview -# REACT_APP_SOCIAL_HOME=https://oasisprotocol.org/ -REACT_APP_PRODUCTION_URLS=https://explorer.oasis.io, https://explorer.prd.oasis.io -REACT_APP_STAGING_URLS=https://explorer.stg.oasis.io -REACT_APP_SHOW_BUILD_BANNERS=true +REACT_APP_META_TITLE=Pontus-X Explorer +REACT_APP_META_IMAGE=pontusx-og-image.jpg +REACT_APP_META_MANIFEST=pontusx.webmanifest +REACT_APP_META_FAVICON=pontusx/favicon.ico +REACT_APP_META_APPLE_ICON=pontusx/logo192.png +REACT_APP_META_DESC=Official explorer for the Pontus-X Network. +# REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity +# REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol +# REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord +# REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ +# REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/ +REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/76729586 +REACT_APP_SOCIAL_DOCS=https://docs.pontus-x.eu/ +REACT_APP_SOCIAL_HOME=https://www.pontus-x.eu/ +# REACT_APP_PRODUCTION_URLS=https://explorer.oasis.io, https://explorer.prd.oasis.io +# REACT_APP_STAGING_URLS=https://explorer.stg.oasis.io +REACT_APP_SHOW_BUILD_BANNERS=false # REACT_APP_FIXED_NETWORK=testnet -# REACT_APP_FIXED_LAYER=sapphire -REACT_APP_SHOW_FIAT_VALUES=true +REACT_APP_FIXED_LAYER=pontusx +REACT_APP_SHOW_FIAT_VALUES=false +REACT_APP_SHOW_PRIVACY_POLICY=true diff --git a/package.json b/package.json index 9d75b9755..7290c0aba 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "axios": "1.6.7", "bignumber.js": "9.1.2", "bip39": "^3.1.0", + "chance": "^1.1.11", "date-fns": "3.3.1", "ethers": "^6.11.1", "i18next": "23.10.0", @@ -88,7 +89,9 @@ }, "devDependencies": { "@emotion/jest": "11.11.0", + "@mdx-js/react": "^1.6.22", "@parcel/packager-raw-url": "2.11.0", + "@parcel/transformer-mdx": "2.11.0", "@parcel/transformer-webmanifest": "2.11.0", "@plasmohq/parcel-transformer-inject-env": "^0.2.11", "@storybook/addon-actions": "7.6.17", @@ -102,6 +105,7 @@ "@testing-library/jest-dom": "6.4.2", "@testing-library/react": "14.2.1", "@testing-library/user-event": "14.5.2", + "@types/chance": "^1.1.6", "@types/jest": "^29.5.12", "@types/node": "20.11.20", "@types/react": "18.2.59", diff --git a/public/pontusx-og-image.jpg b/public/pontusx-og-image.jpg new file mode 100644 index 000000000..3eebcb95f Binary files /dev/null and b/public/pontusx-og-image.jpg differ diff --git a/public/pontusx.webmanifest b/public/pontusx.webmanifest new file mode 100644 index 000000000..b8812a83e --- /dev/null +++ b/public/pontusx.webmanifest @@ -0,0 +1,26 @@ +{ + "$schema": "https://json.schemastore.org/web-manifest-combined.json", + "short_name": "Pontus-X Explorer", + "name": "Official explorer for the Pontus-X Network.", + "icons": [ + { + "src": "./pontusx/favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "./pontusx/logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "./pontusx/logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/public/pontusx/apple-touch.png b/public/pontusx/apple-touch.png new file mode 100644 index 000000000..aa467c6b3 Binary files /dev/null and b/public/pontusx/apple-touch.png differ diff --git a/public/pontusx/favicon-16x16.png b/public/pontusx/favicon-16x16.png new file mode 100644 index 000000000..69da7c66b Binary files /dev/null and b/public/pontusx/favicon-16x16.png differ diff --git a/public/pontusx/favicon-32x32.png b/public/pontusx/favicon-32x32.png new file mode 100644 index 000000000..879e4a58e Binary files /dev/null and b/public/pontusx/favicon-32x32.png differ diff --git a/public/pontusx/favicon.ico b/public/pontusx/favicon.ico new file mode 100644 index 000000000..596497342 Binary files /dev/null and b/public/pontusx/favicon.ico differ diff --git a/public/pontusx/logo192.png b/public/pontusx/logo192.png new file mode 100644 index 000000000..4032ba6f4 Binary files /dev/null and b/public/pontusx/logo192.png differ diff --git a/public/pontusx/logo512.png b/public/pontusx/logo512.png new file mode 100644 index 000000000..88006d2a7 Binary files /dev/null and b/public/pontusx/logo512.png differ diff --git a/public/pontusx/logo_blue.svg b/public/pontusx/logo_blue.svg new file mode 100644 index 000000000..986b51129 --- /dev/null +++ b/public/pontusx/logo_blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/pontusx/logo_only_blue.svg b/public/pontusx/logo_only_blue.svg new file mode 100644 index 000000000..873ccc556 --- /dev/null +++ b/public/pontusx/logo_only_blue.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/pontusx/logo_only_white.svg b/public/pontusx/logo_only_white.svg new file mode 100644 index 000000000..e5d7441ad --- /dev/null +++ b/public/pontusx/logo_only_white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/pontusx/logo_white.svg b/public/pontusx/logo_white.svg new file mode 100644 index 000000000..83e28864b --- /dev/null +++ b/public/pontusx/logo_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/components/Account/AccountLink.tsx b/src/app/components/Account/AccountLink.tsx index b204aa377..abdaa10d0 100644 --- a/src/app/components/Account/AccountLink.tsx +++ b/src/app/components/Account/AccountLink.tsx @@ -1,40 +1,147 @@ -import { FC } from 'react' +import { FC, ReactNode } from 'react' import { Link as RouterLink } from 'react-router-dom' import { useScreenSize } from '../../hooks/useScreensize' import Link from '@mui/material/Link' -import { TrimLinkLabel } from '../TrimLinkLabel' import { RouteUtils } from '../../utils/route-utils' +import InfoIcon from '@mui/icons-material/Info' import Typography from '@mui/material/Typography' import { COLORS } from '../../../styles/theme/colors' import { SearchScope } from '../../../types/searchScope' +import { useAccountName } from '../../hooks/useAccountName' +import { trimLongString } from '../../utils/trimLongString' +import { MaybeWithTooltip } from '../AdaptiveTrimmer/MaybeWithTooltip' +import Box from '@mui/material/Box' +import { HighlightedText } from '../HighlightedText' +import { AdaptiveHighlightedText } from '../HighlightedText/AdaptiveHighlightedText' +import { AdaptiveTrimmer } from '../AdaptiveTrimmer/AdaptiveTrimmer' -export const AccountLink: FC<{ - scope: SearchScope - address: string - alwaysTrim?: boolean +const WithTypographyAndLink: FC<{ + to: string plain?: boolean -}> = ({ scope, address, alwaysTrim, plain }) => { - const { isTablet } = useScreenSize() - const to = RouteUtils.getAccountRoute(scope, address) + mobile?: boolean + children: ReactNode +}> = ({ children, to, plain, mobile }) => { return ( - {alwaysTrim || isTablet ? ( - - ) : plain ? ( - address + {plain ? ( + children ) : ( - {address} + {children} )} ) } + +export const AccountLink: FC<{ + scope: SearchScope + address: string + + /** + * Should we always trim the text to a short line? + */ + alwaysTrim?: boolean + + /** + * Plain mode? (No link required) + */ + plain?: boolean + + /** + * What part of the name should be highlighted (if any) + */ + highlightedPartOfName?: string | undefined + + /** + * Any extra tooltips to display + * + * (Besides the content necessary because of potential shortening) + */ + extraTooltip?: ReactNode +}> = ({ scope, address, alwaysTrim, plain, highlightedPartOfName, extraTooltip }) => { + const { isTablet } = useScreenSize() + const { name: accountName } = useAccountName(scope, address) + const to = RouteUtils.getAccountRoute(scope, address) + + const tooltipPostfix = extraTooltip ? ( + <> + + {extraTooltip} + + ) : undefined + + // Are we in a table? + if (alwaysTrim) { + // In a table, we only ever want one short line + + return ( + + + {accountName} + {address} + {tooltipPostfix} + + ) : ( + address + ) + } + > + {accountName ? trimLongString(accountName, 12, 0) : trimLongString(address, 6, 6)} + + + ) + } + + if (!isTablet) { + // Details in desktop mode. + // We want one long line, with name and address. + + return ( + + + {accountName ? ( + + ({address}) + + ) : ( + address + )} + + + ) + } + + // We need to show the data in details mode on mobile. + // We want two lines, one for name (if available), one for address + // Both line adaptively shortened to fill available space + return ( + + <> + + + + + ) +} diff --git a/src/app/components/Account/ContractCreatorInfo.tsx b/src/app/components/Account/ContractCreatorInfo.tsx index a944c8362..fed858109 100644 --- a/src/app/components/Account/ContractCreatorInfo.tsx +++ b/src/app/components/Account/ContractCreatorInfo.tsx @@ -14,7 +14,11 @@ import Box from '@mui/material/Box' import Skeleton from '@mui/material/Skeleton' import { useScreenSize } from '../../hooks/useScreensize' -const TxSender: FC<{ scope: SearchScope; txHash: string }> = ({ scope, txHash }) => { +const TxSender: FC<{ scope: SearchScope; txHash: string; alwaysTrim?: boolean }> = ({ + scope, + txHash, + alwaysTrim, +}) => { const { t } = useTranslation() if (scope.layer === Layer.consensus) { throw AppErrors.UnsupportedLayer @@ -31,7 +35,7 @@ const TxSender: FC<{ scope: SearchScope; txHash: string }> = ({ scope, txHash }) }} /> ) : senderAddress ? ( - + ) : ( t('common.missing') ) @@ -41,7 +45,8 @@ export const ContractCreatorInfo: FC<{ scope: SearchScope isLoading?: boolean creationTxHash: string | undefined -}> = ({ scope, isLoading, creationTxHash }) => { + alwaysTrim?: boolean +}> = ({ scope, isLoading, creationTxHash, alwaysTrim }) => { const { t } = useTranslation() const { isMobile } = useScreenSize() @@ -59,9 +64,9 @@ export const ContractCreatorInfo: FC<{ minWidth: '25%', }} > - + {t('contract.createdAt')} - + ) } @@ -69,7 +74,8 @@ export const ContractCreatorInfo: FC<{ export const DelayedContractCreatorInfo: FC<{ scope: SearchScope contractOasisAddress: string | undefined -}> = ({ scope, contractOasisAddress }) => { + alwaysTrim?: boolean +}> = ({ scope, contractOasisAddress, alwaysTrim }) => { const accountQuery = useGetRuntimeAccountsAddress( scope.network, scope.layer as Runtime, @@ -82,6 +88,11 @@ export const DelayedContractCreatorInfo: FC<{ const creationTxHash = contract?.eth_creation_tx || contract?.creation_tx return ( - + ) } diff --git a/src/app/components/Account/index.tsx b/src/app/components/Account/index.tsx index 3658ba25d..f614ea69e 100644 --- a/src/app/components/Account/index.tsx +++ b/src/app/components/Account/index.tsx @@ -30,9 +30,17 @@ type AccountProps = { isLoading: boolean tokenPrices: AllTokenPrices showLayer?: boolean + highlightedPartOfName: string | undefined } -export const Account: FC = ({ account, token, isLoading, tokenPrices, showLayer }) => { +export const Account: FC = ({ + account, + token, + isLoading, + tokenPrices, + showLayer, + highlightedPartOfName, +}) => { const { t } = useTranslation() const { isMobile } = useScreenSize() const address = account ? account.address_eth ?? account.address : undefined @@ -67,7 +75,7 @@ export const Account: FC = ({ account, token, isLoading, tokenPric
- +
@@ -100,6 +108,7 @@ export const Account: FC = ({ account, token, isLoading, tokenPric diff --git a/src/app/components/AccountList/index.tsx b/src/app/components/AccountList/index.tsx index 89050ff45..7f467c356 100644 --- a/src/app/components/AccountList/index.tsx +++ b/src/app/components/AccountList/index.tsx @@ -39,7 +39,7 @@ export const AccountList: FC = ({ isLoading, limit, pagination key: 'size', }, { - content: , + content: , key: 'address', }, ...(verbose diff --git a/src/app/components/AdaptiveTrimmer/AdaptiveDynamicTrimmer.tsx b/src/app/components/AdaptiveTrimmer/AdaptiveDynamicTrimmer.tsx new file mode 100644 index 000000000..d020a7fc2 --- /dev/null +++ b/src/app/components/AdaptiveTrimmer/AdaptiveDynamicTrimmer.tsx @@ -0,0 +1,151 @@ +import { FC, ReactNode, useCallback, useEffect, useRef, useState } from 'react' +import Box from '@mui/material/Box' +import InfoIcon from '@mui/icons-material/Info' +import { MaybeWithTooltip } from './MaybeWithTooltip' + +type AdaptiveDynamicTrimmerProps = { + getFullContent: () => { + content: ReactNode + length: number + } + getShortenedContent: (wantedLength: number) => ReactNode + extraTooltip: ReactNode +} + +/** + * Display content, potentially shortened as needed. + * + * This component will do automatic detection of available space, + * and determine the best way to display content accordingly. + * + * The difference compared to AdaptiveTrimmer is that this component + * expects a function to provide a shortened version of the components. + */ +export const AdaptiveDynamicTrimmer: FC = ({ + getFullContent, + getShortenedContent, + extraTooltip, +}) => { + // Initial setup + const textRef = useRef(null) + const { content: fullContent, length: fullLength } = getFullContent() + + // Data about the currently rendered version + const [currentContent, setCurrentContent] = useState() + const [currentLength, setCurrentLength] = useState(0) + + // Known good - this fits + const [largestKnownGood, setLargestKnownGood] = useState(0) + + // Known bad - this doesn't fit + const [smallestKnownBad, setSmallestKnownBad] = useState(fullLength + 1) + + // Are we exploring our possibilities now? + const [inDiscovery, setInDiscovery] = useState(false) + + const attemptContent = useCallback((content: ReactNode, length: number) => { + setCurrentContent(content) + setCurrentLength(length) + }, []) + + const attemptShortenedContent = useCallback( + (wantedLength: number) => attemptContent(getShortenedContent(wantedLength), wantedLength), + [attemptContent, getShortenedContent], + ) + + const initDiscovery = useCallback(() => { + setLargestKnownGood(0) + setSmallestKnownBad(fullLength + 1) + attemptContent(fullContent, fullLength) + setInDiscovery(true) + }, [fullContent, fullLength, attemptContent]) + + useEffect(() => { + initDiscovery() + const handleResize = () => { + initDiscovery() + } + + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, [initDiscovery]) + + useEffect(() => { + if (inDiscovery) { + if (!textRef.current) { + return + } + const isOverflow = textRef.current.scrollWidth > textRef.current.clientWidth + // log('Overflow?', isOverflow) + + if (isOverflow) { + // This is too much + + // Update known bad length + const newSmallestKnownBad = Math.min(currentLength, smallestKnownBad) + setSmallestKnownBad(newSmallestKnownBad) + + // We should try something smaller + attemptShortenedContent(Math.floor((largestKnownGood + newSmallestKnownBad) / 2)) + } else { + // This is OK + + // Update known good length + const newLargestKnownGood = Math.max(currentLength, largestKnownGood) + setLargestKnownGood(currentLength) + + if (currentLength === fullLength) { + // The whole thing fits, so we are good. + setInDiscovery(false) + } else { + if (currentLength + 1 === smallestKnownBad) { + // This the best we can do, for now + setInDiscovery(false) + } else { + // So far, so good, but we should try something longer + attemptShortenedContent(Math.floor((newLargestKnownGood + smallestKnownBad) / 2)) + } + } + } + } + }, [ + attemptShortenedContent, + currentLength, + fullContent, + fullLength, + inDiscovery, + initDiscovery, + largestKnownGood, + smallestKnownBad, + ]) + + const title = + currentLength !== fullLength ? ( + + {fullContent} + {extraTooltip && ( + + + {extraTooltip} + + )} + + ) : ( + extraTooltip + ) + + return ( + + + {currentContent} + + + ) +} diff --git a/src/app/components/AdaptiveTrimmer/AdaptiveTrimmer.tsx b/src/app/components/AdaptiveTrimmer/AdaptiveTrimmer.tsx new file mode 100644 index 000000000..878252654 --- /dev/null +++ b/src/app/components/AdaptiveTrimmer/AdaptiveTrimmer.tsx @@ -0,0 +1,140 @@ +import { FC, ReactNode, useCallback, useEffect, useRef, useState } from 'react' +import Box from '@mui/material/Box' +import InfoIcon from '@mui/icons-material/Info' +import { trimLongString } from '../../utils/trimLongString' +import { MaybeWithTooltip } from './MaybeWithTooltip' + +type AdaptiveTrimmerProps = { + text: string | undefined + strategy: 'middle' | 'end' + extraTooltip?: ReactNode +} + +/** + * Display content, potentially shortened as needed. + * + * This component will do automatic detection of available space, + * and determine the best way to display content accordingly. + */ +export const AdaptiveTrimmer: FC = ({ text = '', strategy = 'end', extraTooltip }) => { + // Initial setup + const fullLength = text.length + const textRef = useRef(null) + + // Data about the currently rendered version + const [currentContent, setCurrentContent] = useState('') + const [currentLength, setCurrentLength] = useState(0) + + // Known good - this fits + const [largestKnownGood, setLargestKnownGood] = useState(0) + + // Known bad - this doesn't fit + const [smallestKnownBad, setSmallestKnownBad] = useState(fullLength + 1) + + // Are we exploring our possibilities now? + const [inDiscovery, setInDiscovery] = useState(false) + + const attemptContent = useCallback((content: string, length: number) => { + setCurrentContent(content) + setCurrentLength(length) + }, []) + + const attemptShortenedContent = useCallback( + (length: number) => { + const content = + strategy === 'middle' + ? trimLongString(text, Math.floor(length / 2) - 1, Math.floor(length / 2) - 1)! + : trimLongString(text, length, 0)! + + attemptContent(content, length) + }, + [strategy, text, attemptContent], + ) + + const initDiscovery = useCallback(() => { + setLargestKnownGood(0) + setSmallestKnownBad(fullLength + 1) + attemptContent(text, fullLength) + setInDiscovery(true) + }, [text, fullLength, attemptContent]) + + useEffect(() => { + initDiscovery() + const handleResize = () => { + initDiscovery() + } + + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, [initDiscovery]) + + useEffect(() => { + if (inDiscovery) { + if (!textRef.current) { + return + } + const isOverflow = textRef.current.scrollWidth > textRef.current.clientWidth + + if (isOverflow) { + // This is too much + + // Update known bad length + const newSmallestKnownBad = Math.min(currentLength, smallestKnownBad) + setSmallestKnownBad(newSmallestKnownBad) + + // We should try something smaller + attemptShortenedContent(Math.floor((largestKnownGood + newSmallestKnownBad) / 2)) + } else { + // This is OK + + // Update known good length + const newLargestKnownGood = Math.max(currentLength, largestKnownGood) + setLargestKnownGood(currentLength) + + if (currentLength === fullLength) { + // The whole thing fits, so we are good. + setInDiscovery(false) + } else { + if (currentLength + 1 === smallestKnownBad) { + // This the best we can do, for now + setInDiscovery(false) + } else { + // So far, so good, but we should try something longer + attemptShortenedContent(Math.floor((newLargestKnownGood + smallestKnownBad) / 2)) + } + } + } + } + }, [inDiscovery, currentLength, largestKnownGood, smallestKnownBad, attemptShortenedContent, fullLength]) + + if (!text) return null + + const title = + currentLength !== fullLength ? ( + + {text} + {extraTooltip && ( + + + {extraTooltip} + + )} + + ) : ( + extraTooltip + ) + + return ( + + + {currentContent} + + + ) +} diff --git a/src/app/components/AdaptiveTrimmer/MaybeWithTooltip.tsx b/src/app/components/AdaptiveTrimmer/MaybeWithTooltip.tsx new file mode 100644 index 000000000..4edfe0c26 --- /dev/null +++ b/src/app/components/AdaptiveTrimmer/MaybeWithTooltip.tsx @@ -0,0 +1,65 @@ +import { FC, ReactNode } from 'react' +import Tooltip from '@mui/material/Tooltip' +import Box from '@mui/material/Box' +import { SxProps } from '@mui/material/styles' + +type MaybeWithTooltipProps = { + /** + * Do we want to show the tooltip? + * + * Default is true + */ + tooltipWanted?: boolean + + /** + * What should be the content of the tooltip? + * + * Undefined means no tooltip + */ + title?: ReactNode + + /** + * Any extra styles to apply to the span + */ + spanSx?: SxProps + + /** + * The content to show + */ + children: ReactNode +} + +/** + * A component to display some content with or without a tooltip + */ +export const MaybeWithTooltip: FC = ({ + tooltipWanted = true, + title, + children, + spanSx, +}) => + tooltipWanted && !!title ? ( + + {title} + + } + > + + {children} + + + ) : ( + + {children} + + ) diff --git a/src/app/components/Blocks/BlockLink.tsx b/src/app/components/Blocks/BlockLink.tsx index f9673b7c8..53a55cf7c 100644 --- a/src/app/components/Blocks/BlockLink.tsx +++ b/src/app/components/Blocks/BlockLink.tsx @@ -7,6 +7,7 @@ import { RouteUtils } from '../../utils/route-utils' import { TrimLinkLabel } from '../TrimLinkLabel' import { SearchScope } from '../../../types/searchScope' import { useScreenSize } from '../../hooks/useScreensize' +import { AdaptiveTrimmer } from '../AdaptiveTrimmer/AdaptiveTrimmer' export const BlockLink: FC<{ scope: SearchScope; height: number }> = ({ scope, height }) => ( @@ -23,15 +24,34 @@ export const BlockHashLink: FC<{ alwaysTrim?: boolean }> = ({ scope, hash, height, alwaysTrim }) => { const { isTablet } = useScreenSize() - return ( - - {isTablet || alwaysTrim ? ( - - ) : ( - + const to = RouteUtils.getBlockRoute(scope, height) + + if (alwaysTrim) { + // Table view + return ( + + + + ) + } + + if (!isTablet) { + // Desktop view + return ( + + {hash} - )} + + ) + } + + // Mobile view + return ( + + + + ) } diff --git a/src/app/components/CustomIcons/pontusx.svg b/src/app/components/CustomIcons/pontusx.svg new file mode 100644 index 000000000..063e9756d --- /dev/null +++ b/src/app/components/CustomIcons/pontusx.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/app/components/HighlightedText/AdaptiveHighlightedText.tsx b/src/app/components/HighlightedText/AdaptiveHighlightedText.tsx new file mode 100644 index 000000000..0676bfc26 --- /dev/null +++ b/src/app/components/HighlightedText/AdaptiveHighlightedText.tsx @@ -0,0 +1,66 @@ +import { FC, ReactNode } from 'react' +import InfoIcon from '@mui/icons-material/Info' +import { HighlightedText, HighlightOptions } from './index' +import { AdaptiveDynamicTrimmer } from '../AdaptiveTrimmer/AdaptiveDynamicTrimmer' +import { HighlightedTrimmedText } from './HighlightedTrimmedText' + +type AdaptiveHighlightedTextProps = { + /** + * The text to display + */ + text: string | undefined + + /** + * The pattern to search for (and highlight) + */ + pattern: string | undefined + + /** + * Options for highlighting (case sensitivity, styling, etc.) + * + * (This is optional, sensible defaults are provided.) + */ + options?: HighlightOptions + + /** + * Extra content to put into the tooltip + */ + extraTooltip?: ReactNode +} + +/** + * Display a text with a part highlighted, potentially trimmed to an adaptive length around the highlight + */ +export const AdaptiveHighlightedText: FC = ({ + text, + pattern, + options, + extraTooltip, +}) => { + const fullContent = + + return text ? ( + ({ + content: fullContent, + length: text.length, + })} + getShortenedContent={wantedLength => ( + + )} + extraTooltip={ + extraTooltip ? ( + <> + + {extraTooltip} + + ) : undefined + } + /> + ) : undefined +} diff --git a/src/app/components/HighlightedText/HighlightedTrimmedText.tsx b/src/app/components/HighlightedText/HighlightedTrimmedText.tsx new file mode 100644 index 000000000..ff7c816b9 --- /dev/null +++ b/src/app/components/HighlightedText/HighlightedTrimmedText.tsx @@ -0,0 +1,43 @@ +import { FC } from 'react' + +import { HighlightedText, HighlightOptions } from './index' +import { cutAroundMatch } from './text-cutting' + +type HighlightedTrimmedTextProps = { + /** + * The text to display + */ + text: string | undefined + + /** + * The pattern to search for (and highlight) + */ + pattern: string | undefined + + /** + * Options for highlighting (case sensitivity, styling, etc.) + * + * (This is optional, sensible defaults are provided.) + */ + options?: HighlightOptions + + /** + * What should be the length of the fragment delivered, which + * has the pattern inside it? + */ + fragmentLength: number +} + +/** + * Display a text with a part highlighted, potentially trimmed to shorter length around the highlight + */ +export const HighlightedTrimmedText: FC = props => { + const { text, pattern, fragmentLength, options } = props + return ( + + ) +} diff --git a/src/app/components/HighlightedText/text-cutting.ts b/src/app/components/HighlightedText/text-cutting.ts new file mode 100644 index 000000000..cb7555fa6 --- /dev/null +++ b/src/app/components/HighlightedText/text-cutting.ts @@ -0,0 +1,74 @@ +import { findTextMatch, NormalizerOptions } from './text-matching' + +export interface CutAroundOptions extends NormalizerOptions { + /** + * What should be the length of the fragment delivered, which + * has the pattern inside it? + * + * The default value is 80. + */ + fragmentLength?: number +} + +/** + * Return a part of the corpus that contains the match to the pattern, if any + * + * If the corpus is undefined or empty, undefined is returned. + * + * If either the pattern is undefined or empty, or there is no match, + * an adequately sized part from the beginning of the corpus is returned. + * + * If there is a match, but the corpus is at most as long as the desired fragment length, + * the whole corpus is returned. + * + * If there is a match, and the corpus is longer than the desired fragment length, + * then a part of a corpus is returned, so that the match is within the returned part, + * around the middle. + */ +export function cutAroundMatch( + corpus: string | undefined, + pattern: string | undefined, + options: CutAroundOptions = {}, +): { + hasMatch: boolean + part: string | undefined +} { + const { fragmentLength = 80, ...matchOptions } = options + + if (!corpus) { + // there is nothing to see here + return { + hasMatch: false, + part: undefined, + } + } + + // do we have a match? + const match = pattern ? findTextMatch(corpus, [pattern], matchOptions) : undefined + + if (corpus.length <= fragmentLength) { + // the whole corpus fits into the max size, no need to cut. + return { + hasMatch: !!match, + part: corpus, + } + } + + // how much extra space do we have? + const buffer = fragmentLength - (pattern || '').length + + const matchStart = match?.startPos ?? 0 + + // We will start before the start of the match, by buffer / 2 chars + const startPos = Math.max(Math.min(matchStart - Math.floor(buffer / 2), corpus.length - fragmentLength), 0) + const endPos = Math.min(startPos + fragmentLength, corpus.length) + + // compile the result + const part = + (startPos ? '…' : '') + corpus.substring(startPos, endPos) + (endPos < corpus.length - 1 ? '…' : '') + + return { + hasMatch: true, + part, + } +} diff --git a/src/app/components/PageLayout/Footer.tsx b/src/app/components/PageLayout/Footer.tsx index 5c5d3db3d..b89adacf5 100644 --- a/src/app/components/PageLayout/Footer.tsx +++ b/src/app/components/PageLayout/Footer.tsx @@ -4,12 +4,14 @@ import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' import { useScreenSize } from '../../hooks/useScreensize' import { styled } from '@mui/material/styles' +import { Link as RouterLink } from 'react-router-dom' import Link from '@mui/material/Link' import { useTheme } from '@mui/material/styles' import { useConstant } from '../../hooks/useConstant' import { AppendMobileSearch } from '../AppendMobileSearch' import { SearchScope } from '../../../types/searchScope' import { api, github } from '../../utils/externalLinks' +import { showPrivacyPolicy } from '../../../config' const FooterBox = styled(Box)(({ theme }) => ({ display: 'flex', @@ -128,6 +130,13 @@ export const Footer: FC = ({ scope, mobileSearchAction }) => { + {showPrivacyPolicy && ( + + + {t('privacyPolicy.title')} + + + )} {isTablet ? t('footer.mobileTitle') : t('footer.title')} | {currentYear} diff --git a/src/app/components/PageLayout/Logotype.tsx b/src/app/components/PageLayout/Logotype.tsx index 1745e8b0a..e8726fdce 100644 --- a/src/app/components/PageLayout/Logotype.tsx +++ b/src/app/components/PageLayout/Logotype.tsx @@ -4,7 +4,7 @@ import { useScreenSize } from '../../hooks/useScreensize' import { useTheme } from '@mui/material/styles' import Box from '@mui/material/Box' import { Link as RouterLink } from 'react-router-dom' -import { OasisIcon } from '../CustomIcons/OasisIcon' +import pontusXIcon from '../CustomIcons/pontusx.svg' import Typography from '@mui/material/Typography' import { getAppTitle } from '../../../config' @@ -36,7 +36,7 @@ export const Logotype: FC = ({ color, showText }) => { color: color || theme.palette.layout.main, }} > - + Pontus-X logo {showText && ( {getAppTitle()} diff --git a/src/app/components/Search/search-utils.ts b/src/app/components/Search/search-utils.ts index 752c2fc78..c097b1eee 100644 --- a/src/app/components/Search/search-utils.ts +++ b/src/app/components/Search/search-utils.ts @@ -112,6 +112,11 @@ export const validateAndNormalize = { return searchTerm.toLowerCase() } }, + accountNameFragment: (searchTerm: string) => { + if (searchTerm?.length >= textSearchMininumLength) { + return searchTerm.toLowerCase() + } + }, } satisfies { [name: string]: (searchTerm: string) => string | undefined } export function isSearchValid(searchTerm: string) { diff --git a/src/app/components/StyledDescriptionList/index.tsx b/src/app/components/StyledDescriptionList/index.tsx index 2ba5058ac..08865f369 100644 --- a/src/app/components/StyledDescriptionList/index.tsx +++ b/src/app/components/StyledDescriptionList/index.tsx @@ -61,6 +61,8 @@ export const StyledDescriptionList = styled(InlineDescriptionList, { color: COLORS.brandExtraDark, overflowWrap: 'anywhere', alignItems: 'center', + maxWidth: '100%', + overflowX: 'hidden', }, ...(standalone && { '&&': { diff --git a/src/app/components/Tokens/TokenHolders.tsx b/src/app/components/Tokens/TokenHolders.tsx index 77daeae62..8bc52e63b 100644 --- a/src/app/components/Tokens/TokenHolders.tsx +++ b/src/app/components/Tokens/TokenHolders.tsx @@ -53,7 +53,11 @@ export const TokenHolders: FC = ({ { key: 'address', content: ( - + ), }, { diff --git a/src/app/components/Tokens/TokenList.tsx b/src/app/components/Tokens/TokenList.tsx index 676f8aa60..ec2e0bea4 100644 --- a/src/app/components/Tokens/TokenList.tsx +++ b/src/app/components/Tokens/TokenList.tsx @@ -107,7 +107,11 @@ export const TokenList = (props: TokensProps) => { { content: ( - + ), diff --git a/src/app/components/Tokens/TokenTransfers.tsx b/src/app/components/Tokens/TokenTransfers.tsx index fdf69be27..7daecf45b 100644 --- a/src/app/components/Tokens/TokenTransfers.tsx +++ b/src/app/components/Tokens/TokenTransfers.tsx @@ -116,7 +116,7 @@ export const TokenTransfers: FC = ({ ownAddress, }) => { const { t } = useTranslation() - const { isMobile } = useScreenSize() + const { isTablet } = useScreenSize() const tableColumns: TableColProps[] = [ { key: 'hash', content: t('common.hash') }, { key: 'block', content: t('common.block') }, @@ -139,7 +139,7 @@ export const TokenTransfers: FC = ({ content: ( ), @@ -186,7 +186,7 @@ export const TokenTransfers: FC = ({ {trimLongString(fromAddress)} ) : ( - + )} @@ -210,7 +210,7 @@ export const TokenTransfers: FC = ({ {trimLongString(toAddress)} ) : ( - + ), }, ...(differentTokens diff --git a/src/app/components/Transactions/ConsensusTransactions.tsx b/src/app/components/Transactions/ConsensusTransactions.tsx index 928ee697e..45518dbf4 100644 --- a/src/app/components/Transactions/ConsensusTransactions.tsx +++ b/src/app/components/Transactions/ConsensusTransactions.tsx @@ -1,11 +1,9 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' -import Typography from '@mui/material/Typography' import { Transaction } from '../../../oasis-nexus/api' import { Table, TableCellAlign, TableColProps } from '../../components/Table' import { RoundedBalance } from '../../components/RoundedBalance' -import { trimLongString } from '../../utils/trimLongString' import { TablePaginationProps } from '../Table/TablePagination' import { BlockLink } from '../Blocks/BlockLink' import { AccountLink } from '../Account/AccountLink' @@ -66,7 +64,7 @@ export const ConsensusTransactions: FC = ({ key: 'success', }, { - content: , + content: , key: 'hash', }, { @@ -95,19 +93,12 @@ export const ConsensusTransactions: FC = ({ pr: 3, }} > - {!!ownAddress && transaction.sender === ownAddress ? ( - - {trimLongString(transaction.sender)} - - ) : ( - - )} + ), key: 'from', diff --git a/src/app/components/Transactions/RuntimeTransactions.tsx b/src/app/components/Transactions/RuntimeTransactions.tsx index 0734a689d..dd2519673 100644 --- a/src/app/components/Transactions/RuntimeTransactions.tsx +++ b/src/app/components/Transactions/RuntimeTransactions.tsx @@ -14,8 +14,6 @@ import { TablePaginationProps } from '../Table/TablePagination' import { BlockLink } from '../Blocks/BlockLink' import { AccountLink } from '../Account/AccountLink' import { TransactionLink } from './TransactionLink' -import { trimLongString } from '../../utils/trimLongString' -import Typography from '@mui/material/Typography' import { doesAnyOfTheseLayersSupportEncryptedTransactions } from '../../../types/layers' import { TransactionEncryptionStatus } from '../TransactionEncryptionStatus' import { Age } from '../Age' @@ -104,11 +102,7 @@ export const RuntimeTransactions: FC = ({ : []), { content: ( - + ), key: 'hash', }, @@ -139,24 +133,15 @@ export const RuntimeTransactions: FC = ({ pr: 3, }} > - {!!ownAddress && - (transaction.sender_0_eth === ownAddress || transaction.sender_0 === ownAddress) ? ( - - {trimLongString(transaction.sender_0_eth || transaction.sender_0)} - - ) : ( - - )} + {targetAddress && ( @@ -168,19 +153,14 @@ export const RuntimeTransactions: FC = ({ }, { content: targetAddress ? ( - !!ownAddress && (transaction.to_eth === ownAddress || transaction.to === ownAddress) ? ( - - {trimLongString(targetAddress)} - - ) : ( - - ) + ) : null, key: 'to', }, diff --git a/src/app/components/Transactions/TransactionLink.tsx b/src/app/components/Transactions/TransactionLink.tsx index afcd30e8f..a13319bb3 100644 --- a/src/app/components/Transactions/TransactionLink.tsx +++ b/src/app/components/Transactions/TransactionLink.tsx @@ -1,33 +1,88 @@ -import { FC } from 'react' +import { FC, ReactNode } from 'react' import { Link as RouterLink } from 'react-router-dom' import Link from '@mui/material/Link' import Typography from '@mui/material/Typography' +import InfoIcon from '@mui/icons-material/Info' import { useScreenSize } from '../../hooks/useScreensize' -import { TrimLinkLabel } from '../TrimLinkLabel' import { RouteUtils } from '../../utils/route-utils' import { SearchScope } from '../../../types/searchScope' import { COLORS } from '../../../styles/theme/colors' +import { AdaptiveTrimmer } from '../AdaptiveTrimmer/AdaptiveTrimmer' +import { MaybeWithTooltip } from '../AdaptiveTrimmer/MaybeWithTooltip' +import { trimLongString } from '../../utils/trimLongString' +import Box from '@mui/material/Box' + +const WithTypographyAndLink: FC<{ children: ReactNode; plain?: boolean; mobile?: boolean; to: string }> = ({ + children, + plain, + mobile, + to, +}) => ( + + {plain ? ( + children + ) : ( + + {children} + + )} + +) export const TransactionLink: FC<{ alwaysTrim?: boolean scope: SearchScope hash: string plain?: boolean -}> = ({ alwaysTrim, hash, scope, plain }) => { - const { isMobile } = useScreenSize() + extraTooltip?: ReactNode +}> = ({ alwaysTrim, hash, scope, plain, extraTooltip }) => { + const { isTablet } = useScreenSize() const to = RouteUtils.getTransactionRoute(scope, hash) + const tooltipPostfix = extraTooltip ? ( + + + {extraTooltip} + + ) : undefined + + if (alwaysTrim) { + // Table mode + return ( + + + {hash} + {tooltipPostfix} + + } + > + {trimLongString(hash, 6, 6)} + + + ) + } + + if (!isTablet) { + // Desktop mode + return ( + + {hash} + + ) + } + // Mobile mode return ( - - {alwaysTrim || isMobile ? ( - - ) : plain ? ( - hash - ) : ( - - {hash} - - )} - + + + ) } diff --git a/src/app/data/pontusx-account-names.ts b/src/app/data/pontusx-account-names.ts new file mode 100644 index 000000000..b5474f0c9 --- /dev/null +++ b/src/app/data/pontusx-account-names.ts @@ -0,0 +1,94 @@ +import axios from 'axios' +import { useQuery } from '@tanstack/react-query' +import type { AccountNameInfo } from '../hooks/useAccountName' +import { Layer } from '../../oasis-nexus/api' +import { Network } from '../../types/network' +import { findTextMatch } from '../components/HighlightedText/text-matching' +import * as process from 'process' + +const DATA_SOURCE_URL = 'https://raw.githubusercontent.com/deltaDAO/mvg-portal/main/pontusxAddresses.json' + +type AccountMap = Map +type AccountEntry = { + name: string + address: string +} +type AccountData = { + map: AccountMap + list: AccountEntry[] +} + +const getPontusXAccountNames = () => + new Promise((resolve, reject) => { + axios.get(DATA_SOURCE_URL).then(response => { + if (response.status !== 200) reject("Couldn't load names") + if (!response.data) reject("Couldn't load names") + const map = new Map() + const list: AccountEntry[] = [] + Object.entries(response.data).forEach(([address, name]) => { + map.set(address, name) + const normalizedEntry: AccountEntry = { + name: name as string, + address, + } + list.push(normalizedEntry) + }) + resolve({ + map, + list, + }) + }, reject) + }) + +export const usePontusXAccountNames = (enabled: boolean) => { + return useQuery(['pontusXNames'], getPontusXAccountNames, { + enabled, + staleTime: Infinity, + }) +} + +export const usePontusXAccountName = (address: string, enabled: boolean): AccountNameInfo => { + // When running jest tests, we don't want to load from Pontus-X. + if (process.env.NODE_ENV === 'test') { + return { + name: undefined, + loading: false, + } + } + // This is not a condition that can change while the app is running, so it's OK. + // eslint-disable-next-line react-hooks/rules-of-hooks + const { isLoading, error, data: allNames } = usePontusXAccountNames(enabled) + if (error) { + console.log('Failed to load Pontus-X account names', error) + } + return { + name: allNames?.map.get(address), + loading: isLoading, + } +} + +export const useSearchForPontusXAccountsByName = ( + network: Network, + nameFragment: string, + enabled: boolean, +) => { + const { isLoading, error, data: allNames } = usePontusXAccountNames(enabled) + if (error) { + console.log('Failed to load Pontus-X account names', error) + } + + const textMatcher = + nameFragment && enabled + ? (entry: AccountEntry): boolean => { + return !!findTextMatch(entry.name, [nameFragment]) + } + : () => false + return { + results: (allNames?.list || []).filter(textMatcher).map(entry => ({ + network, + layer: Layer.pontusx, + address: entry.address, + })), + isLoading, + } +} diff --git a/src/app/hooks/useAccountName.ts b/src/app/hooks/useAccountName.ts new file mode 100644 index 000000000..fe7a95ea7 --- /dev/null +++ b/src/app/hooks/useAccountName.ts @@ -0,0 +1,72 @@ +import { SearchScope } from '../../types/searchScope' +import Chance from 'chance' +import { Layer } from '../../oasis-nexus/api' +import { usePontusXAccountName, useSearchForPontusXAccountsByName } from '../data/pontusx-account-names' + +const NO_MATCH = '__no_match__' + +export type AccountNameInfo = { + name: string | undefined + loading: boolean +} + +/** + * Do we want to see some random names? + */ +const DEBUG_MODE = true + +/** + * Look up the name of an account. + */ +const lookupName = (scope: SearchScope, _address: string): string | undefined => { + switch (scope.layer) { + // TODO: look up the data + default: + // If debug mode is on, return mock names in ~50% of the cases, no nome otherwise + return DEBUG_MODE && Math.random() < 0.5 ? new Chance().name() : undefined + } +} + +const nameCache: Map = new Map() + +/** + * Find out the name of an account + * + * This is the entry point that should be used by the application, + * since this function also includes caching. + */ +export const useAccountName = (scope: SearchScope, address: string, dropCache = false): AccountNameInfo => { + const isPontusX = scope.layer === Layer.pontusx + + const pontusXName = usePontusXAccountName(address, isPontusX) + if (isPontusX) return pontusXName + + const key = `${scope.network}.${scope.layer}.${address}` + + if (dropCache) nameCache.delete(key) + const hasMatch = nameCache.has(key) + if (hasMatch) { + const cachedName = nameCache.get(key) + return { + name: cachedName === NO_MATCH ? undefined : cachedName, + loading: false, + } + } + const name = lookupName(scope, address) + nameCache.set(key, name ?? NO_MATCH) + return { + name, + loading: false, + } +} + +export const useSearchForAccountsByName = (scope: SearchScope, nameFragment = '') => { + const isValidPontusXSearch = scope.layer === Layer.pontusx && !!nameFragment + const pontusXResults = useSearchForPontusXAccountsByName(scope.network, nameFragment, isValidPontusXSearch) + return isValidPontusXSearch + ? pontusXResults + : { + isLoading: false, + results: [], + } +} diff --git a/src/app/pages/AccountDetailsPage/AccountDetailsCard.tsx b/src/app/pages/AccountDetailsPage/AccountDetailsCard.tsx index 23bfe2975..6f81242fe 100644 --- a/src/app/pages/AccountDetailsPage/AccountDetailsCard.tsx +++ b/src/app/pages/AccountDetailsPage/AccountDetailsCard.tsx @@ -12,6 +12,7 @@ type AccountDetailsProps = { account: RuntimeAccount | undefined token: EvmToken | undefined tokenPrices: AllTokenPrices + highlightedPartOfName?: string | undefined } export const AccountDetailsCard: FC = ({ @@ -21,6 +22,7 @@ export const AccountDetailsCard: FC = ({ account, token, tokenPrices, + highlightedPartOfName, }) => { const { t } = useTranslation() return ( @@ -35,6 +37,7 @@ export const AccountDetailsCard: FC = ({ account={account} token={token} tokenPrices={tokenPrices} + highlightedPartOfName={highlightedPartOfName} /> ) diff --git a/src/app/pages/AccountDetailsPage/AccountDetailsView.tsx b/src/app/pages/AccountDetailsPage/AccountDetailsView.tsx index 0c9f70225..4e94e2a4d 100644 --- a/src/app/pages/AccountDetailsPage/AccountDetailsView.tsx +++ b/src/app/pages/AccountDetailsPage/AccountDetailsView.tsx @@ -12,7 +12,8 @@ export const AccountDetailsView: FC<{ token?: EvmToken tokenPrices: AllTokenPrices showLayer?: boolean -}> = ({ isLoading, isError, account, token, tokenPrices, showLayer }) => { + highlightedPartOfName?: string | undefined +}> = ({ isLoading, isError, account, token, tokenPrices, showLayer, highlightedPartOfName }) => { const { t } = useTranslation() return isError ? ( @@ -23,6 +24,7 @@ export const AccountDetailsView: FC<{ isLoading={isLoading} tokenPrices={tokenPrices} showLayer={showLayer} + highlightedPartOfName={highlightedPartOfName} /> ) } diff --git a/src/app/pages/AccountDetailsPage/AccountNFTCollectionCard.tsx b/src/app/pages/AccountDetailsPage/AccountNFTCollectionCard.tsx index a03739e64..3ab574b8b 100644 --- a/src/app/pages/AccountDetailsPage/AccountNFTCollectionCard.tsx +++ b/src/app/pages/AccountDetailsPage/AccountNFTCollectionCard.tsx @@ -44,7 +44,7 @@ export const AccountNFTCollectionCard: FC = ({ scope, add isFetched && firstToken && ( - + ) diff --git a/src/app/pages/AccountDetailsPage/AccountTokensCard.tsx b/src/app/pages/AccountDetailsPage/AccountTokensCard.tsx index 71e15c55e..b7b6f3a53 100644 --- a/src/app/pages/AccountDetailsPage/AccountTokensCard.tsx +++ b/src/app/pages/AccountDetailsPage/AccountTokensCard.tsx @@ -30,10 +30,14 @@ type AccountTokensCardProps = AccountDetailsContext & { export const accountTokenContainerId = 'tokens' -export const ContractLink: FC<{ scope: SearchScope; address: string }> = ({ scope, address }) => { +export const ContractLink: FC<{ scope: SearchScope; address: string; alwaysTrim?: boolean }> = ({ + scope, + address, + alwaysTrim, +}) => { return ( - + ) @@ -79,7 +83,11 @@ export const AccountTokensCard: FC = ({ scope, account, { content: ( - + ), key: 'hash', diff --git a/src/app/pages/AccountDetailsPage/DeferredConsensusAccountDetails.tsx b/src/app/pages/AccountDetailsPage/DeferredConsensusAccountDetails.tsx new file mode 100644 index 000000000..5720b4ecd --- /dev/null +++ b/src/app/pages/AccountDetailsPage/DeferredConsensusAccountDetails.tsx @@ -0,0 +1,21 @@ +import { FC } from 'react' +import { AllTokenPrices } from '../../../coin-gecko/api' +import { Network } from '../../../types/network' + +/** + * Load and display details of a RuntimeAccount + */ +export const DeferredConsensusAccountDetails: FC<{ + network: Network + address: string + tokenPrices: AllTokenPrices + highlightedPartOfName: string | undefined + showLayer?: boolean +}> = () => + // { + // network, address, tokenPrices, highlightedPartOfName, showLayer + // }, + { + // TODO: load and display consensus account details when API and component becomes available + return null + } diff --git a/src/app/pages/AccountDetailsPage/DeferredRuntimeAccountDetails.tsx b/src/app/pages/AccountDetailsPage/DeferredRuntimeAccountDetails.tsx new file mode 100644 index 000000000..882f742cc --- /dev/null +++ b/src/app/pages/AccountDetailsPage/DeferredRuntimeAccountDetails.tsx @@ -0,0 +1,29 @@ +import { FC } from 'react' +import { Runtime, useGetRuntimeAccountsAddress } from '../../../oasis-nexus/api' +import { AllTokenPrices } from '../../../coin-gecko/api' +import { AccountDetailsView } from './AccountDetailsView' +import { Network } from '../../../types/network' + +/** + * Load and display details of a RuntimeAccount + */ +export const DeferredRuntimeAccountDetails: FC<{ + network: Network + layer: Runtime + address: string + tokenPrices: AllTokenPrices + highlightedPartOfName: string | undefined + showLayer?: boolean +}> = ({ network, layer, address, tokenPrices, highlightedPartOfName, showLayer }) => { + const { data, isLoading, isError } = useGetRuntimeAccountsAddress(network, layer, address) + return ( + + ) +} diff --git a/src/app/pages/AccountDetailsPage/index.tsx b/src/app/pages/AccountDetailsPage/index.tsx index 41dbb7e93..2c9c5810e 100644 --- a/src/app/pages/AccountDetailsPage/index.tsx +++ b/src/app/pages/AccountDetailsPage/index.tsx @@ -32,7 +32,7 @@ export const AccountDetailsPage: FC = () => { const { t } = useTranslation() const scope = useRequiredScopeParam() - const { address } = useLoaderData() as AddressLoaderData + const { address, searchTerm } = useLoaderData() as AddressLoaderData const { account, isLoading: isAccountLoading, isError } = useAccount(scope, address) const isContract = !!account?.evm_contract const { token, isLoading: isTokenLoading } = useTokenInfo(scope, address, isContract) @@ -62,6 +62,7 @@ export const AccountDetailsPage: FC = () => { account={account} token={token} tokenPrices={tokenPrices} + highlightedPartOfName={searchTerm} /> = ({ isFetched, isLoa }} > - + diff --git a/src/app/pages/ParatimeDashboardPage/LatestRuntimeTransactions.tsx b/src/app/pages/ParatimeDashboardPage/LatestRuntimeTransactions.tsx index 74592c188..1d21d04b7 100644 --- a/src/app/pages/ParatimeDashboardPage/LatestRuntimeTransactions.tsx +++ b/src/app/pages/ParatimeDashboardPage/LatestRuntimeTransactions.tsx @@ -7,14 +7,13 @@ import { Link as RouterLink } from 'react-router-dom' import Link from '@mui/material/Link' import { Layer, useGetRuntimeTransactions } from '../../../oasis-nexus/api' import { RuntimeTransactions } from '../../components/Transactions' -import { NUMBER_OF_ITEMS_ON_DASHBOARD } from '../../config' import { COLORS } from '../../../styles/theme/colors' import { AppErrors } from '../../../types/errors' import { RouteUtils } from '../../utils/route-utils' import { useScreenSize } from '../../hooks/useScreensize' import { SearchScope } from '../../../types/searchScope' -const limit = NUMBER_OF_ITEMS_ON_DASHBOARD +const limit = 15 // NUMBER_OF_ITEMS_ON_DASHBOARD export const LatestRuntimeTransactions: FC<{ scope: SearchScope }> = ({ scope }) => { const { isTablet } = useScreenSize() diff --git a/src/app/pages/ParatimeDashboardPage/index.tsx b/src/app/pages/ParatimeDashboardPage/index.tsx index 53c73a6eb..a00a925cf 100644 --- a/src/app/pages/ParatimeDashboardPage/index.tsx +++ b/src/app/pages/ParatimeDashboardPage/index.tsx @@ -12,11 +12,14 @@ import { PageLayout } from '../../components/PageLayout' import { ParaTimeSnapshot } from './ParaTimeSnapshot' import { TopTokens } from './TopTokens' import { useRequiredScopeParam } from '../../hooks/useScopeParam' +import { paraTimesConfig } from '../../../config' export const ParatimeDashboardPage: FC = () => { const { isMobile } = useScreenSize() const scope = useRequiredScopeParam() + const { hideTokensFromDashboard } = paraTimesConfig[scope.layer]! + return ( @@ -35,7 +38,7 @@ export const ParatimeDashboardPage: FC = () => { )} - {!isMobile && } + {!hideTokensFromDashboard && !isMobile && } diff --git a/src/app/pages/PrivacyPage/index.tsx b/src/app/pages/PrivacyPage/index.tsx new file mode 100644 index 000000000..702e060c8 --- /dev/null +++ b/src/app/pages/PrivacyPage/index.tsx @@ -0,0 +1,19 @@ +import React, { FC } from 'react' +import { useTranslation } from 'react-i18next' + +import { PageLayout } from '../../components/PageLayout' +import { SubPageCard } from '../../components/SubPageCard' + +import Policy from '../../../docs/pontusx_privacy_2024.mdx' + +export const PrivacyPage: FC = () => { + const { t } = useTranslation() + + return ( + + + + + + ) +} diff --git a/src/app/pages/RuntimeTransactionDetailPage/index.tsx b/src/app/pages/RuntimeTransactionDetailPage/index.tsx index 0ea100a08..cde8153cc 100644 --- a/src/app/pages/RuntimeTransactionDetailPage/index.tsx +++ b/src/app/pages/RuntimeTransactionDetailPage/index.tsx @@ -1,4 +1,4 @@ -import { FC, PropsWithChildren, useState } from 'react' +import { FC, useState } from 'react' import { useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { @@ -20,7 +20,6 @@ import Alert from '@mui/material/Alert' import { CopyToClipboard } from '../../components/CopyToClipboard' import { AppErrors } from '../../../types/errors' import { TextSkeleton } from '../../components/Skeleton' -import Box from '@mui/material/Box' import { BlockLink } from '../../components/Blocks/BlockLink' import { TransactionLink } from '../../components/Transactions/TransactionLink' import { TransactionEvents } from '../../components/Transactions/TransactionEvents' @@ -30,8 +29,6 @@ import { getNameForTicker, Ticker } from '../../../types/ticker' import { AllTokenPrices, useAllTokenPrices } from '../../../coin-gecko/api' import { CurrentFiatValue } from './CurrentFiatValue' import { AddressSwitch, AddressSwitchOption } from '../../components/AddressSwitch' -import InfoIcon from '@mui/icons-material/Info' -import Tooltip from '@mui/material/Tooltip' import { TransactionEncrypted, TransactionNotEncrypted } from '../../components/TransactionEncryptionStatus' import Typography from '@mui/material/Typography' import { LongDataDisplay } from '../../components/LongDataDisplay' @@ -145,23 +142,6 @@ export type TransactionDetailRuntimeBlock = RuntimeTransaction & { markAsNew?: boolean } -const TransactionInfoTooltip: FC> = ({ label, children }) => { - return ( - - - {label} - - } - > - {children} - - ) -} - export const RuntimeTransactionDetailView: FC<{ isLoading?: boolean transaction: TransactionDetailRuntimeBlock | undefined @@ -213,23 +193,21 @@ export const RuntimeTransactionDetailView: FC<{ <>
{t('common.hash')}
- - - + /> + {hash && }
@@ -270,24 +248,21 @@ export const RuntimeTransactionDetailView: FC<{ <>
{t('common.from')}
- - - + /> {from && }
@@ -297,21 +272,18 @@ export const RuntimeTransactionDetailView: FC<{ <>
{t('common.to')}
- - - + /> {to && }
diff --git a/src/app/pages/SearchResultsPage/SearchResultsList.tsx b/src/app/pages/SearchResultsPage/SearchResultsList.tsx index 366259b74..c2903c46d 100644 --- a/src/app/pages/SearchResultsPage/SearchResultsList.tsx +++ b/src/app/pages/SearchResultsPage/SearchResultsList.tsx @@ -7,6 +7,7 @@ import { RuntimeTransactionDetailView } from '../RuntimeTransactionDetailPage' import { AccountDetailsView } from '../AccountDetailsPage/AccountDetailsView' import { AccountResult, + AccountAddressResult, BlockResult, ContractResult, ProposalResult, @@ -21,6 +22,9 @@ import { AllTokenPrices } from '../../../coin-gecko/api' import { ResultListFrame } from './ResultListFrame' import { TokenDetails } from '../../components/Tokens/TokenDetails' import { ProposalDetailView } from '../ProposalDetailsPage' +import { DeferredRuntimeAccountDetails } from '../AccountDetailsPage/DeferredRuntimeAccountDetails' +import { Layer } from '../../../oasis-nexus/api' +import { DeferredConsensusAccountDetails } from '../AccountDetailsPage/DeferredConsensusAccountDetails' /** * Component for displaying a list of search results @@ -94,6 +98,35 @@ export const SearchResultsList: FC<{ linkLabel={t('search.results.accounts.viewLink')} /> + item.resultType === 'accountAddress', + )} + resultComponent={item => + item.layer === Layer.consensus ? ( + + ) : ( + + ) + } + link={acc => RouteUtils.getAccountRoute(acc, acc.address)} + linkLabel={t('search.results.accounts.viewLink')} + /> + item.resultType === 'contract')} diff --git a/src/app/pages/SearchResultsPage/hooks.ts b/src/app/pages/SearchResultsPage/hooks.ts index 395a96cbf..b5450202e 100644 --- a/src/app/pages/SearchResultsPage/hooks.ts +++ b/src/app/pages/SearchResultsPage/hooks.ts @@ -19,6 +19,7 @@ import { import { RouteUtils } from '../../utils/route-utils' import { SearchParams } from '../../components/Search/search-utils' import { SearchScope } from '../../../types/searchScope' +import { useSearchForAccountsByName } from '../../hooks/useAccountName' function isDefined(item: T): item is NonNullable { return item != null @@ -27,7 +28,7 @@ function isDefined(item: T): item is NonNullable { export type ConditionalResults = { isLoading: boolean; results: T[] } type SearchResultItemCore = HasScope & { - resultType: 'block' | 'transaction' | 'account' | 'contract' | 'token' | 'proposal' + resultType: 'block' | 'transaction' | 'account' | 'accountAddress' | 'contract' | 'token' | 'proposal' } export type BlockResult = SearchResultItemCore & RuntimeBlock & { resultType: 'block' } @@ -36,6 +37,10 @@ export type TransactionResult = SearchResultItemCore & RuntimeTransaction & { re export type AccountResult = SearchResultItemCore & RuntimeAccount & { resultType: 'account' } +export type AccountAddressResult = SearchResultItemCore & { address: string } & { + resultType: 'accountAddress' +} + export type ContractResult = SearchResultItemCore & RuntimeAccount & { resultType: 'contract' } export type TokenResult = SearchResultItemCore & EvmToken & { resultType: 'token' } @@ -46,6 +51,7 @@ export type SearchResultItem = | BlockResult | TransactionResult | AccountResult + | AccountAddressResult | ContractResult | TokenResult | ProposalResult @@ -193,6 +199,25 @@ export function useNetworkProposalsConditionally( } } +type AccountAddressInfo = Pick + +export function useNamedAccountConditionally( + currentScope: SearchScope | undefined, + nameFragment: string | undefined, +): ConditionalResults { + const queries = RouteUtils.getVisibleScopes(currentScope).map(scope => + // eslint-disable-next-line react-hooks/rules-of-hooks + useSearchForAccountsByName(scope, nameFragment), + ) + return { + isLoading: queries.some(query => query.isLoading), + results: queries + .map(query => query.results) + .filter(isDefined) + .flat(), + } +} + export const useSearch = (currentScope: SearchScope | undefined, q: SearchParams) => { const queries = { blockHeight: useBlocksByHeightConditionally(currentScope, q.blockHeight), @@ -201,6 +226,7 @@ export const useSearch = (currentScope: SearchScope | undefined, q: SearchParams oasisAccount: useRuntimeAccountConditionally(currentScope, q.consensusAccount), // TODO: remove evmBech32Account and use evmAccount when API is ready evmBech32Account: useRuntimeAccountConditionally(currentScope, q.evmBech32Account), + accountsByName: useNamedAccountConditionally(currentScope, q.accountNameFragment), tokens: useRuntimeTokenConditionally(currentScope, q.evmTokenNameFragment), proposals: useNetworkProposalsConditionally(q.networkProposalNameFragment), } @@ -211,6 +237,7 @@ export const useSearch = (currentScope: SearchScope | undefined, q: SearchParams ...(queries.oasisAccount.results || []), ...(queries.evmBech32Account.results || []), ].filter(isAccountNonEmpty) + const accountAddresses = queries.accountsByName.results || [] const tokens = queries.tokens.results .map(l => l.evm_tokens) .flat() @@ -228,6 +255,9 @@ export const useSearch = (currentScope: SearchScope | undefined, q: SearchParams ...accounts .filter(account => account.evm_contract) .map((account): ContractResult => ({ ...account, resultType: 'contract' })), + ...accountAddresses.map( + (account): AccountAddressResult => ({ ...account, resultType: 'accountAddress' }), + ), ...tokens.map((token): TokenResult => ({ ...token, resultType: 'token' })), ...proposals.map((proposal): ProposalResult => ({ ...proposal, resultType: 'proposal' })), ] diff --git a/src/app/pages/SearchResultsPage/useRedirectIfSingleResult.ts b/src/app/pages/SearchResultsPage/useRedirectIfSingleResult.ts index ebcb816b8..e3e704e02 100644 --- a/src/app/pages/SearchResultsPage/useRedirectIfSingleResult.ts +++ b/src/app/pages/SearchResultsPage/useRedirectIfSingleResult.ts @@ -35,6 +35,9 @@ export function useRedirectIfSingleResult( case 'account': redirectTo = RouteUtils.getAccountRoute(item, item.address_eth ?? item.address) break + case 'accountAddress': + redirectTo = `${RouteUtils.getAccountRoute(item, item.address)}?q=${searchTerm}` + break case 'contract': redirectTo = RouteUtils.getAccountRoute(item, item.address_eth ?? item.address) break diff --git a/src/app/pages/TokenDashboardPage/NFTLinks.tsx b/src/app/pages/TokenDashboardPage/NFTLinks.tsx index e70dd170d..a2c6979dc 100644 --- a/src/app/pages/TokenDashboardPage/NFTLinks.tsx +++ b/src/app/pages/TokenDashboardPage/NFTLinks.tsx @@ -66,8 +66,9 @@ export const NFTInstanceLink: FC = ({ scope, instance }) => { type NFTOwnerLinkProps = { scope: SearchScope owner: string + alwaysTrim?: boolean } -export const NFTOwnerLink: FC = ({ scope, owner }) => { +export const NFTOwnerLink: FC = ({ scope, owner, alwaysTrim }) => { const { t } = useTranslation() return ( @@ -76,7 +77,7 @@ export const NFTOwnerLink: FC = ({ scope, owner }) => { i18nKey="nft.ownerLink" t={t} components={{ - OwnerLink: , + OwnerLink: , }} /> diff --git a/src/app/pages/TokenDashboardPage/TokenDetailsCard.tsx b/src/app/pages/TokenDashboardPage/TokenDetailsCard.tsx index caa1511f5..7c6f91ac9 100644 --- a/src/app/pages/TokenDashboardPage/TokenDetailsCard.tsx +++ b/src/app/pages/TokenDashboardPage/TokenDetailsCard.tsx @@ -69,7 +69,11 @@ export const TokenDetailsCard: FC<{ scope: SearchScope; address: string; searchT
{t('contract.creator')}
- +
{t('common.balance')}
diff --git a/src/app/pages/TokenDashboardPage/TokenTitleCard.tsx b/src/app/pages/TokenDashboardPage/TokenTitleCard.tsx index cfad3b218..fdfb5ab8a 100644 --- a/src/app/pages/TokenDashboardPage/TokenTitleCard.tsx +++ b/src/app/pages/TokenDashboardPage/TokenTitleCard.tsx @@ -25,7 +25,11 @@ export const TokenTitleCard: FC<{ scope: SearchScope; address: string; searchTer {token && ( <> - + )} diff --git a/src/config.ts b/src/config.ts index b87fc4a5f..ec54605cb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -38,6 +38,8 @@ type LayerConfig = { * Specified in milliseconds. */ outOfDateThreshold: number + + hideTokensFromDashboard?: boolean } export enum RuntimeTypes { @@ -148,6 +150,7 @@ const pontusxConfig: LayerConfig = { decimals: 18, type: RuntimeTypes.Evm, outOfDateThreshold: 2 * 60 * 1000, + hideTokensFromDashboard: true, } type LayersConfig = { @@ -198,3 +201,5 @@ export const getFiatCurrencyForScope = (scope: SearchScope | undefined) => (scope ? paraTimesConfig[scope.layer]?.[scope.network]?.fiatCurrency : undefined) ?? 'usd' export const showFiatValues = process.env.REACT_APP_SHOW_FIAT_VALUES === 'true' + +export const showPrivacyPolicy = process.env.REACT_APP_SHOW_PRIVACY_POLICY === 'true' diff --git a/src/docs/pontusx_privacy_2024.mdx b/src/docs/pontusx_privacy_2024.mdx new file mode 100644 index 000000000..129b50ba5 --- /dev/null +++ b/src/docs/pontusx_privacy_2024.mdx @@ -0,0 +1,138 @@ +This privacy policy informs you about how deltaDAO AG (in the following deltaDAO, we, us, our) processes your personal data when you visit our Pontus-X Blockchain Explorer (in the following explorer) and when you contact us. Moreover, this privacy policy informs you about your rights. + +_Last updated on February 23, 2024._ + +### 1. Contact details of the controller and Data Protection Officer + +The controller pursuant to the EU General Data Protection Regulation ("GDPR") for the processing of your personal data is: + +deltaDAO AG +Katharinenstraße 30a +20457 Hamburg +Germany +E-mail: contact@delta-dao.com + +If you have any questions about the protection of your personal data at deltaDAO, please contact our Data Protection Officer: + +Data Protection Officer +deltaDAO AG +Katharinenstraße 30a +20457 Hamburg +Germany +E-mail: privacy@delta-dao.com + +### 2. What's personal data? + +Personal data is any information that can be (directly or indirectly) related to you. deltaDAO processes a minimal amount of personal data, as we believe your personal data belongs to you. We process the following personal data. + +- **Logfile data and IP addresses:** Logfile data including IP addresses are processed when visiting our explorer. +- **E-mail:** If you contact deltaDAO via e-mail, we process your e-mail address and any personal data you decide to provide in your message (such as your name). + +For detailed information about the processing operations, lawfulness, purposes, and how your personal data serves to reach these purposes, please take a look at the chapter "Processing operations according to Article 13 GDPR". + +### 3. Recipients and cross-border data transfer + +**Visiting our explorer** +When visiting our explorer, your IP address and logfile data are processed by Vercel Inc., 340 S Lemon Ave #4133, Walnut, CA 91789, USA. Vercel is a service provider that hosts our explorer. Our explorer is served by Vercel using a Content Delivery Network, a geographically distributed network, with servers within and outside of the European Economic Area (EEA). This means, if you are located within the EEA, your personal data will most likely (but not guaranteed) be processed on a Vercel server within the EEA. + +There is no adequacy decision for the USA from the European Commission. Our cooperation with Vercel is based on a Data Processing Agreement (DPA) including Standard Contractual Clauses (SCC). You have the right to receive a copy of these SCC. To exercise your right, please contact us at . + +- Here you can find Vercel's current [**DPA including SCC** ↗](https://vercel.com/legal/dpa). +- Here you can find Vercel's [**privacy policy** ↗](https://vercel.com/legal/privacy-policy). + +**Contact via e-mail** +When you contact us via e-mail, our (mail) service provider Microsoft Corporation (located at 1 Microsoft Way, Redmond, Washington 98052-8300, USA) supports us in processing your personal data so we can communicate with you. + +There is no adequacy decision for the USA from the European Commission. However, Microsoft is EU-US DPF (EU‐US Data Privacy Framework) certified. The EU‐US DPF is an adequacy decision of the European Commission, limited to certified entities. Moreover, we have restricted storage on the EEA and signed SCC with our provider. You have the right to receive a copy of these SCC. To exercise your right, please contact us at . + +- Here you can find Microsoft's current [**DPA including SCC** ↗](https://www.microsoft.com/licensing/docs/view/Microsoft-Products-and-Services-Data-Protection-Addendum-DPA). +- Here you can find Microsoft's [**privacy policy** ↗](https://privacy.microsoft.com/de-de/privacystatement). +- Here you can find Microsoft's [**Data Privacy Framework List entry** ↗](https://www.dataprivacyframework.gov/list). + +### 4. Processing operations according to Article 13 GDPR + +#### **4.1 Providing the Pontus-X Blockchain Explorer** + +The Pontus-X Blockchain Explorer for the Pontus-X ParaTime enables users to inspect and analyze the transactions made on the Pontus-X ParaTime, including the tracking of transaction histories, wallet interactions, and smart contract operations. Logfile data and IP addresses are collected, used, and stored for providing our explorer and its functionalities and to maintain its security. Our explorer is hosted externally by our service provider Vercel (see also chapter 3). + +**Purposes:** Collecting and using your IP address is necessary for providing our explorer because it is a technical requirement for ensuring communication between your device and our explorer. Logfiles including your IP address are created for security, fraud-prevention, abuse-prevention, and troubleshooting purposes. + +**Legal basis:** The legal basis for this processing is our legitimate interest, pursuant to Art. 6(1)(f) GDPR. + +**Legitimate interests:** Our legitimate interest is to provide our explorer and its functionalities to you and to enable security, a technically error-free presentation, and the optimization of the explorer. + +**Retention period:** Vercel retains your personal data for as long as needed to provide their services. Additionally, Vercel does not delete the data when it is needed for the establishment, exercise, or defense of legal claims. In this case, the information is retained as long as needed for exercising respective potential legal claims. + +#### **4.2 Contact via e-mail** + +If you contact us via e-mail, deltaDAO collects, uses and stores your e-mail address, and any other information you provide us in your message, such as your name. + +**Purposes:** +We collect, use and store this personal data to respond to your inquiries. + +**Legal basis:** +The legal basis for this processing is our legitimate interest, according to Art. 6(1)(f) GDPR. + +**Legitimate interests:** +Our legitimate interest is to answer your inquiries. + +**Retention period:** +deltaDAO deletes your personal data as soon as we no longer require them for processing your inquiry, except deltaDAO is obliged to comply with legal retention periods or in case of legal disputes. + +### 5. Automated decision making including profiling according to Article 13(2)(f) GDPR + +Automated decision making including profiling does not take place. + +### 6. Cookies + +A cookie is a small file that stores Internet settings. Your web browser downloads it on the first visit to a website. The next time you open this website with the same device, the cookie and the information stored in it are either sent back to the website that created it (first-party cookie) or sent to another website it belongs to (third-party cookie). This enables the website to detect that you have opened it previously with this browser and, in some cases, to vary the displayed content. + +Our explorer does not use cookies. + +### 7. External links + +Our explorer contains links to external websites that are beyond the control and responsibility of deltaDAO. + +### 8. Your rights + +If you want to make use of your rights described below, do not hesitate to contact us. + +**8.1 Right of access (Art. 15 GDPR)** + +You have the right to obtain confirmation as to whether deltaDAO processes personal data about you. If we are processing personal data about you, you have the right to access these personal data and to gain the information defined in Art. 15 GDPR. + +**8.2 Right to rectification (Art. 16 GDPR)** + +You have the right to obtain without undue delay the rectification of inaccurate personal data about you. Additionally, you have the right that incomplete personal data about you are completed. + +**8.3 Right to erasure (Art. 17 GDPR)** + +You have the right to obtain without undue delay the rectification of inaccurate personal data about you. Additionally, you have the right that incomplete personal data about you are completed. + +**8.4 Right to restriction (Art. 18 GDPR)** + +Moreover, you have the right to obtain the restriction of processing your personal data where the defined legal grounds in Art. 18 GDPR apply. + +**8.5 Right to data portability (Art. 20 GDPR)** + +You have the right to receive your personal data in a structured, commonly used, and machine-readable format. Additionally, you have the right to transmit those data to another controller without hindrance, where the defined legal grounds in Art. 20 GDPR apply. You can make use of your right to data portability by contacting us. + +**8.6 Right to object (Art. 21 GDPR)** + +On grounds relating to your particular situation, you have the right to object to the processing of your personal data where we based the processing on legitimate interests (Art. 6(1)(f) GDPR). If you object, deltaDAO will no longer process your personal data unless we can demonstrate compelling legitimate grounds for the processing, overriding your rights, freedoms, and interests, or if the processing is required to establish, exercise, or defend legal claims. + +**8.7 Right to lodge a complaint (Art. 77 GDPR)** + +You have the right to lodge a complaint with a supervisory authority if you consider the processing of your personal data by deltaDAO to infringe the GDPR. You can lodge a complaint in particular + +- in the Member State of your habitual residence, +- in the Member State of your place of work, and +- in the place of the alleged infringement. + +### 9. Questions + +For any requests regarding our privacy policy, please send us an e-mail to [privacy@delta-dao.com](mailto:privacy@delta-dao.com). + +### 10. Changes to the Privacy Policy + +We adjust this policy from time to time, by publishing a new version on our explorer. You can find the date of the current version at the beginning of this policy. The latest version of this policy applies to the processing of your personal data. diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 204d6e604..4f664d28c 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -299,7 +299,7 @@ "last": "Last" }, "social": { - "description": "Be part of the community and stay in the loop on everything Oasis", + "description": "Learn more about Pontus-X and the team behind it", "discord": "Discord", "header": "Join us", "reddit": "Reddit", @@ -314,8 +314,8 @@ "changeView": "Change view" }, "testnetFaucet": { - "description": "Drops are limited to 10 TEST tokens", - "header": "Oasis Testnet Faucet", + "description": "Do you need some tokens for testing?", + "header": "Test tokens", "request": "Request test tokens" }, "tokens": { @@ -530,6 +530,9 @@ "noData": "No data available", "nodes": "Nodes:" }, + "privacyPolicy": { + "title": "Privacy Policy" + }, "search": { "placeholder": "Address, Block, Contract, Transaction hash, Token name, etc.", "error": { diff --git a/src/routes.tsx b/src/routes.tsx index 8021b6bb4..de2fc197c 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -47,6 +47,8 @@ import { ConsensusAccountsPage } from './app/pages/ConsensusAccountsPage' import { ConsensusTransactionsPage } from './app/pages/ConsensusTransactionsPage' import { ConsensusAccountDetailsPage } from './app/pages/ConsensusAccountDetailsPage' import { FC, useEffect } from 'react' +import { PrivacyPage } from './app/pages/PrivacyPage' +import { showPrivacyPolicy } from './config' const NetworkSpecificPart = () => ( @@ -264,6 +266,14 @@ export const routes: RouteObject[] = [ }, ], }, + ...(showPrivacyPolicy + ? [ + { + path: '/privacy', + element: withDefaultTheme(), + }, + ] + : []), ], }, ] diff --git a/src/types/global.d.ts b/src/types/global.d.ts index cac3fc202..697768d7c 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -26,6 +26,7 @@ declare global { REACT_APP_FIXED_NETWORK?: string REACT_APP_FIXED_LAYER?: string REACT_APP_SHOW_FIAT_VALUES?: string + REACT_APP_SHOW_PRIVACY_POLICY?: string } } } diff --git a/yarn.lock b/yarn.lock index 9353fb299..681a4c6e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -66,6 +66,28 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.11.tgz#8033acaa2aa24c3f814edaaa057f3ce0ba559c24" @@ -117,6 +139,16 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" +"@babel/generator@^7.12.5", "@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== + dependencies: + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/generator@^7.22.10", "@babel/generator@^7.7.2": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" @@ -147,16 +179,6 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -331,6 +353,17 @@ dependencies: "@babel/types" "^7.22.15" +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" @@ -342,17 +375,6 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" - "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" @@ -360,6 +382,11 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" @@ -470,6 +497,15 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.10" +"@babel/helpers@^7.12.5": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.0.tgz#a3dd462b41769c95db8091e49cfe019389a9409b" + integrity sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.0" + "@babel/types" "^7.24.0" + "@babel/helpers@^7.22.11": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.11.tgz#b02f5d5f2d7abc21ab59eeed80de410ba70b056a" @@ -520,6 +556,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905" integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g== +"@babel/parser@^7.12.7", "@babel/parser@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.0.tgz#26a3d1ff49031c53a97d03b604375f028746a9ac" + integrity sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg== + "@babel/parser@^7.22.15", "@babel/parser@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" @@ -610,6 +651,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-proposal-optional-chaining@^7.16.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" @@ -737,6 +787,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx@^7.22.5", "@babel/plugin-syntax-jsx@^7.7.2": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" @@ -772,7 +829,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== @@ -1385,6 +1442,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-parameters@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" @@ -1392,13 +1456,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-parameters@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" - integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-transform-private-methods@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" @@ -1977,6 +2034,15 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/template@^7.12.7", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + "@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -1995,6 +2061,22 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/traverse@^7.12.9", "@babel/traverse@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.0.tgz#4a408fbf364ff73135c714a2ab46a5eab2831b1e" + integrity sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + "@babel/traverse@^7.18.9", "@babel/traverse@^7.22.11", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5" @@ -2020,6 +2102,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.12.7", "@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@babel/types@^7.18.9", "@babel/types@^7.22.19", "@babel/types@^7.23.3", "@babel/types@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.4.tgz#7206a1810fc512a7f7f7d4dace4cb4c1c9dbfb8e" @@ -2811,6 +2902,36 @@ resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.8.5.tgz#8233e8762440b0f4632c47a09b1b6f23de8b934c" integrity sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ== +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + "@mdx-js/react@^2.1.5": version "2.3.0" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3" @@ -2819,6 +2940,11 @@ "@types/mdx" "^2.0.0" "@types/react" ">=16" +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + "@metamask/jazzicon@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/jazzicon/-/jazzicon-2.0.0.tgz#5615528e91c0fc5c9d79202d1f0954a7922525a0" @@ -3731,6 +3857,14 @@ "@parcel/plugin" "2.11.0" json5 "^2.2.0" +"@parcel/transformer-mdx@2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@parcel/transformer-mdx/-/transformer-mdx-2.11.0.tgz#f8288a95bfff86c7483e02f90d63e9d8c67a7b94" + integrity sha512-eajmj52AxgtUFvPu4Iak8+tmABxlzyCpAkJtKBoOWIJn03SsggK2rxVYOrE4fYe0gWfcTBSz2u5Bc15S/Ur3Rg== + dependencies: + "@mdx-js/mdx" "^1.6.22" + "@parcel/plugin" "2.11.0" + "@parcel/transformer-postcss@2.11.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.11.0.tgz#df5e776a7383400a34c54a64a0bd8f998b656316" @@ -5377,6 +5511,11 @@ "@types/connect" "*" "@types/node" "*" +"@types/chance@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.6.tgz#2fe3de58742629602c3fbab468093b27207f04ad" + integrity sha512-V+pm3stv1Mvz8fSKJJod6CglNGVqEQ6OyuqitoDkWywEODM/eJd1eSuIp9xt6DrX8BWZ2eDSIzbw1tPCUTvGbQ== + "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -5542,6 +5681,13 @@ dependencies: "@types/node" "*" +"@types/hast@^2.0.0": + version "2.3.10" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.10.tgz#5c9d9e0b304bbb8879b857225c5ebab2d81d7643" + integrity sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw== + dependencies: + "@types/unist" "^2" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -5608,6 +5754,13 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.197.tgz#e95c5ddcc814ec3e84c891910a01e0c8a378c54b" integrity sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g== +"@types/mdast@^3.0.0": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.15.tgz#49c524a263f30ffa28b71ae282f813ed000ab9f5" + integrity sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ== + dependencies: + "@types/unist" "^2" + "@types/mdx@^2.0.0": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.7.tgz#c7482e995673e01b83f8e96df83b3843ea76401f" @@ -5673,6 +5826,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + "@types/pretty-hrtime@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" @@ -5786,6 +5944,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/unist@^2", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + "@types/unist@^2.0.0": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.7.tgz#5b06ad6894b236a1d2bd6b2f07850ca5c59cf4d6" @@ -6532,6 +6695,21 @@ babel-plugin-add-react-displayname@^0.0.5: resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" integrity sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw== +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -6663,6 +6841,11 @@ babel-preset-react-app@^10.0.1: babel-plugin-macros "^3.1.0" babel-plugin-transform-react-remove-prop-types "^0.4.24" +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -6900,6 +7083,11 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -6930,6 +7118,11 @@ cborg@^2.0.3: resolved "https://registry.yarnpkg.com/cborg/-/cborg-2.0.5.tgz#b5393c8b1843d5c1a61f2b79b4c9f752052a4d44" integrity sha512-xVW1rSIw1ZXbkwl2XhJ7o/jAv0vnVoQv/QlfQxV8a7V5PlA4UU/AcIiXqmpyybwNWy/GPQU1m/aBVNIWr7/T0w== +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -6955,11 +7148,31 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chance@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.11.tgz#78e10e1f9220a5bbc60a83e3f28a5d8558d84d1b" + integrity sha512-kqTg3WWywappJPqtgrdvbA380VoXO2eu9VCV895JgbyHsaErXdyHK9LOZ911OvAk6L0obK7kDk9CGs8+oBawVA== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -7093,6 +7306,11 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + collect-v8-coverage@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" @@ -7150,6 +7368,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -7675,6 +7898,13 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + detect-indent@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -8879,7 +9109,7 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gensync@^1.0.0-beta.2: +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -9149,6 +9379,74 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -9222,6 +9520,11 @@ html-tags@^3.1.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + html-webpack-plugin@^5.5.0: version "5.5.3" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz#72270f4a78e222b5825b296e5e3e1328ad525a3e" @@ -9378,7 +9681,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -9388,6 +9691,11 @@ ini@~4.1.0: resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== +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== + inquirer@^8.2.0: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" @@ -9445,6 +9753,19 @@ is-absolute-url@^3.0.0: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -9496,6 +9817,11 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -9515,6 +9841,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-deflate@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" @@ -9566,6 +9897,11 @@ is-gzip@^1.0.0: resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -9621,6 +9957,11 @@ is-path-inside@^3.0.2, is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-plain-object@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -9714,6 +10055,16 @@ is-what@^4.1.8: resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.15.tgz#de43a81090417a425942d67b1ae86e7fae2eee0e" integrity sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA== +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -10654,6 +11005,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.uniq@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -10756,6 +11112,11 @@ map-or-similar@^1.5.0: resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + markdown-it@14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.0.0.tgz#b4b2ddeb0f925e88d981f84c183b59bac9e3741b" @@ -10801,6 +11162,13 @@ markdownlint@~0.33.0: markdown-it "14.0.0" markdownlint-micromark "0.1.8" +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + mdast-util-definitions@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" @@ -10808,6 +11176,20 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" @@ -10818,6 +11200,11 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + mdurl@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" @@ -11469,6 +11856,18 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -11479,6 +11878,11 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse5@^7.0.0, parse5@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -11816,6 +12220,13 @@ prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + protobufjs@~7.2.5: version "7.2.5" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d" @@ -12318,6 +12729,47 @@ remark-external-links@^8.0.0: space-separated-tokens "^1.0.0" unist-util-visit "^2.0.0" +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + remark-slug@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" @@ -12327,6 +12779,13 @@ remark-slug@^6.0.0: mdast-util-to-string "^1.0.0" unist-util-visit "^2.0.0" +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + remove-accents@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" @@ -12343,6 +12802,11 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" +repeat-string@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -12389,7 +12853,7 @@ resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.2 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.22.1: +resolve@^1.22.1, resolve@^1.3.2: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -12537,7 +13001,7 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -12676,7 +13140,7 @@ source-map-support@^0.5.16, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.7: +source-map@^0.5.0, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== @@ -12754,6 +13218,11 @@ stackframe@^1.3.4: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -12934,6 +13403,13 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" @@ -13200,6 +13676,21 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + ts-api-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.2.tgz#7c094f753b6705ee4faee25c3c684ade52d66d99" @@ -13390,6 +13881,14 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -13413,6 +13912,18 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -13420,11 +13931,47 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + unist-util-is@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" @@ -13433,7 +13980,7 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit@^2.0.0: +unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== @@ -13596,6 +14143,29 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + victory-vendor@^36.6.8: version "36.6.11" resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.6.11.tgz#acae770717c2dae541a54929c304ecab5ab6ac2a" @@ -13664,6 +14234,11 @@ web-encoding@^1.1.5: optionalDependencies: "@zxing/text-encoding" "0.9.0" +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -13896,7 +14471,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -13961,3 +14536,8 @@ yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==