diff --git a/package.json b/package.json index 0c63f995b..ee991b94a 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "react": "18.2.0", "react-aria": "3.27.0", "react-cookie": "6.1.0", + "react-countup": "^6.4.2", "react-csv": "2.2.2", "react-dom": "18.2.0", "react-error-boundary": "4.0.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f67e263b9..447e719cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,6 +203,9 @@ dependencies: react-cookie: specifier: 6.1.0 version: 6.1.0(react@18.2.0) + react-countup: + specifier: ^6.4.2 + version: 6.4.2(@babel/core@7.20.12)(react@18.2.0) react-csv: specifier: 2.2.2 version: 2.2.2 @@ -476,7 +479,7 @@ packages: '@babel/parser': 7.20.13 '@babel/template': 7.20.7 '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -502,7 +505,7 @@ packages: '@babel/parser': 7.20.13 '@babel/template': 7.20.7 '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 convert-source-map: 1.8.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -538,7 +541,7 @@ packages: resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 jsesc: 2.5.2 source-map: 0.5.7 dev: true @@ -555,7 +558,7 @@ packages: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: @@ -563,7 +566,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-compilation-targets@7.20.7(@babel/core@7.17.8): @@ -665,7 +668,7 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-function-name@7.19.0: @@ -673,26 +676,26 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 /@babel/helper-member-expression-to-functions@7.20.7: resolution: {integrity: sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 /@babel/helper-module-transforms@7.20.11: resolution: {integrity: sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==} @@ -713,7 +716,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-plugin-utils@7.10.4: @@ -735,7 +738,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true @@ -749,7 +752,7 @@ packages: '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true @@ -758,20 +761,20 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 /@babel/helper-skip-transparent-expression-wrappers@7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} @@ -780,7 +783,6 @@ packages: /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} @@ -789,7 +791,6 @@ packages: /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} @@ -802,7 +803,7 @@ packages: '@babel/helper-function-name': 7.19.0 '@babel/template': 7.20.7 '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true @@ -821,7 +822,7 @@ packages: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 @@ -838,7 +839,7 @@ packages: resolution: {integrity: sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==} engines: {node: '>=6.0.0'} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/parser@7.20.13: @@ -1531,7 +1532,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.20.11 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.20 transitivePeerDependencies: - supports-color dev: true @@ -1644,7 +1645,7 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.20.12) - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.20.12): @@ -1841,7 +1842,7 @@ packages: '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.20.12) '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.20.12) '@babel/preset-modules': 0.1.5(@babel/core@7.20.12) - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.20.12) babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.20.12) babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.20.12) @@ -1872,7 +1873,7 @@ packages: '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.12) '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.20.12) - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 esutils: 2.0.3 dev: true @@ -1971,7 +1972,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -1999,7 +2000,7 @@ packages: resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 dev: true @@ -2018,7 +2019,6 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} @@ -5729,6 +5729,24 @@ packages: reselect: 4.1.8 dev: false + /@rollup/plugin-babel@6.0.4(@babel/core@7.20.12): + resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + rollup: + optional: true + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.18.6 + '@rollup/pluginutils': 5.0.2(rollup@2.78.0) + dev: false + /@rollup/plugin-commonjs@24.0.0(rollup@2.78.0): resolution: {integrity: sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g==} engines: {node: '>=14.0.0'} @@ -7074,7 +7092,7 @@ packages: '@babel/plugin-transform-react-jsx': 7.20.13(@babel/core@7.20.12) '@babel/preset-env': 7.20.2(@babel/core@7.20.12) '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/mdx1-csf': 0.0.1(@babel/core@7.20.12) core-js: 3.27.2 @@ -7248,7 +7266,7 @@ packages: '@babel/generator': 7.20.7 '@babel/parser': 7.20.13 '@babel/preset-env': 7.20.2(@babel/core@7.20.12) - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 '@mdx-js/mdx': 1.6.22 '@types/lodash': 4.14.191 js-string-escape: 1.0.1 @@ -7754,7 +7772,7 @@ packages: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: '@babel/parser': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.0 @@ -7763,20 +7781,20 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@types/babel__traverse@7.18.0: resolution: {integrity: sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==} dependencies: - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 dev: true /@types/bn.js@5.1.2: @@ -9421,7 +9439,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/template': 7.20.7 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 '@types/babel__core': 7.1.19 '@types/babel__traverse': 7.18.0 dev: true @@ -10514,6 +10532,10 @@ packages: path-type: 4.0.0 yaml: 1.10.2 + /countup.js@2.8.0: + resolution: {integrity: sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ==} + dev: false + /cp-file@7.0.0: resolution: {integrity: sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==} engines: {node: '>=8'} @@ -11795,7 +11817,7 @@ packages: engines: {node: '>=8.3.0'} dependencies: '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 c8: 7.12.0 transitivePeerDependencies: - supports-color @@ -14298,7 +14320,7 @@ packages: '@babel/generator': 7.20.7 '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.20.12) '@babel/traverse': 7.20.13 - '@babel/types': 7.20.7 + '@babel/types': 7.23.0 '@jest/expect-utils': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 @@ -16870,6 +16892,20 @@ packages: universal-cookie: 6.1.1 dev: false + /react-countup@6.4.2(@babel/core@7.20.12)(react@18.2.0): + resolution: {integrity: sha512-wdDrNb2lPFGbLb+i0FTgswPbWziubS6KZRII8NRpXmUCoZsi15PFbIHgBz60Dyxd4KPuRvwsK5aawIU4OPP3jA==} + peerDependencies: + react: '>= 16.3.0' + dependencies: + '@rollup/plugin-babel': 6.0.4(@babel/core@7.20.12) + countup.js: 2.8.0 + react: 18.2.0 + transitivePeerDependencies: + - '@babel/core' + - '@types/babel__core' + - rollup + dev: false + /react-csv@2.2.2: resolution: {integrity: sha512-RG5hOcZKZFigIGE8LxIEV/OgS1vigFQT4EkaHeKgyuCbUAu9Nbd/1RYq++bJcJJ9VOqO/n9TZRADsXNDR4VEpw==} dev: false diff --git a/src/app/HomeClient.tsx b/src/app/HomeClient.tsx index 536213770..958a7c37d 100644 --- a/src/app/HomeClient.tsx +++ b/src/app/HomeClient.tsx @@ -8,14 +8,19 @@ import * as React from 'react'; import { FC } from 'react'; import { DefaultTxListTabs } from './common/components/tx-lists/tabs/DefaultTxListTabs'; -import { BlocksList } from './components/BlockList'; +import { BlockList } from '@/features/blocks/components/BlockList'; import { Stats } from './stats/Stats'; export const HomeClientBase: FC = () => { console.log('[DEBUG] rendering home'); const { activeNetwork } = useGlobalContext(); return ( - + { {!activeNetwork.isSubnet && } - + + ); }; diff --git a/src/common/constants.ts b/src/common/constants.ts deleted file mode 100644 index 9e190d4eb..000000000 --- a/src/common/constants.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Transaction } from '@stacks/stacks-blockchain-api-types'; - -import packageJson from '../../package.json'; -import { TxStatus } from './types/tx'; - -export const MICROBLOCKS_ENABLED = true; - -export const IS_BROWSER = typeof document !== 'undefined'; - -export const STACKS_EXPLORER_APP_ICON = - 'https://blockstack-www.imgix.net/stacks-explorer-icon.png?auto=format&w=72'; - -export const DEFAULT_V2_INFO_ENDPOINT = '/v2/info'; - -export const SITE_NOTICE_BANNER_LABEL = - process.env.NEXT_PUBLIC_SITE_NOTICE_BANNER_LABEL || - process.env.NEXT_PUBLIC_SITE_NOTICE_BANNER_LABEL; - -export const SITE_NOTICE_BANNER_MESSAGE = - process.env.NEXT_PUBLIC_SITE_NOTICE_BANNER_MESSAGE || - process.env.NEXT_PUBLIC_SITE_NOTICE_BANNER_MESSAGE; -export const SITE_NOTICE_ENABLED = SITE_NOTICE_BANNER_LABEL && SITE_NOTICE_BANNER_MESSAGE; - -export const APP_DETAILS = { - name: 'Stacks Explorer', - icon: STACKS_EXPLORER_APP_ICON, -}; - -export const CONNECT_AUTH_ORIGIN = - process.env.NEXT_PUBLIC_CONNECT_AUTH_ORIGIN || 'https://pr-725.app.stacks.engineering'; - -export const DEFAULT_MAINNET_SERVER = - process.env.NEXT_PUBLIC_MAINNET_API_SERVER || 'https://api.hiro.so'; - -export const MAINNET_BTC_BLOCK_BASE_URL = - process.env.NEXT_PUBLIC_MAINNET_BTC_BLOCK_BASE_URL || 'https://mempool.space/block'; - -export const MAINNET_BTC_TX_BASE_URL = - process.env.NEXT_PUBLIC_MAINNET_BTC_TX_BASE_URL || 'https://mempool.space/tx'; - -export const MAINNET_BTC_ADDRESS_BASE_URL = - process.env.NEXT_PUBLIC_MAINNET_BTC_ADDRESS_BASE_URL || 'https://mempool.space/address'; - -export const DEFAULT_TESTNET_SERVER = - process.env.NEXT_PUBLIC_TESTNET_API_SERVER || 'https://api.testnet.hiro.so'; - -export const TESTNET_BTC_BLOCK_BASE_URL = - process.env.NEXT_PUBLIC_TESTNET_BTC_BLOCK_BASE_URL || 'https://mempool.space/testnet/block'; - -export const TESTNET_BTC_TX_BASE_URL = - process.env.NEXT_PUBLIC_TESTNET_BTC_TX_BASE_URL || 'https://mempool.space/testnet/tx'; - -export const TESTNET_BTC_ADDRESS_BASE_URL = - process.env.NEXT_PUBLIC_TESTNET_BTC_ADDRESS_BASE_URL || 'https://mempool.space/testnet/address'; - -export const DEFAULT_DEVNET_SERVER = 'http://localhost:3999'; - -export const VERSION = process.env.VERSION || process.env.VERSION || packageJson.version; - -export enum MODALS { - SEARCH = 'modals/search', - ADD_NETWORK = 'modals/add-network', - UNLOCKING_SCHEDULE = 'modals/unlocking-schedule', -} - -export const withApiServer = (apiServer: string) => (path?: string) => - path ? apiServer + path : apiServer; - -export const X_API_KEY = process.env.X_API_KEY ?? process.env.X_API_KEY ?? ''; - -export const RELEASE_TAG_NAME = - process.env.RELEASE_TAG_NAME ?? process.env.RELEASE_TAG_NAME ?? null; - -export const HIRO_HEADERS: HeadersInit = { - 'x-api-key': X_API_KEY, - 'x-hiro-product': 'explorer', - 'x-hiro-version': VERSION, -}; - -export const DEFAULT_BLOCKS_LIST_LIMIT = 10; -export const DEFAULT_LIST_LIMIT_SMALL = 10; -export const DEFAULT_LIST_LIMIT = 30; -export const DEFAULT_TX_EVENTS_LIMIT = 100; - -export const MAX_BLOCK_TRANSACTIONS_PER_CALL = 200; - -export const TransactionType = { - SMART_CONTRACT: 'smart_contract' as Transaction['tx_type'], - CONTRACT_CALL: 'contract_call' as Transaction['tx_type'], - TOKEN_TRANSFER: 'token_transfer' as Transaction['tx_type'], - COINBASE: 'coinbase' as Transaction['tx_type'], - POISON_MICROBLOCK: 'poison_microblock' as Transaction['tx_type'], -} as const; - -export const TransactionStatus: Record = { - PENDING: 'pending', - SUCCESS_ANCHOR_BLOCK: 'success_anchor_block', - SUCCESS_MICROBLOCK: 'success_microblock', - NON_CANONICAL: 'non_canonical', - FAILED: 'failed', - DROPPED: 'dropped', -}; - -export const BTC_BNS_CONTRACT = 'SP000000000000000000002Q6VF78.bns::names'; - -export const SUBNETS_PARENT_NETWORK_IDS = { - mainnet: '0x17000000', - testnet: '0xff000000', -}; - -export const PAGE_MAX_WIDTH = '1280px'; - -export const REDIS_URL = process.env.REDIS_URL || ''; diff --git a/src/components/section.tsx b/src/components/section.tsx index 94f563e09..b396c6159 100644 --- a/src/components/section.tsx +++ b/src/components/section.tsx @@ -62,3 +62,24 @@ export const Section: React.FC< ); } ); + +interface SectionWithControlsProps extends Omit { + title: string; + controls?: ReactNode; + footer?: ReactNode; +} + +export function SectionWithControls({ title, controls, footer }: SectionWithControlsProps) { + return ( + + + {title} + + {controls} + + + {footer} + + + ); +} diff --git a/src/features/blocks-visualizer/index.tsx b/src/features/blocks-visualizer/index.tsx deleted file mode 100644 index 643220a11..000000000 --- a/src/features/blocks-visualizer/index.tsx +++ /dev/null @@ -1,315 +0,0 @@ -import { buildUrl } from '@/app/common/utils/buildUrl'; -import { useApi } from '@/common/api/client'; -import { useGlobalContext } from '@/common/context/useAppContext'; -import { Box, Icon, Spinner, Tooltip } from '@/ui/components'; -import { StxIcon } from '@/ui/icons/StxIcon'; -import { useColorMode } from '@chakra-ui/react'; -import { css } from '@emotion/react'; -import { useRouter } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; -import { AiFillCheckCircle } from 'react-icons/ai'; -import { BsArrowRight, BsFillExclamationCircleFill } from 'react-icons/bs'; -import { TbCurrencyBitcoin } from 'react-icons/tb'; - -import { Block as BlockType } from '@stacks/stacks-blockchain-api-types'; - -import { useBlockListInfinite } from '../../app/common/queries/useBlockListInfinite'; - -const wrapperStyle = css` - display: flex; - width: 100%; - flex-wrap: nowrap; - justify-content: center; - padding: 0 15px 0 65px; - @media (max-width: 1024px) { - flex-direction: column-reverse; - align-items: center; - padding: 0 15px 45px 15px; - } -`; - -const blockStyle = css` - height: 60px; - width: 100%; - line-height: 60px; - background-color: #d9d9d9; - border-radius: 6px; - overflow-wrap: anywhere; - text-align: center; - cursor: pointer; - font-size: 12px; - color: #1b1b1b; -`; - -const blockWrapperStyle = css` - display: flex; - flex-wrap: nowrap; - flex-direction: column; - align-items: center; - justify-content: space-between; - padding: 10px; - border-radius: 10px; - margin-right: 28px; - flex-basis: 25%; - &:last-child { - margin-right: 0; - } - @media (max-width: 1024px) { - flex-direction: row; - height: auto; - flex-basis: 100%; - width: 100%; - margin-right: 0; - margin-bottom: 28px; - &:first-child { - margin-bottom: 0; - } - } -`; - -const blockAndArrowStyle = css` - display: flex; - flex-wrap: nowrap; - align-items: center; - position: relative; - width: 100%; - @media (max-width: 1024px) { - flex-direction: column; - } -`; - -const currentBlockStyle = css` - ${blockWrapperStyle}; - border-width: 2px; - border-style: dashed; - .block-box { - box-shadow: none; - background-color: #d9d9d9; - } -`; - -const blockchainIconWrapperStyle = css` - display: flex; - align-items: center; - justify-content: center; - text-align: center; - height: 40px; - width: 40px; - border-radius: 50%; - position: absolute; - left: -65px; - @media (max-width: 1024px) { - left: auto; - bottom: -65px; - } -`; -const timeStyle = css` - width: 100%; - @media (max-width: 1024px) { - //flex-direction: row; - } -`; - -const timeContainerStyle = css` - display: flex; - height: 60px; - font-size: 12px; - align-items: center; - justify-content: center; - gap: 7px; - @media (max-width: 1024px) { - height: auto; - } -`; - -const iconStyle = css``; - -const arrowStyle = css` - position: absolute; - right: -37px; - @media (max-width: 1024px) { - transform: rotate(-90deg); - top: -37px; - right: auto; - } -`; - -const secondsToHMS = (seconds: number) => ({ - numHours: Math.floor(((seconds % 31536000) % 86400) / 3600), - numMinutes: Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), - numSeconds: (((seconds % 31536000) % 86400) % 3600) % 60, -}); - -function secondsToString(seconds: number) { - const { numHours, numMinutes, numSeconds } = secondsToHMS(seconds); - if (numHours) - return `${numHours}h${numMinutes === 1 ? '' : 's'}${ - numMinutes ? ` ${numMinutes}m${numMinutes === 1 ? '' : 's'}` : '' - }`; - if (numMinutes) return `${numMinutes} min${numMinutes === 1 ? '' : 's'}`; - return `${numSeconds} sec${numMinutes === 1 ? '' : 's'}`; -} - -function formatSeconds(seconds?: number) { - if (typeof seconds === 'undefined') return ''; - const { numHours, numMinutes, numSeconds } = secondsToHMS(seconds); - return `${numHours ? `${numHours < 10 ? `0${numHours}` : numHours}:` : ''}${`${ - numMinutes < 10 ? `0${numMinutes}` : numMinutes - }:`}${`${numSeconds < 10 ? `0${numSeconds}` : numSeconds}`}`; -} - -interface BlockProps { - block: BlockType; - previousBtcBlockBurnTime: number; - displayBlockchainIcons?: boolean; -} - -interface BlockInfoProps { - tooltip: string; - icon: React.ReactNode; - time: string; -} - -const BlockInfo: React.FC = ({ tooltip, icon, time }) => ( - - - - {icon} - {time} - - - -); - -const Block: React.FC = ({ - displayBlockchainIcons, - previousBtcBlockBurnTime, - block, -}) => { - const stxBlockHeight = block.height; - const btcBlockBurnTime = block.burn_block_time; - const btcBlockHeight = block.burn_block_height; - const timeBetweenBlocks = btcBlockBurnTime - previousBtcBlockBurnTime; - const delayedBlock = timeBetweenBlocks && timeBetweenBlocks >= 11 * 60; - const BlockInfoIcon = delayedBlock ? BsFillExclamationCircleFill : AiFillCheckCircle; - const timeBetweenBlocksFormatted = secondsToString(timeBetweenBlocks); - const router = useRouter(); - const network = useGlobalContext().activeNetwork; - const colorMode = useColorMode().colorMode; - - return ( - - - {displayBlockchainIcons && ( - - - - )} - { - window?.open(`${network.btcBlockBaseUrl}/${btcBlockHeight}`, '_blank')?.focus(); - }} - > - {btcBlockHeight} - - - - } - time={timeBetweenBlocksFormatted} - /> - - {displayBlockchainIcons && ( - - - - )} - router.push(buildUrl(`/block/${encodeURIComponent(block.hash)}`, network))} - > - {stxBlockHeight} - - - - - ); -}; - -interface CurrentBlockProps { - lastBlock: BlockType; -} - -const CurrentBlock: React.FC = ({ lastBlock }) => { - const btcBlockHeight = lastBlock.burn_block_height + 1; - const stxBlockHeight = lastBlock.height + 1; - const lastBtcBlockBurnTime = lastBlock.burn_block_time; - const colorMode = useColorMode().colorMode; - - const [currentBlockWaitTime, setCurrentBlockWaitTime] = useState( - Math.ceil(Date.now() / 1000) - lastBtcBlockBurnTime - ); - - const formattedBlockWaitTime = formatSeconds(currentBlockWaitTime); - - useEffect(() => { - const handler = setInterval(() => { - setCurrentBlockWaitTime(currentBlockWaitTime + 1); - }, 1000); - return () => clearInterval(handler); - }); - - return ( - - - - {btcBlockHeight} - - - - - - {stxBlockHeight} - - - - ); -}; - -export const BlocksVisualizer: React.FC = () => { - const api = useApi(); - const { data: blocks, isLoading } = useBlockListInfinite(api); - - if (isLoading) return ; - - const lastFourBlocks = (blocks?.pages?.[0]?.results || []).slice(0, 4).reverse(); - if (lastFourBlocks.length === 0) return null; - const lastBlock = lastFourBlocks[3]; - return ( - - {lastFourBlocks.map((block: BlockType, i: number) => { - if (i === 0) return null; - return ( - - ); - })} - - - ); -}; diff --git a/src/features/blocks/components/BlockList.tsx b/src/features/blocks/components/BlockList.tsx new file mode 100644 index 000000000..8823b44f6 --- /dev/null +++ b/src/features/blocks/components/BlockList.tsx @@ -0,0 +1,207 @@ +import { SectionWithControls } from '@/components/section'; +import { Switch } from '@/ui/Switch'; +import { FormLabel } from '@/ui/FormLabel'; +import { FormControl } from '@/ui/FormControl'; +import React, { useEffect } from 'react'; +import { Stack } from '@/ui/Stack'; +import { Flex } from '@/ui/Flex'; +import { Text } from '@/ui/typography'; +import { Icon } from '@/ui/Icon'; +import { TfiReload } from 'react-icons/tfi'; +import { TextLink } from '@/ui/TextLink'; +import { useColorMode } from '@chakra-ui/react'; +import { keyframes } from '@emotion/css'; +import CountUp from 'react-countup'; +import { Button } from '@/ui/Button'; +import { HiOutlineHashtag } from 'react-icons/hi'; +import { BsGrid } from 'react-icons/bs'; +import { Tooltip } from '@/ui/Tooltip'; +import { HiMiniArrowUpRight } from 'react-icons/hi2'; + +const spin = keyframes` + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +`; + +export function BlockList() { + const colorMode = useColorMode().colorMode; + const [loading, setLoading] = React.useState(false); + const [oldBlocksCount, setOldBlocksCount] = React.useState(0); + const [newBlocksCount, setNewBlocksCount] = React.useState(16); + const [groupedByBtc, setGroupedByBtc] = React.useState(true); + const [liveUpdates, setLiveUpdates] = React.useState(false); + const [heightView, setHeightView] = React.useState(false); + const [blockView, setBlockView] = React.useState(true); + + useEffect(() => { + const intervalId = setInterval(() => { + setOldBlocksCount(newBlocksCount); + setNewBlocksCount(newBlocksCount + Math.floor(Math.random() * 50) + 3); + }, 5000); + + return () => { + clearInterval(intervalId); // Clear the interval when the component unmounts + }; + }, [newBlocksCount]); + return ( + + + + { + setGroupedByBtc(!groupedByBtc); + }} + isChecked={groupedByBtc} + /> + + Group by Bitcoin block + + + + + + + + + + + + + setLiveUpdates(!liveUpdates)} + isChecked={liveUpdates} + /> + + Live updates + + + + + + + {({ countUpRef, start }) => } + + {' '} + new Stacks blocks have come in + + { + console.log('clicked'); + setLoading(true); + setOldBlocksCount(newBlocksCount); + setNewBlocksCount(0); + setTimeout(() => { + setLoading(false); + }, 1250); + }} + > + + + Update + + + + + } + footer={ + <> + + + } + > + abc + + ); +} diff --git a/src/ui/Switch.tsx b/src/ui/Switch.tsx index abe674f3c..57a030dc5 100644 --- a/src/ui/Switch.tsx +++ b/src/ui/Switch.tsx @@ -18,6 +18,8 @@ export const Switch = forwardRef(({ children, size, ...rest minWidth={size || rest.minWidth} minHeight={size || rest.minHeight} borderColor={`border.${useColorMode().colorMode}`} + position={'relative'} + top={'1px'} {...rest} > {children}