From b1af1fb4edd2fe2457e3a52d20adbb46c705eefd Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 9 Aug 2019 13:05:38 +0800 Subject: [PATCH] feat: display recent activities in sentence style --- .../src/components/Overview/index.tsx | 203 +++++++++++------- packages/neuron-ui/src/locales/en.json | 11 +- packages/neuron-ui/src/locales/zh.json | 11 +- .../neuron-ui/src/states/initStates/chain.ts | 2 +- .../neuron-ui/src/stories/History.stories.tsx | 3 +- .../src/stories/Overview.stories.tsx | 12 +- .../src/stories/data/transactions.ts | 68 +++++- packages/neuron-ui/src/theme.tsx | 5 + packages/neuron-ui/src/types/App/index.d.ts | 3 +- packages/neuron-ui/src/utils/const.ts | 1 + 10 files changed, 229 insertions(+), 90 deletions(-) diff --git a/packages/neuron-ui/src/components/Overview/index.tsx b/packages/neuron-ui/src/components/Overview/index.tsx index dc868d7ee9..0ddff0bad2 100644 --- a/packages/neuron-ui/src/components/Overview/index.tsx +++ b/packages/neuron-ui/src/components/Overview/index.tsx @@ -8,6 +8,7 @@ import { DefaultButton, DetailsList, DetailsRow, + Icon, IColumn, CheckboxVisibility, DetailsListLayoutMode, @@ -24,66 +25,96 @@ import PropertyList, { Property } from 'widgets/PropertyList' import { StateWithDispatch } from 'states/stateProvider/reducer' import { updateTransactionList, addPopup } from 'states/stateProvider/actionCreators' -import { showErrorMessage } from 'services/remote' +import { showTransactionDetails, showErrorMessage } from 'services/remote' import { localNumberFormatter, shannonToCKBFormatter, uniformTimeFormatter as timeFormatter } from 'utils/formatters' -import { PAGE_SIZE } from 'utils/const' +import { PAGE_SIZE, Routes, CONFIRMATION_THRESHOLD } from 'utils/const' const TITLE_FONT_SIZE = 'xxLarge' +export type ActivityItem = State.Transaction & { confirmations: string; typeLabel: string } + +const genTypeLabel = (type: 'send' | 'receive', confirmationCount: number) => { + switch (type) { + case 'send': { + if (confirmationCount < CONFIRMATION_THRESHOLD) { + return 'sending' + } + return 'sent' + } + case 'receive': { + if (confirmationCount < CONFIRMATION_THRESHOLD) { + return 'receiving' + } + return 'received' + } + default: { + return type + } + } +} const ActivityList = ({ columns, items, + onGoToHistory, + t, ...props }: React.PropsWithoutRef<{ columns: IColumn[] items: any + onGoToHistory: any + t: any isHeaderVisible?: boolean onRenderRow?: IRenderFunction [index: string]: any }>) => ( - + { + showTransactionDetails(item.hash) + }} + styles={{ + root: { + backgroundColor: 'transparent', }, - }, - contentWrapper: { - selectors: { - '.ms-DetailsRow': { - backgroundColor: 'transparent', - }, - '.ms-DetailsRow-cell': { - fontSize: FontSizes.mediumPlus, + contentWrapper: { + selectors: { + '.ms-DetailsRow': { + backgroundColor: 'transparent', + }, + '.ms-DetailsRow-cell': { + fontSize: FontSizes.mediumPlus, + }, }, }, - }, - }} - {...props} - /> + }} + {...props} + /> + {items.length > 10 ? ( + + + {t('overview.more')} + + + ) : null} + ) const Overview = ({ dispatch, app: { tipBlockNumber, chain, epoch, difficulty }, wallet: { id, name, balance = '', addresses = [] }, chain: { + tipBlockNumber: syncedBlockNumber, codeHash = '', transactions: { items = [] }, }, + history, }: React.PropsWithoutRef) => { const [t] = useTranslation() const [displayBlockchainInfo, setDisplayBlockchainInfo] = useState(false) @@ -100,6 +131,9 @@ const Overview = ({ walletID: id, })(dispatch) }, [id, dispatch]) + const onGoToHistory = useCallback(() => { + history.push(Routes.History) + }, [history]) const onTransactionRowRender = useCallback((props?: IDetailsRowProps) => { if (props) { @@ -108,29 +142,29 @@ const Overview = ({ animationDuration: '0!important', }, } - if (props.item.status === 'failed') { - customStyles.root = { - animationDuration: '0!important', - color: 'red', - } - } return } return null }, []) - const onTransactionTypeRender = useCallback((item?: any) => { + const onTransactionActivityRender = useCallback((item?: ActivityItem) => { if (item) { return ( - - {item.type} - + <> + + {`${item.typeLabel} ${shannonToCKBFormatter(item.value)} CKB`} + + + {item.confirmations} + + ) } return null @@ -149,6 +183,24 @@ const Overview = ({ const activityColumns: IColumn[] = useMemo(() => { return [ + { + key: 'status', + name: t('overview.status'), + minWidth: 20, + maxWidth: 20, + onRender: (item?: State.Transaction) => { + if (!item) { + return null + } + let iconName = 'TransactionPending' + if (item.status === 'success') { + iconName = 'TransactionSuccess' + } else if (item.status === 'failed') { + iconName = 'TransactionFailure' + } + return + }, + }, { key: 'timestamp', name: t('overview.datetime'), @@ -157,30 +209,11 @@ const Overview = ({ onRender: onTimestampRender, }, { - key: 'type', - name: t('overview.type'), + key: 'activity', + name: t('overview.activity'), minWidth: 100, - maxWidth: 100, - onRender: onTransactionTypeRender, - }, - { - key: 'status', - name: t('overview.status'), - minWidth: 100, - maxWidth: 100, - }, - { - key: 'value', - name: t('overview.amount'), - title: 'value', - minWidth: 100, - maxWidth: 500, - onRender: (item?: State.Transaction) => { - if (item) { - return {`${shannonToCKBFormatter(item.value)} CKB`} - } - return '-' - }, + maxWidth: 600, + onRender: onTransactionActivityRender, }, ].map( (col): IColumn => ({ @@ -189,7 +222,7 @@ const Overview = ({ ...col, }) ) - }, [t, onTimestampRender, onTransactionTypeRender]) + }, [t, onTimestampRender, onTransactionActivityRender]) const balanceProperties: Property[] = useMemo( () => [ @@ -249,6 +282,24 @@ const Overview = ({ } }, [defaultAddress, t, hideMinerInfo, dispatch]) + const activityItems = useMemo( + () => + items.map(item => { + let confirmations = '(-)' + let typeLabel: string = item.type + if (item.blockNumber !== undefined) { + const confirmationCount = 1 + Math.max(+syncedBlockNumber, +tipBlockNumber) - +item.blockNumber + typeLabel = genTypeLabel(item.type, confirmationCount) + confirmations = + confirmationCount > 1 + ? `(${t('overview.confirmations', { confirmationCount: localNumberFormatter(confirmationCount) })})` + : `(${t('overview.confirmation', { confirmationCount: localNumberFormatter(confirmationCount) })})` + } + return { ...item, confirmations, typeLabel: t(`overview.${typeLabel}`) } + }), + [items, t, syncedBlockNumber, tipBlockNumber] + ) + return ( @@ -273,7 +324,13 @@ const Overview = ({ {t('overview.recent-activities')} {items.length ? ( - + ) : (
{t('overview.no-recent-activities')}
)} diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index eec771eccc..3f35f21808 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -16,7 +16,7 @@ "balance": "Balance", "recent-activities": "Recent Activities", "no-recent-activities": "No Recent Activities", - "type": "Type", + "activity": "Activity", "datetime": "Date & Time", "status": "Status", "amount": "Amount", @@ -29,7 +29,14 @@ "lock-arg": "Lock Arg", "address": "Address", "code-hash": "Code Hash", - "copy-pubkey-hash": "Copy Lock Arg (Pubkey Hash)" + "copy-pubkey-hash": "Copy Lock Arg (Pubkey Hash)", + "confirmation": "{{confirmationCount}} Confirmation", + "confirmations": "{{confirmationCount}} Confirmations", + "sent": "Sent", + "sending": "Sending", + "received": "Received", + "receiving": "Receiving", + "more": "More Transactions" }, "wizard": { "welcome-to-nervos-neuron": "Welcome to Neuron", diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index d2cba99e1f..1a8750e249 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -16,7 +16,7 @@ "balance": "余额", "recent-activities": "近期活动", "no-recent-activities": "近期没有活动", - "type": "类型", + "activity": "行为", "datetime": "时间", "status": "状态", "amount": "金额", @@ -29,7 +29,14 @@ "lock-arg": "Lock Arg", "address": "地址", "code-hash": "Code Hash", - "copy-pubkey-hash": "复制 Lock Arg (Pubkey Hash)" + "copy-pubkey-hash": "复制 Lock Arg (Pubkey Hash)", + "confirmation": "已确认 {{confirmationCount}} 次数", + "confirmations": "已确认 {{confirmationCount}} 次数", + "sent": "已发送", + "sending": "发送", + "received": "已收款", + "receiving": "收款", + "more": "查看更多交易" }, "wizard": { "welcome-to-nervos-neuron": "欢迎使用 Neuron", diff --git a/packages/neuron-ui/src/states/initStates/chain.ts b/packages/neuron-ui/src/states/initStates/chain.ts index 00b0e59484..a45f93cae4 100644 --- a/packages/neuron-ui/src/states/initStates/chain.ts +++ b/packages/neuron-ui/src/states/initStates/chain.ts @@ -4,7 +4,7 @@ import { ConnectionStatus } from 'utils/const' export const transactionState: State.DetailedTransaction = { value: '', hash: '', - type: 'other', + type: 'receive', createdAt: '0', updatedAt: '0', timestamp: '0', diff --git a/packages/neuron-ui/src/stories/History.stories.tsx b/packages/neuron-ui/src/stories/History.stories.tsx index 7bfdfd7f85..59cfd4e513 100644 --- a/packages/neuron-ui/src/stories/History.stories.tsx +++ b/packages/neuron-ui/src/stories/History.stories.tsx @@ -122,13 +122,14 @@ stories.addDecorator(withKnobs).add('With knobs', () => { pageSize: number('Page Size', 15), totalCount: number('Total Count', 200), items: transactions['Content List'].map((tx, idx) => ({ - type: text(`${idx}-Type`, tx.type) as 'send' | 'receive' | 'other', + type: text(`${idx}-Type`, tx.type) as 'send' | 'receive', createdAt: text(`${idx}-Created at`, tx.createdAt), updatedAt: text(`${idx}-Updated at`, tx.updatedAt), timestamp: text(`${idx}-Timestamp`, tx.timestamp), value: text(`${idx}-Value`, tx.value), hash: text(`${idx}-Hash`, tx.hash), description: text(`${idx}-Description`, tx.description), + blockNumber: text(`${idx}-BlockNumber`, tx.blockNumber), status: text(`${idx}-Status`, tx.status) as 'pending' | 'success' | 'failed', })), keywords: '', diff --git a/packages/neuron-ui/src/stories/Overview.stories.tsx b/packages/neuron-ui/src/stories/Overview.stories.tsx index c110397828..40055d0a15 100644 --- a/packages/neuron-ui/src/stories/Overview.stories.tsx +++ b/packages/neuron-ui/src/stories/Overview.stories.tsx @@ -49,7 +49,14 @@ const states = { ...stateTemplate, chain: { ...stateTemplate.chain, transactions: { ...stateTemplate.chain.transactions, items: [] } }, }, - 'Has Activities': stateTemplate, + 'Has 10 Activities': { + ...stateTemplate, + chain: { + ...stateTemplate.chain, + transactions: { ...stateTemplate.chain.transactions, items: stateTemplate.chain.transactions.items.slice(0, 10) }, + }, + }, + 'Has more than 10 Activities': stateTemplate, } const OverviewWithRouteProps = (props: StateWithDispatch) => ( @@ -84,13 +91,14 @@ stories.addDecorator(withKnobs).add('With knobs', () => { transactions: { ...initStates.chain.transactions, items: transactions[`Content List`].map((tx, idx) => ({ - type: text(`${idx}-Type`, tx.type) as 'send' | 'receive' | 'other', + type: text(`${idx}-Type`, tx.type) as 'send' | 'receive', createdAt: text(`${idx}-Created at`, tx.createdAt), updatedAt: text(`${idx}-Updated at`, tx.updatedAt), timestamp: text(`${idx}-Timestamp`, tx.timestamp), value: text(`${idx}-Value`, tx.value), hash: text(`${idx}-Hash`, tx.hash), description: text(`${idx}-Description`, tx.description), + blockNumber: text(`${idx}-BlockNumber`, tx.blockNumber), status: text(`${idx}-Status`, tx.status) as 'pending' | 'success' | 'failed', })), }, diff --git a/packages/neuron-ui/src/stories/data/transactions.ts b/packages/neuron-ui/src/stories/data/transactions.ts index 07ccb40751..6de401e1b1 100644 --- a/packages/neuron-ui/src/stories/data/transactions.ts +++ b/packages/neuron-ui/src/stories/data/transactions.ts @@ -10,7 +10,8 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab61', - description: 'description of sending transaction', + description: 'description of send transaction', + blockNumber: '120', status: 'pending', }, { @@ -20,7 +21,8 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab62', - description: 'description of receiving transaction', + description: 'description of receive transaction', + blockNumber: '120', status: 'pending', }, { @@ -30,7 +32,8 @@ const transactions: { timestamp: (new Date(1565240655845).getTime() - 300000).toString(), value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab63', - description: 'description of sending transaction', + description: 'description of send transaction', + blockNumber: '0', status: 'success', }, { @@ -40,7 +43,8 @@ const transactions: { timestamp: (new Date(1565240655845).getTime() - 400000).toString(), value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab64', - description: 'description of receiving transaction', + description: 'description of receive transaction', + blockNumber: '0', status: 'success', }, { @@ -50,7 +54,8 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab65', - description: 'description of sending transaction', + description: 'description of send transaction', + blockNumber: '0', status: 'failed', }, { @@ -60,7 +65,8 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab66', - description: 'description of receiving transaction', + description: 'description of receive transaction', + blockNumber: '0', status: 'failed', }, { @@ -70,7 +76,8 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab67', - description: 'description of sending transaction', + description: 'description of send transaction', + blockNumber: '0', status: 'failed', }, { @@ -80,7 +87,52 @@ const transactions: { timestamp: '', value: '10000', hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab68', - description: 'description of receiving transaction', + description: 'description of receive transaction', + blockNumber: '0', + status: 'failed', + }, + { + type: 'send', + createdAt: new Date('2019-04-18').getTime().toString(), + updatedAt: '', + timestamp: '', + value: '10000', + hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab69', + description: 'description of send transaction', + blockNumber: '0', + status: 'failed', + }, + { + type: 'receive', + createdAt: new Date('2019-04-18').getTime().toString(), + updatedAt: '', + timestamp: '', + value: '10000', + hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab10', + description: 'description of receive transaction', + blockNumber: '0', + status: 'failed', + }, + { + type: 'send', + createdAt: new Date('2019-04-17').getTime().toString(), + updatedAt: '', + timestamp: '', + value: '10000', + hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab11', + description: 'description of send transaction', + blockNumber: '0', + status: 'failed', + }, + { + type: 'receive', + createdAt: new Date('2019-04-16').getTime().toString(), + updatedAt: '', + timestamp: '', + value: '10000', + hash: '0x70abeeaa2ed08b7d7659341a122b9a2f2ede99bb6bd0df7398d7ffe488beab12', + description: 'description of receive transaction', + blockNumber: '0', status: 'failed', }, ], diff --git a/packages/neuron-ui/src/theme.tsx b/packages/neuron-ui/src/theme.tsx index c09d5406a6..acdb959304 100644 --- a/packages/neuron-ui/src/theme.tsx +++ b/packages/neuron-ui/src/theme.tsx @@ -7,6 +7,7 @@ import { Checkmark as SuccessIcon, CircleInformation as InfoIcon, Close as DismissIcon, + Close as FailIcon, Copy as CopyIcon, Down as ArrowDownIcon, FormClose as ClearIcon, @@ -25,6 +26,7 @@ import { StatusGood as MatchedIcon, SubtractCircle as RemoveIcon, Update as UpdateIcon, + Update as PendingIcon, } from 'grommet-icons' import { registerIcons } from 'utils/icons' @@ -76,6 +78,9 @@ registerIcons({ More: , Matched: , Unmatched: , + TransactionSuccess: , + TransactionFailure: , + TransactionPending: , }, }) diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index f514ba2ebd..4064f7ef14 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -1,12 +1,13 @@ declare namespace State { interface Transaction { - type: 'send' | 'receive' | 'other' + type: 'send' | 'receive' createdAt: string updatedAt: string timestamp: string value: string hash: string description: string + blockNumber: string status: 'pending' | 'success' | 'failed' } interface DetailedTransaction extends Transaction { diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index 327f16a3bc..b6f53e4ac3 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -6,6 +6,7 @@ export const MIN_AMOUNT = 61 export const PAGE_SIZE = 15 export const UNREMOVABLE_NETWORK = 'Testnet' export const UNREMOVABLE_NETWORK_ID = '0' +export const CONFIRMATION_THRESHOLD = 6 export enum ConnectionStatus { Online = 'online',