Commit d7252d4c authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into address-metamask

parents d04e2d33 6f6e325f
...@@ -43,6 +43,7 @@ NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FORNEXT_PUBLIC_AD_ADBUTLER_ON__ ...@@ -43,6 +43,7 @@ NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FORNEXT_PUBLIC_AD_ADBUTLER_ON__
# api config # api config
NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__ NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__
NEXT_PUBLIC_API_BASE_PATH=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_BASE_PATH__ NEXT_PUBLIC_API_BASE_PATH=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_BASE_PATH__
NEXT_PUBLIC_STATS_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_STATS_API_HOST__
# external services config # external services config
NEXT_PUBLIC_SENTRY_DSN=__PLACEHOLDER_FOR_NEXT_PUBLIC_SENTRY_DSN__ NEXT_PUBLIC_SENTRY_DSN=__PLACEHOLDER_FOR_NEXT_PUBLIC_SENTRY_DSN__
......
...@@ -94,6 +94,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -94,6 +94,7 @@ The app instance could be customized by passing following variables to NodeJS en
| --- | --- | --- | --- | | --- | --- | --- | --- |
| NEXT_PUBLIC_API_HOST | `string` *(optional)* | By default the API endpoint base URL will be set as `https://blockscout.com`. If it is not the case, pass the API host in this variable | `my-host.com` | | NEXT_PUBLIC_API_HOST | `string` *(optional)* | By default the API endpoint base URL will be set as `https://blockscout.com`. If it is not the case, pass the API host in this variable | `my-host.com` |
| NEXT_PUBLIC_API_BASE_PATH | `string` *(optional)* | Base path for API endpoint url | `/poa/core` | | NEXT_PUBLIC_API_BASE_PATH | `string` *(optional)* | Base path for API endpoint url | `/poa/core` |
| NEXT_PUBLIC_STATS_API_HOST | `string` *(optional)* | Pass the Stats API host in this variable | `https://my-host.com` |
### Featured network configuration properties ### Featured network configuration properties
......
...@@ -94,6 +94,9 @@ const config = Object.freeze({ ...@@ -94,6 +94,9 @@ const config = Object.freeze({
socket: apiHost ? `wss://${ apiHost }` : 'wss://blockscout.com', socket: apiHost ? `wss://${ apiHost }` : 'wss://blockscout.com',
basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''), basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''),
}, },
statsApi: {
endpoint: getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST),
},
homepage: { homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [], charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [],
plateGradient: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT) || plateGradient: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT) ||
......
...@@ -12,3 +12,4 @@ NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom ...@@ -12,3 +12,4 @@ NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout.com NEXT_PUBLIC_API_HOST=blockscout.com
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
...@@ -20,3 +20,4 @@ NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout','id':'token-approval-t ...@@ -20,3 +20,4 @@ NEXT_PUBLIC_MARKETPLACE_APP_LIST=[{'author': 'Blockscout','id':'token-approval-t
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout-main.test.aws-k8s.blockscout.com NEXT_PUBLIC_API_HOST=blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
...@@ -10,3 +10,4 @@ NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom ...@@ -10,3 +10,4 @@ NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout.com NEXT_PUBLIC_API_HOST=blockscout.com
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
...@@ -5,6 +5,7 @@ NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','url':'https://blockscout ...@@ -5,6 +5,7 @@ NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Gnosis Chain','url':'https://blockscout
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}]
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cup'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cup']
NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=radial-gradient(at 12% 37%, hsla(324,73%,67%,1) 0px, transparent 50%), radial-gradient(at 62% 14%, hsla(256,87%,73%,1) 0px, transparent 50%), radial-gradient(at 84% 80%, hsla(128,75%,73%,1) 0px, transparent 50%), radial-gradient(at 57% 46%, hsla(285,63%,72%,1) 0px, transparent 50%), radial-gradient(at 37% 30%, hsla(174,70%,61%,1) 0px, transparent 50%), radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%), radial-gradient(at 67% 57%, hsla(14,95%,76%,1) 0px, transparent 50%) NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=radial-gradient(at 12% 37%, hsla(324,73%,67%,1) 0px, transparent 50%), radial-gradient(at 62% 14%, hsla(256,87%,73%,1) 0px, transparent 50%), radial-gradient(at 84% 80%, hsla(128,75%,73%,1) 0px, transparent 50%), radial-gradient(at 57% 46%, hsla(285,63%,72%,1) 0px, transparent 50%), radial-gradient(at 37% 30%, hsla(174,70%,61%,1) 0px, transparent 50%), radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%), radial-gradient(at 67% 57%, hsla(14,95%,76%,1) 0px, transparent 50%)
#NEXT_PUBLIC_NETWORK_LOGO=https://placekitten.com/300/60
#NEXT_PUBLIC_NETWORK_SMALL_LOGO=https://placekitten.com/300/300 #NEXT_PUBLIC_NETWORK_SMALL_LOGO=https://placekitten.com/300/300
# network config # network config
......
...@@ -14,3 +14,4 @@ NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true ...@@ -14,3 +14,4 @@ NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout.com NEXT_PUBLIC_API_HOST=blockscout.com
NEXT_PUBLIC_STATS_API_HOST=https://stats-test.aws-k8s.blockscout.com
...@@ -455,6 +455,7 @@ frontend: ...@@ -455,6 +455,7 @@ frontend:
- "/blocks" - "/blocks"
- "/block" - "/block"
- "/address" - "/address"
- "/stats"
resources: resources:
limits: limits:
memory: memory:
...@@ -518,6 +519,8 @@ frontend: ...@@ -518,6 +519,8 @@ frontend:
_default: unknown _default: unknown
NEXT_PUBLIC_API_HOST: NEXT_PUBLIC_API_HOST:
_default: blockscout.com/eth/goerli _default: blockscout.com/eth/goerli
NEXT_PUBLIC_STATS_API_HOST:
_default: https://stats-test.aws-k8s.blockscout.com/
NEXT_PUBLIC_APP_HOST: NEXT_PUBLIC_APP_HOST:
_default: blockscout.com/eth/goerli _default: blockscout.com/eth/goerli
NEXT_PUBLIC_LOGOUT_URL: NEXT_PUBLIC_LOGOUT_URL:
......
...@@ -318,6 +318,7 @@ frontend: ...@@ -318,6 +318,7 @@ frontend:
- "/block" - "/block"
- "/login" - "/login"
- "/address" - "/address"
- "/stats"
resources: resources:
limits: limits:
memory: memory:
...@@ -373,6 +374,8 @@ frontend: ...@@ -373,6 +374,8 @@ frontend:
NEXT_PUBLIC_API_HOST: NEXT_PUBLIC_API_HOST:
_default: blockscout.com _default: blockscout.com
review: blockscout-main.test.aws-k8s.blockscout.com review: blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_STATS_API_HOST:
_default: https://stats-test.aws-k8s.blockscout.com/
NEXT_PUBLIC_AUTH_URL: NEXT_PUBLIC_AUTH_URL:
_default: https://blockscout-main.test.aws-k8s.blockscout.com _default: https://blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_API_BASE_PATH: NEXT_PUBLIC_API_BASE_PATH:
......
<svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.822 15.052h-6.9v2.731h3.19l-.01 3.433c-.123.132-.269.24-.43.319-.21.108-.43.197-.656.267a5.653 5.653 0 0 1-1.656.247 3.012 3.012 0 0 1-2.715-1.424 7.746 7.746 0 0 1-.9-4.064v-2.6a9.44 9.44 0 0 1 .265-2.36 5.76 5.76 0 0 1 .715-1.71c.267-.427.63-.785 1.06-1.047.4-.237.858-.36 1.323-.357a2.922 2.922 0 0 1 2.191.735c.526.615.84 1.383.895 2.191h3.627a8.472 8.472 0 0 0-.615-2.458 5.315 5.315 0 0 0-1.304-1.857 5.626 5.626 0 0 0-2.047-1.164 9.016 9.016 0 0 0-2.84-.4 6.71 6.71 0 0 0-2.774.572A6.354 6.354 0 0 0 2.016 7.77a7.927 7.927 0 0 0-1.483 2.659 10.954 10.954 0 0 0-.532 3.557v2.575a11.475 11.475 0 0 0 .51 3.557c.3.97.793 1.871 1.45 2.646a6.251 6.251 0 0 0 2.264 1.65 7.406 7.406 0 0 0 2.966.573c.765.005 1.529-.07 2.278-.221a10.497 10.497 0 0 0 1.914-.58 7.636 7.636 0 0 0 1.477-.8c.362-.247.691-.54.98-.87l-.018-7.464Zm2.66 2.783c-.01.97.143 1.935.451 2.855.281.837.73 1.61 1.317 2.269a5.985 5.985 0 0 0 2.133 1.5 7.278 7.278 0 0 0 2.88.54 7.194 7.194 0 0 0 2.86-.54 5.935 5.935 0 0 0 2.118-1.5 6.602 6.602 0 0 0 1.311-2.27c.308-.92.46-1.884.45-2.854v-.274a8.673 8.673 0 0 0-.45-2.84 6.532 6.532 0 0 0-1.317-2.27 6.078 6.078 0 0 0-2.125-1.508 7.812 7.812 0 0 0-5.74 0 6.082 6.082 0 0 0-2.12 1.508 6.516 6.516 0 0 0-1.317 2.27 8.647 8.647 0 0 0-.45 2.84v.274Zm3.681-.273a7.394 7.394 0 0 1 .174-1.625c.1-.478.284-.936.541-1.352a2.633 2.633 0 0 1 2.357-1.26c.495-.016.984.1 1.418.337.387.224.712.542.947.923.253.417.435.874.535 1.352a7.4 7.4 0 0 1 .173 1.625v.273a7.613 7.613 0 0 1-.172 1.659c-.1.478-.281.935-.537 1.352a2.622 2.622 0 0 1-2.337 1.255 2.818 2.818 0 0 1-1.424-.338 2.759 2.759 0 0 1-.96-.917 4.143 4.143 0 0 1-.541-1.352 7.6 7.6 0 0 1-.174-1.659v-.274.001Zm-1.747-10.3c.086.196.212.37.37.514.167.147.36.262.57.338.472.164.985.164 1.456 0 .21-.076.404-.19.57-.338.16-.143.285-.318.37-.514a1.594 1.594 0 0 0 0-1.274 1.562 1.562 0 0 0-.37-.52 1.7 1.7 0 0 0-.57-.344 2.206 2.206 0 0 0-1.456 0 1.7 1.7 0 0 0-.57.345 1.562 1.562 0 0 0-.502 1.157c0 .219.045.436.133.637v-.001Zm6.357.013c.086.197.212.374.37.52a1.7 1.7 0 0 0 .57.345c.47.165.984.165 1.456 0a1.7 1.7 0 0 0 .57-.345c.157-.146.283-.323.37-.52.089-.205.134-.427.132-.65a1.561 1.561 0 0 0-.503-1.151 1.761 1.761 0 0 0-.57-.338 2.206 2.206 0 0 0-1.456 0c-.21.075-.403.19-.57.338a1.546 1.546 0 0 0-.503 1.151c-.002.224.043.446.134.65Z" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M13.822 15.052h-6.9v2.731h3.19l-.01 3.433c-.123.132-.269.24-.43.319-.21.108-.43.197-.656.267a5.653 5.653 0 0 1-1.656.247 3.012 3.012 0 0 1-2.715-1.424 7.746 7.746 0 0 1-.9-4.064v-2.6a9.44 9.44 0 0 1 .265-2.36 5.76 5.76 0 0 1 .715-1.71c.267-.427.63-.785 1.06-1.047.4-.237.858-.36 1.323-.357a2.922 2.922 0 0 1 2.191.735c.526.615.84 1.383.895 2.191h3.627a8.472 8.472 0 0 0-.615-2.458 5.315 5.315 0 0 0-1.304-1.857 5.626 5.626 0 0 0-2.047-1.164 9.016 9.016 0 0 0-2.84-.4 6.71 6.71 0 0 0-2.774.572A6.354 6.354 0 0 0 2.016 7.77a7.927 7.927 0 0 0-1.483 2.659 10.954 10.954 0 0 0-.532 3.557v2.575a11.475 11.475 0 0 0 .51 3.557c.3.97.793 1.871 1.45 2.646a6.251 6.251 0 0 0 2.264 1.65 7.406 7.406 0 0 0 2.966.573c.765.005 1.529-.07 2.278-.221a10.497 10.497 0 0 0 1.914-.58 7.636 7.636 0 0 0 1.477-.8 5.2 5.2 0 0 0 .98-.87l-.018-7.464Zm2.66 2.783c-.01.97.143 1.935.451 2.855.281.837.73 1.61 1.317 2.269a5.985 5.985 0 0 0 2.133 1.5 7.278 7.278 0 0 0 2.88.54 7.194 7.194 0 0 0 2.86-.54 5.935 5.935 0 0 0 2.118-1.5 6.602 6.602 0 0 0 1.311-2.27c.308-.92.46-1.884.45-2.854v-.274a8.673 8.673 0 0 0-.45-2.84 6.532 6.532 0 0 0-1.317-2.27 6.078 6.078 0 0 0-2.125-1.508 7.812 7.812 0 0 0-5.74 0 6.082 6.082 0 0 0-2.12 1.508 6.516 6.516 0 0 0-1.317 2.27 8.647 8.647 0 0 0-.45 2.84v.274Zm3.681-.273a7.394 7.394 0 0 1 .174-1.625c.1-.478.284-.936.541-1.352a2.633 2.633 0 0 1 2.357-1.26c.495-.016.984.1 1.418.337.387.224.712.542.947.923.253.417.435.874.535 1.352a7.4 7.4 0 0 1 .173 1.625v.273a7.613 7.613 0 0 1-.172 1.659c-.1.478-.281.935-.537 1.352a2.622 2.622 0 0 1-2.337 1.255 2.818 2.818 0 0 1-1.424-.338 2.759 2.759 0 0 1-.96-.917 4.143 4.143 0 0 1-.541-1.352 7.6 7.6 0 0 1-.174-1.659v-.274.001Zm-1.747-10.3c.086.196.212.37.37.514.167.147.36.262.57.338.472.164.985.164 1.456 0 .21-.076.404-.19.57-.338.16-.143.285-.318.37-.514a1.594 1.594 0 0 0 0-1.274 1.562 1.562 0 0 0-.37-.52 1.7 1.7 0 0 0-.57-.344 2.206 2.206 0 0 0-1.456 0 1.7 1.7 0 0 0-.57.345 1.562 1.562 0 0 0-.502 1.157c0 .219.045.436.133.637v-.001Zm6.357.013c.086.197.212.374.37.52a1.7 1.7 0 0 0 .57.345c.47.165.984.165 1.456 0a1.7 1.7 0 0 0 .57-.345c.157-.146.283-.323.37-.52.089-.205.134-.427.132-.65a1.561 1.561 0 0 0-.503-1.151 1.761 1.761 0 0 0-.57-.338 2.206 2.206 0 0 0-1.456 0c-.21.075-.403.19-.57.338a1.546 1.546 0 0 0-.503 1.151c-.002.224.043.446.134.65Z" fill="currentColor"/>
</svg> </svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.485 7.012h1.446a.918.918 0 1 1 0 1.84H4.253a.919.919 0 0 1-.92-.92V4.254a.919.919 0 1 1 1.84 0v1.47l.505-.505a6.436 6.436 0 0 1 9.103 0 6.436 6.436 0 0 1 0 9.103 6.436 6.436 0 0 1-9.103 0 .92.92 0 0 1 1.302-1.301 4.597 4.597 0 1 0 0-6.503l-.495.494Z" fill="currentColor"/>
</svg>
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.615 5.192c0-.934.758-1.692 1.693-1.692h2.538c.935 0 1.692.758 1.692 1.692v18.616c0 .934-.757 1.692-1.692 1.692h-2.538a1.692 1.692 0 0 1-1.693-1.692V5.192Zm4.231.339a.339.339 0 0 0-.338-.339h-1.862a.339.339 0 0 0-.338.339v17.938c0 .187.151.339.338.339h1.862a.339.339 0 0 0 .338-.339V5.531ZM4 19.577c0-.935.758-1.692 1.692-1.692h2.539c.934 0 1.692.757 1.692 1.692v4.23c0 .935-.758 1.693-1.692 1.693H5.692A1.692 1.692 0 0 1 4 23.808v-4.231Zm4.23.338a.339.339 0 0 0-.338-.338H6.031a.339.339 0 0 0-.339.338v3.554a.34.34 0 0 0 .339.339h1.861a.338.338 0 0 0 .339-.339v-3.554Zm12.693-9.646c-.934 0-1.692.758-1.692 1.693v11.846c0 .934.757 1.692 1.692 1.692h2.539c.934 0 1.692-.758 1.692-1.692V11.962c0-.935-.758-1.693-1.692-1.693h-2.539Zm2.2 1.693c.187 0 .339.151.339.338v11.17a.338.338 0 0 1-.339.338h-1.861a.338.338 0 0 1-.339-.339V12.3c0-.187.152-.338.339-.338h1.861Z" fill="currentColor"/>
</svg>
...@@ -8,14 +8,17 @@ import * as cookies from 'lib/cookies'; ...@@ -8,14 +8,17 @@ import * as cookies from 'lib/cookies';
// first arg can be only a string // first arg can be only a string
// FIXME migrate to RequestInfo later if needed // FIXME migrate to RequestInfo later if needed
export default function fetchFactory(_req: NextApiRequest) { export default function fetchFactory(
_req: NextApiRequest,
apiEndpoint: string = appConfig.api.endpoint,
) {
return function fetch(path: string, init?: RequestInit): Promise<Response> { return function fetch(path: string, init?: RequestInit): Promise<Response> {
const headers = { const headers = {
accept: 'application/json', accept: 'application/json',
'content-type': 'application/json', 'content-type': 'application/json',
cookie: `${ cookies.NAMES.API_TOKEN }=${ _req.cookies[cookies.NAMES.API_TOKEN] }`, cookie: `${ cookies.NAMES.API_TOKEN }=${ _req.cookies[cookies.NAMES.API_TOKEN] }`,
}; };
const url = new URL(path, appConfig.api.endpoint); const url = new URL(path, apiEndpoint);
httpLogger.logger.info({ httpLogger.logger.info({
message: 'Trying to call API', message: 'Trying to call API',
......
...@@ -6,7 +6,7 @@ import { httpLogger } from 'lib/api/logger'; ...@@ -6,7 +6,7 @@ import { httpLogger } from 'lib/api/logger';
type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE'; type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE';
export default function createHandler(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>) { export default function createHandler(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>, apiEndpoint?: string) {
const handler = async(_req: NextApiRequest, res: NextApiResponse) => { const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
httpLogger(_req, res); httpLogger(_req, res);
...@@ -18,8 +18,8 @@ export default function createHandler(getUrl: (_req: NextApiRequest) => string, ...@@ -18,8 +18,8 @@ export default function createHandler(getUrl: (_req: NextApiRequest) => string,
const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD'; const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD';
const url = getUrlWithNetwork(_req, `/api${ getUrl(_req) }`); const url = apiEndpoint ? `/api${ getUrl(_req) }` : getUrlWithNetwork(_req, `/api${ getUrl(_req) }`);
const fetch = fetchFactory(_req); const fetch = fetchFactory(_req, apiEndpoint);
const response = await fetch(url, { const response = await fetch(url, {
method: _req.method, method: _req.method,
body: isBodyDisallowed ? undefined : _req.body, body: isBodyDisallowed ? undefined : _req.body,
......
import BigNumber from 'bignumber.js';
interface Params {
value: string;
exchangeRate?: string | null;
accuracy?: number;
accuracyUsd?: number;
decimals?: string | null;
}
export default function getCurrencyValue({ value, accuracy, accuracyUsd, decimals, exchangeRate }: Params) {
const valueCurr = BigNumber(value).div(BigNumber(10 ** Number(decimals || '18')));
const valueResult = accuracy ? valueCurr.dp(accuracy).toFormat() : valueCurr.toFormat();
let usdResult: string | undefined;
if (exchangeRate) {
const exchangeRateBn = new BigNumber(exchangeRate);
const usdBn = valueCurr.times(exchangeRateBn);
if (accuracyUsd && !usdBn.isEqualTo(0)) {
const usdBnDp = usdBn.dp(accuracyUsd);
usdResult = usdBnDp.isEqualTo(0) ? usdBn.precision(accuracyUsd).toFormat() : usdBnDp.toFormat();
} else {
usdResult = usdBn.toFormat();
}
}
return { valueStr: valueResult, usd: usdResult };
}
import { useRouter } from 'next/router';
import link from 'lib/link/link';
export default function useLoginUrl() {
const router = useRouter();
return link('auth', {}, { path: router.asPath });
}
...@@ -9,6 +9,7 @@ import blocksIcon from 'icons/block.svg'; ...@@ -9,6 +9,7 @@ import blocksIcon from 'icons/block.svg';
import privateTagIcon from 'icons/privattags.svg'; import privateTagIcon from 'icons/privattags.svg';
import profileIcon from 'icons/profile.svg'; import profileIcon from 'icons/profile.svg';
import publicTagIcon from 'icons/publictags.svg'; import publicTagIcon from 'icons/publictags.svg';
import statsIcon from 'icons/stats.svg';
import tokensIcon from 'icons/token.svg'; import tokensIcon from 'icons/token.svg';
import transactionsIcon from 'icons/transactions.svg'; import transactionsIcon from 'icons/transactions.svg';
import watchlistIcon from 'icons/watchlist.svg'; import watchlistIcon from 'icons/watchlist.svg';
...@@ -28,6 +29,7 @@ export default function useNavItems() { ...@@ -28,6 +29,7 @@ export default function useNavItems() {
{ text: 'Tokens', url: link('tokens'), icon: tokensIcon, isActive: currentRoute === 'tokens', isNewUi: false }, { text: 'Tokens', url: link('tokens'), icon: tokensIcon, isActive: currentRoute === 'tokens', isNewUi: false },
isMarketplaceFilled ? isMarketplaceFilled ?
{ text: 'Apps', url: link('apps'), icon: appsIcon, isActive: currentRoute === 'apps', isNewUi: true } : null, { text: 'Apps', url: link('apps'), icon: appsIcon, isActive: currentRoute === 'apps', isNewUi: true } : null,
{ text: 'Charts & stats', url: link('stats'), icon: statsIcon, isActive: currentRoute === 'stats', isNewUi: true },
// there should be custom site sections like Stats, Faucet, More, etc but never an 'other' // there should be custom site sections like Stats, Faucet, More, etc but never an 'other'
// examples https://explorer-edgenet.polygon.technology/ and https://explorer.celo.org/ // examples https://explorer-edgenet.polygon.technology/ and https://explorer.celo.org/
// at this stage custom menu items is under development, we will implement it later // at this stage custom menu items is under development, we will implement it later
......
import React from 'react';
// prevent set focus on button when closing modal
export default function usePreventFocusAfterModalClosing() {
return React.useCallback((e: React.SyntheticEvent) => e.stopPropagation(), []);
}
...@@ -3,9 +3,9 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; ...@@ -3,9 +3,9 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
import { pick, omit } from 'lodash'; import { pick, omit } from 'lodash';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll, scroller } from 'react-scroll';
import { PAGINATION_FIELDS } from 'types/api/pagination'; import { PAGINATION_FIELDS, PAGINATION_FILTERS_FIELDS } from 'types/api/pagination';
import type { PaginationParams, PaginatedResponse, PaginatedQueryKeys, PaginationFilters } from 'types/api/pagination'; import type { PaginationParams, PaginatedResponse, PaginatedQueryKeys, PaginationFilters } from 'types/api/pagination';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
...@@ -16,9 +16,17 @@ interface Params<QueryName extends PaginatedQueryKeys> { ...@@ -16,9 +16,17 @@ interface Params<QueryName extends PaginatedQueryKeys> {
queryIds?: Array<string>; queryIds?: Array<string>;
filters?: PaginationFilters<QueryName>; filters?: PaginationFilters<QueryName>;
options?: Omit<UseQueryOptions<unknown, unknown, PaginatedResponse<QueryName>>, 'queryKey' | 'queryFn'>; options?: Omit<UseQueryOptions<unknown, unknown, PaginatedResponse<QueryName>>, 'queryKey' | 'queryFn'>;
scroll?: { elem: string; offset: number };
} }
export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>({ queryName, filters, options, apiPath, queryIds }: Params<QueryName>) { export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>({
queryName,
filters,
options,
apiPath,
queryIds,
scroll,
}: Params<QueryName>) {
const paginationFields = PAGINATION_FIELDS[queryName]; const paginationFields = PAGINATION_FIELDS[queryName];
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
...@@ -31,6 +39,10 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>( ...@@ -31,6 +39,10 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
const queryKey = [ queryName, ...(queryIds || []), { page, filters } ]; const queryKey = [ queryName, ...(queryIds || []), { page, filters } ];
const scrollToTop = useCallback(() => {
scroll ? scroller.scrollTo(scroll.elem, { offset: scroll.offset }) : animateScroll.scrollToTop({ duration: 0 });
}, [ scroll ]);
const queryResult = useQuery<unknown, unknown, PaginatedResponse<QueryName>>( const queryResult = useQuery<unknown, unknown, PaginatedResponse<QueryName>>(
queryKey, queryKey,
async() => { async() => {
...@@ -65,10 +77,10 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>( ...@@ -65,10 +77,10 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }) router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
.then(() => { .then(() => {
animateScroll.scrollToTop({ duration: 0 }); scrollToTop();
setPage(prev => prev + 1); setPage(prev => prev + 1);
}); });
}, [ data?.next_page_params, page, pageParams.length, router ]); }, [ data?.next_page_params, page, pageParams.length, router, scrollToTop ]);
const onPrevPageClick = useCallback(() => { const onPrevPageClick = useCallback(() => {
// returning to the first page // returning to the first page
...@@ -85,21 +97,44 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>( ...@@ -85,21 +97,44 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
router.query = nextPageQuery; router.query = nextPageQuery;
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }) router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
.then(() => { .then(() => {
animateScroll.scrollToTop({ duration: 0 }); scrollToTop();
setPage(prev => prev - 1); setPage(prev => prev - 1);
page === 2 && queryClient.clear(); page === 2 && queryClient.clear();
}); });
}, [ router, page, paginationFields, pageParams, queryClient ]); }, [ router, page, paginationFields, pageParams, queryClient, scrollToTop ]);
const resetPage = useCallback(() => { const resetPage = useCallback(() => {
queryClient.clear();
router.push({ pathname: router.pathname, query: omit(router.query, paginationFields, 'page') }, undefined, { shallow: true }).then(() => { router.push({ pathname: router.pathname, query: omit(router.query, paginationFields, 'page') }, undefined, { shallow: true }).then(() => {
animateScroll.scrollToTop({ duration: 0 }); queryClient.removeQueries({ queryKey: [ queryName ] });
scrollToTop();
setPage(1); setPage(1);
setPageParams([ ]); setPageParams([ ]);
canGoBackwards.current = true; canGoBackwards.current = true;
}); });
}, [ queryClient, router, paginationFields ]); }, [ queryClient, queryName, router, paginationFields, scrollToTop ]);
const onFilterChange = useCallback((newFilters: PaginationFilters<QueryName> | undefined) => {
const newQuery = omit(router.query, PAGINATION_FIELDS[queryName], 'page', PAGINATION_FILTERS_FIELDS[queryName]);
if (newFilters) {
Object.entries(newFilters).forEach(([ key, value ]) => {
if (value) {
newQuery[key] = Array.isArray(value) ? value.join(',') : (value || '');
}
});
}
router.push(
{
pathname: router.pathname,
query: newQuery,
},
undefined,
{ shallow: true },
).then(() => {
setPage(1);
setPageParams([ ]);
scrollToTop();
});
}, [ queryName, router, scrollToTop, setPageParams, setPage ]);
const hasPaginationParams = Object.keys(currPageParams).length > 0; const hasPaginationParams = Object.keys(currPageParams).length > 0;
const nextPageParams = data?.next_page_params; const nextPageParams = data?.next_page_params;
...@@ -129,5 +164,5 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>( ...@@ -129,5 +164,5 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
}, 0); }, 0);
}, []); }, []);
return { ...queryResult, pagination }; return { ...queryResult, pagination, onFilterChange };
} }
...@@ -5,7 +5,7 @@ import React from 'react'; ...@@ -5,7 +5,7 @@ import React from 'react';
import { QueryKeys } from 'types/client/accountQueries'; import { QueryKeys } from 'types/client/accountQueries';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import link from 'lib/link/link'; import useLoginUrl from 'lib/hooks/useLoginUrl';
export interface ErrorType { export interface ErrorType {
error?: { error?: {
...@@ -19,6 +19,7 @@ export default function useRedirectForInvalidAuthToken() { ...@@ -19,6 +19,7 @@ export default function useRedirectForInvalidAuthToken() {
const state = queryClient.getQueryState<unknown, ErrorType>([ QueryKeys.profile ]); const state = queryClient.getQueryState<unknown, ErrorType>([ QueryKeys.profile ]);
const errorStatus = state?.error?.error?.status; const errorStatus = state?.error?.error?.status;
const loginUrl = useLoginUrl();
React.useEffect(() => { React.useEffect(() => {
if (errorStatus === 401) { if (errorStatus === 401) {
...@@ -26,9 +27,8 @@ export default function useRedirectForInvalidAuthToken() { ...@@ -26,9 +27,8 @@ export default function useRedirectForInvalidAuthToken() {
if (apiToken) { if (apiToken) {
Sentry.captureException(new Error('Invalid api token'), { tags: { source: 'fetch' } }); Sentry.captureException(new Error('Invalid api token'), { tags: { source: 'fetch' } });
const authURL = link('auth'); window.location.assign(loginUrl);
window.location.assign(authURL);
} }
} }
}, [ errorStatus ]); }, [ errorStatus, loginUrl ]);
} }
...@@ -4,13 +4,18 @@ import React from 'react'; ...@@ -4,13 +4,18 @@ import React from 'react';
import type { RouteName } from 'lib/link/routes'; import type { RouteName } from 'lib/link/routes';
import { ROUTES } from 'lib/link/routes'; import { ROUTES } from 'lib/link/routes';
const PATH_PARAM_REGEXP = /\/:(\w+)/g;
export default function useCurrentRoute() { export default function useCurrentRoute() {
const { route: nextRoute } = useRouter(); const { route: nextRoute } = useRouter();
return React.useCallback((): RouteName => { return React.useCallback((): RouteName => {
for (const routeName in ROUTES) { for (const routeName in ROUTES) {
const route = ROUTES[routeName as RouteName]; const route = ROUTES[routeName as RouteName];
if (route.pattern === nextRoute) { const formattedRoute = route.pattern.replace(PATH_PARAM_REGEXP, (_, paramName: string) => {
return `/[${ paramName }]`;
});
if (formattedRoute === nextRoute) {
return routeName as RouteName; return routeName as RouteName;
} }
} }
......
...@@ -7,6 +7,10 @@ SocketMessage.BlocksIndexStatus | ...@@ -7,6 +7,10 @@ SocketMessage.BlocksIndexStatus |
SocketMessage.TxStatusUpdate | SocketMessage.TxStatusUpdate |
SocketMessage.NewTx | SocketMessage.NewTx |
SocketMessage.NewPendingTx | SocketMessage.NewPendingTx |
SocketMessage.AddressBalance |
SocketMessage.AddressCurrentCoinBalance |
SocketMessage.AddressTokenBalance |
SocketMessage.AddressCoinBalance |
SocketMessage.Unknown; SocketMessage.Unknown;
interface SocketMessageParamsGeneric<Event extends string | undefined, Payload extends object | unknown> { interface SocketMessageParamsGeneric<Event extends string | undefined, Payload extends object | unknown> {
...@@ -15,6 +19,16 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e ...@@ -15,6 +19,16 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e
handler: (payload: Payload) => void; handler: (payload: Payload) => void;
} }
export interface AddressCoinBalancePayload {
coin_balance: {
block_number: number;
block_timestamp?: string;
delta?: string;
transaction_hash?: string | null;
value?: string;
};
}
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace SocketMessage { export namespace SocketMessage {
export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>; export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>;
...@@ -22,5 +36,10 @@ export namespace SocketMessage { ...@@ -22,5 +36,10 @@ export namespace SocketMessage {
export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>;
export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>; export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>;
export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>; export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>;
export type AddressBalance = SocketMessageParamsGeneric<'balance', { balance: string; block_number: number; exchange_rate: string }>;
export type AddressCurrentCoinBalance =
SocketMessageParamsGeneric<'current_coin_balance', { coin_balance: string; block_number: number; exchange_rate: string }>;
export type AddressTokenBalance = SocketMessageParamsGeneric<'token_balance', { block_number: number }>;
export type AddressCoinBalance = SocketMessageParamsGeneric<'coin_balance', AddressCoinBalancePayload>;
export type Unknown = SocketMessageParamsGeneric<undefined, unknown>; export type Unknown = SocketMessageParamsGeneric<undefined, unknown>;
} }
...@@ -21,7 +21,7 @@ export function middleware(req: NextRequest) { ...@@ -21,7 +21,7 @@ export function middleware(req: NextRequest) {
const apiToken = req.cookies.get(NAMES.API_TOKEN); const apiToken = req.cookies.get(NAMES.API_TOKEN);
if ((isAccountRoute || isProfileRoute) && !apiToken && appConfig.isAccountSupported) { if ((isAccountRoute || isProfileRoute) && !apiToken && appConfig.isAccountSupported) {
const authUrl = link('auth'); const authUrl = link('auth', undefined, { path: req.nextUrl.pathname });
return NextResponse.redirect(authUrl); return NextResponse.redirect(authUrl);
} }
......
import type { AddressTokenBalance } from 'types/api/address';
export const erc20a: AddressTokenBalance = {
token: {
address: '0xb2a90505dc6680a7a695f7975d0d32EeF610f456',
decimals: '18',
exchange_rate: null,
holders: '23',
name: 'hyfi.token',
symbol: 'HyFi',
total_supply: '369000000000000000000000000',
type: 'ERC-20',
},
token_id: null,
value: '1169320000000000000000000',
};
export const erc20b: AddressTokenBalance = {
token: {
address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7',
decimals: '6',
exchange_rate: '0.982',
holders: '17',
name: 'USD Coin',
symbol: 'USDC',
total_supply: '900000000000000000000000000',
type: 'ERC-20',
},
token_id: null,
value: '872500000000',
};
export const erc20c: AddressTokenBalance = {
token: {
address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7',
decimals: '18',
exchange_rate: '1328.89',
holders: '17',
name: 'Ethereum',
symbol: 'ETH',
total_supply: '1000000000000000000000000',
type: 'ERC-20',
},
token_id: null,
value: '9852000000000000000000',
};
export const erc20d: AddressTokenBalance = {
token: {
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195',
decimals: '18',
exchange_rate: null,
holders: '102625',
name: 'Zeta',
symbol: 'ZETA',
total_supply: '2100000000000000000000000000',
type: 'ERC-20',
},
token_id: null,
value: '39000000000000000000',
};
export const erc721a: AddressTokenBalance = {
token: {
address: '0xDe7cAc71E072FCBd4453E5FB3558C2684d1F88A0',
decimals: null,
exchange_rate: null,
holders: '7',
name: 'HyFi Athena',
symbol: 'HYFI_ATHENA',
total_supply: '105',
type: 'ERC-721',
},
token_id: null,
value: '51',
};
export const erc721b: AddressTokenBalance = {
token: {
address: '0xA8d5C7beEA8C9bB57f5fBa35fB638BF45550b11F',
decimals: null,
exchange_rate: null,
holders: '2',
name: 'World Of Women Galaxy',
symbol: 'WOWG',
total_supply: null,
type: 'ERC-721',
},
token_id: null,
value: '1',
};
export const erc721c: AddressTokenBalance = {
token: {
address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992',
decimals: null,
exchange_rate: null,
holders: '12',
name: 'Puma',
symbol: 'PUMA',
total_supply: null,
type: 'ERC-721',
},
token_id: null,
value: '5',
};
export const erc1155a: AddressTokenBalance = {
token: {
address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e',
decimals: null,
exchange_rate: null,
holders: '22',
name: 'HyFi Membership',
symbol: 'HYFI_MEMBERSHIP',
total_supply: '482',
type: 'ERC-1155',
},
token_id: '42',
value: '24',
};
export const erc1155b: AddressTokenBalance = {
token: {
address: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefc',
decimals: null,
exchange_rate: null,
holders: '100',
name: 'WinkyVerse Collections',
symbol: 'WVC',
total_supply: '4943',
type: 'ERC-1155',
},
token_id: '100010000000001',
value: '11',
};
export const erc1155withoutName: AddressTokenBalance = {
token: {
address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e',
decimals: null,
exchange_rate: null,
holders: '22',
name: null,
symbol: null,
total_supply: '482',
type: 'ERC-1155',
},
token_id: '64532245',
value: '42',
};
export const baseList = [
erc20a,
erc20b,
erc20c,
erc721a,
erc721b,
erc721c,
erc1155withoutName,
erc1155a,
erc1155b,
];
...@@ -29,6 +29,7 @@ export const erc20: TokenTransfer = { ...@@ -29,6 +29,7 @@ export const erc20: TokenTransfer = {
name: 'ARIANEE', name: 'ARIANEE',
symbol: 'ARIA', symbol: 'ARIA',
type: 'ERC-20', type: 'ERC-20',
total_supply: '0',
}, },
total: { total: {
decimals: '18', decimals: '18',
...@@ -67,6 +68,7 @@ export const erc721: TokenTransfer = { ...@@ -67,6 +68,7 @@ export const erc721: TokenTransfer = {
name: 'Arianee Smart-Asset', name: 'Arianee Smart-Asset',
symbol: 'AriaSA', symbol: 'AriaSA',
type: 'ERC-721', type: 'ERC-721',
total_supply: '0',
}, },
total: { total: {
token_id: '875879856', token_id: '875879856',
...@@ -104,6 +106,7 @@ export const erc1155: TokenTransfer = { ...@@ -104,6 +106,7 @@ export const erc1155: TokenTransfer = {
name: null, name: null,
symbol: null, symbol: null,
type: 'ERC-1155', type: 'ERC-1155',
total_supply: '0',
}, },
total: { total: {
token_id: '123', token_id: '123',
......
...@@ -27,14 +27,14 @@ ...@@ -27,14 +27,14 @@
"test:jest:watch": "jest --watch" "test:jest:watch": "jest --watch"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/react": "2.3.1", "@chakra-ui/react": "2.4.3",
"@chakra-ui/theme-tools": "^2.0.2", "@chakra-ui/theme-tools": "^2.0.14",
"@emotion/react": "^11", "@emotion/react": "^11.10.4",
"@emotion/styled": "^11", "@emotion/styled": "^11.10.4",
"@metamask/providers": "^10.2.1", "@metamask/providers": "^10.2.1",
"@sentry/nextjs": "^7.12.1", "@sentry/nextjs": "^7.12.1",
"@sentry/react": "^7.13.0", "@sentry/react": "^7.24.0",
"@sentry/tracing": "^7.13.0", "@sentry/tracing": "^7.24.0",
"@tanstack/react-query": "^4.0.10", "@tanstack/react-query": "^4.0.10",
"@tanstack/react-query-devtools": "^4.0.10", "@tanstack/react-query-devtools": "^4.0.10",
"@types/react-scroll": "^1.8.4", "@types/react-scroll": "^1.8.4",
...@@ -42,15 +42,16 @@ ...@@ -42,15 +42,16 @@
"d3": "^7.6.1", "d3": "^7.6.1",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"ethers": "^5.7.1", "ethers": "^5.7.1",
"framer-motion": "^6", "framer-motion": "^6.5.1",
"lodash": "^4.0.0", "lodash": "^4.0.0",
"next": "12.2.5", "next": "12.2.5",
"node-fetch": "^3.2.9", "node-fetch": "^3.2.9",
"phoenix": "^1.6.15", "phoenix": "^1.6.15",
"pino-http": "^8.2.1", "pino-http": "^8.2.1",
"pino-pretty": "^9.1.1", "pino-pretty": "^9.1.1",
"react": "18.1.0", "qrcode": "^1.5.1",
"react-dom": "18.1.0", "react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.33.1", "react-hook-form": "^7.33.1",
"react-identicons": "^1.2.5", "react-identicons": "^1.2.5",
"react-jazzicon": "^1.0.4", "react-jazzicon": "^1.0.4",
...@@ -66,6 +67,7 @@ ...@@ -66,6 +67,7 @@
"@types/jest": "^29.2.0", "@types/jest": "^29.2.0",
"@types/node": "17.0.36", "@types/node": "17.0.36",
"@types/phoenix": "^1.5.4", "@types/phoenix": "^1.5.4",
"@types/qrcode": "^1.5.0",
"@types/react": "18.0.9", "@types/react": "18.0.9",
"@types/react-dom": "18.0.5", "@types/react-dom": "18.0.5",
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
......
import type { NextApiRequest } from 'next';
import getSearchParams from 'lib/api/getSearchParams';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const searchParamsStr = getSearchParams(req);
return `/v2/addresses/${ req.query.id }/token-transfers${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
import type { NextApiRequest } from 'next';
import getSearchParams from 'lib/api/getSearchParams';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const searchParamsStr = getSearchParams(req);
return `/v2/addresses/${ req.query.id }/transactions${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
import type { NextApiRequest } from 'next';
import appConfig from 'configs/app/config';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const { name, from, to } = req.query;
return `/v1/charts/line?name=${ name }${ from ? `&from=${ from }&to=${ to }` : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ], appConfig.statsApi.endpoint);
export default requestHandler;
import appConfig from 'configs/app/config';
import handler from 'lib/api/handler';
const getUrl = () => '/v1/counters';
const requestHandler = handler(getUrl, [ 'GET' ], appConfig.statsApi.endpoint);
export default requestHandler;
...@@ -2,6 +2,7 @@ import type { TestFixture, Page } from '@playwright/test'; ...@@ -2,6 +2,7 @@ import type { TestFixture, Page } from '@playwright/test';
import type { WebSocket } from 'ws'; import type { WebSocket } from 'ws';
import { WebSocketServer } from 'ws'; import { WebSocketServer } from 'ws';
import type { AddressCoinBalancePayload } from 'lib/socket/types';
import type { NewBlockSocketResponse } from 'types/api/block'; import type { NewBlockSocketResponse } from 'types/api/block';
type ReturnType = () => Promise<WebSocket>; type ReturnType = () => Promise<WebSocket>;
...@@ -53,6 +54,8 @@ export const joinChannel = async(socket: WebSocket, channelName: string) => { ...@@ -53,6 +54,8 @@ export const joinChannel = async(socket: WebSocket, channelName: string) => {
}); });
}; };
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'coin_balance', payload: AddressCoinBalancePayload): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_balance', payload: { block_number: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transaction: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transaction: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transaction: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transaction: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'new_block', payload: NewBlockSocketResponse): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'new_block', payload: NewBlockSocketResponse): void;
......
...@@ -51,15 +51,15 @@ const variantOutline = defineStyle((props) => { ...@@ -51,15 +51,15 @@ const variantOutline = defineStyle((props) => {
const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props); const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
const activeColor = (() => { const activeColor = (() => {
if (c === 'gray') { if (c === 'gray') {
return mode('blue.400', 'gray.50')(props); return mode('blue.600', 'gray.50')(props);
} }
if (c === 'gray-dark') { if (c === 'gray-dark') {
return mode('blue.700', 'gray.50')(props); return mode('blue.600', 'gray.50')(props);
} }
if (c === 'blue') { if (c === 'blue') {
return mode('blue.400', 'gray.50')(props); return mode('blue.600', 'gray.50')(props);
} }
return 'blue.400'; return 'blue.600';
})(); })();
return { return {
...@@ -77,11 +77,17 @@ const variantOutline = defineStyle((props) => { ...@@ -77,11 +77,17 @@ const variantOutline = defineStyle((props) => {
bg: props.isActive ? activeBg : 'transparent', bg: props.isActive ? activeBg : 'transparent',
borderColor: props.isActive ? activeBg : 'blue.400', borderColor: props.isActive ? activeBg : 'blue.400',
color: props.isActive ? activeColor : 'blue.400', color: props.isActive ? activeColor : 'blue.400',
p: {
color: 'blue.400',
},
}, },
_disabled: { _disabled: {
color, color,
borderColor, borderColor,
}, },
p: {
color: 'blue.400',
},
}, },
_disabled: { _disabled: {
opacity: 0.2, opacity: 0.2,
...@@ -94,6 +100,9 @@ const variantOutline = defineStyle((props) => { ...@@ -94,6 +100,9 @@ const variantOutline = defineStyle((props) => {
color, color,
borderColor, borderColor,
}, },
p: {
color: activeColor,
},
}, },
}; };
}); });
......
import { formAnatomy as parts } from '@chakra-ui/anatomy'; import { formAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'; import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools'; import { getColor } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getDefaultFormColors from '../utils/getDefaultFormColors';
import FormLabel from './FormLabel'; import FormLabel from './FormLabel';
...@@ -13,7 +13,7 @@ const { definePartsStyle, defineMultiStyleConfig } = ...@@ -13,7 +13,7 @@ const { definePartsStyle, defineMultiStyleConfig } =
function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) { function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
const { theme } = props; const { theme } = props;
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props); const { focusPlaceholderColor, errorColor } = getDefaultFormColors(props);
const activeLabelStyles = { const activeLabelStyles = {
...FormLabel.variants?.floating?.(props)._focusWithin, ...FormLabel.variants?.floating?.(props)._focusWithin,
...@@ -62,7 +62,7 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction ...@@ -62,7 +62,7 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
label: FormLabel.sizes?.[size](props) || {}, label: FormLabel.sizes?.[size](props) || {},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles, 'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': { 'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': {
color: getColor(theme, ec), color: getColor(theme, errorColor),
}, },
// input styles // input styles
...@@ -78,20 +78,20 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction ...@@ -78,20 +78,20 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
// indicator styles // indicator styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': { 'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, fc), color: getColor(theme, focusPlaceholderColor),
}, },
'input[aria-invalid=true] + label .chakra-form__required-indicator, textarea[aria-invalid=true] + label .chakra-form__required-indicator': { 'input[aria-invalid=true] + label .chakra-form__required-indicator, textarea[aria-invalid=true] + label .chakra-form__required-indicator': {
color: getColor(theme, ec), color: getColor(theme, errorColor),
}, },
}, },
}; };
} }
const baseStyle = definePartsStyle((props) => { const baseStyle = definePartsStyle(() => {
return { return {
requiredIndicator: { requiredIndicator: {
marginStart: 0, marginStart: 0,
color: mode('gray.500', 'whiteAlpha.400')(props), color: 'gray.500',
}, },
}; };
}); });
......
...@@ -19,7 +19,7 @@ const baseStyle = defineStyle({ ...@@ -19,7 +19,7 @@ const baseStyle = defineStyle({
const variantFloating = defineStyle((props) => { const variantFloating = defineStyle((props) => {
const { theme, backgroundColor } = props; const { theme, backgroundColor } = props;
const { focusColor: fc } = getDefaultFormColors(props); const { focusPlaceholderColor } = getDefaultFormColors(props);
const bc = backgroundColor || mode('white', 'black')(props); const bc = backgroundColor || mode('white', 'black')(props);
return { return {
...@@ -40,7 +40,7 @@ const variantFloating = defineStyle((props) => { ...@@ -40,7 +40,7 @@ const variantFloating = defineStyle((props) => {
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
_focusWithin: { _focusWithin: {
backgroundColor: bc, backgroundColor: bc,
color: getColor(theme, fc), color: getColor(theme, focusPlaceholderColor),
fontSize: 'xs', fontSize: 'xs',
lineHeight: '16px', lineHeight: '16px',
borderTopRightRadius: 'none', borderTopRightRadius: 'none',
......
...@@ -26,6 +26,10 @@ const baseStyleContent = defineStyle((props) => { ...@@ -26,6 +26,10 @@ const baseStyleContent = defineStyle((props) => {
bg: $popperBg.reference, bg: $popperBg.reference,
[$arrowBg.variable]: $popperBg.reference, [$arrowBg.variable]: $popperBg.reference,
[$arrowShadowColor.variable]: `colors.${ shadowColor }`, [$arrowShadowColor.variable]: `colors.${ shadowColor }`,
_dark: {
[$popperBg.variable]: `colors.gray.900`,
[$arrowShadowColor.variable]: `colors.whiteAlpha.300`,
},
width: 'xs', width: 'xs',
border: 'none', border: 'none',
borderColor: 'inherit', borderColor: 'inherit',
......
...@@ -4,7 +4,8 @@ import { mode } from '@chakra-ui/theme-tools'; ...@@ -4,7 +4,8 @@ import { mode } from '@chakra-ui/theme-tools';
export default function getDefaultFormColors(props: StyleFunctionProps) { export default function getDefaultFormColors(props: StyleFunctionProps) {
const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props; const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props;
return { return {
focusColor: fc || mode('brand.700', 'brand.300')(props), focusBorderColor: fc || mode('blue.500', 'blue.300')(props),
focusPlaceholderColor: fc || 'gray.500',
errorColor: ec || mode('red.400', 'red.300')(props), errorColor: ec || mode('red.400', 'red.300')(props),
filledColor: flc || mode('gray.300', 'gray.600')(props), filledColor: flc || mode('gray.300', 'gray.600')(props),
}; };
......
...@@ -6,7 +6,7 @@ import getDefaultTransitionProps from './getDefaultTransitionProps'; ...@@ -6,7 +6,7 @@ import getDefaultTransitionProps from './getDefaultTransitionProps';
export default function getOutlinedFieldStyles(props: StyleFunctionProps) { export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
const { theme, borderColor } = props; const { theme, borderColor } = props;
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props); const { focusBorderColor, errorColor } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps(); const transitionProps = getDefaultTransitionProps();
return { return {
...@@ -32,12 +32,12 @@ export default function getOutlinedFieldStyles(props: StyleFunctionProps) { ...@@ -32,12 +32,12 @@ export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
}, },
}, },
_invalid: { _invalid: {
borderColor: getColor(theme, ec), borderColor: getColor(theme, errorColor),
boxShadow: `none`, boxShadow: `none`,
}, },
_focusVisible: { _focusVisible: {
zIndex: 1, zIndex: 1,
borderColor: getColor(theme, fc), borderColor: getColor(theme, focusBorderColor),
boxShadow: 'md', boxShadow: 'md',
}, },
_placeholder: { _placeholder: {
......
import type { AddressParam } from './addressParams';
export interface AddressTag { export interface AddressTag {
address_hash: string; address_hash: string;
name: string; name: string;
...@@ -63,6 +64,7 @@ export interface WatchlistAddress { ...@@ -63,6 +64,7 @@ export interface WatchlistAddress {
notification_settings: NotificationSettings; notification_settings: NotificationSettings;
notification_methods: NotificationMethods; notification_methods: NotificationMethods;
id: string; id: string;
address?: AddressParam;
} }
export interface WatchlistAddressNew { export interface WatchlistAddressNew {
......
import type { Transaction } from 'types/api/transaction';
import type { AddressTag, WatchlistName } from './addressParams'; import type { AddressTag, WatchlistName } from './addressParams';
import type { TokenInfo } from './tokenInfo'; import type { TokenInfo, TokenType } from './tokenInfo';
import type { TokenTransfer, TokenTransferPagination } from './tokenTransfer';
export interface Address { export interface Address {
block_number_balance_updated_at: number | null; block_number_balance_updated_at: number | null;
...@@ -20,10 +23,10 @@ export interface Address { ...@@ -20,10 +23,10 @@ export interface Address {
} }
export interface AddressCounters { export interface AddressCounters {
transaction_count: string; transactions_count: string;
token_transfer_count: string; token_transfers_count: string;
gas_usage_count: string; gas_usage_count: string;
validation_count: string | null; validations_count: string | null;
} }
export interface AddressTokenBalance { export interface AddressTokenBalance {
...@@ -31,3 +34,28 @@ export interface AddressTokenBalance { ...@@ -31,3 +34,28 @@ export interface AddressTokenBalance {
token_id: string | null; token_id: string | null;
value: string; value: string;
} }
export interface AddressTransactionsResponse {
items: Array<Transaction>;
next_page_params: {
block_number: number;
index: number;
items_count: number;
} | null;
}
type AddressFromToFilter = 'from' | 'to' | undefined;
export type AddressTxsFilters = {
filter: AddressFromToFilter;
}
export interface AddressTokenTransferResponse {
items: Array<TokenTransfer>;
next_page_params: TokenTransferPagination | null;
}
export type AddressTokenTransferFilters = {
filter: AddressFromToFilter;
type: TokenType;
}
import type { AddressTransactionsResponse, AddressTokenTransferResponse, AddressTxsFilters, AddressTokenTransferFilters } from 'types/api/address';
import type { BlocksResponse, BlockTransactionsResponse, BlockFilters } from 'types/api/block'; import type { BlocksResponse, BlockTransactionsResponse, BlockFilters } from 'types/api/block';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { LogsResponse } from 'types/api/log'; import type { LogsResponse } from 'types/api/log';
...@@ -8,6 +9,8 @@ import { QueryKeys } from 'types/client/queries'; ...@@ -8,6 +9,8 @@ import { QueryKeys } from 'types/client/queries';
import type { KeysOfObjectOrNull } from 'types/utils/KeysOfObjectOrNull'; import type { KeysOfObjectOrNull } from 'types/utils/KeysOfObjectOrNull';
export type PaginatedQueryKeys = export type PaginatedQueryKeys =
QueryKeys.addressTxs |
QueryKeys.addressTokenTransfers |
QueryKeys.blocks | QueryKeys.blocks |
QueryKeys.blocksReorgs | QueryKeys.blocksReorgs |
QueryKeys.blocksUncles | QueryKeys.blocksUncles |
...@@ -19,6 +22,8 @@ export type PaginatedQueryKeys = ...@@ -19,6 +22,8 @@ export type PaginatedQueryKeys =
QueryKeys.txTokenTransfers; QueryKeys.txTokenTransfers;
export type PaginatedResponse<Q extends PaginatedQueryKeys> = export type PaginatedResponse<Q extends PaginatedQueryKeys> =
Q extends QueryKeys.addressTxs ? AddressTransactionsResponse :
Q extends QueryKeys.addressTokenTransfers ? AddressTokenTransferResponse :
Q extends (QueryKeys.blocks | QueryKeys.blocksReorgs | QueryKeys.blocksUncles) ? BlocksResponse : Q extends (QueryKeys.blocks | QueryKeys.blocksReorgs | QueryKeys.blocksUncles) ? BlocksResponse :
Q extends QueryKeys.blockTxs ? BlockTransactionsResponse : Q extends QueryKeys.blockTxs ? BlockTransactionsResponse :
Q extends QueryKeys.txsValidate ? TransactionsResponseValidated : Q extends QueryKeys.txsValidate ? TransactionsResponseValidated :
...@@ -29,6 +34,8 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> = ...@@ -29,6 +34,8 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> =
never never
export type PaginationFilters<Q extends PaginatedQueryKeys> = export type PaginationFilters<Q extends PaginatedQueryKeys> =
Q extends QueryKeys.addressTxs ? AddressTxsFilters :
Q extends QueryKeys.addressTokenTransfers ? AddressTokenTransferFilters :
Q extends QueryKeys.blocks ? BlockFilters : Q extends QueryKeys.blocks ? BlockFilters :
Q extends QueryKeys.txsValidate ? TTxsFilters : Q extends QueryKeys.txsValidate ? TTxsFilters :
Q extends QueryKeys.txsPending ? TTxsFilters : Q extends QueryKeys.txsPending ? TTxsFilters :
...@@ -42,6 +49,8 @@ type PaginationFields = { ...@@ -42,6 +49,8 @@ type PaginationFields = {
} }
export const PAGINATION_FIELDS: PaginationFields = { export const PAGINATION_FIELDS: PaginationFields = {
[QueryKeys.addressTxs]: [ 'block_number', 'items_count', 'index' ],
[QueryKeys.addressTokenTransfers]: [ 'block_number', 'items_count', 'index', 'transaction_hash' ],
[QueryKeys.blocks]: [ 'block_number', 'items_count' ], [QueryKeys.blocks]: [ 'block_number', 'items_count' ],
[QueryKeys.blocksReorgs]: [ 'block_number', 'items_count' ], [QueryKeys.blocksReorgs]: [ 'block_number', 'items_count' ],
[QueryKeys.blocksUncles]: [ 'block_number', 'items_count' ], [QueryKeys.blocksUncles]: [ 'block_number', 'items_count' ],
...@@ -52,3 +61,21 @@ export const PAGINATION_FIELDS: PaginationFields = { ...@@ -52,3 +61,21 @@ export const PAGINATION_FIELDS: PaginationFields = {
[QueryKeys.txTokenTransfers]: [ 'block_number', 'items_count', 'transaction_hash', 'index' ], [QueryKeys.txTokenTransfers]: [ 'block_number', 'items_count', 'transaction_hash', 'index' ],
[QueryKeys.txLogs]: [ 'items_count', 'transaction_hash', 'index' ], [QueryKeys.txLogs]: [ 'items_count', 'transaction_hash', 'index' ],
}; };
type PaginationFiltersFields = {
[K in PaginatedQueryKeys]: Array<KeysOfObjectOrNull<PaginationFilters<K>>>
}
export const PAGINATION_FILTERS_FIELDS: PaginationFiltersFields = {
[QueryKeys.addressTxs]: [ 'filter' ],
[QueryKeys.addressTokenTransfers]: [ 'filter', 'type' ],
[QueryKeys.blocks]: [ 'type' ],
[QueryKeys.txsValidate]: [ 'filter', 'type', 'method' ],
[QueryKeys.txsPending]: [ 'filter', 'type', 'method' ],
[QueryKeys.txTokenTransfers]: [ 'type' ],
[QueryKeys.blocksReorgs]: [],
[QueryKeys.blocksUncles]: [],
[QueryKeys.blockTxs]: [],
[QueryKeys.txInternals]: [],
[QueryKeys.txLogs]: [],
};
...@@ -20,5 +20,12 @@ export type GasPrices = { ...@@ -20,5 +20,12 @@ export type GasPrices = {
} }
export type Stats = { export type Stats = {
total_blocks: string; totalBlocksAllTime: string;
}
export type Charts = {
'chart': Array<{
date: string;
value: string;
}>;
} }
...@@ -8,6 +8,7 @@ export interface TokenInfo { ...@@ -8,6 +8,7 @@ export interface TokenInfo {
decimals: string | null; decimals: string | null;
holders: string | null; holders: string | null;
exchange_rate: string | null; exchange_rate: string | null;
total_supply: string | null;
} }
export type TokenInfoGeneric<Type extends TokenType> = Omit<TokenInfo, 'type'> & { type: Type }; export type TokenInfoGeneric<Type extends TokenType> = Omit<TokenInfo, 'type'> & { type: Type };
...@@ -38,14 +38,16 @@ interface TokenTransferBase { ...@@ -38,14 +38,16 @@ interface TokenTransferBase {
to: AddressParam; to: AddressParam;
} }
export interface TokenTransferResponse { export type TokenTransferPagination = {
items: Array<TokenTransfer>;
next_page_params: {
block_number: number; block_number: number;
index: number; index: number;
items_count: number; items_count: number;
transaction_hash: string; transaction_hash: string;
} | null; }
export interface TokenTransferResponse {
items: Array<TokenTransfer>;
next_page_params: TokenTransferPagination | null;
} }
export interface TokenTransferFilters { export interface TokenTransferFilters {
......
import type { AddressParam } from './addressParams';
export type TransactionReward = {
types: Array<string>;
emission_reward: string;
block_hash: string;
from: AddressParam;
to: AddressParam;
}
...@@ -5,6 +5,7 @@ export enum QueryKeys { ...@@ -5,6 +5,7 @@ export enum QueryKeys {
txsPending = 'txs-pending', txsPending = 'txs-pending',
homeStats='homeStats', homeStats='homeStats',
stats='stats', stats='stats',
charts='stats',
tx = 'tx', tx = 'tx',
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLogs = 'tx-logs', txLogs = 'tx-logs',
...@@ -23,4 +24,6 @@ export enum QueryKeys { ...@@ -23,4 +24,6 @@ export enum QueryKeys {
address='address', address='address',
addressCounters='address-counters', addressCounters='address-counters',
addressTokenBalances='address-token-balances', addressTokenBalances='address-token-balances',
addressTxs='addressTxs',
addressTokenTransfers='addressTokenTransfers',
} }
...@@ -23,10 +23,4 @@ export type StatsChart = { ...@@ -23,10 +23,4 @@ export type StatsChart = {
id: string; id: string;
title: string; title: string;
description: string; description: string;
apiMethodURL: string;
}
export interface ModalChart {
id: string;
title: string;
} }
import { Box, Flex, Text, Icon, Button, Grid, Select } from '@chakra-ui/react'; import { Box, Flex, Text, Icon, Grid, Link } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
...@@ -9,18 +9,25 @@ import type { Address as TAddress, AddressCounters, AddressTokenBalance } from ' ...@@ -9,18 +9,25 @@ import type { Address as TAddress, AddressCounters, AddressTokenBalance } from '
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import qrCodeIcon from 'icons/qr_code.svg'; import blockIcon from 'icons/block.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import walletIcon from 'icons/wallet.svg';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import link from 'lib/link/link';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import ExternalLink from 'ui/shared/ExternalLink'; import ExternalLink from 'ui/shared/ExternalLink';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import AddressAddToMetaMask from './details/AddressAddToMetaMask'; import AddressAddToMetaMask from './details/AddressAddToMetaMask';
import AddressBalance from './details/AddressBalance';
import AddressDetailsSkeleton from './details/AddressDetailsSkeleton';
import AddressFavoriteButton from './details/AddressFavoriteButton';
import AddressNameInfo from './details/AddressNameInfo';
import AddressQrCode from './details/AddressQrCode';
import TokenSelect from './tokenSelect/TokenSelect';
interface Props { interface Props {
addressQuery: UseQueryResult<TAddress>; addressQuery: UseQueryResult<TAddress>;
...@@ -48,14 +55,15 @@ const AddressDetails = ({ addressQuery }: Props) => { ...@@ -48,14 +55,15 @@ const AddressDetails = ({ addressQuery }: Props) => {
); );
if (countersQuery.isLoading || addressQuery.isLoading || tokenBalancesQuery.isLoading) { if (countersQuery.isLoading || addressQuery.isLoading || tokenBalancesQuery.isLoading) {
return <Box>loading</Box>; return <AddressDetailsSkeleton/>;
} }
if (countersQuery.isError || addressQuery.isError || tokenBalancesQuery.isError) { if (countersQuery.isError || addressQuery.isError || tokenBalancesQuery.isError) {
return <Box>error</Box>; return <DataFetchAlert/>;
} }
const explorers = appConfig.network.explorers.filter(({ paths }) => paths.address); const explorers = appConfig.network.explorers.filter(({ paths }) => paths.address);
const validationsCount = Number(countersQuery.data.validations_count);
return ( return (
<Box> <Box>
...@@ -66,16 +74,12 @@ const AddressDetails = ({ addressQuery }: Props) => { ...@@ -66,16 +74,12 @@ const AddressDetails = ({ addressQuery }: Props) => {
</Text> </Text>
<CopyToClipboard text={ addressQuery.data.hash }/> <CopyToClipboard text={ addressQuery.data.hash }/>
{ addressQuery.data.is_contract && addressQuery.data.token && <AddressAddToMetaMask ml={ 2 } token={ addressQuery.data.token }/> } { addressQuery.data.is_contract && addressQuery.data.token && <AddressAddToMetaMask ml={ 2 } token={ addressQuery.data.token }/> }
<Button variant="outline" size="sm" ml={ 3 }> <AddressFavoriteButton hash={ addressQuery.data.hash } isAdded={ Boolean(addressQuery.data.watchlist_names?.length) } ml={ 3 }/>
<Icon as={ starOutlineIcon } boxSize={ 5 }/> <AddressQrCode hash={ addressQuery.data.hash } ml={ 2 }/>
</Button>
<Button variant="outline" size="sm" ml={ 2 }>
<Icon as={ qrCodeIcon } boxSize={ 5 }/>
</Button>
</Flex> </Flex>
{ explorers.length > 0 && ( { explorers.length > 0 && (
<Flex mt={ 8 } columnGap={ 4 } flexWrap="wrap"> <Flex mt={ 8 } columnGap={ 4 } flexWrap="wrap">
<Text>Verify with other explorers</Text> <Text fontSize="sm">Verify with other explorers</Text>
{ explorers.map((explorer) => { { explorers.map((explorer) => {
const url = new URL(explorer.paths.tx + '/' + router.query.id, explorer.baseUrl); const url = new URL(explorer.paths.tx + '/' + router.query.id, explorer.baseUrl);
return <ExternalLink key={ explorer.baseUrl } title={ explorer.title } href={ url.toString() }/>; return <ExternalLink key={ explorer.baseUrl } title={ explorer.title } href={ url.toString() }/>;
...@@ -85,46 +89,40 @@ const AddressDetails = ({ addressQuery }: Props) => { ...@@ -85,46 +89,40 @@ const AddressDetails = ({ addressQuery }: Props) => {
<Grid <Grid
mt={ 8 } mt={ 8 }
columnGap={ 8 } columnGap={ 8 }
rowGap={{ base: 3, lg: 3 }} rowGap={{ base: 1, lg: 3 }}
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden" templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
> >
<AddressNameInfo data={ addressQuery.data }/>
{ addressQuery.data.is_contract && addressQuery.data.creation_tx_hash && addressQuery.data.creator_address_hash && (
<DetailsInfoItem
title="Creator"
hint="Transaction and address of creation."
>
<AddressLink hash={ addressQuery.data.creator_address_hash } truncation="constant"/>
<Text whiteSpace="pre"> at </Text>
<AddressLink hash={ addressQuery.data.creation_tx_hash } truncation="constant"/>
</DetailsInfoItem>
) }
<AddressBalance data={ addressQuery.data }/>
<DetailsInfoItem <DetailsInfoItem
title="Tokens" title="Tokens"
hint="All tokens in the account and total value." hint="All tokens in the account and total value."
alignSelf="center" alignSelf="center"
py={ 0 }
> >
{ tokenBalancesQuery.data.length > 0 ? ( <TokenSelect/>
<>
{ /* TODO will be fixed later when we implement select with custom menu */ }
<Select
size="sm"
borderRadius="base"
focusBorderColor="none"
display="inline-block"
w="auto"
>
{ tokenBalancesQuery.data.map((token) =>
<option key={ token.token.address } value={ token.token.address }>{ token.token.symbol }</option>) }
</Select>
<Button variant="outline" size="sm" ml={ 3 }>
<Icon as={ walletIcon } boxSize={ 5 }/>
</Button>
</>
) : (
'-'
) }
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Transactions" title="Transactions"
hint="Number of transactions related to this address." hint="Number of transactions related to this address."
> >
{ Number(countersQuery.data.transaction_count).toLocaleString() } { Number(countersQuery.data.transactions_count).toLocaleString() }
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Transfers" title="Transfers"
hint="Number of transfers to/from this address." hint="Number of transfers to/from this address."
> >
{ Number(countersQuery.data.token_transfer_count).toLocaleString() } { Number(countersQuery.data.token_transfers_count).toLocaleString() }
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Gas used" title="Gas used"
...@@ -132,12 +130,29 @@ const AddressDetails = ({ addressQuery }: Props) => { ...@@ -132,12 +130,29 @@ const AddressDetails = ({ addressQuery }: Props) => {
> >
{ BigNumber(countersQuery.data.gas_usage_count).toFormat() } { BigNumber(countersQuery.data.gas_usage_count).toFormat() }
</DetailsInfoItem> </DetailsInfoItem>
{ countersQuery.data.validation_count && ( { !Object.is(validationsCount, NaN) && validationsCount > 0 && (
<DetailsInfoItem <DetailsInfoItem
title="Blocks validated" title="Blocks validated"
hint="Number of blocks validated by this validator." hint="Number of blocks validated by this validator."
> >
{ Number(countersQuery.data.validation_count).toLocaleString() } { validationsCount.toLocaleString() }
</DetailsInfoItem>
) }
{ addressQuery.data.block_number_balance_updated_at && (
<DetailsInfoItem
title="Last balance update"
hint="Block number in which the address was updated."
alignSelf="center"
py={{ base: '2px', lg: 1 }}
>
<Link
href={ link('block', { id: String(addressQuery.data.block_number_balance_updated_at) }) }
display="flex"
alignItems="center"
>
<Icon as={ blockIcon } boxSize={ 6 } mr={ 2 }/>
{ addressQuery.data.block_number_balance_updated_at }
</Link>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
</Grid> </Grid>
......
import { Box } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { base as txMock } from 'mocks/txs/tx';
import TestApp from 'playwright/TestApp';
import AddressTxs from './AddressTxs';
const API_URL = '/node-api/addresses/0xd789a607CEac2f0E14867de4EB15b15C9FFB5859/transactions';
const hooksConfig = {
router: {
query: { id: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' },
},
};
test('address txs +@mobile +@desktop-xl', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock, txMock ], next_page_params: { block: 1 } }),
}));
const component = await mount(
<TestApp>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig },
);
await page.waitForResponse(API_URL),
await expect(component).toHaveScreenshot();
});
import { useRouter } from 'next/router';
import React from 'react';
import { Element } from 'react-scroll';
import { QueryKeys } from 'types/client/queries';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/Pagination';
import TxsContent from 'ui/txs/TxsContent';
import AddressTxsFilter from './AddressTxsFilter';
const FILTER_VALUES = [ 'from', 'to' ] as const;
type FilterType = typeof FILTER_VALUES[number];
const getFilterValue = (val: string | Array<string> | undefined): FilterType | undefined => {
if (typeof val === 'string' && FILTER_VALUES.includes(val as FilterType)) {
return val as FilterType;
}
};
const SCROLL_ELEM = 'address-txs';
const SCROLL_OFFSET = -100;
const AddressTxs = () => {
const router = useRouter();
const isMobile = useIsMobile();
const [ filterValue, setFilterValue ] = React.useState<'from' | 'to' | undefined>(getFilterValue(router.query.filter));
const addressTxsQuery = useQueryWithPages({
apiPath: `/node-api/addresses/${ router.query.id }/transactions`,
queryName: QueryKeys.addressTxs,
filters: { filter: filterValue },
scroll: { elem: SCROLL_ELEM, offset: SCROLL_OFFSET },
});
const handleFilterChange = React.useCallback((val: string | Array<string>) => {
const newVal = getFilterValue(val);
setFilterValue(newVal);
addressTxsQuery.onFilterChange({ filter: newVal });
}, [ addressTxsQuery ]);
const isPaginatorHidden =
!addressTxsQuery.isLoading &&
!addressTxsQuery.isError &&
addressTxsQuery.pagination.page === 1 &&
!addressTxsQuery.pagination.hasNextPage;
const filter = (
<AddressTxsFilter
defaultFilter={ filterValue }
onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) }
/>
);
return (
<Element name={ SCROLL_ELEM }>
{ !isMobile && (
<ActionBar mt={ -6 }>
{ filter }
{ !isPaginatorHidden && <Pagination { ...addressTxsQuery.pagination }/> }
</ActionBar>
) }
<TxsContent
filter={ filter }
query={ addressTxsQuery }
showSocketInfo={ false }
currentAddress={ typeof router.query.id === 'string' ? router.query.id : undefined }
/>
</Element>
);
};
export default AddressTxs;
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuItemOption,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react';
import FilterButton from 'ui/shared/FilterButton';
interface Props {
isActive: boolean;
defaultFilter: 'from' | 'to' | undefined;
onFilterChange: (nextValue: string | Array<string>) => void;
}
const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive }: Props) => {
const { isOpen, onToggle } = useDisclosure();
return (
<Menu>
<MenuButton>
<FilterButton
isActive={ isOpen || isActive }
onClick={ onToggle }
/>
</MenuButton>
<MenuList zIndex={ 2 }>
<MenuOptionGroup defaultValue={ defaultFilter || 'all' } title="Address" type="radio" onChange={ onFilterChange }>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="from">From</MenuItemOption>
<MenuItemOption value="to">To</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
);
};
export default React.memo(AddressTxsFilter);
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Address } from 'types/api/address';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props {
data: Address;
}
const AddressBalance = ({ data }: Props) => {
const [ lastBlockNumber, setLastBlockNumber ] = React.useState<number>(data.block_number_balance_updated_at || 0);
const queryClient = useQueryClient();
const updateData = React.useCallback((balance: string, exchangeRate: string, blockNumber: number) => {
if (blockNumber < lastBlockNumber) {
return;
}
setLastBlockNumber(blockNumber);
queryClient.setQueryData([ QueryKeys.address, data.hash ], (prevData: Address | undefined) => {
if (!prevData) {
return;
}
return {
...prevData,
coin_balance: balance,
exchange_rate: exchangeRate,
block_number_balance_updated_at: blockNumber,
};
});
}, [ data.hash, lastBlockNumber, queryClient ]);
const handleNewBalanceMessage: SocketMessage.AddressBalance['handler'] = React.useCallback((payload) => {
updateData(payload.balance, payload.exchange_rate, payload.block_number);
}, [ updateData ]);
const handleNewCoinBalanceMessage: SocketMessage.AddressCurrentCoinBalance['handler'] = React.useCallback((payload) => {
updateData(payload.coin_balance, payload.exchange_rate, payload.block_number);
}, [ updateData ]);
const channel = useSocketChannel({
topic: `addresses:${ data.hash.toLowerCase() }`,
isDisabled: !data.coin_balance,
});
useSocketMessage({
channel,
event: 'balance',
handler: handleNewBalanceMessage,
});
useSocketMessage({
channel,
event: 'current_coin_balance',
handler: handleNewCoinBalanceMessage,
});
if (!data.coin_balance) {
return null;
}
return (
<DetailsInfoItem
title="Balance"
hint={ `Address balance in ${ appConfig.network.currency.symbol }. Doesn't include ERC20, ERC721 and ERC1155 tokens.` }
>
<TokenLogo
hash={ appConfig.network.currency.address }
name={ appConfig.network.currency.name }
boxSize={ 5 }
mr={ 2 }
fontSize="sm"
/>
<CurrencyValue
value={ data.coin_balance }
exchangeRate={ data.exchange_rate }
decimals={ String(appConfig.network.currency.decimals) }
currency={ appConfig.network.currency.symbol }
accuracyUsd={ 2 }
accuracy={ 8 }
/>
</DetailsInfoItem>
);
};
export default React.memo(AddressBalance);
import { Box, Flex, Grid, Skeleton, SkeletonCircle } from '@chakra-ui/react';
import React from 'react';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
const AddressDetailsSkeleton = () => {
return (
<Box>
<Flex align="center">
<SkeletonCircle boxSize={ 6 } flexShrink={ 0 }/>
<Skeleton h={ 6 } w={{ base: '100px', lg: '420px' }} ml={ 2 }/>
<Skeleton h={ 6 } w="24px" ml={ 2 } flexShrink={ 0 }/>
<Skeleton h={ 8 } w="48px" ml={ 3 } flexShrink={ 0 }/>
<Skeleton h={ 8 } w="48px" ml={ 3 } flexShrink={ 0 }/>
</Flex>
<Flex align="center" columnGap={ 4 } mt={ 8 }>
<Skeleton h={ 6 } w="200px"/>
<Skeleton h={ 6 } w="80px"/>
</Flex>
<Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '150px 1fr' }} maxW="1000px" mt={ 8 }>
<DetailsSkeletonRow w="30%"/>
<DetailsSkeletonRow w="30%"/>
<DetailsSkeletonRow w="10%"/>
<DetailsSkeletonRow w="10%"/>
<DetailsSkeletonRow w="20%"/>
<DetailsSkeletonRow w="20%"/>
</Grid>
</Box>
);
};
export default AddressDetailsSkeleton;
import { Icon, chakra, Tooltip, IconButton, useDisclosure } from '@chakra-ui/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
import type { UserInfo } from 'types/api/account';
import type { TWatchlist } from 'types/client/account';
import { QueryKeys as AccountQueryKeys } from 'types/client/accountQueries';
import { QueryKeys } from 'types/client/queries';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import useFetch from 'lib/hooks/useFetch';
import useLoginUrl from 'lib/hooks/useLoginUrl';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import WatchlistAddModal from 'ui/watchlist/AddressModal/AddressModal';
import DeleteAddressModal from 'ui/watchlist/DeleteAddressModal';
interface Props {
className?: string;
hash: string;
isAdded: boolean;
}
const AddressFavoriteButton = ({ className, hash, isAdded }: Props) => {
const addModalProps = useDisclosure();
const deleteModalProps = useDisclosure();
const queryClient = useQueryClient();
const router = useRouter();
const fetch = useFetch();
const profileData = queryClient.getQueryData<UserInfo>([ AccountQueryKeys.profile ]);
const isAuth = Boolean(profileData);
const loginUrl = useLoginUrl();
const watchListQuery = useQuery<unknown, unknown, TWatchlist>(
[ AccountQueryKeys.watchlist ],
async() => fetch('/node-api/account/watchlist'),
{
enabled: isAdded,
},
);
const handleClick = React.useCallback(() => {
if (!isAuth) {
window.location.assign(loginUrl);
return;
}
isAdded ? deleteModalProps.onOpen() : addModalProps.onOpen();
}, [ addModalProps, deleteModalProps, isAdded, isAuth, loginUrl ]);
const handleAddOrDeleteSuccess = React.useCallback(async() => {
await queryClient.refetchQueries({ queryKey: [ QueryKeys.address, router.query.id ] });
addModalProps.onClose();
}, [ addModalProps, queryClient, router.query.id ]);
const handleAddModalClose = React.useCallback(() => {
addModalProps.onClose();
}, [ addModalProps ]);
const handleDeleteModalClose = React.useCallback(() => {
deleteModalProps.onClose();
}, [ deleteModalProps ]);
const formData = React.useMemo(() => {
return {
address_hash: hash,
// FIXME temporary solution
// there is no endpoint in api what can return watchlist address info by its hash
// so we look up in the whole watchlist and hope we can find a necessary item
id: watchListQuery.data?.find((address) => address.address?.hash === hash)?.id || '',
};
}, [ hash, watchListQuery.data ]);
return (
<>
<Tooltip label={ `${ isAdded ? 'Remove address from Watch list' : 'Add address to Watch list' }` }>
<IconButton
isActive={ isAdded }
className={ className }
aria-label="edit"
variant="outline"
size="sm"
pl="6px"
pr="6px"
onClick={ handleClick }
icon={ <Icon as={ isAdded ? starFilledIcon : starOutlineIcon } boxSize={ 5 }/> }
onFocusCapture={ usePreventFocusAfterModalClosing() }
/>
</Tooltip>
<WatchlistAddModal
{ ...addModalProps }
isAdd
onClose={ handleAddModalClose }
onSuccess={ handleAddOrDeleteSuccess }
data={ formData }
/>
<DeleteAddressModal
{ ...deleteModalProps }
onClose={ handleDeleteModalClose }
data={ formData }
onSuccess={ handleAddOrDeleteSuccess }
/>
</>
);
};
export default chakra(AddressFavoriteButton);
import { Link } from '@chakra-ui/react';
import React from 'react';
import type { Address } from 'types/api/address';
import link from 'lib/link/link';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
interface Props {
data: Address;
}
const AddressNameInfo = ({ data }: Props) => {
if (data.token) {
return (
<DetailsInfoItem
title="Token name"
hint="Token name and symbol"
>
<Link href={ link('token_index', { hash: data.token.address }) }>
{ data.token.name } ({ data.token.symbol })
</Link>
</DetailsInfoItem>
);
}
if (data.is_contract && data.name) {
return (
<DetailsInfoItem
title="Contract name"
hint="The name found in the source code of the Contract"
>
{ data.name }
</DetailsInfoItem>
);
}
if (data.name) {
return (
<DetailsInfoItem
title="Validator name"
hint="The name of the validator"
>
{ data.name }
</DetailsInfoItem>
);
}
return null;
};
export default React.memo(AddressNameInfo);
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import AddressQrCode from './AddressQrCode';
test('default view +@mobile +@dark-mode', async({ mount, page }) => {
await mount(
<TestApp>
<AddressQrCode hash="0x363574E6C5C71c343d7348093D84320c76d5Dd29"/>
</TestApp>,
);
await page.getByRole('button', { name: /qr code/i }).click();
await expect(page).toHaveScreenshot();
});
import { chakra, Alert, Icon, Modal, ModalBody, ModalContent, ModalCloseButton, ModalOverlay, Box, useDisclosure, Tooltip, IconButton } from '@chakra-ui/react';
import * as Sentry from '@sentry/react';
import QRCode from 'qrcode';
import React from 'react';
import qrCodeIcon from 'icons/qr_code.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
const SVG_OPTIONS = {
margin: 0,
};
interface Props {
className?: string;
hash: string;
}
const AddressQrCode = ({ hash, className }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const isMobile = useIsMobile();
const [ qr, setQr ] = React.useState('');
const [ error, setError ] = React.useState('');
React.useEffect(() => {
if (isOpen) {
QRCode.toString(hash, SVG_OPTIONS, (error: Error | null | undefined, svg: string) => {
if (error) {
setError('We were unable to generate QR code.');
Sentry.captureException(error, { tags: { source: 'QR code' } });
return;
}
setError('');
setQr(svg);
});
}
}, [ hash, isOpen, onClose ]);
return (
<>
<Tooltip label="Click to view QR code">
<IconButton
className={ className }
aria-label="Show QR code"
variant="outline"
size="sm"
pl="6px"
pr="6px"
onClick={ onOpen }
icon={ <Icon as={ qrCodeIcon } boxSize={ 5 }/> }
/>
</Tooltip>
<Modal isOpen={ isOpen } onClose={ onClose } size={{ base: 'full', lg: 'sm' }}>
<ModalOverlay/>
<ModalContent bgColor={ error ? undefined : 'white' }>
{ isMobile && <ModalCloseButton/> }
<ModalBody mb={ 0 }>
{ error ? <Alert status="warning">{ error }</Alert> : <Box dangerouslySetInnerHTML={{ __html: qr }}/> }
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
export default React.memo(chakra(AddressQrCode));
import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
import { QueryKeys } from 'types/client/queries';
import useFetch from 'lib/hooks/useFetch';
const MockAddressPage = ({ children }: { children: JSX.Element }): JSX.Element => {
const router = useRouter();
const fetch = useFetch();
const { data } = useQuery(
[ QueryKeys.address, router.query.id ],
async() => await fetch(`/node-api/addresses/${ router.query.id }`),
{
enabled: Boolean(router.query.id),
},
);
if (!data) {
return <div/>;
}
return children;
};
export default MockAddressPage;
import { Flex } from '@chakra-ui/react';
import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react';
import * as tokenBalanceMock from 'mocks/address/tokenBalance';
import * as socketServer from 'playwright/fixtures/socketServer';
import TestApp from 'playwright/TestApp';
import MockAddressPage from 'ui/address/testUtils/MockAddressPage';
import TokenSelect from './TokenSelect';
const ASSET_URL = 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/poa/assets/0xb2a90505dc6680a7a695f7975d0d32EeF610f456/logo.png';
const TOKENS_API_URL = '/node-api/addresses/1/token-balances';
const ADDRESS_API_URL = '/node-api/addresses/1';
const hooksConfig = {
router: {
query: { id: '1' },
},
};
const CLIPPING_AREA = { x: 0, y: 0, width: 360, height: 500 };
const test = base.extend({
page: async({ page }, use) => {
await page.route(ASSET_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
await page.route(ADDRESS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ hash: '1' }),
}), { times: 1 });
await page.route(TOKENS_API_URL, async(route) => route.fulfill({
status: 200,
body: JSON.stringify(tokenBalanceMock.baseList),
}), { times: 1 });
use(page);
},
});
test('base view +@dark-mode', async({ mount, page }) => {
await mount(
<TestApp>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.getByRole('button', { name: /select/i }).click();
await page.getByText('USD Coin').hover();
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
await page.mouse.move(100, 200);
await page.mouse.wheel(0, 1000);
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
});
test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', async({ mount, page }) => {
await mount(
<TestApp>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.getByRole('button', { name: /select/i }).click();
await page.getByText('USD Coin').hover();
await expect(page).toHaveScreenshot();
});
});
test('sort', async({ mount, page }) => {
await mount(
<TestApp>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.getByRole('button', { name: /select/i }).click();
await page.locator('a[aria-label="Sort ERC-20 tokens"]').click();
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
await page.mouse.move(100, 200);
await page.mouse.wheel(0, 1000);
await page.locator('a[aria-label="Sort ERC-1155 tokens"]').click();
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
});
test('filter', async({ mount, page }) => {
await mount(
<TestApp>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.getByRole('button', { name: /select/i }).click();
await page.getByPlaceholder('Search by token name').type('c');
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
});
test.describe('socket', () => {
const testWithSocket = test.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket,
});
testWithSocket.describe.configure({ mode: 'serial' });
testWithSocket('new item after token balance update', async({ page, mount, createSocket }) => {
await mount(
<TestApp withSocket>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.route(TOKENS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify([
...tokenBalanceMock.baseList,
tokenBalanceMock.erc20d,
]),
}));
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:1');
socketServer.sendMessage(socket, channel, 'token_balance', {
block_number: 1,
});
const button = page.getByRole('button', { name: /select/i });
const text = await button.innerText();
expect(text).toContain('10');
});
testWithSocket('new item after coin balance update', async({ page, mount, createSocket }) => {
await mount(
<TestApp withSocket>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.route(TOKENS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify([
...tokenBalanceMock.baseList,
tokenBalanceMock.erc20d,
]),
}));
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'addresses:1');
socketServer.sendMessage(socket, channel, 'coin_balance', {
coin_balance: {
block_number: 1,
},
});
const button = page.getByRole('button', { name: /select/i });
const text = await button.innerText();
expect(text).toContain('10');
});
});
import { Box, Flex, Icon, IconButton, Skeleton, Tooltip } from '@chakra-ui/react';
import { useQuery, useQueryClient, useIsFetching } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Address, AddressTokenBalance } from 'types/api/address';
import { QueryKeys } from 'types/client/queries';
import walletIcon from 'icons/wallet.svg';
import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import TokenSelectDesktop from './TokenSelectDesktop';
import TokenSelectMobile from './TokenSelectMobile';
const TokenSelect = () => {
const router = useRouter();
const fetch = useFetch();
const isMobile = useIsMobile();
const queryClient = useQueryClient();
const [ blockNumber, setBlockNumber ] = React.useState<number>();
const addressQueryData = queryClient.getQueryData<Address>([ QueryKeys.address, router.query.id ]);
const { data, isError, isLoading, refetch } = useQuery<unknown, unknown, Array<AddressTokenBalance>>(
[ QueryKeys.addressTokenBalances, addressQueryData?.hash ],
async() => await fetch(`/node-api/addresses/${ addressQueryData?.hash }/token-balances`),
{
enabled: Boolean(addressQueryData),
},
);
const balancesIsFetching = useIsFetching({ queryKey: [ QueryKeys.addressTokenBalances, addressQueryData?.hash ] });
const handleTokenBalanceMessage: SocketMessage.AddressTokenBalance['handler'] = React.useCallback((payload) => {
if (payload.block_number !== blockNumber) {
refetch();
setBlockNumber(payload.block_number);
}
}, [ blockNumber, refetch ]);
const handleCoinBalanceMessage: SocketMessage.AddressCoinBalance['handler'] = React.useCallback((payload) => {
if (payload.coin_balance.block_number !== blockNumber) {
refetch();
setBlockNumber(payload.coin_balance.block_number);
}
}, [ blockNumber, refetch ]);
const channel = useSocketChannel({
topic: `addresses:${ addressQueryData?.hash.toLowerCase() }`,
isDisabled: !addressQueryData,
});
useSocketMessage({
channel,
event: 'coin_balance',
handler: handleCoinBalanceMessage,
});
useSocketMessage({
channel,
event: 'token_balance',
handler: handleTokenBalanceMessage,
});
if (isLoading) {
return <Skeleton h={ 8 } w="160px"/>;
}
if (isError || data.length === 0) {
return <Box py="6px">0</Box>;
}
return (
<Flex columnGap={ 3 } mt={{ base: '6px', lg: 0 }}>
{ isMobile ?
<TokenSelectMobile data={ data } isLoading={ balancesIsFetching === 1 }/> :
<TokenSelectDesktop data={ data } isLoading={ balancesIsFetching === 1 }/>
}
<Tooltip label="Show all tokens">
<IconButton
aria-label="Show all tokens"
variant="outline"
size="sm"
pl="6px"
pr="6px"
icon={ <Icon as={ walletIcon } boxSize={ 5 }/> }
/>
</Tooltip>
</Flex>
);
};
export default React.memo(TokenSelect);
import { Box, Button, Icon, Skeleton, Text, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import arrowIcon from 'icons/arrows/east-mini.svg';
import tokensIcon from 'icons/tokens.svg';
import { ZERO } from 'lib/consts';
import type { EnhancedData } from './utils';
interface Props {
isOpen: boolean;
isLoading: boolean;
onClick: () => void;
data: Array<EnhancedData>;
}
const TokenSelectButton = ({ isOpen, isLoading, onClick, data }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const totalBn = data.reduce((result, item) => !item.usd ? result : result.plus(BigNumber(item.usd)), ZERO);
const skeletonBgColor = useColorModeValue('white', 'black');
const handleClick = React.useCallback(() => {
if (isLoading && !isOpen) {
return;
}
onClick();
}, [ isLoading, isOpen, onClick ]);
return (
<Box position="relative">
<Button
ref={ ref }
size="sm"
variant="outline"
colorScheme="gray"
onClick={ handleClick }
aria-label="Token select"
>
<Icon as={ tokensIcon } boxSize={ 4 } mr={ 2 }/>
<Text fontWeight={ 600 }>{ data.length }</Text>
<Text whiteSpace="pre" variant="secondary" fontWeight={ 400 }> (${ totalBn.toFormat(2) })</Text>
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 3 }/>
</Button>
{ isLoading && !isOpen && <Skeleton h="100%" w="100%" position="absolute" top={ 0 } left={ 0 } bgColor={ skeletonBgColor }/> }
</Box>
);
};
export default React.forwardRef(TokenSelectButton);
import { useColorModeValue, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import TokenSelectButton from './TokenSelectButton';
import TokenSelectMenu from './TokenSelectMenu';
import useTokenSelect from './useTokenSelect';
interface Props {
data: Array<AddressTokenBalance>;
isLoading: boolean;
}
const TokenSelectDesktop = ({ data, isLoading }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const bgColor = useColorModeValue('white', 'gray.900');
const result = useTokenSelect(data);
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<TokenSelectButton isOpen={ isOpen } onClick={ onToggle } data={ result.modifiedData } isLoading={ isLoading }/>
</PopoverTrigger>
<PopoverContent w="355px" maxH="450px" overflowY="scroll">
<PopoverBody px={ 4 } py={ 6 } bgColor={ bgColor } boxShadow="2xl" >
<TokenSelectMenu { ...result }/>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default React.memo(TokenSelectDesktop);
import { Flex, Text, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import link from 'lib/link/link';
import HashStringShorten from 'ui/shared/HashStringShorten';
import TokenLogo from 'ui/shared/TokenLogo';
import type { EnhancedData } from './utils';
interface Props {
data: EnhancedData;
}
const TokenSelectItem = ({ data }: Props) => {
const secondRow = (() => {
switch (data.token.type) {
case 'ERC-20': {
const tokenDecimals = Number(data.token.decimals) || 18;
return (
<>
<Text >{ BigNumber(data.value).dividedBy(10 ** tokenDecimals).toFormat(2) } { data.token.symbol }</Text>
{ data.token.exchange_rate && <Text >@{ data.token.exchange_rate }</Text> }
</>
);
}
case 'ERC-721': {
return <Text >{ BigNumber(data.value).toFormat() } { data.token.symbol }</Text>;
}
case 'ERC-1155': {
return (
<>
<Text >#{ data.token_id || 0 }</Text>
<Text >{ BigNumber(data.value).toFormat() }</Text>
</>
);
}
}
})();
// TODO add filter param when token page is ready
const url = link('token_index', { hash: data.token.address });
return (
<Flex
px={ 1 }
py="10px"
display="flex"
flexDir="column"
rowGap={ 2 }
borderColor={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') }
borderBottomWidth="1px"
_hover={{
bgColor: useColorModeValue('blue.50', 'gray.800'),
}}
fontSize="sm"
cursor="pointer"
as="a"
href={ url }
>
<Flex alignItems="center" w="100%">
<TokenLogo hash={ data.token.address } name={ data.token.name } boxSize={ 6 }/>
<Text fontWeight={ 700 } ml={ 2 }>{ data.token.name || <HashStringShorten hash={ data.token.address }/> }</Text>
{ data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> }
</Flex>
<Flex alignItems="center" justifyContent="space-between" w="100%">
{ secondRow }
</Flex>
</Flex>
);
};
export default React.memo(TokenSelectItem);
import { Icon, Text, Box, Input, InputGroup, InputLeftElement, useColorModeValue, Flex, Link } from '@chakra-ui/react';
import type { Dictionary } from 'lodash';
import type { ChangeEvent } from 'react';
import React from 'react';
import type { TokenType } from 'types/api/tokenInfo';
import arrowIcon from 'icons/arrows/east.svg';
import searchIcon from 'icons/search.svg';
import TokenSelectItem from './TokenSelectItem';
import type { Sort, EnhancedData } from './utils';
import { sortTokenGroups, sortingFns } from './utils';
interface Props {
searchTerm: string;
erc20sort: Sort;
erc1155sort: Sort;
modifiedData: Array<EnhancedData>;
groupedData: Dictionary<Array<EnhancedData>>;
onInputChange: (event: ChangeEvent<HTMLInputElement>) => void;
onSortClick: (event: React.SyntheticEvent) => void;
}
const TokenSelectMenu = ({ erc20sort, erc1155sort, modifiedData, groupedData, onInputChange, onSortClick, searchTerm }: Props) => {
const searchIconColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
const inputBorderColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.200');
return (
<>
<InputGroup size="xs" mb={ 5 }>
<InputLeftElement >
<Icon as={ searchIcon } boxSize={ 4 } color={ searchIconColor }/>
</InputLeftElement>
<Input
paddingInlineStart="38px"
placeholder="Search by token name"
ml="1px"
onChange={ onInputChange }
borderColor={ inputBorderColor }
/>
</InputGroup>
<Flex flexDir="column" rowGap={ 6 }>
{ Object.entries(groupedData).sort(sortTokenGroups).map(([ tokenType, tokenInfo ]) => {
const type = tokenType as TokenType;
const arrowTransform = (type === 'ERC-1155' && erc1155sort === 'desc') || (type === 'ERC-20' && erc20sort === 'desc') ?
'rotate(90deg)' :
'rotate(-90deg)';
const sortDirection: Sort = (() => {
switch (type) {
case 'ERC-1155':
return erc1155sort;
case 'ERC-20':
return erc20sort;
default:
return 'desc';
}
})();
const hasSort = type === 'ERC-1155' || (type === 'ERC-20' && tokenInfo.some(({ usd }) => usd));
return (
<Box key={ type }>
<Flex justifyContent="space-between">
<Text mb={ 3 } color="gray.500" fontWeight={ 600 } fontSize="sm">{ type } tokens ({ tokenInfo.length })</Text>
{ hasSort && (
<Link data-type={ type } onClick={ onSortClick } aria-label={ `Sort ${ type } tokens` }>
<Icon as={ arrowIcon } boxSize={ 5 } transform={ arrowTransform } transitionDuration="faster"/>
</Link>
) }
</Flex>
{ tokenInfo.sort(sortingFns[type](sortDirection)).map((data) => <TokenSelectItem key={ data.token.address + data.token_id } data={ data }/>) }
</Box>
);
}) }
</Flex>
{ modifiedData.length === 0 && searchTerm && <Text fontSize="sm">Could not find any matches.</Text> }
</>
);
};
export default React.memo(TokenSelectMenu);
import { useDisclosure, Modal, ModalContent, ModalCloseButton } from '@chakra-ui/react';
import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import TokenSelectButton from './TokenSelectButton';
import TokenSelectMenu from './TokenSelectMenu';
import useTokenSelect from './useTokenSelect';
interface Props {
data: Array<AddressTokenBalance>;
isLoading: boolean;
}
const TokenSelectMobile = ({ data, isLoading }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const result = useTokenSelect(data);
return (
<>
<TokenSelectButton isOpen={ isOpen } onClick={ onToggle } data={ result.modifiedData } isLoading={ isLoading }/>
<Modal isOpen={ isOpen } onClose={ onClose } size="full">
<ModalContent>
<ModalCloseButton/>
<TokenSelectMenu { ...result }/>
</ModalContent>
</Modal>
</>
);
};
export default React.memo(TokenSelectMobile);
import _groupBy from 'lodash/groupBy';
import type { ChangeEvent } from 'react';
import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import type { Sort } from './utils';
import { calculateUsdValue, filterTokens } from './utils';
export default function useData(data: Array<AddressTokenBalance>) {
const [ searchTerm, setSearchTerm ] = React.useState('');
const [ erc1155sort, setErc1155Sort ] = React.useState<Sort>('desc');
const [ erc20sort, setErc20Sort ] = React.useState<Sort>('desc');
const onInputChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
}, []);
const onSortClick = React.useCallback((event: React.SyntheticEvent) => {
const tokenType = (event.currentTarget as HTMLAnchorElement).getAttribute('data-type');
if (tokenType === 'ERC-1155') {
setErc1155Sort((prevValue) => prevValue === 'desc' ? 'asc' : 'desc');
}
if (tokenType === 'ERC-20') {
setErc20Sort((prevValue) => prevValue === 'desc' ? 'asc' : 'desc');
}
}, []);
const modifiedData = React.useMemo(() => {
return data.filter(filterTokens(searchTerm.toLowerCase())).map(calculateUsdValue);
}, [ data, searchTerm ]);
const groupedData = React.useMemo(() => {
return _groupBy(modifiedData, 'token.type');
}, [ modifiedData ]);
return {
searchTerm,
erc20sort,
erc1155sort,
onInputChange,
onSortClick,
modifiedData,
groupedData,
};
}
import BigNumber from 'bignumber.js';
import type { AddressTokenBalance } from 'types/api/address';
import type { TokenType } from 'types/api/tokenInfo';
export type EnhancedData = AddressTokenBalance & {
usd?: BigNumber ;
}
export type Sort = 'desc' | 'asc';
const TOKEN_GROUPS_ORDER: Array<TokenType> = [ 'ERC-20', 'ERC-721', 'ERC-1155' ];
type TokenGroup = [string, Array<AddressTokenBalance>];
export const sortTokenGroups = (groupA: TokenGroup, groupB: TokenGroup) => {
return TOKEN_GROUPS_ORDER.indexOf(groupA[0] as TokenType) > TOKEN_GROUPS_ORDER.indexOf(groupB[0] as TokenType) ? 1 : -1;
};
const sortErc1155Tokens = (sort: Sort) => (dataA: AddressTokenBalance, dataB: AddressTokenBalance) => {
if (dataA.value === dataB.value) {
return 0;
}
if (sort === 'desc') {
return Number(dataA.value) > Number(dataB.value) ? -1 : 1;
}
return Number(dataA.value) > Number(dataB.value) ? 1 : -1;
};
const sortErc20Tokens = (sort: Sort) => (dataA: EnhancedData, dataB: EnhancedData) => {
if (!dataA.usd && !dataB.usd) {
return 0;
}
// keep tokens without usd value in the end of the group
if (!dataB.usd) {
return -1;
}
if (!dataA.usd) {
return 0;
}
if (sort === 'desc') {
return dataA.usd.gt(dataB.usd) ? -1 : 1;
}
return dataA.usd.gt(dataB.usd) ? 1 : -1;
};
const sortErc721Tokens = () => () => 0;
export const sortingFns = {
'ERC-20': sortErc20Tokens,
'ERC-721': sortErc721Tokens,
'ERC-1155': sortErc1155Tokens,
};
export const filterTokens = (searchTerm: string) => ({ token }: AddressTokenBalance) => {
if (!token.name) {
return !searchTerm ? true : token.address.toLowerCase().includes(searchTerm);
}
return token.name?.toLowerCase().includes(searchTerm);
};
export const calculateUsdValue = (data: AddressTokenBalance): EnhancedData => {
if (data.token.type !== 'ERC-20') {
return data;
}
const exchangeRate = data.token.exchange_rate;
if (!exchangeRate) {
return data;
}
const decimals = Number(data.token.decimals || '18');
return {
...data,
usd: BigNumber(data.value).div(BigNumber(10 ** decimals)).multipliedBy(BigNumber(exchangeRate)),
};
};
...@@ -112,9 +112,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -112,9 +112,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH } maxLength={ NAME_MAX_LENGTH }
/> />
<FormLabel>
<InputPlaceholder text="Application name for API key (e.g Web3 project)" error={ errors.name }/> <InputPlaceholder text="Application name for API key (e.g Web3 project)" error={ errors.name }/>
</FormLabel>
</FormControl> </FormControl>
); );
}, [ errors, formBackgroundColor ]); }, [ errors, formBackgroundColor ]);
......
import { Grid, GridItem, Skeleton, SkeletonCircle } from '@chakra-ui/react'; import { Grid, GridItem, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
const SkeletonRow = ({ w = '100%' }: { w?: string }) => ( import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
<>
<GridItem display="flex" columnGap={ 2 } w={{ base: '50%', lg: 'auto' }} _notFirst={{ mt: { base: 3, lg: 0 } }}>
<SkeletonCircle h={ 5 } w={ 5 }/>
<Skeleton flexGrow={ 1 } h={ 5 } borderRadius="full"/>
</GridItem>
<GridItem pl={{ base: 7, lg: 0 }}>
<Skeleton h={ 5 } borderRadius="full" w={{ base: '100%', lg: w }}/>
</GridItem>
</>
);
const BlockDetailsSkeleton = () => { const BlockDetailsSkeleton = () => {
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>; const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
return ( return (
<Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px"> <Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
<SkeletonRow w="25%"/> <DetailsSkeletonRow w="25%"/>
<SkeletonRow w="25%"/> <DetailsSkeletonRow w="25%"/>
<SkeletonRow w="65%"/> <DetailsSkeletonRow w="65%"/>
<SkeletonRow w="25%"/> <DetailsSkeletonRow w="25%"/>
<SkeletonRow/> <DetailsSkeletonRow/>
<SkeletonRow/> <DetailsSkeletonRow/>
{ sectionGap } { sectionGap }
<SkeletonRow w="50%"/> <DetailsSkeletonRow w="50%"/>
<SkeletonRow w="25%"/> <DetailsSkeletonRow w="25%"/>
<SkeletonRow w="50%"/> <DetailsSkeletonRow w="50%"/>
<SkeletonRow w="50%"/> <DetailsSkeletonRow w="50%"/>
<SkeletonRow w="50%"/> <DetailsSkeletonRow w="50%"/>
{ sectionGap } { sectionGap }
<GridItem colSpan={{ base: undefined, lg: 2 }}> <GridItem colSpan={{ base: undefined, lg: 2 }}>
<Skeleton h={ 5 } borderRadius="full" w="100px"/> <Skeleton h={ 5 } borderRadius="full" w="100px"/>
......
...@@ -82,10 +82,10 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -82,10 +82,10 @@ const BlocksContent = ({ type, query }: Props) => {
if (query.isLoading) { if (query.isLoading) {
return ( return (
<> <>
<Show below="lg" key="skeleton-mobile"> <Show below="lg" key="skeleton-mobile" ssr={ false }>
<BlocksSkeletonMobile/> <BlocksSkeletonMobile/>
</Show> </Show>
<Hide below="lg" key="skeleton-desktop"> <Hide below="lg" key="skeleton-desktop" ssr={ false }>
<SkeletonTable columns={ [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }/> <SkeletonTable columns={ [ '125px', '120px', '21%', '64px', '35%', '22%', '22%' ] }/>
</Hide> </Hide>
</> </>
...@@ -103,8 +103,8 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -103,8 +103,8 @@ const BlocksContent = ({ type, query }: Props) => {
return ( return (
<> <>
{ socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> } { socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> }
<Show below="lg" key="content-mobile"><BlocksList data={ query.data.items }/></Show> <Show below="lg" key="content-mobile" ssr={ false }><BlocksList data={ query.data.items }/></Show>
<Hide below="lg" key="content-desktop"><BlocksTable data={ query.data.items } top={ 80 } page={ 1 }/></Hide> <Hide below="lg" key="content-desktop" ssr={ false }><BlocksTable data={ query.data.items } top={ 80 } page={ 1 }/></Hide>
</> </>
); );
...@@ -115,7 +115,7 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -115,7 +115,7 @@ const BlocksContent = ({ type, query }: Props) => {
return ( return (
<> <>
{ isMobile && !isPaginatorHidden && ( { isMobile && !isPaginatorHidden && (
<ActionBar> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...query.pagination }/> <Pagination ml="auto" { ...query.pagination }/>
</ActionBar> </ActionBar>
) } ) }
......
...@@ -21,7 +21,7 @@ const BlocksTabSlot = ({ pagination }: Props) => { ...@@ -21,7 +21,7 @@ const BlocksTabSlot = ({ pagination }: Props) => {
const statsQuery = useQuery<unknown, unknown, HomeStats>( const statsQuery = useQuery<unknown, unknown, HomeStats>(
[ QueryKeys.homeStats ], [ QueryKeys.homeStats ],
() => fetch('/node-api/stats'), () => fetch('/node-api/home-stats'),
); );
if (isMobile) { if (isMobile) {
......
...@@ -8,7 +8,7 @@ import TestApp from 'playwright/TestApp'; ...@@ -8,7 +8,7 @@ import TestApp from 'playwright/TestApp';
import LatestBlocks from './LatestBlocks'; import LatestBlocks from './LatestBlocks';
const STATS_API_URL = '/node-api/stats'; const STATS_API_URL = '/node-api/home-stats';
const BLOCKS_API_URL = '/node-api/index/blocks'; const BLOCKS_API_URL = '/node-api/index/blocks';
export const test = base.extend<socketServer.SocketServerFixture>({ export const test = base.extend<socketServer.SocketServerFixture>({
......
...@@ -33,7 +33,7 @@ const LatestBlocks = () => { ...@@ -33,7 +33,7 @@ const LatestBlocks = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const statsQueryResult = useQuery<unknown, unknown, HomeStats>( const statsQueryResult = useQuery<unknown, unknown, HomeStats>(
[ QueryKeys.homeStats ], [ QueryKeys.homeStats ],
() => fetch('/node-api/stats'), () => fetch('/node-api/home-stats'),
); );
const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => { const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => {
......
...@@ -14,7 +14,7 @@ export const test = base.extend<socketServer.SocketServerFixture>({ ...@@ -14,7 +14,7 @@ export const test = base.extend<socketServer.SocketServerFixture>({
}); });
test('default view +@mobile +@dark-mode', async({ mount, page }) => { test('default view +@mobile +@dark-mode', async({ mount, page }) => {
await page.route('/node-api/stats', (route) => route.fulfill({ await page.route('/node-api/home-stats', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(statsMock.base), body: JSON.stringify(statsMock.base),
})); }));
...@@ -47,7 +47,7 @@ test.describe('socket', () => { ...@@ -47,7 +47,7 @@ test.describe('socket', () => {
}; };
test('new item', async({ mount, page, createSocket }) => { test('new item', async({ mount, page, createSocket }) => {
await page.route('/node-api/stats', (route) => route.fulfill({ await page.route('/node-api/home-stats', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(statsMock.base), body: JSON.stringify(statsMock.base),
})); }));
......
...@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp'; ...@@ -7,7 +7,7 @@ import TestApp from 'playwright/TestApp';
import Stats from './Stats'; import Stats from './Stats';
const API_URL = '/node-api/stats'; const API_URL = '/node-api/home-stats';
test('all items +@mobile +@dark-mode +@desktop-xl', async({ mount, page }) => { test('all items +@mobile +@dark-mode +@desktop-xl', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
......
...@@ -29,7 +29,7 @@ const Stats = () => { ...@@ -29,7 +29,7 @@ const Stats = () => {
const { data, isLoading, isError } = useQuery<unknown, unknown, HomeStats>( const { data, isLoading, isError } = useQuery<unknown, unknown, HomeStats>(
[ QueryKeys.homeStats ], [ QueryKeys.homeStats ],
async() => await fetch(`/node-api/stats`), async() => await fetch(`/node-api/home-stats`),
); );
if (isError) { if (isError) {
......
...@@ -7,8 +7,8 @@ import TestApp from 'playwright/TestApp'; ...@@ -7,8 +7,8 @@ import TestApp from 'playwright/TestApp';
import ChainIndicators from './ChainIndicators'; import ChainIndicators from './ChainIndicators';
const STATS_API_URL = '/node-api/stats'; const STATS_API_URL = '/node-api/home-stats';
const TX_CHART_API_URL = '/node-api/stats/charts/transactions'; const TX_CHART_API_URL = '/node-api/home-stats/charts/transactions';
test('daily txs chart +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) => { test('daily txs chart +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({ await page.route(STATS_API_URL, (route) => route.fulfill({
......
...@@ -37,7 +37,7 @@ const ChainIndicators = () => { ...@@ -37,7 +37,7 @@ const ChainIndicators = () => {
const fetch = useFetch(); const fetch = useFetch();
const statsQueryResult = useQuery<unknown, unknown, HomeStats>( const statsQueryResult = useQuery<unknown, unknown, HomeStats>(
[ QueryKeys.homeStats ], [ QueryKeys.homeStats ],
() => fetch('/node-api/stats'), () => fetch('/node-api/home-stats'),
); );
const bgColorDesktop = useColorModeValue('white', 'gray.900'); const bgColorDesktop = useColorModeValue('white', 'gray.900');
......
...@@ -19,7 +19,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = { ...@@ -19,7 +19,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = {
hint: `The total daily number of transactions on the blockchain for the last month.`, hint: `The total daily number of transactions on the blockchain for the last month.`,
api: { api: {
queryName: QueryKeys.chartsTxs, queryName: QueryKeys.chartsTxs,
path: '/node-api/stats/charts/transactions', path: '/node-api/home-stats/charts/transactions',
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: item.tx_count })) .map((item) => ({ date: new Date(item.date), value: item.tx_count }))
...@@ -38,7 +38,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -38,7 +38,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
hint: `${ appConfig.network.currency.symbol } token daily price in USD.`, hint: `${ appConfig.network.currency.symbol } token daily price in USD.`,
api: { api: {
queryName: QueryKeys.chartsMarket, queryName: QueryKeys.chartsMarket,
path: '/node-api/stats/charts/market', path: '/node-api/home-stats/charts/market',
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) })) .map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) }))
...@@ -58,7 +58,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -58,7 +58,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.', hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.',
api: { api: {
queryName: QueryKeys.chartsMarket, queryName: QueryKeys.chartsMarket,
path: '/node-api/stats/charts/market', path: '/node-api/home-stats/charts/market',
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) * Number(response.available_supply) })) .map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) * Number(response.available_supply) }))
......
...@@ -9,6 +9,7 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; ...@@ -9,6 +9,7 @@ import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import AddressDetails from 'ui/address/AddressDetails'; import AddressDetails from 'ui/address/AddressDetails';
import AddressTxs from 'ui/address/AddressTxs';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
...@@ -32,7 +33,7 @@ const AddressPageContent = () => { ...@@ -32,7 +33,7 @@ const AddressPageContent = () => {
].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>); ].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>);
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
{ id: 'txs', title: 'Transactions', component: null }, { id: 'txs', title: 'Transactions', component: <AddressTxs/> },
{ id: 'token_transfers', title: 'Token transfers', component: null }, { id: 'token_transfers', title: 'Token transfers', component: null },
{ id: 'tokens', title: 'Tokens', component: null }, { id: 'tokens', title: 'Tokens', component: null },
{ id: 'internal_txn', title: 'Internal txn', component: null }, { id: 'internal_txn', title: 'Internal txn', component: null },
......
...@@ -9,7 +9,7 @@ import TestApp from 'playwright/TestApp'; ...@@ -9,7 +9,7 @@ import TestApp from 'playwright/TestApp';
import Blocks from './Blocks'; import Blocks from './Blocks';
const BLOCKS_API_URL = '/node-api/blocks?type=block'; const BLOCKS_API_URL = '/node-api/blocks?type=block';
const STATS_API_URL = '/node-api/stats'; const STATS_API_URL = '/node-api/home-stats';
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { tab: 'blocks' }, query: { tab: 'blocks' },
...@@ -17,7 +17,7 @@ const hooksConfig = { ...@@ -17,7 +17,7 @@ const hooksConfig = {
}, },
}; };
export const test = base.extend<socketServer.SocketServerFixture>({ const test = base.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket, createSocket: socketServer.createSocket,
}); });
......
...@@ -11,7 +11,7 @@ import TestApp from 'playwright/TestApp'; ...@@ -11,7 +11,7 @@ import TestApp from 'playwright/TestApp';
import Home from './Home'; import Home from './Home';
test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount, page }) => { test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount, page }) => {
await page.route('/node-api/stats', (route) => route.fulfill({ await page.route('/node-api/home-stats', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(statsMock.base), body: JSON.stringify(statsMock.base),
})); }));
...@@ -30,7 +30,7 @@ test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount, ...@@ -30,7 +30,7 @@ test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount,
txMock.withTokenTransfer, txMock.withTokenTransfer,
]), ]),
})); }));
await page.route('/node-api/stats/charts/transactions', (route) => route.fulfill({ await page.route('/node-api/home-stats/charts/transactions', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(dailyTxsMock.base), body: JSON.stringify(dailyTxsMock.base),
})); }));
......
...@@ -18,9 +18,6 @@ const Stats = () => { ...@@ -18,9 +18,6 @@ const Stats = () => {
handleIntervalChange, handleIntervalChange,
debounceFilterCharts, debounceFilterCharts,
displayedCharts, displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
} = useStats(); } = useStats();
return ( return (
...@@ -43,9 +40,7 @@ const Stats = () => { ...@@ -43,9 +40,7 @@ const Stats = () => {
<ChartsWidgetsList <ChartsWidgetsList
charts={ displayedCharts } charts={ displayedCharts }
onChartFullscreenClick={ showChartFullscreen } interval={ interval }
fullscreenChart={ fullscreenChart }
onModalClose={ clearFullscreenChart }
/> />
</Page> </Page>
); );
......
import { Box, Button, Skeleton, useDisclosure } from '@chakra-ui/react'; import { Box, Button, Skeleton, useDisclosure } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { TWatchlist, TWatchlistItem } from 'types/client/account'; import type { TWatchlist, TWatchlistItem } from 'types/client/account';
...@@ -22,6 +22,7 @@ import WatchlistTable from 'ui/watchlist/WatchlistTable/WatchlistTable'; ...@@ -22,6 +22,7 @@ import WatchlistTable from 'ui/watchlist/WatchlistTable/WatchlistTable';
const WatchList: React.FC = () => { const WatchList: React.FC = () => {
const { data, isLoading, isError } = const { data, isLoading, isError } =
useQuery<unknown, unknown, TWatchlist>([ QueryKeys.watchlist ], async() => fetch('/node-api/account/watchlist/get-with-tokens')); useQuery<unknown, unknown, TWatchlist>([ QueryKeys.watchlist ], async() => fetch('/node-api/account/watchlist/get-with-tokens'));
const queryClient = useQueryClient();
const addressModalProps = useDisclosure(); const addressModalProps = useDisclosure();
const deleteModalProps = useDisclosure(); const deleteModalProps = useDisclosure();
...@@ -42,6 +43,12 @@ const WatchList: React.FC = () => { ...@@ -42,6 +43,12 @@ const WatchList: React.FC = () => {
addressModalProps.onClose(); addressModalProps.onClose();
}, [ addressModalProps ]); }, [ addressModalProps ]);
const onAddOrEditSuccess = useCallback(async() => {
await queryClient.refetchQueries([ QueryKeys.watchlist ]);
setAddressModalData(undefined);
addressModalProps.onClose();
}, [ addressModalProps, queryClient ]);
const onDeleteClick = useCallback((data: TWatchlistItem) => { const onDeleteClick = useCallback((data: TWatchlistItem) => {
setDeleteModalData(data); setDeleteModalData(data);
deleteModalProps.onOpen(); deleteModalProps.onOpen();
...@@ -52,6 +59,12 @@ const WatchList: React.FC = () => { ...@@ -52,6 +59,12 @@ const WatchList: React.FC = () => {
deleteModalProps.onClose(); deleteModalProps.onClose();
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const onDeleteSuccess = useCallback(async() => {
queryClient.setQueryData([ QueryKeys.watchlist ], (prevData: TWatchlist | undefined) => {
return prevData?.filter((item) => item.id !== deleteModalData?.id);
});
}, [ deleteModalData?.id, queryClient ]);
const description = ( const description = (
<AccountPageDescription> <AccountPageDescription>
An email notification can be sent to you when an address on your watch list sends or receives any transactions. An email notification can be sent to you when an address on your watch list sends or receives any transactions.
...@@ -107,8 +120,21 @@ const WatchList: React.FC = () => { ...@@ -107,8 +120,21 @@ const WatchList: React.FC = () => {
Add address Add address
</Button> </Button>
</Box> </Box>
<AddressModal { ...addressModalProps } onClose={ onAddressModalClose } data={ addressModalData }/> <AddressModal
{ deleteModalData && <DeleteAddressModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> } { ...addressModalProps }
onClose={ onAddressModalClose }
onSuccess={ onAddOrEditSuccess }
data={ addressModalData }
isAdd={ !addressModalData }
/>
{ deleteModalData && (
<DeleteAddressModal
{ ...deleteModalProps }
onClose={ onDeleteModalClose }
onSuccess={ onDeleteSuccess }
data={ deleteModalData }
/>
) }
</> </>
); );
} }
......
import type { InputProps } from '@chakra-ui/react'; import type { InputProps } from '@chakra-ui/react';
import { FormControl, FormLabel, Textarea } from '@chakra-ui/react'; import { FormControl, Textarea } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form'; import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
...@@ -24,9 +24,7 @@ export default function PublicTagFormComment({ control, error, size }: Props) { ...@@ -24,9 +24,7 @@ export default function PublicTagFormComment({ control, error, size }: Props) {
{ ...field } { ...field }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
/> />
<FormLabel>
<InputPlaceholder text="Specify the reason for adding tags and color preference(s)" error={ error }/> <InputPlaceholder text="Specify the reason for adding tags and color preference(s)" error={ error }/>
</FormLabel>
</FormControl> </FormControl>
); );
}, [ error, size ]); }, [ error, size ]);
......
import { Box, Text, chakra } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import getCurrencyValue from 'lib/getCurrencyValue';
interface Props { interface Props {
value: string; value: string;
currency?: string; currency?: string;
...@@ -20,32 +21,14 @@ const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className ...@@ -20,32 +21,14 @@ const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className
</Box> </Box>
); );
} }
const valueCurr = BigNumber(value).div(BigNumber(10 ** Number(decimals || '18'))); const { valueStr: valueResult, usd: usdResult } = getCurrencyValue({ value, accuracy, accuracyUsd, exchangeRate, decimals });
const valueResult = accuracy ? valueCurr.dp(accuracy).toFormat() : valueCurr.toFormat();
let usdContent;
if (exchangeRate !== undefined && exchangeRate !== null) {
const exchangeRateBn = new BigNumber(exchangeRate);
const usdBn = valueCurr.times(exchangeRateBn);
let usdResult: string;
if (accuracyUsd && !usdBn.isEqualTo(0)) {
const usdBnDp = usdBn.dp(accuracyUsd);
usdResult = usdBnDp.isEqualTo(0) ? usdBn.precision(accuracyUsd).toFormat() : usdBnDp.toFormat();
} else {
usdResult = usdBn.toFormat();
}
usdContent = (
<Text as="span" variant="secondary" fontWeight={ 400 }>(${ usdResult })</Text>
);
}
return ( return (
<Box as="span" className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }> <Box as="span" className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }>
<Text display="inline-block"> <Text display="inline-block">
{ valueResult }{ currency ? ` ${ currency }` : '' } { valueResult }{ currency ? ` ${ currency }` : '' }
</Text> </Text>
{ usdContent } { usdResult && <Text as="span" variant="secondary" fontWeight={ 400 }>(${ usdResult })</Text> }
</Box> </Box>
); );
}; };
......
...@@ -2,16 +2,28 @@ import { Tag, chakra } from '@chakra-ui/react'; ...@@ -2,16 +2,28 @@ import { Tag, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
interface Props { interface Props {
baseAddress: string; isIn: boolean;
addressFrom: string; isOut: boolean;
className?: string; className?: string;
} }
const InOutTag = ({ baseAddress, addressFrom, className }: Props) => { const InOutTag = ({ isIn, isOut, className }: Props) => {
const isOut = addressFrom === baseAddress; if (!isIn && !isOut) {
return null;
}
const colorScheme = isOut ? 'orange' : 'green'; const colorScheme = isOut ? 'orange' : 'green';
return <Tag className={ className } colorScheme={ colorScheme }>{ isOut ? 'OUT' : 'IN' }</Tag>; return (
<Tag
className={ className }
colorScheme={ colorScheme }
display="flex"
justifyContent="center"
>
{ isOut ? 'OUT' : 'IN' }
</Tag>
);
}; };
export default React.memo(chakra(InOutTag)); export default React.memo(chakra(InOutTag));
...@@ -96,7 +96,8 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => ...@@ -96,7 +96,8 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) =>
}} }}
bgColor={ listBgColor } bgColor={ listBgColor }
transitionProperty="top,box-shadow,background-color,color" transitionProperty="top,box-shadow,background-color,color"
transitionDuration="slow" transitionDuration="normal"
transitionTimingFunction="ease"
{ {
...(stickyEnabled ? { ...(stickyEnabled ? {
position: 'sticky', position: 'sticky',
......
import { Tooltip, IconButton, Icon, HStack } from '@chakra-ui/react'; import { Tooltip, IconButton, Icon, HStack } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React from 'react';
import DeleteIcon from 'icons/delete.svg'; import DeleteIcon from 'icons/delete.svg';
import EditIcon from 'icons/edit.svg'; import EditIcon from 'icons/edit.svg';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
type Props = { type Props = {
onEditClick: () => void; onEditClick: () => void;
...@@ -10,8 +11,7 @@ type Props = { ...@@ -10,8 +11,7 @@ type Props = {
} }
const TableItemActionButtons = ({ onEditClick, onDeleteClick }: Props) => { const TableItemActionButtons = ({ onEditClick, onDeleteClick }: Props) => {
// prevent set focus on button when closing modal const onFocusCapture = usePreventFocusAfterModalClosing();
const onFocusCapture = useCallback((e: React.SyntheticEvent) => e.stopPropagation(), []);
return ( return (
<HStack spacing={ 6 } alignSelf="flex-end"> <HStack spacing={ 6 } alignSelf="flex-end">
......
...@@ -21,13 +21,13 @@ const EmptyElement = ({ className, letter }: { className?: string; letter: strin ...@@ -21,13 +21,13 @@ const EmptyElement = ({ className, letter }: { className?: string; letter: strin
}; };
interface Props { interface Props {
hash: string; hash?: string;
name?: string | null; name?: string | null;
className?: string; className?: string;
} }
const TokenLogo = ({ hash, name, className }: Props) => { const TokenLogo = ({ hash, name, className }: Props) => {
const logoSrc = appConfig.network.assetsPathname ? [ const logoSrc = appConfig.network.assetsPathname && hash ? [
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/', 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/',
appConfig.network.assetsPathname, appConfig.network.assetsPathname,
'/assets/', '/assets/',
...@@ -37,6 +37,7 @@ const TokenLogo = ({ hash, name, className }: Props) => { ...@@ -37,6 +37,7 @@ const TokenLogo = ({ hash, name, className }: Props) => {
return ( return (
<Image <Image
borderRadius="base"
className={ className } className={ className }
src={ logoSrc } src={ logoSrc }
alt={ `${ name || 'token' } logo` } alt={ `${ name || 'token' } logo` }
......
...@@ -53,7 +53,7 @@ const TokenTransferListItem = ({ token, total, tx_hash: txHash, from, to, baseAd ...@@ -53,7 +53,7 @@ const TokenTransferListItem = ({ token, total, tx_hash: txHash, from, to, baseAd
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash }/> <AddressLink ml={ 2 } fontWeight="500" hash={ from.hash }/>
</Address> </Address>
{ baseAddress ? { baseAddress ?
<InOutTag baseAddress={ baseAddress } addressFrom={ from.hash } w="50px" textAlign="center"/> : <InOutTag isIn={ baseAddress === to.hash } isOut={ baseAddress === from.hash } w="50px" textAlign="center"/> :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/> <Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500"/>
} }
<Address width={ addressWidth }> <Address width={ addressWidth }>
......
...@@ -59,7 +59,7 @@ const TokenTransferTableItem = ({ token, total, tx_hash: txHash, from, to, baseA ...@@ -59,7 +59,7 @@ const TokenTransferTableItem = ({ token, total, tx_hash: txHash, from, to, baseA
</Td> </Td>
{ baseAddress && ( { baseAddress && (
<Td px={ 0 }> <Td px={ 0 }>
<InOutTag baseAddress={ baseAddress } addressFrom={ from.hash } w="50px" textAlign="center" mt="3px"/> <InOutTag isIn={ baseAddress === to.hash } isOut={ baseAddress === from.hash } w="50px" textAlign="center" mt="3px"/>
</Td> </Td>
) } ) }
<Td> <Td>
......
...@@ -18,10 +18,9 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) => ...@@ -18,10 +18,9 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) =>
const borderColor = useToken('colors', 'blue.200'); const borderColor = useToken('colors', 'blue.200');
const ref = React.useRef(null); const ref = React.useRef(null);
const isPressed = React.useRef(false); const isActive = React.useRef(false);
const startX = React.useRef<number>(); const startX = React.useRef<number>();
const endX = React.useRef<number>(); const endX = React.useRef<number>();
const startIndex = React.useRef<number>(0);
const getIndexByX = React.useCallback((x: number) => { const getIndexByX = React.useCallback((x: number) => {
const xDate = scale.invert(x); const xDate = scale.invert(x);
...@@ -51,20 +50,33 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) => ...@@ -51,20 +50,33 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) =>
.attr('width', Math.abs(diffX)); .attr('width', Math.abs(diffX));
}, []); }, []);
const handelMouseUp = React.useCallback(() => { const handleSelect = React.useCallback((x0: number, x1: number) => {
isPressed.current = false; const index0 = getIndexByX(x0);
const index1 = getIndexByX(x1);
if (Math.abs(index0 - index1) > SELECTION_THRESHOLD) {
onSelect([ Math.min(index0, index1), Math.max(index0, index1) ]);
}
}, [ getIndexByX, onSelect ]);
const cleanUp = React.useCallback(() => {
isActive.current = false;
startX.current = undefined; startX.current = undefined;
endX.current = undefined;
d3.select(ref.current).attr('opacity', 0); d3.select(ref.current).attr('opacity', 0);
}, [ ]);
if (!endX.current) { const handelMouseUp = React.useCallback(() => {
if (!isActive.current) {
return; return;
} }
const index = getIndexByX(endX.current); if (startX.current && endX.current) {
if (Math.abs(index - startIndex.current) > SELECTION_THRESHOLD) { handleSelect(startX.current, endX.current);
onSelect([ Math.min(index, startIndex.current), Math.max(index, startIndex.current) ]);
} }
}, [ getIndexByX, onSelect ]);
cleanUp();
}, [ cleanUp, handleSelect ]);
React.useEffect(() => { React.useEffect(() => {
if (!anchorEl) { if (!anchorEl) {
...@@ -76,20 +88,34 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) => ...@@ -76,20 +88,34 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) =>
anchorD3 anchorD3
.on('mousedown.selectionX', (event: MouseEvent) => { .on('mousedown.selectionX', (event: MouseEvent) => {
const [ x ] = d3.pointer(event, anchorEl); const [ x ] = d3.pointer(event, anchorEl);
isPressed.current = true; isActive.current = true;
startX.current = x; startX.current = x;
const index = getIndexByX(x);
startIndex.current = index;
}) })
.on('mouseup.selectionX', handelMouseUp)
.on('mousemove.selectionX', (event: MouseEvent) => { .on('mousemove.selectionX', (event: MouseEvent) => {
if (isPressed.current) { if (isActive.current) {
const [ x ] = d3.pointer(event, anchorEl); const [ x ] = d3.pointer(event, anchorEl);
startX.current && drawSelection(startX.current, x); startX.current && drawSelection(startX.current, x);
endX.current = x; endX.current = x;
} }
}); })
.on('mouseup.selectionX', handelMouseUp)
.on('touchstart.selectionX', (event: TouchEvent) => {
const pointers = d3.pointers(event, anchorEl);
isActive.current = pointers.length === 2;
})
.on('touchmove.selectionX', (event: TouchEvent) => {
if (isActive.current) {
const pointers = d3.pointers(event, anchorEl);
if (pointers.length === 2 && Math.abs(pointers[0][0] - pointers[1][0]) > 5) {
drawSelection(pointers[0][0], pointers[1][0]);
startX.current = pointers[0][0];
endX.current = pointers[1][0];
}
}
})
.on('touchend.selectionX', handelMouseUp);
d3.select('body').on('mouseup.selectionX', function(event) { d3.select('body').on('mouseup.selectionX', function(event) {
const isOutside = startX.current !== undefined && event.target !== anchorD3.node(); const isOutside = startX.current !== undefined && event.target !== anchorD3.node();
...@@ -99,10 +125,10 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) => ...@@ -99,10 +125,10 @@ const ChartSelectionX = ({ anchorEl, height, scale, data, onSelect }: Props) =>
}); });
return () => { return () => {
anchorD3.on('mousedown.selectionX mouseup.selectionX mousemove.selectionX', null); anchorD3.on('.selectionX', null);
d3.select('body').on('mouseup.selectionX', null); d3.select('body').on('.selectionX', null);
}; };
}, [ anchorEl, drawSelection, getIndexByX, handelMouseUp ]); }, [ anchorEl, cleanUp, drawSelection, getIndexByX, handelMouseUp, handleSelect ]);
return ( return (
<g className="ChartSelectionX" ref={ ref } opacity={ 0 }> <g className="ChartSelectionX" ref={ ref } opacity={ 0 }>
......
...@@ -32,6 +32,8 @@ const ChartTooltip = ({ chartId, xScale, yScale, width, height, data, anchorEl, ...@@ -32,6 +32,8 @@ const ChartTooltip = ({ chartId, xScale, yScale, width, height, data, anchorEl,
const bgColor = useToken('colors', 'blackAlpha.900'); const bgColor = useToken('colors', 'blackAlpha.900');
const ref = React.useRef(null); const ref = React.useRef(null);
const trackerId = React.useRef<number>();
const isVisible = React.useRef(false);
const drawLine = React.useCallback( const drawLine = React.useCallback(
(x: number) => { (x: number) => {
...@@ -129,67 +131,78 @@ const ChartTooltip = ({ chartId, xScale, yScale, width, height, data, anchorEl, ...@@ -129,67 +131,78 @@ const ChartTooltip = ({ chartId, xScale, yScale, width, height, data, anchorEl,
}, [ drawPoints, drawLine, drawContent ]); }, [ drawPoints, drawLine, drawContent ]);
const showContent = React.useCallback(() => { const showContent = React.useCallback(() => {
if (!isVisible.current) {
d3.select(ref.current).attr('opacity', 1); d3.select(ref.current).attr('opacity', 1);
d3.select(ref.current) d3.select(ref.current)
.selectAll('.ChartTooltip__point') .selectAll('.ChartTooltip__point')
.attr('opacity', 1); .attr('opacity', 1);
isVisible.current = true;
}
}, []); }, []);
const hideContent = React.useCallback(() => { const hideContent = React.useCallback(() => {
d3.select(ref.current).attr('opacity', 0); d3.select(ref.current).attr('opacity', 0);
isVisible.current = false;
}, []); }, []);
const createPointerTracker = React.useCallback((event: PointerEvent, isSubsequentCall?: boolean) => { const createPointerTracker = React.useCallback((event: PointerEvent, isSubsequentCall?: boolean) => {
let isShown = false;
let isPressed = event.pointerType === 'mouse' && event.type === 'pointerdown' && !isSubsequentCall; let isPressed = event.pointerType === 'mouse' && event.type === 'pointerdown' && !isSubsequentCall;
if (isPressed) { if (isPressed) {
hideContent(); hideContent();
} }
trackPointer(event, { return trackPointer(event, {
move: (pointer) => { move: (pointer) => {
if (!pointer.point || isPressed) { if (!pointer.point || isPressed) {
return; return;
} }
draw(pointer); draw(pointer);
if (!isShown) {
showContent(); showContent();
isShown = true;
}
}, },
out: () => { out: () => {
hideContent(); hideContent();
isShown = false; trackerId.current = undefined;
}, },
end: (tracker) => { end: () => {
hideContent(); hideContent();
const isOutside = tracker.sourceEvent?.offsetX && width && (tracker.sourceEvent.offsetX > width || tracker.sourceEvent.offsetX < 0); trackerId.current = undefined;
if (!isOutside && isPressed) {
window.setTimeout(() => {
createPointerTracker(event, true);
}, 0);
}
isShown = false;
isPressed = false; isPressed = false;
}, },
}); });
}, [ draw, hideContent, showContent, width ]); }, [ draw, hideContent, showContent ]);
React.useEffect(() => { React.useEffect(() => {
const anchorD3 = d3.select(anchorEl); const anchorD3 = d3.select(anchorEl);
let isMultiTouch = false; // disabling creation of new tracker in multi touch mode
anchorD3 anchorD3
.on('touchmove.tooltip', (event: PointerEvent) => event.preventDefault()) // prevent scrolling .on('touchmove.tooltip', (event: TouchEvent) => event.preventDefault()) // prevent scrolling
.on(`touchstart.tooltip`, (event: TouchEvent) => {
isMultiTouch = event.touches.length > 1;
})
.on(`touchend.tooltip`, (event: TouchEvent) => {
if (isMultiTouch && event.touches.length === 0) {
isMultiTouch = false;
}
})
.on('pointerenter.tooltip pointerdown.tooltip', (event: PointerEvent) => { .on('pointerenter.tooltip pointerdown.tooltip', (event: PointerEvent) => {
createPointerTracker(event); if (!isMultiTouch) {
trackerId.current = createPointerTracker(event);
}
})
.on('pointermove.tooltip', (event: PointerEvent) => {
if (event.pointerType === 'mouse' && !isMultiTouch && trackerId.current === undefined) {
trackerId.current = createPointerTracker(event);
}
}); });
return () => { return () => {
anchorD3.on('touchmove.tooltip pointerenter.tooltip pointerdown.tooltip', null); anchorD3.on('touchmove.tooltip pointerenter.tooltip pointerdown.tooltip', null);
trackerId.current && anchorD3.on(
[ 'pointerup', 'pointercancel', 'lostpointercapture', 'pointermove', 'pointerout' ].map((event) => `${ event }.${ trackerId.current }`).join(' '),
null,
);
}; };
}, [ anchorEl, createPointerTracker, draw, hideContent, showContent ]); }, [ anchorEl, createPointerTracker, draw, hideContent, showContent ]);
......
...@@ -14,7 +14,7 @@ export interface PointerOptions { ...@@ -14,7 +14,7 @@ export interface PointerOptions {
end?: (tracker: Pointer) => void; end?: (tracker: Pointer) => void;
} }
export function trackPointer(event: PointerEvent, { start, move, out, end }: PointerOptions) { export function trackPointer(event: PointerEvent, { start, move, out, end }: PointerOptions): number {
const tracker: Pointer = { const tracker: Pointer = {
id: event.pointerId, id: event.pointerId,
point: null, point: null,
...@@ -26,16 +26,27 @@ export function trackPointer(event: PointerEvent, { start, move, out, end }: Poi ...@@ -26,16 +26,27 @@ export function trackPointer(event: PointerEvent, { start, move, out, end }: Poi
tracker.point = d3.pointer(event, target); tracker.point = d3.pointer(event, target);
target.setPointerCapture(id); target.setPointerCapture(id);
const untrack = (sourceEvent: PointerEvent) => {
tracker.sourceEvent = sourceEvent;
d3.select(target).on(`.${ id }`, null);
target.releasePointerCapture(id);
end?.(tracker);
};
d3.select(target) d3.select(target)
.on(`touchstart.${ id }`, (sourceEvent: PointerEvent) => {
const target = sourceEvent.target as Element;
const touches = d3.pointers(sourceEvent, target);
// disable current tracker when entering multi touch mode
touches.length > 1 && untrack(sourceEvent);
})
.on(`pointerup.${ id } pointercancel.${ id } lostpointercapture.${ id }`, (sourceEvent: PointerEvent) => { .on(`pointerup.${ id } pointercancel.${ id } lostpointercapture.${ id }`, (sourceEvent: PointerEvent) => {
if (sourceEvent.pointerId !== id) { if (sourceEvent.pointerId !== id) {
return; return;
} }
tracker.sourceEvent = sourceEvent; untrack(sourceEvent);
d3.select(target).on(`.${ id }`, null);
target.releasePointerCapture(id);
end?.(tracker);
}) })
.on(`pointermove.${ id }`, (sourceEvent) => { .on(`pointermove.${ id }`, (sourceEvent) => {
if (sourceEvent.pointerId !== id) { if (sourceEvent.pointerId !== id) {
...@@ -57,5 +68,5 @@ export function trackPointer(event: PointerEvent, { start, move, out, end }: Poi ...@@ -57,5 +68,5 @@ export function trackPointer(event: PointerEvent, { start, move, out, end }: Poi
start?.(tracker); start?.(tracker);
return [ 'pointerup', 'pointercancel', 'lostpointercapture', 'pointermove', 'pointerout' ].map((event) => `${ event }.${ id }`); return id;
} }
import { GridItem, Skeleton, SkeletonCircle } from '@chakra-ui/react';
import React from 'react';
const DetailsSkeletonRow = ({ w = '100%' }: { w?: string }) => {
return (
<>
<GridItem display="flex" columnGap={ 2 } w={{ base: '50%', lg: 'auto' }} _notFirst={{ mt: { base: 3, lg: 0 } }}>
<SkeletonCircle h={ 5 } w={ 5 }/>
<Skeleton flexGrow={ 1 } h={ 5 } borderRadius="full"/>
</GridItem>
<GridItem pl={{ base: 7, lg: 0 }}>
<Skeleton h={ 5 } borderRadius="full" w={{ base: '100%', lg: w }}/>
</GridItem>
</>
);
};
export default DetailsSkeletonRow;
...@@ -69,7 +69,9 @@ const NavigationDesktop = () => { ...@@ -69,7 +69,9 @@ const NavigationDesktop = () => {
w="100%" w="100%"
px={{ lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }} px={{ lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }}
h={ 10 } h={ 10 }
{ ...getDefaultTransitionProps({ transitionProperty: 'padding' }) } transitionProperty="padding"
transitionDuration="normal"
transitionTimingFunction="ease"
> >
<NetworkLogo isCollapsed={ isCollapsed }/> <NetworkLogo isCollapsed={ isCollapsed }/>
<NetworkMenu isCollapsed={ isCollapsed }/> <NetworkMenu isCollapsed={ isCollapsed }/>
......
import { Icon, Box, Image, useColorModeValue, useBreakpointValue } from '@chakra-ui/react'; import { Icon, Box, Image, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
...@@ -6,7 +6,6 @@ import smallLogoPlaceholder from 'icons/networks/icons/placeholder.svg'; ...@@ -6,7 +6,6 @@ import smallLogoPlaceholder from 'icons/networks/icons/placeholder.svg';
import logoPlaceholder from 'icons/networks/logos/blockscout.svg'; import logoPlaceholder from 'icons/networks/logos/blockscout.svg';
import link from 'lib/link/link'; import link from 'lib/link/link';
import ASSETS from 'lib/networks/networkAssets'; import ASSETS from 'lib/networks/networkAssets';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
interface Props { interface Props {
isCollapsed?: boolean; isCollapsed?: boolean;
...@@ -16,58 +15,70 @@ interface Props { ...@@ -16,58 +15,70 @@ interface Props {
const NetworkLogo = ({ isCollapsed, onClick }: Props) => { const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
const logoColor = useColorModeValue('blue.600', 'white'); const logoColor = useColorModeValue('blue.600', 'white');
const href = link('network_index'); const href = link('network_index');
const [ isLogoError, setLogoError ] = React.useState(false);
const [ isSmallLogoError, setSmallLogoError ] = React.useState(false);
const style = useColorModeValue({}, { filter: 'brightness(0) invert(1)' }); const style = useColorModeValue({}, { filter: 'brightness(0) invert(1)' });
const isLg = useBreakpointValue({ base: false, lg: true, xl: false }, { ssr: true });
const handleSmallLogoError = React.useCallback(() => {
setSmallLogoError(true);
}, []);
const handleLogoError = React.useCallback(() => {
setLogoError(true);
}, []);
const logoEl = (() => { const logoEl = (() => {
const showSmallLogo = isCollapsed || (isCollapsed !== false && isLg); const fallbackLogoSrc = appConfig.network.type ? ASSETS[appConfig.network.type]?.logo : undefined;
if (showSmallLogo) { const fallbackSmallLogoSrc = appConfig.network.type ? ASSETS[appConfig.network.type]?.smallLogo || ASSETS[appConfig.network.type]?.icon : undefined;
if (appConfig.network.smallLogo) {
return ( const logo = appConfig.network.logo;
<Image const smallLogo = appConfig.network.smallLogo;
w="auto"
h="100%" const fallbackLogo = (
src={ appConfig.network.smallLogo } <Icon
alt={ `${ appConfig.network.name } network logo` } as={ fallbackLogoSrc || logoPlaceholder }
width="auto"
height="100%"
color={ fallbackLogoSrc ? undefined : logoColor }
display={{ base: 'block', lg: isCollapsed === false ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}
style={ style }
/> />
); );
} const fallbackSmallLogo = (
const smallLogo = appConfig.network.type ? ASSETS[appConfig.network.type]?.smallLogo || ASSETS[appConfig.network.type]?.icon : undefined;
return (
<Icon <Icon
as={ smallLogo || smallLogoPlaceholder } as={ fallbackSmallLogoSrc || smallLogoPlaceholder }
width="auto" width="auto"
height="100%" height="100%"
color={ smallLogo ? undefined : logoColor } color={ fallbackSmallLogoSrc ? undefined : logoColor }
{ ...getDefaultTransitionProps() } display={{ base: 'none', lg: isCollapsed === false ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }}
style={ style } style={ style }
/> />
); );
}
if (appConfig.network.logo) {
return ( return (
<>
{ /* big logo */ }
<Image <Image
w="auto" w="auto"
h="100%" h="100%"
src={ appConfig.network.logo } src={ logo }
display={{ base: 'block', lg: isCollapsed === false ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}
alt={ `${ appConfig.network.name } network logo` } alt={ `${ appConfig.network.name } network logo` }
fallback={ isLogoError || !logo ? fallbackSmallLogo : undefined }
onError={ handleLogoError }
/> />
); { /* small logo */ }
} <Image
w="auto"
const logo = appConfig.network.type ? ASSETS[appConfig.network.type]?.logo : undefined; h="100%"
return ( src={ smallLogo }
<Icon display={{ base: 'none', lg: isCollapsed === false ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }}
as={ logo || logoPlaceholder } alt={ `${ appConfig.network.name } network logo` }
width="auto" fallback={ isSmallLogoError || !smallLogo ? fallbackLogo : undefined }
height="100%" onError={ handleSmallLogoError }
color={ logo ? undefined : logoColor }
{ ...getDefaultTransitionProps() }
style={ style }
/> />
</>
); );
})(); })();
...@@ -82,7 +93,6 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => { ...@@ -82,7 +93,6 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
overflow="hidden" overflow="hidden"
onClick={ onClick } onClick={ onClick }
flexShrink={ 0 } flexShrink={ 0 }
{ ...getDefaultTransitionProps({ transitionProperty: 'width' }) }
aria-label="Link to main page" aria-label="Link to main page"
> >
{ logoEl } { logoEl }
......
...@@ -2,13 +2,13 @@ import { Popover, PopoverContent, PopoverBody, PopoverTrigger, Button } from '@c ...@@ -2,13 +2,13 @@ import { Popover, PopoverContent, PopoverBody, PopoverTrigger, Button } from '@c
import React from 'react'; import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import link from 'lib/link/link'; import useLoginUrl from 'lib/hooks/useLoginUrl';
import UserAvatar from 'ui/shared/UserAvatar'; import UserAvatar from 'ui/shared/UserAvatar';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent'; import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
const ProfileMenuDesktop = () => { const ProfileMenuDesktop = () => {
const { data, isFetched } = useFetchProfileInfo(); const { data, isFetched } = useFetchProfileInfo();
const loginUrl = link('auth'); const loginUrl = useLoginUrl();
return ( return (
<Popover openDelay={ 300 } placement="bottom-end" gutter={ 10 } isLazy> <Popover openDelay={ 300 } placement="bottom-end" gutter={ 10 } isLazy>
......
...@@ -2,7 +2,7 @@ import { Flex, Box, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useDisclos ...@@ -2,7 +2,7 @@ import { Flex, Box, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useDisclos
import React from 'react'; import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import link from 'lib/link/link'; import useLoginUrl from 'lib/hooks/useLoginUrl';
import UserAvatar from 'ui/shared/UserAvatar'; import UserAvatar from 'ui/shared/UserAvatar';
import ColorModeToggler from 'ui/snippets/header/ColorModeToggler'; import ColorModeToggler from 'ui/snippets/header/ColorModeToggler';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent'; import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
...@@ -11,7 +11,7 @@ const ProfileMenuMobile = () => { ...@@ -11,7 +11,7 @@ const ProfileMenuMobile = () => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { data, isFetched } = useFetchProfileInfo(); const { data, isFetched } = useFetchProfileInfo();
const loginUrl = link('auth'); const loginUrl = useLoginUrl();
return ( return (
<> <>
......
import { Box, Button, Grid, Heading, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Text, useColorModeValue, VisuallyHidden } from '@chakra-ui/react'; import { Box, Grid, Heading, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Text, useColorModeValue, VisuallyHidden } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
import type { ModalChart } from 'types/client/stats'; import type { Charts } from 'types/api/stats';
import { QueryKeys } from 'types/client/queries';
import type { StatsIntervalIds } from 'types/client/stats';
import dotsIcon from 'icons/vertical-dots.svg'; import repeatArrow from 'icons/repeat_arrow.svg';
import dotsIcon from 'icons/vertical_dots.svg';
import useFetch from 'lib/hooks/useFetch';
import ChartWidgetGraph from './ChartWidgetGraph'; import ChartWidgetGraph from './ChartWidgetGraph';
import { demoChartsData } from './constants/demo-charts-data'; import ChartWidgetSkeleton from './ChartWidgetSkeleton';
import { STATS_INTERVALS } from './constants';
import FullscreenChartModal from './FullscreenChartModal';
type Props = { type Props = {
id: string; id: string;
onFullscreenClick: (chart: ModalChart) => void;
apiMethodURL: string;
title: string; title: string;
description: string; description: string;
interval: StatsIntervalIds;
} }
const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => { function formatDate(date: Date) {
return date.toISOString().substring(0, 10);
}
const ChartWidget = ({ id, title, description, interval }: Props) => {
const fetch = useFetch();
const selectedInterval = STATS_INTERVALS[interval];
const [ isFullscreen, setIsFullscreen ] = useState(false);
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true); const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
const endDate = selectedInterval.start ? formatDate(new Date()) : undefined;
const startDate = selectedInterval.start ? formatDate(selectedInterval.start) : undefined;
const menuButtonColor = useColorModeValue('black', 'white');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const url = `/node-api/stats/charts?name=${ id }${ startDate ? `&from=${ startDate }&to=${ endDate }` : '' }`;
const { data, isLoading } = useQuery<unknown, unknown, Charts>(
[ QueryKeys.charts, id, startDate ],
async() => await fetch(url),
);
const handleZoom = useCallback(() => { const handleZoom = useCallback(() => {
setIsZoomResetInitial(false); setIsZoomResetInitial(false);
}, []); }, []);
...@@ -27,20 +55,43 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => { ...@@ -27,20 +55,43 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => {
setIsZoomResetInitial(true); setIsZoomResetInitial(true);
}, []); }, []);
const handleFullscreenClick = useCallback(() => { const showChartFullscreen = useCallback(() => {
onFullscreenClick({ id, title }); setIsFullscreen(true);
}, [ id, title, onFullscreenClick ]);
if (!document.fullscreenElement && document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen();
}
}, []);
const clearFullscreenChart = useCallback(() => {
setIsFullscreen(false);
if (document.fullscreenElement) {
document.exitFullscreen();
}
}, []);
if (isLoading) {
return <ChartWidgetSkeleton/>;
}
if (data) {
const items = data.chart
.map((item) => {
return { date: new Date(item.date), value: Number(item.value) };
});
return ( return (
<>
<Box <Box
padding={{ base: 3, md: 4 }} padding={{ base: 3, md: 4 }}
borderRadius="md" borderRadius="md"
border="1px" border="1px"
borderColor={ useColorModeValue('gray.200', 'gray.600') } borderColor={ borderColor }
> >
<Grid <Grid
gridTemplateColumns="auto auto 36px" gridTemplateColumns="auto auto 36px"
gridColumnGap={ 4 } gridColumnGap={ 2 }
> >
<Heading <Heading
mb={ 1 } mb={ 1 }
...@@ -59,19 +110,22 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => { ...@@ -59,19 +110,22 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => {
{ description } { description }
</Text> </Text>
{ !isZoomResetInitial && ( <IconButton
<Button hidden={ isZoomResetInitial }
aria-label="Reset zoom"
title="Reset zoom"
colorScheme="blue"
w={ 9 }
h={ 8 }
gridColumn={ 2 } gridColumn={ 2 }
justifySelf="end" justifySelf="end"
alignSelf="top" alignSelf="top"
gridRow="1/3" gridRow="1/3"
size="sm" size="sm"
variant="outline" variant="ghost"
onClick={ handleZoomResetClick } onClick={ handleZoomResetClick }
> icon={ <Icon as={ repeatArrow } w={ 4 } h={ 4 } color="blue.700"/> }
Reset zoom />
</Button>
) }
<Menu> <Menu>
<MenuButton <MenuButton
...@@ -80,7 +134,7 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => { ...@@ -80,7 +134,7 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => {
justifySelf="end" justifySelf="end"
w="36px" w="36px"
h="32px" h="32px"
icon={ <Icon as={ dotsIcon } w={ 4 } h={ 4 } color={ useColorModeValue('black', 'white') }/> } icon={ <Icon as={ dotsIcon } w={ 4 } h={ 4 } color={ menuButtonColor }/> }
colorScheme="transparent" colorScheme="transparent"
as={ IconButton } as={ IconButton }
> >
...@@ -89,19 +143,30 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => { ...@@ -89,19 +143,30 @@ const ChartWidget = ({ id, title, description, onFullscreenClick }: Props) => {
</VisuallyHidden> </VisuallyHidden>
</MenuButton> </MenuButton>
<MenuList> <MenuList>
<MenuItem onClick={ handleFullscreenClick }>View fullscreen</MenuItem> <MenuItem onClick={ showChartFullscreen }>View fullscreen</MenuItem>
</MenuList> </MenuList>
</Menu> </Menu>
</Grid> </Grid>
<ChartWidgetGraph <ChartWidgetGraph
items={ demoChartsData } items={ items }
onZoom={ handleZoom } onZoom={ handleZoom }
isZoomResetInitial={ isZoomResetInitial } isZoomResetInitial={ isZoomResetInitial }
title={ title } title={ title }
/> />
</Box> </Box>
<FullscreenChartModal
isOpen={ isFullscreen }
items={ items }
title={ title }
onClose={ clearFullscreenChart }
/>
</>
); );
}
return null;
}; };
export default ChartWidget; export default ChartWidget;
...@@ -3,6 +3,7 @@ import React, { useEffect, useMemo } from 'react'; ...@@ -3,6 +3,7 @@ import React, { useEffect, useMemo } from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types'; import type { TimeChartItem } from 'ui/shared/chart/types';
import useIsMobile from 'lib/hooks/useIsMobile';
import ChartArea from 'ui/shared/chart/ChartArea'; import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis'; import ChartAxis from 'ui/shared/chart/ChartAxis';
import ChartGridLine from 'ui/shared/chart/ChartGridLine'; import ChartGridLine from 'ui/shared/chart/ChartGridLine';
...@@ -23,12 +24,13 @@ interface Props { ...@@ -23,12 +24,13 @@ interface Props {
const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 }; const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => { const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => {
const isMobile = useIsMobile();
const ref = React.useRef<SVGSVGElement>(null); const ref = React.useRef<SVGSVGElement>(null);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const overlayRef = React.useRef<SVGRectElement>(null);
const color = useToken('colors', 'blue.200'); const color = useToken('colors', 'blue.200');
const overlayRef = React.useRef<SVGRectElement>(null);
const chartId = useMemo(() => `chart-${ crypto.randomUUID() }`, []); const chartId = useMemo(() => `chart-${ crypto.randomUUID() }`, []);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) => const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) =>
({ ...d, date: new Date(d.date) })), [ items, range ]); ({ ...d, date: new Date(d.date) })), [ items, range ]);
...@@ -52,9 +54,9 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -52,9 +54,9 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
}, [ isZoomResetInitial ]); }, [ isZoomResetInitial ]);
return ( return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer" id={ chartId }> <svg width="100%" height="100%" ref={ ref } cursor="pointer" id={ chartId }>
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }> <g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ 1 }>
<ChartGridLine <ChartGridLine
type="horizontal" type="horizontal"
scale={ yScale } scale={ yScale }
...@@ -93,7 +95,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -93,7 +95,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
type="bottom" type="bottom"
scale={ xScale } scale={ xScale }
transform={ `translate(0, ${ innerHeight })` } transform={ `translate(0, ${ innerHeight })` }
ticks={ 5 } ticks={ isMobile ? 1 : 3 }
anchorEl={ overlayRef.current } anchorEl={ overlayRef.current }
disableAnimation disableAnimation
/> />
......
import { Box, Skeleton } from '@chakra-ui/react';
import React from 'react';
const ChartWidgetSkeleton = () => {
return (
<Box
height="235px"
paddingY={{ base: 3, md: 4 }}
>
<Skeleton w="75%" h="24px" mb={ 1 }/>
<Skeleton w="50%" h="18px" mb={ 5 }/>
<Skeleton w="100%" h="150px"/>
</Box>
);
};
export default ChartWidgetSkeleton;
import { Box, Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react'; import { Box, Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ModalChart, StatsSection } from 'types/client/stats'; import type { StatsIntervalIds, StatsSection } from 'types/client/stats';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import EmptySearchResult from '../apps/EmptySearchResult'; import EmptySearchResult from '../apps/EmptySearchResult';
import ChartWidget from './ChartWidget'; import ChartWidget from './ChartWidget';
import FullscreenChartModal from './FullscreenChartModal';
type Props = { type Props = {
charts: Array<StatsSection>; charts: Array<StatsSection>;
onChartFullscreenClick: (chart: ModalChart) => void; interval: StatsIntervalIds;
fullscreenChart: ModalChart | null;
onModalClose: () => void;
} }
const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, onModalClose }: Props) => { const ChartsWidgetsList = ({ charts, interval }: Props) => {
const isAnyChartDisplayed = charts.some((section) => section.charts.some(chart => chart.visible)); const isAnyChartDisplayed = charts.some((section) => section.charts.some(chart => chart.visible));
return ( return (
...@@ -42,7 +39,7 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on ...@@ -42,7 +39,7 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on
<Grid <Grid
templateColumns={{ templateColumns={{
sm: 'repeat(2, 1fr)', lg: 'repeat(2, 1fr)',
}} }}
gap={ 4 } gap={ 4 }
> >
...@@ -53,10 +50,9 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on ...@@ -53,10 +50,9 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on
> >
<ChartWidget <ChartWidget
id={ chart.id } id={ chart.id }
onFullscreenClick={ onChartFullscreenClick }
apiMethodURL={ chart.apiMethodURL }
title={ chart.title } title={ chart.title }
description={ chart.description } description={ chart.description }
interval={ interval }
/> />
</GridItem> </GridItem>
)) } )) }
...@@ -68,14 +64,6 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on ...@@ -68,14 +64,6 @@ const ChartsWidgetsList = ({ charts, onChartFullscreenClick, fullscreenChart, on
) : ( ) : (
<EmptySearchResult text={ `Couldn${ apos }t find a chart that matches your filter query.` }/> <EmptySearchResult text={ `Couldn${ apos }t find a chart that matches your filter query.` }/>
) } ) }
{ fullscreenChart && (
<FullscreenChartModal
id={ fullscreenChart.id }
title={ fullscreenChart.title }
onClose={ onModalClose }
/>
) }
</Box> </Box>
); );
}; };
......
import { Button, Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react'; import { Button, Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { TimeChartItem } from '../shared/chart/types';
import ChartWidgetGraph from './ChartWidgetGraph'; import ChartWidgetGraph from './ChartWidgetGraph';
import { demoChartsData } from './constants/demo-charts-data';
type Props = { type Props = {
id: string; isOpen: boolean;
title: string; title: string;
items: Array<TimeChartItem>;
onClose: () => void; onClose: () => void;
} }
const FullscreenChartModal = ({ const FullscreenChartModal = ({
id, isOpen,
title, title,
items,
onClose, onClose,
}: Props) => { }: Props) => {
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true); const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
...@@ -27,7 +30,7 @@ const FullscreenChartModal = ({ ...@@ -27,7 +30,7 @@ const FullscreenChartModal = ({
return ( return (
<Modal <Modal
isOpen={ Boolean(id) } isOpen={ isOpen }
onClose={ onClose } onClose={ onClose }
size="full" size="full"
isCentered isCentered
...@@ -71,13 +74,13 @@ const FullscreenChartModal = ({ ...@@ -71,13 +74,13 @@ const FullscreenChartModal = ({
<ModalCloseButton/> <ModalCloseButton/>
<ModalBody <ModalBody
h="100%" h="75%"
> >
<ChartWidgetGraph <ChartWidgetGraph
items={ demoChartsData } items={ items }
onZoom={ handleZoom } onZoom={ handleZoom }
isZoomResetInitial={ isZoomResetInitial } isZoomResetInitial={ isZoomResetInitial }
title="test" title={ title }
/> />
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
......
...@@ -17,8 +17,7 @@ const NumberWidgetsList = () => { ...@@ -17,8 +17,7 @@ const NumberWidgetsList = () => {
const { data, isLoading } = useQuery<unknown, unknown, Stats>( const { data, isLoading } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ], [ QueryKeys.stats ],
// TODO: Just temporary. Remove this when the API is ready. async() => await fetch(`/node-api/stats/counters`),
async() => await fetch(`/node-api/stats`),
); );
return ( return (
...@@ -30,8 +29,8 @@ const NumberWidgetsList = () => { ...@@ -30,8 +29,8 @@ const NumberWidgetsList = () => {
.map((e, i) => <NumberWidgetSkeleton key={ i }/>) : .map((e, i) => <NumberWidgetSkeleton key={ i }/>) :
( (
<NumberWidget <NumberWidget
label="Total blocks" label="Total blocks all time"
value={ Number(data?.total_blocks).toLocaleString() } value={ Number(data?.totalBlocksAllTime).toLocaleString() }
/> />
) } ) }
</Grid> </Grid>
......
import { Box, Button, Icon, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup } from '@chakra-ui/react'; import { Box, Button, Icon, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup, Text } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import eastMiniArrowIcon from 'icons/arrows/east-mini.svg'; import eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
...@@ -31,9 +31,15 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec ...@@ -31,9 +31,15 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec
as="span" as="span"
display="flex" display="flex"
alignItems="center" alignItems="center"
>
<Text
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
> >
{ selectedCategory?.title } { selectedCategory?.title }
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/> </Text>
<Icon transform="rotate(-90deg)" ml="auto" as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Box> </Box>
</MenuButton> </MenuButton>
......
...@@ -21,7 +21,7 @@ const sectionsList = Object.keys(STATS_SECTIONS) ...@@ -21,7 +21,7 @@ const sectionsList = Object.keys(STATS_SECTIONS)
const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({ const intervalList = Object.keys(STATS_INTERVALS).map((id: string) => ({
id: id, id: id,
title: STATS_INTERVALS[id as StatsIntervalIds], title: STATS_INTERVALS[id as StatsIntervalIds].title,
})) as Array<StatsInterval>; })) as Array<StatsInterval>;
type Props = { type Props = {
...@@ -48,7 +48,7 @@ const StatsFilters = ({ ...@@ -48,7 +48,7 @@ const StatsFilters = ({
"section interval"`, "section interval"`,
lg: `"input section interval"`, lg: `"input section interval"`,
}} }}
gridTemplateColumns={{ lg: '1fr auto auto' }} gridTemplateColumns={{ base: 'repeat(2, minmax(0, 1fr))', lg: '1fr auto auto' }}
> >
<GridItem <GridItem
w="100%" w="100%"
......
...@@ -9,31 +9,7 @@ export const statsChartsScheme: Array<StatsSection> = [ ...@@ -9,31 +9,7 @@ export const statsChartsScheme: Array<StatsSection> = [
id: 'new-blocks', id: 'new-blocks',
title: 'New blocks', title: 'New blocks',
description: 'New blocks number per day', description: 'New blocks number per day',
apiMethodURL: '/node-api/stats/charts/transactions', visible: true,
},
{
id: 'average-block-size',
title: 'Average block size',
description: 'Average size of blocks in bytes per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
],
},
{
id: 'transactions',
title: 'Transactions',
charts: [
{
id: 'transaction-fees',
title: 'Transaction fees',
description: 'Amount of tokens paid as fees per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
{
id: 'native-coin-holders-growth',
title: 'Native coin holders growth',
description: 'Total token holders number per day',
apiMethodURL: '/node-api/stats/charts/transactions',
}, },
], ],
}, },
......
import type { TimeChartItem } from 'ui/shared/chart/types';
export const demoChartsData: Array<TimeChartItem> = [ { date: new Date('2022-10-17T00:00:00.000Z'), value: 432670 }, {
date: new Date('2022-10-18T00:00:00.000Z'),
value: 370100,
}, { date: new Date('2022-10-19T00:00:00.000Z'), value: 283234 }, { date: new Date('2022-10-20T00:00:00.000Z'), value: 420910 }, {
date: new Date('2022-10-21T00:00:00.000Z'),
value: 411988,
}, { date: new Date('2022-10-22T00:00:00.000Z'), value: 356269 }, { date: new Date('2022-10-23T00:00:00.000Z'), value: 389747 }, {
date: new Date('2022-10-24T00:00:00.000Z'),
value: 387130,
}, { date: new Date('2022-10-25T00:00:00.000Z'), value: 428785 }, { date: new Date('2022-10-26T00:00:00.000Z'), value: 63809 }, {
date: new Date('2022-10-27T00:00:00.000Z'),
value: 50518,
}, { date: new Date('2022-10-28T00:00:00.000Z'), value: 39087 }, { date: new Date('2022-10-29T00:00:00.000Z'), value: 36789 }, {
date: new Date('2022-10-30T00:00:00.000Z'),
value: 48569,
}, { date: new Date('2022-10-31T00:00:00.000Z'), value: 62519 }, { date: new Date('2022-11-01T00:00:00.000Z'), value: 152059 }, {
date: new Date('2022-11-02T00:00:00.000Z'),
value: 63743,
}, { date: new Date('2022-11-03T00:00:00.000Z'), value: 83667 }, { date: new Date('2022-11-04T00:00:00.000Z'), value: 91725 }, {
date: new Date('2022-11-05T00:00:00.000Z'),
value: 82897,
}, { date: new Date('2022-11-06T00:00:00.000Z'), value: 62477 }, { date: new Date('2022-11-07T00:00:00.000Z'), value: 58131 }, {
date: new Date('2022-11-08T00:00:00.000Z'),
value: 74197,
}, { date: new Date('2022-11-09T00:00:00.000Z'), value: 43691 }, { date: new Date('2022-11-10T00:00:00.000Z'), value: 92887 }, {
date: new Date('2022-11-11T00:00:00.000Z'),
value: 79493,
}, { date: new Date('2022-11-12T00:00:00.000Z'), value: 86764 }, { date: new Date('2022-11-13T00:00:00.000Z'), value: 22338 }, {
date: new Date('2022-11-14T00:00:00.000Z'),
value: 62266,
}, { date: new Date('2022-11-15T00:00:00.000Z'), value: 84084 }, { date: new Date('2022-11-16T00:00:00.000Z'), value: 75898 } ];
...@@ -8,10 +8,30 @@ export const STATS_SECTIONS: { [key in StatsSectionIds]?: string } = { ...@@ -8,10 +8,30 @@ export const STATS_SECTIONS: { [key in StatsSectionIds]?: string } = {
gas: 'Gas', gas: 'Gas',
}; };
export const STATS_INTERVALS: { [key in StatsIntervalIds]: string } = { export const STATS_INTERVALS: { [key in StatsIntervalIds]: { title: string; start?: Date } } = {
all: 'All time', all: {
oneMonth: '1 month', title: 'All time',
threeMonths: '3 months', },
sixMonths: '6 months', oneMonth: {
oneYear: '1 year', title: '1 month',
start: getStartDateInPast(1),
},
threeMonths: {
title: '3 months',
start: getStartDateInPast(3),
},
sixMonths: {
title: '6 months',
start: getStartDateInPast(6),
},
oneYear: {
title: '1 year',
start: getStartDateInPast(12),
},
}; };
function getStartDateInPast(months: number): Date {
const date = new Date();
date.setMonth(date.getMonth() - months);
return date;
}
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { ModalChart, StatsChart, StatsIntervalIds, StatsSection, StatsSectionIds } from 'types/client/stats'; import type { StatsChart, StatsIntervalIds, StatsSection, StatsSectionIds } from 'types/client/stats';
import { statsChartsScheme } from './constants/charts-scheme'; import { statsChartsScheme } from './constants/charts-scheme';
...@@ -16,8 +16,7 @@ function isChartNameMatches(q: string, chart: StatsChart) { ...@@ -16,8 +16,7 @@ function isChartNameMatches(q: string, chart: StatsChart) {
export default function useStats() { export default function useStats() {
const [ displayedCharts, setDisplayedCharts ] = useState<Array<StatsSection>>(statsChartsScheme); const [ displayedCharts, setDisplayedCharts ] = useState<Array<StatsSection>>(statsChartsScheme);
const [ section, setSection ] = useState<StatsSectionIds>('all'); const [ section, setSection ] = useState<StatsSectionIds>('all');
const [ interval, setInterval ] = useState<StatsIntervalIds>('all'); const [ interval, setInterval ] = useState<StatsIntervalIds>('oneMonth');
const [ fullscreenChart, setFullscreenChart ] = useState<ModalChart | null>(null);
const [ filterQuery, setFilterQuery ] = useState(''); const [ filterQuery, setFilterQuery ] = useState('');
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
...@@ -48,22 +47,6 @@ export default function useStats() { ...@@ -48,22 +47,6 @@ export default function useStats() {
setInterval(newInterval); setInterval(newInterval);
}, []); }, []);
const showChartFullscreen = useCallback((chart: ModalChart) => {
setFullscreenChart(chart);
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
}
}, []);
const clearFullscreenChart = useCallback(() => {
setFullscreenChart(null);
if (document.fullscreenElement) {
document.exitFullscreen();
}
}, []);
useEffect(() => { useEffect(() => {
filterCharts(filterQuery, section); filterCharts(filterQuery, section);
}, [ filterQuery, section, filterCharts ]); }, [ filterQuery, section, filterCharts ]);
...@@ -75,9 +58,6 @@ export default function useStats() { ...@@ -75,9 +58,6 @@ export default function useStats() {
handleIntervalChange, handleIntervalChange,
debounceFilterCharts, debounceFilterCharts,
displayedCharts, displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
}), [ }), [
section, section,
handleSectionChange, handleSectionChange,
...@@ -85,8 +65,5 @@ export default function useStats() { ...@@ -85,8 +65,5 @@ export default function useStats() {
handleIntervalChange, handleIntervalChange,
debounceFilterCharts, debounceFilterCharts,
displayedCharts, displayedCharts,
showChartFullscreen,
clearFullscreenChart,
fullscreenChart,
]); ]);
} }
...@@ -26,7 +26,7 @@ test('between addresses +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -26,7 +26,7 @@ test('between addresses +@mobile +@dark-mode', async({ mount, page }) => {
{ hooksConfig }, { hooksConfig },
); );
await page.waitForResponse(API_URL), await page.waitForResponse(API_URL);
await page.getByText('View details').click(); await page.getByText('View details').click();
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
......
import { Grid, GridItem, Skeleton, SkeletonCircle } from '@chakra-ui/react'; import { Grid, GridItem, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
const SkeletonRow = ({ w = '100%' }: { w?: string }) => ( import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
<>
<GridItem display="flex" columnGap={ 2 } w={{ base: '50%', lg: 'auto' }} _notFirst={{ mt: { base: 3, lg: 0 } }}>
<SkeletonCircle h={ 5 } w={ 5 }/>
<Skeleton flexGrow={ 1 } h={ 5 } borderRadius="full"/>
</GridItem>
<GridItem pl={{ base: 7, lg: 0 }}>
<Skeleton h={ 5 } borderRadius="full" w={{ base: '100%', lg: w }}/>
</GridItem>
</>
);
const TxDetailsSkeleton = () => { const TxDetailsSkeleton = () => {
const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>; const sectionGap = <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>;
return ( return (
<Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px"> <Grid columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
<SkeletonRow/> <DetailsSkeletonRow/>
<SkeletonRow w="20%"/> <DetailsSkeletonRow w="20%"/>
<SkeletonRow w="50%"/> <DetailsSkeletonRow w="50%"/>
<SkeletonRow/> <DetailsSkeletonRow/>
<SkeletonRow w="70%"/> <DetailsSkeletonRow w="70%"/>
<SkeletonRow w="70%"/> <DetailsSkeletonRow w="70%"/>
{ sectionGap } { sectionGap }
<SkeletonRow w="40%"/> <DetailsSkeletonRow w="40%"/>
<SkeletonRow w="40%"/> <DetailsSkeletonRow w="40%"/>
<SkeletonRow w="40%"/> <DetailsSkeletonRow w="40%"/>
<SkeletonRow w="40%"/> <DetailsSkeletonRow w="40%"/>
<SkeletonRow w="40%"/> <DetailsSkeletonRow w="40%"/>
{ sectionGap } { sectionGap }
<GridItem colSpan={{ base: undefined, lg: 2 }}> <GridItem colSpan={{ base: undefined, lg: 2 }}>
<Skeleton h={ 5 } borderRadius="full" w="100px"/> <Skeleton h={ 5 } borderRadius="full" w="100px"/>
......
...@@ -24,9 +24,11 @@ type Props = { ...@@ -24,9 +24,11 @@ type Props = {
query: QueryResult; query: QueryResult;
showBlockInfo?: boolean; showBlockInfo?: boolean;
showSocketInfo?: boolean; showSocketInfo?: boolean;
currentAddress?: string;
filter?: React.ReactNode;
} }
const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Props) => { const TxsContent = ({ filter, query, showBlockInfo = true, showSocketInfo = true, currentAddress }: Props) => {
const { data, isLoading, isError, setSortByField, setSortByValue, sorting } = useTxsSort(query); const { data, isLoading, isError, setSortByField, setSortByValue, sorting } = useTxsSort(query);
const isPaginatorHidden = !isLoading && !isError && query.pagination.page === 1 && !query.pagination.hasNextPage; const isPaginatorHidden = !isLoading && !isError && query.pagination.page === 1 && !query.pagination.hasNextPage;
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -60,7 +62,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop ...@@ -60,7 +62,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop
{ ({ content }) => <Box>{ content }</Box> } { ({ content }) => <Box>{ content }</Box> }
</TxsNewItemNotice> </TxsNewItemNotice>
) } ) }
{ txs.map(tx => <TxsListItem tx={ tx } key={ tx.hash } showBlockInfo={ showBlockInfo }/>) } { txs.map(tx => <TxsListItem tx={ tx } key={ tx.hash } showBlockInfo={ showBlockInfo } currentAddress={ currentAddress }/>) }
</Box> </Box>
</Show> </Show>
<Hide below="lg" ssr={ false }> <Hide below="lg" ssr={ false }>
...@@ -71,6 +73,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop ...@@ -71,6 +73,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop
showBlockInfo={ showBlockInfo } showBlockInfo={ showBlockInfo }
showSocketInfo={ showSocketInfo } showSocketInfo={ showSocketInfo }
top={ isPaginatorHidden ? 0 : 80 } top={ isPaginatorHidden ? 0 : 80 }
currentAddress={ currentAddress }
/> />
</Hide> </Hide>
</> </>
...@@ -86,6 +89,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop ...@@ -86,6 +89,7 @@ const TxsContent = ({ query, showBlockInfo = true, showSocketInfo = true }: Prop
setSorting={ setSortByValue } setSorting={ setSortByValue }
paginationProps={ query.pagination } paginationProps={ query.pagination }
showPagination={ !isPaginatorHidden } showPagination={ !isPaginatorHidden }
filterComponent={ filter }
/> />
) } ) }
{ content } { content }
......
...@@ -17,18 +17,14 @@ type Props = { ...@@ -17,18 +17,14 @@ type Props = {
paginationProps: PaginationProps; paginationProps: PaginationProps;
className?: string; className?: string;
showPagination?: boolean; showPagination?: boolean;
filterComponent?: React.ReactNode;
} }
const TxsHeaderMobile = ({ sorting, setSorting, paginationProps, className, showPagination = true }: Props) => { const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps, className, showPagination = true }: Props) => {
return ( return (
<ActionBar className={ className }> <ActionBar className={ className }>
<HStack> <HStack>
{ /* api is not implemented */ } { filterComponent }
{ /* <TxsFilters
filters={ filters }
onFiltersChange={ setFilters }
appliedFiltersNum={ 0 }
/> */ }
<TxsSorting <TxsSorting
isActive={ Boolean(sorting) } isActive={ Boolean(sorting) }
setSorting={ setSorting } setSorting={ setSorting }
......
...@@ -24,17 +24,30 @@ import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton'; ...@@ -24,17 +24,30 @@ import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import InOutTag from 'ui/shared/InOutTag';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from 'ui/txs/TxType'; import TxType from 'ui/txs/TxType';
const TxsListItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boolean}) => { type Props = {
tx: Transaction;
showBlockInfo: boolean;
currentAddress?: string;
}
const TAG_WIDTH = 48;
const ARROW_WIDTH = 24;
const TxsListItem = ({ tx, showBlockInfo, currentAddress }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const iconColor = useColorModeValue('blue.600', 'blue.300'); const iconColor = useColorModeValue('blue.600', 'blue.300');
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200'); const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const dataTo = tx.to ? tx.to : tx.created_contract; const dataTo = tx.to ? tx.to : tx.created_contract;
const isOut = Boolean(currentAddress && currentAddress === tx.from.hash);
const isIn = Boolean(currentAddress && currentAddress === tx.to?.hash);
return ( return (
<> <>
<Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor }}> <Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor }}>
...@@ -83,7 +96,7 @@ const TxsListItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boo ...@@ -83,7 +96,7 @@ const TxsListItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boo
</Box> </Box>
) } ) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address width="calc((100%-40px)/2)"> <Address width={ `calc((100%-${ currentAddress ? TAG_WIDTH : ARROW_WIDTH + 8 }px)/2)` }>
<AddressIcon hash={ tx.from.hash }/> <AddressIcon hash={ tx.from.hash }/>
<AddressLink <AddressLink
hash={ tx.from.hash } hash={ tx.from.hash }
...@@ -92,12 +105,15 @@ const TxsListItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boo ...@@ -92,12 +105,15 @@ const TxsListItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boo
ml={ 2 } ml={ 2 }
/> />
</Address> </Address>
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mr={ 2 }/> : (
<Icon <Icon
as={ rightArrowIcon } as={ rightArrowIcon }
boxSize={ 6 } boxSize={ 6 }
mx={ 2 } mx={ 2 }
color="gray.500" color="gray.500"
/> />
) }
<Address width="calc((100%-40px)/2)"> <Address width="calc((100%-40px)/2)">
<AddressIcon hash={ dataTo.hash }/> <AddressIcon hash={ dataTo.hash }/>
<AddressLink <AddressLink
......
...@@ -18,9 +18,10 @@ type Props = { ...@@ -18,9 +18,10 @@ type Props = {
top: number; top: number;
showBlockInfo: boolean; showBlockInfo: boolean;
showSocketInfo: boolean; showSocketInfo: boolean;
currentAddress?: string;
} }
const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo }: Props) => { const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo, currentAddress }: Props) => {
return ( return (
<Table variant="simple" minWidth="950px" size="xs"> <Table variant="simple" minWidth="950px" size="xs">
<TheadSticky top={ top }> <TheadSticky top={ top }>
...@@ -31,7 +32,7 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo }: Pr ...@@ -31,7 +32,7 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo }: Pr
<Th width="15%">Method</Th> <Th width="15%">Method</Th>
{ showBlockInfo && <Th width="11%">Block</Th> } { showBlockInfo && <Th width="11%">Block</Th> }
<Th width={{ xl: '128px', base: '66px' }}>From</Th> <Th width={{ xl: '128px', base: '66px' }}>From</Th>
<Th width={{ xl: '36px', base: '0' }}></Th> <Th width={{ xl: currentAddress ? '48px' : '36px', base: '0' }}></Th>
<Th width={{ xl: '128px', base: '66px' }}>To</Th> <Th width={{ xl: '128px', base: '66px' }}>To</Th>
<Th width="18%" isNumeric> <Th width="18%" isNumeric>
<Link onClick={ sort('val') } display="flex" justifyContent="end"> <Link onClick={ sort('val') } display="flex" justifyContent="end">
...@@ -60,6 +61,7 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo }: Pr ...@@ -60,6 +61,7 @@ const TxsTable = ({ txs, sort, sorting, top, showBlockInfo, showSocketInfo }: Pr
key={ item.hash } key={ item.hash }
tx={ item } tx={ item }
showBlockInfo={ showBlockInfo } showBlockInfo={ showBlockInfo }
currentAddress={ currentAddress }
/> />
)) } )) }
</Tbody> </Tbody>
......
...@@ -28,13 +28,23 @@ import Address from 'ui/shared/address/Address'; ...@@ -28,13 +28,23 @@ import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import InOutTag from 'ui/shared/InOutTag';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxType from './TxType'; import TxType from './TxType';
const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: boolean }) => { type Props = {
tx: Transaction;
showBlockInfo: boolean;
currentAddress?: string;
}
const TxsTableItem = ({ tx, showBlockInfo, currentAddress }: Props) => {
const isOut = Boolean(currentAddress && currentAddress === tx.from.hash);
const isIn = Boolean(currentAddress && currentAddress === tx.to?.hash);
const addressFrom = ( const addressFrom = (
<Address> <Address>
<Tooltip label={ tx.from.implementation_name }> <Tooltip label={ tx.from.implementation_name }>
...@@ -110,8 +120,11 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo ...@@ -110,8 +120,11 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo
<Td> <Td>
{ addressFrom } { addressFrom }
</Td> </Td>
<Td> <Td px={ 0 }>
<Icon as={ rightArrowIcon } boxSize={ 6 } mr={ 2 } color="gray.500"/> { (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mr={ 2 }/> :
<Icon as={ rightArrowIcon } boxSize={ 6 } mx="6px" color="gray.500"/>
}
</Td> </Td>
<Td> <Td>
{ addressTo } { addressTo }
...@@ -121,6 +134,8 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo ...@@ -121,6 +134,8 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo
<Td colSpan={ 3 }> <Td colSpan={ 3 }>
<Box> <Box>
{ addressFrom } { addressFrom }
{ (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" my={ 2 }/> : (
<Icon <Icon
as={ rightArrowIcon } as={ rightArrowIcon }
boxSize={ 6 } boxSize={ 6 }
...@@ -129,6 +144,7 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo ...@@ -129,6 +144,7 @@ const TxsTableItem = ({ tx, showBlockInfo }: {tx: Transaction; showBlockInfo: bo
color="gray.500" color="gray.500"
transform="rotate(90deg)" transform="rotate(90deg)"
/> />
) }
{ addressTo } { addressTo }
</Box> </Box>
</Td> </Td>
......
...@@ -4,14 +4,13 @@ import { ...@@ -4,14 +4,13 @@ import {
Text, Text,
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form'; import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import type { WatchlistErrors } from 'types/api/account'; import type { WatchlistErrors } from 'types/api/account';
import type { TWatchlistItem } from 'types/client/account'; import type { TWatchlistItem } from 'types/client/account';
import { QueryKeys } from 'types/client/accountQueries';
import getErrorMessage from 'lib/getErrorMessage'; import getErrorMessage from 'lib/getErrorMessage';
import type { ErrorType } from 'lib/hooks/useFetch'; import type { ErrorType } from 'lib/hooks/useFetch';
...@@ -29,9 +28,10 @@ const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const; ...@@ -29,9 +28,10 @@ const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const;
const TAG_MAX_LENGTH = 35; const TAG_MAX_LENGTH = 35;
type Props = { type Props = {
data?: TWatchlistItem; data?: Partial<TWatchlistItem>;
onClose: () => void; onSuccess: () => Promise<void>;
setAlertVisible: (isAlertVisible: boolean) => void; setAlertVisible: (isAlertVisible: boolean) => void;
isAdd: boolean;
} }
type Inputs = { type Inputs = {
...@@ -62,12 +62,12 @@ type Checkboxes = 'notification' | ...@@ -62,12 +62,12 @@ type Checkboxes = 'notification' |
'notification_settings.ERC-721.outcoming' | 'notification_settings.ERC-721.outcoming' |
'notification_settings.ERC-721.incoming'; 'notification_settings.ERC-721.incoming';
const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { const AddressForm: React.FC<Props> = ({ data, onSuccess, setAlertVisible, isAdd }) => {
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const formBackgroundColor = useColorModeValue('white', 'gray.900'); const formBackgroundColor = useColorModeValue('white', 'gray.900');
let notificationsDefault = {} as Inputs['notification_settings']; let notificationsDefault = {} as Inputs['notification_settings'];
if (!data) { if (!data?.notification_settings) {
NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: true, outcoming: true }); NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: true, outcoming: true });
} else { } else {
notificationsDefault = data.notification_settings; notificationsDefault = data.notification_settings;
...@@ -77,13 +77,12 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -77,13 +77,12 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
defaultValues: { defaultValues: {
address: data?.address_hash || '', address: data?.address_hash || '',
tag: data?.name || '', tag: data?.name || '',
notification: data ? data.notification_methods.email : true, notification: data?.notification_methods ? data.notification_methods.email : true,
notification_settings: notificationsDefault, notification_settings: notificationsDefault,
}, },
mode: 'onTouched', mode: 'onTouched',
}); });
const queryClient = useQueryClient();
const fetch = useFetch(); const fetch = useFetch();
function updateWatchlist(formData: Inputs) { function updateWatchlist(formData: Inputs) {
...@@ -95,7 +94,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -95,7 +94,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
email: formData.notification, email: formData.notification,
}, },
}; };
if (data) { if (!isAdd && data) {
// edit address // edit address
return fetch<TWatchlistItem, WatchlistErrors>(`/node-api/account/watchlist/${ data.id }`, { method: 'PUT', body }); return fetch<TWatchlistItem, WatchlistErrors>(`/node-api/account/watchlist/${ data.id }`, { method: 'PUT', body });
...@@ -106,11 +105,9 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -106,11 +105,9 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
} }
const { mutate } = useMutation(updateWatchlist, { const { mutate } = useMutation(updateWatchlist, {
onSuccess: () => { onSuccess: async() => {
queryClient.refetchQueries([ QueryKeys.watchlist ]).then(() => { await onSuccess();
onClose();
setPending(false); setPending(false);
});
}, },
onError: (e: ErrorType<WatchlistErrors>) => { onError: (e: ErrorType<WatchlistErrors>) => {
setPending(false); setPending(false);
...@@ -193,7 +190,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -193,7 +190,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
isLoading={ pending } isLoading={ pending }
disabled={ !isValid || !isDirty } disabled={ !isValid || !isDirty }
> >
{ data ? 'Save changes' : 'Add address' } { !isAdd ? 'Save changes' : 'Add address' }
</Button> </Button>
</Box> </Box>
</form> </form>
......
...@@ -7,20 +7,22 @@ import FormModal from 'ui/shared/FormModal'; ...@@ -7,20 +7,22 @@ import FormModal from 'ui/shared/FormModal';
import AddressForm from './AddressForm'; import AddressForm from './AddressForm';
type Props = { type Props = {
isAdd: boolean;
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
data?: TWatchlistItem; onSuccess: () => Promise<void>;
data?: Partial<TWatchlistItem>;
} }
const AddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { const AddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data, isAdd }) => {
const title = data ? 'Edit watch list address' : 'New address to watch list'; const title = !isAdd ? 'Edit watch list address' : 'New address to watch list';
const text = !data ? 'An email notification can be sent to you when an address on your watch list sends or receives any transactions.' : ''; const text = isAdd ? 'An email notification can be sent to you when an address on your watch list sends or receives any transactions.' : '';
const [ isAlertVisible, setAlertVisible ] = useState(false); const [ isAlertVisible, setAlertVisible ] = useState(false);
const renderForm = useCallback(() => { const renderForm = useCallback(() => {
return <AddressForm data={ data } onClose={ onClose } setAlertVisible={ setAlertVisible }/>; return <AddressForm data={ data } onSuccess={ onSuccess } setAlertVisible={ setAlertVisible } isAdd={ isAdd }/>;
}, [ data, onClose ]); }, [ data, isAdd, onSuccess ]);
return ( return (
<FormModal<TWatchlistItem> <FormModal<TWatchlistItem>
......
import { Text } from '@chakra-ui/react'; import { Text } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { TWatchlistItem, TWatchlist } from 'types/client/account'; import type { TWatchlistItem } from 'types/client/account';
import { QueryKeys } from 'types/client/accountQueries';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -12,11 +10,11 @@ import DeleteModal from 'ui/shared/DeleteModal'; ...@@ -12,11 +10,11 @@ import DeleteModal from 'ui/shared/DeleteModal';
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
data: TWatchlistItem; onSuccess: () => Promise<void>;
data: Pick<TWatchlistItem, 'address_hash' | 'id'>;
} }
const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, onSuccess, data }) => {
const queryClient = useQueryClient();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const fetch = useFetch(); const fetch = useFetch();
...@@ -24,12 +22,6 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { ...@@ -24,12 +22,6 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
return fetch(`/node-api/account/watchlist/${ data?.id }`, { method: 'DELETE' }); return fetch(`/node-api/account/watchlist/${ data?.id }`, { method: 'DELETE' });
}, [ data?.id, fetch ]); }, [ data?.id, fetch ]);
const onSuccess = useCallback(async() => {
queryClient.setQueryData([ QueryKeys.watchlist ], (prevData: TWatchlist | undefined) => {
return prevData?.filter((item) => item.id !== data.id);
});
}, [ data, queryClient ]);
const address = data?.address_hash; const address = data?.address_hash;
const renderModalContent = useCallback(() => { const renderModalContent = useCallback(() => {
......
...@@ -1076,734 +1076,751 @@ ...@@ -1076,734 +1076,751 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@chakra-ui/accordion@2.0.12": "@chakra-ui/accordion@2.1.4":
version "2.0.12" version "2.1.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-2.0.12.tgz#dd260fbecb639748314f440c89052ed45006c585" resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-2.1.4.tgz#a3eca38f8e52d5a5f4b9528fb9d269dcdcb035ac"
integrity sha512-O3qq8mILo1QODjCGr2xwxC5LNFakBoMzTjEgpvpIMynxWc/1RKfGuFLis3IDfpHIicXmBTK6sNiZXewmna88CQ== integrity sha512-PQFW6kr+Bdru0DjKA8akC4BAz1VAJisLgo4TsJwjPO2gTS0zr99C+3bBs9uoDnjSJAf18/Q5zdXv11adA8n2XA==
dependencies: dependencies:
"@chakra-ui/descendant" "3.0.9" "@chakra-ui/descendant" "3.0.11"
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-controllable-state" "2.0.3" "@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/transition" "2.0.9" "@chakra-ui/transition" "2.0.12"
"@chakra-ui/alert@2.0.9": "@chakra-ui/alert@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-2.0.9.tgz#25e88c105e095def374c9fe1e3c8a3f6fab08f6c" resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-2.0.13.tgz#11d48346e501988074affe12a448add1a6060296"
integrity sha512-hFRIh6ZzQJ0sAESRym15mW/mcZE/yu4z6lFtdToBhpfSlhZLuE7gDdOTxqGkg417hY//48NiNXOCoQ2dUUuHKw== integrity sha512-7LqPv6EUBte4XM/Q2qBFIT5o4BC0dSlni9BHOH2BgAc5B1NF+pBAMDTUH7JNBiN7RHTV7EHAIWDziiX/NK28+Q==
dependencies: dependencies:
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/spinner" "2.0.9" "@chakra-ui/spinner" "2.0.11"
"@chakra-ui/anatomy@2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/anatomy/-/anatomy-2.0.6.tgz#83164841d27eaa271ffa747534519bcd323c312f"
integrity sha512-Vgop2FFdhVtX7BydjZdJWZAWy+DdXBU1IMaBppz6COaH+/7OXxoI2ec2bs17ehJyBO0M+ud3OLj5UCFQ79YsoQ==
"@chakra-ui/anatomy@2.0.7": "@chakra-ui/anatomy@2.1.0":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@chakra-ui/anatomy/-/anatomy-2.0.7.tgz#33e60c7c4d6e5f949f6f8308249dc571f84ead1e"
integrity sha512-vzcB2gcsGCxhrKbldQQV6LnBPys4eSSsH2UA2mLsT+J3WlXw0aodZw0eE/nH7yLxe4zaQ4Gnc0KjkFW4EWNKSg==
"@chakra-ui/avatar@2.1.0":
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/@chakra-ui/avatar/-/avatar-2.1.0.tgz#009b4e126c58ef0183618cfbfb29f8e7e3357ee9" resolved "https://registry.yarnpkg.com/@chakra-ui/anatomy/-/anatomy-2.1.0.tgz#8aeb9b753f0412f262743adf68519dfa85120b3e"
integrity sha512-SRQeH6NNvIBgUc4OsO14ypvcn8I66ndw7r4piIkm+R2zqbYnrzpp1d2zNPNHkChc4xQY71/GenenYO5Fhsi2DA== integrity sha512-E3jMPGqKuGTbt7mKtc8g/MOOenw2c4wqRC1vOypyFgmC8wsewdY+DJJNENF3atXAK7p5VMBKQfZ7ipNlHnDAwA==
"@chakra-ui/avatar@2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@chakra-ui/avatar/-/avatar-2.2.1.tgz#3946d8c3b1d49dc425aa80f22d2f53661395e394"
integrity sha512-sgiogfLM8vas8QJTt7AJI4XxNXYdViCWj+xYJwyOwUN93dWKImqqx3O2ihCXoXTIqQWg1rcEgoJ5CxCg6rQaQQ==
dependencies: dependencies:
"@chakra-ui/image" "2.0.10" "@chakra-ui/image" "2.0.12"
"@chakra-ui/react-children-utils" "2.0.1" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/breadcrumb@2.0.9": "@chakra-ui/breadcrumb@2.1.1":
version "2.0.9" version "2.1.1"
resolved "https://registry.yarnpkg.com/@chakra-ui/breadcrumb/-/breadcrumb-2.0.9.tgz#357e4e2a50cdad87c0b3b59656aafa85671e6142" resolved "https://registry.yarnpkg.com/@chakra-ui/breadcrumb/-/breadcrumb-2.1.1.tgz#e8a682a4909cf8ee5771f7b287524df2be383b8a"
integrity sha512-cc3WbxrJNRUph4v45qCdcIKJI0xECeV9VikQNIactBB+iexN4d+5P66xZABAkD8wWGmyH5KuSZcd9sFYNmC13w== integrity sha512-OSa+F9qJ1xmF0zVxC1GU46OWbbhGf0kurHioSB729d+tRw/OMzmqrrfCJ7KVUUN8NEnTZXT5FIgokMvHGEt+Hg==
dependencies: dependencies:
"@chakra-ui/react-children-utils" "2.0.1" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/breakpoint-utils@2.0.3": "@chakra-ui/breakpoint-utils@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.3.tgz#af7f7603f31a7d8d0166307a47e88cf5902401b4" resolved "https://registry.yarnpkg.com/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.5.tgz#55b571038b66e9f6d41633c102ea904c679dac5c"
integrity sha512-smi41ZtaiPw4mXaCgicyAh5M45Drt20wypThP+qQUT2CQ51UFZhYlItRA2lCXKQ9QB83POcHPC/oAwIsNOAfTg== integrity sha512-8uhrckMwoR/powlAhxiFZPM0s8vn0B2yEyEaRcwpy5NmRAJSTEotC2WkSyQl/Cjysx9scredumB5g+fBX7IqGQ==
"@chakra-ui/button@2.0.9": "@chakra-ui/button@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/button/-/button-2.0.9.tgz#f005f98bb5f1d5673a244957e6b3e2396acdf395" resolved "https://registry.yarnpkg.com/@chakra-ui/button/-/button-2.0.13.tgz#5db6aa3425a6bebc2102cd9f58e434d5508dd999"
integrity sha512-4BuDBiBlChHW1rQ9iod9MKs87AY3IyvZQwjV3DZTU4IG0KcDDfLQf++jj4dkg9Ttu+pIWhwF42pzA40JxW1oNg== integrity sha512-T9W/zHpHZVcbx/BMg0JIXCgRycut/eYoTYee/E+eBxyPCH45n308AsYU2bZ8TgZxUwbYNRgMp4qRL/KHUQDv5g==
dependencies: dependencies:
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/spinner" "2.0.9" "@chakra-ui/spinner" "2.0.11"
"@chakra-ui/checkbox@2.1.8": "@chakra-ui/card@2.1.2":
version "2.1.8" version "2.1.2"
resolved "https://registry.yarnpkg.com/@chakra-ui/checkbox/-/checkbox-2.1.8.tgz#d04a9a65494cf22e8bcfafa161bb7185d92dc13b" resolved "https://registry.yarnpkg.com/@chakra-ui/card/-/card-2.1.2.tgz#6a8b58d365557fd33da8e257e207fd8ba3640919"
integrity sha512-HhRs3nwTFoIE/UpX4N2AZxxW39Xm/Vw01HjwP/59X60kdKs3RBXlm52cODkfUDfveyT9o5ezLhU/jRf0qA909Q== integrity sha512-zLfrLe7NP14xWgzS+vVz07yapZNOTm3W0Gh9RCwwoQz7l4FNFNjGPmbktQGGp3fLqDUk9n9ugdFCNmxEcQ8wXA==
dependencies:
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/checkbox@2.2.5":
"@chakra-ui/react-context" "2.0.3" version "2.2.5"
"@chakra-ui/react-types" "2.0.3" resolved "https://registry.yarnpkg.com/@chakra-ui/checkbox/-/checkbox-2.2.5.tgz#ce1c409647d11bf947ff0316bc397bc7cf25316c"
"@chakra-ui/react-use-callback-ref" "2.0.3" integrity sha512-7fNH+Q2nB2uMSnYAPtYxnuwZ1MOJqblZHa/ScfZ/fjiPDyEae1m068ZP/l9yJ5zlawYMTkp83m/JVcu5QFYurA==
"@chakra-ui/react-use-controllable-state" "2.0.3" dependencies:
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/react-use-safe-layout-effect" "2.0.1" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-update-effect" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/visually-hidden" "2.0.9" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/visually-hidden" "2.0.13"
"@zag-js/focus-visible" "0.1.0" "@zag-js/focus-visible" "0.1.0"
"@chakra-ui/clickable@2.0.9": "@chakra-ui/clickable@2.0.11":
version "2.0.9" version "2.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/clickable/-/clickable-2.0.9.tgz#c06486d36f4a4cb517ea75176e05021dfde117cd" resolved "https://registry.yarnpkg.com/@chakra-ui/clickable/-/clickable-2.0.11.tgz#d0afcdb40ed1b1ceeabb4ac3e9f2f51fd3cbdac7"
integrity sha512-tGXYM6M6I954fif98QkNu5M76oBZmksCTj2mILOan9/BSimpFpu06aPGX3ZIkNsz300nIObn0FdtMvKpIEQueA== integrity sha512-5Y2dl5cxNgOxHbjxyxsL6Vdze4wUUvwsMCCW3kXwgz2OUI2y5UsBZNcvhNJx3RchJEd0fylMKiKoKmnZMHN2aw==
dependencies: dependencies:
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/close-button@2.0.9": "@chakra-ui/close-button@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-2.0.9.tgz#dab2d66c7a240c4d3d150e370980709336a4a266" resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-2.0.13.tgz#c549d682c66f3e08b1f37e98a83ebe0421846496"
integrity sha512-0RI/zLR+/mycGbYCCwDAc9hAVG7IIVmdikmo1ET7+rYip4TN94aWR0hA4dYtWqqghG1oW/pYQ9Yja6fEY90V5w== integrity sha512-ZI/3p84FPlW0xoDCZYqsnIvR6bTc2d/TlhwyTHsDDxq9ZOWp9c2JicVn6WTdWGdshk8itnZZdG50IcnizGnimA==
dependencies: dependencies:
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/color-mode@2.1.7": "@chakra-ui/color-mode@2.1.10":
version "2.1.7" version "2.1.10"
resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-2.1.7.tgz#91c02e82e551c5448081e4934efeddb10bb732c5" resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-2.1.10.tgz#8d446550af80cf01a2ccd7470861cb0180112049"
integrity sha512-GAoKJzVRQeuEfCa2i0BZdMwxuOoaGknU3+5wgvLuaSpwlov4OyqpjKMRdSdpjr4IFiqqHK47dsr3H4LQsbO+9w== integrity sha512-aUPouOUPn7IPm1v00/9AIkRuNrkCwJlbjVL1kJzLzxijYjbHvEHPxntITt+JWjtXPT8xdOq6mexLYCOGA67JwQ==
dependencies: dependencies:
"@chakra-ui/react-use-safe-layout-effect" "2.0.1" "@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/control-box@2.0.9": "@chakra-ui/control-box@2.0.11":
version "2.0.9" version "2.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/control-box/-/control-box-2.0.9.tgz#b3cd98ceb1ce683c00445ab7469e65ba3d90c3cc" resolved "https://registry.yarnpkg.com/@chakra-ui/control-box/-/control-box-2.0.11.tgz#b2deec368fc83f6675964785f823e4c0c1f5d4ac"
integrity sha512-/viS9OBah1wCLNZbgfwkoQOnVRUYgp8Gypjqk9QNQwnNdFUTEgWc1RWN+1RYO85esJzHLkA2hZFIrYu1TZeZ6g== integrity sha512-UJb4vqq+/FPuwTCuaPeHa2lwtk6u7eFvLuwDCST2e/sBWGJC1R+1/Il5pHccnWs09FWxyZ9v/Oxkg/CG3jZR4Q==
"@chakra-ui/counter@2.0.9": "@chakra-ui/counter@2.0.11":
version "2.0.9" version "2.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/counter/-/counter-2.0.9.tgz#b1b7c74c4e5d1ac506d699d93da57d535370a702" resolved "https://registry.yarnpkg.com/@chakra-ui/counter/-/counter-2.0.11.tgz#b49aa76423e5f4a4a8e717750c190fa5050a3dca"
integrity sha512-LuqtpyxCOZM19gAmV0vtVeaFd9ccPmEjoGJQ0NoO8CFheltgLC/7m/8YpDbgWiG4+BAkTUfIG+5nLg5hwvvQxw== integrity sha512-1YRt/jom+m3iWw9J9trcM6rAHDvD4lwThiO9raxUK7BRsYUhnPZvsMpcXU1Moax218C4rRpbI9KfPLaig0m1xQ==
dependencies: dependencies:
"@chakra-ui/number-utils" "2.0.3" "@chakra-ui/number-utils" "2.0.5"
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/css-reset@2.0.7": "@chakra-ui/css-reset@2.0.10":
version "2.0.7" version "2.0.10"
resolved "https://registry.yarnpkg.com/@chakra-ui/css-reset/-/css-reset-2.0.7.tgz#28f4284c36230e30892dc3f2de2464aaacc4f623" resolved "https://registry.yarnpkg.com/@chakra-ui/css-reset/-/css-reset-2.0.10.tgz#cb6cd97ee38f8069789f08c31a828bf3a7e339ea"
integrity sha512-ztGdFQ6U1hX2k6a3HZ8D3A/dZWVxlGe2F5mvUrRU554mFWBYmsq0ydZ7UBEPlykv9NoCz4nN8VCkIxcKJ3p29Q== integrity sha512-FwHOfw2P4ckbpSahDZef2KoxcvHPUg09jlicWdp24/MjdsOO5PAB/apm2UBvQflY4WAJyOqYaOdnXFlR6nF4cQ==
"@chakra-ui/descendant@3.0.9": "@chakra-ui/descendant@3.0.11":
version "3.0.9" version "3.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/descendant/-/descendant-3.0.9.tgz#6574a1ce00067c49a070c5b005f8f1ca399006ea" resolved "https://registry.yarnpkg.com/@chakra-ui/descendant/-/descendant-3.0.11.tgz#cb8bca7b6e8915afc58cdb1444530a2d1b03efd3"
integrity sha512-30E5yMWvxgBx43PoI/67r9h9OhbpDfLb/MLOCjtEwebSbD0V5+fmnmCoUELScQbhozQVjA9t195X6UP0VQWj8w== integrity sha512-sNLI6NS6uUgrvYS6Imhoc1YlI6bck6pfxMBJcnXVSfdIjD6XjCmeY2YgzrtDS+o+J8bB3YJeIAG/vsVy5USE5Q==
dependencies: dependencies:
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/dom-utils@2.0.1": "@chakra-ui/dom-utils@2.0.4":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@chakra-ui/dom-utils/-/dom-utils-2.0.1.tgz#3061819ac365f5947423d63a5fcc26a281bbb5c2"
integrity sha512-sbob9AHQq1+KIQ3XKslafislwtC8pYcpwM0S1SLzgyZumHRwhDimKwdi4MtRQfOCenub0E3diRjp4RpGRL0JuQ==
"@chakra-ui/editable@2.0.9":
version "2.0.9"
resolved "https://registry.yarnpkg.com/@chakra-ui/editable/-/editable-2.0.9.tgz#a31d2e1c176c0817574e98a8567314c1a74444ab"
integrity sha512-s5F3UMR09s6ga3eVhw0UBMGmegtxg6jCp29VLqaEwP5BuWIEOjcJz358gTlnFr3dhvb31e3rcr+B1XiYv4wxqg==
dependencies:
"@chakra-ui/react-context" "2.0.3"
"@chakra-ui/react-types" "2.0.3"
"@chakra-ui/react-use-controllable-state" "2.0.3"
"@chakra-ui/react-use-focus-on-pointer-down" "2.0.1"
"@chakra-ui/react-use-merge-refs" "2.0.3"
"@chakra-ui/react-use-safe-layout-effect" "2.0.1"
"@chakra-ui/react-use-update-effect" "2.0.3"
"@chakra-ui/shared-utils" "2.0.1"
"@chakra-ui/event-utils@2.0.4":
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/event-utils/-/event-utils-2.0.4.tgz#eeb3eb4f37c3828955dbbc182ea43a8a3238a599" resolved "https://registry.yarnpkg.com/@chakra-ui/dom-utils/-/dom-utils-2.0.4.tgz#367fffecbd287e16836e093d4030dc6e3785d402"
integrity sha512-J2YgAM5Dw9hMkwfMsWhsiAG848GfTMxNclUIUcgV9RQhLEs0eTFhelzNiKVOMA3vBxlT6lOARuRun/ESiFZgGg== integrity sha512-P936+WKinz5fgHzfwiUQjE/t7NC8bU89Tceim4tbn8CIm/9b+CsHX64eNw4vyJqRwt78TXQK7aGBIbS18R0q5Q==
"@chakra-ui/focus-lock@2.0.10": "@chakra-ui/editable@2.0.16":
version "2.0.10" version "2.0.16"
resolved "https://registry.yarnpkg.com/@chakra-ui/focus-lock/-/focus-lock-2.0.10.tgz#8f17d212786bd2977afc07f72b520aeae30b9434" resolved "https://registry.yarnpkg.com/@chakra-ui/editable/-/editable-2.0.16.tgz#8a45ff26d77f06841ea986484d71ede312b4c22e"
integrity sha512-LeRZYzwfJp0eq84oO8e1pC2qC8v8fJw/P4nYDrCDjuJU753DV6nVjp5MKMRqbkp+6IAElPc+ojy/sp2a9GCocw== integrity sha512-kIFPufzIlViNv7qi2PxxWWBvjLb+3IP5hUGmqOA9qcYz5TAdqblQqDClm0iajlIDNUFWnS4h056o8jKsQ42a5A==
dependencies:
"@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-focus-on-pointer-down" "2.0.4"
"@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/shared-utils" "2.0.3"
"@chakra-ui/event-utils@2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/event-utils/-/event-utils-2.0.6.tgz#5e04d68ea070ef52ce212c2a99be9afcc015cfaf"
integrity sha512-ZIoqUbgJ5TcCbZRchMv4n7rOl1JL04doMebED88LO5mux36iVP9er/nnOY4Oke1bANKKURMrQf5VTT9hoYeA7A==
"@chakra-ui/focus-lock@2.0.13":
version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/focus-lock/-/focus-lock-2.0.13.tgz#19d6ca35555965a9aaa241b991a67bbc875ee53d"
integrity sha512-AVSJt+3Ukia/m9TCZZgyWvTY7pw88jArivWVJ2gySGYYIs6z/FJMnlwbCVldV2afS0g3cYaii7aARb/WrlG34Q==
dependencies: dependencies:
"@chakra-ui/dom-utils" "2.0.1" "@chakra-ui/dom-utils" "2.0.4"
react-focus-lock "^2.9.1" react-focus-lock "^2.9.1"
"@chakra-ui/form-control@2.0.9": "@chakra-ui/form-control@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-2.0.9.tgz#10678857e6586e7d1be0a34c8d045c6484c1180b" resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-2.0.13.tgz#51831f981a2e937b0258b4fd2dd4ceacda03c01a"
integrity sha512-P8Tr45z/XSAa1m6uAma0eKf1h7Ltg2sLj2jK5YhaXJER9VUUY18iGe96D4JrAXlgEWDhTyWMb63nB+eYO1tKtw== integrity sha512-J964JlgrxP+LP3kYmLk1ttbl73u6ghT+JQDjEjkEUc8lSS9Iv4u9XkRDQHuz2t2y0KHjQdH12PUfUfBqcITbYw==
dependencies: dependencies:
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/hooks@2.0.9": "@chakra-ui/hooks@2.1.2":
version "2.0.9" version "2.1.2"
resolved "https://registry.yarnpkg.com/@chakra-ui/hooks/-/hooks-2.0.9.tgz#1f6d6157968a60dd9678112763d8f5fd3bdeffad" resolved "https://registry.yarnpkg.com/@chakra-ui/hooks/-/hooks-2.1.2.tgz#1e413f6624e97b854569e8a19846c9162a4ec153"
integrity sha512-0JRgEPtsBaXr9nQW1xEKlWGA7WwFbLNqac7fQXp9zQvoHOWTfNJkK/NJaVBvyFPgfTLxy37WKHooVSwNG/Lwmg== integrity sha512-/vDBOqqnho9q++lay0ZcvnH8VuE0wT2OkZj+qDwFwjiHAtGPVxHCSpu9KC8BIHME5TlWjyO6riVyUCb2e2ip6w==
dependencies: dependencies:
"@chakra-ui/react-utils" "2.0.6" "@chakra-ui/react-utils" "2.0.9"
"@chakra-ui/utils" "2.0.9" "@chakra-ui/utils" "2.0.12"
compute-scroll-into-view "1.0.14" compute-scroll-into-view "1.0.14"
copy-to-clipboard "3.3.1" copy-to-clipboard "3.3.1"
"@chakra-ui/icon@3.0.9": "@chakra-ui/icon@3.0.13":
version "3.0.9" version "3.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-3.0.9.tgz#ba127d9eefd727f62e9bce07a23eca39ae506744" resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-3.0.13.tgz#1782f8bd81946eabb39d4dde9eccebba1e5571ba"
integrity sha512-P2Pwm/za6m1W1oqL2kGHH6XrrymsBjqYAFwOW2lB5Q6mI1e+RYe/iMxDoPSLHMYhqdfH7vyib/ffE3Vv3a5oTA== integrity sha512-RaDLC4psd8qyInY2RX4AlYRfpLBNw3VsMih17BFf8EESVhBXNJcYy7Q9eMV/K4NvZfZT42vuVqGVNFmkG89lBQ==
dependencies: dependencies:
"@chakra-ui/shared-utils" "2.0.1" "@chakra-ui/shared-utils" "2.0.3"
"@chakra-ui/image@2.0.10": "@chakra-ui/image@2.0.12":
version "2.0.10" version "2.0.12"
resolved "https://registry.yarnpkg.com/@chakra-ui/image/-/image-2.0.10.tgz#712c0e1c579d959225bd8316d8d8f66cbeb95bb8" resolved "https://registry.yarnpkg.com/@chakra-ui/image/-/image-2.0.12.tgz#e90b1d2a5f87fff90b1ef86ca75bfe6b44ac545d"
integrity sha512-Atc1bdog4V5xv7IbpF2F2UkKWfgG/TD74cIac09JuSpQcYyh7lrJ7iVvhTkeP+LDdCs+QCD7SnTUM4Y0ZlaHbA== integrity sha512-uclFhs0+wq2qujGu8Wk4eEWITA3iZZQTitGiFSEkO9Ws5VUH+Gqtn3mUilH0orubrI5srJsXAmjVTuVwge1KJQ==
dependencies: dependencies:
"@chakra-ui/react-use-safe-layout-effect" "2.0.1" "@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/input@2.0.9": "@chakra-ui/input@2.0.14":
version "2.0.9" version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-2.0.9.tgz#905b82ed647a20080a25a6a7e6740e3bb65586c1" resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-2.0.14.tgz#eec3d04834ab1ac7dab344e9bf14d779c4f4da31"
integrity sha512-6MKydxTyF7JV7PtQHircQ5HBTd6Ik9Vn7p8fCLeAieT0TK8UQTxMWZVPminS7TRWMutrq8W99DcQOBlMz0cKrw== integrity sha512-CkSrUJeKWogOSt2pUf2vVv5s0bUVcAi4/XGj1JVCCfyIX6a6h1m8R69MShTPxPiQ0Mdebq5ATrW/aZQQXZzRGQ==
dependencies: dependencies:
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/object-utils" "2.0.3" "@chakra-ui/object-utils" "2.0.5"
"@chakra-ui/react-children-utils" "2.0.1" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/shared-utils" "2.0.1" "@chakra-ui/shared-utils" "2.0.3"
"@chakra-ui/layout@2.1.6": "@chakra-ui/layout@2.1.11":
version "2.1.6" version "2.1.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-2.1.6.tgz#3dfdd8b3f08d9ff34fc923d44ebe4bc86291b889" resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-2.1.11.tgz#6b0005dd897a901f2fded99c19fe47f60db80cb3"
integrity sha512-QDNaVu44UI46c+YlSF1KrzJkiwua0UtRXNTnR3jBE1uzcuqRow7xgr3E60dLphY2cPFqAljfQZUNlP3sgvCLww== integrity sha512-UP19V8EeI/DEODbWrZlqC0sg248bpFaWpMiM/+g9Bsxs9aof3yexpMD/7gb0yrfbIrkdvSBrcQeqxXGzbfoopw==
dependencies: dependencies:
"@chakra-ui/breakpoint-utils" "2.0.3" "@chakra-ui/breakpoint-utils" "2.0.5"
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/object-utils" "2.0.3" "@chakra-ui/object-utils" "2.0.5"
"@chakra-ui/react-children-utils" "2.0.1" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/shared-utils" "2.0.1" "@chakra-ui/shared-utils" "2.0.3"
"@chakra-ui/lazy-utils@2.0.1": "@chakra-ui/lazy-utils@2.0.3":
version "2.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/lazy-utils/-/lazy-utils-2.0.1.tgz#6814836552028fa0823563ce3d39d22bccb203e1" resolved "https://registry.yarnpkg.com/@chakra-ui/lazy-utils/-/lazy-utils-2.0.3.tgz#5ba459a2541ad0c98cd98b20a8054664c129e9b4"
integrity sha512-986YjYq+hEzHDLZiqYlYbdqfiKdC3h2g896Eoe5K2UXtAVxqZI3UOnMH781X6N1R7rGJWquskzG681qFigW/BA== integrity sha512-SQ5I5rJrcHpVUcEftHLOh8UyeY+06R8Gv3k2RjcpvM6mb2Gktlz/4xl2GcUh3LWydgGQDW/7Rse5rQhKWgzmcg==
"@chakra-ui/live-region@2.0.9": "@chakra-ui/live-region@2.0.11":
version "2.0.9" version "2.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/live-region/-/live-region-2.0.9.tgz#f26cf1b96df51515cd3a0897f9516f8b5f6bbfec" resolved "https://registry.yarnpkg.com/@chakra-ui/live-region/-/live-region-2.0.11.tgz#1008c5b629aa4120e5158be53f13d8d34bc2d71a"
integrity sha512-ilbo/C5wcUoSHDU5owFPQP3KsabPYGzDEbwV+Z76BlyNdFN2PD0j13RGEH+sBNNZ3HzLyyuuc1YmkVcJi7ycQg== integrity sha512-ltObaKQekP75GCCbN+vt1/mGABSCaRdQELmotHTBc5AioA3iyCDHH69ev+frzEwLvKFqo+RomAdAAgqBIMJ02Q==
"@chakra-ui/media-query@3.2.5": "@chakra-ui/media-query@3.2.8":
version "3.2.5" version "3.2.8"
resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-3.2.5.tgz#c0b9dc4bc6245d9abddcbe17693e40bf5dfe34f8" resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-3.2.8.tgz#7d5feccb7ac52891426c060dd2ed1df37420956d"
integrity sha512-V+Dngi/r7u/uj7JhsZerM1RI597Oo4wED2ojNfclnnEVb/IoqktiuFy6RQgbo3HmE7M/E5B1i4yYzt7tQJhXlg== integrity sha512-djmEg/eJ5Qrjn7SArTqjsvlwF6mNeMuiawrTwnU+0EKq9Pq/wVSb7VaIhxdQYJLA/DbRhE/KPMogw1LNVKa4Rw==
dependencies: dependencies:
"@chakra-ui/breakpoint-utils" "2.0.3" "@chakra-ui/breakpoint-utils" "2.0.5"
"@chakra-ui/react-env" "2.0.9" "@chakra-ui/react-env" "2.0.11"
"@chakra-ui/menu@2.0.13": "@chakra-ui/menu@2.1.5":
version "2.0.13" version "2.1.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-2.0.13.tgz#3ac5f448efc894045769c606ebe1376051556be9" resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-2.1.5.tgz#73b6db93d6dec9d04ab7c2630a7984e3180fd769"
integrity sha512-XZYoq9k/txAELUgn5OokyxfXEpVZwBueVYXiT9ji0XvMuzXVxeHd40klJEkiJUctNsOahZf10t5yxlT4B00pwA== integrity sha512-2UusrQtxHcqcO9n/0YobNN3RJC8yAZU6oJbRPuvsQ9IL89scEWCTIxXEYrnIjeh/5zikcSEDGo9zM9Udg/XcsA==
dependencies: dependencies:
"@chakra-ui/clickable" "2.0.9" "@chakra-ui/clickable" "2.0.11"
"@chakra-ui/descendant" "3.0.9" "@chakra-ui/descendant" "3.0.11"
"@chakra-ui/lazy-utils" "2.0.1" "@chakra-ui/lazy-utils" "2.0.3"
"@chakra-ui/popper" "3.0.7" "@chakra-ui/popper" "3.0.10"
"@chakra-ui/react-children-utils" "2.0.1" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-animation-state" "2.0.3" "@chakra-ui/react-use-animation-state" "2.0.6"
"@chakra-ui/react-use-controllable-state" "2.0.3" "@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-disclosure" "2.0.3" "@chakra-ui/react-use-disclosure" "2.0.6"
"@chakra-ui/react-use-focus-effect" "2.0.3" "@chakra-ui/react-use-focus-effect" "2.0.7"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-outside-click" "2.0.3" "@chakra-ui/react-use-outside-click" "2.0.5"
"@chakra-ui/react-use-update-effect" "2.0.3" "@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/transition" "2.0.9" "@chakra-ui/transition" "2.0.12"
"@chakra-ui/modal@2.1.7": "@chakra-ui/modal@2.2.5":
version "2.1.7" version "2.2.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-2.1.7.tgz#dba55bddd407689f4c2bba886b03a5355578a20d" resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-2.2.5.tgz#adcbf06c29d853b5fa17fec3a91b8096eecffe6b"
integrity sha512-A+CbvhQYpmLH3SrqJ1wJysUCGm0mNoSDxRjP4wX98j56nMTDAsMYlzttpuLmKaSzvbJ7uEQDLtQV8lZjB0gUuw== integrity sha512-QIoN89bT5wnR71wxZFHt7vsS65yF9WCfIwDtFk8ifxJORPi/UkLMwBpjTV2Jfsxd22W6Oo2VOpRR0a5WFeK+jA==
dependencies: dependencies:
"@chakra-ui/close-button" "2.0.9" "@chakra-ui/close-button" "2.0.13"
"@chakra-ui/focus-lock" "2.0.10" "@chakra-ui/focus-lock" "2.0.13"
"@chakra-ui/portal" "2.0.9" "@chakra-ui/portal" "2.0.12"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/transition" "2.0.9" "@chakra-ui/transition" "2.0.12"
aria-hidden "^1.1.1" aria-hidden "^1.1.1"
react-remove-scroll "^2.5.4" react-remove-scroll "^2.5.4"
"@chakra-ui/number-input@2.0.9": "@chakra-ui/number-input@2.0.14":
version "2.0.9" version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-2.0.9.tgz#c5ebfa0311f7586fb4c1e5f4284355b1d1f04383" resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-2.0.14.tgz#e6336228b9210f9543fe440bfc6478537810d59c"
integrity sha512-RsDzoNvSBZMgyXjN543AtQ2v99U1p/0xnGWZy4NCkgCDWMBn3kIXqSzQq5CB9Ot0MD8nnKF5VYdVdXWguXExEQ== integrity sha512-IARUAbP4pn1gP5fY2dK4wtbR3ONjzHgTjH4Zj3ErZvdu/yTURLaZmlb6UGHwgqjWLyioactZ/+n4Njj5CRjs8w==
dependencies: dependencies:
"@chakra-ui/counter" "2.0.9" "@chakra-ui/counter" "2.0.11"
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-event-listener" "2.0.3" "@chakra-ui/react-use-event-listener" "2.0.5"
"@chakra-ui/react-use-interval" "2.0.1" "@chakra-ui/react-use-interval" "2.0.3"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-safe-layout-effect" "2.0.1" "@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/react-use-update-effect" "2.0.3" "@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/number-utils@2.0.3": "@chakra-ui/number-utils@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/number-utils/-/number-utils-2.0.3.tgz#2cf1190647ac5a17c90baaf8176226a98eb3bfff" resolved "https://registry.yarnpkg.com/@chakra-ui/number-utils/-/number-utils-2.0.5.tgz#c7595fc919fca7c43fe172bfd6c5197c653ee572"
integrity sha512-oN03kYAUCCp/FNtpLr5mh+cvd/sRTzZWTBoFydmxc955psXq/X950gzs6o5kzoeFCpgXaxMmHAXQm3ReEK2NsQ== integrity sha512-Thhohnlqze0i5HBJO9xkfOPq1rv3ji/hNPf2xh1fh4hxrNzdm3HCkz0c6lyRQwGuVoeltEHysYZLH/uWLFTCSQ==
"@chakra-ui/object-utils@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/object-utils/-/object-utils-2.0.3.tgz#0bc8d1c7c452fe1ce8fcda439336e0392e867d7e"
integrity sha512-36prckrqTynVD/JTzyCr8OCWVOrMs/awZo3djVbIiNxRIcJ5iEwUVy26h3MWN4ENSopipBtxNfAwPNTLU5Si/g==
"@chakra-ui/pin-input@2.0.12": "@chakra-ui/object-utils@2.0.5":
version "2.0.12" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/pin-input/-/pin-input-2.0.12.tgz#1ff927fdc61433a7b9b4421ceafad5674299e91a" resolved "https://registry.yarnpkg.com/@chakra-ui/object-utils/-/object-utils-2.0.5.tgz#231602066ddb96ae91dcc7da243b97ad46972398"
integrity sha512-gaMRp5AFW+qAJCUj93V1WluuYBBZ/5A3Wy5q796g8Auvw7vufgkVtl6EBznwvtynZN8gJwbRFpMtJxQyXCkUiw== integrity sha512-/rIMoYI3c2uLtFIrnTFOPRAI8StUuu335WszqKM0KAW1lwG9H6uSbxqlpZT1Pxi/VQqZKfheGiMQOx5lfTmM/A==
dependencies:
"@chakra-ui/descendant" "3.0.9"
"@chakra-ui/react-children-utils" "2.0.1"
"@chakra-ui/react-context" "2.0.3"
"@chakra-ui/react-use-controllable-state" "2.0.3"
"@chakra-ui/react-use-merge-refs" "2.0.3"
"@chakra-ui/popover@2.0.9": "@chakra-ui/pin-input@2.0.16":
version "2.0.9" version "2.0.16"
resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-2.0.9.tgz#7f2df4cbbc3eee7440c311750e18ba00150f973f" resolved "https://registry.yarnpkg.com/@chakra-ui/pin-input/-/pin-input-2.0.16.tgz#d31a6e2bce85aa2d1351ccb4cd9bf7a5134d3fb9"
integrity sha512-+7tH4RVuheFQOyAZ5KT9x+qsLvz7rGuKaHtb0427+5bhUzLaSAghtr/afzOKHDwUVBwF2tTUNanR23ipW1fXDg== integrity sha512-51cioNYpBSgi9/jq6CrzoDvo8fpMwFXu3SaFRbKO47s9Dz/OAW0MpjyabTfSpwOv0xKZE+ayrYGJopCzZSWXPg==
dependencies: dependencies:
"@chakra-ui/close-button" "2.0.9" "@chakra-ui/descendant" "3.0.11"
"@chakra-ui/hooks" "2.0.9" "@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/lazy-utils" "2.0.1" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/popper" "3.0.7" "@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-types" "2.0.3"
"@chakra-ui/react-use-disclosure" "2.0.3" "@chakra-ui/popover@2.1.4":
"@chakra-ui/react-use-merge-refs" "2.0.3" version "2.1.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-2.1.4.tgz#c44df775875faabe09ef13ce32150a2631c768dd"
"@chakra-ui/popper@3.0.7": integrity sha512-NXVtyMxYzDKzzQph/+GFRSM3tEj3gNvlCX/xGRsCOt9I446zJ1InCd/boXQKAc813coEN9McSOjNWgo+NCBD+Q==
version "3.0.7" dependencies:
resolved "https://registry.yarnpkg.com/@chakra-ui/popper/-/popper-3.0.7.tgz#af3428bf5d64ad9372210a70181f69a9d79eefb2" "@chakra-ui/close-button" "2.0.13"
integrity sha512-xLYhuNsk1gOjymtek1ZdZlG21hmg2a7Iu2KsD9Hi7+aUxc2K5/XxX+/vyjjz8u4s0gmj83pTqnauQRynb/TCXA== "@chakra-ui/lazy-utils" "2.0.3"
dependencies: "@chakra-ui/popper" "3.0.10"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-animation-state" "2.0.6"
"@chakra-ui/react-use-disclosure" "2.0.6"
"@chakra-ui/react-use-focus-effect" "2.0.7"
"@chakra-ui/react-use-focus-on-pointer-down" "2.0.4"
"@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/popper@3.0.10":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@chakra-ui/popper/-/popper-3.0.10.tgz#5d382c36359615e349e679445eb58c139dbb4d4f"
integrity sha512-6LacbBGX0piHWY/DYxOGCTTFAoRGRHpGIRzTgfNy8jxw4f+rukaVudd4Pc2fwjCTdobJKM8nGNYIYNv9/Dmq9Q==
dependencies:
"@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.5"
"@popperjs/core" "^2.9.3" "@popperjs/core" "^2.9.3"
"@chakra-ui/portal@2.0.9": "@chakra-ui/portal@2.0.12":
version "2.0.9" version "2.0.12"
resolved "https://registry.yarnpkg.com/@chakra-ui/portal/-/portal-2.0.9.tgz#b427c383a9d602c5b52b21312b4b1c0ffecaf583" resolved "https://registry.yarnpkg.com/@chakra-ui/portal/-/portal-2.0.12.tgz#bba0dac00f5610efbf2f15b0e5e6e984135c82c2"
integrity sha512-9e9S0MLbkpofPGlyYA12jNYSdndugy6ylPi5pC9nr3/VqG2Kn+8VcBChAeXW8K4ms7WFc74rNX1pBY/UVwr4qg== integrity sha512-8D/1fFUdbJtzyGL5sCBIb4oyTnPG2v6rx/L/qbG43FcXDrongmzLj0+tJ//PbJr+5hxjXAWFUjpPvyx10pTN6Q==
dependencies: dependencies:
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-safe-layout-effect" "2.0.1" "@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/progress@2.0.10": "@chakra-ui/progress@2.1.2":
version "2.0.10" version "2.1.2"
resolved "https://registry.yarnpkg.com/@chakra-ui/progress/-/progress-2.0.10.tgz#9191ae9061ef08066d37c5cb8341fedc10214a29" resolved "https://registry.yarnpkg.com/@chakra-ui/progress/-/progress-2.1.2.tgz#dc3dcae7ddc94d18c3d7b64c2a84a83055038f4f"
integrity sha512-my0Pi3NG1PYhlvCav4fybg3gL5HBNe+7lO4PVdri4QHEyfJlrDeBWID+1GgqlpUWdTj3sOf7ysku+FEgkeOeSA== integrity sha512-ofhMWTqCxnm1NiP/zH4SV7EvOLogfX15MSMTNfGqZv6t8eSSeTn6oRRzsTSllJfSqDey7oZNCRbP7vDhvx9HtQ==
dependencies: dependencies:
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/provider@2.0.16": "@chakra-ui/provider@2.0.25":
version "2.0.16" version "2.0.25"
resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-2.0.16.tgz#a4afdb4c8f2050beb5d2b61db8971a145481c6f2" resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-2.0.25.tgz#6f27abcb8744a7d3c4ebd09409a9e00e0f90c566"
integrity sha512-4t/PmjJ7WXPPaPfoYgw8F1/rVtorZuvknugHfOZcOtAPGQmOPotSv28qjKpu/mCvc1GMGV0swMsvCeInYz7g0w== integrity sha512-9BQm9aqiUK/5xeYECpD9dfULd/BfxV4nncFGQNXFqZr0Nx54BALndeKdtId3j+36j9MwcKqjHgKyEpv4s+4vkQ==
dependencies: dependencies:
"@chakra-ui/css-reset" "2.0.7" "@chakra-ui/css-reset" "2.0.10"
"@chakra-ui/portal" "2.0.9" "@chakra-ui/portal" "2.0.12"
"@chakra-ui/react-env" "2.0.9" "@chakra-ui/react-env" "2.0.11"
"@chakra-ui/system" "2.2.9" "@chakra-ui/system" "2.3.4"
"@chakra-ui/utils" "2.0.9" "@chakra-ui/utils" "2.0.12"
"@chakra-ui/radio@2.0.10": "@chakra-ui/radio@2.0.14":
version "2.0.10" version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-2.0.10.tgz#82eb02313efafc460da3030d011bfd434b1ecabc" resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-2.0.14.tgz#f214f728235782a2ac49c0eb507f151612e31b2e"
integrity sha512-LhAWsY22cmb+M/iyhFgkzf2+V9TJmAC77Cd+GbP3M3sxDSEUDtq08KOc3JjoYc3GzeZml3JL1yssbxh+liY3xA== integrity sha512-e/hY1g92Xdu5d5A27NFfa1+ccE2q/A5H7sc/M7p0fId6KO33Dst25Hy+HThtqnYN0Y3Om58fiXEKo5SsdtvSfA==
dependencies: dependencies:
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@zag-js/focus-visible" "0.1.0" "@zag-js/focus-visible" "0.1.0"
"@chakra-ui/react-children-utils@2.0.1": "@chakra-ui/react-children-utils@2.0.4":
version "2.0.1" version "2.0.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-children-utils/-/react-children-utils-2.0.1.tgz#321ac05362ade1495a34ea74052d3c7da3d9e923" resolved "https://registry.yarnpkg.com/@chakra-ui/react-children-utils/-/react-children-utils-2.0.4.tgz#6e4284297a8a9b4e6f5f955b099bb6c2c6bbf8b9"
integrity sha512-sEgpuh/vWSt2+W0F49EGYXXUyjmg0lbosjVg6qUKHv9sAyx5tbrOrZ6df/TaMUSAe9m3AUOMGqUIPLpxno0DjA== integrity sha512-qsKUEfK/AhDbMexWo5JhmdlkxLg5WEw2dFh4XorvU1/dTYsRfP6cjFfO8zE+X3F0ZFNsgKz6rbN5oU349GLEFw==
"@chakra-ui/react-context@2.0.3": "@chakra-ui/react-context@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-context/-/react-context-2.0.3.tgz#e988be62f5f5fe29d6a8496c79cbf934f840fa5a" resolved "https://registry.yarnpkg.com/@chakra-ui/react-context/-/react-context-2.0.5.tgz#c434013ecc46c780539791d756dafdfc7c64320e"
integrity sha512-KmPq6sb1y05WsOUqXZtBBC4LsNKZIFrp2thTsLBwcuH7lkXZwPMHmJGKa9K980P+SWEgfH2s2PY2z+QrIuqWGg== integrity sha512-WYS0VBl5Q3/kNShQ26BP+Q0OGMeTQWco3hSiJWvO2wYLY7N1BLq6dKs8vyKHZfpwKh2YL2bQeAObi+vSkXp6tQ==
"@chakra-ui/react-env@2.0.9": "@chakra-ui/react-env@2.0.11":
version "2.0.9" version "2.0.11"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-env/-/react-env-2.0.9.tgz#d51efc31d77197a3526e2c4b2f2fde557396bb3c" resolved "https://registry.yarnpkg.com/@chakra-ui/react-env/-/react-env-2.0.11.tgz#d9d65fb695de7aff15e1d0e97d57bb7bedce5fa2"
integrity sha512-4AJHNUGBR19hzVyOILYpZZgq8jGrpEcbhvR++CppbvPH7vfPZpoz6L/cBtHxS07YwDtUeBL8yCNiLlTxctV//Q== integrity sha512-rPwUHReSWh7rbCw0HePa8Pvc+Q82fUFvVjHTIbXKnE6d+01cCE7j4f1NLeRD9pStKPI6sIZm9xTGvOCzl8F8iw==
"@chakra-ui/react-types@2.0.3": "@chakra-ui/react-types@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-types/-/react-types-2.0.3.tgz#dc454c4703b4de585e6461fd607304ede06fe595" resolved "https://registry.yarnpkg.com/@chakra-ui/react-types/-/react-types-2.0.5.tgz#1e4c99ef0e59b5fe9263d0e186cd66afdfb6c87b"
integrity sha512-1mJYOQldFTALE0Wr3j6tk/MYvgQIp6CKkJulNzZrI8QN+ox/bJOh8OVP4vhwqvfigdLTui0g0k8M9h+j2ub/Mw== integrity sha512-GApp+R/VjS1UV5ms5irrij5LOIgUM0dqSVHagyEFEz88LRKkqMD9RuO577ZsVd4Gn0ULsacVJCUA0HtNUBJNzA==
"@chakra-ui/react-use-animation-state@2.0.3": "@chakra-ui/react-use-animation-state@2.0.6":
version "2.0.3" version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.0.3.tgz#c10e575de76e907a84358595884a819039b24200" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.0.6.tgz#2a324d3c67015a542ed589f899672d681889e1e7"
integrity sha512-sjGgzMMmxurwKDSFhDLpLNn3SWUERI5iAZOOa0pYnyOLGVXMowgIjK6jpZxre1vc3A+unjJk5P4qeiyY+C4uwQ== integrity sha512-M2kUzZkSBgDpfvnffh3kTsMIM3Dvn+CTMqy9zfY97NL4P3LAWL1MuFtKdlKfQ8hs/QpwS/ew8CTmCtaywn4sKg==
dependencies: dependencies:
"@chakra-ui/dom-utils" "2.0.1" "@chakra-ui/dom-utils" "2.0.4"
"@chakra-ui/react-use-event-listener" "2.0.3" "@chakra-ui/react-use-event-listener" "2.0.5"
"@chakra-ui/react-use-callback-ref@2.0.3": "@chakra-ui/react-use-callback-ref@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.0.3.tgz#532f993ae0dda27b2638d41e98f42c83751cd3b6" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.0.5.tgz#862430dfbab8e1f0b8e04476e5e25469bd044ec9"
integrity sha512-kdYlhgnQKWWLNwl3WSv/Oq3+mlnu2p3y4Xc1AqKVHVcBOdQE9lpW3d7ZaOoK2aIXXWq1rocscOiXBUtM0Vqd2A== integrity sha512-vKnXleD2PzB0nGabY35fRtklMid4z7cecbMG0fkasNNsgWmrQcXJOuEKUUVCynL6FBU6gBnpKFi5Aqj6x+K4tw==
"@chakra-ui/react-use-controllable-state@2.0.3": "@chakra-ui/react-use-controllable-state@2.0.6":
version "2.0.3" version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.0.3.tgz#7aa3f9c038513763332f6754e69ece90aed55a9c" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.0.6.tgz#ec62aff9b9c00324a0a4c9a4523824a9ad5ef9aa"
integrity sha512-su8efwCWWnY2LQUU6PEnYwSGJX8kvPSO2KyUKuymx8q3fNWuyhzAZriG/TbeeCxESLp70+wuniUlSGRa4vxylQ== integrity sha512-7WuKrhQkpSRoiI5PKBvuIsO46IIP0wsRQgXtStSaIXv+FIvIJl9cxQXTbmZ5q1Ds641QdAUKx4+6v0K/zoZEHg==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-disclosure@2.0.3": "@chakra-ui/react-use-disclosure@2.0.6":
version "2.0.3" version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.0.3.tgz#c27bfc7e3af0728423b9e2def1e1665d0ba941bb" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.0.6.tgz#db707ee119db829e9b21ff1c05e867938f1e27ba"
integrity sha512-3IdrzvQZcgjqSx5wTVffInOyhMU+d3ZlIE26JmqejMyN/B+qAs932iKfm0A1mTMPTz38ZnNtuaKazmzyfR1ePg== integrity sha512-4UPePL+OcCY37KZ585iLjg8i6J0sjpLm7iZG3PUwmb97oKHVHq6DpmWIM0VfSjcT6AbSqyGcd5BXZQBgwt8HWQ==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-event-listener@2.0.3": "@chakra-ui/react-use-event-listener@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.0.3.tgz#11b5409c4442888e7981d5288c9e781acdacd685" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.0.5.tgz#949aa99878b25b23709452d3c80a1570c99747cc"
integrity sha512-m3ZdJjo3QQ1HcQGnehlBTgHaCVewz5fwIRTXVzbZTraVJr4k589Zf87eagW57tT4dyv656lSmdhaFGZ8p5Snww== integrity sha512-etLBphMigxy/cm7Yg22y29gQ8u/K3PniR5ADZX7WVX61Cgsa8ciCqjTE9sTtlJQWAQySbWxt9+mjlT5zaf+6Zw==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-focus-effect@2.0.3": "@chakra-ui/react-use-focus-effect@2.0.7":
version "2.0.3" version "2.0.7"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.0.3.tgz#3e221a5e9b06a7b832a6fca1f883e6335fd69f19" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.0.7.tgz#bd03290cac32e0de6a71ce987f939a5e697bca04"
integrity sha512-N0rho7P+rH5cn13dbS8GUOye+6RYXAmXhmlS+WW/3lWidGH3HAbMoOVf56UiuSnE1+2or8/U7qRshUryj2H1nA== integrity sha512-wI8OUNwfbkusajLac8QtjfSyNmsNu1D5pANmnSHIntHhui6Jwv75Pxx7RgmBEnfBEpleBndhR9E75iCjPLhZ/A==
dependencies: dependencies:
"@chakra-ui/dom-utils" "2.0.1" "@chakra-ui/dom-utils" "2.0.4"
"@chakra-ui/react-use-event-listener" "2.0.3" "@chakra-ui/react-use-event-listener" "2.0.5"
"@chakra-ui/react-use-update-effect" "2.0.3" "@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/react-use-focus-on-pointer-down@2.0.1": "@chakra-ui/react-use-focus-on-pointer-down@2.0.4":
version "2.0.1" version "2.0.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.0.1.tgz#be0668ff844dec8bbfe978d6eaff50534f290c48" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.0.4.tgz#aeba543c451ac1b0138093234e71d334044daf84"
integrity sha512-f0qL2iWvajUo+0jwDZyJpUMJ6J6BH3WjDZE2Rp6cns4pgI6uYuv2gj+FqQ5jnoYdXkeER6lBI56a+aIW/1RYiA== integrity sha512-L3YKouIi77QbXH9mSLGEFzJbJDhyrPlcRcuu+TSC7mYaK9E+3Ap+RVSAVxj+CfQz7hCWpikPecKDuspIPWlyuA==
dependencies: dependencies:
"@chakra-ui/react-use-event-listener" "2.0.3" "@chakra-ui/react-use-event-listener" "2.0.5"
"@chakra-ui/react-use-interval@2.0.1": "@chakra-ui/react-use-interval@2.0.3":
version "2.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-interval/-/react-use-interval-2.0.1.tgz#a8f5dbf83607f5dc53022aa2a766fdcb09d8a081" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-interval/-/react-use-interval-2.0.3.tgz#d5c7bce117fb25edb54e3e2c666e900618bb5bb2"
integrity sha512-6ZLzKA7Ga894UZcXO3bbGYThlhviiau1oxZ1UcJG5pUXNM9Up7O/4Joq31sL+KcpteCN45vd1etomilsv/blxw== integrity sha512-Orbij5c5QkL4NuFyU4mfY/nyRckNBgoGe9ic8574VVNJIXfassevZk0WB+lvqBn5XZeLf2Tj+OGJrg4j4H9wzw==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-merge-refs@2.0.3": "@chakra-ui/react-use-latest-ref@2.0.3":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.0.3.tgz#cd8dac79c62dd45daaf4acc4507721d23dc5dc51" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.0.3.tgz#27cf703858e65ecb5a0eef26215c794ad2a5353d"
integrity sha512-n35BmVbasy5Esa6qxznWmiV3NaRxGpqMpZH0n+X7aXt8VkGAJzRpAVjUmKCLNYyCLpqsQceCmAEK8a5SR6vxqw== integrity sha512-exNSQD4rPclDSmNwtcChUCJ4NuC2UJ4amyNGBqwSjyaK5jNHk2kkM7rZ6I0I8ul+26lvrXlSuhyv6c2PFwbFQQ==
"@chakra-ui/react-use-outside-click@2.0.3": "@chakra-ui/react-use-merge-refs@2.0.5":
version "2.0.3" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.0.3.tgz#d0136d1c2fb45d86361224e98e3a50648bf9b85f" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.0.5.tgz#13181e1a43219c6a04a01f84de0188df042ee92e"
integrity sha512-r5OohM8lOuZTz6e3vVHvfm/3sEkd06nUPBNU+r3rWh1I7bR9z5Gia/BOQD6GE4jUTanDkHcH76Pf9qJ45kpibQ== integrity sha512-uc+MozBZ8asaUpO8SWcK6D4svRPACN63jv5uosUkXJR+05jQJkUofkfQbf2HeGVbrWCr0XZsftLIm4Mt/QMoVw==
"@chakra-ui/react-use-outside-click@2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.0.5.tgz#6a9896d2c2d35f3c301c3bb62bed1bf5290d1e60"
integrity sha512-WmtXUeVaMtxP9aUGGG+GQaDeUn/Bvf8TI3EU5mE1+TtqLHxyA9wtvQurynrogvpilLaBADwn/JeBeqs2wHpvqA==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-pan-event@2.0.4": "@chakra-ui/react-use-pan-event@2.0.6":
version "2.0.4" version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.0.4.tgz#bfc2c1a2a44b2996951a729182566f02c7dc05e4" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.0.6.tgz#e489d61672e6f473b7fd362d816e2e27ed3b2af6"
integrity sha512-lcEjngfCgIjE5qZeJiaDx+aJzZPLjbjUmbWumi8pIgWOnDL8Ffjh7AMKW4CddP5OgcRnDDb+7aqJbb55wraboA== integrity sha512-Vtgl3c+Mj4hdehFRFIgruQVXctwnG1590Ein1FiU8sVnlqO6bpug6Z+B14xBa+F+X0aK+DxnhkJFyWI93Pks2g==
dependencies: dependencies:
"@chakra-ui/event-utils" "2.0.4" "@chakra-ui/event-utils" "2.0.6"
"@chakra-ui/react-use-latest-ref" "2.0.3"
framesync "5.3.0" framesync "5.3.0"
"@chakra-ui/react-use-previous@2.0.1": "@chakra-ui/react-use-previous@2.0.3":
version "2.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-previous/-/react-use-previous-2.0.1.tgz#e19f6b363271f62c36c9f3bd91dc60caa4c4e340" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-previous/-/react-use-previous-2.0.3.tgz#9da3d53fd75f1c3da902bd6af71dcb1a7be37f31"
integrity sha512-ROi+/puVd8D1QaxBSOcGlJNqV2x02ppSgmXzZZJhM8ryFLZjY9ojV3HhamB2IJ/7SIb1rMSSV1GPedFw7YMCwA== integrity sha512-A2ODOa0rm2HM4aqXfxxI0zPLcn5Q7iBEjRyfIQhb+EH+d2OFuj3L2slVoIpp6e/km3Xzv2d+u/WbjgTzdQ3d0w==
"@chakra-ui/react-use-safe-layout-effect@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.0.1.tgz#76f8882abaf17078c3b6eb93e1bb26f8c319f3f7"
integrity sha512-H+ZOjkPqv3KBPEoP68JKpQBNdLOI0mwzEiTT397UdvBVCCJ+1/ijWVUT+Ub/pYic60O6xUghy5ORaWqJHhnKDA==
"@chakra-ui/react-use-size@2.0.3": "@chakra-ui/react-use-safe-layout-effect@2.0.3":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-size/-/react-use-size-2.0.3.tgz#ae3bd683eb87a40208cf0dd467a5dafb68d87b3e" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.0.3.tgz#bf63ac8c94460aa1b20b6b601a0ea873556ffb1b"
integrity sha512-hr4hKepPUmM2paXseSZiOTK2y+ZqnSzYNusDEB01f+cDerFjdN1jSfNJKXpiKF0+hNESXfOPQb3Zt0eDusRdoA== integrity sha512-dlTvQURzmdfyBbNdydgO4Wy2/HV8aJN8LszTtyb5vRZsyaslDM/ftcxo8E8QjHwRLD/V1Epb/A8731QfimfVaQ==
dependencies:
"@zag-js/element-size" "0.1.0"
"@chakra-ui/react-use-timeout@2.0.1": "@chakra-ui/react-use-size@2.0.5":
version "2.0.1" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-timeout/-/react-use-timeout-2.0.1.tgz#acacadfb7c1443aacf634ddce710b1cd7cf3b6ec" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-size/-/react-use-size-2.0.5.tgz#4bbffb64f97dcfe1d7edeb0f03bb1d5fbc48df64"
integrity sha512-zXh9RH+GciKr8hvaOADHOoHP72B7UZUEymA8CWCV4WEs/9s/PfQJH7X1bwvaj43CcOmfVQg4oODWqCYQM1lSsg== integrity sha512-4arAApdiXk5uv5ZeFKltEUCs5h3yD9dp6gTIaXbAdq+/ENK3jMWTwlqzNbJtCyhwoOFrblLSdBrssBMIsNQfZQ==
dependencies: dependencies:
"@chakra-ui/react-use-callback-ref" "2.0.3" "@zag-js/element-size" "0.1.0"
"@chakra-ui/react-use-update-effect@2.0.3": "@chakra-ui/react-use-timeout@2.0.3":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.0.3.tgz#5b0128fe1325b5b1413690db6bc8dd0712d01e29" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-timeout/-/react-use-timeout-2.0.3.tgz#16ca397dbca55a64811575884cb81a348d86d4e2"
integrity sha512-8hkP1o/UUUA49w/R+XyAlPiCjxXTCWCNsHWUOEhAitjJfoCNUjgaNKOD52hT07kc5ACJEcJQHA5327LnwtiIlg== integrity sha512-rBBUkZSQq3nJQ8fuMkgZNY2Sgg4vKiKNp05GxAwlT7TitOfVZyoTriqQpqz296bWlmkICTZxlqCWfE5fWpsTsg==
"@chakra-ui/react-utils@2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@chakra-ui/react-utils/-/react-utils-2.0.6.tgz#bb471ce2bff724b99563685962145a2cc56bf61d"
integrity sha512-ZL0FPaolovXOxMzYRSLHgBYtvxIkA/c5GTSYpXL8DcC+TBLZnAmQ8BPTS2b6xys6xvwdQjkZRUeQ0cBNFaryJg==
dependencies: dependencies:
"@chakra-ui/utils" "2.0.9" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react@2.3.1": "@chakra-ui/react-use-update-effect@2.0.5":
version "2.3.1" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-2.3.1.tgz#da7e393f48e64d3ae64b6e2370d15729259ebd3a" resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.0.5.tgz#aede8f13f2b3de254b4ffa3b8cec1b70bd2876c5"
integrity sha512-ROaIyQqpN5AXhhxDGkrP37QmnPu/ZJnT1nLdzUxHZE1sO7ujBbir3G+zHlj/Hg2DbYClkB3yfAhA08imeiJh8g== integrity sha512-y9tCMr1yuDl8ATYdh64Gv8kge5xE1DMykqPDZw++OoBsTaWr3rx40wblA8NIWuSyJe5ErtKP2OeglvJkYhryJQ==
dependencies:
"@chakra-ui/accordion" "2.0.12" "@chakra-ui/react-utils@2.0.9":
"@chakra-ui/alert" "2.0.9" version "2.0.9"
"@chakra-ui/avatar" "2.1.0" resolved "https://registry.yarnpkg.com/@chakra-ui/react-utils/-/react-utils-2.0.9.tgz#5cdf0bc8dee57c15f15ace04fbba574ec8aa6ecc"
"@chakra-ui/breadcrumb" "2.0.9" integrity sha512-nlwPBVlQmcl1PiLzZWyrT3FSnt3vKSkBMzQ0EF4SJWA/nOIqTvmffb5DCzCqPzgQaE/Da1Xgus+JufFGM8GLCQ==
"@chakra-ui/button" "2.0.9" dependencies:
"@chakra-ui/checkbox" "2.1.8" "@chakra-ui/utils" "2.0.12"
"@chakra-ui/close-button" "2.0.9"
"@chakra-ui/control-box" "2.0.9" "@chakra-ui/react@2.4.3":
"@chakra-ui/counter" "2.0.9" version "2.4.3"
"@chakra-ui/css-reset" "2.0.7" resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-2.4.3.tgz#c86b92b38ebaa482f22e4515208da429cd4c94bf"
"@chakra-ui/editable" "2.0.9" integrity sha512-OzeN8lb/yoJZMuLYgnX00ZcrvMwgMomIHvq3lD7v0jtfdzAQKEySgu7/CmHOsvWnDB7Qy9LMlSb8PLUII1aDow==
"@chakra-ui/form-control" "2.0.9" dependencies:
"@chakra-ui/hooks" "2.0.9" "@chakra-ui/accordion" "2.1.4"
"@chakra-ui/icon" "3.0.9" "@chakra-ui/alert" "2.0.13"
"@chakra-ui/image" "2.0.10" "@chakra-ui/avatar" "2.2.1"
"@chakra-ui/input" "2.0.9" "@chakra-ui/breadcrumb" "2.1.1"
"@chakra-ui/layout" "2.1.6" "@chakra-ui/button" "2.0.13"
"@chakra-ui/live-region" "2.0.9" "@chakra-ui/card" "2.1.2"
"@chakra-ui/media-query" "3.2.5" "@chakra-ui/checkbox" "2.2.5"
"@chakra-ui/menu" "2.0.13" "@chakra-ui/close-button" "2.0.13"
"@chakra-ui/modal" "2.1.7" "@chakra-ui/control-box" "2.0.11"
"@chakra-ui/number-input" "2.0.9" "@chakra-ui/counter" "2.0.11"
"@chakra-ui/pin-input" "2.0.12" "@chakra-ui/css-reset" "2.0.10"
"@chakra-ui/popover" "2.0.9" "@chakra-ui/editable" "2.0.16"
"@chakra-ui/popper" "3.0.7" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/portal" "2.0.9" "@chakra-ui/hooks" "2.1.2"
"@chakra-ui/progress" "2.0.10" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/provider" "2.0.16" "@chakra-ui/image" "2.0.12"
"@chakra-ui/radio" "2.0.10" "@chakra-ui/input" "2.0.14"
"@chakra-ui/react-env" "2.0.9" "@chakra-ui/layout" "2.1.11"
"@chakra-ui/select" "2.0.10" "@chakra-ui/live-region" "2.0.11"
"@chakra-ui/skeleton" "2.0.15" "@chakra-ui/media-query" "3.2.8"
"@chakra-ui/slider" "2.0.10" "@chakra-ui/menu" "2.1.5"
"@chakra-ui/spinner" "2.0.9" "@chakra-ui/modal" "2.2.5"
"@chakra-ui/stat" "2.0.9" "@chakra-ui/number-input" "2.0.14"
"@chakra-ui/switch" "2.0.11" "@chakra-ui/pin-input" "2.0.16"
"@chakra-ui/system" "2.2.9" "@chakra-ui/popover" "2.1.4"
"@chakra-ui/table" "2.0.9" "@chakra-ui/popper" "3.0.10"
"@chakra-ui/tabs" "2.1.1" "@chakra-ui/portal" "2.0.12"
"@chakra-ui/tag" "2.0.9" "@chakra-ui/progress" "2.1.2"
"@chakra-ui/textarea" "2.0.10" "@chakra-ui/provider" "2.0.25"
"@chakra-ui/theme" "2.1.10" "@chakra-ui/radio" "2.0.14"
"@chakra-ui/toast" "3.0.9" "@chakra-ui/react-env" "2.0.11"
"@chakra-ui/tooltip" "2.0.10" "@chakra-ui/select" "2.0.14"
"@chakra-ui/transition" "2.0.9" "@chakra-ui/skeleton" "2.0.19"
"@chakra-ui/utils" "2.0.9" "@chakra-ui/slider" "2.0.15"
"@chakra-ui/visually-hidden" "2.0.9" "@chakra-ui/spinner" "2.0.11"
"@chakra-ui/stat" "2.0.13"
"@chakra-ui/select@2.0.10": "@chakra-ui/styled-system" "2.4.0"
version "2.0.10" "@chakra-ui/switch" "2.0.17"
resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-2.0.10.tgz#827028484769a32205f99baed3098a115da292b3" "@chakra-ui/system" "2.3.4"
integrity sha512-7AslBWwI/JyczjMMGtPuN34M/C38koVd+N/pb6swHoIP9TRkkdvDlonIakcmtO1oLEzlNIFKmt4FQ7bUp9ea5Q== "@chakra-ui/table" "2.0.12"
"@chakra-ui/tabs" "2.1.5"
"@chakra-ui/tag" "2.0.13"
"@chakra-ui/textarea" "2.0.14"
"@chakra-ui/theme" "2.2.2"
"@chakra-ui/theme-utils" "2.0.5"
"@chakra-ui/toast" "4.0.5"
"@chakra-ui/tooltip" "2.2.3"
"@chakra-ui/transition" "2.0.12"
"@chakra-ui/utils" "2.0.12"
"@chakra-ui/visually-hidden" "2.0.13"
"@chakra-ui/select@2.0.14":
version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-2.0.14.tgz#b2230702e31d2b9b4cc7848b18ba7ae8e4c89bdb"
integrity sha512-fvVGxAtLaIXGOMicrzSa6imMw5h26S1ar3xyNmXgR40dbpTPHmtQJkbHBf9FwwQXgSgKWgBzsztw5iDHCpPVzA==
dependencies: dependencies:
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/form-control" "2.0.13"
"@chakra-ui/shared-utils@2.0.1": "@chakra-ui/shared-utils@2.0.3":
version "2.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/shared-utils/-/shared-utils-2.0.1.tgz#41e314e42c96039e8ffb265e73145cf755813ab4" resolved "https://registry.yarnpkg.com/@chakra-ui/shared-utils/-/shared-utils-2.0.3.tgz#97cbc11282e381ebd9f581c603088f9d60ead451"
integrity sha512-NXDBl/u4wrSNp0ON5R3r3evkRurrAz2yuO7neooaG+O5HEenVouGqm4CsXd6lUAPmjwiGzA0LQFNCt0Hj92dXg== integrity sha512-pCU+SUGdXzjAuUiUT8mriekL3tJVfNdwSTIaNeip7k/SWDzivrKGMwAFBxd3XVTDevtVusndkO4GJuQ3yILzDg==
"@chakra-ui/skeleton@2.0.15": "@chakra-ui/skeleton@2.0.19":
version "2.0.15" version "2.0.19"
resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-2.0.15.tgz#fd41383bf84319e47c6ea1f4f3138f5f5e0dabca" resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-2.0.19.tgz#c2f8619edf06d8411aeb8fac8748134bfba7ea35"
integrity sha512-QVMkXwrH9jLfim8uJTZcjHeGjzoquNcHGXD5wapd7eDqp9BygvmMXAHBxFm8eEJLHuvIqLX94P6DLeiieYwX7Q== integrity sha512-i/U70wB9rX3DdO9i50kQLfPVdLPw8oIZlKynq15DkXEch3uXzLMY8ZzcEQW99B/PnynC1gni0+P9jMfeX7Wi+Q==
dependencies: dependencies:
"@chakra-ui/media-query" "3.2.5" "@chakra-ui/media-query" "3.2.8"
"@chakra-ui/react-use-previous" "2.0.1" "@chakra-ui/react-use-previous" "2.0.3"
"@chakra-ui/slider@2.0.10": "@chakra-ui/slider@2.0.15":
version "2.0.10" version "2.0.15"
resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-2.0.10.tgz#ffd7376d4a7fd9aa02e72b148e3ad4739c880498" resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-2.0.15.tgz#e574020d3240490b12204a6b1db4f40101567f87"
integrity sha512-F0RGl2ruADbXO/GnoBUiTEl+przxhZo2e0tfw9VTtS+RsJZ22uHrTNVvVJHNmjK7/E3++kBfaLCacoJFz/io+g== integrity sha512-1PwTBgaPKe8L1aOryGd1i52snqQY615Jd7d1Jyjzr/7s3uvLnpCmTE7bazu/cJU0h1qSpluMv++A+d+fjICdmA==
dependencies: dependencies:
"@chakra-ui/number-utils" "2.0.3" "@chakra-ui/number-utils" "2.0.5"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-callback-ref" "2.0.3" "@chakra-ui/react-use-callback-ref" "2.0.5"
"@chakra-ui/react-use-controllable-state" "2.0.3" "@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-latest-ref" "2.0.3"
"@chakra-ui/react-use-pan-event" "2.0.4" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-size" "2.0.3" "@chakra-ui/react-use-pan-event" "2.0.6"
"@chakra-ui/react-use-update-effect" "2.0.3" "@chakra-ui/react-use-size" "2.0.5"
"@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/spinner@2.0.9":
version "2.0.9" "@chakra-ui/spinner@2.0.11":
resolved "https://registry.yarnpkg.com/@chakra-ui/spinner/-/spinner-2.0.9.tgz#1d8544cc136699a590c3f5c518ae2c14abb459cf" version "2.0.11"
integrity sha512-9ALl51fiVWptDu2J2xcv0TSfGf4buumpHrEXHvV2Qy+HZ6rYnUmSThBSb/VgoQS+rASG8bAbLUPlQTQ+v9ibFg== resolved "https://registry.yarnpkg.com/@chakra-ui/spinner/-/spinner-2.0.11.tgz#a5dd76b6cb0f3524d9b90b73fa4acfb6adc69f33"
integrity sha512-piO2ghWdJzQy/+89mDza7xLhPnW7pA+ADNbgCb1vmriInWedS41IBKe+pSPz4IidjCbFu7xwKE0AerFIbrocCA==
"@chakra-ui/stat@2.0.9": "@chakra-ui/stat@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-2.0.9.tgz#cecf35a4392a88227c3b85e80a45f0f5ac5f298d" resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-2.0.13.tgz#1805817ab54f9d9b663b465fcb255285d22d0152"
integrity sha512-C9cytqegWSGJ/hh3/qwsgGlerXLYHrU0iQcJQ+pKSRFJhshXsv3go5IR6kVL72Yf2s4Gs5c3GsMZrLM22ePpDg== integrity sha512-6XeuE/7w0BjyCHSxMbsf6/rNOOs8BSit1NS7g7+Jd/40Pc/SKlNWLd3kxXPid4eT3RwyNIdMPtm30OActr9nqQ==
dependencies: dependencies:
"@chakra-ui/icon" "3.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.3" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/styled-system@2.3.1": "@chakra-ui/styled-system@2.4.0":
version "2.3.1" version "2.4.0"
resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-2.3.1.tgz#abf7c4e1638aaa9d92e7cf9acde17785703d166e" resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-2.4.0.tgz#4b50079606331e4e8fda7ea59da9db51b446d40c"
integrity sha512-jyR9s2yk5TEyq4HUfjrgUeaOzd9ZTZrbjK96UjtiTCZGO/q4j2RXtYvfheUjUyW1UnzI2A1ffHOJca8tBMDjpA== integrity sha512-G4HpbFERq4C1cBwKNDNkpCiliOICLXjYwKI/e/6hxNY+GlPxt8BCzz3uhd3vmEoG2vRM4qjidlVjphhWsf6vRQ==
dependencies: dependencies:
csstype "^3.0.11" csstype "^3.0.11"
lodash.mergewith "4.6.2" lodash.mergewith "4.6.2"
"@chakra-ui/switch@2.0.11": "@chakra-ui/switch@2.0.17":
version "2.0.11" version "2.0.17"
resolved "https://registry.yarnpkg.com/@chakra-ui/switch/-/switch-2.0.11.tgz#57117417d1bb072f506c8c30e1a961ee7f78496b" resolved "https://registry.yarnpkg.com/@chakra-ui/switch/-/switch-2.0.17.tgz#1d6904b6cde2469212bbd8311b749b96c653a9a3"
integrity sha512-gY8OGBnoPosZpq7dDNVf432t67pTc/cz5VkGhbtER7bbjXSoXe0DAiAYL+HT2kD7mbTJQzzHK/y0St0WimR1Mw== integrity sha512-BQabfC6qYi5xBJvEFPzKq0yl6fTtTNNEHTid5r7h0PWcCnAiHwQJTpQRpxp+AjK569LMLtTXReTZvNBrzEwOrA==
dependencies: dependencies:
"@chakra-ui/checkbox" "2.1.8" "@chakra-ui/checkbox" "2.2.5"
"@chakra-ui/system@2.2.9": "@chakra-ui/system@2.3.4":
version "2.2.9" version "2.3.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-2.2.9.tgz#d6e7dfb9a954b8ab03c28e14c69aad56a9fcffbd" resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-2.3.4.tgz#425bf7eebf61bd92aa68f60a6b62c380274fbe4e"
integrity sha512-SyTeIGm+goyYK8vqX4dU6oeLhxUAeGI3Cl+mxA+aiKIX01YTALhTWhpbrsuMYBevV+l9EGK12egPUQE+Mo3WlQ== integrity sha512-/2m8hFfFzOMO2OlwHxTWqINOBJMjxWwU5V/AcB7C0qS51Dcj9c7kupilM6QdqiOLLdMS7mIVRSYr8jn8gMw9fA==
dependencies: dependencies:
"@chakra-ui/color-mode" "2.1.7" "@chakra-ui/color-mode" "2.1.10"
"@chakra-ui/react-utils" "2.0.6" "@chakra-ui/react-utils" "2.0.9"
"@chakra-ui/styled-system" "2.3.1" "@chakra-ui/styled-system" "2.4.0"
"@chakra-ui/utils" "2.0.9" "@chakra-ui/theme-utils" "2.0.5"
"@chakra-ui/utils" "2.0.12"
react-fast-compare "3.2.0" react-fast-compare "3.2.0"
"@chakra-ui/table@2.0.9": "@chakra-ui/table@2.0.12":
version "2.0.9" version "2.0.12"
resolved "https://registry.yarnpkg.com/@chakra-ui/table/-/table-2.0.9.tgz#2ddb0202e8146e517bf602e62195d13fee8f1b0a" resolved "https://registry.yarnpkg.com/@chakra-ui/table/-/table-2.0.12.tgz#387653cf660318b13086b6497aca2b671deb055a"
integrity sha512-XRz6+x4dMeQX3xyViyG2H/P1STI/2vwvgU2cjzzwS+5fZ2JdGaTgYzBb+IZoH9agEq1Ma3rlKMUPDrRCFb7kLQ== integrity sha512-TSxzpfrOoB+9LTdNTMnaQC6OTsp36TlCRxJ1+1nAiCmlk+m+FiNzTQsmBalDDhc29rm+6AdRsxSPsjGWB8YVwg==
dependencies:
"@chakra-ui/react-context" "2.0.3"
"@chakra-ui/tabs@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@chakra-ui/tabs/-/tabs-2.1.1.tgz#0fb540782c2e4122b63a203fc1f04eff850f2c0e"
integrity sha512-xA+vwqpAHb0nBLrkiO5Lea2UDGROyAIBqsyp/8XXXEr6eKxtNe1I6WJPbDQy0aazB2ToAA0R6fT34HjLaXP8MQ==
dependencies:
"@chakra-ui/clickable" "2.0.9"
"@chakra-ui/descendant" "3.0.9"
"@chakra-ui/lazy-utils" "2.0.1"
"@chakra-ui/react-children-utils" "2.0.1"
"@chakra-ui/react-context" "2.0.3"
"@chakra-ui/react-use-controllable-state" "2.0.3"
"@chakra-ui/react-use-merge-refs" "2.0.3"
"@chakra-ui/react-use-safe-layout-effect" "2.0.1"
"@chakra-ui/tag@2.0.9":
version "2.0.9"
resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-2.0.9.tgz#bf8530aa766bd6b9196d374ff75b0b1ce62cd0d3"
integrity sha512-NKARwhsZ04t2vkrdRhNcakEiVtg1q44yUUsDw2Jwdu4idAWQupZGGochQI2Ac4T2MI1b66zQUkaGnm3l1mhTtg==
dependencies: dependencies:
"@chakra-ui/icon" "3.0.9" "@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-context" "2.0.3"
"@chakra-ui/textarea@2.0.10": "@chakra-ui/tabs@2.1.5":
version "2.0.10" version "2.1.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-2.0.10.tgz#dbbc8df8adddb488d0ee97164917e7be33d6b247" resolved "https://registry.yarnpkg.com/@chakra-ui/tabs/-/tabs-2.1.5.tgz#827b0e71eb173c09c31dcbbe05fc1146f4267229"
integrity sha512-HSo0EPsY8XKGA+Af6jTob1oe1T6NKZwgjLmX0binK3MMM9pDTXsUTw8GD0g971lxw9oktVMLK/O9QVAgVAm5mw== integrity sha512-XmnKDclAJe0FoW4tdC8AlnZpPN5fcj92l4r2sqiL9WyYVEM71hDxZueETIph/GTtfMelG7Z8e5vBHP4rh1RT5g==
dependencies:
"@chakra-ui/clickable" "2.0.11"
"@chakra-ui/descendant" "3.0.11"
"@chakra-ui/lazy-utils" "2.0.3"
"@chakra-ui/react-children-utils" "2.0.4"
"@chakra-ui/react-context" "2.0.5"
"@chakra-ui/react-use-controllable-state" "2.0.6"
"@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/react-use-safe-layout-effect" "2.0.3"
"@chakra-ui/tag@2.0.13":
version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-2.0.13.tgz#ad7349bfcdd5642d3894fadb43728acc0f061101"
integrity sha512-W1urf+tvGMt6J3cc31HudybYSl+B5jYUP5DJxzXM9p+n3JrvXWAo4D6LmpLBHY5zT2mNne14JF1rVeRcG4Rtdg==
dependencies: dependencies:
"@chakra-ui/form-control" "2.0.9" "@chakra-ui/icon" "3.0.13"
"@chakra-ui/react-context" "2.0.5"
"@chakra-ui/theme-tools@2.0.11": "@chakra-ui/textarea@2.0.14":
version "2.0.11" version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/theme-tools/-/theme-tools-2.0.11.tgz#de97b422799627b5a514ae424ca08c1d348bc2a5" resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-2.0.14.tgz#a79a3fdd850a3303e6ebb68d64b7c334de03da4d"
integrity sha512-0Juf98bAyOgnBeQ39nMKWqRsOxZDw75BbAB8o0oVyjhYVS1wJh7tFX1ZRV8N/+AN6fuRXEznZPpyUh3J+ZTiRg== integrity sha512-r8hF1rCi+GseLtY/IGeVWXFN0Uve2b820UQumRj4qxj7PsPqw1hFg7Cecbbb9zwF38K/m+D3IdwFeJzI1MtgRA==
dependencies: dependencies:
"@chakra-ui/anatomy" "2.0.6" "@chakra-ui/form-control" "2.0.13"
"@ctrl/tinycolor" "^3.4.0"
"@chakra-ui/theme-tools@^2.0.2": "@chakra-ui/theme-tools@2.0.14", "@chakra-ui/theme-tools@^2.0.14":
version "2.0.12" version "2.0.14"
resolved "https://registry.yarnpkg.com/@chakra-ui/theme-tools/-/theme-tools-2.0.12.tgz#b29d9fb626d35e3b00f532c64f95ea261d8f6997" resolved "https://registry.yarnpkg.com/@chakra-ui/theme-tools/-/theme-tools-2.0.14.tgz#6c523284ab384ca57a3aef1fcfa7c32ed357fbde"
integrity sha512-mnMlKSmXkCjHUJsKWmJbgBTGF2vnLaMLv1ihkBn5eQcCubMQrBLTiMAEFl5pZdzuHItU6QdnLGA10smcXbNl0g== integrity sha512-lVcDmq5pyU0QbsIFKjt/iVUFDap7di2QHvPvGChA1YSjtg1PtuUi+BxEXWzp3Nfgw/N4rMvlBs+S0ynJypdwbg==
dependencies: dependencies:
"@chakra-ui/anatomy" "2.0.7" "@chakra-ui/anatomy" "2.1.0"
"@ctrl/tinycolor" "^3.4.0" color2k "^2.0.0"
"@chakra-ui/theme@2.1.10": "@chakra-ui/theme-utils@2.0.5":
version "2.1.10" version "2.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-2.1.10.tgz#3bd3e212e50f902d65165ffe408d63c1554b6cf2" resolved "https://registry.yarnpkg.com/@chakra-ui/theme-utils/-/theme-utils-2.0.5.tgz#ad1e53fc7f71326d15b9b01a157c7e2a029f3dda"
integrity sha512-7V1ReVD2B29amMO9LdRrNsyzAacXzC4Xb7P/0RDDSPWo0rZxPPrPtfhiuPi+bYqk6NN9tJ8dApVhSY+hgIH//A== integrity sha512-QQowSM8fvQlTmT0w9wtqUlWOB4i+9eA7P4XRm4bfhBMZ7XpK4ctV95sPeGqaXVccsz5m0q1AuGWa+j6eMCbrrg==
dependencies: dependencies:
"@chakra-ui/anatomy" "2.0.6" "@chakra-ui/styled-system" "2.4.0"
"@chakra-ui/theme-tools" "2.0.11" "@chakra-ui/theme" "2.2.2"
lodash.mergewith "4.6.2"
"@chakra-ui/toast@3.0.9": "@chakra-ui/theme@2.2.2":
version "3.0.9" version "2.2.2"
resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-3.0.9.tgz#25d00bb2d205047062c9c111600cef5e858c1003" resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-2.2.2.tgz#5ea69adde78ee6ea59f9dce674947ed8be2ebc62"
integrity sha512-FbGiza882r5cseN3MZ6uCjGRgtoE8ZHMckQfkmM9Pbhh80I7WMWkWBlr7TjQo6q+EcZ+/4YRHrAw/TRp78v5uw== integrity sha512-7DlOQiXmnaqYyqXwqmfFSCWGkUonuqmNC5mmUCwxI435KgHNCaE2bIm6DI7N2NcIcuVcfc8Vn0UqrDoGU3zJBg==
dependencies: dependencies:
"@chakra-ui/alert" "2.0.9" "@chakra-ui/anatomy" "2.1.0"
"@chakra-ui/close-button" "2.0.9" "@chakra-ui/theme-tools" "2.0.14"
"@chakra-ui/portal" "2.0.9"
"@chakra-ui/react-use-timeout" "2.0.1"
"@chakra-ui/react-use-update-effect" "2.0.3"
"@chakra-ui/theme" "2.1.10"
"@chakra-ui/tooltip@2.0.10": "@chakra-ui/toast@4.0.5":
version "2.0.10" version "4.0.5"
resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-2.0.10.tgz#2166753f9f246dd217d3170fd85f95a86392d9b6" resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-4.0.5.tgz#b501d7160ab67d9bcfa4df5c8b763d4aa789731e"
integrity sha512-pBILBdZoux2K3EW9V6JuyZYUWz2/Y7oYCVO6AwNOesiEBGAONyzoDwFV728EzPEHe9e+YBcKOSZ9tEpDdrzHMA== integrity sha512-avJKRiZZACi6v04cUc2uRcmta3gAes67HGkQhq5sHG/VV2cH9wXV2NMT3DolT8WiU9j7g8lWKxWe7bqFFrX+wA==
dependencies:
"@chakra-ui/alert" "2.0.13"
"@chakra-ui/close-button" "2.0.13"
"@chakra-ui/portal" "2.0.12"
"@chakra-ui/react-use-timeout" "2.0.3"
"@chakra-ui/react-use-update-effect" "2.0.5"
"@chakra-ui/styled-system" "2.4.0"
"@chakra-ui/theme" "2.2.2"
"@chakra-ui/tooltip@2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-2.2.3.tgz#92c9ed224e4c16839310acd0759c0017caec3134"
integrity sha512-yOne9ofFYfW2XHsbCEPWgLUTnHKm5z21f/cPjwEqtmvCS7aTCOLFiwz2ckRS8yJbIAy+mw0UG6jQsblYKgXj4A==
dependencies: dependencies:
"@chakra-ui/popper" "3.0.7" "@chakra-ui/popper" "3.0.10"
"@chakra-ui/portal" "2.0.9" "@chakra-ui/portal" "2.0.12"
"@chakra-ui/react-types" "2.0.3" "@chakra-ui/react-types" "2.0.5"
"@chakra-ui/react-use-disclosure" "2.0.3" "@chakra-ui/react-use-disclosure" "2.0.6"
"@chakra-ui/react-use-event-listener" "2.0.3" "@chakra-ui/react-use-event-listener" "2.0.5"
"@chakra-ui/react-use-merge-refs" "2.0.3" "@chakra-ui/react-use-merge-refs" "2.0.5"
"@chakra-ui/transition@2.0.9": "@chakra-ui/transition@2.0.12":
version "2.0.9" version "2.0.12"
resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-2.0.9.tgz#1967fd77f44b57681a9efe4e87561c82420cd2a2" resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-2.0.12.tgz#876c6ed24e442a720a8570490a93cb1f87008700"
integrity sha512-cVfKdZl128AEj0LDS8M9dzXao4wmTVj3gRJBnm91Qcg243Pm8OlgIBNbHEwsq/Fps+PsN431BtEGfL4w79wQEA== integrity sha512-ff6eU+m08ccYfCkk0hKfY/XlmGxCrfbBgsKgV4mirZ4SKUL1GVye8CYuHwWQlBJo+8s0yIpsTNxAuX4n/cW9/w==
"@chakra-ui/utils@2.0.9": "@chakra-ui/utils@2.0.12":
version "2.0.9" version "2.0.12"
resolved "https://registry.yarnpkg.com/@chakra-ui/utils/-/utils-2.0.9.tgz#1af3882b31fb46e0a411998d8e3607656f8d5043" resolved "https://registry.yarnpkg.com/@chakra-ui/utils/-/utils-2.0.12.tgz#5ab8a4529fca68d9f8c6722004f6a5129b0b75e9"
integrity sha512-7ct5562Jw6pZdtj63XfUkEUXXsCCVqdqIXyLtQ9VgOKtRQWwDxzc8uPI5Zjdw9AleEITZFUH8TNKWn75nm54kQ== integrity sha512-1Z1MgsrfMQhNejSdrPJk8v5J4gCefHo+1wBmPPHTz5bGEbAAbZ13aXAfXy8w0eFy0Nvnawn0EHW7Oynp/MdH+Q==
dependencies: dependencies:
"@types/lodash.mergewith" "4.6.6" "@types/lodash.mergewith" "4.6.6"
css-box-model "1.2.1" css-box-model "1.2.1"
framesync "5.3.0" framesync "5.3.0"
lodash.mergewith "4.6.2" lodash.mergewith "4.6.2"
"@chakra-ui/visually-hidden@2.0.9": "@chakra-ui/visually-hidden@2.0.13":
version "2.0.9" version "2.0.13"
resolved "https://registry.yarnpkg.com/@chakra-ui/visually-hidden/-/visually-hidden-2.0.9.tgz#b43a3dd0bc1108954ad0eeb50d0261887ab5e31c" resolved "https://registry.yarnpkg.com/@chakra-ui/visually-hidden/-/visually-hidden-2.0.13.tgz#6553467d93f206d17716bcbe6e895a84eef87472"
integrity sha512-PkNxrRGp9H3bdqEaoo8XGt/AL9UuGRTom0/9XJa+G/Dj8Cy1sDuamOWk3pN/ZQs46RokfK9Uh5LqPY5dwSDweg== integrity sha512-sDEeeEjLfID333EC46NdCbhK2HyMXlpl5HzcJjuwWIpyVz4E1gKQ9hlwpq6grijvmzeSywQ5D3tTwUrvZck4KQ==
"@cspotcode/source-map-support@^0.8.0": "@cspotcode/source-map-support@^0.8.0":
version "0.8.1" version "0.8.1"
...@@ -1812,44 +1829,39 @@ ...@@ -1812,44 +1829,39 @@
dependencies: dependencies:
"@jridgewell/trace-mapping" "0.3.9" "@jridgewell/trace-mapping" "0.3.9"
"@ctrl/tinycolor@^3.4.0":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32"
integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==
"@cush/relative@^1.0.0": "@cush/relative@^1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@cush/relative/-/relative-1.0.0.tgz#8cd1769bf9bde3bb27dac356b1bc94af40f6cc16" resolved "https://registry.yarnpkg.com/@cush/relative/-/relative-1.0.0.tgz#8cd1769bf9bde3bb27dac356b1bc94af40f6cc16"
integrity sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA== integrity sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==
"@emotion/babel-plugin@^11.10.0": "@emotion/babel-plugin@^11.10.5":
version "11.10.2" version "11.10.5"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz#65fa6e1790ddc9e23cc22658a4c5dea423c55c3c"
integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA== integrity sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==
dependencies: dependencies:
"@babel/helper-module-imports" "^7.16.7" "@babel/helper-module-imports" "^7.16.7"
"@babel/plugin-syntax-jsx" "^7.17.12" "@babel/plugin-syntax-jsx" "^7.17.12"
"@babel/runtime" "^7.18.3" "@babel/runtime" "^7.18.3"
"@emotion/hash" "^0.9.0" "@emotion/hash" "^0.9.0"
"@emotion/memoize" "^0.8.0" "@emotion/memoize" "^0.8.0"
"@emotion/serialize" "^1.1.0" "@emotion/serialize" "^1.1.1"
babel-plugin-macros "^3.1.0" babel-plugin-macros "^3.1.0"
convert-source-map "^1.5.0" convert-source-map "^1.5.0"
escape-string-regexp "^4.0.0" escape-string-regexp "^4.0.0"
find-root "^1.1.0" find-root "^1.1.0"
source-map "^0.5.7" source-map "^0.5.7"
stylis "4.0.13" stylis "4.1.3"
"@emotion/cache@^11.10.0": "@emotion/cache@^11.10.5":
version "11.10.3" version "11.10.5"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12"
integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==
dependencies: dependencies:
"@emotion/memoize" "^0.8.0" "@emotion/memoize" "^0.8.0"
"@emotion/sheet" "^1.2.0" "@emotion/sheet" "^1.2.1"
"@emotion/utils" "^1.2.0" "@emotion/utils" "^1.2.0"
"@emotion/weak-memoize" "^0.3.0" "@emotion/weak-memoize" "^0.3.0"
stylis "4.0.13" stylis "4.1.3"
"@emotion/hash@^0.9.0": "@emotion/hash@^0.9.0":
version "0.9.0" version "0.9.0"
...@@ -1880,24 +1892,24 @@ ...@@ -1880,24 +1892,24 @@
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
"@emotion/react@^11": "@emotion/react@^11.10.4":
version "11.10.4" version "11.10.5"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.5.tgz#95fff612a5de1efa9c0d535384d3cfa115fe175d"
integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA== integrity sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==
dependencies: dependencies:
"@babel/runtime" "^7.18.3" "@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.10.0" "@emotion/babel-plugin" "^11.10.5"
"@emotion/cache" "^11.10.0" "@emotion/cache" "^11.10.5"
"@emotion/serialize" "^1.1.0" "@emotion/serialize" "^1.1.1"
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
"@emotion/utils" "^1.2.0" "@emotion/utils" "^1.2.0"
"@emotion/weak-memoize" "^0.3.0" "@emotion/weak-memoize" "^0.3.0"
hoist-non-react-statics "^3.3.1" hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.1.0": "@emotion/serialize@^1.1.1":
version "1.1.0" version "1.1.1"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0"
integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA== integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==
dependencies: dependencies:
"@emotion/hash" "^0.9.0" "@emotion/hash" "^0.9.0"
"@emotion/memoize" "^0.8.0" "@emotion/memoize" "^0.8.0"
...@@ -1905,20 +1917,20 @@ ...@@ -1905,20 +1917,20 @@
"@emotion/utils" "^1.2.0" "@emotion/utils" "^1.2.0"
csstype "^3.0.2" csstype "^3.0.2"
"@emotion/sheet@^1.2.0": "@emotion/sheet@^1.2.1":
version "1.2.0" version "1.2.1"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
"@emotion/styled@^11": "@emotion/styled@^11.10.4":
version "11.10.4" version "11.10.5"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.4.tgz#e93f84a4d54003c2acbde178c3f97b421fce1cd4" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.5.tgz#1fe7bf941b0909802cb826457e362444e7e96a79"
integrity sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ== integrity sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==
dependencies: dependencies:
"@babel/runtime" "^7.18.3" "@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.10.0" "@emotion/babel-plugin" "^11.10.5"
"@emotion/is-prop-valid" "^1.2.0" "@emotion/is-prop-valid" "^1.2.0"
"@emotion/serialize" "^1.1.0" "@emotion/serialize" "^1.1.1"
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
"@emotion/utils" "^1.2.0" "@emotion/utils" "^1.2.0"
...@@ -2837,6 +2849,16 @@ ...@@ -2837,6 +2849,16 @@
"@sentry/utils" "7.15.0" "@sentry/utils" "7.15.0"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/browser@7.24.2":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.24.2.tgz#1e514448cd07ff7da78d02797149ecc1922ffcc2"
integrity sha512-X6NbQT0Dp+h54j73TPLgWf3yyLyTZGJI5WQSGEsNIroqhVzD3UF8M+E+3roYpSJDDyYdfuM+WBme+MYkmeqHIw==
dependencies:
"@sentry/core" "7.24.2"
"@sentry/types" "7.24.2"
"@sentry/utils" "7.24.2"
tslib "^1.9.3"
"@sentry/cli@^1.74.4": "@sentry/cli@^1.74.4":
version "1.74.5" version "1.74.5"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8"
...@@ -2859,6 +2881,15 @@ ...@@ -2859,6 +2881,15 @@
"@sentry/utils" "7.15.0" "@sentry/utils" "7.15.0"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/core@7.24.2":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.24.2.tgz#d3b69cc9c5703a4b35be1d648379804b099894e6"
integrity sha512-CDfrVvr3PQ0qImJv7/6yN/5hxhwxy1HicxTL9K5RwSDoXqgK3kUGv/WmTvPNIVB2RQKodLwzS2T52NFRxRoqNw==
dependencies:
"@sentry/types" "7.24.2"
"@sentry/utils" "7.24.2"
tslib "^1.9.3"
"@sentry/integrations@7.15.0": "@sentry/integrations@7.15.0":
version "7.15.0" version "7.15.0"
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.15.0.tgz#c2af3a2d2c0667216d76d829f24c5125b110e6c8" resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.15.0.tgz#c2af3a2d2c0667216d76d829f24c5125b110e6c8"
...@@ -2900,7 +2931,7 @@ ...@@ -2900,7 +2931,7 @@
lru_map "^0.3.3" lru_map "^0.3.3"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/react@7.15.0", "@sentry/react@^7.13.0": "@sentry/react@7.15.0":
version "7.15.0" version "7.15.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.15.0.tgz#441ed851ca64afeef10abcb00302e0c95846404e" resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.15.0.tgz#441ed851ca64afeef10abcb00302e0c95846404e"
integrity sha512-a+5+Og93YPtWSCmOFYa/qzrbvfgIZXShJk1bsIaEI0KdltTOVJBdwvLQc8OiIOBe/CMDVCmK1t2DqiWfOWj41w== integrity sha512-a+5+Og93YPtWSCmOFYa/qzrbvfgIZXShJk1bsIaEI0KdltTOVJBdwvLQc8OiIOBe/CMDVCmK1t2DqiWfOWj41w==
...@@ -2911,7 +2942,18 @@ ...@@ -2911,7 +2942,18 @@
hoist-non-react-statics "^3.3.2" hoist-non-react-statics "^3.3.2"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/tracing@7.15.0", "@sentry/tracing@^7.13.0": "@sentry/react@^7.24.0":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.24.2.tgz#cc471cd75727c518f8942d8cf9a7777752fcf4b5"
integrity sha512-NK4/SDIWyQVYdi/EPfHfp7d0+flGNHbBuqV/GG/+CLSekUCuACsczSEWgMSyEad4ptbF9850yt5WN15oL5vAXg==
dependencies:
"@sentry/browser" "7.24.2"
"@sentry/types" "7.24.2"
"@sentry/utils" "7.24.2"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/tracing@7.15.0":
version "7.15.0" version "7.15.0"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.15.0.tgz#ea516957b2ed39f389c21132f433b6470d54b465" resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.15.0.tgz#ea516957b2ed39f389c21132f433b6470d54b465"
integrity sha512-c0Y3+z6EWsc+EJsfBcRtc58ugkWYa6+6KTu3ceMkx2ZgZTCmRUuzAb7yodMt/gwezBsxzq706fnQivx1lQgzlQ== integrity sha512-c0Y3+z6EWsc+EJsfBcRtc58ugkWYa6+6KTu3ceMkx2ZgZTCmRUuzAb7yodMt/gwezBsxzq706fnQivx1lQgzlQ==
...@@ -2921,11 +2963,26 @@ ...@@ -2921,11 +2963,26 @@
"@sentry/utils" "7.15.0" "@sentry/utils" "7.15.0"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/tracing@^7.24.0":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.24.2.tgz#d9f0cf7d3055283a50fd38b14e0891b729c5d107"
integrity sha512-rK1HUeCLM27DGGah1+5DN0C9Y4g9dnyMU5rdrRxGQGqxIJiwzHYwJI9xoNoAVMmt8jqFliDEpYvh2jsW8593IA==
dependencies:
"@sentry/core" "7.24.2"
"@sentry/types" "7.24.2"
"@sentry/utils" "7.24.2"
tslib "^1.9.3"
"@sentry/types@7.15.0": "@sentry/types@7.15.0":
version "7.15.0" version "7.15.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.15.0.tgz#50c57c924993d4dd16b43172d310c66384d17463" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.15.0.tgz#50c57c924993d4dd16b43172d310c66384d17463"
integrity sha512-MN9haDRh9ZOsTotoDTHu2BT3sT8Vs1F0alhizUpDyjN2YgBCqR6JV+AbAE1XNHwS2+5zbppch1PwJUVeE58URQ== integrity sha512-MN9haDRh9ZOsTotoDTHu2BT3sT8Vs1F0alhizUpDyjN2YgBCqR6JV+AbAE1XNHwS2+5zbppch1PwJUVeE58URQ==
"@sentry/types@7.24.2":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.24.2.tgz#2ef728db8eea14de8ba916896837d0cbeb3d28da"
integrity sha512-x2LEnKBPzUVzTGspvB0CjZmt1dWeJsLVHGeDKPUMUm004nIscFCxJsmYefqaJQdaIUMqDit5ApwcmKchuK6VKQ==
"@sentry/utils@7.15.0": "@sentry/utils@7.15.0":
version "7.15.0" version "7.15.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.15.0.tgz#cda642a353a58fd6631979c1e5986788e6db6c43" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.15.0.tgz#cda642a353a58fd6631979c1e5986788e6db6c43"
...@@ -2934,6 +2991,14 @@ ...@@ -2934,6 +2991,14 @@
"@sentry/types" "7.15.0" "@sentry/types" "7.15.0"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/utils@7.24.2":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.24.2.tgz#7120a8d36bd1d05043c902a0f22fbc2012fe2116"
integrity sha512-VuuYEF39v43Qk6YZMid8Em/N0HqCsS5ItuTSvunMtBai2dzDAIkJ2LqemF95wWFAXrzpLy4Nx3QyGVHayMn31A==
dependencies:
"@sentry/types" "7.24.2"
tslib "^1.9.3"
"@sentry/webpack-plugin@1.19.0": "@sentry/webpack-plugin@1.19.0":
version "1.19.0" version "1.19.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.19.0.tgz#2b134318f1552ba7f3e3f9c83c71a202095f7a44" resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.19.0.tgz#2b134318f1552ba7f3e3f9c83c71a202095f7a44"
...@@ -3533,6 +3598,13 @@ ...@@ -3533,6 +3598,13 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
"@types/qrcode@^1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.5.0.tgz#6a98fe9a9a7b2a9a3167b6dde17eff999eabe40b"
integrity sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==
dependencies:
"@types/node" "*"
"@types/react-dom@18.0.5": "@types/react-dom@18.0.5":
version "18.0.5" version "18.0.5"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a"
...@@ -4220,7 +4292,7 @@ callsites@^3.0.0: ...@@ -4220,7 +4292,7 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
camelcase@^5.3.1: camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1" version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
...@@ -4303,6 +4375,15 @@ cli-truncate@^3.1.0: ...@@ -4303,6 +4375,15 @@ cli-truncate@^3.1.0:
slice-ansi "^5.0.0" slice-ansi "^5.0.0"
string-width "^5.0.0" string-width "^5.0.0"
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
cliui@^8.0.1: cliui@^8.0.1:
version "8.0.1" version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
...@@ -4351,6 +4432,11 @@ color-name@~1.1.4: ...@@ -4351,6 +4432,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color2k@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.0.tgz#86992c82e248c29f524023ed0822bc152c4fa670"
integrity sha512-DWX9eXOC4fbJNiuvdH4QSHvvfLWyFo9TuFp7V9OzdsbPAdrWAuYc8qvFP2bIQ/LKh4LrAVnJ6vhiQYPvAHdtTg==
colorette@^2.0.16, colorette@^2.0.17, colorette@^2.0.7: colorette@^2.0.16, colorette@^2.0.17, colorette@^2.0.7:
version "2.0.19" version "2.0.19"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
...@@ -4823,6 +4909,11 @@ debug@^3.2.7: ...@@ -4823,6 +4909,11 @@ debug@^3.2.7:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decimal.js@^10.4.1: decimal.js@^10.4.1:
version "10.4.2" version "10.4.2"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e"
...@@ -4914,6 +5005,11 @@ diff@^4.0.1: ...@@ -4914,6 +5005,11 @@ diff@^4.0.1:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dijkstrajs@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
integrity sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==
dir-glob@^3.0.1: dir-glob@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
...@@ -5035,6 +5131,11 @@ emoji-regex@^9.2.2: ...@@ -5035,6 +5131,11 @@ emoji-regex@^9.2.2:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
encode-utf8@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
end-of-stream@^1.1.0, end-of-stream@^1.4.4: end-of-stream@^1.1.0, end-of-stream@^1.4.4:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
...@@ -5809,7 +5910,7 @@ formdata-polyfill@^4.0.10: ...@@ -5809,7 +5910,7 @@ formdata-polyfill@^4.0.10:
dependencies: dependencies:
fetch-blob "^3.1.2" fetch-blob "^3.1.2"
framer-motion@^6: framer-motion@^6.5.1:
version "6.5.1" version "6.5.1"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7"
integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==
...@@ -5886,7 +5987,7 @@ gensync@^1.0.0-beta.2: ...@@ -5886,7 +5987,7 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
get-caller-file@^2.0.5: get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
...@@ -7746,6 +7847,11 @@ playwright@^1.28.0: ...@@ -7746,6 +7847,11 @@ playwright@^1.28.0:
dependencies: dependencies:
playwright-core "1.28.0" playwright-core "1.28.0"
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
popmotion@11.0.3: popmotion@11.0.3:
version "11.0.3" version "11.0.3"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9"
...@@ -7867,6 +7973,16 @@ punycode@^2.1.0, punycode@^2.1.1: ...@@ -7867,6 +7973,16 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qrcode@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.1.tgz#0103f97317409f7bc91772ef30793a54cd59f0cb"
integrity sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==
dependencies:
dijkstrajs "^1.0.1"
encode-utf8 "^1.0.3"
pngjs "^5.0.0"
yargs "^15.3.1"
querystringify@^2.1.1: querystringify@^2.1.1:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
...@@ -7889,13 +8005,13 @@ react-clientside-effect@^1.2.6: ...@@ -7889,13 +8005,13 @@ react-clientside-effect@^1.2.6:
dependencies: dependencies:
"@babel/runtime" "^7.12.13" "@babel/runtime" "^7.12.13"
react-dom@18.1.0: react-dom@18.2.0:
version "18.1.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w== integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
scheduler "^0.22.0" scheduler "^0.23.0"
react-fast-compare@3.2.0: react-fast-compare@3.2.0:
version "3.2.0" version "3.2.0"
...@@ -7995,10 +8111,10 @@ react@17.0.2: ...@@ -7995,10 +8111,10 @@ react@17.0.2:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"
react@18.1.0: react@18.2.0:
version "18.1.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ== integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
...@@ -8150,6 +8266,11 @@ require-directory@^2.1.1: ...@@ -8150,6 +8266,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
requires-port@^1.0.0: requires-port@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
...@@ -8294,10 +8415,10 @@ saxes@^6.0.0: ...@@ -8294,10 +8415,10 @@ saxes@^6.0.0:
dependencies: dependencies:
xmlchars "^2.2.0" xmlchars "^2.2.0"
scheduler@^0.22.0: scheduler@^0.23.0:
version "0.22.0" version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ== integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
...@@ -8332,7 +8453,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: ...@@ -8332,7 +8453,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
set-blocking@~2.0.0: set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
...@@ -8601,10 +8722,10 @@ styled-jsx@5.0.4: ...@@ -8601,10 +8722,10 @@ styled-jsx@5.0.4:
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.4.tgz#5b1bd0b9ab44caae3dd1361295559706e044aa53" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.4.tgz#5b1bd0b9ab44caae3dd1361295559706e044aa53"
integrity sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ== integrity sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==
stylis@4.0.13: stylis@4.1.3:
version "4.0.13" version "4.1.3"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
sucrase@^3.20.0: sucrase@^3.20.0:
version "3.28.0" version "3.28.0"
...@@ -9123,6 +9244,11 @@ which-collection@^1.0.1: ...@@ -9123,6 +9244,11 @@ which-collection@^1.0.1:
is-weakmap "^2.0.1" is-weakmap "^2.0.1"
is-weakset "^2.0.1" is-weakset "^2.0.1"
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==
which-typed-array@^1.1.8: which-typed-array@^1.1.8:
version "1.1.9" version "1.1.9"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
...@@ -9205,6 +9331,11 @@ xmlchars@^2.2.0: ...@@ -9205,6 +9331,11 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
y18n@^5.0.5: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
...@@ -9225,11 +9356,36 @@ yaml@^2.1.1: ...@@ -9225,11 +9356,36 @@ yaml@^2.1.1:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207"
integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg== integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^21.0.1, yargs-parser@^21.1.1: yargs-parser@^21.0.1, yargs-parser@^21.1.1:
version "21.1.1" version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^15.3.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^17.3.1: yargs@^17.3.1:
version "17.6.2" version "17.6.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment