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 && }
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 (