From cc3a959cdf3b12a2665b6c0440f9738f4f4e2989 Mon Sep 17 00:00:00 2001 From: homura Date: Tue, 24 Oct 2023 01:31:47 +0800 Subject: [PATCH 1/3] fix: cannot call hooks in useMemo (#1357) --- src/pages/StatisticsChart/common/index.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/StatisticsChart/common/index.tsx b/src/pages/StatisticsChart/common/index.tsx index 9dbe7139e..8fd7955cc 100644 --- a/src/pages/StatisticsChart/common/index.tsx +++ b/src/pages/StatisticsChart/common/index.tsx @@ -197,10 +197,7 @@ export function SmartChartPage({ } }, [onFetched, query.data]) - const option = useMemo( - () => getEChartOption(dataList, ChartColor, isMobile, isThumbnail), - [dataList, getEChartOption, isMobile, isThumbnail], - ) + const option = getEChartOption(dataList, ChartColor, isMobile, isThumbnail) const content = query.isLoading ? ( From c1ed55a8ec2d811af9a3d42100b5a0d8a55d603a Mon Sep 17 00:00:00 2001 From: Shinya Date: Tue, 24 Oct 2023 22:37:54 +0800 Subject: [PATCH 2/3] refactor: remove any (#122) * refactor: remove anys * refactor: remove anys in styled components * refactor: reomve anys * chore: fix hook order * chore: update by comments * chore: change to comments * chore: udpate to comments * refactor: import types directly from React * chore: update footer icon type * chore: pass react node to footer image * chore: update types * chore: udpate types --- src/components/Card/OverviewCard/index.tsx | 8 +-- src/components/Card/TitleCard/index.tsx | 5 +- src/components/Content/index.tsx | 4 +- src/components/Footer/index.tsx | 35 ++++++------- .../Header/BlockchainComp/styled.tsx | 3 +- src/components/Header/LanguageComp/styled.tsx | 6 ++- src/components/Header/MenusComp/index.tsx | 49 ++++++++++--------- src/components/Page/index.tsx | 3 +- src/components/Pagination/index.tsx | 6 +-- src/components/Search/index.tsx | 28 ++++++----- src/components/SimpleButton/index.tsx | 10 ++-- src/components/Table/index.tsx | 10 +++- src/components/Table/styled.tsx | 2 +- src/components/Text/CopyTooltipText/index.tsx | 2 +- src/components/Toast/index.tsx | 16 +++++- .../TransactionIncome/styled.tsx | 2 +- .../TransactionItemCell/styled.tsx | 10 ++-- src/pages/BlockList/index.tsx | 2 +- src/pages/NervosDao/DaoOverview/index.tsx | 17 +++---- src/pages/NervosDao/DaoOverview/styled.tsx | 2 +- src/pages/Script/ScriptsComp.tsx | 38 +++++++------- .../activities/AddressBalanceRank.tsx | 14 +++--- .../activities/AddressCount.tsx | 5 +- .../activities/BalanceDistribution.tsx | 23 ++++++--- .../StatisticsChart/activities/CellCount.tsx | 18 +++++-- .../activities/TransactionCount.tsx | 5 +- .../activities/TxFeeHistory.tsx | 5 +- .../block/AverageBlockTime.tsx | 12 +++-- .../block/BlockTimeDistribution.tsx | 5 +- .../block/EpochTimeDistribution.tsx | 7 +-- src/pages/StatisticsChart/common/index.tsx | 19 +++---- .../StatisticsChart/mining/Difficulty.tsx | 5 +- .../mining/DifficultyHashRate.tsx | 21 +++++--- .../mining/DifficultyUncleRateEpoch.tsx | 12 +++-- src/pages/StatisticsChart/mining/HashRate.tsx | 5 +- .../mining/MinerAddressDistribution.tsx | 10 ++-- .../StatisticsChart/mining/UncleRate.tsx | 7 +-- .../monetary/AnnualPercentageCompensation.tsx | 5 +- .../monetary/InflationRate.tsx | 12 +++-- .../StatisticsChart/monetary/Liquidity.tsx | 18 +++++-- .../monetary/SecondaryIssuance.tsx | 15 ++++-- .../StatisticsChart/monetary/TotalSupply.tsx | 18 +++++-- .../nervosDao/CirculationRatio.tsx | 5 +- .../nervosDao/NewDaoDeposit.tsx | 18 +++++-- .../nervosDao/TotalDaoDeposit.tsx | 18 +++++-- .../Transaction/TransactionCell/styled.tsx | 2 +- .../Transaction/TransactionCellList/index.tsx | 4 +- .../TransactionCellScript/index.tsx | 10 +++- .../TransactionCellScript/styled.tsx | 4 +- src/types/echarts.d.ts | 31 ++++++++++++ src/types/index.d.ts | 2 + src/utils/chart.ts | 49 +++++++++++++++++++ src/utils/transformer.ts | 41 ++++++++++++++++ src/utils/util.ts | 4 +- 54 files changed, 466 insertions(+), 221 deletions(-) create mode 100644 src/types/echarts.d.ts create mode 100644 src/utils/transformer.ts diff --git a/src/components/Card/OverviewCard/index.tsx b/src/components/Card/OverviewCard/index.tsx index e68d470d3..7022b48de 100644 --- a/src/components/Card/OverviewCard/index.tsx +++ b/src/components/Card/OverviewCard/index.tsx @@ -10,20 +10,20 @@ export type OverviewItemData = { content: ReactNode contentWrapperClass?: string filled?: boolean - icon?: any + icon?: string isAsset?: boolean tooltip?: TooltipProps['title'] valueTooltip?: string } | null const handleOverviewItems = (items: OverviewItemData[], isMobile: boolean) => ({ - leftItems: isMobile ? items : items.filter((_item: any, index: number) => index % 2 === 0), - rightItems: isMobile ? [] : items.filter((_item: any, index: number) => index % 2 !== 0), + leftItems: isMobile ? items : items.filter((_item, index) => index % 2 === 0), + rightItems: isMobile ? [] : items.filter((_item, index) => index % 2 !== 0), }) export const OverviewItem = ({ item, hideLine }: { item: OverviewItemData; hideLine: boolean }) => item ? ( - +
{item.icon && (
diff --git a/src/components/Card/TitleCard/index.tsx b/src/components/Card/TitleCard/index.tsx index 5bc020e3e..926cb6a9e 100644 --- a/src/components/Card/TitleCard/index.tsx +++ b/src/components/Card/TitleCard/index.tsx @@ -1,4 +1,5 @@ import classNames from 'classnames' +import { ReactNode } from 'react' import { TitleCardPanel } from './styled' export default ({ @@ -8,10 +9,10 @@ export default ({ rear, rearClassName, }: { - title: React.ReactNode + title: ReactNode isSingle?: boolean className?: string - rear?: React.ReactNode + rear?: ReactNode rearClassName?: string }) => ( diff --git a/src/components/Content/index.tsx b/src/components/Content/index.tsx index 9846cd689..50653f07f 100644 --- a/src/components/Content/index.tsx +++ b/src/components/Content/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react' +import { CSSProperties, ReactNode } from 'react' import styled from 'styled-components' const ContentPanel = styled.div` @@ -7,6 +7,6 @@ const ContentPanel = styled.div` flex: 1; background: #ededed; ` -export default ({ children, style }: { children: ReactNode; style?: any }) => { +export default ({ children, style }: { children: ReactNode; style?: CSSProperties }) => { return {children} } diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 06053853b..d6944fdc2 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,4 +1,4 @@ -import { memo, useMemo } from 'react' +import { ReactNode, memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ReactComponent as XIcon } from '../../assets/footer_X.svg' import { ReactComponent as MediumIcon } from '../../assets/footer_medium.svg' @@ -15,7 +15,7 @@ import { udtSubmitEmail } from '../../utils/util' interface FooterLinkItem { label?: string url?: string - icon?: any + icon?: ReactNode } interface FooterLink { @@ -34,9 +34,10 @@ const FooterItem = ({ item }: { item: FooterLinkItem }) => { const FooterImageItem = ({ item }: { item: FooterLinkItem }) => { const { label, url, icon: IconComponent } = item + return ( - + {IconComponent} {label} ) @@ -45,7 +46,7 @@ const FooterImageItem = ({ item }: { item: FooterLinkItem }) => { export default memo(() => { const isMobile = useIsMobile() const [t] = useTranslation() - const Footers: FooterLink[] = useMemo( + const Footers = useMemo( () => [ { name: t('footer.nervos_foundation'), @@ -86,37 +87,37 @@ export default memo(() => { items: [ { label: t('footer.discord'), - icon: Discord, + icon: , url: 'https://discord.com/invite/FKh8Zzvwqa', }, { label: t('footer.X'), - icon: XIcon, + icon: , url: 'https://x.com/nervosnetwork', }, { label: t('footer.blog'), - icon: MediumIcon, + icon: , url: 'https://medium.com/nervosnetwork', }, { label: t('footer.telegram'), - icon: TelegramIcon, + icon: , url: 'https://t.me/nervosnetwork', }, { label: t('footer.reddit'), - icon: RedditIcon, + icon: , url: 'https://www.reddit.com/r/NervosNetwork/', }, { label: t('footer.youtube'), - icon: YoutubeIcon, + icon: , url: 'https://www.youtube.com/channel/UCONuJGdMzUY0Y6jrPBOzH7A', }, { label: t('footer.forum'), - icon: ForumIcon, + icon: , url: 'https://talk.nervos.org/', }, ], @@ -129,7 +130,7 @@ export default memo(() => {
{Footers[0].name}
- {Footers[0].items.map((item: any) => ( + {Footers[0].items.map(item => ( ))}
@@ -137,31 +138,31 @@ export default memo(() => {
{Footers[1].name}
{Footers[1].items .filter(item => item.label !== undefined) - .map((item: any) => ( + .map(item => ( ))}
{isMobile ? (
- {Footers[2].items.map((item: any) => ( + {Footers[2].items.map(item => ( ))}
) : ( <>
- {Footers[2].items.slice(0, 3).map((item: any) => ( + {Footers[2].items.slice(0, 3).map(item => ( ))}
- {Footers[2].items.slice(3, 6).map((item: any) => ( + {Footers[2].items.slice(3, 6).map(item => ( ))}
- {Footers[2].items.slice(6).map((item: any) => ( + {Footers[2].items.slice(6).map(item => ( ))}
diff --git a/src/components/Header/BlockchainComp/styled.tsx b/src/components/Header/BlockchainComp/styled.tsx index 70d42b1a1..c183acdf5 100644 --- a/src/components/Header/BlockchainComp/styled.tsx +++ b/src/components/Header/BlockchainComp/styled.tsx @@ -69,7 +69,8 @@ export const MobileSubMenuPanel = styled.div` } .mobileMenusMainItemContent { - color: ${(props: { showSubMenu: boolean; theme: any }) => (props.showSubMenu ? props.theme.primary : 'white')}; + color: ${(props: { showSubMenu: boolean; theme: State.Theme }) => + props.showSubMenu ? props.theme.primary : 'white'}; } .mobileMenusMainItemContentHighlight { diff --git a/src/components/Header/LanguageComp/styled.tsx b/src/components/Header/LanguageComp/styled.tsx index 36bf6495f..6ba02f502 100644 --- a/src/components/Header/LanguageComp/styled.tsx +++ b/src/components/Header/LanguageComp/styled.tsx @@ -4,7 +4,8 @@ export const HeaderLanguagePanel = styled.div` display: flex; align-items: center; height: 100%; - color: ${(props: { theme: any; showLanguage: boolean }) => (props.showLanguage ? props.theme.secondary : 'white')}; + color: ${(props: { theme: { primary: string; secondary: string }; showLanguage: boolean }) => + props.showLanguage ? props.theme.secondary : 'white'}; padding: 10px 0; margin-bottom: 4px; @@ -48,7 +49,8 @@ export const MobileSubMenuPanel = styled.div` } .mobileMenusMainItemContent { - color: ${(props: { showSubMenu: boolean; theme: any }) => (props.showSubMenu ? props.theme.primary : 'white')}; + color: ${(props: { showSubMenu: boolean; theme: State.Theme }) => + props.showSubMenu ? props.theme.primary : 'white'}; } .mobileMenusMainItemContentHighlight { diff --git a/src/components/Header/MenusComp/index.tsx b/src/components/Header/MenusComp/index.tsx index 40bee6c88..be4a4b6b2 100644 --- a/src/components/Header/MenusComp/index.tsx +++ b/src/components/Header/MenusComp/index.tsx @@ -10,9 +10,15 @@ export enum LinkType { Outer, } +interface MenuData { + type: LinkType + name: string + url: string +} + const useMenuDataList = () => { const { t } = useTranslation() - return [ + const list: MenuData[] = [ { type: LinkType.Inner, name: t('navbar.home'), @@ -43,17 +49,18 @@ const useMenuDataList = () => { name: t('navbar.fee_rate'), url: '/fee-rate-tracker', }, - !isMainnet() - ? { - type: LinkType.Outer, - name: t('navbar.faucet'), - url: 'https://faucet.nervos.org/', - } - : {}, ] + if (!isMainnet()) { + list.push({ + type: LinkType.Outer, + name: t('navbar.faucet'), + url: 'https://faucet.nervos.org/', + }) + } + return list } -const MenuItemLink = ({ menu }: { menu: any }) => { +const MenuItemLink = ({ menu }: { menu: MenuData }) => { const { url, type, name } = menu return ( @@ -74,19 +81,17 @@ export default memo(() => { ) : ( - {menuList - .filter(menu => menu.name !== undefined) - .map(menu => - menu.type === LinkType.Inner ? ( - - {menu.name} - - ) : ( - - {menu.name} - - ), - )} + {menuList.map(menu => + menu.type === LinkType.Inner ? ( + + {menu.name} + + ) : ( + + {menu.name} + + ), + )} ) }) diff --git a/src/components/Page/index.tsx b/src/components/Page/index.tsx index aa73f701f..5a100aa04 100644 --- a/src/components/Page/index.tsx +++ b/src/components/Page/index.tsx @@ -1,5 +1,6 @@ +import { CSSProperties, ReactNode } from 'react' import { PagePanel } from './styled' -export default ({ children, style }: { children: any; style?: object }) => ( +export default ({ children, style }: { children: ReactNode; style?: CSSProperties }) => ( {children} ) diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index cb6e5ae71..e828f5e55 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -82,11 +82,11 @@ const Pagination = ({ pattern="[0-9]*" className="paginationInputPage" value={inputPage} - onChange={(event: any) => { + onChange={event => { const pageNo = parseInt(event.target.value, 10) - setInputPage(Number.isNaN(pageNo) ? event.target.value : Math.min(pageNo, total)) + setInputPage(Number.isNaN(pageNo) ? Number(event.target.value) : Math.min(pageNo, total)) }} - onKeyUp={(event: any) => { + onKeyUp={event => { if (event.keyCode === 13) { changePage(inputPage) } diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index cdc3a39d7..9d9e4b090 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect, FC, memo } from 'react' +import { useState, useRef, useEffect, FC, memo, RefObject, ChangeEvent } from 'react' import { useHistory } from 'react-router' import { AxiosError } from 'axios' import { TFunction, useTranslation } from 'react-i18next' @@ -19,21 +19,23 @@ enum SearchResultType { UDT = 'udt', } -const clearSearchInput = (inputElement: any) => { - const input: HTMLInputElement = inputElement.current +const clearSearchInput = (inputElement: RefObject) => { + const input = inputElement.current if (input) { input.value = '' input.blur() } } -const setSearchLoading = (inputElement: any, t: TFunction) => { - const input: HTMLInputElement = inputElement.current - input.value = t('search.loading') +const setSearchLoading = (inputElement: RefObject, t: TFunction) => { + const input = inputElement.current + if (input) { + input.value = t('search.loading') + } } -const setSearchContent = (inputElement: any, content: string) => { - const input: HTMLInputElement = inputElement.current +const setSearchContent = (inputElement: RefObject, content: string) => { + const input = inputElement.current if (input) { input.value = content } @@ -41,7 +43,7 @@ const setSearchContent = (inputElement: any, content: string) => { const handleSearchResult = ( searchValue: string, - inputElement: any, + inputElement: RefObject, setSearchValue: Function, history: ReturnType, t: TFunction, @@ -131,12 +133,12 @@ const Search: FC<{ } } - const inputChangeAction = (event: any) => { + const inputChangeAction = (event: ChangeEvent) => { setSearchValue(event.target.value) if (!event.target.value) onEditEnd?.() } - const searchKeyAction = (event: any) => { + const searchKeyAction = (event: React.KeyboardEvent) => { if (event.keyCode === 13) { handleSearchResult(searchValue, inputElement, setSearchValue, history, t) onEditEnd?.() @@ -157,8 +159,8 @@ const Search: FC<{ ref={inputElement} placeholder={placeholder} defaultValue={searchValue || ''} - onChange={(event: any) => inputChangeAction(event)} - onKeyUp={(event: any) => searchKeyAction(event)} + onChange={event => inputChangeAction(event)} + onKeyUp={event => searchKeyAction(event)} /> {searchValue && } diff --git a/src/components/SimpleButton/index.tsx b/src/components/SimpleButton/index.tsx index 9ebfc429f..495bea4f3 100644 --- a/src/components/SimpleButton/index.tsx +++ b/src/components/SimpleButton/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react' +import { MouseEventHandler, ReactNode } from 'react' import styled from 'styled-components' const ButtonPanel = styled.div` @@ -14,8 +14,8 @@ export default ({ }: { id?: string className?: string - onClick?: Function - onMouseOver?: Function + onClick?: MouseEventHandler + onMouseOver?: MouseEventHandler children: ReactNode | string }) => ( {}} - onMouseOver={(event: any) => { + onMouseOver={event => { if (onMouseOver) { onMouseOver(event) } }} - onClick={(event: any) => { + onClick={event => { if (onClick) { onClick(event) } diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 67b5e31a1..ef22d87c9 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -10,7 +10,15 @@ export const TableTitleItem = ({ width, title }: { width: string; title: string ) -export const TableContentItem = ({ width, content, to }: { width: string; content: string | ReactNode; to?: any }) => { +export const TableContentItem = ({ + width, + content, + to, +}: { + width: string + content: string | ReactNode + to?: string +}) => { const highLight = to !== undefined return ( diff --git a/src/components/Table/styled.tsx b/src/components/Table/styled.tsx index 52964634c..adf87a0dc 100644 --- a/src/components/Table/styled.tsx +++ b/src/components/Table/styled.tsx @@ -96,7 +96,7 @@ export const TableMinerContentPanel = styled.div` margin: auto 0; .tableMinerContent { - color: ${(props: { theme: any }) => props.theme.primary}; + color: ${(props: { theme: State.Theme }) => props.theme.primary}; text-decoration: none; } diff --git a/src/components/Text/CopyTooltipText/index.tsx b/src/components/Text/CopyTooltipText/index.tsx index 5e970be81..f68fe3095 100644 --- a/src/components/Text/CopyTooltipText/index.tsx +++ b/src/components/Text/CopyTooltipText/index.tsx @@ -10,7 +10,7 @@ export default ({ content }: { content: string }) => { return ( { + onClick={event => { event.stopPropagation() copyElementValue(document.getElementById(`copy__content__${content}`)) setToast({ message: t('common.copied') }) diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index 88a9cff2f..c958f54f4 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -59,12 +59,24 @@ const ToastItem = ({ data, willLeave }: { data: State.ToastMessage; willLeave: F ) } -const initialState = { +interface State { + toasts: State.ToastMessage[] + toast: string +} + +interface Action { + type: 'ADD' | 'REMOVE' + payload: { + toast: State.ToastMessage + } +} + +const initialState: State = { toasts: [] as State.ToastMessage[], toast: '', } -const reducer = (state: any, action: any) => { +const reducer = (state: State, action: Action) => { switch (action.type) { case 'ADD': return { diff --git a/src/components/TransactionItem/TransactionIncome/styled.tsx b/src/components/TransactionItem/TransactionIncome/styled.tsx index 0d550a523..c65803210 100644 --- a/src/components/TransactionItem/TransactionIncome/styled.tsx +++ b/src/components/TransactionItem/TransactionIncome/styled.tsx @@ -19,7 +19,7 @@ export const TransactionCapacityValuePanel = styled.div` display: flex; align-items: center; justify-content: flex-end; - color: ${(props: { increased: boolean; theme: any }) => (props.increased ? props.theme.primary : '#FF6347')}; + color: ${(props: { increased: boolean; theme: State.Theme }) => (props.increased ? props.theme.primary : '#FF6347')}; font-size: 16px; img { diff --git a/src/components/TransactionItem/TransactionItemCell/styled.tsx b/src/components/TransactionItem/TransactionItemCell/styled.tsx index 6c6bee6ee..a78197d4d 100644 --- a/src/components/TransactionItem/TransactionItemCell/styled.tsx +++ b/src/components/TransactionItem/TransactionItemCell/styled.tsx @@ -18,7 +18,7 @@ export const TransactionCellPanel = styled.div` } .transactionCellAddress { - color: ${({ highLight = false, theme }: { highLight?: boolean; theme: any }) => + color: ${({ highLight = false, theme }: { highLight?: boolean; theme: State.Theme }) => highLight ? `${theme.primary}` : '#000000'}; font-weight: 500; min-width: 0; @@ -33,11 +33,11 @@ export const TransactionCellPanel = styled.div` } a { - color: ${({ theme }: { theme: any }) => `${theme.primary}`}; + color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; } a:hover { - color: ${({ theme }: { theme: any }) => `${theme.primary}`}; + color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; } } ` @@ -194,10 +194,10 @@ export const WithdrawItemPanel = styled.div` } a { - color: ${({ theme }: { theme: any }) => theme.primary}; + color: ${({ theme }: { theme: State.Theme }) => theme.primary}; } a:hover { - color: ${({ theme }: { theme: any }) => `${theme.primary}`}; + color: ${({ theme }: { theme: State.Theme }) => `${theme.primary}`}; } ` diff --git a/src/pages/BlockList/index.tsx b/src/pages/BlockList/index.tsx index f6bfe5246..81609378a 100644 --- a/src/pages/BlockList/index.tsx +++ b/src/pages/BlockList/index.tsx @@ -39,7 +39,7 @@ interface TableTitleData { interface TableContentData { width: string - to?: any + to?: string content: string } diff --git a/src/pages/NervosDao/DaoOverview/index.tsx b/src/pages/NervosDao/DaoOverview/index.tsx index 6a53428a5..d27550d82 100644 --- a/src/pages/NervosDao/DaoOverview/index.tsx +++ b/src/pages/NervosDao/DaoOverview/index.tsx @@ -28,6 +28,7 @@ import { useIsLGScreen, useIsMobile } from '../../../utils/hook' import { ReactChartCore } from '../../StatisticsChart/common' import { HelpTip } from '../../../components/HelpTip' import { ChartColor } from '../../../constants/common' +import { assertNotArray } from '../../../utils/chart' interface NervosDaoItemContent { title: string @@ -41,7 +42,7 @@ interface NervosDaoItemContent { interface NervosDaoPieItemContent { title: string content: ReactNode - color: any + color: string } const numberSymbol = (num: number, isCapacity = true) => { @@ -209,19 +210,17 @@ const useOption = (nervosDao: State.NervosDao, colors: string[], isMobile: boole title: t('nervos_dao.burnt'), }, ] - const selectedData: any = { - first: true, - } - selectedData[names[0]] = true - selectedData[names[1]] = true - selectedData[names[2]] = true return { color: colors, tooltip: { trigger: 'item', - formatter: (value: any) => - `${value.data.title}: ${localeNumberString(value.data.value)} ${t('common.ckb_unit')} (${value.data.name})`, + formatter: value => { + assertNotArray(value) + return `${value.data.title}: ${localeNumberString(value.data.value)} ${t('common.ckb_unit')} (${ + value.data.name + })` + }, position: ['10%', '50%'], }, series: [ diff --git a/src/pages/NervosDao/DaoOverview/styled.tsx b/src/pages/NervosDao/DaoOverview/styled.tsx index 038ccbea4..e6eda928a 100644 --- a/src/pages/NervosDao/DaoOverview/styled.tsx +++ b/src/pages/NervosDao/DaoOverview/styled.tsx @@ -201,7 +201,7 @@ export const DaoOverviewLeftItemPanel = styled.div` .daoOverviewItemChange { font-size: 12px; font-weight: bold; - color: ${(props: { symbol?: string; theme: any }) => + color: ${(props: { symbol?: string; theme: State.Theme }) => props.symbol === 'negative' ? '#FF464F' : props.theme.primary}; cursor: default; diff --git a/src/pages/Script/ScriptsComp.tsx b/src/pages/Script/ScriptsComp.tsx index bde0e6c0a..dc6d4a43f 100644 --- a/src/pages/Script/ScriptsComp.tsx +++ b/src/pages/Script/ScriptsComp.tsx @@ -22,6 +22,7 @@ import AddressText from '../../components/AddressText' import { ReactComponent as CopyIcon } from '../../assets/copy_icon.svg' import { ReactComponent as InfoMoreIcon } from '../../assets/info_more_icon.svg' import { useSetToast } from '../../components/Toast' +import { CellBasicInfo, transformToTransaction } from '../../utils/transformer' export const ScriptTransactions = ({ page, size }: { page: number; size: number }) => { const history = useHistory() @@ -62,22 +63,16 @@ export const ScriptTransactions = ({ page, size }: { page: number; size: number {data => (
{data.ckbTransactions && - data.ckbTransactions.map(tr => { - const transaction = { - ...tr, - transactionHash: tr.txHash, - } as any as State.Transaction - return ( - - ) - })} + data.ckbTransactions.map(tr => ( + + ))}
)} @@ -90,7 +85,7 @@ export const ScriptTransactions = ({ page, size }: { page: number; size: number ) } -export const CellInfo = ({ cell }: { cell: State.Cell }) => { +export const CellInfo = ({ cell }: { cell: CellBasicInfo }) => { const [showModal, setShowModal] = useState(false) return ( @@ -194,7 +189,14 @@ export const ScriptCells = ({
- +
diff --git a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx index 25ceb9ede..98cc212db 100644 --- a/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx +++ b/src/pages/StatisticsChart/activities/AddressBalanceRank.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react' import { useHistory } from 'react-router' import { useTranslation } from 'react-i18next' import { useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, parseNumericAbbr } from '../../../utils/chart' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { localeNumberString } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage, SmartChartPageProps } from '../common' @@ -10,7 +10,8 @@ import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useAdaptPCEllipsis } from '../../../utils/hook' -const getAddressWithRanking = (statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[], ranking: string) => { +const getAddressWithRanking = (statisticAddressBalanceRanks: State.StatisticAddressBalanceRank[], ranking?: string) => { + if (!ranking) return '' const addressBalanceRank = statisticAddressBalanceRanks.find(rank => rank.ranking === ranking) return addressBalanceRank ? addressBalanceRank.address : '' } @@ -44,7 +45,8 @@ const useOption = () => { tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 60 : 35) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.address'))} ${getAdaptAddressText( getAddressWithRanking(statisticAddressBalanceRanks, dataList[0].name), @@ -119,8 +121,8 @@ export const AddressBalanceRankChart = ({ isThumbnail = false }: { isThumbnail?: const [statisticAddressBalanceRanks, setStatisticAddressBalanceRanks] = useState( [], ) - const clickEvent = useCallback( - (param: any) => { + const handleClick = useCallback( + (param: echarts.CallbackDataParams) => { if (param && param.name && statisticAddressBalanceRanks.length > 0) { const address = getAddressWithRanking(statisticAddressBalanceRanks, param.name) if (address) { @@ -143,7 +145,7 @@ export const AddressBalanceRankChart = ({ isThumbnail = false }: { isThumbnail?: title={t('statistic.balance_ranking')} description={t('statistic.balance_ranking_description')} isThumbnail={isThumbnail} - chartProps={{ clickEvent: !isThumbnail ? clickEvent : undefined }} + chartProps={{ onClick: !isThumbnail ? handleClick : undefined }} fetchData={fetchStatisticAddressBalanceRanks} onFetched={setStatisticAddressBalanceRanks} getEChartOption={getEChartOption} diff --git a/src/pages/StatisticsChart/activities/AddressCount.tsx b/src/pages/StatisticsChart/activities/AddressCount.tsx index 4defb2c32..b933c96f1 100644 --- a/src/pages/StatisticsChart/activities/AddressCount.tsx +++ b/src/pages/StatisticsChart/activities/AddressCount.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { explorerService } from '../../../services/ExplorerService' @@ -34,7 +34,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 155 : 110) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}\ diff --git a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx index 7af5da615..28557e57c 100644 --- a/src/pages/StatisticsChart/activities/BalanceDistribution.tsx +++ b/src/pages/StatisticsChart/activities/BalanceDistribution.tsx @@ -1,7 +1,14 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, handleAxis, handleLogGroupAxis } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsString, + assertSerialsItem, + handleAxis, + handleLogGroupAxis, +} from '../../../utils/chart' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { ChartCachedKeys } from '../../../constants/cache' @@ -45,16 +52,20 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as (SeriesItem & { data: string })[] + formatter: dataList => { + assertIsArray(dataList) + const firstData = dataList[0] + assertSerialsItem(firstData) let result = `
${tooltipColor('#333333')}${widthSpan( t('statistic.addresses_balance'), currentLanguage, )} ${handleLogGroupAxis( - new BigNumber(list[0].name), - list[0].dataIndex === statisticBalanceDistributions.length - 1 ? '+' : '', + new BigNumber(firstData.name), + firstData.dataIndex === statisticBalanceDistributions.length - 1 ? '+' : '', )} ${t('common.ckb_unit')}
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsString(data) result += parseTooltip({ ...data, currentLanguage }) }) return result diff --git a/src/pages/StatisticsChart/activities/CellCount.tsx b/src/pages/StatisticsChart/activities/CellCount.tsx index 9ebe59486..ced46d668 100644 --- a/src/pages/StatisticsChart/activities/CellCount.tsx +++ b/src/pages/StatisticsChart/activities/CellCount.tsx @@ -1,7 +1,13 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf4, + assertSerialsItem, + handleAxis, +} from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' @@ -65,12 +71,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ - list[0].data[0] + dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf4(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/activities/TransactionCount.tsx b/src/pages/StatisticsChart/activities/TransactionCount.tsx index e1756fea4..357b76bc1 100644 --- a/src/pages/StatisticsChart/activities/TransactionCount.tsx +++ b/src/pages/StatisticsChart/activities/TransactionCount.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' @@ -36,7 +36,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 120 : 65) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan( diff --git a/src/pages/StatisticsChart/activities/TxFeeHistory.tsx b/src/pages/StatisticsChart/activities/TxFeeHistory.tsx index f21cf514b..defd36968 100644 --- a/src/pages/StatisticsChart/activities/TxFeeHistory.tsx +++ b/src/pages/StatisticsChart/activities/TxFeeHistory.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { shannonToCkbDecimal } from '../../../utils/util' @@ -37,7 +37,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 145 : 90) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan(t('statistic.tx_fee'))} ${handleAxis( diff --git a/src/pages/StatisticsChart/block/AverageBlockTime.tsx b/src/pages/StatisticsChart/block/AverageBlockTime.tsx index ed573a62e..da2676fe9 100644 --- a/src/pages/StatisticsChart/block/AverageBlockTime.tsx +++ b/src/pages/StatisticsChart/block/AverageBlockTime.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import { parseDateNoTime, parseSimpleDate, parseSimpleDateNoSecond } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, assertSerialsDataIsString, assertSerialsItem } from '../../../utils/chart' import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' @@ -58,14 +58,16 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${parseSimpleDateNoSecond( - new Date(list[0].data[0]), + new Date(dataList[0].data[0]), '/', false, )}
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsString(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx index fe06a2929..0b116ce25 100644 --- a/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/BlockTimeDistribution.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -33,7 +33,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 80 : 80) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.time'))} ${dataList[0].name}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan(t('statistic.block_count'))} ${ diff --git a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx index 39358624a..a7ed4b87f 100644 --- a/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx +++ b/src/pages/StatisticsChart/block/EpochTimeDistribution.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' import { localeNumberString } from '../../../utils/number' import { parseHourFromMinute } from '../../../utils/date' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { explorerService } from '../../../services/ExplorerService' import { ChartCachedKeys } from '../../../constants/cache' import { useCurrentLanguage } from '../../../utils/i18n' @@ -36,10 +36,11 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 80 : 80) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.time_hour'))} ${parseHourFromMinute( - dataList[0].name, + dataList[0].name ?? '0', )}
` result += `\
${tooltipColor(chartColor.colors[0])}\ diff --git a/src/pages/StatisticsChart/common/index.tsx b/src/pages/StatisticsChart/common/index.tsx index 9dbe7139e..d20266b92 100644 --- a/src/pages/StatisticsChart/common/index.tsx +++ b/src/pages/StatisticsChart/common/index.tsx @@ -47,7 +47,7 @@ const ChartLoading = ({ show, isThumbnail = false }: { show: boolean; isThumbnai const ReactChartCore = ({ option, isThumbnail, - clickEvent, + onClick, notMerge = false, lazyUpdate = false, style, @@ -55,7 +55,7 @@ const ReactChartCore = ({ }: { option: EChartOption isThumbnail?: boolean - clickEvent?: any + onClick?: (param: echarts.CallbackDataParams) => void notMerge?: boolean lazyUpdate?: boolean style?: CSSProperties @@ -64,7 +64,7 @@ const ReactChartCore = ({ const chartRef = useRef(null) const chartInstanceRef = useRef(null) const prevOption = usePrevious(option) - const prevClickEvent = usePrevious(clickEvent) + const prevClickEvent = usePrevious(onClick) useEffect(() => { let chartInstance: ECharts | null = null @@ -81,8 +81,8 @@ const ReactChartCore = ({ if (!isDeepEqual(prevOption, option, ['formatter'])) { chartInstance.setOption(option, { notMerge, lazyUpdate }) } - if (clickEvent && typeof clickEvent === 'function' && clickEvent !== prevClickEvent) { - chartInstance.on('click', clickEvent) + if (onClick && typeof onClick === 'function' && onClick !== prevClickEvent) { + chartInstance.on('click', onClick) } } catch (error) { console.error('error', error) @@ -91,7 +91,7 @@ const ReactChartCore = ({ } } } - }, [clickEvent, lazyUpdate, notMerge, option, prevClickEvent, prevOption]) + }, [onClick, lazyUpdate, notMerge, option, prevClickEvent, prevOption]) useWindowResize(() => { if (chartInstanceRef.current) { @@ -102,7 +102,7 @@ const ReactChartCore = ({ return
} -const dataToCsv = (data: any[] | undefined) => { +const dataToCsv = (data?: (string | number)[][]) => { if (!data || data.length === 0) { return undefined } @@ -197,10 +197,7 @@ export function SmartChartPage({ } }, [onFetched, query.data]) - const option = useMemo( - () => getEChartOption(dataList, ChartColor, isMobile, isThumbnail), - [dataList, getEChartOption, isMobile, isThumbnail], - ) + const option = getEChartOption(dataList, ChartColor, isMobile, isThumbnail) const content = query.isLoading ? ( diff --git a/src/pages/StatisticsChart/mining/Difficulty.tsx b/src/pages/StatisticsChart/mining/Difficulty.tsx index 75274cb3a..56b8aa720 100644 --- a/src/pages/StatisticsChart/mining/Difficulty.tsx +++ b/src/pages/StatisticsChart/mining/Difficulty.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { handleDifficulty } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' @@ -36,7 +36,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 70 : 35) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}\ diff --git a/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx b/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx index f8ae4840c..ecda3699e 100644 --- a/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx +++ b/src/pages/StatisticsChart/mining/DifficultyHashRate.tsx @@ -1,6 +1,12 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsString, + assertSerialsItem, + handleAxis, +} from '../../../utils/chart' import { handleDifficulty, handleHashRate } from '../../../utils/number' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { explorerService } from '../../../services/ExplorerService' @@ -11,7 +17,6 @@ const useOption = ( statisticDifficultyHashRates: State.StatisticDifficultyHashRate[], chartColor: State.ChartColor, isMobile: boolean, - isThumbnail = false, ): echarts.EChartOption => { const { t } = useTranslation() @@ -53,10 +58,12 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any): string => { - const list = dataList as Array - let result = `
${tooltipColor('#333333')}${widthSpan(t('block.epoch'))} ${list[0].name}
` - list.forEach(data => { + formatter: (dataList): string => { + assertIsArray(dataList) + let result = `
${tooltipColor('#333333')}${widthSpan(t('block.epoch'))} ${dataList[0].name}
` + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsString(data) result += parseTooltip(data) }) return result @@ -171,7 +178,7 @@ const useOption = ( }, ], label: { - formatter: (params: any) => `${params.value}%`, + formatter: (params: { value: string }) => `${params.value}%`, }, }, data: statisticDifficultyHashRates.map(data => (Number(data.uncleRate) * 100).toFixed(2)), diff --git a/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx b/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx index d371058ff..e44710a2b 100644 --- a/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx +++ b/src/pages/StatisticsChart/mining/DifficultyUncleRateEpoch.tsx @@ -1,7 +1,7 @@ import { FC } from 'react' import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { handleAxis } from '../../../utils/chart' +import { assertSerialsDataIsString, assertIsArray, assertSerialsItem, handleAxis } from '../../../utils/chart' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { parseHourFromMillisecond } from '../../../utils/date' import { ChartCachedKeys } from '../../../constants/cache' @@ -63,12 +63,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('block.epoch'), currentLanguage)} ${ - list[0].name + dataList[0].name }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsString(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/mining/HashRate.tsx b/src/pages/StatisticsChart/mining/HashRate.tsx index 678bd8861..0ec49dbf3 100644 --- a/src/pages/StatisticsChart/mining/HashRate.tsx +++ b/src/pages/StatisticsChart/mining/HashRate.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' -import { DATA_ZOOM_CONFIG, handleAxis } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray, handleAxis } from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { handleHashRate } from '../../../utils/number' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' @@ -37,7 +37,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 75 : 50) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan(t('block.hash_rate'))} ${handleHashRate( diff --git a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx index edf3bced3..aa58ea77d 100644 --- a/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx +++ b/src/pages/StatisticsChart/mining/MinerAddressDistribution.tsx @@ -6,6 +6,7 @@ import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' import { useAdaptMobileEllipsis, useAdaptPCEllipsis, useIsMobile } from '../../../utils/hook' import { useCurrentLanguage } from '../../../utils/i18n' +import { assertNotArray } from '../../../utils/chart' const Colors = [ '#069ECD', @@ -49,7 +50,8 @@ const useOption = () => { color: [chartColor.colors[0], ...Colors], tooltip: !isThumbnail ? { - formatter: (data: any) => { + formatter: data => { + assertNotArray(data) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 60 : 65) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.address'))} ${getAdaptAddressText( data.data.title, @@ -107,8 +109,8 @@ export const MinerAddressDistributionChart = ({ isThumbnail = false }: { isThumb const [t] = useTranslation() const history = useHistory() - const clickEvent = useCallback( - (param: any) => { + const onClick = useCallback( + (param: echarts.CallbackDataParams) => { if (param && param.data.title) { history.push(`/address/${param.data.title}`) } @@ -130,7 +132,7 @@ export const MinerAddressDistributionChart = ({ isThumbnail = false }: { isThumb { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 75 : 50) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan(t('block.uncle_rate'))} ${ @@ -95,7 +96,7 @@ const useOption = ( }, ], label: { - formatter: (label: any) => `${label.data.value}%`, + formatter: (label: { data: { value: string } }) => `${label.data.value}%`, }, }, }, diff --git a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx index 9047f2711..3972873fa 100644 --- a/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx +++ b/src/pages/StatisticsChart/monetary/AnnualPercentageCompensation.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'react-i18next' import { useCurrentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -34,7 +34,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 220 : 80) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.year'))} ${dataList[0].data[0]}
` result += `
${tooltipColor(chartColor.colors[0])}${widthSpan(t('statistic.nominal_apc'))} ${ diff --git a/src/pages/StatisticsChart/monetary/InflationRate.tsx b/src/pages/StatisticsChart/monetary/InflationRate.tsx index 335cab5ad..5f1ec06c8 100644 --- a/src/pages/StatisticsChart/monetary/InflationRate.tsx +++ b/src/pages/StatisticsChart/monetary/InflationRate.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'react-i18next' import { useCurrentLanguage } from '../../../utils/i18n' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertSerialsDataIsString, assertIsArray, assertSerialsItem } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -51,10 +51,12 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array - let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.year'))} ${list[0].name}
` - list.forEach(data => { + formatter: dataList => { + assertIsArray(dataList) + let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.year'))} ${dataList[0].name}
` + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsString(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/monetary/Liquidity.tsx b/src/pages/StatisticsChart/monetary/Liquidity.tsx index fadefb080..a5de0a39b 100644 --- a/src/pages/StatisticsChart/monetary/Liquidity.tsx +++ b/src/pages/StatisticsChart/monetary/Liquidity.tsx @@ -2,7 +2,13 @@ import { useTranslation } from 'react-i18next' import { useCurrentLanguage } from '../../../utils/i18n' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' -import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf4, + assertSerialsItem, + parseNumericAbbr, +} from '../../../utils/chart' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -63,12 +69,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ - list[0].data[0] + dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf4(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx b/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx index 3ab2afc32..b5e9f890c 100644 --- a/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx +++ b/src/pages/StatisticsChart/monetary/SecondaryIssuance.tsx @@ -2,7 +2,12 @@ import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf4, + assertSerialsItem, +} from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -59,12 +64,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf4(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/monetary/TotalSupply.tsx b/src/pages/StatisticsChart/monetary/TotalSupply.tsx index a67d40980..15dde1706 100644 --- a/src/pages/StatisticsChart/monetary/TotalSupply.tsx +++ b/src/pages/StatisticsChart/monetary/TotalSupply.tsx @@ -1,7 +1,13 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf4, + assertSerialsItem, + parseNumericAbbr, +} from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SeriesItem, SmartChartPage } from '../common' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' @@ -68,12 +74,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ - list[0].data[0] + dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf4(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx index afcacf9ee..51d812a73 100644 --- a/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx +++ b/src/pages/StatisticsChart/nervosDao/CirculationRatio.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import { useCurrentLanguage } from '../../../utils/i18n' import { parseDateNoTime } from '../../../utils/date' import { tooltipColor, tooltipWidth, SmartChartPage } from '../common' -import { DATA_ZOOM_CONFIG } from '../../../utils/chart' +import { DATA_ZOOM_CONFIG, assertIsArray } from '../../../utils/chart' import { ChartCachedKeys } from '../../../constants/cache' import { explorerService } from '../../../services/ExplorerService' @@ -33,7 +33,8 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { + formatter: dataList => { + assertIsArray(dataList) const widthSpan = (value: string) => tooltipWidth(value, currentLanguage === 'en' ? 185 : 165) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'))} ${dataList[0].data[0]}
` if (dataList[0].data) { diff --git a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx index e8bd4a41f..3baf2c035 100644 --- a/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/NewDaoDeposit.tsx @@ -1,7 +1,13 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf3, + assertSerialsItem, + parseNumericAbbr, +} from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' @@ -60,12 +66,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as (SeriesItem & { data: [string, string, string] })[] + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ - list[0].data[0] + dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf3(data) result += parseTooltip(data) }) return result diff --git a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx index d809ea2bd..eb0cd5f07 100644 --- a/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx +++ b/src/pages/StatisticsChart/nervosDao/TotalDaoDeposit.tsx @@ -1,7 +1,13 @@ import BigNumber from 'bignumber.js' import { useTranslation } from 'react-i18next' import { LanuageType, useCurrentLanguage } from '../../../utils/i18n' -import { DATA_ZOOM_CONFIG, parseNumericAbbr } from '../../../utils/chart' +import { + DATA_ZOOM_CONFIG, + assertIsArray, + assertSerialsDataIsStringArrayOf3, + assertSerialsItem, + parseNumericAbbr, +} from '../../../utils/chart' import { parseDateNoTime } from '../../../utils/date' import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { isMainnet } from '../../../utils/chain' @@ -61,12 +67,14 @@ const useOption = ( tooltip: !isThumbnail ? { trigger: 'axis', - formatter: (dataList: any) => { - const list = dataList as Array + formatter: dataList => { + assertIsArray(dataList) let result = `
${tooltipColor('#333333')}${widthSpan(t('statistic.date'), currentLanguage)} ${ - list[0].data[0] + dataList[0].data[0] }
` - list.forEach(data => { + dataList.forEach(data => { + assertSerialsItem(data) + assertSerialsDataIsStringArrayOf3(data) result += parseTooltip(data) }) return result diff --git a/src/pages/Transaction/TransactionCell/styled.tsx b/src/pages/Transaction/TransactionCell/styled.tsx index e36d30951..d1e2aa071 100644 --- a/src/pages/Transaction/TransactionCell/styled.tsx +++ b/src/pages/Transaction/TransactionCell/styled.tsx @@ -99,7 +99,7 @@ export const TransactionCellAddressPanel = styled.div` ` export const TransactionCellHashPanel = styled.div` - color: ${({ highLight = false, theme }: { highLight?: boolean; theme: any }) => + color: ${({ highLight = false, theme }: { highLight?: boolean; theme: State.Theme }) => highLight ? `${theme.primary}` : '#000000'}; text-align: ${({ highLight = false }: { highLight?: boolean }) => (highLight ? 'left' : 'center')}; flex: 1; diff --git a/src/pages/Transaction/TransactionCellList/index.tsx b/src/pages/Transaction/TransactionCellList/index.tsx index 135f87336..df6e0a4e5 100644 --- a/src/pages/Transaction/TransactionCellList/index.tsx +++ b/src/pages/Transaction/TransactionCellList/index.tsx @@ -37,7 +37,7 @@ export default ({ const isScroll = cells.length > PAGE_CELL_COUNT const handleScroll = useCallback( - (event: Event) => { + (event: React.UIEvent) => { if (cells.length <= offset) { setIsEnd(true) return @@ -94,7 +94,7 @@ export default ({
{cellTitle()}
-
handleScroll(event)}> +
handleScroll(event)}> {cells && cells .slice(0, offset) diff --git a/src/pages/Transaction/TransactionCellScript/index.tsx b/src/pages/Transaction/TransactionCellScript/index.tsx index 6acf57507..5bfba4e20 100644 --- a/src/pages/Transaction/TransactionCellScript/index.tsx +++ b/src/pages/Transaction/TransactionCellScript/index.tsx @@ -26,6 +26,7 @@ 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' +import { CellBasicInfo } from '../../../utils/transformer' const initScriptContent = { lock: 'null', @@ -35,6 +36,11 @@ const initScriptContent = { }, } +type TransactionCellScriptProps = { + cell: CellBasicInfo + onClose: Function +} + type CapacityUsage = Record<'declared' | 'occupied', string | null> const updateJsonFormat = (content: State.Script | State.Data | CapacityUsage | null): string => { @@ -54,7 +60,7 @@ const updateJsonFormat = (content: State.Script | State.Data | CapacityUsage | n } const handleFetchCellInfo = async ( - cell: State.Cell, + cell: CellBasicInfo, state: CellState, setScriptFetchStatus: (val: boolean) => void, setContent: Function, @@ -228,7 +234,7 @@ const ScriptContentJson = ({ ) -export default ({ cell, onClose }: { cell: State.Cell; onClose: Function }) => { +export default ({ cell, onClose }: TransactionCellScriptProps) => { const setToast = useSetToast() const { t } = useTranslation() const [scriptFetched, setScriptFetched] = useState(false) diff --git a/src/pages/Transaction/TransactionCellScript/styled.tsx b/src/pages/Transaction/TransactionCellScript/styled.tsx index fa545293c..4b1585d4a 100644 --- a/src/pages/Transaction/TransactionCellScript/styled.tsx +++ b/src/pages/Transaction/TransactionCellScript/styled.tsx @@ -28,10 +28,10 @@ export const TransactionDetailItem = styled.div` left: 2px; bottom: 0; content: ''; - background: ${(props: { theme: any }) => `${props.theme.primary}`}; + background: ${(props: { theme: State.Theme }) => `${props.theme.primary}`}; width: calc(100% - 4px); height: 5px; - display: ${(props: { theme: any; selected: boolean }) => (props.selected ? 'block' : 'none')}; + display: ${(props: { theme: State.Theme; selected: boolean }) => (props.selected ? 'block' : 'none')}; } ` diff --git a/src/types/echarts.d.ts b/src/types/echarts.d.ts new file mode 100644 index 000000000..1f0a4c835 --- /dev/null +++ b/src/types/echarts.d.ts @@ -0,0 +1,31 @@ +declare namespace echarts { + // this type is provided by echarts since v5.0.0, but now that we use v4 so we need to define it by ourselves, copied from: + // https://github.com/apache/echarts/blob/babe688f40feefe3b3f53b31e0d227256fcb36ce/src/util/types.ts#L678-L707 + export type CallbackDataParams = { + // component main type + componentType: string + // component sub type + componentSubType: string + componentIndex: number + // series component sub type + seriesType?: string + // series component index (the alias of `componentIndex` for series) + seriesIndex?: number + seriesId?: string + seriesName?: string + name: string + dataIndex: number + data: OptionDataItem + dataType?: SeriesDataType + value: OptionDataItem | OptionDataValue + color?: ZRColor + opacity?: number + borderColor?: string + dimensionNames?: DimensionName[] + encode?: DimensionUserOuputEncode + marker?: TooltipMarker + status?: DisplayState + dimensionIndex?: number + percent?: number // Only for chart like 'pie' + } +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 6ec2d61a2..ccdbef7c0 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -600,4 +600,6 @@ declare namespace State { } type SortOrderTypes = 'asc' | 'desc' + + type Theme = { primary: string } } diff --git a/src/utils/chart.ts b/src/utils/chart.ts index e76435b05..b3c451fae 100644 --- a/src/utils/chart.ts +++ b/src/utils/chart.ts @@ -1,4 +1,6 @@ import BigNumber from 'bignumber.js' +import { EChartOption } from 'echarts' +import { SeriesItem } from '../pages/StatisticsChart/common' export const DATA_ZOOM_CONFIG = [ { @@ -125,3 +127,50 @@ export const getFeeRateSamples = (feeRates: Array(value: T | T[]): asserts value is T[] { + if (!Array.isArray(value)) { + throw new Error(`Value is expected to be an array, but got a ${typeof value}`) + } +} + +export function assertNotArray(value: T | T[]): asserts value is T { + if (Array.isArray(value)) { + throw new Error('value should not be an array') + } +} + +type AssertSerialsItem = (value: EChartOption.Tooltip.Format) => asserts value is SeriesItem +export const assertSerialsItem: AssertSerialsItem = (value: EChartOption.Tooltip.Format) => { + if ( + typeof value.seriesName !== 'string' || + typeof value.name !== 'string' || + typeof value.color !== 'string' || + typeof value.dataIndex !== 'number' + ) { + throw new Error('invalid SeriesItem') + } +} + +export const assertSerialsDataIsString: (value: EChartOption.Tooltip.Format) => asserts value is { data: string } = ( + value: EChartOption.Tooltip.Format, +) => { + if (typeof value.data !== 'string') { + throw new Error(`Value is expected to be an array, but got a ${typeof value.data}`) + } +} + +export const assertSerialsDataIsStringArrayOf3: ( + value: EChartOption.Tooltip.Format, +) => asserts value is { data: [string, string, string] } = (value: EChartOption.Tooltip.Format) => { + if (!Array.isArray(value.data) || value.data.length !== 3 || value.data.every(item => typeof item !== 'string')) { + throw new Error('invalid SeriesItem length of 3') + } +} +export const assertSerialsDataIsStringArrayOf4: ( + value: EChartOption.Tooltip.Format, +) => asserts value is { data: [string, string, string, string] } = (value: EChartOption.Tooltip.Format) => { + if (!Array.isArray(value.data) || value.data.length !== 4 || value.data.every(item => typeof item !== 'string')) { + throw new Error('invalid SeriesItem length of 4') + } +} diff --git a/src/utils/transformer.ts b/src/utils/transformer.ts new file mode 100644 index 000000000..e5c1789a0 --- /dev/null +++ b/src/utils/transformer.ts @@ -0,0 +1,41 @@ +import { CellInScript, CkbTransactionInScript } from '../pages/Script/types' + +export const transformToTransaction = (tx: CkbTransactionInScript): State.Transaction => { + return { + transactionHash: tx.txHash, + blockNumber: tx.blockNumber, + blockTimestamp: tx.blockTimestamp, + transactionFee: String(tx.transactionFee), + isCellbase: tx.isCellbase, + displayInputs: tx.displayInputs, + displayOutputs: tx.displayOutputs, + txStatus: tx.txStatus, + + // defaults + income: '', + targetBlockNumber: 0, + version: 0, + cellDeps: [], + headerDeps: [], + witnesses: [], + liveCellChanges: '', + capacityInvolved: '', + detailedMessage: '', + bytes: 0, + largestTxInEpoch: 0, + largestTx: 0, + cycles: null, + maxCyclesInEpoch: null, + maxCycles: null, + } +} + +export type CellBasicInfo = Pick +export const transformToCellBasicInfo = (cell: CellInScript): CellBasicInfo => { + return { + id: cell.id, + capacity: cell.capacity, + occupiedCapacity: String(cell.occupiedCapacity), + isGenesisOutput: false, + } +} diff --git a/src/utils/util.ts b/src/utils/util.ts index bfb7c8214..c8c2f6d1c 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -1,4 +1,4 @@ -import { ReactNode } from 'react' +import { ReactNode, SyntheticEvent } from 'react' import camelcaseKeys from 'camelcase-keys' import JSBI from 'jsbi' import BigNumber from 'bignumber.js' @@ -152,7 +152,7 @@ export const handleRedirectFromAggron = () => { return false } -export const handleNftImgError = (e: React.SyntheticEvent) => { +export const handleNftImgError = (e: SyntheticEvent) => { e.currentTarget.src = '/images/nft_placeholder.png' } From 8394e7511ff7d506b27495b354d8f18e22e3ae05 Mon Sep 17 00:00:00 2001 From: Shinya Date: Tue, 24 Oct 2023 23:21:55 +0800 Subject: [PATCH 3/3] feat: lite version of transaction information (#25) Co-authored-by: WhiteMind Co-authored-by: zhb666 <462612421@qq.com> --- .gitignore | 2 + renovate.json | 4 +- src/components/Card/HashCard/index.tsx | 54 ++- .../Card/HashCard/styles.module.scss | 54 +++ src/components/DecimalCapacity/index.tsx | 24 +- src/components/DecimalCapacity/styled.tsx | 12 +- .../DecimalCapacity/styles.module.scss | 3 + .../TransactionIncome/index.tsx | 5 +- .../TransactionItemCell/index.tsx | 8 +- .../TransactionLiteIncome/index.module.scss | 2 +- .../TransactionLiteIncome/index.tsx | 10 +- src/constants/common.ts | 5 + src/index.css | 2 + src/locales/en.json | 4 +- src/locales/zh.json | 4 +- src/pages/Address/AddressComp.tsx | 20 +- .../Transaction/TransactionCell/index.tsx | 2 +- src/pages/Transaction/TransactionComp.tsx | 422 ------------------ .../TransactionComp/TransactionComp.tsx | 54 +++ .../TransactionBadge.module.scss | 16 + .../TransactionLite/TransactionBadge.tsx | 40 ++ .../TransactionLite.module.scss | 141 ++++++ .../TransactionLite/TransactionLite.tsx | 152 +++++++ .../TransactionComp/TransactionOverview.tsx | 389 ++++++++++++++++ .../{ => TransactionComp}/styled.tsx | 13 + src/pages/Transaction/index.tsx | 28 +- src/pages/Transaction/state.ts | 7 + src/services/ExplorerService/fetcher.ts | 5 + src/types/index.d.ts | 44 ++ src/utils/number.ts | 8 +- src/utils/util.ts | 6 + 31 files changed, 1067 insertions(+), 473 deletions(-) create mode 100644 src/components/DecimalCapacity/styles.module.scss delete mode 100644 src/pages/Transaction/TransactionComp.tsx create mode 100644 src/pages/Transaction/TransactionComp/TransactionComp.tsx create mode 100644 src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.module.scss create mode 100644 src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx create mode 100644 src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.module.scss create mode 100644 src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx create mode 100644 src/pages/Transaction/TransactionComp/TransactionOverview.tsx rename src/pages/Transaction/{ => TransactionComp}/styled.tsx (92%) diff --git a/.gitignore b/.gitignore index 9cdabea55..4476332cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store .vscode .idea/ +.eslintcache node_modules/ build/ @@ -8,6 +9,7 @@ dist/ npm-debug.log *.log coverage/ +dist/ *.orig *.swp *.bak diff --git a/renovate.json b/renovate.json index 39a2b6e9a..4bd832f5f 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,4 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ] + "extends": ["config:base"] } diff --git a/src/components/Card/HashCard/index.tsx b/src/components/Card/HashCard/index.tsx index d1824c1d5..e2831e6f8 100644 --- a/src/components/Card/HashCard/index.tsx +++ b/src/components/Card/HashCard/index.tsx @@ -1,11 +1,12 @@ import type { FC, ReactNode } from 'react' import { Link } from 'react-router-dom' -import { Tooltip } from 'antd' +import { Radio, Tooltip } from 'antd' import { useTranslation } from 'react-i18next' +import { LayoutLiteProfessional } from '../../../constants/common' import CopyIcon from '../../../assets/copy.png' import { explorerService } from '../../../services/ExplorerService' import SmallLoading from '../../Loading/SmallLoading' -import { useIsMobile, useNewAddr, useDeprecatedAddr } from '../../../utils/hook' +import { useIsMobile, useNewAddr, useDeprecatedAddr, useSearchParams, useUpdateSearchParams } from '../../../utils/hook' import SimpleButton from '../../SimpleButton' import { ReactComponent as OpenInNew } from '../../../assets/open_in_new.svg' import { ReactComponent as DownloadIcon } from '../../../assets/download_tx.svg' @@ -48,6 +49,7 @@ export default ({ showDASInfoOnHeader?: boolean | string }) => { const isMobile = useIsMobile() + const { Professional, Lite } = LayoutLiteProfessional const setToast = useSetToast() const { t } = useTranslation() @@ -56,6 +58,19 @@ export default ({ const deprecatedAddr = useDeprecatedAddr(hash) const counterpartAddr = newAddr === hash ? deprecatedAddr : newAddr + const searchParams = useSearchParams('layout') + const defaultLayout = Professional + const updateSearchParams = useUpdateSearchParams<'layout'>() + const layout = searchParams.layout === Lite ? Lite : defaultLayout + + const onChangeLayout = (layoutType: LayoutLiteProfessional) => { + updateSearchParams(params => + layoutType === defaultLayout + ? Object.fromEntries(Object.entries(params).filter(entry => entry[0] !== 'layout')) + : { ...params, layout: layoutType }, + ) + } + const handleExportTxClick = async () => { const res = await explorerService.api.requesterV2(`transactions/${hash}/raw`).catch(error => { setToast({ message: error.message }) @@ -86,7 +101,6 @@ export default ({
{title}
)} -
{loading ? ( @@ -133,11 +147,26 @@ export default ({ ) : null}
+ {!isMobile && isTx && !loading ? ( +
+ onChangeLayout(value)} + value={layout} + optionType="button" + buttonStyle="solid" + /> +
+ ) : null} + {(showDASInfoOnHeader || showDASInfoOnHeader === '') && ( )}
- {specialAddress && ( @@ -149,6 +178,23 @@ export default ({ {hash}
+ + {isMobile && isTx && !loading ? ( +
+ onChangeLayout(value)} + value={layout} + optionType="button" + buttonStyle="solid" + /> +
+ ) : null} + {children} ) diff --git a/src/components/Card/HashCard/styles.module.scss b/src/components/Card/HashCard/styles.module.scss index 62b91cc6f..c7edcd4ef 100644 --- a/src/components/Card/HashCard/styles.module.scss +++ b/src/components/Card/HashCard/styles.module.scss @@ -85,3 +85,57 @@ color: #333; } } + +.professionalLiteBox { + margin-left: 10px; + + .layoutButtons { + > label { + 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 !important; + + &::before { + content: none !important; + } + + &:hover { + color: #333; + background: #fff; + } + + &: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: #333; + border-color: #333 !important; + + &:hover { + background: #333; + } + } + } + + @media screen and (width <= 750px) { + width: 100%; + margin: 6px 0 20px; + + > label { + height: 40px; + width: 50%; + } + } + } +} diff --git a/src/components/DecimalCapacity/index.tsx b/src/components/DecimalCapacity/index.tsx index 623bebb48..a45171cab 100644 --- a/src/components/DecimalCapacity/index.tsx +++ b/src/components/DecimalCapacity/index.tsx @@ -1,23 +1,27 @@ +import classNames from 'classnames' import { useTranslation } from 'react-i18next' import { DecimalPanel, DecimalPartPanel, DecimalZerosPanel } from './styled' +import styles from './styles.module.scss' export default ({ value, fontSize, - color, + balanceChangeType = 'none', hideUnit, hideZero, marginBottom = '1px', }: { value: string + balanceChangeType?: 'payment' | 'income' | 'none' fontSize?: string - color?: string hideUnit?: boolean hideZero?: boolean marginBottom?: string }) => { const { t } = useTranslation() const integer = value.split('.')[0] || '0' + const isPayment = balanceChangeType === 'payment' + const balanceChangeTypeClass = isPayment ? 'subtraction' : 'addition' let decimal = value.split('.')[1] || '' let zeros = '' @@ -33,16 +37,24 @@ export default ({ return ( - {integer} - + {integer} + {decimal} {!hideZero && ( - + {zeros} )} - {!hideUnit &&
{t('common.ckb_unit')}
} + {!hideUnit &&
{t('common.ckb_unit')}
}
) } diff --git a/src/components/DecimalCapacity/styled.tsx b/src/components/DecimalCapacity/styled.tsx index 35ed1906c..abc4054bc 100644 --- a/src/components/DecimalCapacity/styled.tsx +++ b/src/components/DecimalCapacity/styled.tsx @@ -5,6 +5,14 @@ export const DecimalPanel = styled.div` justify-content: flex-end; align-items: flex-end; + .subtraction { + color: var(--accent-color); + } + + .addition { + color: var(--primary-color); + } + .decimalUnit { margin-left: 5px; @@ -17,7 +25,7 @@ export const DecimalPanel = styled.div` export const DecimalPartPanel = styled.div` margin-bottom: ${(props: { marginBottom: string }) => (props.marginBottom ? props.marginBottom : '1px')}; font-size: ${(props: { fontSize?: string; color?: string; marginBottom: string }) => - props.fontSize ? props.fontSize : '12px'}; + props.fontSize ? props.fontSize : '14px'}; color: ${(props: { color?: string }) => (props.color ? props.color : '#999999')}; @media (max-width: 1000px) { @@ -32,7 +40,7 @@ export const DecimalPartPanel = styled.div` export const DecimalZerosPanel = styled.div` margin-bottom: ${(props: { marginBottom: string }) => (props.marginBottom ? props.marginBottom : '1px')}; font-size: ${(props: { fontSize?: string; color?: string; marginBottom: string }) => - props.fontSize ? props.fontSize : '12px'}; + props.fontSize ? props.fontSize : '14px'}; color: ${(props: { color?: string }) => (props.color ? props.color : '#999999')}; @media (max-width: 1000px) { diff --git a/src/components/DecimalCapacity/styles.module.scss b/src/components/DecimalCapacity/styles.module.scss new file mode 100644 index 000000000..7d18ea543 --- /dev/null +++ b/src/components/DecimalCapacity/styles.module.scss @@ -0,0 +1,3 @@ +.intergerPart { + font-size: 16px; +} diff --git a/src/components/TransactionItem/TransactionIncome/index.tsx b/src/components/TransactionItem/TransactionIncome/index.tsx index f188b001c..eba38aa33 100644 --- a/src/components/TransactionItem/TransactionIncome/index.tsx +++ b/src/components/TransactionItem/TransactionIncome/index.tsx @@ -15,9 +15,10 @@ export default ({ income }: { income: string }) => { if (bigIncome.isNaN()) { bigIncome = new BigNumber(0) } + const isIncome = bigIncome.isGreaterThanOrEqualTo(0) return ( - + {isMobile && ( current Address @@ -25,7 +26,7 @@ export default ({ income }: { income: string }) => { )} {!isMobile && ( diff --git a/src/components/TransactionItem/TransactionItemCell/index.tsx b/src/components/TransactionItem/TransactionItemCell/index.tsx index 6a32695d0..0e1a11cf1 100644 --- a/src/components/TransactionItem/TransactionItemCell/index.tsx +++ b/src/components/TransactionItem/TransactionItemCell/index.tsx @@ -9,7 +9,7 @@ import CurrentAddressIcon from '../../../assets/current_address.svg' import UDTTokenIcon from '../../../assets/udt_token.png' import { useCurrentLanguage } from '../../../utils/i18n' import { localeNumberString, parseUDTAmount } from '../../../utils/number' -import { shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' +import { isDaoCell, isDaoDepositCell, isDaoWithdrawCell, shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { TransactionCellPanel, TransactionCellCapacityPanel, @@ -30,12 +30,6 @@ import { useBoolean, useIsMobile } from '../../../utils/hook' import CopyTooltipText from '../../Text/CopyTooltipText' import EllipsisMiddle from '../../EllipsisMiddle' -const isDaoDepositCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_deposit' - -const isDaoWithdrawCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_withdrawing' - -const isDaoCell = (cellType: State.CellTypes) => isDaoDepositCell(cellType) || isDaoWithdrawCell(cellType) - const AddressTextWithAlias: FC<{ address: string to?: string diff --git a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss b/src/components/TransactionItem/TransactionLiteIncome/index.module.scss index a7a471803..3b4fabb5a 100644 --- a/src/components/TransactionItem/TransactionLiteIncome/index.module.scss +++ b/src/components/TransactionItem/TransactionLiteIncome/index.module.scss @@ -22,5 +22,5 @@ } .decreased { - color: #fa504f; + color: var(--accent-color); } diff --git a/src/components/TransactionItem/TransactionLiteIncome/index.tsx b/src/components/TransactionItem/TransactionLiteIncome/index.tsx index ee2e6d364..8f389f742 100644 --- a/src/components/TransactionItem/TransactionLiteIncome/index.tsx +++ b/src/components/TransactionItem/TransactionLiteIncome/index.tsx @@ -10,17 +10,13 @@ export default ({ income }: { income: string }) => { if (bigIncome.isNaN()) { bigIncome = new BigNumber(0) } + const isIncome = bigIncome.isGreaterThanOrEqualTo(0) return ( -
+
) diff --git a/src/constants/common.ts b/src/constants/common.ts index eb98f5f3f..c6bc2792b 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -110,5 +110,10 @@ export enum ChainName { Testnet = 'pudge', } +export enum LayoutLiteProfessional { + Lite = 'lite', + Professional = 'professional', +} + export const MAINNET_URL = `https://${config.BASE_URL}` export const TESTNET_URL = `https://${ChainName.Testnet}.${config.BASE_URL}` diff --git a/src/index.css b/src/index.css index 4b3e4603d..9c467eb24 100644 --- a/src/index.css +++ b/src/index.css @@ -2,6 +2,8 @@ body { --primary-color: #00cc9b; + --accent-color: #FA504F; + --primary-text-color: #333; --primary-hover-bg-color: #e8fff1; --primary-chiffon-color: #e6fcf7; --navbar-height: 64px; diff --git a/src/locales/en.json b/src/locales/en.json index dd513713b..46c58145f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -453,7 +453,9 @@ } }, "professional": "Professional", - "lite": "Lite" + "lite": "Lite", + "unknown_assets": "Unknown Assets", + "mint": "Mint" }, "block": { "block": "Block", diff --git a/src/locales/zh.json b/src/locales/zh.json index df7482307..55bfe6acc 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -452,7 +452,9 @@ } }, "professional": "专业版", - "lite": "精简版" + "lite": "精简版", + "unknown_assets": "未知资产", + "mint": "铸造" }, "block": { "block": "区块", diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index b859c3c67..4cdf29964 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -43,6 +43,7 @@ import ArrowUpIcon from '../../assets/arrow_up.png' import ArrowUpBlueIcon from '../../assets/arrow_up_blue.png' import ArrowDownIcon from '../../assets/arrow_down.png' import ArrowDownBlueIcon from '../../assets/arrow_down_blue.png' +import { LayoutLiteProfessional } from '../../constants/common' import { omit } from '../../utils/object' import { CsvExport } from '../../components/CsvExport' import PaginationWithRear from '../../components/PaginationWithRear' @@ -341,17 +342,20 @@ export const AddressTransactions = ({ const isMobile = useIsMobile() const { t } = useTranslation() const { currentPage, pageSize, setPage } = usePaginationParamsInListPage() + const { Professional, Lite } = LayoutLiteProfessional const searchParams = useSearchParams('layout') - const defaultLayout = 'professional' + const defaultLayout = Professional const updateSearchParams = useUpdateSearchParams<'layout' | 'sort' | 'tx_type'>() - const layout = searchParams.layout === 'lite' ? 'lite' : defaultLayout + const layout = searchParams.layout === Lite ? Lite : defaultLayout const totalPages = Math.ceil(total / pageSize) - const onChangeLayout = (lo: 'professional' | 'lite') => { - updateSearchParams(params => (lo === defaultLayout ? omit(params, ['layout']) : { ...params, layout: lo })) + const onChangeLayout = (layoutType: LayoutLiteProfessional) => { + updateSearchParams(params => + layoutType === defaultLayout + ? Object.fromEntries(Object.entries(params).filter(entry => entry[0] !== 'layout')) + : { ...params, layout: layoutType }, + ) } - - // REFACTOR: could be an independent component const handleTimeSort = () => { updateSearchParams( params => @@ -394,8 +398,8 @@ export const AddressTransactions = ({ onChangeLayout(value)} value={layout} diff --git a/src/pages/Transaction/TransactionCell/index.tsx b/src/pages/Transaction/TransactionCell/index.tsx index c75492f7c..a85676598 100644 --- a/src/pages/Transaction/TransactionCell/index.tsx +++ b/src/pages/Transaction/TransactionCell/index.tsx @@ -43,7 +43,7 @@ import { useDASAccount } from '../../../contexts/providers/dasQuery' import styles from './styles.module.scss' import AddressText from '../../../components/AddressText' -const Addr: FC<{ address: string; isCellBase: boolean }> = ({ address, isCellBase }) => { +export const Addr: FC<{ address: string; isCellBase: boolean }> = ({ address, isCellBase }) => { const alias = useDASAccount(address) const { t } = useTranslation() diff --git a/src/pages/Transaction/TransactionComp.tsx b/src/pages/Transaction/TransactionComp.tsx deleted file mode 100644 index 9419bb5fe..000000000 --- a/src/pages/Transaction/TransactionComp.tsx +++ /dev/null @@ -1,422 +0,0 @@ -/* eslint-disable react/no-array-index-key */ -import { useState, ReactNode, FC } from 'react' -import { Link } from 'react-router-dom' -import BigNumber from 'bignumber.js' -import { Trans, useTranslation } from 'react-i18next' -import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' -import { parseSimpleDate } from '../../utils/date' -import { localeNumberString } from '../../utils/number' -import { useFormatConfirmation, shannonToCkb, matchTxHash } from '../../utils/util' -import { - TransactionBlockHeightPanel, - TransactionInfoContentPanel, - TransactionOverviewPanel, - TransactionInfoContentItem, - TransactionInfoItemPanel, -} from './styled' -import TransactionCellList from './TransactionCellList' -import DecimalCapacity from '../../components/DecimalCapacity' -import ArrowUpIcon from '../../assets/arrow_up.png' -import ArrowDownIcon from '../../assets/arrow_down.png' -import ArrowUpBlueIcon from '../../assets/arrow_up_blue.png' -import ArrowDownBlueIcon from '../../assets/arrow_down_blue.png' -import { isMainnet } from '../../utils/chain' -import SimpleButton from '../../components/SimpleButton' -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()) ?? '-' - -const TransactionBlockHeight = ({ blockNumber, txStatus }: { blockNumber: number; txStatus: string }) => ( - - {txStatus === 'committed' ? ( - {localeNumberString(blockNumber)} - ) : ( - {showTxStatus(txStatus)} - )} - -) - -const transactionParamsIcon = (show: boolean) => { - if (show) { - return isMainnet() ? ArrowUpIcon : ArrowUpBlueIcon - } - return isMainnet() ? ArrowDownIcon : ArrowDownBlueIcon -} - -const TransactionInfoItem = ({ - title, - tooltip, - value, - valueTooltip, - linkUrl, - tag, -}: { - title?: string - tooltip?: string - value: string | ReactNode - valueTooltip?: string - linkUrl?: string - tag?: ReactNode -}) => ( - -
- {title ? ( - <> - {title} - {tooltip && } - : - - ) : ( - '' - )} -
-
-
- {linkUrl ? ( - - {value} - - ) : ( - value - )} - {valueTooltip && } -
- {tag &&
{tag}
} -
-
-) - -const TransactionInfoItemWrapper = ({ - title, - tooltip, - value, - linkUrl, -}: { - title?: string - tooltip?: string - value: string | ReactNode - linkUrl?: string -}) => ( - - - -) - -export const TransactionOverview: FC<{ transaction: State.Transaction }> = ({ transaction }) => { - const [showParams, setShowParams] = useState(false) - const tipBlockNumber = useLatestBlockNumber() - const { - blockNumber, - cellDeps, - headerDeps, - witnesses, - blockTimestamp, - transactionFee, - txStatus, - detailedMessage, - bytes, - largestTxInEpoch, - largestTx, - cycles, - maxCyclesInEpoch, - maxCycles, - } = transaction - const { t } = useTranslation() - const parseFormatConfirmation = useFormatConfirmation() - let confirmation = 0 - if (tipBlockNumber && blockNumber) { - confirmation = tipBlockNumber - blockNumber - } - - const OverviewItems: Array = [ - { - title: t('block.block_height'), - tooltip: t('glossary.block_height'), - content: , - }, - ] - if (txStatus === 'committed') { - if (confirmation >= 0) { - OverviewItems.push( - { - title: t('block.timestamp'), - tooltip: t('glossary.timestamp'), - content: parseSimpleDate(blockTimestamp), - }, - bytes - ? { - title: `${t('transaction.transaction_fee')} | ${t('transaction.fee_rate')}`, - content: ( -
- - {` | ${new BigNumber(transactionFee).multipliedBy(1000).dividedToIntegerBy(bytes).toFormat({ - groupSeparator: ',', - groupSize: 3, - })} shannons/kB`} -
- ), - } - : { - title: t('transaction.transaction_fee'), - content: , - }, - - { - title: t('transaction.status'), - tooltip: t('glossary.transaction_status'), - content: parseFormatConfirmation(confirmation), - }, - ) - } - } else { - OverviewItems.push( - { - title: t('block.timestamp'), - tooltip: t('glossary.timestamp'), - content: showTxStatus(txStatus), - }, - { - title: t('transaction.transaction_fee'), - content: , - }, - { - title: t('transaction.status'), - tooltip: t('glossary.transaction_status'), - content: showTxStatus(txStatus), - valueTooltip: txStatus === 'rejected' ? detailedMessage : undefined, - }, - ) - } - - OverviewItems.push( - { - title: t('transaction.size'), - content: bytes ? ( -
- {`${(bytes - 4).toLocaleString('en')} Bytes`} - - {t('transaction.size_in_block', { - bytes: bytes.toLocaleString('en'), - })} - -
- ) : ( - '' - ), - }, - null, - { - title: t('transaction.cycles'), - content: cycles ? ( -
- {`${cycles.toLocaleString('en')}`} - -
- ) : ( - '-' - ), - }, - ) - - const TransactionParams = [ - { - title: t('transaction.cell_deps'), - tooltip: ( - - ), - }} - /> - ), - content: - cellDeps && cellDeps.length > 0 ? ( - cellDeps.map(cellDep => { - const { - outPoint: { txHash, index }, - depType, - } = cellDep - const hashTag = matchTxHash(txHash, index) - return ( - - } - /> - - - - ) - }) - ) : ( - - ), - }, - { - title: t('transaction.header_deps'), - tooltip: t('glossary.header_deps'), - content: - headerDeps && headerDeps.length > 0 ? ( - headerDeps.map(headerDep => ( - - )) - ) : ( - - ), - }, - { - title: t('transaction.witnesses'), - tooltip: t('glossary.witnesses'), - content: - witnesses && witnesses.length > 0 ? ( - witnesses.map((witness, index) => ( - - )) - ) : ( - - ), - }, - ] - - return ( - - -
- setShowParams(!showParams)}> -
{t('transaction.transaction_parameters')}
- transaction parameters -
- {showParams && ( -
- {TransactionParams.map(item => ( - -
- {item.title} - {item.tooltip && } -
-
{item.content}
-
- ))} -
- )} -
-
-
- ) -} - -const handleCellbaseInputs = (inputs: State.Cell[], outputs: State.Cell[]) => { - if (inputs[0] && inputs[0].fromCellbase && outputs[0] && outputs[0].baseReward) { - const resultInputs = inputs - resultInputs[0] = { - ...resultInputs[0], - baseReward: outputs[0].baseReward, - secondaryReward: outputs[0].secondaryReward, - commitReward: outputs[0].commitReward, - proposalReward: outputs[0].proposalReward, - } - return resultInputs - } - return inputs -} - -export default ({ transaction }: { transaction: State.Transaction }) => { - const { transactionHash, displayInputs, displayOutputs, blockNumber, isCellbase } = transaction - - const { isNew: isAddrNew, setIsNew: setIsAddrNew } = useAddrFormatToggle() - const inputs = handleCellbaseInputs(displayInputs, displayOutputs) - - /// [0, 11] block doesn't show block reward and only cellbase show block reward - return ( - <> -
- {inputs && ( - 0 && isCellbase} - addrToggle={{ - isAddrNew, - setIsAddrNew, - }} - /> - )} -
-
- {displayOutputs && ( - - )} -
- - ) -} diff --git a/src/pages/Transaction/TransactionComp/TransactionComp.tsx b/src/pages/Transaction/TransactionComp/TransactionComp.tsx new file mode 100644 index 000000000..85a578974 --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionComp.tsx @@ -0,0 +1,54 @@ +import TransactionCellList from '../TransactionCellList' +import { useAddrFormatToggle } from '../../../utils/hook' + +const handleCellbaseInputs = (inputs: State.Cell[], outputs: State.Cell[]) => { + if (inputs[0] && inputs[0].fromCellbase && outputs[0] && outputs[0].baseReward) { + const resultInputs = inputs + resultInputs[0] = { + ...resultInputs[0], + baseReward: outputs[0].baseReward, + secondaryReward: outputs[0].secondaryReward, + commitReward: outputs[0].commitReward, + proposalReward: outputs[0].proposalReward, + } + return resultInputs + } + return inputs +} + +export const TransactionComp = ({ transaction }: { transaction: State.Transaction }) => { + const { transactionHash, displayInputs, displayOutputs, blockNumber, isCellbase } = transaction + + const { isNew: isAddrNew, setIsNew: setIsAddrNew } = useAddrFormatToggle() + const inputs = handleCellbaseInputs(displayInputs, displayOutputs) + + /// [0, 11] block doesn't show block reward and only cellbase show block reward + return ( + <> +
+ {inputs && ( + 0 && isCellbase} + addrToggle={{ + isAddrNew, + setIsAddrNew, + }} + /> + )} +
+
+ {displayOutputs && ( + + )} +
+ + ) +} diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.module.scss b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.module.scss new file mode 100644 index 000000000..4f2ee999a --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.module.scss @@ -0,0 +1,16 @@ +.transactionBadge { + font-size: 12px; + line-height: 100%; + padding: 5px 8px; + border-radius: 4px; + border: 1px solid #b0cbfc; + background: #d7e5fd; + + @media (width <= 750px) { + margin-left: 12px; + } +} + +.tootip { + text-align: center; +} diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx new file mode 100644 index 000000000..0eec5ecd6 --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionBadge.tsx @@ -0,0 +1,40 @@ +import { Tooltip } from 'antd' +import styles from './TransactionBadge.module.scss' + +type Props = { + cellType: State.CellType + capacity?: string +} +const cellTypeDisplayMap: Record = { + normal: '', + udt: '', + nervos_dao_deposit: 'Nervos DAO Deposit', + nervos_dao_withdrawing: 'Nervos DAO Withdraw', + spore_cell: '', + spore_cluster: '', + cota_regular: '', + cota_registry: '', + m_nft_issuer: '', + m_nft_class: '', + m_nft_token: '', + nrc_721_token: '', + nrc_721_factory: '', +} + +export const TransactionBadge = ({ cellType, capacity }: Props) => { + const displayName = cellTypeDisplayMap[cellType] + if (!displayName) return null + + return ( + +
{displayName}
+
{capacity} CKB
+
+ } + > +
{displayName}
+
+ ) +} diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.module.scss b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.module.scss new file mode 100644 index 000000000..54dfb63d4 --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.module.scss @@ -0,0 +1,141 @@ +.transactionLiteBox { + .transactionLiteBoxHeader { + width: 100%; + height: auto; + margin-bottom: 16px; + overflow-x: hidden; + + .transactionLiteBoxHeaderAddr { + width: 260px; + max-width: 400px; + display: inline-block; + + div { + color: var(--primary-color); + } + } + + @media (width <= 750px) { + .transactionLiteBoxHeaderAddr { + width: 200px; + display: inline-block; + } + } + + .tag { + background: #e8fff1; + color: var(--primary-color); + font-size: 12px; + padding: 2px 6px; + box-shadow: 0 4px 4px rgb(16 16 16 / 5%); + border: 1px solid #caffdf; + border-radius: 4px; + display: inline-block; + margin-left: 8px; + } + } + + .transactionLiteBoxContent { + width: 100%; + height: auto; + + div { + display: inline-block; + } + + .nftId, + .add { + color: var(--primary-color); + } + + .subtraction { + color: var(--accent-color); + } + + .addressDetailLite { + text-align: right; + font-size: 14px; + } + + .capacityChange { + margin-left: 12px; + height: 24px; + + @media (width <= 750px) { + margin-left: 0; + } + } + + & > div { + width: 100%; + height: 22px; + margin-bottom: 12px; + display: flex; + + div { + flex: 1; + + &:first-child { + text-align: left; + color: #333; + } + + .tag { + box-sizing: border-box; + background: #d7e5fd; + padding: 2px 6px; + border: 1px solid #b0cbfc; + border-radius: 4px; + font-size: 12px; + color: #333; + display: inline-block; + margin-right: 12px; + } + } + } + } + + .transactionLiteBoxContentMobile { + width: 100%; + height: auto; + + div { + display: inline-block; + color: var(--primary-color); + } + + .transactionLiteMobileName { + p { + font-size: 16px; + color: var(--primary-text-color); + } + } + + .transactionLiteMobileContent { + &:first-child { + text-align: left; + } + + &:last-child { + text-align: right; + } + } + + .tag { + box-sizing: border-box; + background: #d7e5fd; + padding: 2px 6px; + border: 1px solid #b0cbfc; + border-radius: 4px; + font-size: 12px; + color: #333; + margin-right: 12px; + float: left; + } + + & > div { + width: 100%; + height: auto; + } + } +} diff --git a/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx new file mode 100644 index 000000000..35641a5b0 --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionLite/TransactionLite.tsx @@ -0,0 +1,152 @@ +/* eslint-disable react/no-array-index-key */ +import { FC } from 'react' +import { useQuery } from 'react-query' +import { useParams } from 'react-router-dom' +import BigNumber from 'bignumber.js' +import styles from './TransactionLite.module.scss' +import DecimalCapacity from '../../../../components/DecimalCapacity' +import { parseCKBAmount, localeNumberString } from '../../../../utils/number' +import { shannonToCkb } from '../../../../utils/util' +import { Addr } from '../../TransactionCell' +import { defaultTransactionLiteDetails } from '../../state' +import { TransactionBadge } from './TransactionBadge' +import { fetchTransactionLiteDetailsByHash } from '../../../../services/ExplorerService/fetcher' +import { useIsMobile } from '../../../../utils/hook' + +const getTransferItemTag = (transfer: State.LiteTransfer) => { + const { cellType, udtInfo, mNftInfo } = transfer + if (cellType === 'm_nft_token' || cellType === 'm_nft_class' || cellType === 'm_nft_issuer') { + return `NFT-${mNftInfo?.className ?? 'Unknown'}` + } + if (cellType === 'udt') { + return udtInfo?.symbol || `Uknown Asset #${udtInfo?.typeHash.substring(udtInfo.typeHash.length - 4)}` + } + if (cellType === 'spore_cell' || cellType === 'spore_cluster') { + return 'Spore' + } + if (cellType === 'cota_regular' || cellType === 'cota_registry') { + return 'Cota' + } + if (cellType === 'nervos_dao_deposit' || cellType === 'nervos_dao_withdrawing') { + return 'Nervos DAO' + } + if (cellType === 'nrc_721_token' || cellType === 'nrc_721_factory') { + return 'NRC-721' + } + return 'CKB' +} + +export const TransactionCompLite: FC<{ isCellbase: boolean }> = ({ isCellbase }) => { + const { hash: txHash } = useParams<{ hash: string }>() + const isMobile = useIsMobile() + + const query = useQuery(['ckb_transaction_details', txHash], async () => { + const ckbTransactionDetails = await fetchTransactionLiteDetailsByHash(txHash) + return ckbTransactionDetails.data + }) + const transactionLiteDetails: State.TransactionLiteDetails[] = query.data ?? defaultTransactionLiteDetails + return ( + <> + {transactionLiteDetails && + transactionLiteDetails.map(item => ( +
+
+
+
+ +
+
+ {isMobile ? : } +
+
+ ))} + + ) +} + +export const DesktopTransferItems = (props: { details: State.TransactionLiteDetails }) => { + const { details } = props + const { transfers } = details + return ( +
+ {transfers.map((transfer, index) => { + return ( +
+
{getTransferItemTag(transfer)}
+
+ + +
+
+ ) + })} +
+ ) +} + +export const MobileTransferItems = (props: { details: State.TransactionLiteDetails }) => { + const { details } = props + const { transfers } = details + return ( +
+ {transfers.map((transfer, index) => { + return ( +
+
{getTransferItemTag(transfer)}
+
+ ) + })} + {transfers.map((transfer, index) => { + return ( +
+
+ + +
+
+ ) + })} +
+ ) +} + +const TransferAmount: FC<{ transfer: State.LiteTransfer }> = ({ transfer }) => { + const isUdt = transfer.cellType === 'udt' + const isNft = transfer.cellType === 'm_nft_token' + + const transferCapacity = new BigNumber(transfer.capacity) + const transferAmount = new BigNumber(transfer.udtInfo?.amount ?? 0) + const isIncome = isUdt ? transferAmount.isPositive() : transferCapacity.isPositive() + const decimalPanelType = isIncome ? 'income' : 'payment' + + const amountChange = localeNumberString(shannonToCkb(transferAmount)) + const capacityChange = localeNumberString(shannonToCkb(transferCapacity)) + const isIncomeColor = isIncome ? styles.add : styles.subtraction + + const getUdtComponent = () => { + if (isUdt) { + return ( + <> + +
{`(${capacityChange} CKB)`}
+ + ) + } + if (isNft) { + return ( +
+ {isIncome ? '' : '-'} + ID: {transfer.mNftInfo?.tokenId ?? 'Unknown'} + {`(${capacityChange} CKB)`} +
+ ) + } + return + } + return ( +
+ {isIncome ? '+' : ''} + {getUdtComponent()} +
+ ) +} diff --git a/src/pages/Transaction/TransactionComp/TransactionOverview.tsx b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx new file mode 100644 index 000000000..67f4a5821 --- /dev/null +++ b/src/pages/Transaction/TransactionComp/TransactionOverview.tsx @@ -0,0 +1,389 @@ +/* eslint-disable react/no-array-index-key */ +import { useState, ReactNode, FC } from 'react' +import { Link } from 'react-router-dom' +import BigNumber from 'bignumber.js' +import { Trans, useTranslation } from 'react-i18next' +import OverviewCard, { OverviewItemData } from '../../../components/Card/OverviewCard' +import DecimalCapacity from '../../../components/DecimalCapacity' +import HashTag from '../../../components/HashTag' +import { HelpTip } from '../../../components/HelpTip' +import SimpleButton from '../../../components/SimpleButton' +import ComparedToMaxTooltip from '../../../components/Tooltip/ComparedToMaxTooltip' +import { LayoutLiteProfessional } from '../../../constants/common' +import { isMainnet } from '../../../utils/chain' +import { parseSimpleDate } from '../../../utils/date' +import ArrowUpIcon from '../../../assets/arrow_up.png' +import ArrowDownIcon from '../../../assets/arrow_down.png' +import ArrowUpBlueIcon from '../../../assets/arrow_up_blue.png' +import ArrowDownBlueIcon from '../../../assets/arrow_down_blue.png' +import { localeNumberString } from '../../../utils/number' +import { shannonToCkb, useFormatConfirmation, matchTxHash } from '../../../utils/util' +import { + TransactionBlockHeightPanel, + TransactionInfoContentItem, + TransactionInfoContentPanel, + TransactionOverviewPanel, + TransactionInfoItemPanel, +} from './styled' +import { useLatestBlockNumber } from '../../../services/ExplorerService' + +const showTxStatus = (txStatus: string) => txStatus?.replace(/^\S/, s => s.toUpperCase()) ?? '-' +const TransactionBlockHeight = ({ blockNumber, txStatus }: { blockNumber: number; txStatus: string }) => ( + + {txStatus === 'committed' ? ( + {localeNumberString(blockNumber)} + ) : ( + {showTxStatus(txStatus)} + )} + +) + +const transactionParamsIcon = (show: boolean) => { + if (show) { + return isMainnet() ? ArrowUpIcon : ArrowUpBlueIcon + } + return isMainnet() ? ArrowDownIcon : ArrowDownBlueIcon +} + +const TransactionInfoItem = ({ + title, + tooltip, + value, + valueTooltip, + linkUrl, + tag, +}: { + title?: string + tooltip?: string + value: string | ReactNode + valueTooltip?: string + linkUrl?: string + tag?: ReactNode +}) => ( + +
+ {title ? ( + <> + {title} + {tooltip && } + : + + ) : ( + '' + )} +
+
+
+ {linkUrl ? ( + + {value} + + ) : ( + value + )} + {valueTooltip && } +
+ {tag &&
{tag}
} +
+
+) + +const TransactionInfoItemWrapper = ({ + title, + tooltip, + value, + linkUrl, +}: { + title?: string + tooltip?: string + value: string | ReactNode + linkUrl?: string +}) => ( + + + +) + +export const TransactionOverview: FC<{ transaction: State.Transaction; layout: LayoutLiteProfessional }> = ({ + transaction, + layout, +}) => { + const [showParams, setShowParams] = useState(false) + const tipBlockNumber = useLatestBlockNumber() + const { + blockNumber, + cellDeps, + headerDeps, + witnesses, + blockTimestamp, + transactionFee, + txStatus, + detailedMessage, + bytes, + largestTxInEpoch, + largestTx, + cycles, + maxCyclesInEpoch, + maxCycles, + } = transaction + + const { t } = useTranslation() + const formatConfirmation = useFormatConfirmation() + let confirmation = 0 + const isProfessional = layout === LayoutLiteProfessional.Professional + + if (tipBlockNumber && blockNumber) { + confirmation = tipBlockNumber - blockNumber + } + + const blockHeightData: OverviewItemData = { + title: t('block.block_height'), + tooltip: t('glossary.block_height'), + content: , + } + const timestampData: OverviewItemData = { + title: t('block.timestamp'), + tooltip: t('glossary.timestamp'), + content: parseSimpleDate(blockTimestamp), + } + const feeWithFeeRateData: OverviewItemData = { + title: `${t('transaction.transaction_fee')} | ${t('transaction.fee_rate')}`, + content: ( +
+ + {` | ${new BigNumber(transactionFee).multipliedBy(1000).dividedToIntegerBy(bytes).toFormat({ + groupSeparator: ',', + groupSize: 3, + })} shannons/kB`} +
+ ), + } + const txFeeData: OverviewItemData = { + title: t('transaction.transaction_fee'), + content: , + } + const txStatusData: OverviewItemData = { + title: t('transaction.status'), + tooltip: t('glossary.transaction_status'), + content: formatConfirmation(confirmation), + } + + const liteTxSizeDataContent = bytes ? ( +
+ {`${(bytes - 4).toLocaleString('en')} Bytes`} + + {t('transaction.size_in_block', { + bytes: bytes.toLocaleString('en'), + })} + +
+ ) : ( + '' + ) + const liteTxSizeData: OverviewItemData = { + title: t('transaction.size'), + content: liteTxSizeDataContent, + } + const liteTxCyclesDataContent = cycles ? ( +
+ {`${cycles.toLocaleString('en')}`} + +
+ ) : ( + '-' + ) + const liteTxCyclesData: OverviewItemData = { + title: t('transaction.cycles'), + content: liteTxCyclesDataContent, + } + const overviewItems: Array = [] + if (txStatus === 'committed') { + overviewItems.push(blockHeightData, timestampData) + if (confirmation >= 0) { + if (isProfessional) { + overviewItems.push(bytes ? feeWithFeeRateData : txFeeData, txStatusData) + } else { + overviewItems.push(txStatusData) + } + } + } else if (txStatus === 'rejected') { + overviewItems.push( + blockHeightData, + { + ...timestampData, + content: 'Rejected', + }, + { + ...txStatusData, + content: 'Rejected', + valueTooltip: detailedMessage, + }, + ) + } else { + // pending + overviewItems.push( + { + ...blockHeightData, + content: '···', + }, + { + ...timestampData, + content: '···', + }, + { + ...txStatusData, + content: 'Pending', + }, + ) + } + if (isProfessional) { + overviewItems.push(liteTxSizeData, liteTxCyclesData) + } + const TransactionParams = [ + { + title: t('transaction.cell_deps'), + tooltip: ( + + ), + }} + /> + ), + content: + cellDeps && cellDeps.length > 0 ? ( + cellDeps.map(cellDep => { + const { + outPoint: { txHash, index }, + depType, + } = cellDep + const hashTag = matchTxHash(txHash, index) + return ( + + } + /> + + + + ) + }) + ) : ( + + ), + }, + { + title: t('transaction.header_deps'), + tooltip: t('glossary.header_deps'), + content: + headerDeps && headerDeps.length > 0 ? ( + headerDeps.map(headerDep => ( + + )) + ) : ( + + ), + }, + { + title: t('transaction.witnesses'), + tooltip: t('glossary.witnesses'), + content: + witnesses && witnesses.length > 0 ? ( + witnesses.map((witness, index) => ( + + )) + ) : ( + + ), + }, + ] + + return ( + + + {isProfessional && ( +
+ setShowParams(!showParams)}> +
{t('transaction.transaction_parameters')}
+ transaction parameters +
+ {showParams && ( +
+ {TransactionParams.map(item => ( + +
+ {item.title} + {item.tooltip && } +
+
{item.content}
+
+ ))} +
+ )} +
+ )} +
+
+ ) +} diff --git a/src/pages/Transaction/styled.tsx b/src/pages/Transaction/TransactionComp/styled.tsx similarity index 92% rename from src/pages/Transaction/styled.tsx rename to src/pages/Transaction/TransactionComp/styled.tsx index fe7d5f84c..375958425 100644 --- a/src/pages/Transaction/styled.tsx +++ b/src/pages/Transaction/TransactionComp/styled.tsx @@ -22,6 +22,19 @@ export const TransactionDiv = styled.div.attrs({ width: 100%; margin-top: 20px; } + + .transactionLite { + width: 100%; + border-radius: 6px; + box-shadow: 2px 2px 6px 0 #dfdfdf; + background-color: #fff; + margin-bottom: 10px; + padding: 16px 36px 12px; + + @media (max-width: 750px) { + padding: 16px 18px; + } + } ` export const TransactionOverviewPanel = styled.div` diff --git a/src/pages/Transaction/index.tsx b/src/pages/Transaction/index.tsx index 4c4ef94d1..ad7511194 100644 --- a/src/pages/Transaction/index.tsx +++ b/src/pages/Transaction/index.tsx @@ -3,13 +3,18 @@ import { useQuery } from 'react-query' import { useTranslation } from 'react-i18next' import TransactionHashCard from '../../components/Card/HashCard' import Content from '../../components/Content' -import { TransactionDiv as TransactionPanel } from './styled' -import TransactionComp, { TransactionOverview } from './TransactionComp' +import { TransactionDiv as TransactionPanel } from './TransactionComp/styled' import { explorerService } from '../../services/ExplorerService' import { QueryResult } from '../../components/QueryResult' import { defaultTransactionInfo } from './state' +import { useSearchParams } from '../../utils/hook' +import { LayoutLiteProfessional } from '../../constants/common' +import { TransactionCompLite } from './TransactionComp/TransactionLite/TransactionLite' +import { TransactionComp } from './TransactionComp/TransactionComp' +import { TransactionOverview } from './TransactionComp/TransactionOverview' export default () => { + const { Professional, Lite } = LayoutLiteProfessional const { t } = useTranslation() const { hash: txHash } = useParams<{ hash: string }>() @@ -21,18 +26,29 @@ export default () => { } return transaction }) + const transaction = query.data ?? defaultTransactionInfo const { blockTimestamp, txStatus } = transaction + const searchParams = useSearchParams('layout') + const layout = searchParams.layout === Lite ? Lite : Professional return ( - {txStatus !== 'committed' || blockTimestamp > 0 ? : null} + {txStatus !== 'committed' || blockTimestamp > 0 ? ( + + ) : null} - - {data => } - + {layout === Professional ? ( + + {transaction => } + + ) : ( + + {transaction => } + + )} ) diff --git a/src/pages/Transaction/state.ts b/src/pages/Transaction/state.ts index 17b87938e..b00f3d128 100644 --- a/src/pages/Transaction/state.ts +++ b/src/pages/Transaction/state.ts @@ -23,3 +23,10 @@ export const defaultTransactionInfo: State.Transaction = { maxCyclesInEpoch: null, maxCycles: null, } + +export const defaultTransactionLiteDetails: State.TransactionLiteDetails[] = [ + { + address: '', + transfers: [], + }, +] diff --git a/src/services/ExplorerService/fetcher.ts b/src/services/ExplorerService/fetcher.ts index 832a4b9f8..18310791c 100644 --- a/src/services/ExplorerService/fetcher.ts +++ b/src/services/ExplorerService/fetcher.ts @@ -51,6 +51,11 @@ export const fetchTransactionByHash = (hash: string) => .get(`transactions/${hash}`) .then((res: AxiosResponse) => toCamelcase>(res.data.data)) +export const fetchTransactionLiteDetailsByHash = (hash: string) => + requesterV2 + .get(`ckb_transactions/${hash}/details`) + .then((res: AxiosResponse) => toCamelcase>(res.data)) + export const fetchTransactions = (page: number, size: number, sort?: string) => requesterV1 .get('transactions', { diff --git a/src/types/index.d.ts b/src/types/index.d.ts index ccdbef7c0..652548922 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -100,6 +100,9 @@ declare namespace State { | 'nervos_dao_withdrawing' | 'cota_registry' | 'cota_regular' + | 'nft_transfer' + | 'simple_transfer' + | 'nft_mint' | 'spore_cluster' | 'spore_cell' extraInfo?: never @@ -132,6 +135,47 @@ declare namespace State { type Cell = Cell$NoExtra | Cell$UDT | Cell$NftIssuer | Cell$NftClass | Cell$NftToken | Cell$Nrc721Token + export interface TransactionLiteDetails { + address: string + transfers: LiteTransfer[] + } + + // cell_type comes from: https://github.com/nervosnetwork/ckb-explorer/blob/develop/app/utils/ckb_utils.rb#L380 + type CellType = + | 'normal' + | 'udt' + | 'nervos_dao_deposit' + | 'nervos_dao_withdrawing' + | 'spore_cell' + | 'spore_cluster' + | 'cota_regular' + | 'cota_registry' + | 'm_nft_issuer' + | 'm_nft_class' + | 'm_nft_token' + | 'nrc_721_token' + | 'nrc_721_factory' + interface LiteTransfer { + capacity: string + cellType: CellType + + udtInfo?: { + symbol: string + amount: string + decimal: string + typeHash: string + published: boolean + displayName: string + uan: string + } + + mNftInfo?: { + className: string + tokenId: string // none 0x prefix hex number + total: string // decimal string + } + } + export interface LockInfo { status: 'locked' | 'unlocked' epochNumber: string diff --git a/src/utils/number.ts b/src/utils/number.ts index f35d27b61..e96fc7e46 100644 --- a/src/utils/number.ts +++ b/src/utils/number.ts @@ -67,9 +67,13 @@ export const handleHashRate = (value: BigNumber | string | number) => { return `${handleDifficulty(value)}/s` } -export const parseUDTAmount = (amount: string, decimal: string) => { +export const parseCKBAmount = (capacity: string) => { + return parseUDTAmount(capacity, 8) +} + +export const parseUDTAmount = (amount: string, decimal: string | number) => { try { - const decimalInt = parseInt(decimal, 10) + const decimalInt = typeof decimal === 'string' ? parseInt(decimal, 10) : decimal const amountBigInt = new BigNumber(amount) const result = amountBigInt.dividedBy(new BigNumber(10).pow(decimalInt)) if (decimalInt > 20) { diff --git a/src/utils/util.ts b/src/utils/util.ts index c8c2f6d1c..1e7ceba87 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -332,6 +332,12 @@ export function randomInt(min: number, max: number) { return min + Math.floor(Math.random() * (max - min + 1)) } +export const isDaoDepositCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_deposit' + +export const isDaoWithdrawCell = (cellType: State.CellTypes) => cellType === 'nervos_dao_withdrawing' + +export const isDaoCell = (cellType: State.CellTypes) => isDaoDepositCell(cellType) || isDaoWithdrawCell(cellType) + export default { copyElementValue, shannonToCkb,