diff --git a/.husky/pre-commit b/.husky/pre-commit index 6895490a2..45a45ee2f 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -yarn lint:css +yarn lint-staged yarn test -npx lint-staged diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 000000000..d30228a72 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,64 @@ +const package = require('./package.json') + +module.exports = { + customSyntax: 'postcss-scss', + extends: ['stylelint-config-standard'], + rules: { + 'no-empty-source': null, + // Due to the large number of non-standard names previously used, it is not possible to quickly correct all names, + // so these rules have been temporarily disabled. + 'selector-class-pattern': null, + 'selector-id-pattern': null, + 'custom-property-pattern': null, + // This rule provides little benefit relative to the cost of implementing it, so it has been disabled. + 'no-descending-specificity': null, + + 'selector-pseudo-class-no-unknown': [ + true, + { + // to support `:global` + ignorePseudoClasses: ['global'], + }, + ], + }, + + overrides: [ + { + files: ['*.scss', '**/*.scss'], + extends: ['stylelint-config-standard-scss'], + rules: { + 'scss/dollar-variable-pattern': null, + }, + }, + { + files: ['*.tsx', '**/*.tsx'], + customSyntax: 'postcss-styled-syntax', + // Currently, it is difficult to integrate postcss into styled-components to achieve CSS compatibility, unless a complex but not robust implementation is done manually. + // However, considering the implementation cost and the possibility that we may gradually abandon styled-components in the future, we do not adopt this solution. + // Therefore, without postcss to automatically handle compatibility, we need to handle it manually and avoid introducing syntax that is too high for stylelint. + // So here we introduce stylelint-no-unsupported-browser-features to help identify unsupported features and manually disable some stylelint rules that involve high-version features. + plugins: ['stylelint-no-unsupported-browser-features'], + rules: { + 'media-feature-range-notation': null, + 'plugin/no-unsupported-browser-features': [ + true, + { + browsers: package.browserslist, + // TODO: Perhaps the browserslist should be adjusted to a more reasonable range, at least to a level that is compatible with CSS variables. + ignore: [ + 'css-nesting', + 'css-sticky', + 'css-variables', + 'mdn-text-decoration-shorthand', + 'css-unset-value', + 'flexbox-gap', + 'css-font-stretch', + 'css-overscroll-behavior', + ], + ignorePartialSupport: true, + }, + ], + }, + }, + ], +} diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index 9b3086224..000000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "customSyntax": "postcss-styled-syntax", - "extends": ["stylelint-config-recommended", "stylelint-config-styled-components", "stylelint-config-standard"], - "rules": { - "no-empty-source": null, - "selector-class-pattern": null, - "selector-id-pattern": null - } -} diff --git a/package.json b/package.json index 2e7f37db7..de1db4932 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "jsbi": "3.2.5", "lint-staged": "^13.2.3", "moment": "2.29.4", + "observable-hooks": "^4.2.3", "react": "17.0.2", "react-dom": "17.0.2", "react-i18next": "11.18.6", @@ -38,16 +39,16 @@ "devDependencies": { "@sentry/webpack-plugin": "2.7.1", "@testing-library/react": "12.1.5", - "@types/echarts": "4.9.18", + "@types/echarts": "4.9.19", "@types/eslint": "7.29.0", "@types/jest": "26.0.24", - "@types/node": "16.18.50", + "@types/node": "16.18.58", "@types/react": "17.0.65", "@types/react-dom": "17.0.20", "@types/react-outside-click-handler": "^1.3.0", "@types/react-router-dom": "5.3.3", "@types/react-test-renderer": "^18.0.0", - "@types/styled-components": "5.1.26", + "@types/styled-components": "5.1.28", "@typescript-eslint/eslint-plugin": "^4.29.0", "@typescript-eslint/parser": "5.62.0", "antd-dayjs-webpack-plugin": "^1.0.6", @@ -63,15 +64,17 @@ "husky": "^7.0.1", "jest-styled-components": "^7.0.5", "mockdate": "^2.0.5", + "postcss-scss": "4.0.8", "postcss-styled-syntax": "^0.4.0", "prettier": "^2.8.8", "react-app-rewired": "2.2.1", "react-test-renderer": "^17.0.2", "rxjs": "7.8.1", "stylelint": "^15.10.1", - "stylelint-config-recommended": "^13.0.0", "stylelint-config-standard": "^34.0.0", + "stylelint-config-standard-scss": "^11.0.0", "stylelint-config-styled-components": "^0.1.1", + "stylelint-no-unsupported-browser-features": "7.0.0", "stylelint-processor-styled-components": "^1.10.0", "timezone-mock": "^1.1.4", "ts-jest": "27.1.5", @@ -79,8 +82,6 @@ }, "scripts": { "start": "react-app-rewired start", - "lint": "eslint src/**/*.{ts,tsx} --fix", - "lint:css": "stylelint src/**/*.tsx", "build": "react-app-rewired build", "test": "react-app-rewired test --watchAll=false", "eject": "react-app-rewired eject", @@ -104,6 +105,7 @@ ], "lint-staged": { "*.{ts,tsx}": "eslint --cache --fix", - "*.{ts,tsx,json,html,scss}": "prettier --write" + "*.{ts,tsx,json,html,scss}": "prettier --write", + "*.{scss,css,tsx}": "stylelint --fix" } } diff --git a/src/App.tsx b/src/App.tsx index 15c2baa65..aacfb16b7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,12 @@ import { useMemo } from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { ThemeProvider } from 'styled-components' -import 'antd/dist/antd.css' import Routers from './routes' import Toast from './components/Toast' -import withProviders, { useAppState } from './contexts/providers' import useInitApp from './contexts/providers/hook' import { isMainnet } from './utils/chain' import { DASQueryContextProvider } from './contexts/providers/dasQuery' +import { getPrimaryColor, getSecondaryColor } from './constants/common' const appStyle = { width: '100vw', @@ -17,15 +16,14 @@ const appStyle = { const queryClient = new QueryClient() -const App = withProviders(() => { +const App = () => { useInitApp() - const { app } = useAppState() const theme = useMemo( () => ({ - primary: app.primaryColor, - secondary: app.secondaryColor, + primary: getPrimaryColor(), + secondary: getSecondaryColor(), }), - [app.primaryColor, app.secondaryColor], + [], ) return ( @@ -40,6 +38,6 @@ const App = withProviders(() => { ) -}) +} export default App diff --git a/src/assets/fonts/fonts.css b/src/assets/fonts/fonts.css index 64582cb8f..e32583b1d 100644 --- a/src/assets/fonts/fonts.css +++ b/src/assets/fonts/fonts.css @@ -1,5 +1,5 @@ @font-face { - font-family: 'Lato'; + font-family: Lato; src: local('Lato'), url('./Lato-Regular.ttf') format('truetype'); font-weight: 300; font-style: normal; diff --git a/src/components/Alert/index.tsx b/src/components/Alert/index.tsx deleted file mode 100644 index 1632c9b75..000000000 --- a/src/components/Alert/index.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useEffect } from 'react' -import { useAppState, useDispatch } from '../../contexts/providers' -import { AlertPanel } from './styled' -import i18n, { currentLanguage } from '../../utils/i18n' -import { dayjs, parseSimpleDateNoSecond } from '../../utils/date' -import SimpleButton from '../SimpleButton' -import { ComponentActions } from '../../contexts/actions' -import { AppCachedKeys } from '../../constants/cache' -import { IS_MAINTAINING } from '../../constants/common' -import styles from './styles.module.scss' - -const FIFTEEN_MINUTES = 15 * 60 * 1000 - -const Alert = () => { - const dispatch = useDispatch() - const { - app: { appErrors }, - components: { maintenanceAlertVisible }, - statistics: { reorgStartedAt }, - } = useAppState() - const [startTime, endTime] = appErrors[2].message - - const hideAlert = () => { - sessionStorage.setItem(AppCachedKeys.MaintenanceAlert, 'hide') - dispatch({ - type: ComponentActions.UpdateMaintenanceAlertVisible, - payload: { - maintenanceAlertVisible: false, - }, - }) - } - - useEffect(() => { - const hideMaintenance = sessionStorage.getItem(AppCachedKeys.MaintenanceAlert) === 'hide' - if (startTime && endTime && !hideMaintenance) { - dispatch({ - type: ComponentActions.UpdateMaintenanceAlertVisible, - payload: { - maintenanceAlertVisible: true, - }, - }) - } - }, [startTime, endTime, dispatch]) - - if (reorgStartedAt && new Date(reorgStartedAt).getTime() + FIFTEEN_MINUTES < new Date().getTime()) { - return ( -
- {i18n.t('toast.handling-reorg', { - time: dayjs(reorgStartedAt).format('YYYY-MM-DD HH:mm:ss'), - })} -
- ) - } - - if (IS_MAINTAINING) { - return
{i18n.t('error.maintain')}
- } - - return maintenanceAlertVisible ? ( - -
- - {i18n.t('toast.maintenance', { - start: parseSimpleDateNoSecond(startTime, '-', false), - end: parseSimpleDateNoSecond(endTime, '-', false), - })} - -
- hideAlert()}> - {i18n.t('toast.dismiss')} - -
-
-
- ) : null -} - -export default Alert diff --git a/src/components/Alert/styled.tsx b/src/components/Alert/styled.tsx deleted file mode 100644 index b840dac59..000000000 --- a/src/components/Alert/styled.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import styled from 'styled-components' - -export const AlertPanel = styled.div` - position: sticky; - top: 0; - z-index: 9000; - - > div { - width: 100%; - height: 48px; - background: #fa8f00; - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - padding: 0 120px; - color: white; - font-size: 14px; - font-weight: 450; - - @media (max-width: 1440px) { - padding: 0 100px; - } - - @media (max-width: 1200px) { - padding: 0 45px; - height: 64px; - } - - @media (max-width: 750px) { - padding: 8px 18px; - height: ${(props: { isEn: boolean }) => (props.isEn ? '120px' : '100px')}; - flex-direction: column; - align-items: flex-start; - } - } - - .alert__dismiss__panel { - @media (max-width: 750px) { - width: 100%; - display: flex; - justify-content: flex-end; - } - } - - .alert__dismiss { - width: 100px; - height: 30px; - line-height: 30px; - border-radius: 2px; - border: solid 1px #fff; - text-align: center; - - @media (max-width: 1200px) { - margin-left: 30px; - } - } -` diff --git a/src/components/Banner/index.module.scss b/src/components/Banner/index.module.scss index e98dc92b9..88bd18fa2 100644 --- a/src/components/Banner/index.module.scss +++ b/src/components/Banner/index.module.scss @@ -9,7 +9,8 @@ $backgroudColor: #232323; background-repeat: no-repeat; background-position: center center; background-size: auto 100%; - @media (max-width: 750px) { + + @media (width <= 750px) { background-image: url('../../assets/ckb_explorer_banner_phone.svg'); } } diff --git a/src/components/Card/HashCard/index.tsx b/src/components/Card/HashCard/index.tsx index 4892931ee..a8959fa30 100644 --- a/src/components/Card/HashCard/index.tsx +++ b/src/components/Card/HashCard/index.tsx @@ -3,11 +3,9 @@ import { Link } from 'react-router-dom' import { Tooltip } from 'antd' import CopyIcon from '../../../assets/copy.png' import i18n from '../../../utils/i18n' -import { v2AxiosIns } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { copyElementValue } from '../../../utils/util' -import { AppActions } from '../../../contexts/actions' import SmallLoading from '../../Loading/SmallLoading' -import { useDispatch } from '../../../contexts/providers' import { useIsMobile, useNewAddr, useDeprecatedAddr } from '../../../utils/hook' import SimpleButton from '../../SimpleButton' import { ReactComponent as OpenInNew } from '../../../assets/open_in_new.svg' @@ -16,6 +14,7 @@ import { HashCardPanel, LoadingPanel } from './styled' import styles from './styles.module.scss' import AddressText from '../../AddressText' import { useDASAccount } from '../../../contexts/providers/dasQuery' +import { useSetToast } from '../../Toast' const DASInfo: FC<{ address: string }> = ({ address }) => { const alias = useDASAccount(address) @@ -50,7 +49,7 @@ export default ({ showDASInfoOnHeader?: boolean | string }) => { const isMobile = useIsMobile() - const dispatch = useDispatch() + const setToast = useSetToast() const isTx = i18n.t('transaction.transaction') === title const newAddr = useNewAddr(hash) @@ -58,13 +57,8 @@ export default ({ const counterpartAddr = newAddr === hash ? deprecatedAddr : newAddr const handleExportTxClick = async () => { - const res = await v2AxiosIns(`transactions/${hash}/raw`).catch(error => { - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: error.message, - }, - }) + const res = await explorerService.api.requesterV2(`transactions/${hash}/raw`).catch(error => { + setToast({ message: error.message }) }) if (!res) return @@ -110,12 +104,7 @@ export default ({ className="hash__copy_icon" onClick={() => { copyElementValue(document.getElementById('hash__value')) - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: i18n.t('common.copied'), - }, - }) + setToast({ message: i18n.t('common.copied') }) }} > {!loading && copy} diff --git a/src/components/Card/HashCard/styles.module.scss b/src/components/Card/HashCard/styles.module.scss index a709f450d..62b91cc6f 100644 --- a/src/components/Card/HashCard/styles.module.scss +++ b/src/components/Card/HashCard/styles.module.scss @@ -9,13 +9,16 @@ width: 22px; height: 22px; pointer-events: none; + path { fill: #999; } } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { height: 22px; margin-left: 2px; + svg { width: 16px; height: 16px; @@ -34,9 +37,11 @@ cursor: pointer; width: 22px; height: 22px; + &:hover { color: var(--primary-color); } + svg { pointer-events: none; } @@ -62,7 +67,7 @@ font-size: 16px; font-weight: 500; - @media (max-width: 750px) { + @media (width <= 750px) { max-width: 90px; font-size: 13px; } diff --git a/src/components/Card/TitleCard/index.tsx b/src/components/Card/TitleCard/index.tsx index c960e2919..fe4d9b1ff 100644 --- a/src/components/Card/TitleCard/index.tsx +++ b/src/components/Card/TitleCard/index.tsx @@ -1,3 +1,4 @@ +import classNames from 'classnames' import { TitleCardPanel } from './styled' export default ({ @@ -5,14 +6,16 @@ export default ({ isSingle, className, rear, + rearClassName, }: { title: React.ReactNode isSingle?: boolean className?: string rear?: React.ReactNode + rearClassName?: string }) => (
{title}
- {rear ?
{rear}
: null} + {rear ?
{rear}
: null}
) diff --git a/src/components/Card/TitleCard/styled.tsx b/src/components/Card/TitleCard/styled.tsx index 5be66eb68..8e057da79 100644 --- a/src/components/Card/TitleCard/styled.tsx +++ b/src/components/Card/TitleCard/styled.tsx @@ -41,8 +41,9 @@ export const TitleCardPanel = styled.div` @media (max-width: 750px) { flex-direction: column-reverse; + > div:first-child { - padding: 16px 0 0 0; + padding: 16px 0 0; justify-content: flex-end; } } diff --git a/src/components/Content/index.tsx b/src/components/Content/index.tsx index 2e53749b5..9846cd689 100644 --- a/src/components/Content/index.tsx +++ b/src/components/Content/index.tsx @@ -1,18 +1,12 @@ import { ReactNode } from 'react' import styled from 'styled-components' -import { useAppState } from '../../contexts/providers' -import MobileMenu from '../Header/MobileMenu' const ContentPanel = styled.div` width: 100%; overflow-x: hidden; flex: 1; - margin-top: var(--navbar-height); background: #ededed; ` export default ({ children, style }: { children: ReactNode; style?: any }) => { - const { - components: { mobileMenuVisible }, - } = useAppState() - return {mobileMenuVisible ? : children} + return {children} } diff --git a/src/components/Dropdown/Language/index.tsx b/src/components/Dropdown/Language/index.tsx index cf3df9ac0..4b1b4d7d3 100644 --- a/src/components/Dropdown/Language/index.tsx +++ b/src/components/Dropdown/Language/index.tsx @@ -1,6 +1,4 @@ import i18n, { currentLanguage, changeLanguage } from '../../../utils/i18n' -import { useDispatch } from '../../../contexts/providers' -import { AppActions } from '../../../contexts/actions' import { LanguagePanel } from './styled' import SimpleButton from '../../SimpleButton' @@ -12,19 +10,12 @@ export const languageText = (lan: 'en' | 'zh' | null, reverse?: boolean) => { } export default ({ setShow, left, top }: { setShow: Function; left: number; top: number }) => { - const dispatch = useDispatch() const hideDropdown = () => { setShow(false) } const handleLanguage = () => { hideDropdown() changeLanguage(currentLanguage() === 'en' ? 'zh' : 'en') - dispatch({ - type: AppActions.UpdateAppLanguage, - payload: { - language: currentLanguage() === 'en' ? 'zh' : 'en', - }, - }) } return ( diff --git a/src/components/Header/BlockchainComp/index.tsx b/src/components/Header/BlockchainComp/index.tsx index a0c554aff..22779bd71 100644 --- a/src/components/Header/BlockchainComp/index.tsx +++ b/src/components/Header/BlockchainComp/index.tsx @@ -4,13 +4,12 @@ import { isMainnet } from '../../../utils/chain' import WhiteDropdownIcon from '../../../assets/white_dropdown.png' import BlueDropUpIcon from '../../../assets/blue_drop_up.png' import GreenDropUpIcon from '../../../assets/green_drop_up.png' -import { useAppState } from '../../../contexts/providers' import { HeaderBlockchainPanel, MobileSubMenuPanel } from './styled' import SimpleButton from '../../SimpleButton' import ChainDropdown from '../../Dropdown/ChainType' import { useIsMobile } from '../../../utils/hook' import { ChainName, MAINNET_URL, TESTNET_URL } from '../../../constants/common' -import { fetchNodeVersion } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { AppCachedKeys } from '../../../constants/cache' import { fetchCachedData, storeCachedData } from '../../../utils/cache' @@ -27,15 +26,12 @@ const handleVersion = (nodeVersion: string) => { } const BlockchainDropdown: FC<{ nodeVersion: string }> = ({ nodeVersion }) => { - const { - app: { language }, - } = useAppState() const [showChainType, setShowChainType] = useState(false) const [chainTypeLeft, setChainTypeLeft] = useState(0) const [chainTypeTop, setChainTypeTop] = useState(0) useLayoutEffect(() => { - if (showChainType && language) { + if (showChainType) { const chainDropdownComp = document.getElementById('header__blockchain__panel') if (chainDropdownComp) { const chainDropdownReact = chainDropdownComp.getBoundingClientRect() @@ -45,7 +41,7 @@ const BlockchainDropdown: FC<{ nodeVersion: string }> = ({ nodeVersion }) => { } } } - }, [showChainType, language]) + }, [showChainType]) return ( { const query = useQuery( ['node_version'], async () => { - const wrapper = await fetchNodeVersion() + const wrapper = await explorerService.api.fetchNodeVersion() const nodeVersion = wrapper.attributes.version storeCachedData(AppCachedKeys.Version, `${nodeVersion}&${new Date().getTime()}`) return nodeVersion diff --git a/src/components/Header/LanguageComp/index.tsx b/src/components/Header/LanguageComp/index.tsx index 9fbd85465..0665a6f5e 100644 --- a/src/components/Header/LanguageComp/index.tsx +++ b/src/components/Header/LanguageComp/index.tsx @@ -1,6 +1,4 @@ -import { useState, useLayoutEffect, memo } from 'react' -import { useIsMobile } from '../../../utils/hook' -import { useAppState, useDispatch } from '../../../contexts/providers' +import { useState, useLayoutEffect, FC } from 'react' import i18n, { currentLanguage, changeLanguage } from '../../../utils/i18n' import { HeaderLanguagePanel, MobileSubMenuPanel } from './styled' import SimpleButton from '../../SimpleButton' @@ -11,50 +9,18 @@ import GreenDropUpIcon from '../../../assets/green_drop_up.png' import { isMainnet } from '../../../utils/chain' import LanDropdown, { languageText } from '../../Dropdown/Language' -import { AppDispatch } from '../../../contexts/reducer' -import { ComponentActions, AppActions } from '../../../contexts/actions' - const getDropdownIcon = (showDropdown: boolean) => { if (!showDropdown) return WhiteDropdownIcon return isMainnet() ? GreenDropUpIcon : BlueDropUpIcon } -const languageAction = (dispatch: AppDispatch) => { - changeLanguage(currentLanguage() === 'en' ? 'zh' : 'en') - dispatch({ - type: AppActions.UpdateAppLanguage, - payload: { - language: currentLanguage() === 'en' ? 'zh' : 'en', - }, - }) - dispatch({ - type: ComponentActions.UpdateHeaderMobileMenuVisible, - payload: { - mobileMenuVisible: false, - }, - }) -} - -const hideMobileMenu = (dispatch: AppDispatch) => { - dispatch({ - type: ComponentActions.UpdateHeaderMobileMenuVisible, - payload: { - mobileMenuVisible: false, - }, - }) -} - -const LanguageDropdown = () => { - const { - app: { language }, - } = useAppState() - +export const LanguageDropdown = () => { const [showLanguage, setShowLanguage] = useState(false) const [languageLeft, setLanguageLeft] = useState(0) const [languageTop, setLanguageTop] = useState(0) useLayoutEffect(() => { - if (showLanguage && language) { + if (showLanguage) { const languageDropdownComp = document.getElementById('header__language__panel') if (languageDropdownComp) { const languageDropdownReact = languageDropdownComp.getBoundingClientRect() @@ -64,7 +30,7 @@ const LanguageDropdown = () => { } } } - }, [showLanguage, language]) + }, [showLanguage]) return ( { ) } -const LanguageMenu = () => { - const dispatch = useDispatch() +export const LanguageMenu: FC<{ hideMobileMenu: () => void }> = ({ hideMobileMenu }) => { const [showSubMenu, setShowSubMenu] = useState(false) return ( @@ -116,7 +81,7 @@ const LanguageMenu = () => { { - hideMobileMenu(dispatch) + hideMobileMenu() }} > {currentLanguage() === 'en' ? i18n.t('navbar.language_en') : i18n.t('navbar.language_zh')} @@ -124,7 +89,8 @@ const LanguageMenu = () => { { - languageAction(dispatch) + changeLanguage(currentLanguage() === 'en' ? 'zh' : 'en') + hideMobileMenu() }} > {currentLanguage() === 'en' ? i18n.t('navbar.language_zh') : i18n.t('navbar.language_en')} @@ -134,5 +100,3 @@ const LanguageMenu = () => { ) } - -export default memo(() => (useIsMobile() ? : )) diff --git a/src/components/Header/MobileMenu/index.tsx b/src/components/Header/MobileMenu/index.tsx index 88d0f771b..954c0efd9 100644 --- a/src/components/Header/MobileMenu/index.tsx +++ b/src/components/Header/MobileMenu/index.tsx @@ -1,14 +1,14 @@ import { MobileMenusPanel } from './styled' import MenuItems from '../MenusComp' import { SearchComp } from '../SearchComp' -import LanguageComp from '../LanguageComp' +import { LanguageMenu } from '../LanguageComp' import BlockchainComp from '../BlockchainComp' -export default () => ( +export default ({ hideMobileMenu }: { hideMobileMenu: () => void }) => ( - - {}} /> + + ) diff --git a/src/components/Header/MobileMenu/styled.tsx b/src/components/Header/MobileMenu/styled.tsx index 31ef9adc0..657b970ab 100644 --- a/src/components/Header/MobileMenu/styled.tsx +++ b/src/components/Header/MobileMenu/styled.tsx @@ -1,15 +1,13 @@ import styled from 'styled-components' export const MobileMenusPanel = styled.div` + flex: 1; width: 100%; background: #1c1c1c; display: flex; flex-direction: column; - position: fixed; - position: -webkit-fixed; z-index: 2; color: white; - top: var(--navbar-height); - bottom: 0; - overflow: hidden; + overflow: auto; + overscroll-behavior: contain; ` diff --git a/src/components/Header/SearchComp/index.tsx b/src/components/Header/SearchComp/index.tsx index 08b9074b1..5b0a4c623 100644 --- a/src/components/Header/SearchComp/index.tsx +++ b/src/components/Header/SearchComp/index.tsx @@ -4,10 +4,14 @@ import SearchLogo from '../../../assets/search_white.png' import { HeaderSearchPanel, HeaderSearchBarPanel } from './styled' export const SearchComp: FC<{ - expanded: boolean - setExpanded: (expanded: boolean) => void -}> = memo(({ expanded, setExpanded }) => { - const onEditEnd = useCallback(() => setExpanded(false), [setExpanded]) + expanded?: boolean + setExpanded?: (expanded: boolean) => void + hideMobileMenu?: () => void +}> = memo(({ expanded, setExpanded, hideMobileMenu }) => { + const onEditEnd = useCallback(() => { + setExpanded?.(false) + hideMobileMenu?.() + }, [hideMobileMenu, setExpanded]) if (!expanded) { return ( @@ -16,7 +20,7 @@ export const SearchComp: FC<{ tabIndex={-1} onKeyDown={() => {}} onClick={() => { - setExpanded(true) + setExpanded?.(true) }} > header search bar diff --git a/src/components/Header/index.module.scss b/src/components/Header/index.module.scss index 8cdac0f95..d2643b1a8 100644 --- a/src/components/Header/index.module.scss +++ b/src/components/Header/index.module.scss @@ -15,3 +15,15 @@ min-width: 0; } } + +.StickyContainer { + position: sticky; + top: 0; + z-index: 10; + display: flex; + flex-direction: column; + + &.expanded { + height: 100vh; + } +} diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 006190e57..51430c30e 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,16 +1,19 @@ import { FC, ReactNode, useEffect, useRef, useState } from 'react' import { useLocation } from 'react-router' +import classNames from 'classnames' +import { createBrowserHistory } from 'history' import LogoIcon from '../../assets/ckb_logo.png' import { HeaderPanel, HeaderEmptyPanel, HeaderMobileMenuPanel, HeaderLogoPanel } from './styled' -import { useAppState, useDispatch } from '../../contexts/providers/index' -import { ComponentActions } from '../../contexts/actions' import MenusComp from './MenusComp' import { SearchComp } from './SearchComp' -import LanguageComp from './LanguageComp' +import { LanguageDropdown } from './LanguageComp' import BlockchainComp from './BlockchainComp' -import { currentLanguage } from '../../utils/i18n' import { useElementSize, useIsMobile } from '../../utils/hook' import styles from './index.module.scss' +import MaintainAlert from '../MaintainAlert' +import Sheet from '../Sheet' +import { createGlobalState, useGlobalState } from '../../utils/state' +import MobileMenu from './MobileMenu' const LogoComp = () => ( @@ -18,22 +21,12 @@ const LogoComp = () => ( ) -const MobileMenuComp = () => { - const dispatch = useDispatch() - const { - components: { mobileMenuVisible }, - } = useAppState() +const MobileMenuComp: FC<{ mobileMenuVisible: boolean; setMobileMenuVisible: (value: boolean) => void }> = ({ + mobileMenuVisible, + setMobileMenuVisible, +}) => { return ( - { - dispatch({ - type: ComponentActions.UpdateHeaderMobileMenuVisible, - payload: { - mobileMenuVisible: !mobileMenuVisible, - }, - }) - }} - > + setMobileMenuVisible(!mobileMenuVisible)}>
@@ -65,45 +58,87 @@ const AutoExpand: FC<{ ) } +const globalShowHeaderSearchBarCounter = createGlobalState(0) + +export function useShowSearchBarInHeader(show: boolean) { + const [, setCounter] = useGlobalState(globalShowHeaderSearchBarCounter) + + useEffect(() => { + if (!show) return + + setCounter(counter => counter + 1) + return () => setCounter(counter => counter - 1) + }, [show, setCounter]) +} + +export function useIsShowSearchBarInHeader() { + const [counter] = useGlobalState(globalShowHeaderSearchBarCounter) + return counter > 0 +} + +const useRouterLocation = (callback: () => void) => { + const history = createBrowserHistory() + const savedCallback = useRef(() => {}) + useEffect(() => { + savedCallback.current = callback + }) + useEffect(() => { + const currentCallback = () => { + savedCallback.current() + } + const listen = history.listen(() => { + currentCallback() + }) + return () => { + listen() + } + }, [history]) +} + export default () => { const isMobile = useIsMobile() const { pathname } = useLocation() - const dispatch = useDispatch() - const { - components: { headerSearchBarVisible, maintenanceAlertVisible }, - } = useAppState() + // TODO: This hard-coded implementation is not ideal, but currently the header is loaded before the page component, + // so we can only handle it this way temporarily, otherwise there will be flickering during loading. + const defaultSearchBarVisible = pathname !== '/' && pathname !== '/search/fail' + const isShowSearchBar = useIsShowSearchBarInHeader() + const [mobileMenuVisible, setMobileMenuVisible] = useState(false) - useEffect(() => { - dispatch({ - type: ComponentActions.UpdateHeaderSearchBarVisible, - payload: { - headerSearchBarVisible: pathname !== '/' && pathname !== '/search/fail', - }, - }) - }, [dispatch, pathname]) + useRouterLocation(() => setMobileMenuVisible(false)) return ( - - - {!isMobile && ( - <> - } - expandableWidthRange={{ minimum: 320, maximum: 440 }} - renderExpandable={(expanded, setExpanded) => - headerSearchBarVisible && - } - /> - - - - )} - {isMobile && ( - <> - - - - )} - +
+ + + + {!isMobile && ( + <> + } + expandableWidthRange={{ minimum: 320, maximum: 440 }} + renderExpandable={(expanded, setExpanded) => + (defaultSearchBarVisible || isShowSearchBar) && ( + + ) + } + /> + + + + )} + {isMobile && ( + <> + + + + )} + + + {mobileMenuVisible && setMobileMenuVisible(false)} />} +
) } diff --git a/src/components/Header/styled.tsx b/src/components/Header/styled.tsx index 3da7b6ef4..256871ce9 100644 --- a/src/components/Header/styled.tsx +++ b/src/components/Header/styled.tsx @@ -7,11 +7,7 @@ export const HeaderPanel = styled.div` width: 100%; min-height: var(--navbar-height); background-color: #040607; - position: fixed; - position: -webkit-fixed; overflow: visible; - top: ${(props: { isNotTop?: boolean }) => (props.isNotTop ? '48px' : '0')}; - z-index: 10; display: flex; align-items: center; flex-wrap: wrap; @@ -23,12 +19,10 @@ export const HeaderPanel = styled.div` @media (max-width: 1200px) { padding: 0 45px; - top: ${(props: { isNotTop?: boolean }) => (props.isNotTop ? 'var(--navbar-height)' : '0')}; } @media (max-width: 780px) { padding: 0 18px; - top: ${(props: { isNotTop?: boolean; isEn: boolean }) => (props.isNotTop ? (props.isEn ? '120px' : '100px') : '0')}; } ` diff --git a/src/components/MaintainAlert/index.tsx b/src/components/MaintainAlert/index.tsx new file mode 100644 index 000000000..77e395e14 --- /dev/null +++ b/src/components/MaintainAlert/index.tsx @@ -0,0 +1,13 @@ +import i18n from '../../utils/i18n' +import { IS_MAINTAINING } from '../../constants/common' +import styles from './styles.module.scss' + +const MaintainAlert = () => { + if (IS_MAINTAINING) { + return
{i18n.t('error.maintain')}
+ } + + return null +} + +export default MaintainAlert diff --git a/src/components/Alert/styles.module.scss b/src/components/MaintainAlert/styles.module.scss similarity index 86% rename from src/components/Alert/styles.module.scss rename to src/components/MaintainAlert/styles.module.scss index bb4a92fbf..c50fa3457 100644 --- a/src/components/Alert/styles.module.scss +++ b/src/components/MaintainAlert/styles.module.scss @@ -12,7 +12,7 @@ background-color: #fa8f00; z-index: 9; - @media screen and (max-width: 800px) { + @media screen and (width <= 800px) { text-align: left; } } diff --git a/src/components/NftCollectionInventory/index.tsx b/src/components/NftCollectionInventory/index.tsx index 3fc77be3e..f21baff5a 100644 --- a/src/components/NftCollectionInventory/index.tsx +++ b/src/components/NftCollectionInventory/index.tsx @@ -8,7 +8,7 @@ import { ReactComponent as Cover } from '../../assets/nft_cover.svg' import i18n from '../../utils/i18n' import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { handleNftImgError, patchMibaoImg } from '../../utils/util' const primaryColor = getPrimaryColor() @@ -33,7 +33,7 @@ const NftCollectionInventory: React.FC<{ isLoading: boolean }> = ({ list, collection, isLoading }) => { const { data: info } = useQuery>(['collection-info', collection], () => - v2AxiosIns(`nft/collections/${collection}`), + explorerService.api.requesterV2(`nft/collections/${collection}`), ) if (!list.length) { diff --git a/src/components/NftCollectionInventory/styles.module.scss b/src/components/NftCollectionInventory/styles.module.scss index 66db8fa78..57d796897 100644 --- a/src/components/NftCollectionInventory/styles.module.scss +++ b/src/components/NftCollectionInventory/styles.module.scss @@ -24,21 +24,25 @@ border-radius: 8px; object-fit: cover; } + .tokenId, .owner { display: flex; width: 100%; line-height: 1em; + span:first-of-type { flex-basis: 80px; flex-shrink: 0; } + span, a { overflow: hidden; text-overflow: ellipsis; } } + .tokenId { padding-top: 15px; padding-bottom: 10px; @@ -50,15 +54,15 @@ } } - @media screen and (max-width: 1280px) { + @media screen and (width <= 1280px) { grid-template-columns: repeat(3, 1fr); } - @media screen and (max-width: 860px) { + @media screen and (width <= 860px) { grid-template-columns: repeat(2, 1fr); } - @media screen and (max-width: 540px) { + @media screen and (width <= 540px) { display: flex; flex-direction: column; align-items: center; diff --git a/src/components/NftCollectionOverview/index.tsx b/src/components/NftCollectionOverview/index.tsx index 0fb3ec2ed..7a8730b2a 100644 --- a/src/components/NftCollectionOverview/index.tsx +++ b/src/components/NftCollectionOverview/index.tsx @@ -2,7 +2,7 @@ import type { AxiosResponse } from 'axios' import { Link } from 'react-router-dom' import { useQuery } from 'react-query' import { Tooltip } from 'antd' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import i18n from '../../utils/i18n' import styles from './styles.module.scss' import { handleNftImgError, patchMibaoImg } from '../../utils/util' @@ -23,7 +23,7 @@ interface InfoRes { const NftCollectionOverview = ({ id }: { id: string }) => { const { isLoading, data } = useQuery>(['collection-info', id], () => - v2AxiosIns(`nft/collections/${id}`), + explorerService.api.requesterV2(`nft/collections/${id}`), ) const info = data?.data diff --git a/src/components/NftCollectionOverview/styles.module.scss b/src/components/NftCollectionOverview/styles.module.scss index 3ea56a85a..a5ff0c30c 100644 --- a/src/components/NftCollectionOverview/styles.module.scss +++ b/src/components/NftCollectionOverview/styles.module.scss @@ -3,7 +3,7 @@ background-color: #fff; margin-bottom: 18px; padding: 20px 40px; - box-shadow: #dfdfdf 2px 2px 6px 0px; + box-shadow: #dfdfdf 2px 2px 6px 0; border-radius: 6px; .header { @@ -11,6 +11,7 @@ font-size: 20px; font-weight: 700; line-height: 30px; + img { width: 30px; height: 30px; @@ -35,13 +36,13 @@ color: #494949; } -@media screen and (max-width: 1200px) { +@media screen and (width <= 1200px) { .container { margin: 40px 45px 24px; } } -@media screen and (max-width: 750px) { +@media screen and (width <= 750px) { .container { margin: 40px 8px 24px; } diff --git a/src/components/NftCollectionTransfers/index.tsx b/src/components/NftCollectionTransfers/index.tsx index e7adc26bf..952889f79 100644 --- a/src/components/NftCollectionTransfers/index.tsx +++ b/src/components/NftCollectionTransfers/index.tsx @@ -11,7 +11,7 @@ import i18n from '../../utils/i18n' import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' import { handleNftImgError, patchMibaoImg } from '../../utils/util' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { dayjs } from '../../utils/date' import { useParsedDate, useTimestamp } from '../../utils/hook' @@ -28,7 +28,7 @@ const NftCollectionTransfers: FC = props => { const { collection } = props const { data: info } = useQuery>(['collection-info', collection], () => - v2AxiosIns(`nft/collections/${collection}`), + explorerService.api.requesterV2(`nft/collections/${collection}`), ) return ( diff --git a/src/components/NftCollectionTransfers/styles.module.scss b/src/components/NftCollectionTransfers/styles.module.scss index 40d6acccd..97673636f 100644 --- a/src/components/NftCollectionTransfers/styles.module.scss +++ b/src/components/NftCollectionTransfers/styles.module.scss @@ -10,6 +10,7 @@ position: relative; font-size: 14px; font-weight: 700; + th { vertical-align: middle; padding-top: 12px; @@ -70,7 +71,7 @@ } .noRecord { - text-align: center !important; + text-align: center; } ul { @@ -81,32 +82,39 @@ ul, dl { list-style: none; + .item { padding-bottom: 16px; } } + li { padding: 16px; + &:not(:last-child) { border-bottom: 1px solid #d8d8d8; } } + dl > div { display: flex; width: 100%; } + dt { width: 100px; } + dd { text-align: right; flex: 1; } - @media screen and (max-width: 1200px) { + @media screen and (width <= 1200px) { table { display: none; } + ul { display: block; } @@ -120,13 +128,16 @@ .tokenId { overflow: hidden; text-overflow: ellipsis; - @media screen and (max-width: 1800px) { + + @media screen and (width <= 1800px) { max-width: 200px; } - @media screen and (max-width: 1400px) { + + @media screen and (width <= 1400px) { max-width: 120px; } - @media screen and (max-width: 1200px) { + + @media screen and (width <= 1200px) { max-width: 80px; } } diff --git a/src/components/NftHolderList/styles.module.scss b/src/components/NftHolderList/styles.module.scss index 506d67bad..80b3f3e46 100644 --- a/src/components/NftHolderList/styles.module.scss +++ b/src/components/NftHolderList/styles.module.scss @@ -11,6 +11,7 @@ position: relative; font-size: 14px; font-weight: 700; + th { vertical-align: middle; padding-top: 12px; @@ -18,10 +19,11 @@ text-transform: capitalize; white-space: nowrap; - @media (max-width: 750px) { + @media (width <= 750px) { &:first-child { padding-right: 0; } + &:last-child { padding-left: 0; } @@ -49,7 +51,7 @@ max-width: 0.25vw; &:last-child { - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { text-align: right; } } @@ -58,7 +60,8 @@ &:last-child { padding-left: 40px; padding-right: 40px; - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { padding-left: 16px; padding-right: 16px; } @@ -66,7 +69,7 @@ } .noRecord { - text-align: center !important; + text-align: center; } } @@ -75,13 +78,13 @@ font-family: inherit; } -@media screen and (min-width: 1024px) { +@media screen and (width >= 1024px) { .addr:last-child { display: none; } } -@media screen and (max-width: 1024px) { +@media screen and (width <= 1024px) { .addr:first-child { display: none; } diff --git a/src/components/NftItemTransfers/styles.module.scss b/src/components/NftItemTransfers/styles.module.scss index 85835b945..69f40bfc9 100644 --- a/src/components/NftItemTransfers/styles.module.scss +++ b/src/components/NftItemTransfers/styles.module.scss @@ -2,10 +2,6 @@ background: #fff; border-top-right-radius: 6px; padding: 0 15px; - ul { - margin: 0; - margin-bottom: 4px; - } table { position: relative; @@ -17,6 +13,7 @@ position: relative; font-size: 14px; font-weight: 700; + th { vertical-align: middle; padding-top: 12px; @@ -24,7 +21,8 @@ text-transform: capitalize; white-space: nowrap; } - &:after { + + &::after { position: absolute; left: -15px; top: 44px; @@ -91,37 +89,46 @@ ul { display: none; + margin: 0; + margin-bottom: 4px; } ul, dl { list-style: none; + .item { padding-bottom: 16px; } } + li { padding: 16px; + &:not(:last-child) { border-bottom: 1px solid #d8d8d8; } } + dl > div { display: flex; width: 100%; } + dt { width: 100px; } + dd { text-align: right; flex: 1; } - @media screen and (max-width: 1200px) { + @media screen and (width <= 1200px) { table { display: none; } + ul { display: block; } diff --git a/src/components/Pagination/styled.tsx b/src/components/Pagination/styled.tsx index 19ba57c9a..89b274e84 100644 --- a/src/components/Pagination/styled.tsx +++ b/src/components/Pagination/styled.tsx @@ -203,7 +203,7 @@ export const PaginationRightItem = styled.div` background: #ddd; } - @media (max-max-width: 750px) { + @media (max-width: 750px) { margin-left: 10px; font-size: 12px; } diff --git a/src/components/PaginationWithRear/styles.module.scss b/src/components/PaginationWithRear/styles.module.scss index 2d8847412..361b51cb5 100644 --- a/src/components/PaginationWithRear/styles.module.scss +++ b/src/components/PaginationWithRear/styles.module.scss @@ -1,13 +1,12 @@ .paginationWithRear { display: flex; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; justify-content: flex-end; width: 100%; margin-top: 4px; background-color: white; border-radius: 0 0 6px 6px; - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 2px 6px 0 rgb(0 0 0 / 12%); padding: 0 40px; .pagination { @@ -15,7 +14,8 @@ box-shadow: none; flex: 1 0.8 auto; min-width: 650px; - @media (max-width: 750px) { + + @media (width <= 750px) { min-width: auto; border-radius: 0; } @@ -26,6 +26,7 @@ max-width: 120px; } } + :global(.pagination__goto__page) { min-width: 42px; } @@ -34,7 +35,7 @@ display: flex; justify-content: flex-end; - @media (max-width: 750px) { + @media (width <= 750px) { height: 50px; border-radius: 0 0 6px 6px; border-top: 1px solid #f0f0f0; @@ -42,7 +43,7 @@ } } - @media (max-width: 750px) { + @media (width <= 750px) { justify-content: flex-end; margin-top: 5px; padding: 0 20px; diff --git a/src/components/Search/Filter/styled.tsx b/src/components/Search/Filter/styled.tsx index 3f991cab5..a66b67719 100644 --- a/src/components/Search/Filter/styled.tsx +++ b/src/components/Search/Filter/styled.tsx @@ -29,7 +29,7 @@ export const FilterImage = styled(SimpleButton)` z-index: 2; display: flex; justify-content: center; - cursor: ${(props: { isClear?: boolean }) => (props.isClear ? 'pointer' : 'default !important')}; + cursor: ${(props: { isClear?: boolean }) => (props.isClear ? 'pointer' : 'default')}; @media (max-width: 750px) { margin-left: ${(props: { isClear?: boolean }) => (props.isClear ? '-14%' : '0')}; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 97f3412ab..01f6778d0 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -3,15 +3,12 @@ import { useHistory } from 'react-router' import { AxiosError } from 'axios' import { useTranslation } from 'react-i18next' import { SearchImage, SearchInputPanel, SearchPanel, SearchButton, SearchContainer } from './styled' -import { fetchSearchResult } from '../../service/http/fetcher' +import { explorerService, Response } from '../../services/ExplorerService' import SearchLogo from '../../assets/search_black.png' import ClearLogo from '../../assets/clear.png' import { addPrefixForHash, containSpecialChar } from '../../utils/string' import i18n from '../../utils/i18n' import { HttpErrorCode, SearchFailType } from '../../constants/common' -import { AppDispatch } from '../../contexts/reducer' -import { ComponentActions } from '../../contexts/actions' -import { useDispatch } from '../../contexts/providers' import { useIsMobile } from '../../utils/hook' import { isChainTypeError } from '../../utils/chain' @@ -43,24 +40,12 @@ const setSearchContent = (inputElement: any, content: string) => { } } -const hideMobileMenu = (dispatch: AppDispatch) => { - dispatch({ - type: ComponentActions.UpdateHeaderMobileMenuVisible, - payload: { - mobileMenuVisible: false, - }, - }) -} - const handleSearchResult = ( searchValue: string, inputElement: any, - dispatch: AppDispatch, setSearchValue: Function, history: ReturnType, - isMobile: boolean, ) => { - if (isMobile) hideMobileMenu(dispatch) const query = searchValue.trim().replace(',', '') // remove front and end blank and ',' if (!query || containSpecialChar(query)) { history.push(`/search/fail?q=${query}`) @@ -72,7 +57,8 @@ const handleSearchResult = ( } setSearchLoading(inputElement) - fetchSearchResult(addPrefixForHash(query)) + explorerService.api + .fetchSearchResult(addPrefixForHash(query)) .then((response: any) => { const { data } = response if (!response || !data.type) { @@ -117,7 +103,6 @@ const Search: FC<{ onEditEnd?: () => void }> = memo(({ content, hasButton, onEditEnd }) => { const isMobile = useIsMobile() - const dispatch = useDispatch() const history = useHistory() const [t] = useTranslation() const SearchPlaceholder = useMemo(() => t('navbar.search_placeholder'), [t]) @@ -153,7 +138,7 @@ const Search: FC<{ const searchKeyAction = (event: any) => { if (event.keyCode === 13) { - handleSearchResult(searchValue, inputElement, dispatch, setSearchValue, history, isMobile) + handleSearchResult(searchValue, inputElement, setSearchValue, history) onEditEnd?.() } } @@ -180,7 +165,7 @@ const Search: FC<{ {hasButton && ( { - handleSearchResult(searchValue, inputElement, dispatch, setSearchValue, history, isMobile) + handleSearchResult(searchValue, inputElement, setSearchValue, history) onEditEnd?.() }} > diff --git a/src/components/Sheet/index.tsx b/src/components/Sheet/index.tsx index 69ce549de..0cb700946 100644 --- a/src/components/Sheet/index.tsx +++ b/src/components/Sheet/index.tsx @@ -1,27 +1,28 @@ -import { useAppState } from '../../contexts/providers' import { SheetPanel, SheetPointPanel, SheetItem } from './styled' -import { currentLanguage } from '../../utils/i18n' +import { createGlobalState, createGlobalStateSetter, useGlobalState } from '../../utils/state' + +const globalNetworkErrMsgs = createGlobalState([]) +const globalChainAlerts = createGlobalState([]) + +export const setNetworkErrMsgs = createGlobalStateSetter(globalNetworkErrMsgs) +export const setChainAlerts = createGlobalStateSetter(globalChainAlerts) const Sheet = () => { - const { - app, - components: { maintenanceAlertVisible }, - } = useAppState() - const messages: string[] = app.appErrors[1].message.concat(app.appErrors[0].message) + const [networkErrMsgs] = useGlobalState(globalNetworkErrMsgs) + const [chainAlerts] = useGlobalState(globalChainAlerts) + const messages: string[] = chainAlerts.concat(networkErrMsgs) return messages.length > 0 ? ( - -
- {messages.map((context: string, index: number) => { - const key = index - return ( - - {messages.length > 1 && ·} - {context} - - ) - })} -
+ + {messages.map((context: string, index: number) => { + const key = index + return ( + + {messages.length > 1 && ·} + {context} + + ) + })} ) : null } diff --git a/src/components/Sheet/styled.tsx b/src/components/Sheet/styled.tsx index 8a77cb5ce..be826d8d1 100644 --- a/src/components/Sheet/styled.tsx +++ b/src/components/Sheet/styled.tsx @@ -2,31 +2,16 @@ import styled from 'styled-components' export const SheetPanel = styled.div` - position: sticky; - top: ${(props: { isNotTop?: boolean }) => (props.isNotTop ? '112px' : '64px')}; - z-index: 9000; - - @media (max-width: 1200px) { - top: ${(props: { isNotTop?: boolean }) => (props.isNotTop ? '128px' : '64px')}; - } + width: 100%; + background: #d03a3a; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 15px 0 20px; @media (max-width: 750px) { - top: ${(props: { isNotTop?: boolean; isEn: boolean }) => - props.isNotTop ? (props.isEn ? '184px' : '164px') : '64px'}; - } - - > div { - width: 100%; - background: #d03a3a; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 15px 0 20px; - - @media (max-width: 750px) { - padding: 6px 0 10px; - } + padding: 6px 0 10px; } ` diff --git a/src/components/SortButton/styles.module.scss b/src/components/SortButton/styles.module.scss index 65f4f075b..ee10ee868 100644 --- a/src/components/SortButton/styles.module.scss +++ b/src/components/SortButton/styles.module.scss @@ -13,6 +13,7 @@ fill: var(--primary-color); } } + &[data-order='asc'] { svg > path:last-child { fill: var(--primary-color); diff --git a/src/components/Table/styled.tsx b/src/components/Table/styled.tsx index 39fd2cc12..ddda06be3 100644 --- a/src/components/Table/styled.tsx +++ b/src/components/Table/styled.tsx @@ -29,7 +29,7 @@ export const TableTitleRowItem = styled.div` border: none; outline: none; background-color: transparent; - color: #333333; + color: #333; font-size: 18px; font-weight: 450; text-align: center; diff --git a/src/components/Text/CopyTooltipText/index.tsx b/src/components/Text/CopyTooltipText/index.tsx index 73369ed2a..7582fc369 100644 --- a/src/components/Text/CopyTooltipText/index.tsx +++ b/src/components/Text/CopyTooltipText/index.tsx @@ -1,23 +1,17 @@ import i18n from '../../../utils/i18n' import { copyElementValue } from '../../../utils/util' -import { AppActions } from '../../../contexts/actions' -import { useDispatch } from '../../../contexts/providers' import SimpleButton from '../../SimpleButton' +import { useSetToast } from '../../Toast' export default ({ content }: { content: string }) => { - const dispatch = useDispatch() + const setToast = useSetToast() return ( { event.stopPropagation() copyElementValue(document.getElementById(`copy__content__${content}`)) - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: i18n.t('common.copied'), - }, - }) + setToast({ message: i18n.t('common.copied') }) event.preventDefault() }} > diff --git a/src/components/Text/index.module.scss b/src/components/Text/index.module.scss index 1c450c9da..9a1c829cc 100644 --- a/src/components/Text/index.module.scss +++ b/src/components/Text/index.module.scss @@ -2,7 +2,7 @@ color: var(--primary-color); font-size: 14px; - @media (max-width: 750px) { + @media (width <= 750px) { font-size: 13px; } @@ -10,7 +10,7 @@ color: var(--primary-color); margin-top: 3px; - @media (max-width: 750px) { + @media (width <= 750px) { margin-top: 1px; } } diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index e581f842d..e48541539 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect, useReducer } from 'react' +import { useState, useEffect, useReducer, useCallback } from 'react' import { useTimeoutWithUnmount } from '../../utils/hook' -import { useAppState } from '../../contexts/providers' import { ToastItemPanel, ToastPanel } from './styled' +import { createGlobalState, useGlobalState } from '../../utils/state' const getColor = (type: 'success' | 'warning' | 'danger') => { switch (type) { @@ -81,10 +81,25 @@ const reducer = (state: any, action: any) => { } } +const globalToast = createGlobalState(null) + +export function useSetToast() { + const [, setToast] = useGlobalState(globalToast) + + return useCallback( + (data: Pick & Partial>) => + setToast({ + id: new Date().getTime(), + message: data.message, + type: data.type ?? 'success', + duration: data.duration, + }), + [setToast], + ) +} + export default () => { - const { - app: { toast }, - } = useAppState() + const [toast] = useGlobalState(globalToast) const [state, dispatch] = useReducer(reducer, initialState) useEffect(() => { diff --git a/src/components/Tooltip/ComparedToMaxTooltip/styles.module.scss b/src/components/Tooltip/ComparedToMaxTooltip/styles.module.scss index da93b4fc4..8cc5a6f97 100644 --- a/src/components/Tooltip/ComparedToMaxTooltip/styles.module.scss +++ b/src/components/Tooltip/ComparedToMaxTooltip/styles.module.scss @@ -1,7 +1,7 @@ :global(.ant-tooltip) { max-width: 320px; - @media (max-width: 750px) { + @media (width <= 750px) { max-width: 250px; } } @@ -11,32 +11,39 @@ border-radius: 8px; mix-blend-mode: normal; padding: 10px 16px; + > div { font-weight: 400; margin: 0; + > div:first-child { font-size: 16px; line-height: 19px; - padding: 0 0 3px 0; + padding: 0 0 3px; } + > div:nth-child(2) { font-size: 14px; line-height: 16px; padding: 3px 0; } } + > div.inEpoch { margin-bottom: 10px; + :global(.ant-progress-bg) { background-color: var(--primary-color); } } + > div.inChain { :global(.ant-progress-bg) { background-color: #346dff; } } } + hr { margin: 8px 0; opacity: 0.2; diff --git a/src/components/TransactionItem/TransactionItemCell/index.module.scss b/src/components/TransactionItem/TransactionItemCell/index.module.scss index 31e8cbf6e..fa77318dd 100644 --- a/src/components/TransactionItem/TransactionItemCell/index.module.scss +++ b/src/components/TransactionItem/TransactionItemCell/index.module.scss @@ -22,15 +22,15 @@ min-width: 0; width: 260px; - @media (max-width: 1440px) { + @media (width <= 1440px) { width: 148px; } - @media (max-width: 1200px) { + @media (width <= 1200px) { width: 260px; } - @media (max-width: 750px) { + @media (width <= 750px) { width: 460px; } } @@ -39,7 +39,7 @@ margin-left: 6px; width: 14px; - @media (max-width: 750px) { + @media (width <= 750px) { margin-bottom: 2px; } } diff --git a/src/components/TransactionItem/TransactionItemCell/index.tsx b/src/components/TransactionItem/TransactionItemCell/index.tsx index 478bd8e00..421d2939a 100644 --- a/src/components/TransactionItem/TransactionItemCell/index.tsx +++ b/src/components/TransactionItem/TransactionItemCell/index.tsx @@ -6,7 +6,7 @@ import NervosDAOCellIcon from '../../../assets/nervos_dao_cell.png' import NervosDAOWithdrawingIcon from '../../../assets/nervos_dao_withdrawing.png' import CurrentAddressIcon from '../../../assets/current_address.svg' import UDTTokenIcon from '../../../assets/udt_token.png' -import i18n from '../../../utils/i18n' +import i18n, { currentLanguage } from '../../../utils/i18n' import { localeNumberString, parseUDTAmount } from '../../../utils/number' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { @@ -20,7 +20,6 @@ import { import { CellType } from '../../../constants/common' import TransactionCellArrow from '../../Transaction/TransactionCellArrow' import DecimalCapacity from '../../DecimalCapacity' -import { useAppState } from '../../../contexts/providers' import { parseDiffDate } from '../../../utils/date' import Cellbase from '../../Transaction/Cellbase' import styles from './index.module.scss' @@ -93,9 +92,8 @@ const WithdrawPopoverItem = ({ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { const isMobile = useIsMobile() - const { app } = useAppState() let width = 'short' - if (app.language === 'en') { + if (currentLanguage() === 'en') { width = isDaoDepositCell(cell.cellType) ? 'long' : 'medium' } return ( diff --git a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss b/src/components/TransactionItem/TransactionLiteIncome/index.module.scss index 22ae15c75..a7a471803 100644 --- a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss +++ b/src/components/TransactionItem/TransactionLiteIncome/index.module.scss @@ -3,17 +3,20 @@ font-weight: 500; font-size: 16px; line-height: 19px; + > div:not(:last-child) { font-size: 14px; line-height: 16px; } - @media (min-width: 751px) and (max-width: 960px) { + + @media (width >= 751px) and (width <= 960px) { > div:not(:last-child) { display: none; } } } } + .increased { color: var(--primary-color); } diff --git a/src/components/TransactionItem/TransactionLiteItem/index.module.scss b/src/components/TransactionItem/TransactionLiteItem/index.module.scss index 007fee3d3..dd1a1051c 100644 --- a/src/components/TransactionItem/TransactionLiteItem/index.module.scss +++ b/src/components/TransactionItem/TransactionLiteItem/index.module.scss @@ -1,6 +1,6 @@ .transactionLitePanel { width: 100%; - background-color: #ffffff; + background-color: #fff; padding: 0 40px; display: flex; @@ -33,26 +33,33 @@ font-weight: 400; font-size: 16px; line-height: 19px; - color: #333333; + color: #333; + &:first-child { width: 28%; padding-right: 20px; - @media (min-width: 1200px) { + + @media (width >= 1200px) { padding-right: 50px; } } + &:nth-child(2) { width: 13%; } + &:nth-child(3) { width: 20%; } + &:nth-child(4) { width: 27%; + > span:last-child { margin-left: 16px; } } + &:last-child { width: 12%; text-align: right; @@ -60,37 +67,49 @@ } } } - @media (max-width: 750px) { + + @media (width <= 750px) { padding: 0 16px; margin-bottom: 4px; + .transactionLiteRow { flex-direction: column; border-top: none; padding: 0; + > div { - width: 100% !important; - padding: 16px 0 !important; + // This selector is just to increase the specificity. + &:nth-child(n) { + width: 100%; + padding: 16px 0; + } + > div:first-child { - color: #666666; + color: #666; margin-bottom: 8px; } + &:not(:first-child) { border-top: 1px solid #f0f0f0; } } + > div:first-child { > a > div { padding-right: 38px; } } + > div:nth-child(4) { > span:last-child { margin-left: 24px; } } + > div:last-child { text-align: left; width: 100%; + > div:last-child > div { justify-content: flex-start; } diff --git a/src/constants/cache.ts b/src/constants/cache.ts index 4411ee276..aab0a6aa2 100644 --- a/src/constants/cache.ts +++ b/src/constants/cache.ts @@ -3,7 +3,6 @@ import CONFIG from '../config' export const AppCachedKeys = { AppLanguage: `${CONFIG.CHAIN_TYPE}-AppLanguage`, Version: `${CONFIG.CHAIN_TYPE}-Version`, - MaintenanceAlert: `${CONFIG.CHAIN_TYPE}-MaintenanceAlert`, NewAddrFormat: `is-address-format-new`, } diff --git a/src/constants/common.ts b/src/constants/common.ts index 4dbe8d46f..5e35126a2 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -8,7 +8,6 @@ export const FLUSH_CHART_CACHE_POLLING_TIME = 300000 // 5 minutes export const LOADING_WAITING_TIME = 500 export const DELAY_BLOCK_NUMBER = 11 export const PAGE_CELL_COUNT = 200 -export const MAINTENANCE_ALERT_POLLING_TIME = 3600000 // 1 hour export const NEXT_HARD_FORK_EPOCH = 5414 export const EPOCH_HOURS = 4 export const ONE_DAY_SECOND = 24 * 60 * 60 diff --git a/src/contexts/actions/index.ts b/src/contexts/actions/index.ts deleted file mode 100644 index 1c005368e..000000000 --- a/src/contexts/actions/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -export enum AppActions { - UpdateModal = 'updateModal', - ShowToastMessage = 'showToastMessage', - UpdateAppErrors = 'updateAppErrors', - UpdateTipBlockNumber = 'updateTipBlockNumber', - UpdateAppLanguage = 'updateAppLanguage', - UpdateHardForkStatus = 'updateHardForkStatus', -} - -export enum PageActions { - UpdateStatistics = 'updateStatistics', -} - -export enum ComponentActions { - UpdateHeaderMobileMenuVisible = 'updateHeaderMobileMenuVisible', - UpdateHeaderSearchBarVisible = 'updateHeaderSearchBarVisible', - UpdateMaintenanceAlertVisible = 'updateMaintenanceAlertVisible', -} - -export type StateActions = AppActions | PageActions | ComponentActions - -// eslint-disable-next-line no-undef -export default StateActions diff --git a/src/contexts/providers/dasQuery.tsx b/src/contexts/providers/dasQuery.tsx index 8abd3bd83..f420225a2 100644 --- a/src/contexts/providers/dasQuery.tsx +++ b/src/contexts/providers/dasQuery.tsx @@ -1,6 +1,6 @@ import { createContext, FC, useCallback, useContext, useMemo, useRef } from 'react' import { useQuery } from 'react-query' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { unique } from '../../utils/array' import { throttle } from '../../utils/function' import { pick } from '../../utils/object' @@ -26,7 +26,7 @@ interface PendingQuery { } async function fetchDASAccounts(addresses: string[]): Promise { - const { data } = await v2AxiosIns.post>('das_accounts', { + const { data } = await explorerService.api.requesterV2.post>('das_accounts', { addresses, }) const dataWithNormalizeEmptyValue = Object.fromEntries( diff --git a/src/contexts/providers/hook.ts b/src/contexts/providers/hook.ts index 6a8f03360..3588c9ef6 100644 --- a/src/contexts/providers/hook.ts +++ b/src/contexts/providers/hook.ts @@ -1,61 +1,30 @@ import { useState } from 'react' -import { useHistory } from 'react-router' -import { initAxiosInterceptors } from '../../service/http/interceptors' -import { - MAINTENANCE_ALERT_POLLING_TIME, - FLUSH_CHART_CACHE_POLLING_TIME, - BLOCK_POLLING_TIME, -} from '../../constants/common' +import { FLUSH_CHART_CACHE_POLLING_TIME } from '../../constants/common' import { AppCachedKeys } from '../../constants/cache' -import { AppDispatch } from '../reducer' import { fetchCachedData } from '../../utils/cache' import { changeLanguage } from '../../utils/i18n' -import { useAppState, useDispatch } from '.' -import { AppActions } from '../actions' import { useInterval } from '../../utils/hook' -import { getMaintenanceInfo } from '../../service/app/alert' import flushCacheInfo from '../../service/app/charts/cache' -import getStatistics from '../../service/app/statistics' -const initAppLanguage = (app: State.App, dispatch: AppDispatch) => { - const language = fetchCachedData<'zh' | 'en'>(AppCachedKeys.AppLanguage) || app.language - // Warding: https://github.com/facebook/react/issues/18147 - setTimeout(() => { - dispatch({ - type: AppActions.UpdateAppLanguage, - payload: { - language, - }, - }) - }, 0) +const initAppLanguage = () => { + const language = + fetchCachedData<'zh' | 'en'>(AppCachedKeys.AppLanguage) || (navigator.language.includes('zh') ? 'zh' : 'en') changeLanguage(language) } export const useInitApp = () => { const [init, setInit] = useState(false) - const { app } = useAppState() - const dispatch = useDispatch() - const history = useHistory() if (!init) { setInit(true) - initAxiosInterceptors(dispatch, history) - initAppLanguage(app, dispatch) - getMaintenanceInfo(dispatch) + // TODO: This function may not belong here. + initAppLanguage() flushCacheInfo() - getStatistics(dispatch) } - useInterval(() => { - getMaintenanceInfo(dispatch) - }, MAINTENANCE_ALERT_POLLING_TIME) - useInterval(() => { flushCacheInfo() }, FLUSH_CHART_CACHE_POLLING_TIME) - useInterval(() => { - getStatistics(dispatch) - }, BLOCK_POLLING_TIME) } export default useInitApp diff --git a/src/contexts/providers/index.tsx b/src/contexts/providers/index.tsx deleted file mode 100644 index a2b1d73e0..000000000 --- a/src/contexts/providers/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { createContext, useReducer, useContext } from 'react' -import initState from '../states/index' -import { AppDispatch, reducer } from '../reducer' - -export const AppContext = createContext<{ state: typeof initState; dispatch: AppDispatch }>({ - state: initState, - dispatch: () => {}, -}) - -const withProviders = (Comp: React.ComponentType) => (props: React.Props) => { - const [providers, dispatch] = useReducer(reducer, initState) - - return ( - - - - ) -} - -export const useAppState = () => useContext(AppContext).state -export const useDispatch = () => useContext(AppContext).dispatch - -export default withProviders diff --git a/src/contexts/reducer/app.ts b/src/contexts/reducer/app.ts deleted file mode 100644 index 73ebcb798..000000000 --- a/src/contexts/reducer/app.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ChartColor, getPrimaryColor, getSecondaryColor } from '../../constants/common' -import { AppActions } from '../actions' - -export const appReducer = ( - state: State.AppState, - { type, payload }: { type: AppActions; payload: State.AppPayload }, -): State.AppState => { - switch (type) { - case AppActions.UpdateModal: - return { - ...state, - app: { - ...state.app, - }, - } - case AppActions.ShowToastMessage: - return { - ...state, - app: { - ...state.app, - toast: { - id: new Date().getTime(), - message: payload.message, - type: payload.type, - duration: payload.duration, - }, - }, - } - case AppActions.UpdateAppErrors: - return { - ...state, - app: { - ...state.app, - appErrors: state.app.appErrors.map((error: State.AppError) => { - if (payload.appError.type === error.type) { - return payload.appError - } - return error - }) as typeof state.app.appErrors, - }, - } - case AppActions.UpdateTipBlockNumber: - return { - ...state, - app: { - ...state.app, - tipBlockNumber: payload.tipBlockNumber, - }, - } - case AppActions.UpdateAppLanguage: - return { - ...state, - app: { - ...state.app, - language: payload.language, - }, - } - case AppActions.UpdateHardForkStatus: - return { - ...state, - app: { - ...state.app, - primaryColor: getPrimaryColor(), - secondaryColor: getSecondaryColor(), - chartColor: ChartColor, - }, - } - default: - return state - } -} - -export default appReducer diff --git a/src/contexts/reducer/component.ts b/src/contexts/reducer/component.ts deleted file mode 100644 index a4c1bf7ac..000000000 --- a/src/contexts/reducer/component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ComponentActions } from '../actions' - -export const componentReducer = ( - state: State.AppState, - { type, payload }: { type: ComponentActions; payload: State.Components }, -): State.AppState => { - switch (type) { - case ComponentActions.UpdateHeaderMobileMenuVisible: - return { - ...state, - components: { - ...state.components, - mobileMenuVisible: payload.mobileMenuVisible, - }, - } - case ComponentActions.UpdateHeaderSearchBarVisible: - return { - ...state, - components: { - ...state.components, - headerSearchBarVisible: payload.headerSearchBarVisible, - }, - } - case ComponentActions.UpdateMaintenanceAlertVisible: - return { - ...state, - components: { - ...state.components, - maintenanceAlertVisible: payload.maintenanceAlertVisible, - }, - } - default: - return state - } -} - -export default componentReducer diff --git a/src/contexts/reducer/index.ts b/src/contexts/reducer/index.ts deleted file mode 100644 index 041ba4770..000000000 --- a/src/contexts/reducer/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import StateActions, { AppActions, PageActions, ComponentActions } from '../actions' -import appReducer from './app' -import pageReducer from './page' -import componentReducer from './component' - -export type AppDispatch = React.Dispatch<{ type: StateActions; payload: any }> // TODO: add type of payload -export type StateWithDispatch = State.AppState & { dispatch: AppDispatch } - -export const reducer = ( - state: State.AppState, - { type, payload }: { type: StateActions; payload: any }, -): State.AppState => { - if (Object.values(AppActions).includes(type as AppActions)) { - return appReducer(state, { - type: type as AppActions, - payload, - }) - } - if (Object.values(PageActions).includes(type as PageActions)) { - return pageReducer(state, { - type: type as PageActions, - payload, - }) - } - return componentReducer(state, { - type: type as ComponentActions, - payload, - }) -} diff --git a/src/contexts/reducer/page.ts b/src/contexts/reducer/page.ts deleted file mode 100644 index 312165298..000000000 --- a/src/contexts/reducer/page.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PageActions } from '../actions' - -export const pageReducer = ( - state: State.AppState, - { type, payload }: { type: PageActions; payload: State.PagePayload }, -): State.AppState => { - switch (type) { - // statistic chart page - case PageActions.UpdateStatistics: - return { - ...state, - statistics: payload.statistics, - } - - default: - return state - } -} - -export default pageReducer diff --git a/src/contexts/states/app.ts b/src/contexts/states/app.ts deleted file mode 100644 index db2c956a7..000000000 --- a/src/contexts/states/app.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ChartColor, getPrimaryColor, getSecondaryColor } from '../../constants/common' - -export const initApp: State.App = { - toast: null, - appErrors: [ - { - type: 'Network', - message: [], - }, - { - type: 'ChainAlert', - message: [], - }, - { - type: 'Maintenance', - message: [], - }, - ], - tipBlockNumber: 0, - language: navigator.language.includes('zh') ? 'zh' : 'en', - primaryColor: getPrimaryColor(), - secondaryColor: getSecondaryColor(), - chartColor: ChartColor, -} - -export default initApp diff --git a/src/contexts/states/components.ts b/src/contexts/states/components.ts deleted file mode 100644 index b54d6c2d0..000000000 --- a/src/contexts/states/components.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const initComponents: State.Components = { - mobileMenuVisible: false, - headerSearchBarVisible: false, - maintenanceAlertVisible: false, -} - -export default initComponents diff --git a/src/contexts/states/index.ts b/src/contexts/states/index.ts deleted file mode 100644 index 66c74d352..000000000 --- a/src/contexts/states/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import initApp from './app' -import initStatistics from './statistics' -import initComponents from './components' - -export type FetchStatus = keyof State.FetchStatus - -const initState: State.AppState = { - app: initApp, - statistics: initStatistics, - - components: initComponents, -} - -export default initState diff --git a/src/contexts/states/statistics.ts b/src/contexts/states/statistics.ts deleted file mode 100644 index c47b5c329..000000000 --- a/src/contexts/states/statistics.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const initStatistics: State.Statistics = { - tipBlockNumber: '0', - averageBlockTime: '0', - currentEpochDifficulty: '0', - hashRate: '0', - epochInfo: { - epochNumber: '0', - epochLength: '0', - index: '0', - }, - estimatedEpochTime: '0', - transactionsLast24Hrs: '0', - transactionsCountPerMinute: '0', - reorgStartedAt: null, -} - -export default initStatistics diff --git a/src/index.css b/src/index.css index bbb6b93c9..d7cc03268 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,4 @@ -@import './assets/fonts/fonts.css'; +@import url('./assets/fonts/fonts.css'); body { --primary-color: #00cc9b; @@ -6,6 +6,7 @@ body { --primary-chiffon-color: #e6fcf7; --navbar-height: 64px; --table-separator-color: #f5f5f5; + margin: 0; padding: 0; -webkit-font-smoothing: antialiased; @@ -24,11 +25,11 @@ body[data-chain-type='testnet'] { margin: 0; padding: 0; box-sizing: border-box; - font-family: Lato, sans-serif, Montserrat, PingFang SC, -apple-system; + font-family: Lato, sans-serif, Montserrat, "PingFang SC", -apple-system; } .monospace { - font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } *[role]:focus { @@ -40,21 +41,21 @@ body[data-chain-type='testnet'] { margin: 0 120px; } -@media (max-width: 1440px) { +@media (width <= 1440px) { .container { width: auto; margin: 0 100px; } } -@media (max-width: 1200px) { +@media (width <= 1200px) { .container { width: auto; margin: 0 45px; } } -@media (max-width: 750px) { +@media (width <= 750px) { .container { width: auto; margin: 0 18px; @@ -68,15 +69,15 @@ a { color: inherit; } -@media (max-width: 750px) { +@media (width <= 750px) { .rc-pagination-options-quick-jumper { display: none; } } -@media (max-width: 750px) { +@media (width <= 750px) { .ant-popover-inner-content { - padding: 5px !important; + padding: 5px; } } diff --git a/src/index.tsx b/src/index.tsx index 71b03c878..6c151f60f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,6 @@ import ReactDOM from 'react-dom' +import 'antd/dist/antd.css' +// This should be after all third-party library styles so that it can override them. import './index.css' import './utils/i18n' import App from './App' diff --git a/src/locales/en.json b/src/locales/en.json index 1eb30e271..70e1fc5c2 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -79,8 +79,6 @@ "too_many_request": "Too many requests, please try later", "data_too_large": "The data is too large, not available for download", "result_not_found": "Search result not found", - "maintenance": "Please note that CKB Explorer will be under maintenance from {{start}} to {{end}}. Data update during the period might be delayed.", - "dismiss": "Dismiss", "handling-reorg": "Reorg detected at {{time}} so CKB Explorer is checking data thoroughly. It may take half an hour, please visit later", "migration-notice": "Service of CKB explorer will have a migration on {{time}}, and the server might be unavailable temporarily" }, diff --git a/src/locales/zh.json b/src/locales/zh.json index 137aaf67b..b6c0c176e 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -79,8 +79,6 @@ "too_many_request": "请求次数过多, 请稍后再试", "data_too_large": "数据过大,不支持下载", "result_not_found": "没有找到搜索结果", - "maintenance": "请注意 CKB 浏览器将于 {{start}} 至 {{end}} 处于维护状态,期间数据更新可能会有延迟。", - "dismiss": "知道了", "handling-reorg": " 检测到 {{time}} 发生 Reorg, CKB 浏览器正在彻底检查数据。所需时间约为半小时, 请稍后访问", "migration-notice": "浏览器服务将于 {{time}} 进行迁移, 可能出现短时不可用状态" }, diff --git a/src/pages/404/index.module.scss b/src/pages/404/index.module.scss index 8237578b3..42172c089 100644 --- a/src/pages/404/index.module.scss +++ b/src/pages/404/index.module.scss @@ -9,7 +9,7 @@ margin: 0 auto; display: block; - @media (max-width: 750px) { + @media (width <= 750px) { width: 282px; height: 130px; } diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index 8bf0989f1..4bac42138 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -6,7 +6,7 @@ import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' import TransactionItem from '../../components/TransactionItem/index' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import i18n from '../../utils/i18n' import { parseSporeCellData } from '../../utils/spore' import { localeNumberString, parseUDTAmount } from '../../utils/number' @@ -272,7 +272,7 @@ export const AddressOverview: FC<{ address: State.Address }> = ({ address }) => const { data: initList } = useQuery>( ['cota-list', address.addressHash], - () => v2AxiosIns(`nft/items?owner=${address.addressHash}&standard=cota`), + () => explorerService.api.requesterV2(`nft/items?owner=${address.addressHash}&standard=cota`), { enabled: !!address?.addressHash, }, @@ -281,7 +281,7 @@ export const AddressOverview: FC<{ address: State.Address }> = ({ address }) => const { data: cotaList } = useQuery(['cota-list', initList?.data.pagination.series], () => Promise.all( (initList?.data.pagination.series ?? []).map(p => - v2AxiosIns(`nft/items?owner=${address.addressHash}&standard=cota&page=${p}`), + explorerService.api.requesterV2(`nft/items?owner=${address.addressHash}&standard=cota&page=${p}`), ), ).then(list => { return list.reduce((total, acc) => [...total, ...acc.data.data], [] as CoTAList['data']) diff --git a/src/pages/Address/index.tsx b/src/pages/Address/index.tsx index 284033e32..b2a91539a 100644 --- a/src/pages/Address/index.tsx +++ b/src/pages/Address/index.tsx @@ -5,7 +5,7 @@ import Content from '../../components/Content' import i18n from '../../utils/i18n' import { AddressContentPanel } from './styled' import { AddressTransactions, AddressOverview } from './AddressComp' -import { fetchAddressInfo, fetchTransactionsByAddress } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' import { usePaginationParamsInListPage, useSortParam } from '../../utils/hook' import { isAxiosError } from '../../utils/error' @@ -18,7 +18,7 @@ export const Address = () => { const { sortBy, orderBy, sort } = useSortParam<'time'>(s => s === 'time') const addressInfoQuery = useQuery(['address_info', address], async () => { - const wrapper = await fetchAddressInfo(address) + const wrapper = await explorerService.api.fetchAddressInfo(address) const result: State.Address = { ...wrapper.attributes, type: wrapper.type === 'lock_hash' ? 'LockHash' : 'Address', @@ -30,7 +30,12 @@ export const Address = () => { ['address_transactions', address, currentPage, pageSize, sort], async () => { try { - const { data, meta } = await fetchTransactionsByAddress(address, currentPage, pageSize, sort) + const { data, meta } = await explorerService.api.fetchTransactionsByAddress( + address, + currentPage, + pageSize, + sort, + ) return { transactions: data.map(wrapper => wrapper.attributes), total: meta ? meta.total : 0, diff --git a/src/pages/Address/styles.module.scss b/src/pages/Address/styles.module.scss index 7a118ed89..c91b47fae 100644 --- a/src/pages/Address/styles.module.scss +++ b/src/pages/Address/styles.module.scss @@ -1,11 +1,11 @@ .addressWidthModify { max-width: 80%; - @media (max-width: 1200px) { + @media (width <= 1200px) { max-width: 40%; } - @media (max-width: 750px) { + @media (width <= 750px) { max-width: 100%; } } @@ -13,32 +13,38 @@ .transactionTitleCard { height: 80px; - @media (max-width: 750px) { + @media (width <= 750px) { height: 152px; flex-direction: column; align-items: flex-start; padding: 0; + > div { display: flex; align-items: center; padding: 0 12px; } + > div:first-child { margin: 0; height: 56px; } + > div:last-child { width: 100%; order: 3; height: 92px; + > div { width: 100%; + > label { width: 50%; } } } - &:after { + + &::after { content: ' '; order: 2; background: #ededed; @@ -52,9 +58,11 @@ display: flex; align-items: center; padding-right: 2.6rem; + svg { cursor: pointer; } + &[data-is-active='true'] { svg { path { @@ -65,34 +73,42 @@ } .layoutButtons { - > label { - height: 40px; - width: 120px; - text-align: center; - font-weight: 400; - font-size: 16px; - line-height: 38px; - color: #333333; - border: 1px solid #e5e5e5; - box-shadow: none !important; - &::before { - content: none !important; - } - &:hover { - color: #333333; - background: #ffffff; - } - &:first-child { - border-radius: 4px 0 0 4px; - } - &:last-child { - border-radius: 0 4px 4px 0; - } - &:global(.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)) { - background: #333333; - border-color: #333333 !important; + :global { + .ant-radio-button-wrapper { + height: 40px; + width: 120px; + text-align: center; + font-weight: 400; + font-size: 16px; + line-height: 38px; + color: #333; + border: 1px solid #e5e5e5; + box-shadow: none; + + &::before { + content: none; + } + &:hover { - background: #333333; + color: #333; + background: #fff; + } + + &:first-child { + border-radius: 4px 0 0 4px; + } + + &:last-child { + border-radius: 0 4px 4px 0; + } + + &.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) { + background: #333; + border-color: #333; + + &:hover { + background: #333; + } } } } @@ -101,7 +117,7 @@ .liteTransactionHeader { display: flex; width: 100%; - background-color: #ffffff; + background-color: #fff; margin-top: 4px; padding: 23px 40px 22px; flex-direction: row; @@ -109,20 +125,25 @@ font-weight: 500; font-size: 16px; line-height: 19px; - color: #333333; + color: #333; + > div { &:first-child { width: 28%; } + &:nth-child(2) { width: 13%; } + &:nth-child(3) { width: 20%; } + &:nth-child(4) { width: 20%; } + &:last-child { width: 19%; text-align: right; diff --git a/src/pages/BlockDetail/BlockComp.tsx b/src/pages/BlockDetail/BlockComp.tsx index 97456cab7..127b61187 100644 --- a/src/pages/BlockDetail/BlockComp.tsx +++ b/src/pages/BlockDetail/BlockComp.tsx @@ -10,7 +10,6 @@ import DropDownBlueIcon from '../../assets/content_blue_drop_down.png' import PackUpBlueIcon from '../../assets/content_blue_pack_up.png' import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' import TransactionItem from '../../components/TransactionItem/index' -import { useAppState } from '../../contexts/providers' import { parseSimpleDate } from '../../utils/date' import i18n from '../../utils/i18n' import { localeNumberString, handleDifficulty } from '../../utils/number' @@ -39,6 +38,7 @@ import AddressText from '../../components/AddressText' import ComparedToMaxTooltip from '../../components/Tooltip/ComparedToMaxTooltip' import Filter from '../../components/Search/Filter' import { HelpTip } from '../../components/HelpTip' +import { useLatestBlockNumber } from '../../services/ExplorerService' const CELL_BASE_ANCHOR = 'cellbase' @@ -104,9 +104,7 @@ const BlockMinerReward = ({ export const BlockOverview: FC<{ block: State.Block }> = ({ block }) => { const isMobile = useIsMobile() - const { - statistics: { tipBlockNumber }, - } = useAppState() + const tipBlockNumber = useLatestBlockNumber() const [showAllOverview, setShowAllOverview] = useState(false) const minerReward = const rootInfoItems = [ @@ -338,6 +336,7 @@ export const BlockComp = ({ title={`${i18n.t('transaction.transactions')} (${localeNumberString(total)})`} className={styles.transactionTitleCard} isSingle + rearClassName={styles.rear} rear={ { const filter = new URLSearchParams(search).get('filter') const queryBlock = useQuery(['block', blockHeightOrHash], async () => { - const wrapper = await fetchBlock(blockHeightOrHash) + const wrapper = await explorerService.api.fetchBlock(blockHeightOrHash) const block = wrapper.attributes return block }) @@ -31,7 +31,7 @@ export default () => { async () => { assert(blockHash != null) try { - const { data, meta } = await fetchTransactionsByBlockHash(blockHash, { + const { data, meta } = await explorerService.api.fetchTransactionsByBlockHash(blockHash, { page: currentPage, size: pageSizeParam, filter, diff --git a/src/pages/BlockDetail/styles.module.scss b/src/pages/BlockDetail/styles.module.scss index 78e6778b5..5852a26ec 100644 --- a/src/pages/BlockDetail/styles.module.scss +++ b/src/pages/BlockDetail/styles.module.scss @@ -1,46 +1,56 @@ .blockNumber { display: flex; align-items: center; + .prev, .next { display: flex; align-items: center; + svg { rect { fill: var(--primary-color); opacity: 0.15; } + path { stroke: var(--primary-color); } } + &:hover { svg { rect { fill: var(--primary-color); opacity: 1; } + path { stroke: #fff; } } } + &[data-disabled='true'] { pointer-events: none; + svg { rect { fill: #f0f0f0; opacity: 1; } + path { stroke: #999; } } } } + .prev { margin-right: 8px; } + .next { margin-left: 8px; transform: rotate(0.5turn); @@ -50,31 +60,34 @@ .addressWidthModify { max-width: 80%; - @media (max-width: 1200px) { + @media (width <= 1200px) { max-width: 40%; } - @media (max-width: 750px) { + @media (width <= 750px) { max-width: 100%; } } .transactionTitleCard { - @media (max-width: 750px) { + @media (width <= 750px) { flex-direction: column; justify-content: center; align-items: flex-start; height: 110px; - :global(.title__card__rear > div) { - // hack default style of TitleCard - padding: 0 !important; - justify-content: space-between !important; + + .rear { + > div:first-child { + padding: 0; + justify-content: space-between; + } } img { height: 12px; width: 12px; } + input { padding-left: 38px; } diff --git a/src/pages/BlockList/index.tsx b/src/pages/BlockList/index.tsx index bcc531a3c..3b7563825 100644 --- a/src/pages/BlockList/index.tsx +++ b/src/pages/BlockList/index.tsx @@ -15,7 +15,7 @@ import DecimalCapacity from '../../components/DecimalCapacity' import { ItemCardData, ItemCardGroup } from '../../components/Card/ItemCard' import AddressText from '../../components/AddressText' import { useIsMobile, useMediaQuery, usePaginationParamsInListPage, useSortParam } from '../../utils/hook' -import { fetchBlocks } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { RouteState } from '../../routes/state' import { ReactComponent as SortIcon } from '../../assets/sort_icon.svg' import { CsvExport } from '../../components/CsvExport' @@ -176,7 +176,7 @@ export default () => { const query = useQuery( ['blocks', currentPage, pageSize, sort], async () => { - const { data, meta } = await fetchBlocks(currentPage, pageSize, sort) + const { data, meta } = await explorerService.api.fetchBlocks(currentPage, pageSize, sort) return { blocks: data.map(wrapper => wrapper.attributes), total: meta?.total ?? 0, diff --git a/src/pages/BlockList/styles.module.scss b/src/pages/BlockList/styles.module.scss index 639017514..b0712d109 100644 --- a/src/pages/BlockList/styles.module.scss +++ b/src/pages/BlockList/styles.module.scss @@ -2,11 +2,13 @@ display: flex; margin-left: 8px; cursor: pointer; + &[data-order='asc'] { svg > path:last-child { fill: var(--primary-color); } } + &[data-order='desc'] { svg > path:first-child { fill: var(--primary-color); diff --git a/src/pages/Error/index.module.scss b/src/pages/Error/index.module.scss index 85657907d..50ec13058 100644 --- a/src/pages/Error/index.module.scss +++ b/src/pages/Error/index.module.scss @@ -10,14 +10,14 @@ .pageCrashedError { width: 800px; - margin: 16px auto 0 auto; + margin: 16px auto 0; } .backHome { width: 152px; height: 48px; display: block; - font-family: 'Roboto'; + font-family: Roboto, inherit, sans-serif; font-style: normal; font-weight: 400; font-size: 16px; @@ -25,7 +25,7 @@ text-align: center; text-transform: capitalize; color: #fff; - background: #333333; + background: #333; border-radius: 4px; margin: 40px auto; border: none; @@ -44,7 +44,7 @@ border: 1px dashed #9c9c9c; padding: 6px; - @media (max-width: 750px) { + @media (width <= 750px) { width: 228px; height: 108px; } diff --git a/src/pages/ExportTransactions/index.tsx b/src/pages/ExportTransactions/index.tsx index 9b2af3289..ea6db962a 100644 --- a/src/pages/ExportTransactions/index.tsx +++ b/src/pages/ExportTransactions/index.tsx @@ -15,7 +15,7 @@ import { ReactComponent as BlockIcon } from '../../assets/block_icon.svg' import { ReactComponent as ErrorIcon } from '../../assets/error_icon.svg' import { ReactComponent as SuccessIcon } from '../../assets/success_icon.svg' import { omit } from '../../utils/object' -import { exportTransactions } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' const ExportTransactions = () => { const [t, { language }] = useTranslation() @@ -116,12 +116,13 @@ const ExportTransactions = () => { } setHint({ type: 'success', msg: 'download_processed' }) setIsDownloading(true) - exportTransactions({ - type, - id, - date: tab === 'date' ? { start: startDate, end: endDate } : undefined, - block: tab === 'height' ? { from: fromHeight!, to: toHeight! } : undefined, - }) + explorerService.api + .exportTransactions({ + type, + id, + date: tab === 'date' ? { start: startDate, end: endDate } : undefined, + block: tab === 'height' ? { from: fromHeight!, to: toHeight! } : undefined, + }) .then((resp: string | null) => { setIsDownloading(false) if (!resp) { @@ -238,6 +239,7 @@ const ExportTransactions = () => { value={fromHeight} min={0} parser={heightParser} + controls={false} onChange={h => updateSearchParams( params => @@ -255,6 +257,7 @@ const ExportTransactions = () => { min={0} parser={heightParser} value={toHeight} + controls={false} onChange={h => updateSearchParams( params => (h ? { ...params, 'to-height': h.toString() } : omit(params, ['to-height'])), diff --git a/src/pages/ExportTransactions/styles.module.scss b/src/pages/ExportTransactions/styles.module.scss index b30230eb9..e9e1d9321 100644 --- a/src/pages/ExportTransactions/styles.module.scss +++ b/src/pages/ExportTransactions/styles.module.scss @@ -3,7 +3,7 @@ flex-direction: column; align-items: center; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { margin: 0; } } @@ -12,7 +12,8 @@ display: flex; justify-content: center; margin-top: 40px; - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { margin-top: 20px; width: calc(100% - 32px); justify-content: start; @@ -23,16 +24,18 @@ font-size: 24px; line-height: 28px; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { font-size: 20px; line-height: normal; } } + > span:first-child { - color: #333333; + color: #333; } + > span:last-child { - color: #999999; + color: #999; } } @@ -47,10 +50,10 @@ font-weight: 400; font-size: 16px; line-height: 24px; - color: #333333; + color: #333; } - @media (max-width: 750px) { + @media (width <= 750px) { display: none; } } @@ -65,7 +68,8 @@ min-width: 342px; border-radius: 4px; background: white; - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { margin-top: 12px; margin-bottom: 20px; width: calc(100% - 32px); @@ -76,18 +80,22 @@ justify-content: center; width: 100%; } + .exportHeader { flex-direction: column; + > div:first-child { padding-top: 24px; padding-bottom: 4px; - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { padding: 16px 0; } + text-align: center; font-weight: 500; font-size: 16px; - color: #333333; + color: #333; } :global(div.ant-tabs-nav-wrap) { @@ -102,7 +110,7 @@ justify-content: center; align-items: center; - @media (max-width: 750px) { + @media (width <= 750px) { width: 80px; margin-left: 10px; margin-right: 10px; @@ -118,17 +126,17 @@ font-weight: 400; font-size: 14px; line-height: 16px; - color: #333333; + color: #333; } :global(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) { font-weight: 500; font-size: 14px; line-height: 16px; - color: #333333; + color: #333; } - @media (max-width: 750px) { + @media (width <= 750px) { margin-top: 12px; } } @@ -140,7 +148,7 @@ margin-top: 16px; color: #666; - @media (max-width: 750px) { + @media (width <= 750px) { margin-top: 8px; flex-direction: column; @@ -153,39 +161,47 @@ .heightInputPanel { margin-left: 24px; margin-right: 24px; - @media screen and (max-width: 750px) { - margin-left: 16px; - margin-right: 16px; - } display: flex; flex-direction: column; width: 40%; max-width: 256px; - :global(.ant-picker:hover) { - border-radius: 4px; - &:hover { - border-color: var(--primary-color) !important; - } + + @media screen and (width <= 750px) { + margin-left: 16px; + margin-right: 16px; } - :global(.ant-input-number-affix-wrapper) { - &:hover { - border-color: var(--primary-color) !important; + + :global { + .ant-picker { + border-radius: 4px; + box-shadow: none; + + &:hover, + &.ant-picker-focused { + border-color: var(--primary-color); + } } - border-radius: 4px; - } - :global(.ant-input-number-handler-wrap) { - display: none !important; + .ant-input-number-affix-wrapper { + border-radius: 4px; + box-shadow: none; + + &:hover, + &.ant-input-number-affix-wrapper-focused { + border-color: var(--primary-color); + } + } } - @media (max-width: 750px) { + @media (width <= 750px) { width: 100%; max-width: 312px; } > div:first-child { margin-bottom: 12px; - @media (max-width: 750px) { + + @media (width <= 750px) { margin-bottom: 8px; } } @@ -220,7 +236,7 @@ padding-top: 56px; padding-bottom: 40px; - @media (max-width: 750px) { + @media (width <= 750px) { padding: 24px 8px 16px; max-width: 312px; } @@ -256,22 +272,26 @@ max-width: 560px; font-size: 14px; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { max-width: 312px; } } + &.successHint { - border: 1px solid rgba(0, 204, 155, 1); - background: rgba(230, 252, 247, 1); - color: rgba(0, 204, 155, 1); + border: 1px solid rgb(0 204 155 / 100%); + background: rgb(230 252 247 / 100%); + color: rgb(0 204 155 / 100%); + path { - fill: rgba(0, 204, 155, 1); + fill: rgb(0 204 155 / 100%); } } + .hintText { line-height: 20px; word-break: break-all; } + &.errorHint { border: 1px solid #ffa6a6; background: #fff2f2; @@ -287,14 +307,17 @@ margin-bottom: 56px; width: 152px; height: 48px; + &[disabled] { opacity: 0.5; cursor: not-allowed; } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { margin-top: 20px; margin-bottom: 24px; } + background: var(--primary-color); color: white; border: none; @@ -309,27 +332,23 @@ } .calendar { - :global(.ant-picker-cell-selected .ant-picker-cell-inner) { - background-color: var(--primary-color) !important; - } - - :global(.ant-picker-cell-today .ant-picker-cell-inner) { - &::before { - border: 1px solid var(--primary-color); + :global { + .ant-picker-cell-selected .ant-picker-cell-inner { + background-color: var(--primary-color); } - } - :global(.ant-picker-today-btn) { - color: var(--primary-color); - &:hover { - color: var(--primary-color); + .ant-picker-cell-today .ant-picker-cell-inner { + &::before { + border: 1px solid var(--primary-color); + } } - &:active { - color: var(--primary-color); + + .ant-picker-today-btn { + &, + &:hover, + &:active { + color: var(--primary-color); + } } } } - -:global(.ant-picker-focused) { - border-color: var(--primary-color) !important; -} diff --git a/src/pages/FeeRateTracker/index.tsx b/src/pages/FeeRateTracker/index.tsx index 25d78a6f0..1564a3492 100644 --- a/src/pages/FeeRateTracker/index.tsx +++ b/src/pages/FeeRateTracker/index.tsx @@ -1,6 +1,5 @@ import { useRef, useState } from 'react' import { useQuery } from 'react-query' -import { v2AxiosIns } from '../../service/http/fetcher' import styles from './styles.module.scss' import Content from '../../components/Content' import { toCamelcase } from '../../utils/util' @@ -12,10 +11,10 @@ import { LastNDaysTransactionFeeRateChart, } from './FeeRateTrackerComp' import Loading from '../../components/Loading' -import { useAppState } from '../../contexts/providers' import i18n from '../../utils/i18n' import { localeNumberString } from '../../utils/number' import { getFeeRateSamples } from '../../utils/chart' +import { explorerService, useStatistics } from '../../services/ExplorerService' const FeeRateTracker = () => { const lastFetchedTime = useRef(Number.MAX_SAFE_INTEGER) @@ -23,12 +22,12 @@ const FeeRateTracker = () => { const [secondAfterUpdate, setSecondAfterUpdate] = useState(0) const isMobile = useIsMobile() - const { statistics } = useAppState() + const statistics = useStatistics() const { data: transactionFeesStatistic } = useQuery( ['statistics-transaction_fees'], () => - v2AxiosIns.get(`statistics/transaction_fees`).then(({ status, data }) => { + explorerService.api.requesterV2.get(`statistics/transaction_fees`).then(({ status, data }) => { if (status === 200 && data) { lastFetchedTime.current = Date.now() deltaSecond.current = 0 diff --git a/src/pages/FeeRateTracker/styles.module.scss b/src/pages/FeeRateTracker/styles.module.scss index 638f1b7df..94f94a579 100644 --- a/src/pages/FeeRateTracker/styles.module.scss +++ b/src/pages/FeeRateTracker/styles.module.scss @@ -1,7 +1,6 @@ .feeRateTrackerPanel { display: flex; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; align-items: center; .title { @@ -9,7 +8,7 @@ font-weight: 500; font-size: 24px; line-height: 28px; - color: #333333; + color: #333; margin: 40px 12px 16px; } @@ -19,17 +18,19 @@ flex-wrap: wrap; align-items: center; margin-bottom: 28px; + > div { margin: 12px; display: flex; width: calc(50% - 24px); height: 375px; - background-color: #ffffff; + background-color: #fff; flex-direction: column; justify-content: center; - box-shadow: 0 4px 4px rgba(16, 16, 16, 0.05); + box-shadow: 0 4px 4px rgb(16 16 16 / 5%); border-radius: 4px; } + .feeRateSection { position: relative; @@ -39,7 +40,7 @@ font-weight: 400; font-size: 14px; line-height: 16px; - color: #666666; + color: #666; position: absolute; top: 0; right: 0; @@ -70,6 +71,7 @@ width: 32px; height: 32px; } + > .priority { width: 80px; height: 30px; @@ -81,46 +83,55 @@ margin-top: 20px; margin-bottom: 20px; } + > .shannonsPerByte { - color: #333333; + color: #333; font-weight: 500; font-size: 16px; line-height: 18px; margin-bottom: 12px; text-align: center; } + > .secs { - color: #666666; + color: #666; font-weight: 400; font-size: 14px; line-height: 16px; } } + .card.low { > .icon > svg > path { fill: #23bff0; } + > .priority { color: #23bff0; background-color: #e8f9ff; } } + .card.average { border-left: 1px solid; border-right: 1px solid; border-image: linear-gradient(to bottom, transparent 36%, #e5e5e5 36%, #e5e5e5 64%, transparent 64%) 1; + > .icon > svg > path { fill: #00cc9b; } + > .priority { color: #00cc9b; background-color: #e8fff1; } } + .card.high { > .icon > svg > path { fill: #ff5656; } + > .priority { color: #ff5656; background-color: #ffe8e8; @@ -128,11 +139,12 @@ } } } + .chartTitle { display: flex; justify-content: center; text-align: center; - color: #333333; + color: #333; width: 100%; font-weight: 400; font-size: 16px; @@ -140,18 +152,20 @@ white-space: pre-line; padding: 24px; } + .chart { height: 100%; width: 100%; font-size: 13px; - background-color: #ffffff; + background-color: #fff; box-shadow: 2px 2px 0 0 #dfdfdf; } } - @media (max-width: 1280px) { + @media (width <= 1280px) { .charts { flex-direction: column; + > div { width: 100%; margin: 0 0 20px; @@ -159,29 +173,34 @@ } } - @media (max-width: 750px) { + @media (width <= 750px) { .title { margin: 20px 0 12px; font-size: 20px; line-height: 23px; } + .charts { width: 100%; margin: 0; + .feeRateSection { height: auto; .updatedTimeCounter { padding: 16px; } + .cards { margin-top: 48px; margin-bottom: 32px; flex-direction: column; align-items: center; + .card { padding: 32px 0; } + .card.average { border-width: 1px 0; border-style: solid; diff --git a/src/pages/Home/AverageBlockTimeChart/index.tsx b/src/pages/Home/AverageBlockTimeChart/index.tsx index 8a1ef118e..aa5fa8671 100644 --- a/src/pages/Home/AverageBlockTimeChart/index.tsx +++ b/src/pages/Home/AverageBlockTimeChart/index.tsx @@ -9,7 +9,7 @@ import SmallLoading from '../../../components/Loading/SmallLoading' import { HomeChartLink, ChartLoadingPanel } from './styled' import ChartNoDataImage from '../../../assets/chart_no_data_white.png' import { useChartQueryWithCache, useIsLGScreen } from '../../../utils/hook' -import { fetchStatisticAverageBlockTimes } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { ReactChartCore } from '../../StatisticsChart/common' @@ -106,7 +106,11 @@ const getOption = ( export default memo(() => { const isLG = useIsLGScreen() - const query = useChartQueryWithCache(fetchStatisticAverageBlockTimes, ChartCachedKeys.AverageBlockTime, 'date') + const query = useChartQueryWithCache( + explorerService.api.fetchStatisticAverageBlockTimes, + ChartCachedKeys.AverageBlockTime, + 'date', + ) const fullStatisticAverageBlockTimes = useMemo(() => query.data ?? [], [query.data]) const statisticAverageBlockTimes = useMemo(() => { diff --git a/src/pages/Home/AverageBlockTimeChart/styled.tsx b/src/pages/Home/AverageBlockTimeChart/styled.tsx index 7047e7ee6..46a436996 100644 --- a/src/pages/Home/AverageBlockTimeChart/styled.tsx +++ b/src/pages/Home/AverageBlockTimeChart/styled.tsx @@ -2,8 +2,8 @@ import styled from 'styled-components' import { Link } from 'react-router-dom' export const HomeChartLink = styled(Link)` - div { - cursor: pointer !important; + canvas { + cursor: pointer; } ` diff --git a/src/pages/Home/HashRateChart/index.tsx b/src/pages/Home/HashRateChart/index.tsx index f07fcec0d..3c9608c75 100644 --- a/src/pages/Home/HashRateChart/index.tsx +++ b/src/pages/Home/HashRateChart/index.tsx @@ -10,7 +10,7 @@ import SmallLoading from '../../../components/Loading/SmallLoading' import { HomeChartLink, ChartLoadingPanel } from './styled' import ChartNoDataImage from '../../../assets/chart_no_data_white.png' import { useChartQueryWithCache, useIsLGScreen } from '../../../utils/hook' -import { fetchStatisticHashRate } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { ReactChartCore } from '../../StatisticsChart/common' @@ -100,7 +100,7 @@ const getOption = (statisticHashRates: State.StatisticHashRate[], useMiniStyle: export default memo(() => { const isLG = useIsLGScreen() - const query = useChartQueryWithCache(fetchStatisticHashRate, ChartCachedKeys.HashRate, 'date') + const query = useChartQueryWithCache(explorerService.api.fetchStatisticHashRate, ChartCachedKeys.HashRate, 'date') const fullStatisticHashRates = useMemo(() => query.data ?? [], [query.data]) const statisticHashRates = useMemo(() => { diff --git a/src/pages/Home/HashRateChart/styled.tsx b/src/pages/Home/HashRateChart/styled.tsx index 7047e7ee6..46a436996 100644 --- a/src/pages/Home/HashRateChart/styled.tsx +++ b/src/pages/Home/HashRateChart/styled.tsx @@ -2,8 +2,8 @@ import styled from 'styled-components' import { Link } from 'react-router-dom' export const HomeChartLink = styled(Link)` - div { - cursor: pointer !important; + canvas { + cursor: pointer; } ` diff --git a/src/pages/Home/TableCard/index.module.scss b/src/pages/Home/TableCard/index.module.scss index eb55ad5aa..1f1772608 100644 --- a/src/pages/Home/TableCard/index.module.scss +++ b/src/pages/Home/TableCard/index.module.scss @@ -4,7 +4,7 @@ font-size: 14px; margin-top: 3px; - @media (max-width: 750px) { + @media (width <= 750px) { font-size: 13px; margin-top: 1px; } diff --git a/src/pages/Home/index.module.scss b/src/pages/Home/index.module.scss index ccc0f3965..1e230baf0 100644 --- a/src/pages/Home/index.module.scss +++ b/src/pages/Home/index.module.scss @@ -3,7 +3,7 @@ display: flex; align-items: center; - @media (max-width: 1200px) { + @media (width <= 1200px) { display: block; } @@ -18,10 +18,11 @@ margin-right: 8px; } - @media (max-width: 1200px) { + @media (width <= 1200px) { margin-bottom: 20px; } - @media (max-width: 375px) { + + @media (width <= 375px) { font-size: 16px; margin-bottom: 14px; } @@ -32,7 +33,7 @@ width: 600px; flex-grow: 2; - @media (max-width: 1200px) { + @media (width <= 1200px) { width: 100%; } } @@ -41,15 +42,15 @@ .HomeStatisticBottomPanel { display: flex; padding: 20px 5px; - margin: 0px 0px 20px 0px; - box-shadow: 0 2px 6px 0 rgb(77, 77, 77, 0.2); + margin: 0 0 20px; + box-shadow: 0 2px 6px 0 rgb(77 77 77 / 20%); border-radius: 0 0 6px 6px; - background: #ffffff; + background: #fff; - @media (max-width: 1200px) { + @media (width <= 1200px) { flex-direction: column; - padding: 10px 5px 0 5px; - box-shadow: 0 2px 6px 0 rgb(77, 77, 77, 0.2); + padding: 10px 5px 0; + box-shadow: 0 2px 6px 0 rgb(77 77 77 / 20%); border-radius: 0 0 6px 6px; } @@ -65,7 +66,7 @@ width: auto; margin: 0 30px; - @media (max-width: 750px) { + @media (width <= 750px) { margin: 0 20px; } } @@ -77,6 +78,7 @@ .home__statistic__left__data { background: linear-gradient(134deg, #3fb39e, #1ec196 51%, #3cc6b7 100%); } + .home__statistic__left__chart { background: linear-gradient(134deg, #0bad8e 2%, #20c5a5 48%, #0baab1 99%); } @@ -89,11 +91,11 @@ width: 100%; display: flex; - @media (max-width: 1200px) { + @media (width <= 1200px) { height: 213px; } - @media (max-width: 750px) { + @media (width <= 750px) { flex-direction: column; height: 150px; } @@ -102,16 +104,16 @@ flex: 1; min-width: 0; display: flex; - border-radius: 6px 0 0 0; + border-radius: 6px 0 0; border: 2px solid #6b88cf; - @media (max-width: 1200px) { + @media (width <= 1200px) { flex-direction: column; } - @media (max-width: 750px) { + @media (width <= 750px) { border-radius: 6px 6px 0 0; - border: 0px solid; + border: 0 solid; } .home__statistic__left__data { @@ -121,41 +123,42 @@ display: flex; flex-direction: column; justify-content: space-between; - border-radius: 6px 0 0 0; + border-radius: 6px 0 0; background: linear-gradient(314deg, #6093e4, #6b88cf 49%, #7074de 80%); - @media (min-width: 1440px) { + @media (width >= 1440px) { flex: 0; width: 246px; min-width: 246px; } - @media (max-width: 1200px) { - padding: 15px 30px 10px 30px; + @media (width <= 1200px) { + padding: 15px 30px 10px; flex-direction: row; } - @media (max-width: 750px) { + @media (width <= 750px) { border-radius: 6px 6px 0 0; padding: 15px 23px 10px 20px; - margin: 0px; + margin: 0; } } + .home__statistic__left__chart { flex: 2; margin: -2px; - padding: 5px 5px 0px 10px; + padding: 5px 5px 0 10px; border-radius: 0; background: linear-gradient(304deg, #6e85e0 2%, #577cdb 48%, #486ecc 99%); - @media (max-width: 1200px) { + @media (width <= 1200px) { padding: 6px 20px; } - @media (max-width: 750px) { + @media (width <= 750px) { flex: 0; display: none; - margin: 0px; + margin: 0; } } } @@ -167,13 +170,13 @@ border-radius: 0 6px 0 0; border: 2px solid #334350; - @media (max-width: 1200px) { + @media (width <= 1200px) { flex-direction: column; border-radius: 0 6px 0 0; border: 2px solid #444c5a; } - @media (max-width: 750px) { + @media (width <= 750px) { flex-direction: column; border-radius: 0; } @@ -187,62 +190,66 @@ justify-content: space-between; background: linear-gradient(134deg, #444b57, #39424a 48%, #444c5a 96%); - @media (min-width: 1440px) { + @media (width >= 1440px) { flex: 0; width: 246px; min-width: 246px; } - @media (max-width: 1200px) { - padding: 15px 30px 10px 30px; + @media (width <= 1200px) { + padding: 15px 30px 10px; margin: -2px; - border-radius: 0px 6px 0px 0px; + border-radius: 0 6px 0 0; flex-direction: row; } - @media (max-width: 750px) { - padding: 15px 20px 10px 20px; + @media (width <= 750px) { + padding: 15px 20px 10px; } } + .home__statistic__right__chart { flex: 2; margin: -2px; padding: 5px 5px 0 10px; - border-radius: 0px 6px 0px 0px; + border-radius: 0 6px 0 0; background: linear-gradient(118deg, #31383e, #35414d 48%, #334350); - @media (max-width: 1200px) { - border-radius: 0px; + @media (width <= 1200px) { + border-radius: 0; padding: 6px 20px; } - @media (max-width: 750px) { + @media (width <= 750px) { flex: 0; display: none; - margin: 0px; + margin: 0; } } } -} -.HomeStatisticTopPanel { &.AfterHardFork { .home__statistic__left__panel { border-color: #232323; background-color: #232323; + .home__statistic__left__data { background: #232323; } + .home__statistic__left__chart { background: #232323; } } + .home__statistic__right__panel { border-color: #484e4e; background-color: #484e4e; + .home__statistic__right__data { background: #484e4e; } + .home__statistic__right__chart { background: #484e4e; } @@ -257,7 +264,7 @@ align-items: center; justify-content: center; background: #f8f9fa; - border: 0px solid white; + border: 0 solid white; border-radius: 0 0 6px 6px; cursor: pointer; diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index b07e9f2f8..cbdcbd692 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -1,4 +1,4 @@ -import { FC, memo, useEffect, useMemo, useRef } from 'react' +import { FC, memo, useMemo, useRef } from 'react' import { useHistory } from 'react-router' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' @@ -22,12 +22,10 @@ import { } from '../../constants/common' import { localeNumberString, handleHashRate, handleDifficulty } from '../../utils/number' import { handleBigNumber } from '../../utils/string' -import { useAppState, useDispatch } from '../../contexts/providers' import i18n from '../../utils/i18n' import LatestBlocksIcon from '../../assets/latest_blocks.png' import LatestTransactionsIcon from '../../assets/latest_transactions.png' import { BlockCardItem, TransactionCardItem } from './TableCard' -import { getTipBlockNumber } from '../../service/app/address' import Loading from '../../components/Loading/SmallLoading' import { useElementIntersecting, useInterval, useIsLGScreen, useIsMobile } from '../../utils/hook' import Banner from '../../components/Banner' @@ -35,10 +33,10 @@ import { handleBlockchainAlert } from '../../service/app/blockchain' import Search from '../../components/Search' import AverageBlockTimeChart from './AverageBlockTimeChart' import HashRateChart from './HashRateChart' -import { ComponentActions } from '../../contexts/actions' import styles from './index.module.scss' -import { fetchLatestBlocks, fetchLatestTransactions } from '../../service/http/fetcher' import { RouteState } from '../../routes/state' +import { explorerService, useLatestBlockNumber, useStatistics } from '../../services/ExplorerService' +import { useShowSearchBarInHeader } from '../../components/Header' interface BlockchainData { name: string @@ -116,7 +114,6 @@ const getBlockchainDataList = (statistics: State.Statistics, isMobile: boolean, ] const HomeHeaderTopPanel: FC = memo(() => { - const dispatch = useDispatch() const ref = useRef(null) const { height: resizedHeight } = useResizeDetector({ @@ -125,6 +122,8 @@ const HomeHeaderTopPanel: FC = memo(() => { }) const height = Math.round(resizedHeight ?? ref.current?.clientHeight ?? 0) const selfMarginTop = 20 + // TODO: This does not take into account the height of the Alert and Search when they appear, + // so a dynamic `--headerHeight` variable may be needed. const headerHeight = 64 const intersectingCheckOffset = height + selfMarginTop + headerHeight @@ -140,26 +139,7 @@ const HomeHeaderTopPanel: FC = memo(() => { true, ) - useEffect(() => { - if (ref.current == null) return - - dispatch({ - type: ComponentActions.UpdateHeaderSearchBarVisible, - payload: { - headerSearchBarVisible: !isFullDisplayInScreen, - }, - }) - - // eslint-disable-next-line consistent-return - return () => { - dispatch({ - type: ComponentActions.UpdateHeaderSearchBarVisible, - payload: { - headerSearchBarVisible: true, - }, - }) - } - }, [dispatch, isFullDisplayInScreen]) + useShowSearchBarInHeader(!isFullDisplayInScreen) return (
@@ -204,19 +184,16 @@ const TransactionList: FC<{ transactions: State.Transaction[]; tipBlockNumber: n export default () => { const isMobile = useIsMobile() const isLG = useIsLGScreen() - const dispatch = useDispatch() const history = useHistory() - const { - statistics, - app: { tipBlockNumber }, - } = useAppState() + const statistics = useStatistics() + const tipBlockNumber = useLatestBlockNumber() const [t] = useTranslation() const blocksQuery = useQuery( 'latest_blocks', async () => { // Using the size of list pages to request will be more friendly to the data reuse of the list pages. - const { data, meta } = await fetchLatestBlocks(ListPageParams.PageSize) + const { data, meta } = await explorerService.api.fetchLatestBlocks(ListPageParams.PageSize) const blocks = data.map(wrapper => wrapper.attributes) return { blocks, @@ -231,7 +208,7 @@ export default () => { const transactionsQuery = useQuery( ['latest_transactions'], async () => { - const { data, meta } = await fetchLatestTransactions(ListPageParams.PageSize) + const { data, meta } = await explorerService.api.fetchLatestTransactions(ListPageParams.PageSize) const transactions = data.map(wrapper => wrapper.attributes) return { transactions, @@ -251,11 +228,7 @@ export default () => { ) useInterval(() => { - getTipBlockNumber(dispatch) - }, BLOCK_POLLING_TIME) - - useInterval(() => { - handleBlockchainAlert(dispatch) + handleBlockchainAlert() }, BLOCKCHAIN_ALERT_POLLING_TIME) const blockchainDataList = getBlockchainDataList(statistics, isMobile, isLG) diff --git a/src/pages/NervosDao/DaoOverview/index.tsx b/src/pages/NervosDao/DaoOverview/index.tsx index 335ddacfd..ae2dc4917 100644 --- a/src/pages/NervosDao/DaoOverview/index.tsx +++ b/src/pages/NervosDao/DaoOverview/index.tsx @@ -6,7 +6,6 @@ import 'echarts/lib/component/title' import 'echarts/lib/component/legend' import 'echarts/lib/component/legendScroll' import { Tooltip } from 'antd' -import { useAppState } from '../../../contexts/providers' import { DaoOverviewPanel, DaoOverviewLeftPanel, @@ -28,6 +27,7 @@ import DecimalCapacity from '../../../components/DecimalCapacity' import { useIsLGScreen, useIsMobile } from '../../../utils/hook' import { ReactChartCore } from '../../StatisticsChart/common' import { HelpTip } from '../../../components/HelpTip' +import { ChartColor } from '../../../constants/common' interface NervosDaoItemContent { title: string @@ -288,29 +288,26 @@ const NervosDaoPieItem = ({ item }: { item: NervosDaoPieItemContent }) => ( export default ({ nervosDao }: { nervosDao: State.NervosDao }) => { const isMobile = useIsMobile() const isExactLG = useIsLGScreen(true) - const { - app: { chartColor }, - } = useAppState() const nervosDaoPieItemContents = useCallback( (nervosDao: State.NervosDao): NervosDaoPieItemContent[] => [ { title: i18n.t('nervos_dao.mining_reward'), content: , - color: chartColor.daoColors[0], + color: ChartColor.daoColors[0], }, { title: i18n.t('nervos_dao.deposit_compensation'), content: , - color: chartColor.daoColors[1], + color: ChartColor.daoColors[1], }, { title: i18n.t('nervos_dao.burnt'), content: , - color: chartColor.daoColors[2], + color: ChartColor.daoColors[2], }, ], - [chartColor], + [], ) return ( @@ -324,7 +321,7 @@ export default ({ nervosDao }: { nervosDao: State.NervosDao }) => {
{ const { push } = useHistory() @@ -26,7 +22,7 @@ export const NervosDao = () => { const tab = (params.tab as 'transactions' | 'depositors') || 'transactions' const queryNervosDao = useQuery(['nervos-dao'], async () => { - const wrapper = await fetchNervosDao() + const wrapper = await explorerService.api.fetchNervosDao() const nervosDao = wrapper.attributes return nervosDao }) @@ -34,7 +30,7 @@ export const NervosDao = () => { const queryNervosDaoTransactions = useQuery( ['nervos-dao-transactions', currentPage, _pageSize, params.filter], async () => { - const { data, meta } = await fetchNervosDaoTransactionsByFilter({ + const { data, meta } = await explorerService.api.fetchNervosDaoTransactionsByFilter({ filter: params.filter, page: currentPage, size: _pageSize, @@ -53,7 +49,7 @@ export const NervosDao = () => { const queryNervosDaoDepositors = useQuery( ['nervos-dao-depositors'], async () => { - const { data } = await fetchNervosDaoDepositors() + const { data } = await explorerService.api.fetchNervosDaoDepositors() return { depositors: data.map(wrapper => wrapper.attributes) } }, { diff --git a/src/pages/NervosDao/styled.tsx b/src/pages/NervosDao/styled.tsx index 099573e8f..40499db13 100644 --- a/src/pages/NervosDao/styled.tsx +++ b/src/pages/NervosDao/styled.tsx @@ -37,7 +37,6 @@ export const DaoContentPanel = styled.div` export const DaoTabBarPanel = styled.div` width: 100%; - background: white; display: flex; margin-top: 20px; @@ -53,11 +52,12 @@ export const DaoTabBarPanel = styled.div` } @media (max-width: 750px) { - padding: 10px 0px; + padding: 10px 0; flex-direction: column; justify-content: center; font-size: 14px; align-items: stretch; + > div { padding-left: 10px; padding-right: 10px; diff --git a/src/pages/NftCollectionInfo/index.tsx b/src/pages/NftCollectionInfo/index.tsx index b90c2fab9..116601412 100644 --- a/src/pages/NftCollectionInfo/index.tsx +++ b/src/pages/NftCollectionInfo/index.tsx @@ -11,7 +11,7 @@ import NftCollectionInventory from '../../components/NftCollectionInventory' import Filter from '../../components/Search/Filter' import { ReactComponent as FilterIcon } from '../../assets/filter_icon.svg' import { ReactComponent as SelectedCheckIcon } from '../../assets/selected_check_icon.svg' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import i18n from '../../utils/i18n' import { useSearchParams, useIsMobile } from '../../utils/hook' import styles from './styles.module.scss' @@ -116,7 +116,7 @@ const NftCollectionInfo = () => { const { isLoading: isTransferListLoading, data: transferListRes } = useQuery>( ['nft-collection-transfer-list', id, page, filter, type], () => - v2AxiosIns(`/nft/transfers`, { + explorerService.api.requesterV2(`/nft/transfers`, { params: { page, collection_id: id, @@ -132,7 +132,7 @@ const NftCollectionInfo = () => { const { isLoading: isHolderListLoading, data: rawHolderList } = useQuery>( ['nft-collection-holder-list', id, page, sort, filter], () => - v2AxiosIns(`/nft/collections/${id}/holders`, { + explorerService.api.requesterV2(`/nft/collections/${id}/holders`, { params: { page, sort, @@ -147,7 +147,7 @@ const NftCollectionInfo = () => { const { isLoading: isInventoryLoading, data: inventoryList } = useQuery>( ['nft-collection-inventory', id, page], () => - v2AxiosIns(`/nft/collections/${id}/items`, { + explorerService.api.requesterV2(`/nft/collections/${id}/items`, { params: { page, }, diff --git a/src/pages/NftCollectionInfo/styles.module.scss b/src/pages/NftCollectionInfo/styles.module.scss index 5bd81b489..0febe02be 100644 --- a/src/pages/NftCollectionInfo/styles.module.scss +++ b/src/pages/NftCollectionInfo/styles.module.scss @@ -1,5 +1,5 @@ .container { - margin: 0px 120px 60px; + margin: 0 120px 60px; } .header { @@ -7,6 +7,7 @@ justify-content: space-between; align-items: center; padding-bottom: 20px; + h5 { font-size: 24px; font-weight: bold; @@ -27,7 +28,7 @@ border-top-left-radius: 4px; border-top-right-radius: 4px; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { flex-direction: column; justify-content: stretch; padding: 1rem; @@ -40,7 +41,7 @@ font-size: 1.25rem; align-self: stretch; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { justify-content: space-between; } @@ -53,14 +54,17 @@ &:not(:last-child) { margin-right: 4rem; - @media screen and (max-width: 1440px) { + + @media screen and (width <= 1440px) { margin-right: 3rem; } - @media screen and (max-width: 1280px) { + + @media screen and (width <= 1280px) { margin-right: 1rem; } } - @media screen and (max-width: 1280px) { + + @media screen and (width <= 1280px) { font-size: 0.875rem; } @@ -68,23 +72,28 @@ font-weight: 500; border-bottom-color: var(--primary-color); } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { border-width: 2px; font-size: 1.125rem; } } } + .filters { display: flex; - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { & > div { margin-top: 1.5rem; + &:first-child { flex: 1; } } } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { width: 100%; } } @@ -93,25 +102,30 @@ display: flex; flex-direction: column; width: 200px; + a { display: flex; justify-content: space-between; color: var(--primary-color); - padding: 10px 0px 10px 10px; + padding: 10px 0 10px 10px; margin-right: 8px; border-radius: 8px; + &:hover { background: var(--primary-hover-bg-color); color: var(--primary-color); cursor: pointer; } + &[data-is-active='false'] { color: #000; + svg { display: none; } } } + svg path { fill: var(--primary-color); } @@ -133,6 +147,7 @@ position: relative; font-size: 14px; font-weight: 700; + th { vertical-align: middle; padding-top: 12px; @@ -140,7 +155,8 @@ text-transform: capitalize; white-space: nowrap; } - &:after { + + &::after { position: absolute; left: -15px; bottom: -2px; @@ -174,6 +190,7 @@ &:not(:first-of-type) { text-align: right; } + &:first-child, &:last-child { padding-left: 25px; @@ -186,15 +203,15 @@ } } -@media screen and (max-width: 1200px) { +@media screen and (width <= 1200px) { .container { - margin: 0px 45px 60px; + margin: 0 45px 60px; } } -@media screen and (max-width: 750px) { +@media screen and (width <= 750px) { .container { - margin: 0px 8px 60px; + margin: 0 8px 60px; } } @@ -225,10 +242,12 @@ align-items: center; cursor: pointer; padding-left: 0.8rem; + svg { height: 1rem; width: 1rem; } + &[data-is-active='true'] { svg path { fill: var(--primary-color); @@ -240,25 +259,30 @@ display: flex; flex-direction: column; width: 200px; + a { display: flex; justify-content: space-between; color: var(--primary-color); - padding: 10px 0px 10px 10px; + padding: 10px 0 10px 10px; margin-right: 8px; border-radius: 8px; + &:hover { background: var(--primary-hover-bg-color); color: var(--primary-color); cursor: pointer; } + &[data-is-active='false'] { color: #000; + svg { display: none; } } } + svg path { fill: var(--primary-color); } @@ -268,7 +292,7 @@ :global { .ant-popover-inner { border-radius: 8px; - box-shadow: 0px 2px 10px 0px #eeeeee; + box-shadow: 0 2px 10px 0 #eee; } } } diff --git a/src/pages/NftCollections/List.tsx b/src/pages/NftCollections/List.tsx index 111d1226d..ac63d6db3 100644 --- a/src/pages/NftCollections/List.tsx +++ b/src/pages/NftCollections/List.tsx @@ -323,7 +323,7 @@ export const ListOnMobile: React.FC<{ isLoading: boolean; list: Array{`${item.creator.slice(0, 8)}...${item.creator.slice(-8)}`} diff --git a/src/pages/NftCollections/index.tsx b/src/pages/NftCollections/index.tsx index 1e07c846d..49e1cd212 100644 --- a/src/pages/NftCollections/index.tsx +++ b/src/pages/NftCollections/index.tsx @@ -5,7 +5,7 @@ import Content from '../../components/Content' import { NFTCollection, ListOnDesktop, ListOnMobile, isTxFilterType } from './List' import Pagination from '../../components/Pagination' import { getPrimaryColor } from '../../constants/common' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import i18n from '../../utils/i18n' import { udtSubmitEmail } from '../../utils/util' import { useSearchParams, useSortParam } from '../../utils/hook' @@ -38,7 +38,7 @@ const NftCollections = () => { const isValidFilter = isTxFilterType(type) && type !== 'all' const { isLoading, data } = useQuery>(['nft-collections', page, sort, type], () => - v2AxiosIns('nft/collections', { + explorerService.api.requesterV2('nft/collections', { params: { page, sort, diff --git a/src/pages/NftCollections/styles.module.scss b/src/pages/NftCollections/styles.module.scss index a5712bde1..4823b570d 100644 --- a/src/pages/NftCollections/styles.module.scss +++ b/src/pages/NftCollections/styles.module.scss @@ -7,6 +7,7 @@ justify-content: space-between; align-items: center; padding-bottom: 20px; + h5 { font-size: 24px; font-weight: bold; @@ -25,6 +26,7 @@ position: relative; width: 100%; margin-bottom: 4px; + .typeFilter, .holderMinted { justify-content: end; @@ -35,6 +37,7 @@ position: relative; font-size: 14px; font-weight: 700; + th { vertical-align: middle; padding-top: 12px; @@ -42,7 +45,8 @@ text-transform: capitalize; white-space: nowrap; } - &:after { + + &::after { position: absolute; left: -15px; top: 44px; @@ -76,6 +80,7 @@ &:not(:first-of-type) { text-align: right; } + &:first-child, &:last-child { padding-left: 25px; @@ -115,9 +120,11 @@ .typeFilter { display: flex; align-items: center; + svg { cursor: pointer; } + &[data-is-active='true'] { svg path { fill: var(--primary-color); @@ -127,12 +134,14 @@ .activeIcon { color: var(--primary-color); + svg { path { fill: var(--primary-color); } } } + .filter { height: 1rem; } @@ -142,25 +151,34 @@ display: flex; flex-direction: column; width: 200px; + a { display: flex; + align-items: center; justify-content: space-between; color: var(--primary-color); - padding: 10px 0px 10px 10px; - margin-right: 8px; + padding: 10px 0 10px 10px; border-radius: 8px; + &:hover { background: var(--primary-hover-bg-color); color: var(--primary-color); cursor: pointer; } + + &[data-is-active='true'] { + pointer-events: none; + } + &[data-is-active='false'] { color: #000; + svg { display: none; } } } + svg path { fill: var(--primary-color); } @@ -170,7 +188,11 @@ :global { .ant-popover-inner { border-radius: 8px; - box-shadow: 0px 2px 10px 0px #eeeeee; + box-shadow: 0 2px 10px 0 #eee; + } + + .ant-popover-inner-content { + padding: 14px 24px 14px 16px; } } } @@ -181,6 +203,7 @@ color: var(--primary-color); } } + svg { cursor: pointer; } @@ -190,6 +213,7 @@ display: flex; align-items: center; cursor: pointer; + .sortActive { color: var(--primary-color); } @@ -203,10 +227,12 @@ div[data-role='mobile-list'] { display: flex; height: 3rem; background: #fff; + & > div { white-space: nowrap; flex: 1; } + padding: 0 1rem; border-radius: 8px; overflow: hidden; @@ -217,10 +243,12 @@ div[data-role='mobile-list'] { margin-left: 36px; margin-bottom: 10px; display: flex; + dt { flex: 1; min-width: 105px; } + dd { flex: 1; margin: 0; @@ -229,7 +257,8 @@ div[data-role='mobile-list'] { .listHeader + div { position: relative; - &:before { + + &::before { position: absolute; left: -15px; top: -10px; @@ -240,22 +269,21 @@ div[data-role='mobile-list'] { background-color: #ededed; } } + .listItem { border-bottom: 1px solid #f0f0f0; padding: 1rem 1rem 0; background: #fff; + dt::after { content: ':'; } - dd { - a { - font-weight: 500 !important; - } - } } + .holderMinted { padding-left: 36px; } + .loading { text-align: center; } @@ -267,31 +295,27 @@ div[data-role='mobile-list'] { } } -@media screen and (max-width: 1200px) { +@media screen and (width <= 1200px) { .container { margin: 40px 45px 60px; } } -@media screen and (max-width: 1000px) { +@media screen and (width <= 1000px) { .container { margin: 40px 8px 60px; + table[data-role='desktop-list'] { display: none; } + div[data-role='mobile-list'] { display: block; } } - .antPopover { - :global { - .ant-popover-inner-content { - padding: 14px 15px 14px 12px !important; - } - } - } } -@media screen and (max-width: 350px) { + +@media screen and (width <= 350px) { div[data-role='mobile-list'] { font-size: 0.8rem; } diff --git a/src/pages/NftInfo/index.tsx b/src/pages/NftInfo/index.tsx index e6d397f35..c1aa6c6e1 100644 --- a/src/pages/NftInfo/index.tsx +++ b/src/pages/NftInfo/index.tsx @@ -8,7 +8,7 @@ import i18n from '../../utils/i18n' import NftItemTransfers, { TransferListRes } from '../../components/NftItemTransfers' import Pagination from '../../components/Pagination' import { ReactComponent as Cover } from '../../assets/nft_cover.svg' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { getPrimaryColor } from '../../constants/common' import styles from './styles.module.scss' import { patchMibaoImg, handleNftImgError } from '../../utils/util' @@ -45,12 +45,14 @@ const NftInfo = () => { icon_url: string } }> - >(['nft-item-info', collection, id], () => v2AxiosIns(`nft/collections/${collection}/items/${id}`)) + >(['nft-item-info', collection, id], () => + explorerService.api.requesterV2(`nft/collections/${collection}/items/${id}`), + ) const { isLoading: isTransferListLoading, data: transferListRes } = useQuery>( ['nft-item-transfer-list', collection, id, page], () => - v2AxiosIns(`/nft/transfers`, { + explorerService.api.requesterV2(`/nft/transfers`, { params: { page, collection_id: collection, diff --git a/src/pages/NftInfo/styles.module.scss b/src/pages/NftInfo/styles.module.scss index 722d02b1f..e12f50dc3 100644 --- a/src/pages/NftInfo/styles.module.scss +++ b/src/pages/NftInfo/styles.module.scss @@ -1,18 +1,19 @@ .container { --margin-top: 104px; --margin-bottom: 40px; + width: 1200px; margin: var(--margin-top) auto var(--margin-bottom); flex: 1; - @media screen and (max-width: 1232px) { + @media screen and (width <= 1232px) { box-sizing: border-box; width: 100%; padding-left: 16px; padding-right: 16px; } - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { --margin-top: 84px; --margin-bottom: 20px; } @@ -22,12 +23,15 @@ padding: 24px; background: #fff; display: flex; + & > div { overflow: hidden; } + .info { width: 100%; } + .cover { width: 440px; height: 440px; @@ -45,6 +49,7 @@ text-overflow: ellipsis; line-height: 1; } + .items { dl { margin: 0; @@ -52,12 +57,14 @@ flex-direction: column; line-height: 1; } + dt { font-size: 14px; font-weight: 400; color: #999; margin: 40px 0 20px; } + dd { margin: 0; overflow: hidden; @@ -65,23 +72,27 @@ } } - @media screen and (max-width: 800px) { + @media screen and (width <= 800px) { flex-direction: column; align-items: center; + .cover { margin-bottom: 16px; margin-right: 0; } + .name, .itmes { width: 440px; } } - @media screen and (max-width: 472px) { + + @media screen and (width <= 472px) { .cover { width: 320px; height: 320px; } + .name, .items { width: 100%; diff --git a/src/pages/Script/ScriptsComp.tsx b/src/pages/Script/ScriptsComp.tsx index 349a7d9a6..ae383a85e 100644 --- a/src/pages/Script/ScriptsComp.tsx +++ b/src/pages/Script/ScriptsComp.tsx @@ -6,7 +6,7 @@ import camelcase from 'camelcase' import { useParams } from 'react-router-dom' import Pagination from '../../components/Pagination' import TransactionItem from '../../components/TransactionItem/index' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService, Response } from '../../services/ExplorerService' import i18n from '../../utils/i18n' import { TransactionCellDetailModal, TransactionCellInfoPanel } from '../Transaction/TransactionCell/styled' import SimpleButton from '../../components/SimpleButton' @@ -19,17 +19,16 @@ import { CellInScript, CkbTransactionInScript } from './types' import styles from './styles.module.scss' import { QueryResult } from '../../components/QueryResult' import AddressText from '../../components/AddressText' -import { AppActions } from '../../contexts/actions' -import { useDispatch } from '../../contexts/providers' import { ReactComponent as CopyIcon } from '../../assets/copy_icon.svg' import { ReactComponent as InfoMoreIcon } from '../../assets/info_more_icon.svg' +import { useSetToast } from '../../components/Toast' export const ScriptTransactions = ({ page, size }: { page: number; size: number }) => { const history = useHistory() const { codeHash, hashType } = useParams<{ codeHash: string; hashType: string }>() const transactionsQuery = useQuery(['scripts_ckb_transactions', codeHash, hashType, page, size], async () => { - const { data, meta } = await v2AxiosIns + const { data, meta } = await explorerService.api.requesterV2 .get(`scripts/ckb_transactions`, { params: { code_hash: codeHash, @@ -125,7 +124,7 @@ export const ScriptCells = ({ const { codeHash, hashType } = useParams<{ codeHash: string; hashType: string }>() const cellsQuery = useQuery([`scripts_${cellType}`, codeHash, hashType, page, size], async () => { - const { data, meta } = await v2AxiosIns + const { data, meta } = await explorerService.api.requesterV2 .get(`scripts/${cellType}`, { params: { code_hash: codeHash, @@ -213,21 +212,18 @@ export const ScriptCells = ({ } export const CodeHashMessage = ({ codeHash }: { codeHash: string }) => { - const dispatch = useDispatch() + const setToast = useSetToast() return (
- {codeHash} +
+ {codeHash} +
{ navigator.clipboard.writeText(codeHash).then( () => { - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: i18n.t('common.copied'), - }, - }) + setToast({ message: i18n.t('common.copied') }) }, error => { console.error(error) diff --git a/src/pages/Script/index.tsx b/src/pages/Script/index.tsx index 907d56bf1..08070d8f2 100644 --- a/src/pages/Script/index.tsx +++ b/src/pages/Script/index.tsx @@ -18,7 +18,7 @@ import { shannonToCkb, toCamelcase } from '../../utils/util' import DecimalCapacity from '../../components/DecimalCapacity' import { ScriptInfo, ScriptTabType } from './types' import styles from './styles.module.scss' -import { v2AxiosIns } from '../../service/http/fetcher' +import { explorerService, Response } from '../../services/ExplorerService' const scriptDataList = isMainnet() ? MainnetContractHashTags : TestnetContractHashTags @@ -39,7 +39,7 @@ const scriptHashNameMap = new Map( ) const getScriptInfo = (scriptInfo: ScriptInfo) => { - const { scriptName, scriptType, typeId, codeHash, hashType, capacityOfDeployedCells, capacityOfReferringCells } = + const { scriptName, scriptType, id, codeHash, hashType, capacityOfDeployedCells, capacityOfReferringCells } = scriptInfo const items: OverviewItemData[] = [ { @@ -58,8 +58,7 @@ const getScriptInfo = (scriptInfo: ScriptInfo) => { }, { title: i18n.t('scripts.type_id'), - tooltip: i18n.t('glossary.type_id'), - content: typeId || '-', + content: id ? : '-', }, { title: i18n.t('scripts.code_hash'), @@ -111,7 +110,7 @@ export const ScriptPage = () => { const [pageOfReferringCells, setPageOfReferringCells] = useState(1) const { status, data: resp } = useQuery(['scripts_general_info', codeHash, hashType], () => - v2AxiosIns.get(`scripts/general_info`, { + explorerService.api.requesterV2.get(`scripts/general_info`, { params: { code_hash: codeHash, hash_type: hashType, @@ -123,10 +122,9 @@ export const ScriptPage = () => { status === 'success' && resp ? toCamelcase>(resp?.data)!.data : ({ - id: 0, + id: '-', scriptName: '', scriptType: '', - typeId: '', codeHash, hashType, capacityOfDeployedCells: '0', diff --git a/src/pages/Script/styles.module.scss b/src/pages/Script/styles.module.scss index a4198a024..db280e2be 100644 --- a/src/pages/Script/styles.module.scss +++ b/src/pages/Script/styles.module.scss @@ -10,7 +10,7 @@ margin-top: 25px; margin-bottom: 40px; - @media (max-width: 750px) { + @media (width <= 750px) { margin: 0; padding: 20px; } @@ -21,25 +21,40 @@ flex-direction: column; justify-content: center; width: 100%; - background-color: #ffffff; + background-color: #fff; border-radius: 6px 6px 0 0; - :global(.ant-tabs-nav:before) { - border-bottom-width: 4px; - } - :global(.ant-tabs-nav-list) { - margin-left: 40px; - } - :global(.ant-tabs-tab-btn) { - color: #333333 !important; - font-weight: 400; - font-size: 20px; - line-height: 23px; - margin-bottom: 0; - } - :global(.ant-tabs-tab-btn[aria-selected='true']) { - font-weight: 500; + + :global { + .ant-tabs-nav { + &::before { + border-bottom-width: 4px; + } + } + + .ant-tabs-nav-list { + margin-left: 40px; + } + + .ant-tabs-tab-btn { + color: #333; + font-weight: 400; + font-size: 20px; + line-height: 23px; + margin-bottom: 0; + + &[aria-selected='true'] { + font-weight: 500; + } + } + + .ant-tabs-tab-active { + .ant-tabs-tab-btn { + color: #333; + } + } } - :global(.ant-tabs-ink-bar) { + + & > :global(.ant-tabs-nav .ant-tabs-ink-bar) { background: linear-gradient( to right, transparent 30%, @@ -47,8 +62,8 @@ var(--primary-color) 70%, transparent 70% ); - height: 3px !important; - bottom: 3px !important; + height: 3px; + bottom: 3px; } } @@ -57,56 +72,68 @@ table { width: 100%; line-height: 1.2; + tr { border-bottom: 1px solid #e5e5e5; } + tbody tr:last-child { border-bottom: none; } + th { font-size: 1rem; font-weight: 500; color: #333; } + th, td { white-space: nowrap; padding: 22px 0; user-select: none; cursor: default; + &:nth-child(3) { padding-left: 22px; + div { justify-content: flex-start; } } } + td:first-child { max-width: 25vw; } + a { color: var(--primary-color); } } } + .scriptCellsPanel { padding: 0 40px; - @media screen and (max-width: 1200px) { + @media screen and (width <= 1200px) { padding: 0 10px; } } + .scriptDecimalCenter { > div { align-items: baseline; } } + .cellInfoMore { > div { float: right; } + :global(.transaction__cell__info__content) { - width: fit-content !important; + width: fit-content; padding: 0 20px; line-height: 0; } @@ -123,19 +150,19 @@ min-width: 0; .codeHash { - width: 350px !important; + width: 350px; margin-right: 16px; } - @media screen and (max-width: 1300px) { + @media screen and (width <= 1300px) { .codeHash { - width: 280px !important; + width: 280px; } } - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { .codeHash { - width: 600px !important; + width: 600px; } } } @@ -143,3 +170,8 @@ .hashType { text-transform: capitalize; } + +.typeId { + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/src/pages/Script/types.ts b/src/pages/Script/types.ts index 4df243d3a..e3bcee0f4 100644 --- a/src/pages/Script/types.ts +++ b/src/pages/Script/types.ts @@ -1,8 +1,7 @@ export interface ScriptInfo { - id: number + id: string scriptName: string scriptType: string - typeId: string codeHash: string hashType: 'type' | 'data' capacityOfDeployedCells: string diff --git a/src/pages/ScriptList/styles.module.scss b/src/pages/ScriptList/styles.module.scss index 8762af0eb..a928db18e 100644 --- a/src/pages/ScriptList/styles.module.scss +++ b/src/pages/ScriptList/styles.module.scss @@ -5,9 +5,9 @@ $accent-color: #efefef; font-size: 24px; font-weight: 700; max-width: 1080px; - margin: 45px auto 20px auto; + margin: 45px auto 20px; - @media screen and (max-width: 1140px) { + @media screen and (width <= 1140px) { margin: 45px 15px 20px; } } @@ -15,11 +15,11 @@ $accent-color: #efefef; .container { background: white; max-width: 1080px; - margin: 0 auto 45px auto; + margin: 0 auto 45px; padding: 16px; border-radius: 6px; - @media screen and (max-width: 1140px) { + @media screen and (width <= 1140px) { margin: 20px 15px; } @@ -41,6 +41,7 @@ $accent-color: #efefef; .links { display: flex; align-items: center; + a { margin-right: 10px; margin-bottom: 20px; @@ -57,7 +58,7 @@ $accent-color: #efefef; user-select: none; transition: background 0.3s; - @media screen and (max-width: 400px) { + @media screen and (width <= 400px) { word-break: break-all; } @@ -69,12 +70,12 @@ $accent-color: #efefef; text-decoration: line-through; } - &:before { + &::before { position: absolute; top: 50%; transform: translateY(-50%); left: 0.75rem; - font-family: 'mono'; + font-family: initial; content: '▲'; width: 1.75rem; height: 1.75rem; @@ -98,7 +99,8 @@ $accent-color: #efefef; details[open] { summary { background: $accent-color; - &:before { + + &::before { transform: translateY(-50%) rotate(0.5turn); } } diff --git a/src/pages/SimpleUDT/index.tsx b/src/pages/SimpleUDT/index.tsx index ed2c1e0ef..4d33a8953 100644 --- a/src/pages/SimpleUDT/index.tsx +++ b/src/pages/SimpleUDT/index.tsx @@ -1,12 +1,10 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { Link, useHistory, useLocation, useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' import { Popover } from 'antd' import SimpleUDTHashCard from '../../components/Card/HashCard' import Content from '../../components/Content' -import { useDispatch } from '../../contexts/providers/index' -import { getTipBlockNumber } from '../../service/app/address' import i18n from '../../utils/i18n' import { SimpleUDTContentPanel, UDTTransactionTitlePanel, TypeScriptController } from './styled' import SimpleUDTComp, { SimpleUDTOverview } from './SimpleUDTComp' @@ -20,7 +18,7 @@ import { isMainnet } from '../../utils/chain' import Filter from '../../components/Search/Filter' import { localeNumberString } from '../../utils/number' import Script from '../../components/Script' -import { fetchSimpleUDT, fetchSimpleUDTTransactions } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { deprecatedAddrToNewAddr } from '../../utils/util' import { QueryResult } from '../../components/QueryResult' import { defaultUDTInfo } from './state' @@ -43,7 +41,6 @@ enum TransactionType { export const SimpleUDT = () => { const isMobile = useIsMobile() - const dispatch = useDispatch() const { push } = useHistory() const { search } = useLocation() const [t] = useTranslation() @@ -55,12 +52,8 @@ export const SimpleUDT = () => { const filter = query.get('filter') const type = query.get('type') - useEffect(() => { - getTipBlockNumber(dispatch) - }, [dispatch]) - const querySimpleUDT = useQuery(['simple-udt'], async () => { - const wrapper = await fetchSimpleUDT(typeHash) + const wrapper = await explorerService.api.fetchSimpleUDT(typeHash) const udt = wrapper.attributes return udt }) @@ -70,7 +63,7 @@ export const SimpleUDT = () => { const querySimpleUDTTransactions = useQuery( ['simple-udt-transactions', typeHash, currentPage, _pageSize, filter, type], async () => { - const { data, meta } = await fetchSimpleUDTTransactions({ + const { data, meta } = await explorerService.api.fetchSimpleUDTTransactions({ typeHash, page: currentPage, size: pageSize, diff --git a/src/pages/SimpleUDT/styles.module.scss b/src/pages/SimpleUDT/styles.module.scss index 158777a6e..622176197 100644 --- a/src/pages/SimpleUDT/styles.module.scss +++ b/src/pages/SimpleUDT/styles.module.scss @@ -4,11 +4,13 @@ svg { pointer-events: none; + path { fill: #999; } } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { margin-left: 2px; } } @@ -16,11 +18,11 @@ .addressWidthModify { max-width: 80%; - @media (max-width: 1200px) { + @media (width <= 1200px) { max-width: 40%; } - @media (max-width: 750px) { + @media (width <= 750px) { max-width: 100%; } } @@ -31,7 +33,8 @@ > div:last-child { margin-left: 8px; } - @media screen and (max-width: 750px) { + + @media screen and (width <= 750px) { width: 100%; } } @@ -41,10 +44,12 @@ display: none; cursor: pointer; padding-left: 0.8rem; + svg { height: 1rem; width: 1rem; } + &[data-is-active='true'] { svg path { fill: var(--primary-color); @@ -56,25 +61,30 @@ display: flex; flex-direction: column; width: 200px; + a { display: flex; justify-content: space-between; color: var(--primary-color); - padding: 10px 0px 10px 10px; + padding: 10px 0 10px 10px; margin-right: 8px; border-radius: 8px; + &:hover { background: var(--primary-hover-bg-color); color: var(--primary-color); cursor: pointer; } + &[data-is-active='false'] { color: #000; + svg { display: none; } } } + svg path { fill: var(--primary-color); } @@ -84,7 +94,7 @@ :global { .ant-popover-inner { border-radius: 8px; - box-shadow: 0px 2px 10px 0px #eeeeee; + box-shadow: 0 2px 10px 0 #eee; } } } diff --git a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx index a4ca5707f..3426ee07e 100644 --- a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx +++ b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx @@ -6,7 +6,7 @@ import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { localeNumberString } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage, SmartChartPageProps } from '../common' -import { fetchStatisticAddressBalanceRank } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useAdaptPCEllipsis } from '../../../utils/hook' @@ -17,7 +17,7 @@ const getAddressWithRanking = (statisticAddressBalanceRanks: State.StatisticAddr const getOption = ( statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, getAdaptAddressText: (address: string) => string, @@ -99,7 +99,7 @@ const getOption = ( } const fetchStatisticAddressBalanceRanks = async () => { - const resp = await fetchStatisticAddressBalanceRank() + const resp = await explorerService.api.fetchStatisticAddressBalanceRank() return resp.attributes.addressBalanceRanking } diff --git a/src/pages/StatisticsChart/activities/AddressCount.tsx b/src/pages/StatisticsChart/activities/AddressCount.tsx index 2247f04fa..6d41c6084 100644 --- a/src/pages/StatisticsChart/activities/AddressCount.tsx +++ b/src/pages/StatisticsChart/activities/AddressCount.tsx @@ -4,12 +4,12 @@ import i18n, { currentLanguage } from '../../../utils/i18n' import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' -import { fetchStatisticAddressCount } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' const getOption = ( statisticAddressCounts: State.StatisticAddressCount[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -104,7 +104,7 @@ export const AddressCountChart = ({ isThumbnail = false }: { isThumbnail?: boole title={t('statistic.address_count')} description={t('statistic.address_count_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticAddressCount} + fetchData={explorerService.api.fetchStatisticAddressCount} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.AddressCount} diff --git a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx index 790af37fd..65146d09c 100644 --- a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx +++ b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx @@ -5,7 +5,7 @@ import { DATA_ZOOM_CONFIG, handleAxis, handleLogGroupAxis } from '../../../utils import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticBalanceDistribution } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const widthSpan = (value: string) => tooltipWidth(value, currentLanguage() === 'en' ? 270 : 110) @@ -15,7 +15,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: string } const getOption = ( statisticBalanceDistributions: State.StatisticBalanceDistribution[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -147,7 +147,7 @@ const getOption = ( } const fetchStatisticBalanceDistributions = async () => { - const wrapper = await fetchStatisticBalanceDistribution() + const wrapper = await explorerService.api.fetchStatisticBalanceDistribution() const balanceDistributionArray = wrapper.attributes.addressBalanceDistribution const balanceDistributions = balanceDistributionArray.map(distribution => { const [balance, addresses, sumAddresses] = distribution diff --git a/src/pages/StatisticsChart/activities/CellCount.tsx b/src/pages/StatisticsChart/activities/CellCount.tsx index 769247f61..159f09b0c 100644 --- a/src/pages/StatisticsChart/activities/CellCount.tsx +++ b/src/pages/StatisticsChart/activities/CellCount.tsx @@ -5,7 +5,7 @@ import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticCellCount } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const widthSpan = (value: string) => tooltipWidth(value, currentLanguage() === 'en' ? 125 : 80) @@ -24,7 +24,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: [string, const getOption = ( statisticCellCounts: State.StatisticCellCount[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -165,7 +165,7 @@ export const CellCountChart = ({ isThumbnail = false }: { isThumbnail?: boolean { @@ -100,7 +100,7 @@ export const TransactionCountChart = ({ isThumbnail = false }: { isThumbnail?: b { @@ -107,7 +107,7 @@ export const TxFeeHistoryChart = ({ isThumbnail = false }: { isThumbnail?: boole title={t('statistic.tx_fee_history')} description={t('statistic.tx_fee_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticTxFeeHistory} + fetchData={explorerService.api.fetchStatisticTxFeeHistory} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.TransactionFee} diff --git a/src/pages/StatisticsChart/block/AverageBlockTime.tsx b/src/pages/StatisticsChart/block/AverageBlockTime.tsx index 23f5b463d..128e7a3e3 100644 --- a/src/pages/StatisticsChart/block/AverageBlockTime.tsx +++ b/src/pages/StatisticsChart/block/AverageBlockTime.tsx @@ -4,12 +4,12 @@ import { parseDateNoTime, parseSimpleDate, parseSimpleDateNoSecond } from '../.. import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' -import { fetchStatisticAverageBlockTimes } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' const getOption = ( statisticAverageBlockTimes: State.StatisticAverageBlockTime[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -184,7 +184,7 @@ export const AverageBlockTimeChart = ({ isThumbnail = false }: { isThumbnail?: b title={t('statistic.average_block_time')} description={t('statistic.average_block_time_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticAverageBlockTimes} + fetchData={explorerService.api.fetchStatisticAverageBlockTimes} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.AverageBlockTime} diff --git a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx index 1451712ab..8d4cde2a6 100644 --- a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx @@ -3,11 +3,11 @@ import i18n, { currentLanguage } from '../../../utils/i18n' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticBlockTimeDistribution } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticBlockTimeDistributions: State.StatisticBlockTimeDistribution[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -90,7 +90,7 @@ const getOption = ( const fetchStatisticBlockTimeDistributions = async () => { const { attributes: { blockTimeDistribution }, - } = await fetchStatisticBlockTimeDistribution() + } = await explorerService.api.fetchStatisticBlockTimeDistribution() const sumBlocks = blockTimeDistribution .flatMap(data => Number(data[1])) .reduce((previous, current) => previous + current) diff --git a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx index 58958687b..6b3d552fd 100644 --- a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx @@ -4,12 +4,12 @@ import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { parseHourFromMinute } from '../../../utils/date' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' -import { fetchStatisticEpochTimeDistribution } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' const getOption = ( statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -94,7 +94,7 @@ const getOption = ( const fetchStatisticEpochTimeDistributions = async () => { const { attributes: { epochTimeDistribution }, - } = await fetchStatisticEpochTimeDistribution() + } = await explorerService.api.fetchStatisticEpochTimeDistribution() const statisticEpochTimeDistributions: State.StatisticEpochTimeDistribution[] = epochTimeDistribution.map(data => { const [time, epoch] = data return { diff --git a/src/pages/StatisticsChart/common/index.tsx b/src/pages/StatisticsChart/common/index.tsx index bf1a103e1..5d66b21f2 100644 --- a/src/pages/StatisticsChart/common/index.tsx +++ b/src/pages/StatisticsChart/common/index.tsx @@ -21,9 +21,10 @@ import SmallLoading from '../../../components/Loading/SmallLoading' import i18n from '../../../utils/i18n' import Content from '../../../components/Content' import { useChartQueryWithCache, useIsMobile, usePrevious, useWindowResize } from '../../../utils/hook' -import { useAppState } from '../../../contexts/providers' import { isDeepEqual } from '../../../utils/util' import { HelpTip } from '../../../components/HelpTip' +import { ChartColor } from '../../../constants/common' +import { Response } from '../../../services/ExplorerService' const LoadingComp = ({ isThumbnail }: { isThumbnail?: boolean }) => (isThumbnail ? : ) @@ -160,7 +161,7 @@ export interface SmartChartPageProps { onFetched?: (dataList: T[]) => void getEChartOption: ( dataList: T[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail: boolean, ) => echarts.EChartOption @@ -183,7 +184,6 @@ export function SmartChartPage({ cacheMode = 'forever', }: SmartChartPageProps): ReactElement { const isMobile = useIsMobile() - const { app } = useAppState() const query = useChartQueryWithCache(fetchData, cacheKey, cacheMode) const dataList = useMemo(() => query.data ?? [], [query.data]) @@ -194,8 +194,8 @@ export function SmartChartPage({ }, [onFetched, query.data]) const option = useMemo( - () => getEChartOption(dataList, app.chartColor, isMobile, isThumbnail), - [app.chartColor, dataList, getEChartOption, isMobile, isThumbnail], + () => getEChartOption(dataList, ChartColor, isMobile, isThumbnail), + [dataList, getEChartOption, isMobile, isThumbnail], ) const content = query.isLoading ? ( diff --git a/src/pages/StatisticsChart/mining/Difficulty.tsx b/src/pages/StatisticsChart/mining/Difficulty.tsx index 3a47fbe10..3bd7b723b 100644 --- a/src/pages/StatisticsChart/mining/Difficulty.tsx +++ b/src/pages/StatisticsChart/mining/Difficulty.tsx @@ -6,11 +6,11 @@ import { parseDateNoTime } from '../../../utils/date' import { handleDifficulty } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticDifficulty } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticDifficulties: State.StatisticDifficulty[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -103,7 +103,7 @@ export const DifficultyChart = ({ isThumbnail = false }: { isThumbnail?: boolean { @@ -185,7 +185,7 @@ export const DifficultyHashRateChart = ({ isThumbnail = false }: { isThumbnail?: tooltipWidth(value, currentLanguage() === 'en' ? 90 : 80) @@ -22,7 +22,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: string } const getOption = ( statisticChartData: State.StatisticDifficultyUncleRateEpoch[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -199,7 +199,7 @@ export const DifficultyUncleRateEpochChart: FC<{ isThumbnail?: boolean }> = ({ i { @@ -101,7 +101,7 @@ export const HashRateChart = ({ isThumbnail = false }: { isThumbnail?: boolean } title={t('block.hash_rate')} description={t('glossary.hash_rate')} isThumbnail={isThumbnail} - fetchData={fetchStatisticHashRate} + fetchData={explorerService.api.fetchStatisticHashRate} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.HashRate} diff --git a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx index 16f6e704c..29b67e5dd 100644 --- a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' import i18n, { currentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SmartChartPage, SmartChartPageProps } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticMinerAddressDistribution } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { useAdaptMobileEllipsis, useAdaptPCEllipsis, useIsMobile } from '../../../utils/hook' const Colors = [ @@ -22,7 +22,7 @@ const Colors = [ const getOption = ( statisticMinerAddresses: State.StatisticMinerAddress[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, getAdaptAddressText: (address: string) => string, @@ -85,7 +85,7 @@ const getOption = ( const fetchStatisticMinerAddresses = async () => { const { attributes: { minerAddressDistribution }, - } = await fetchStatisticMinerAddressDistribution() + } = await explorerService.api.fetchStatisticMinerAddressDistribution() const blockSum = Object.values(minerAddressDistribution).reduce((sum, val) => sum + Number(val), 0) const statisticMinerAddresses: State.StatisticMinerAddress[] = Object.entries(minerAddressDistribution).map( ([key, val]) => ({ diff --git a/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx b/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx index 3f8960501..0ff226310 100644 --- a/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerVersionDistribution.tsx @@ -3,7 +3,7 @@ import { EChartOption } from 'echarts' import i18n, { currentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticMinerVersionDistribution } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const Colors = [ '#069ECD', @@ -25,7 +25,7 @@ interface VersionRecord { const getOption = ( list: Array, - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -100,7 +100,7 @@ const getOption = ( } const fetchData = async () => { - const { data: list } = await fetchStatisticMinerVersionDistribution() + const { data: list } = await explorerService.api.fetchStatisticMinerVersionDistribution() const totalBlocks = list.reduce((acc, cur) => acc + cur.blocksCount, 0) return list.map(v => ({ version: v.version, diff --git a/src/pages/StatisticsChart/mining/UncleRate.tsx b/src/pages/StatisticsChart/mining/UncleRate.tsx index f238a3e94..de8589e86 100644 --- a/src/pages/StatisticsChart/mining/UncleRate.tsx +++ b/src/pages/StatisticsChart/mining/UncleRate.tsx @@ -3,7 +3,7 @@ import i18n, { currentLanguage } from '../../../utils/i18n' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' -import { fetchStatisticUncleRate } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' const max = (statisticUncleRates: State.StatisticUncleRate[]) => { @@ -13,7 +13,7 @@ const max = (statisticUncleRates: State.StatisticUncleRate[]) => { const getOption = ( statisticUncleRates: State.StatisticUncleRate[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -117,7 +117,7 @@ export const UncleRateChart = ({ isThumbnail = false }: { isThumbnail?: boolean title={t('block.uncle_rate')} description={t('statistic.uncle_rate_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticUncleRate} + fetchData={explorerService.api.fetchStatisticUncleRate} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.UncleRate} diff --git a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx index d83a87f36..561a136f8 100644 --- a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx +++ b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx @@ -3,11 +3,11 @@ import i18n, { currentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticAnnualPercentageCompensation } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticAnnualPercentageCompensations: State.StatisticAnnualPercentageCompensation[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -93,7 +93,7 @@ const getOption = ( const fetchStatisticAnnualPercentageCompensations = async () => { const { attributes: { nominalApc }, - } = await fetchStatisticAnnualPercentageCompensation() + } = await explorerService.api.fetchStatisticAnnualPercentageCompensation() const statisticAnnualPercentageCompensations = nominalApc .filter((_apc, index) => index % 3 === 0 || index === nominalApc.length - 1) .map((apc, index) => ({ diff --git a/src/pages/StatisticsChart/monetary/InflationRate.tsx b/src/pages/StatisticsChart/monetary/InflationRate.tsx index 1c1859104..b1fbf438f 100644 --- a/src/pages/StatisticsChart/monetary/InflationRate.tsx +++ b/src/pages/StatisticsChart/monetary/InflationRate.tsx @@ -3,11 +3,11 @@ import i18n, { currentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticInflationRate } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticInflationRates: State.StatisticInflationRate[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -142,7 +142,7 @@ const getOption = ( const fetchStatisticInflationRates = async () => { const { attributes: { nominalApc, nominalInflationRate, realInflationRate }, - } = await fetchStatisticInflationRate() + } = await explorerService.api.fetchStatisticInflationRate() const statisticInflationRates = [] for (let i = 0; i < nominalApc.length; i++) { if (i % 6 === 0 || i === nominalApc.length - 1) { diff --git a/src/pages/StatisticsChart/monetary/Liquidity.tsx b/src/pages/StatisticsChart/monetary/Liquidity.tsx index fe03c4d7f..15c8b55d7 100644 --- a/src/pages/StatisticsChart/monetary/Liquidity.tsx +++ b/src/pages/StatisticsChart/monetary/Liquidity.tsx @@ -5,11 +5,11 @@ import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../commo import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticLiquidity } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticLiquidity: State.StatisticLiquidity[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -183,7 +183,7 @@ export const LiquidityChart = ({ isThumbnail = false }: { isThumbnail?: boolean tooltipWidth(value, currentLanguage() === 'en' ? 155 : 70) @@ -23,7 +23,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: [string, const getOption = ( statisticSecondaryIssuance: State.StatisticSecondaryIssuance[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -168,7 +168,7 @@ export const SecondaryIssuanceChart = ({ isThumbnail = false }: { isThumbnail?: title={t('nervos_dao.secondary_issuance')} description={t('statistic.secondary_issuance_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticSecondaryIssuance} + fetchData={explorerService.api.fetchStatisticSecondaryIssuance} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.SecondaryIssuance} diff --git a/src/pages/StatisticsChart/monetary/TotalSupply.tsx b/src/pages/StatisticsChart/monetary/TotalSupply.tsx index 5e3d3461d..6b187d07c 100644 --- a/src/pages/StatisticsChart/monetary/TotalSupply.tsx +++ b/src/pages/StatisticsChart/monetary/TotalSupply.tsx @@ -6,7 +6,7 @@ import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticTotalSupply } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const widthSpan = (value: string) => tooltipWidth(value, currentLanguage() === 'en' ? 125 : 80) @@ -28,7 +28,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: [string, const getOption = ( statisticTotalSupplies: State.StatisticTotalSupply[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -179,7 +179,7 @@ export const TotalSupplyChart = ({ isThumbnail = false }: { isThumbnail?: boolea title={t('statistic.total_supply')} description={t('statistic.total_supply_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticTotalSupply} + fetchData={explorerService.api.fetchStatisticTotalSupply} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.TotalSupply} diff --git a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx index c4083051b..635918e4c 100644 --- a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx +++ b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx @@ -4,11 +4,11 @@ import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { DATA_ZOOM_CONFIG } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticCirculationRatio } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const getOption = ( statisticCirculationRatios: State.StatisticCirculationRatio[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -105,7 +105,7 @@ export const CirculationRatioChart = ({ isThumbnail = false }: { isThumbnail?: b title={t('statistic.circulation_ratio')} description={t('statistic.deposit_to_circulation_ratio_description')} isThumbnail={isThumbnail} - fetchData={fetchStatisticCirculationRatio} + fetchData={explorerService.api.fetchStatisticCirculationRatio} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.DepositCirculationRatio} diff --git a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx index cea097a73..1f1fce355 100644 --- a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx @@ -7,7 +7,7 @@ import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' import { tooltipWidth, tooltipColor, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticNewDaoDeposit } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const widthSpan = (value: string) => tooltipWidth(value, currentLanguage() === 'en' ? 140 : 120) @@ -30,7 +30,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: [string, const getOption = ( statisticNewDaoDeposits: State.StatisticNewDaoDeposit[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -178,7 +178,7 @@ export const NewDaoDepositChart = ({ isThumbnail = false }: { isThumbnail?: bool title={t('statistic.new_dao_deposit_depositor')} note={isMainnet() ? `${t('common.note')}1MB = 1,000,000 CKBytes` : undefined} isThumbnail={isThumbnail} - fetchData={fetchStatisticNewDaoDeposit} + fetchData={explorerService.api.fetchStatisticNewDaoDeposit} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.DailyDeposit} diff --git a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx index ee08cf6db..bcea6e440 100644 --- a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx @@ -7,7 +7,7 @@ import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' -import { fetchStatisticTotalDaoDeposit } from '../../../service/http/fetcher' +import { explorerService } from '../../../services/ExplorerService' const widthSpan = (value: string) => tooltipWidth(value, currentLanguage() === 'en' ? 168 : 110) @@ -30,7 +30,7 @@ const parseTooltip = ({ seriesName, data, color }: SeriesItem & { data: [string, const getOption = ( statisticTotalDaoDeposits: State.StatisticTotalDaoDeposit[], - chartColor: State.App['chartColor'], + chartColor: State.ChartColor, isMobile: boolean, isThumbnail = false, ): echarts.EChartOption => { @@ -176,7 +176,7 @@ export const TotalDaoDepositChart = ({ isThumbnail = false }: { isThumbnail?: bo description={t('statistic.total_dao_deposit_description')} note={isMainnet() ? `${t('common.note')}1GB = 1,000,000,000 CKBytes` : undefined} isThumbnail={isThumbnail} - fetchData={fetchStatisticTotalDaoDeposit} + fetchData={explorerService.api.fetchStatisticTotalDaoDeposit} getEChartOption={getOption} toCSV={toCSV} cacheKey={ChartCachedKeys.TotalDeposit} diff --git a/src/pages/Tokens/index.tsx b/src/pages/Tokens/index.tsx index a56d0c6b2..5418ab54c 100644 --- a/src/pages/Tokens/index.tsx +++ b/src/pages/Tokens/index.tsx @@ -24,7 +24,7 @@ import { udtSubmitEmail } from '../../utils/util' import SmallLoading from '../../components/Loading/SmallLoading' import styles from './styles.module.scss' import { useIsMobile, usePaginationParamsInPage } from '../../utils/hook' -import { fetchTokens } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' const TokenItem = ({ token, isLast }: { token: State.UDT; isLast?: boolean }) => { @@ -102,7 +102,7 @@ export default () => { const sort = new URLSearchParams(location.search).get('sort') const query = useQuery(['tokens', currentPage, _pageSize, sort], async () => { - const { data, meta } = await fetchTokens(currentPage, _pageSize, sort ?? undefined) + const { data, meta } = await explorerService.api.fetchTokens(currentPage, _pageSize, sort ?? undefined) if (data == null || data.length === 0) { throw new Error('Tokens empty') } diff --git a/src/pages/Tokens/styled.tsx b/src/pages/Tokens/styled.tsx index f3676a6d1..f1e3fc732 100644 --- a/src/pages/Tokens/styled.tsx +++ b/src/pages/Tokens/styled.tsx @@ -66,19 +66,19 @@ export const TokensTableTitle = styled.div` } @media (max-width: 1000px) { - >span: nth-child(1) { + > span:nth-child(1) { flex: 3.4; } - >span: nth-child(2) { + > span:nth-child(2) { flex: 2.5; } - >span: nth-child(3) { + > span:nth-child(3) { flex: 2.1; } - >span: nth-child(4) { + > span:nth-child(4) { flex: 2; } } @@ -88,11 +88,12 @@ export const TokensTableTitle = styled.div` height: fit-content; padding: 5px 20px; - > span { + /* This selector is just to increase the specificity. */ + > span:nth-child(n) { display: inline-block; white-space: nowrap; margin: 10px 0; - flex: 0 !important; + flex: 0; } } ` @@ -197,7 +198,7 @@ export const TokensTableItem = styled.div` flex: 1.8; font-size: 12px; text-align: right; - color: #000000; + color: #000; @media (max-width: 1000px) { flex: 2; diff --git a/src/pages/Transaction/TransactionCell/styles.module.scss b/src/pages/Transaction/TransactionCell/styles.module.scss index 071083558..79114f071 100644 --- a/src/pages/Transaction/TransactionCell/styles.module.scss +++ b/src/pages/Transaction/TransactionCell/styles.module.scss @@ -3,6 +3,7 @@ height: 15px; margin-left: 2px; cursor: pointer; + &:hover { path { fill: var(--primary-color); diff --git a/src/pages/Transaction/TransactionCellList/styles.module.scss b/src/pages/Transaction/TransactionCellList/styles.module.scss index cc986ed73..1e54a5c6d 100644 --- a/src/pages/Transaction/TransactionCellList/styles.module.scss +++ b/src/pages/Transaction/TransactionCellList/styles.module.scss @@ -9,11 +9,13 @@ padding: 0 6px; opacity: 0.5; cursor: pointer; + svg { pointer-events: none; width: 24px; height: 22.5px; } + &:hover { opacity: 1; } diff --git a/src/pages/Transaction/TransactionCellScript/index.tsx b/src/pages/Transaction/TransactionCellScript/index.tsx index 117a57ea0..0a33bfba6 100644 --- a/src/pages/Transaction/TransactionCellScript/index.tsx +++ b/src/pages/Transaction/TransactionCellScript/index.tsx @@ -1,7 +1,7 @@ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ import { useEffect, useState, ReactNode, useRef } from 'react' import BigNumber from 'bignumber.js' -import { fetchCellData, fetchScript } from '../../../service/http/fetcher' +import { explorerService, Response } from '../../../services/ExplorerService' import { CellState } from '../../../constants/common' import { hexToUtf8 } from '../../../utils/string' import { @@ -17,10 +17,7 @@ import { TransactionDetailScriptButton, } from './styled' import i18n from '../../../utils/i18n' -import { AppDispatch } from '../../../contexts/reducer' -import { AppActions } from '../../../contexts/actions' import SmallLoading from '../../../components/Loading/SmallLoading' -import { useDispatch } from '../../../contexts/providers' import CloseIcon from '../../../assets/modal_close.png' import { getContractHashTag } from '../../../utils/util' import { localeNumberString } from '../../../utils/number' @@ -28,6 +25,7 @@ import HashTag from '../../../components/HashTag' import { ReactComponent as CopyIcon } from '../../../assets/copy_icon.svg' import { ReactComponent as OuterLinkIcon } from '../../../assets/outer_link_icon.svg' import { HelpTip } from '../../../components/HelpTip' +import { useSetToast } from '../../../components/Toast' const initScriptContent = { lock: 'null', @@ -60,13 +58,16 @@ const handleFetchCellInfo = async ( state: CellState, setScriptFetchStatus: (val: boolean) => void, setContent: Function, - dispatch: AppDispatch, + setToast: ReturnType, ) => { setScriptFetchStatus(false) const fetchLock = async () => { if (cell.id) { - const wrapper: Response.Wrapper | null = await fetchScript('lock_scripts', `${cell.id}`) + const wrapper: Response.Wrapper | null = await explorerService.api.fetchScript( + 'lock_scripts', + `${cell.id}`, + ) return wrapper ? wrapper.attributes : initScriptContent.lock } return initScriptContent.lock @@ -74,7 +75,10 @@ const handleFetchCellInfo = async ( const fetchType = async () => { if (cell.id) { - const wrapper: Response.Wrapper | null = await fetchScript('type_scripts', `${cell.id}`) + const wrapper: Response.Wrapper | null = await explorerService.api.fetchScript( + 'type_scripts', + `${cell.id}`, + ) return wrapper ? wrapper.attributes : initScriptContent.type } return initScriptContent.type @@ -82,7 +86,8 @@ const handleFetchCellInfo = async ( const fetchData = async () => { if (cell.id) { - return fetchCellData(`${cell.id}`) + return explorerService.api + .fetchCellData(`${cell.id}`) .then((wrapper: Response.Wrapper | null) => { const dataValue: State.Data = wrapper ? wrapper.attributes : initScriptContent.data if (wrapper && cell.isGenesisOutput) { @@ -94,12 +99,9 @@ const handleFetchCellInfo = async ( if (error.response && error.response.data && error.response.data[0]) { const err = error.response.data[0] if (err.status === 400 && err.code === 1022) { - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: i18n.t('toast.data_too_large'), - type: 'warning', - }, + setToast({ + message: i18n.t('toast.data_too_large'), + type: 'warning', }) return null } @@ -244,7 +246,7 @@ const ScriptContentJson = ({ ) export default ({ cell, onClose }: { cell: State.Cell; onClose: Function }) => { - const dispatch = useDispatch() + const setToast = useSetToast() const [scriptFetched, setScriptFetched] = useState(false) const [content, setContent] = useState(null as State.Script | State.Data | CapacityUsage | null) const [state, setState] = useState(CellState.LOCK as CellState) @@ -255,18 +257,13 @@ export default ({ cell, onClose }: { cell: State.Cell; onClose: Function }) => { } useEffect(() => { - handleFetchCellInfo(cell, state, setScriptFetched, setContent, dispatch) - }, [cell, state, dispatch]) + handleFetchCellInfo(cell, state, setScriptFetched, setContent, setToast) + }, [cell, state, setToast]) const onClickCopy = () => { navigator.clipboard.writeText(updateJsonFormat(content)).then( () => { - dispatch({ - type: AppActions.ShowToastMessage, - payload: { - message: i18n.t('common.copied'), - }, - }) + setToast({ message: i18n.t('common.copied') }) }, error => { console.error(error) diff --git a/src/pages/Transaction/TransactionCellScript/styled.tsx b/src/pages/Transaction/TransactionCellScript/styled.tsx index 9602b1b57..4fc1708cf 100644 --- a/src/pages/Transaction/TransactionCellScript/styled.tsx +++ b/src/pages/Transaction/TransactionCellScript/styled.tsx @@ -22,6 +22,7 @@ export const TransactionDetailItem = styled.div` @media (max-width: 750px) { margin-top: 5px; } + &::after { position: absolute; left: 2px; diff --git a/src/pages/Transaction/TransactionComp.tsx b/src/pages/Transaction/TransactionComp.tsx index 5d850dea9..91bbf97c0 100644 --- a/src/pages/Transaction/TransactionComp.tsx +++ b/src/pages/Transaction/TransactionComp.tsx @@ -4,7 +4,6 @@ import { Link } from 'react-router-dom' import BigNumber from 'bignumber.js' import { Trans } from 'react-i18next' import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' -import { useAppState } from '../../contexts/providers/index' import { parseSimpleDate } from '../../utils/date' import i18n from '../../utils/i18n' import { localeNumberString } from '../../utils/number' @@ -28,6 +27,7 @@ import HashTag from '../../components/HashTag' import { useAddrFormatToggle } from '../../utils/hook' import ComparedToMaxTooltip from '../../components/Tooltip/ComparedToMaxTooltip' import { HelpTip } from '../../components/HelpTip' +import { useLatestBlockNumber } from '../../services/ExplorerService' const showTxStatus = (txStatus: string) => txStatus?.replace(/^\S/, s => s.toUpperCase()) ?? '-' @@ -109,9 +109,7 @@ const TransactionInfoItemWrapper = ({ export const TransactionOverview: FC<{ transaction: State.Transaction }> = ({ transaction }) => { const [showParams, setShowParams] = useState(false) - const { - app: { tipBlockNumber }, - } = useAppState() + const tipBlockNumber = useLatestBlockNumber() const { blockNumber, cellDeps, diff --git a/src/pages/Transaction/index.tsx b/src/pages/Transaction/index.tsx index d32b4dff7..1f4046f18 100644 --- a/src/pages/Transaction/index.tsx +++ b/src/pages/Transaction/index.tsx @@ -1,23 +1,19 @@ -import { useEffect } from 'react' import { useParams } from 'react-router-dom' import { useQuery } from 'react-query' import TransactionHashCard from '../../components/Card/HashCard' import Content from '../../components/Content' -import { getTipBlockNumber } from '../../service/app/address' import i18n from '../../utils/i18n' import { TransactionDiv as TransactionPanel } from './styled' import TransactionComp, { TransactionOverview } from './TransactionComp' -import { useDispatch } from '../../contexts/providers' -import { fetchTransactionByHash } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' import { defaultTransactionInfo } from './state' export default () => { - const dispatch = useDispatch() const { hash: txHash } = useParams<{ hash: string }>() const query = useQuery(['transaction', txHash], async () => { - const wrapper = await fetchTransactionByHash(txHash) + const wrapper = await explorerService.api.fetchTransactionByHash(txHash) const transaction = wrapper.attributes if (transaction.displayOutputs && transaction.displayOutputs.length > 0) { transaction.displayOutputs[0].isGenesisOutput = transaction.blockNumber === 0 @@ -27,8 +23,6 @@ export default () => { const transaction = query.data ?? defaultTransactionInfo const { blockTimestamp, txStatus } = transaction - useEffect(() => getTipBlockNumber(dispatch), [dispatch]) - return ( diff --git a/src/pages/TransactionList/index.module.scss b/src/pages/TransactionList/index.module.scss index e143ae6ff..7abba6184 100644 --- a/src/pages/TransactionList/index.module.scss +++ b/src/pages/TransactionList/index.module.scss @@ -1,7 +1,7 @@ .tabs { - margin: 24px 0 40px 0; + margin: 24px 0 40px; - @media screen and (max-width: 750px) { + @media screen and (width <= 750px) { margin: 20px 0; } @@ -13,7 +13,7 @@ overflow: auto; background: #fff; border-radius: 4px 4px 0 0; - box-shadow: 0px 4px 4px rgba(16, 16, 16, 0.05); + box-shadow: 0 4px 4px rgb(16 16 16 / 5%); .tab { position: relative; @@ -99,7 +99,7 @@ margin-top: 4px; padding: 0 40px; background: #fff; - box-shadow: 0px 4px 4px rgba(16, 16, 16, 0.05); + box-shadow: 0 4px 4px rgb(16 16 16 / 5%); .colHash { font-size: 14px; @@ -113,11 +113,13 @@ display: flex; margin-left: 8px; cursor: pointer; + &[data-order='asc'] { svg > path:last-child { fill: var(--primary-color); } } + &[data-order='desc'] { svg > path:first-child { fill: var(--primary-color); diff --git a/src/pages/TransactionList/index.tsx b/src/pages/TransactionList/index.tsx index b96e0ae2e..f3854cf9a 100644 --- a/src/pages/TransactionList/index.tsx +++ b/src/pages/TransactionList/index.tsx @@ -12,7 +12,7 @@ import DecimalCapacity from '../../components/DecimalCapacity' import { ItemCardData, ItemCardGroup } from '../../components/Card/ItemCard' import AddressText from '../../components/AddressText' import { useIsMobile, usePaginationParamsInListPage, useSearchParams, useSortParam } from '../../utils/hook' -import { fetchPendingTransactions, fetchPendingTransactionsCount, fetchTransactions } from '../../service/http/fetcher' +import { explorerService } from '../../services/ExplorerService' import { Tabs } from './Tabs' import styles from './index.module.scss' import { QueryResult } from '../../components/QueryResult' @@ -241,7 +241,7 @@ const TransactionsPanel: FC<{ type: TxStatus }> = ({ type }) => { const [, type] = queryKey switch (type) { case 'pending': { - const resp = await fetchPendingTransactions(currentPage, pageSize, sort) + const resp = await explorerService.api.fetchPendingTransactions(currentPage, pageSize, sort) return { transactions: resp.data, total: resp.meta?.total ?? 0, @@ -249,7 +249,7 @@ const TransactionsPanel: FC<{ type: TxStatus }> = ({ type }) => { } case 'confirmed': default: { - const resp = await fetchTransactions(currentPage, pageSize, sort) + const resp = await explorerService.api.fetchTransactions(currentPage, pageSize, sort) return { transactions: resp.data.map(wrapper => wrapper.attributes) ?? [], total: resp.meta?.total ?? 0, @@ -309,7 +309,7 @@ const TransactionsPanel: FC<{ type: TxStatus }> = ({ type }) => { export default () => { const { tab } = useSearchParams('tab') - const { data } = useQuery(['transactions-count'], fetchPendingTransactionsCount) + const { data } = useQuery(['transactions-count'], explorerService.api.fetchPendingTransactionsCount) return ( diff --git a/src/routes/index.tsx b/src/routes/index.tsx index e81b25a9b..933f9f947 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,15 +1,9 @@ -import { useEffect, useRef, Suspense, lazy, Component } from 'react' -import { BrowserRouter as Router, Route, Redirect, Switch, useLocation } from 'react-router-dom' -import { createBrowserHistory } from 'history' +import { Suspense, lazy, Component } from 'react' +import { BrowserRouter as Router, Route, Redirect, Switch, RouteProps } from 'react-router-dom' import Page from '../components/Page' import Header from '../components/Header' import Footer from '../components/Footer' -import Sheet from '../components/Sheet' -import { useDispatch, useAppState } from '../contexts/providers' -import { ComponentActions } from '../contexts/actions' -import { useIsMobile } from '../utils/hook' import { isChainTypeError } from '../utils/chain' -import Alert from '../components/Alert' const Home = lazy(() => import('../pages/Home')) const Block = lazy(() => import('../pages/BlockDetail')) @@ -58,316 +52,191 @@ const ScriptList = lazy(() => import('../pages/ScriptList')) const FeeRateTracker = lazy(() => import('../pages/FeeRateTracker')) const ExportTransactions = lazy(() => import('../pages/ExportTransactions')) -const Containers: CustomRouter.Route[] = [ +const routes: RouteProps[] = [ { - name: 'Home', path: '/', - exact: true, - comp: Home, + component: Home, }, { - name: 'BlockList', path: '/block/list', - exact: true, - comp: BlockList, + component: BlockList, }, { - name: 'Address', path: '/address/:address', - exact: true, - comp: Address, + render: routeProps => { + const { pathname } = routeProps.location + if (isChainTypeError(pathname.substring(pathname.lastIndexOf('/') + 1))) { + return + } + return
+ }, }, { - name: 'Script', path: '/script/:codeHash/:hashType/:tab?', - exact: true, - comp: ScriptPage, + component: ScriptPage, }, { - name: 'Block', path: '/block/:param', - exact: true, - comp: Block, + component: Block, }, { - name: 'TransactionList', path: '/transaction/list', - exact: true, - comp: TransactionList, + component: TransactionList, }, { - name: 'Transaction', path: '/transaction/:hash', - exact: true, - comp: Transaction, + component: Transaction, }, { - name: 'SimpleUDT', path: '/sudt/:hash', - exact: true, - comp: SimpleUDT, + component: SimpleUDT, }, { - name: 'NftCollections', path: '/nft-collections', - exact: true, - comp: NftCollections, + component: NftCollections, }, { - name: 'NftCollectionInfo', path: '/nft-collections/:id', - exact: true, - comp: NftCollectionInfo, + component: NftCollectionInfo, }, { - name: 'NftInfo', path: '/nft-info/:collection/:id', - exact: true, - comp: NftInfo, + component: NftInfo, }, { - name: 'NervosDao', path: '/nervosdao', - exact: true, - comp: NervosDao, + component: NervosDao, }, { - name: 'Tokens', path: '/tokens', - exact: true, - comp: Tokens, + component: Tokens, }, { - name: 'Charts', path: '/charts', - exact: true, - comp: StatisticsChart, + component: StatisticsChart, }, { - name: 'DifficultyHashRateChart', path: '/charts/difficulty-hash-rate', - exact: true, - comp: DifficultyHashRateChart, + component: DifficultyHashRateChart, }, { - name: 'DifficultyUncleRateEpochChart', path: '/charts/epoch-time-length', - exact: true, - comp: DifficultyUncleRateEpochChart, + component: DifficultyUncleRateEpochChart, }, { - name: 'DifficultyChart', path: '/charts/difficulty', - exact: true, - comp: DifficultyChart, + component: DifficultyChart, }, { - name: 'HashRateChart', path: '/charts/hash-rate', - exact: true, - comp: HashRateChart, + component: HashRateChart, }, { - name: 'UncleRateChart', path: '/charts/uncle-rate', - exact: true, - comp: UncleRateChart, + component: UncleRateChart, }, { - name: 'MinerAddressDistributionChart', path: '/charts/miner-address-distribution', - exact: true, - comp: MinerAddressDistributionChart, + component: MinerAddressDistributionChart, }, { - name: 'MinerVersionDistributionChart', path: '/charts/miner-version-distribution', - exact: true, - comp: MinerVersionDistributionChart, + component: MinerVersionDistributionChart, }, { - name: 'TransactionCountChart', path: '/charts/transaction-count', - exact: true, - comp: TransactionCountChart, + component: TransactionCountChart, }, { - name: 'AddressCountChart', path: '/charts/address-count', - exact: true, - comp: AddressCountChart, + component: AddressCountChart, }, { - name: 'TotalDaoDepositChart', path: '/charts/total-dao-deposit', - exact: true, - comp: TotalDaoDepositChart, + component: TotalDaoDepositChart, }, { - name: 'NewDaoDepositChart', path: '/charts/new-dao-deposit', - exact: true, - comp: NewDaoDepositChart, + component: NewDaoDepositChart, }, { - name: 'CirculationRatioChart', path: '/charts/circulation-ratio', - exact: true, - comp: CirculationRatioChart, + component: CirculationRatioChart, }, { - name: 'CellCountChart', path: '/charts/cell-count', - exact: true, - comp: CellCountChart, + component: CellCountChart, }, { - name: 'AddressBalanceRankChart', path: '/charts/address-balance-rank', - exact: true, - comp: AddressBalanceRankChart, + component: AddressBalanceRankChart, }, { - name: 'BalanceDistributionChart', path: '/charts/balance-distribution', - exact: true, - comp: BalanceDistributionChart, + component: BalanceDistributionChart, }, { - name: 'TxFeeHistoryChart', path: '/charts/tx-fee-history', - exact: true, - comp: TxFeeHistoryChart, + component: TxFeeHistoryChart, }, { - name: 'BlockTimeDistributionChart', path: '/charts/block-time-distribution', - exact: true, - comp: BlockTimeDistributionChart, + component: BlockTimeDistributionChart, }, { - name: 'AverageBlockTimeChart', path: '/charts/average-block-time', - exact: true, - comp: AverageBlockTimeChart, + component: AverageBlockTimeChart, }, { - name: 'EpochTimeDistributionChart', path: '/charts/epoch-time-distribution', - exact: true, - comp: EpochTimeDistributionChart, + component: EpochTimeDistributionChart, }, { - name: 'TotalSupplyChart', path: '/charts/total-supply', - exact: true, - comp: TotalSupplyChart, + component: TotalSupplyChart, }, { - name: 'AnnualPercentageCompensationChart', path: '/charts/nominal-apc', - exact: true, - comp: AnnualPercentageCompensationChart, + component: AnnualPercentageCompensationChart, }, { - name: 'SecondaryIssuanceChart', path: '/charts/secondary-issuance', - exact: true, - comp: SecondaryIssuanceChart, + component: SecondaryIssuanceChart, }, { - name: 'InflationRateChart', path: '/charts/inflation-rate', - exact: true, - comp: InflationRateChart, + component: InflationRateChart, }, { - name: 'LiquidityChart', path: '/charts/liquidity', - exact: true, - comp: LiquidityChart, + component: LiquidityChart, }, { - name: 'SearchFail', path: '/search/fail', - exact: true, - comp: SearchFail, + component: SearchFail, }, { - name: 'ScriptList', path: '/scripts', - exact: true, - comp: ScriptList, + component: ScriptList, }, { - name: 'FeeRateTracker', path: '/fee-rate-tracker', - exact: true, - comp: FeeRateTracker, + component: FeeRateTracker, }, { - name: '404', path: '/404', - exact: true, - comp: NotFoundPage, + component: NotFoundPage, }, { - name: 'Error', path: '/error', - exact: true, - comp: ErrorPage, + component: ErrorPage, }, { - name: 'ExportTransactions', path: '/export-transactions', - exact: true, - comp: ExportTransactions, + component: ExportTransactions, }, ] -const useRouter = (callback: Function) => { - const history = createBrowserHistory() - useEffect(() => { - let currentUrl = `${history.location.pathname}${history.location.search}` - const listen = history.listen((location: any) => { - if (currentUrl !== `${location.pathname}${location.search}`) { - callback() - } - currentUrl = `${location.pathname}${location.search}` - }) - return () => { - listen() - } - }, [callback, history]) -} - -const useRouterLocation = (callback: () => void) => { - const history = createBrowserHistory() - const savedCallback = useRef(() => {}) - useEffect(() => { - savedCallback.current = callback - }) - useEffect(() => { - const currentCallback = () => { - savedCallback.current() - } - const listen = history.listen(() => { - currentCallback() - }) - return () => { - listen() - } - }, [history]) -} - -const RouterComp = ({ container, routeProps }: { container: CustomRouter.Route; routeProps: any }) => { - const { pathname = '' } = useLocation() - if (container.name === 'Address' && isChainTypeError(pathname.substring(pathname.lastIndexOf('/') + 1))) { - return - } - return -} - type PageErrorBoundaryState = { error?: Error | null info: { @@ -417,52 +286,24 @@ class PageErrorBoundary extends Component { - const isMobile = useIsMobile() - const dispatch = useDispatch() - const { components } = useAppState() - const { mobileMenuVisible } = components - - useRouter(() => { - window.scrollTo(0, 0) - }) - - useRouterLocation(() => { - if (mobileMenuVisible) { - dispatch({ - type: ComponentActions.UpdateHeaderMobileMenuVisible, - payload: { - mobileMenuVisible: false, - }, - }) - } - }) - return ( - ( - - -
- - }> - - - {Containers.map(container => ( - } - /> - ))} - - - - {!(isMobile && mobileMenuVisible) &&