From 0fd2b84009f060d522bb7ba7fba48f915ed5734d Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Tue, 6 Aug 2024 10:52:24 +0700 Subject: [PATCH 01/13] Fixed PastelID links --- src/pages/Tickets/Tickets.helpers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Tickets/Tickets.helpers.tsx b/src/pages/Tickets/Tickets.helpers.tsx index 6e1433ce..51adb8ff 100644 --- a/src/pages/Tickets/Tickets.helpers.tsx +++ b/src/pages/Tickets/Tickets.helpers.tsx @@ -990,7 +990,7 @@ export const transformInferenceAPICreditPackData = (data: TicketsList[]) => {parseContractTicket.ticket_input_data_dict .credit_pack_purchase_request_confirmation_dict.requesting_end_user_pastelid ? ( Date: Tue, 6 Aug 2024 15:30:13 +0700 Subject: [PATCH 02/13] add UI for multi volume file --- .../CascadeDetails/CascadeDetails.styles.ts | 29 ++++++++ src/pages/Details/CascadeDetails/FileInfo.tsx | 66 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts index 94a46aa6..128bde40 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts +++ b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts @@ -251,3 +251,32 @@ export const Img = styled.img` width: 80px; } `; + +export const FileItem = styled.div` + display: flex; + gap: 4px; + align-items: center; + + .MuiSvgIcon-root { + width: 20px; + height: 20px; + + &.completed { + color: #00D097; + } + + &.fail { + color: #FF754C; + } + + &.downloading { + color: #BAA806; + } + } + + .file-name { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } +`; diff --git a/src/pages/Details/CascadeDetails/FileInfo.tsx b/src/pages/Details/CascadeDetails/FileInfo.tsx index 0c6a11ff..880cbaeb 100644 --- a/src/pages/Details/CascadeDetails/FileInfo.tsx +++ b/src/pages/Details/CascadeDetails/FileInfo.tsx @@ -8,12 +8,18 @@ import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material'; import CloseIcon from '@mui/icons-material/Close'; import DoneIcon from '@mui/icons-material/Done'; import parse from 'html-react-parser'; +import CloudDownloadIcon from '@mui/icons-material/CloudDownload'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import ErrorIcon from '@mui/icons-material/Error'; +import DownloadingIcon from '@mui/icons-material/Downloading'; +import IconButton from '@mui/material/IconButton'; import RouterLink from '@components/RouterLink/RouterLink'; import * as ROUTES from '@utils/constants/routes'; import { translate } from '@utils/helpers/i18n'; import { formatAddress, formatBytes } from '@utils/helpers/format'; import * as TicketStyles from '@components/Ticket/Ticket.styles'; +import * as NftDetailsStyles from '@pages/Details/NftDetails/NftDetails.styles'; import RqIds from './RqIds'; import { getFileIcon } from './CascadeDetails.helpers'; @@ -147,6 +153,66 @@ const FileInfo: React.FC = ({ data }) => { + + + + + Files + + + + + Download All + + + + + + + + cybernetic_utopia.jpg_5675.jpg + + + + + + + + + + cybernetic_utopia.jpg_5675.jpg + + + + + + + + + + cybernetic_utopia.jpg_5675.jpg + + + + + + + + + + cybernetic_utopia.jpg_5675.jpg + + + + + + + + + + {parse(translate('pages.cascade.raptorQParameters'))} From 32c936318e4a4a0a5fc4714a092107945101b824 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Wed, 7 Aug 2024 17:00:50 +0700 Subject: [PATCH 03/13] Updated Time in Minutes Between Blocks --- src/pages/Blocks/Blocks.helpers.tsx | 34 +++++---------------------- src/pages/Tickets/Tickets.helpers.tsx | 2 +- src/utils/types/IBlocks.ts | 1 + 3 files changed, 8 insertions(+), 29 deletions(-) diff --git a/src/pages/Blocks/Blocks.helpers.tsx b/src/pages/Blocks/Blocks.helpers.tsx index a5181132..1ac22b09 100644 --- a/src/pages/Blocks/Blocks.helpers.tsx +++ b/src/pages/Blocks/Blocks.helpers.tsx @@ -1,4 +1,3 @@ -import { differenceInSeconds } from 'date-fns'; import Tooltip from '@mui/material/Tooltip'; import parse from 'html-react-parser'; import TimeAgo from 'react-timeago'; @@ -42,26 +41,12 @@ export const DATA_FETCH_LIMIT = 40; export const DATA_OFFSET = 0; export const DATA_DEFAULT_SORT = 'DESC'; -const getDifferenceInMinutes = (startDate: number, endDate: number, variant = '') => { - const diff = parseFloat((differenceInSeconds(endDate, startDate) / 60).toFixed(1)); - if (variant === 'text') { - return `${diff} ${translate(diff === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')}`; - } - - return ( - <> - {diff} {translate(diff === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')} - - ); -}; - export const transformTableData = (transactions: Array) => transactions .slice(0, transactions.length - 1) .map( ( - { id, timestamp, transactionCount, height, ticketsList, size, totalTickets, type }, - index, + { id, timestamp, transactionCount, height, ticketsList, size, totalTickets, type, timeInMinutesBetweenBlocks }, ) => { const ticketsTypeList = getTicketsTypeList(ticketsList || ''); return { @@ -121,12 +106,9 @@ export const transformTableData = (transactions: Array) => ), [TIMESTAMP_BETWEEN_BLOCKS_KEY]: (
- {transactions[index + 1]?.timestamp ? ( + {timeInMinutesBetweenBlocks ? ( <> - {getDifferenceInMinutes( - Number(transactions[index + 1].timestamp) * 1000, - Number(timestamp) * 1000, - )} + {timeInMinutesBetweenBlocks.toFixed(1)} {translate(timeInMinutesBetweenBlocks === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')} ) : ( <>0 {translate('pages.blocks.minutes')} @@ -157,18 +139,14 @@ const getTotalTicket = (height: number, totalTickets: number, ticketsList: strin export const getCsvData = (blocks: Array) => { return blocks .slice(0, blocks.length - 1) - .map(({ id, timestamp, transactionCount, height, ticketsList, size, totalTickets }, index) => ({ + .map(({ id, timestamp, transactionCount, height, ticketsList, size, totalTickets, timeInMinutesBetweenBlocks }) => ({ [BLOCK_ID_KEY]: height, [BLOCK_HASH]: id, [TRANSACTIONS_QTY_KEY]: transactionCount, [TOTAL_TICKETS]: getTotalTicket(height, totalTickets, ticketsList), [BLOCK_SIZE]: formatNumber(size / 1024, { decimalsLength: 2 }), - [TIMESTAMP_BETWEEN_BLOCKS_KEY]: blocks[index + 1]?.timestamp - ? getDifferenceInMinutes( - Number(blocks[index + 1].timestamp) * 1000, - Number(timestamp) * 1000, - 'text', - ) + [TIMESTAMP_BETWEEN_BLOCKS_KEY]: timeInMinutesBetweenBlocks + ? `${timeInMinutesBetweenBlocks.toFixed(1)} ${translate(timeInMinutesBetweenBlocks === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')}` : `0 ${translate('pages.blocks.minutes')}`, [TIMESTAMP_BLOCKS_KEY]: formattedDate(timestamp, { dayName: false }), })); diff --git a/src/pages/Tickets/Tickets.helpers.tsx b/src/pages/Tickets/Tickets.helpers.tsx index 6e1433ce..51adb8ff 100644 --- a/src/pages/Tickets/Tickets.helpers.tsx +++ b/src/pages/Tickets/Tickets.helpers.tsx @@ -990,7 +990,7 @@ export const transformInferenceAPICreditPackData = (data: TicketsList[]) => {parseContractTicket.ticket_input_data_dict .credit_pack_purchase_request_confirmation_dict.requesting_end_user_pastelid ? ( Date: Wed, 7 Aug 2024 17:24:27 +0700 Subject: [PATCH 04/13] fixed eslint --- src/pages/Blocks/Blocks.helpers.tsx | 54 +++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/pages/Blocks/Blocks.helpers.tsx b/src/pages/Blocks/Blocks.helpers.tsx index 1ac22b09..9b1d931e 100644 --- a/src/pages/Blocks/Blocks.helpers.tsx +++ b/src/pages/Blocks/Blocks.helpers.tsx @@ -45,9 +45,17 @@ export const transformTableData = (transactions: Array) => transactions .slice(0, transactions.length - 1) .map( - ( - { id, timestamp, transactionCount, height, ticketsList, size, totalTickets, type, timeInMinutesBetweenBlocks }, - ) => { + ({ + id, + timestamp, + transactionCount, + height, + ticketsList, + size, + totalTickets, + type, + timeInMinutesBetweenBlocks, + }) => { const ticketsTypeList = getTicketsTypeList(ticketsList || ''); return { id, @@ -108,7 +116,12 @@ export const transformTableData = (transactions: Array) =>
{timeInMinutesBetweenBlocks ? ( <> - {timeInMinutesBetweenBlocks.toFixed(1)} {translate(timeInMinutesBetweenBlocks === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')} + {timeInMinutesBetweenBlocks.toFixed(1)}{' '} + {translate( + timeInMinutesBetweenBlocks === 1 + ? 'pages.blocks.minute' + : 'pages.blocks.minutes', + )} ) : ( <>0 {translate('pages.blocks.minutes')} @@ -139,17 +152,28 @@ const getTotalTicket = (height: number, totalTickets: number, ticketsList: strin export const getCsvData = (blocks: Array) => { return blocks .slice(0, blocks.length - 1) - .map(({ id, timestamp, transactionCount, height, ticketsList, size, totalTickets, timeInMinutesBetweenBlocks }) => ({ - [BLOCK_ID_KEY]: height, - [BLOCK_HASH]: id, - [TRANSACTIONS_QTY_KEY]: transactionCount, - [TOTAL_TICKETS]: getTotalTicket(height, totalTickets, ticketsList), - [BLOCK_SIZE]: formatNumber(size / 1024, { decimalsLength: 2 }), - [TIMESTAMP_BETWEEN_BLOCKS_KEY]: timeInMinutesBetweenBlocks - ? `${timeInMinutesBetweenBlocks.toFixed(1)} ${translate(timeInMinutesBetweenBlocks === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')}` - : `0 ${translate('pages.blocks.minutes')}`, - [TIMESTAMP_BLOCKS_KEY]: formattedDate(timestamp, { dayName: false }), - })); + .map( + ({ + id, + timestamp, + transactionCount, + height, + ticketsList, + size, + totalTickets, + timeInMinutesBetweenBlocks, + }) => ({ + [BLOCK_ID_KEY]: height, + [BLOCK_HASH]: id, + [TRANSACTIONS_QTY_KEY]: transactionCount, + [TOTAL_TICKETS]: getTotalTicket(height, totalTickets, ticketsList), + [BLOCK_SIZE]: formatNumber(size / 1024, { decimalsLength: 2 }), + [TIMESTAMP_BETWEEN_BLOCKS_KEY]: timeInMinutesBetweenBlocks + ? `${timeInMinutesBetweenBlocks.toFixed(1)} ${translate(timeInMinutesBetweenBlocks === 1 ? 'pages.blocks.minute' : 'pages.blocks.minutes')}` + : `0 ${translate('pages.blocks.minutes')}`, + [TIMESTAMP_BLOCKS_KEY]: formattedDate(timestamp, { dayName: false }), + }), + ); }; export const transformMempoolTableData = (transactions: Array) => From 16d6541c01d45224695f218a2c672b257f20aa53 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Thu, 15 Aug 2024 09:43:14 +0700 Subject: [PATCH 05/13] update cascade multi volume --- public/locales/en/translation.json | 134 +++++ .../Ticket/CascadeMultiVolumeTicket.tsx | 477 ++++++++++++++++++ src/components/Ticket/Ticket.styles.ts | 5 + src/components/Ticket/index.ts | 6 +- src/pages/Details/BlockDetails/Tickets.tsx | 11 +- .../CascadeDetails/CascadeDetails.helpers.tsx | 46 ++ .../CascadeDetails/CascadeDetails.styles.ts | 13 + .../Details/CascadeDetails/CascadeDetails.tsx | 182 ++++++- src/pages/Details/CascadeDetails/FileInfo.tsx | 356 ++++++++++--- src/pages/Details/CascadeDetails/mockup.js | 282 +++++++++++ src/pages/Tickets/Tickets.helpers.tsx | 118 ++++- src/pages/TicketsType/TicketList.tsx | 13 +- src/utils/types/ITransactions.ts | 76 +++ 13 files changed, 1644 insertions(+), 75 deletions(-) create mode 100644 src/components/Ticket/CascadeMultiVolumeTicket.tsx create mode 100644 src/pages/Details/CascadeDetails/mockup.js diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index c7bbb19b..bb2a5123 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -27,6 +27,10 @@ "usd": { "message": "USD", "description": "Used for the common" + }, + "mb": { + "message": "MB", + "description": "Used for the common" } }, "components": { @@ -389,6 +393,10 @@ "inferenceAPICreditPack": { "message": "Inference API Credit Pack Ticket", "description": "Used for the ticket title component" + }, + "cascadeMultiVolume": { + "message": "Cascade Multi Volume", + "description": "Used for the ticket title component" } }, "acceptTicket": { @@ -1280,6 +1288,72 @@ "description": "Used for the PastelIDRegistrationTicket component" } } + }, + "multiVolume": { + "fileName": { + "message": "Filename:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "totalFile": { + "message": "Total file:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "secondaryKey": { + "message": "secondary_key:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "totalFee": { + "message": "Total fee:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "fileID": { + "message": "File ID:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "uploadTimestamp": { + "message": "Upload timestamp:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "taskID": { + "message": "Task ID:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "regTxID": { + "message": "Reg txID:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "activationTxID": { + "message": "Activation txID:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "reqBurnTxnAmount": { + "message": "Req burn txn amount:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "burnTxnId": { + "message": "Burn txn id:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "reqAmount": { + "message": "Req amount:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "isConcluded": { + "message": "Is concluded:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "cascadeMetadataTicketId": { + "message": "Cascade metadata ticket id:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "startBlock": { + "message": "Start block:", + "description": "Used for the CascadeMultiVolumeTicket component" + }, + "doneBlock": { + "message": "Done block:", + "description": "Used for the CascadeMultiVolumeTicket component" + } } }, "historicalStatisticsLayout": { @@ -3484,6 +3558,46 @@ "costPerCredit": { "message": "Cost per credit", "description": "Used for the Tickets page" + }, + "completed": { + "message": "Completed", + "description": "Used for the Tickets page" + }, + "inProgress": { + "message": "In Progress", + "description": "Used for the Tickets page" + }, + "fail": { + "message": "Fail", + "description": "Used for the Tickets page" + }, + "key": { + "message": "Key", + "description": "Used for the Tickets page" + }, + "secondaryKey": { + "message": "Secondary key", + "description": "Used for the Tickets page" + }, + "fileHash": { + "message": "File hash", + "description": "Used for the Tickets page" + }, + "totalFee": { + "message": "Multisig tx total fee", + "description": "Used for the Tickets page" + }, + "multisigOutputsCount": { + "message": "File count", + "description": "Used for the Tickets page" + }, + "files": { + "message": "Files", + "description": "Used for the Tickets page" + }, + "volumes": { + "message": "Volumes", + "description": "Used for the Tickets page" } }, "ticketsType": { @@ -3786,6 +3900,26 @@ "transfers": { "message": "Transfers", "description": "Used for the Cascade Details page" + }, + "totalVolumes": { + "message": "Total Volumes", + "description": "Used for the Cascade Details page" + }, + "downloadedVolumes": { + "message": "Downloaded Volumes", + "description": "Used for the Cascade Details page" + }, + "volumesDownloadInProgress": { + "message": "Volumes download in progress", + "description": "Used for the Cascade Details page" + }, + "volumesPendingDownload": { + "message": "Volumes pending download", + "description": "Used for the Cascade Details page" + }, + "volumesDownloadFailed": { + "message": "Volumes download failed", + "description": "Used for the Cascade Details page" } }, "collection": { diff --git a/src/components/Ticket/CascadeMultiVolumeTicket.tsx b/src/components/Ticket/CascadeMultiVolumeTicket.tsx new file mode 100644 index 00000000..de021769 --- /dev/null +++ b/src/components/Ticket/CascadeMultiVolumeTicket.tsx @@ -0,0 +1,477 @@ +import { useState } from 'react'; +import Grid from '@mui/material/Grid'; +import Box from '@mui/material/Box'; +import parse from 'html-react-parser'; +import CloseIcon from '@mui/icons-material/Close'; +import DoneIcon from '@mui/icons-material/Done'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material'; + +import { getCurrencyName } from '@utils/appInfo'; +import RouterLink from '@components/RouterLink/RouterLink'; +import { formattedDate, formatFullDate } from '@utils/helpers/date/date'; +import { formatNumber } from '@utils/helpers/formatNumbers/formatNumbers'; +import { + IMultiVolumeTicket, + IMultiVolumeFIle, +} from '@utils/types/ITransactions'; +import * as ROUTES from '@utils/constants/routes'; +import { translate } from '@utils/helpers/i18n'; +import { formatBytes, formatAddress } from '@utils/helpers/format'; +import { fakeFilesData } from '@pages/Details/CascadeDetails/mockup'; +import * as CascadeDetailsStyles from '@pages/Details/CascadeDetails/CascadeDetails.styles'; +import * as TicketStyles from '@components/Ticket/Ticket.styles'; + +import { useStorageFee } from './Ticket.helpers'; +import * as Styles from './Ticket.styles'; + +interface ICascadeMultiVolumeTicketProps { + ticket: IMultiVolumeTicket; + showFull?: boolean; +} + +const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { + const [isExpanded, setIsExpanded] = useState(false); + + return ( + setIsExpanded(isPanelExpanded)} + > + + + + + {parse(translate('pages.tickets.files'))}: + + + + + {isExpanded + ? parse(translate('components.ticket.actionRegistrationTicket.hideDetail')) + : parse( + translate('components.ticket.actionRegistrationTicket.clickToSeeDetail'), + )}{' '} + + + + + + + + {files?.map((item) => { + const reqBurnTxnAmount = useStorageFee(item.req_burn_txn_amount); + const reqAmount = useStorageFee(item.req_amount); + return ( + + + + + {parse(translate('components.ticket.multiVolume.fileID'))} + + + {item.file_id} + + + + + {parse(translate('components.ticket.multiVolume.uploadTimestamp'))} + + + {formatFullDate(new Date(item.upload_timestamp).getTime())} + + + + + {parse(translate('components.ticket.multiVolume.taskID'))} + + + {item.task_id} + + + + + {parse(translate('components.ticket.multiVolume.regTxID'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.activationTxID'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.reqBurnTxnAmount'))} + + + {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {reqBurnTxnAmount.storageFee} + + + + + {parse(translate('components.ticket.multiVolume.burnTxnId'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.reqAmount'))} + + + {formatNumber(item.req_amount)} {getCurrencyName()} {reqAmount.storageFee} + + + + + {parse(translate('components.ticket.multiVolume.isConcluded'))} + + + + {item.is_concluded ? : } + + + + + + {parse(translate('components.ticket.multiVolume.cascadeMetadataTicketId'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.startBlock'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.doneBlock'))} + + + + + + + + ) + })} + + + + ) +} + +const CascadeMultiVolumeTicket: React.FC = ({ + ticket, + showFull = false, +}) => { + if (!ticket?.tx_info) { + return null; + } + const txInfo = JSON.parse(ticket.tx_info); + const { storageFee } = useStorageFee(txInfo.multisig_tx_total_fee); + + if (showFull) { + return ( + + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.height'))} + + + + + + + + + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.key'))} + + + + {ticket.key} + + + + + + {parse(translate('components.ticket.multiVolume.secondaryKey'))} + + + + {ticket.secondary_key} + + + + + + {parse(translate('pages.cascade.dataHash'))} + + + + {ticket.contract_ticket.sha3_256_hash_of_original_file} + + + + + + {parse(translate('components.ticket.multiVolume.fileName'))} + + + + {ticket.fileName} + + + + + + {parse( + translate( + 'components.ticket.multiVolume.totalFee', + ), + )} + + + + + {txInfo.multisig_tx_total_fee ? formatNumber(txInfo.multisig_tx_total_fee) : parse(translate('common.na'))} {getCurrencyName()} {storageFee} + + + + + + + {parse( + translate( + 'components.ticket.multiVolume.totalFile', + ), + )} + + + + + {txInfo.multisig_outputs_count} + + + + + + + {parse( + translate( + 'pages.cascade.fileSize', + ), + )} + + + + + {ticket.contract_ticket.size_of_original_file_mb ? + formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) : parse(translate('common.na')) + } + + + + {ticket?.timestamp ? ( + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.timestamp'))} + + + + + {formattedDate(Number(ticket.timestamp), { dayName: false })} + + + + ) : null} + + + ); + } + + return ( + + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.height'))} + + + + + + + + + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.key'))} + + + + {ticket.key} + + + + + + {parse(translate('components.ticket.multiVolume.secondaryKey'))} + + + + {ticket.secondary_key} + + + + + + {parse(translate('pages.cascade.dataHash'))} + + + + {ticket.contract_ticket.sha3_256_hash_of_original_file} + + + + + + {parse(translate('components.ticket.multiVolume.fileName'))} + + + + {ticket.fileName} + + + + + + {parse( + translate( + 'components.ticket.multiVolume.totalFee', + ), + )} + + + + + {txInfo.multisig_tx_total_fee ? formatNumber(txInfo.multisig_tx_total_fee) : parse(translate('common.na'))} {getCurrencyName()} {storageFee} + + + + + + + {parse( + translate( + 'components.ticket.multiVolume.totalFile', + ), + )} + + + + + {txInfo.multisig_outputs_count} + + + + + + + {parse( + translate( + 'pages.cascade.fileSize', + ), + )} + + + + + {ticket.contract_ticket.size_of_original_file_mb ? + formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) : parse(translate('common.na')) + } + + + + {ticket?.timestamp ? ( + + + + {parse(translate('components.ticket.inferenceAPICreditPackTicket.timestamp'))} + + + + + {formattedDate(Number(ticket.timestamp), { dayName: false })} + + + + ) : null} + + ); +}; + +export default CascadeMultiVolumeTicket; diff --git a/src/components/Ticket/Ticket.styles.ts b/src/components/Ticket/Ticket.styles.ts index 832483ce..3a1985db 100644 --- a/src/components/Ticket/Ticket.styles.ts +++ b/src/components/Ticket/Ticket.styles.ts @@ -115,6 +115,11 @@ export const Accordion = styled(MuiAccordion)` width: 100%; } } + + .action-ticket-status.icon svg { + width: 14px; + height: 14px; + } `; export const ButtonLink = styled.button` diff --git a/src/components/Ticket/index.ts b/src/components/Ticket/index.ts index 14354257..b1f60a12 100644 --- a/src/components/Ticket/index.ts +++ b/src/components/Ticket/index.ts @@ -13,11 +13,12 @@ import NFTRoyaltyTicket from './NFTRoyaltyTicket'; import ActionActivationTicket from './ActionActivationTicket'; import ActionRegistrationTicket from './ActionRegistrationTicket'; import InferenceAPICreditPackTicket from './InferenceAPICreditPackTicket'; +import CascadeMultiVolumeTicket from './CascadeMultiVolumeTicket'; import OfferTicket from './OfferTicket'; import AcceptTicket from './AcceptTicket'; import TransferTicket from './TransferTicket'; -const getTicketTitle = (type: TTicketType, itemType = '') => { +const getTicketTitle = (type: TTicketType, itemType = '', sub_type = '') => { switch (type) { case 'pastelid': return parse(translate('components.ticket.ticketsTitle.pastelid')); @@ -46,7 +47,7 @@ const getTicketTitle = (type: TTicketType, itemType = '') => { case 'transfer': return parse(translate('components.ticket.ticketsTitle.transfer')); case 'contract': - return parse(translate('pages.tickets.inferenceTicketType')); + return sub_type === 'cascade_multi_volume_metadata' ? parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')) : parse(translate('pages.tickets.inferenceTicketType')); default: return ''; } @@ -66,5 +67,6 @@ export { AcceptTicket, TransferTicket, InferenceAPICreditPackTicket, + CascadeMultiVolumeTicket, getTicketTitle, }; diff --git a/src/pages/Details/BlockDetails/Tickets.tsx b/src/pages/Details/BlockDetails/Tickets.tsx index b817c508..0b0b0729 100644 --- a/src/pages/Details/BlockDetails/Tickets.tsx +++ b/src/pages/Details/BlockDetails/Tickets.tsx @@ -29,6 +29,7 @@ import { TSenseRequests, ICascadeApiTicket, IInferenceAPICreditPackTicket, + IMultiVolumeTicket, } from '@utils/types/ITransactions'; import { PastelIDRegistrationTicket, @@ -41,6 +42,7 @@ import { ActionActivationTicket, ActionRegistrationTicket, InferenceAPICreditPackTicket, + CascadeMultiVolumeTicket, OfferTicket, AcceptTicket, TransferTicket, @@ -367,8 +369,10 @@ const TicketsList: React.FC = ({ | IOfferTicket | IAcceptTicket | IInferenceAPICreditPackTicket + | IMultiVolumeTicket | ITransferTicket, transactionHash: string, + sub_type?: string, ) => { switch (type) { case 'username-change': @@ -408,6 +412,9 @@ const TicketsList: React.FC = ({ case 'transfer': return ; case 'contract': + if (sub_type === 'cascade_multi_volume_metadata') { + return + } return ( ); @@ -426,6 +433,7 @@ const TicketsList: React.FC = ({ data[0].type as TTicketType, (data[0].data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, + data[0].sub_type )} @@ -467,6 +475,7 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket) ?.collection_ticket?.item_type, + ticket.sub_type )} @@ -474,7 +483,7 @@ const TicketsList: React.FC = ({ ) : null} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash)} + {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} ))} diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx index 3d2f94d2..4a58c4f9 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx @@ -51,3 +51,49 @@ export const getFileIcon = (file_type: string) => { return unknown; } }; + +export const getCascadeVolumeIcon = (file_name: string) => { + if (!file_name) { + return unknown; + } + const parseFileName = file_name.split('.') + const fileExtension = parseFileName[parseFileName.length - 1] + switch (fileExtension) { + case 'jpg': + return jpg; + case 'doc': + case 'docx': + return doc; + case 'gif': + return gif; + case 'mp3': + return mp3; + case 'pdf': + return pdf; + case 'svg': + return svg; + case 'txt': + return txt; + case 'xls': + case 'xlsx': + return xls; + case 'png': + return png; + case 'mp4': + return mp4; + case 'mxf': + return mxf; + case 'avi': + return avi; + case 'mov': + return mov; + case 'zip': + case 'tar': + case 'tar': + case 'gz': + case '7z': + return zip; + default: + return unknown; + } +}; diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts index 128bde40..05684de0 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts +++ b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts @@ -257,6 +257,14 @@ export const FileItem = styled.div` gap: 4px; align-items: center; + &.file-item { + padding: 12px 16px; + + &:nth-of-type(even) { + background-color: ${props => props.theme.table.odd}; + } + } + .MuiSvgIcon-root { width: 20px; height: 20px; @@ -274,6 +282,11 @@ export const FileItem = styled.div` } } + .action-ticket-status.icon svg { + width: 14px; + height: 14px; + } + .file-name { max-width: 100%; overflow: hidden; diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.tsx index b0a81185..4ad7e6f8 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.tsx @@ -53,9 +53,23 @@ export const BlockItemLayout: React.FC = ({ ); }; +let timeout: NodeJS.Timeout | null = null; + const CascadeDetails = () => { const [status, setStatus] = useState(''); const [openSnackbar, setOpenSnackbar] = useState(false); + const [fileId, setFileId] = useState(''); + const [downloadStatus, setDownloadStatus] = useState({ + totalVolumes: 0, + downloadedVolumes: 0, + volumesDownloadInProgress: 0, + volumesPendingDownload: 0, + volumesDownloadFailed: 0, + taskStatus: '', + sizeOfTheFileMegabytes: 0, + dataDownloadedMegabytes: 0, + details: null, + }); const txid = getParameterByName('txid'); const { cascadeData, isLoading } = useCascadeDetails(txid); @@ -66,6 +80,36 @@ const CascadeDetails = () => { } }; + const checkDownloadStatus = async (_fileId: string) => { + const url = `${process.env.REACT_APP_EXPLORER_OPENNODE_API_URL}/openapi/cascade/downloads/${_fileId}/status`; + axiosInstance + .get(url, { responseType: 'blob' }) + .then(res => { + if (res?.data) { + setDownloadStatus({ + totalVolumes: res.data.total_volumes, + downloadedVolumes: res.data.downloaded_volumes, + volumesDownloadInProgress: res.data.volumes_download_in_progress, + volumesPendingDownload: res.data.volumes_pending_download, + volumesDownloadFailed: res.data.volumes_download_failed, + taskStatus: res.data.task_status, + sizeOfTheFileMegabytes: res.data.size_of_the_file_megabytes, + dataDownloadedMegabytes: res.data.data_downloaded_megabytes, + details: res.data.details, + }); + if ((res.data.task_status === 'Completed' || res.data.task_status === 'Failed') && timeout) { + clearInterval(timeout); + } + } + }) + .catch(() => { + setStatus('error'); + if (timeout) { + clearInterval(timeout); + } + }); + } + useEffect(() => { window.addEventListener('beforeunload', handleReloadPage); return () => { @@ -73,6 +117,14 @@ const CascadeDetails = () => { }; }, [status]); + useEffect(() => { + if (fileId) { + timeout = setInterval(() => { + checkDownloadStatus(fileId); + }, 5000) // ~ 1s + } + }, [fileId]) + const decodeApiTicket = (apiTicket: string) => { let data = null; try { @@ -102,6 +154,19 @@ const CascadeDetails = () => { }; }; + const getCascadeMultiVolumeInfo = () => { + if (!cascadeData?.ticket?.contract_ticket) { + return null; + } + const contractTicket = cascadeData?.ticket?.contract_ticket; + const parseActionTicket = JSON.parse(decode(contractTicket)); + return { + ...cascadeData, + ...parseActionTicket, + contract_ticket: undefined, + }; + }; + if (isLoading) { return ( @@ -126,7 +191,33 @@ const CascadeDetails = () => { axiosInstance .get(url, { responseType: 'blob' }) .then(res => { - link.href = URL.createObjectURL(new Blob([res.data], { type: fileType })); + // link.href = URL.createObjectURL(new Blob([res.data], { type: fileType })); + // link.click(); + // setStatus('done'); + if (res?.data?.file_id) { + setFileId(res.data.file_id) + } + }) + .catch(() => { + setStatus('error'); + }); + } + }; + + const handleDownloadMultiVolumeFile = () => { + const cascadeInfo = getCascadeMultiVolumeInfo(); + + if (cascadeInfo?.creatorPastelID && txid) { + setOpenSnackbar(true); + setStatus('downloading'); + const url = `${process.env.REACT_APP_EXPLORER_OPENNODE_API_URL}/openapi/cascade/v2/download?pid=${cascadeInfo?.creatorPastelID}&txid=${txid}`; + const link = document.createElement('a'); + link.target = '_blank'; + link.download = cascadeInfo.name_of_original_file; + axiosInstance + .get(url, { responseType: 'blob' }) + .then(res => { + link.href = URL.createObjectURL(new Blob([res.data])); link.click(); setStatus('done'); }) @@ -158,6 +249,95 @@ const CascadeDetails = () => { ); }; + const getMultiVolumeSummaryTitle = () => { + const cascadeInfo = getCascadeMultiVolumeInfo(); + return ( + + {parse(translate('pages.cascade.fileInfo'))} + + {status === 'downloading' + ? parse(translate('pages.cascade.downloading')) + : parse(translate('pages.nftDetails.downloadThisFile'))} + + + ); + }; + + if (cascadeData?.ticket?.sub_type === 'cascade_multi_volume_metadata') { + return ( + + + + + + {parse(translate('pages.cascade.ticketDetail'))}:{' '} + + + + + + + + + + + setOpenSnackbar(false)} + > + + +
+ {parse(translate('pages.cascade.totalVolumes'))}: {downloadStatus.totalVolumes} +
+
+ {parse(translate('pages.cascade.downloadedVolumes'))}: {downloadStatus.downloadedVolumes} +
+
+ {parse(translate('pages.cascade.volumesDownloadInProgress'))}: {downloadStatus.volumesDownloadInProgress} +
+
+ {parse(translate('pages.cascade.volumesPendingDownload'))}: {downloadStatus.volumesPendingDownload} +
+
+ {parse(translate('pages.cascade.volumesDownloadFailed'))}: {downloadStatus.volumesDownloadFailed} +
+
+
+
+ setStatus('')} + autoHideDuration={7000} + > + setStatus('')} + > + {parse(translate('pages.cascade.downloadFailedNetworkError'))} + + +
+ ) + } + return cascadeData ? ( diff --git a/src/pages/Details/CascadeDetails/FileInfo.tsx b/src/pages/Details/CascadeDetails/FileInfo.tsx index 880cbaeb..6b06ce03 100644 --- a/src/pages/Details/CascadeDetails/FileInfo.tsx +++ b/src/pages/Details/CascadeDetails/FileInfo.tsx @@ -8,22 +8,22 @@ import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material'; import CloseIcon from '@mui/icons-material/Close'; import DoneIcon from '@mui/icons-material/Done'; import parse from 'html-react-parser'; -import CloudDownloadIcon from '@mui/icons-material/CloudDownload'; -import CheckCircleIcon from '@mui/icons-material/CheckCircle'; -import ErrorIcon from '@mui/icons-material/Error'; -import DownloadingIcon from '@mui/icons-material/Downloading'; -import IconButton from '@mui/material/IconButton'; +import { formatFullDate } from '@utils/helpers/date/date'; +import { formatNumber } from '@utils/helpers/formatNumbers/formatNumbers'; +import { useUsdPrice } from '@hooks/useTransactionDetails'; +import { getStorageFee } from '@pages/Tickets/Tickets.helpers'; +import { getCurrencyName } from '@utils/appInfo'; import RouterLink from '@components/RouterLink/RouterLink'; import * as ROUTES from '@utils/constants/routes'; import { translate } from '@utils/helpers/i18n'; import { formatAddress, formatBytes } from '@utils/helpers/format'; import * as TicketStyles from '@components/Ticket/Ticket.styles'; -import * as NftDetailsStyles from '@pages/Details/NftDetails/NftDetails.styles'; import RqIds from './RqIds'; -import { getFileIcon } from './CascadeDetails.helpers'; +import { getFileIcon, getCascadeVolumeIcon } from './CascadeDetails.helpers'; import * as Styles from './CascadeDetails.styles'; +import { fakeFilesData } from './mockup' type TCascadeData = { data_hash: string; @@ -37,6 +37,34 @@ type TCascadeData = { rq_ids: string[]; creatorPastelID: string; currentOwnerPastelID: string; + sub_type?: string; + name_of_original_file?: string; + size_of_original_file_mb?: number; + key?: string; + secondary_key?: string; + sha3_256_hash_of_original_file?: string; + ticket?: { + action_ticket: string; + action_type: string; + called_at: number; + key: string; + label: string; + storage_fee: number; + type: string; + version: number; + sub_type?: string; + secondary_key?: string; + contract_ticket?: string; + }; + tx_info?: { + compressed_size: number; + compression_ratio: string; + is_compressed: boolean; + multisig_outputs_count: number; + multisig_tx_total_fee: number; + uncompressed_size: number; + }; + volumes?: string[]; }; interface IFileInfo { @@ -57,12 +85,266 @@ const getDataHash = (dataHash: string) => { }; const FileInfo: React.FC = ({ data }) => { + const { usdPrice } = useUsdPrice(); const [opened, setOpened] = useState(false); if (!data) { return null; } + if (data?.ticket?.sub_type === 'cascade_multi_volume_metadata') { + return ( + + + {getCascadeVolumeIcon(data?.name_of_original_file || '')} + + + {data.name_of_original_file} + + + + + + + {parse(translate('pages.cascade.fileSize'))}: + + + {data.size_of_original_file_mb ? + formatBytes(data.size_of_original_file_mb * 1000000) : parse(translate('common.na')) + } + + + + + + + {parse(translate('pages.cascade.dataHash'))}: + + + {data.sha3_256_hash_of_original_file ? + + {formatAddress(data.sha3_256_hash_of_original_file, 30, -5)} + : parse(translate('common.na')) + } + + + + + + + {parse(translate('pages.cascade.makePubliclyAccessible'))}: + + + + {data.make_publicly_accessible ? : } + + + + + + + + {parse(translate('pages.cascade.creatorPastelID'))}: + + + {data.creatorPastelID ? ( + + ) : ( + parse(translate('common.na')) + )} + + + + + + + {parse(translate('pages.tickets.multisigOutputsCount'))}: + + + {data?.tx_info?.multisig_outputs_count ? formatNumber(data?.tx_info.multisig_outputs_count) : parse(translate('common.na'))} + + + + + + + {parse(translate('pages.tickets.key'))}: + + + {data?.ticket?.key ? + + {formatAddress(getDataHash(data?.ticket?.key), 30, -5)} + : parse(translate('common.na')) + } + + + + + + + + + + + + {parse(translate('pages.tickets.files'))} + + + + + {fakeFilesData?.map((item) => ( + + + + + {parse(translate('components.ticket.multiVolume.fileID'))} + + + {item.file_id} + + + + + {parse(translate('components.ticket.multiVolume.uploadTimestamp'))} + + + {formatFullDate(new Date(item.upload_timestamp).getTime())} + + + + + {parse(translate('components.ticket.multiVolume.taskID'))} + + + {item.task_id} + + + + + {parse(translate('components.ticket.multiVolume.regTxID'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.activationTxID'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.reqBurnTxnAmount'))} + + + {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {getStorageFee(item.req_burn_txn_amount, usdPrice)} + + + + + {parse(translate('components.ticket.multiVolume.burnTxnId'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.reqAmount'))} + + + {formatNumber(item.req_amount)} {getCurrencyName()} {getStorageFee(item.req_amount, usdPrice)} + + + + + {parse(translate('components.ticket.multiVolume.isConcluded'))} + + + + {item.is_concluded ? : } + + + + + + {parse(translate('components.ticket.multiVolume.cascadeMetadataTicketId'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.startBlock'))} + + + + + + + + {parse(translate('components.ticket.multiVolume.doneBlock'))} + + + + + + + + ))} + + + + ) + } + return ( @@ -153,66 +435,6 @@ const FileInfo: React.FC = ({ data }) => { - - - - - Files - - - - - Download All - - - - - - - - cybernetic_utopia.jpg_5675.jpg - - - - - - - - - - cybernetic_utopia.jpg_5675.jpg - - - - - - - - - - cybernetic_utopia.jpg_5675.jpg - - - - - - - - - - cybernetic_utopia.jpg_5675.jpg - - - - - - - - - - {parse(translate('pages.cascade.raptorQParameters'))} diff --git a/src/pages/Details/CascadeDetails/mockup.js b/src/pages/Details/CascadeDetails/mockup.js new file mode 100644 index 00000000..2c20d68c --- /dev/null +++ b/src/pages/Details/CascadeDetails/mockup.js @@ -0,0 +1,282 @@ +export const fakeFilesData = [ + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.001", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "0", + "base_file_id": "NwWDvdIg", + "task_id": "HskCThhY", + "reg_txid": "7195a4b67d82f242f7f3979407b072ac0ca7e8bca8c157ad0cfdcbc34176f358", + "activation_txid": "e448cb9fbd78f580d0feaeaee34bba8937406fdff9cb534165978bf1cebcc47e", + "req_burn_txn_amount": 204, + "burn_txn_id": "a4a52e3c67e2d3825d046f6e75145f104c2e3b11c320bee58ac3032b4862d5e4", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "ddb4c8e7-39d8-45e0-afc7-4a4753b01af8", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97050, + "registration_attempts": [ + { + "id": 27, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.001", + "reg_started_at": "2024-07-16 16:08:03.363832595 +0000 UTC", + "processor_sns": "154.12.243.33:24444,154.12.246.128:24444,154.12.246.131:24444,", + "finished_at": "2024-07-16 16:13:34.896512035 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 25, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.001", + "activation_attempt_at": "2024-07-16 16:45:03.09246019 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.002", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "1", + "base_file_id": "NwWDvdIg", + "task_id": "6cRf8Zff", + "reg_txid": "1a567609281db81f5f119d6c2b5e6e8df2976cc1b450ddd1d239712534084d01", + "activation_txid": "eb986726b716fbdd09ed399347d82aa0397c50ff2d92f57abf255dadd59676ec", + "req_burn_txn_amount": 204, + "burn_txn_id": "0786b7388d557b63ad2cb859288dd69012f858b5c8b58fd83bb56a00ffe3c3d3", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "89dc2c94-4623-4010-a58d-b2d70631bbb4", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97048, + "registration_attempts": [ + { + "id": 28, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.002", + "reg_started_at": "2024-07-16 16:08:03.41780498 +0000 UTC", + "processor_sns": "154.38.164.99:24444,154.12.246.129:24444,154.12.246.131:24444,", + "finished_at": "2024-07-16 16:10:19.899360674 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 23, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.002", + "activation_attempt_at": "2024-07-16 16:36:45.992604839 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.003", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "2", + "base_file_id": "NwWDvdIg", + "task_id": "szcAqL9V", + "reg_txid": "4afb36a8d91d619341dfb932ff5ca0079c8bddb5e3bd35f1f3ac365855a249c8", + "activation_txid": "66da2108c5c3805eb7ca0b96ab197da8a09494b748110defc7deaacc984d6566", + "req_burn_txn_amount": 204, + "burn_txn_id": "47917dd82864aa92949ddff83dee71c61795a13f903b25f503737ade708ddc86", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "dbb98261-75a5-4f89-9b12-d1d9de157d7d", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97050, + "registration_attempts": [ + { + "id": 29, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.003", + "reg_started_at": "2024-07-16 16:08:03.444456683 +0000 UTC", + "processor_sns": "207.244.236.251:24444,154.12.246.130:24444,154.38.164.104:24444,", + "finished_at": "2024-07-16 16:11:03.871768764 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 27, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.003", + "activation_attempt_at": "2024-07-16 16:45:04.003916154 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.004", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "3", + "base_file_id": "NwWDvdIg", + "task_id": "XkD8T37l", + "reg_txid": "a777884586342015a47fc9d223fa9c3ee866990acd770795eba56ea32a980a2e", + "activation_txid": "bff4a79c5d671cce7ecbc22404d6ecbc850e6bfa4b69a55fbf6d342ae8a03ee2", + "req_burn_txn_amount": 204, + "burn_txn_id": "b60283f595a813f90acf15179249c61b5b1a4c480eac8624a0a42a11b56d06f4", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "75638e9b-84da-45ba-88b7-9aca2e2ee9b0", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97050, + "registration_attempts": [ + { + "id": 30, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.004", + "reg_started_at": "2024-07-16 16:08:03.474462947 +0000 UTC", + "processor_sns": "154.12.246.128:24444,154.12.246.130:24444,207.244.236.251:24444,", + "finished_at": "2024-07-16 16:11:00.604286877 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 26, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.004", + "activation_attempt_at": "2024-07-16 16:45:03.157739528 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.005", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "4", + "base_file_id": "NwWDvdIg", + "task_id": "z26N4I05", + "reg_txid": "047fa18ed022e8a36dcb4b08b1cf16c167aa3e47c21aa20369111b602520cc0b", + "activation_txid": "109f73144b1e665b6bf2ca4a2ce2e9fd0dd9d59821e421c0b5c9cf2d77912c42", + "req_burn_txn_amount": 204, + "burn_txn_id": "ae07f7f55312a4e74e8011249603bd52b7c46f60a69a2ca4215dc4f1763acd36", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "105fb7ac-533f-44db-a68b-6143ae635d42", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97050, + "registration_attempts": [ + { + "id": 31, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.005", + "reg_started_at": "2024-07-16 16:08:03.503680527 +0000 UTC", + "processor_sns": "154.12.246.131:24444,154.38.164.99:24444,154.12.246.129:24444,", + "finished_at": "2024-07-16 16:10:44.896195133 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 24, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.005", + "activation_attempt_at": "2024-07-16 16:45:01.521751888 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.006", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "5", + "base_file_id": "NwWDvdIg", + "task_id": "7qAn2Xf7", + "reg_txid": "1d87d071cc83b7917b371e8f040e09adadbd07d92fccc85df0e4115369b0e988", + "activation_txid": "53abacb80b32f88dfee8cec55e1b9372f0cc0708f2d667e4dbfa69f3e1cb1bbe", + "req_burn_txn_amount": 204, + "burn_txn_id": "2dea791cb8a57148e1d702fe68affeb9ba929b28e96806883c121c85903c4469", + "req_amount": 1020, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "08a8b64e-4015-4156-9c65-e0acbf718025", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97051, + "registration_attempts": [ + { + "id": 32, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.006", + "reg_started_at": "2024-07-16 16:08:03.534410197 +0000 UTC", + "processor_sns": "154.12.246.129:24444,154.12.246.131:24444,154.12.246.128:24444,", + "finished_at": "2024-07-16 16:12:23.515338118 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 29, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.006", + "activation_attempt_at": "2024-07-16 16:45:44.123257433 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + }, + { + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.007", + "upload_timestamp": "2024-07-16 15:50:40.395810811 +0000 UTC", + "file_index": "6", + "base_file_id": "NwWDvdIg", + "task_id": "CXbK5zVw", + "reg_txid": "18480f0941684181b8c227b8a48aa7c36751c80218be6b435b03ad2c4d7ab209", + "activation_txid": "1f817f66fb2bec073a4b4db720a539633fa808e970b1e8e67bf048760ae27428", + "req_burn_txn_amount": 34, + "burn_txn_id": "6e33a3e055203c0d0fd7847aedffac50cf45622927700dac5f843cfe655977ec", + "req_amount": 170, + "is_concluded": true, + "cascade_metadata_ticket_id": "4d11ce243a64de54cf918a2f759fe52ff79768be44a66b6157040de3ac3eb15e", + "uuid_key": "40aeb7fd-165e-42eb-a127-a7c8628764a1", + "hash_of_original_big_file": "dd5e035fb0ce7afeede98081ae808b9cfd0d3cc94279d202599b67b29ff2c386", + "name_of_original_big_file_with_ext": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz", + "size_of_original_big_file": 129965900, + "start_block": 97036, + "done_block": 97050, + "registration_attempts": [ + { + "id": 33, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.007", + "reg_started_at": "2024-07-16 16:08:03.568282649 +0000 UTC", + "processor_sns": "154.12.246.128:24444,154.38.164.104:24444,154.12.246.130:24444,", + "finished_at": "2024-07-16 16:12:49.95335445 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ], + "activation_attempts": [ + { + "id": 28, + "file_id": "google-cloud-cli-475.0.0-linux-x86_64.tar.gz.7z.007", + "activation_attempt_at": "2024-07-16 16:45:41.925499054 +0000 UTC", + "is_successful": true, + "error_message": "" + } + ] + } +] diff --git a/src/pages/Tickets/Tickets.helpers.tsx b/src/pages/Tickets/Tickets.helpers.tsx index 51adb8ff..a2fea546 100644 --- a/src/pages/Tickets/Tickets.helpers.tsx +++ b/src/pages/Tickets/Tickets.helpers.tsx @@ -22,7 +22,7 @@ import { TAppTheme } from '@theme/index'; import breakpoints from '@theme/breakpoints'; import * as TicketsStyles from '@components/Ticket/Ticket.styles'; import { translate, translateDropdown } from '@utils/helpers/i18n'; -import { getFileIcon } from '@pages/Details/CascadeDetails/CascadeDetails.helpers'; +import { getFileIcon, getCascadeVolumeIcon } from '@pages/Details/CascadeDetails/CascadeDetails.helpers'; import noImagePlaceholder from '@assets/images/no-image-placeholder.svg'; import { @@ -85,7 +85,7 @@ export const StyledTableRow = withStyles((theme: TAppTheme) => ({ }, }))(TableRow); -const getStorageFee = (pslPrice: number, usdPrice: number) => { +export const getStorageFee = (pslPrice: number, usdPrice: number) => { if (pslPrice && usdPrice) { return ` (${formatNumber(pslPrice * usdPrice, { decimalsLength: 2 })} ${translateDropdown( 'common.usd', @@ -105,7 +105,121 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = fileType, fileName, fileSize, + tx_info, + sub_type, + contract_ticket, }) => { + if (sub_type === 'cascade_multi_volume_metadata') { + const contractTicket = contract_ticket ? JSON.parse(contract_ticket) : null; + return { + id: transactionHash, + [TXID_KEY]: ( + + + {parse(translate('pages.tickets.txID'))} + + + + + + + + + {parse(translate('pages.tickets.fileHash'))} + + {contractTicket?.sha3_256_hash_of_original_file ? + + + {formatAddress(contractTicket?.sha3_256_hash_of_original_file, 5, -5)} + + : + <> + {parse(translate('common.na'))} + + } + + + + {parse(translate('pages.tickets.fileName'))} + + {contractTicket?.name_of_original_file ? + + {contractTicket?.name_of_original_file} + : parse(translate('common.na')) + } + + + + {parse(translate('pages.tickets.cascadeOutput'))} + + + {parse(translate('pages.tickets.fileSize'))}: {formatBytes(contractTicket?.size_of_original_file_mb * 1000000 || 0)} + + {' - '} + + + + + + + {getCascadeVolumeIcon(contractTicket?.name_of_original_file || '')} + + + + + {parse(translate('pages.tickets.multisigOutputsCount'))} + + {tx_info?.multisig_outputs_count ? formatNumber(tx_info.multisig_outputs_count) : parse(translate('common.na'))} + + + + + + {getCascadeVolumeIcon(contractTicket?.name_of_original_file || '')} + + + + + {parse(translate('pages.tickets.fee'))} + + {tx_info ? + <> + {formatNumber(tx_info.multisig_tx_total_fee)} {getCurrencyName()} {getStorageFee(tx_info.multisig_tx_total_fee, usdPrice)} + : parse(translate('common.na')) + } + + + + {parse(translate('pages.tickets.timestamp'))} + + {timestamp ? formatFullDate(timestamp, { dayName: false }) : '--'} + + + + + + {getCascadeVolumeIcon(contractTicket?.name_of_original_file || '')} + + + + + ), + }; + } + return { id: transactionHash, [TXID_KEY]: ( diff --git a/src/pages/TicketsType/TicketList.tsx b/src/pages/TicketsType/TicketList.tsx index c58ddfe1..16ed26fb 100644 --- a/src/pages/TicketsType/TicketList.tsx +++ b/src/pages/TicketsType/TicketList.tsx @@ -31,6 +31,7 @@ import { TSenseRequests, ICascadeApiTicket, IInferenceAPICreditPackTicket, + IMultiVolumeTicket, } from '@utils/types/ITransactions'; import { PastelIDRegistrationTicket, @@ -43,6 +44,7 @@ import { ActionActivationTicket, ActionRegistrationTicket, InferenceAPICreditPackTicket, + CascadeMultiVolumeTicket, OfferTicket, AcceptTicket, TransferTicket, @@ -265,8 +267,10 @@ const TicketsList: React.FC = ({ | IOfferTicket | IAcceptTicket | IInferenceAPICreditPackTicket + | IMultiVolumeTicket | ITransferTicket, transactionHash: string, + sub_type?: string, ) => { switch (type) { case 'username-change': @@ -306,6 +310,9 @@ const TicketsList: React.FC = ({ case 'transfer': return ; case 'contract': + if (sub_type === 'cascade_multi_volume_metadata') { + return + } return ( ); @@ -334,7 +341,7 @@ const TicketsList: React.FC = ({ return parse(translate('pages.ticketsType.senseAndNFTCollectionTickets')); } if (ticketType === 'contract') { - return parse(translate('pages.ticketsType.inferenceAPICreditPack')); + return parse(translate('pages.ticketsType.inferenceAPICreditPack')); } const ticket = TICKET_TYPE_OPTIONS.find(t => t.value === ticketType); return ( @@ -410,6 +417,7 @@ const TicketsList: React.FC = ({ name: translateDropdown(option.name), })); }; + return ( @@ -538,11 +546,12 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, + ticket.sub_type )} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash)} + {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} ))} {!data.length && !isLoading ? ( diff --git a/src/utils/types/ITransactions.ts b/src/utils/types/ITransactions.ts index 4ccd8c8f..5b8d0e70 100644 --- a/src/utils/types/ITransactions.ts +++ b/src/utils/types/ITransactions.ts @@ -470,6 +470,7 @@ export interface ITicket { transactionHash: string; imageFileHash?: string; id: string; + sub_type?: string; } export type TTicketType = @@ -694,6 +695,17 @@ export type TicketsList = { nftId?: string; fileSize?: number; contract_ticket?: string; + tx_info?: { + compressed_size: number; + compression_ratio: string; + is_compressed: boolean; + multisig_outputs_count: number; + multisig_tx_total_fee: number; + uncompressed_size: number; + }; + secondary_key?: string; + sub_type?: string; + key?: string; }; export type TCascade = { @@ -708,6 +720,9 @@ export type TCascade = { storage_fee: number; type: string; version: number; + sub_type?: string; + secondary_key?: string; + contract_ticket?: string; }; tx_info: { compressed_size: number; @@ -850,3 +865,64 @@ export type TTransfer = { copy_serial_nr: number; transactionTime: number; }; + + +interface IRegistrationAttempt { + id: number; + file_id: string; + reg_started_at: string; + processor_sns: string; + finished_at: string; + is_successful: boolean; + error_message: string; +} + +interface IActivationAttempt { + id: number; + file_id: string; + activation_attempt_at: string; + is_successful: boolean; + error_message: string; +} + +export interface IMultiVolumeFIle { + file_id: string; + upload_timestamp: string; + file_index: string; + base_file_id: string; + task_id: string; + reg_txid: string; + activation_txid: string; + req_burn_txn_amount: number; + burn_txn_id: string; + req_amount: number; + is_concluded: boolean; + cascade_metadata_ticket_id: string; + uuid_key: string; + hash_of_original_big_file: string; + name_of_original_big_file_with_ext: string; + size_of_original_big_file: number; + start_block: number; + done_block: number; + registration_attempts: IRegistrationAttempt[], + activation_attempts: IActivationAttempt; +} + +export interface IMultiVolumeTicket { + contract_ticket: { + name_of_original_file: string; + sha3_256_hash_of_original_file: string; + size_of_original_file_mb: number; + }; + height: number; + key: string; + secondary_key: string; + sub_type: string; + timestamp: string; + type: string; + version: string; + transactionTime: number; + tx_info: string; + fileName: string; + files?: IMultiVolumeFIle[]; +} From f58af2b4f748531b6fb7ce97b8acd5e6c078a4ea Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 19 Aug 2024 17:15:00 +0700 Subject: [PATCH 06/13] update peroid default for Circulating Supply and Total Supply chart --- .../CirculatingSupply/index.tsx | 14 +++++++------- .../HistoricalStatistics/TotalSupply/index.tsx | 2 +- src/utils/helpers/statisticsLib.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx b/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx index 73d76b91..a9fe315c 100644 --- a/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx +++ b/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx @@ -16,20 +16,20 @@ import { EChartsLineChart } from '../Chart/EChartsLineChart'; function CirculatingSupply() { const [chartData, setChartData] = useState(null); const [currentBgColor, handleBgColorChange] = useBackgroundChart(); - const [period, setPeriod] = useState(periods[1][0]); + const [period, setPeriod] = useState(periods[6][0]); const [isLoading, setLoading] = useState(false); const swrData = useCirculatingSupply(period); useEffect(() => { - let currentCache = readCacheValue(cacheList.circulatingSupply) || {}; - if (currentCache[period]) { + let currentCache = readCacheValue(cacheList?.circulatingSupply) || {}; + if (currentCache[period]?.parseData) { setChartData(currentCache[period].parseData as TLineChartData); setLoading(false); } else { setLoading(true); } - if (!swrData.isLoading && swrData.data) { - const parseData = transformStatisticsChart(swrData.data, period, ''); + if (!swrData?.isLoading && swrData?.data) { + const parseData = transformStatisticsChart(swrData?.data, period, ''); setChartData(parseData); currentCache = { ...currentCache, @@ -38,7 +38,7 @@ function CirculatingSupply() { }, }; setCacheValue( - cacheList.circulatingSupply, + cacheList?.circulatingSupply, JSON.stringify({ currentCache, lastDate: Date.now(), @@ -46,7 +46,7 @@ function CirculatingSupply() { ); setLoading(false); } - }, [period, swrData.isLoading, swrData.data]); + }, [period, swrData?.isLoading, swrData?.data]); const handlePeriodFilterChange = (value: PeriodTypes) => { setPeriod(value); diff --git a/src/pages/HistoricalStatistics/TotalSupply/index.tsx b/src/pages/HistoricalStatistics/TotalSupply/index.tsx index 9bea459c..df412db9 100644 --- a/src/pages/HistoricalStatistics/TotalSupply/index.tsx +++ b/src/pages/HistoricalStatistics/TotalSupply/index.tsx @@ -16,7 +16,7 @@ import { EChartsLineChart } from '../Chart/EChartsLineChart'; function TotalSupply() { const [chartData, setChartData] = useState(null); const [currentBgColor, handleBgColorChange] = useBackgroundChart(); - const [period, setPeriod] = useState(periods[1][0]); + const [period, setPeriod] = useState(periods[6][0]); const [isLoading, setLoading] = useState(false); const swrData = useTotalSupply(period); diff --git a/src/utils/helpers/statisticsLib.ts b/src/utils/helpers/statisticsLib.ts index 578d6424..07140f42 100644 --- a/src/utils/helpers/statisticsLib.ts +++ b/src/utils/helpers/statisticsLib.ts @@ -507,7 +507,7 @@ export function transformStatisticsChart( dataY.push(value); dataX.push(new Date(trans[i].time).toLocaleString()); } - if (period === '24h' && !timestamp && checkValidateData(trans[trans.length - 1]?.time)) { + if (period === '24h' && !timestamp && trans.length && checkValidateData(trans[trans.length - 1]?.time)) { dataX.push(new Date().toLocaleString()); dataY.push(dataY[dataY.length - 1]); } From 490de5a2327ea10344b9f0df480e03e6e9421be9 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 19 Aug 2024 17:31:32 +0700 Subject: [PATCH 07/13] fixed eslint --- src/utils/helpers/statisticsLib.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/helpers/statisticsLib.ts b/src/utils/helpers/statisticsLib.ts index 07140f42..45a31e89 100644 --- a/src/utils/helpers/statisticsLib.ts +++ b/src/utils/helpers/statisticsLib.ts @@ -507,7 +507,12 @@ export function transformStatisticsChart( dataY.push(value); dataX.push(new Date(trans[i].time).toLocaleString()); } - if (period === '24h' && !timestamp && trans.length && checkValidateData(trans[trans.length - 1]?.time)) { + if ( + period === '24h' && + !timestamp && + trans.length && + checkValidateData(trans[trans.length - 1]?.time) + ) { dataX.push(new Date().toLocaleString()); dataY.push(dataY[dataY.length - 1]); } From e521472ab211de4ded9c452fcfbb7c5c471f02e0 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 26 Aug 2024 14:09:13 +0700 Subject: [PATCH 08/13] update UI for multi volume file ticket --- src/components/Ticket/index.ts | 2 ++ .../Details/CascadeDetails/CascadeDetails.tsx | 2 +- src/pages/Details/PastelIdDetails/Overview.tsx | 9 +++++++-- .../PastelIdDetails/PastelIdDetails.helpers.tsx | 5 +++++ .../Details/PastelIdDetails/TicketList.tsx | 17 ++++++++++++++++- src/pages/Movement/Movement.helpers.tsx | 6 +++++- src/utils/types/ITransactions.ts | 1 + 7 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/Ticket/index.ts b/src/components/Ticket/index.ts index b1f60a12..949fbf18 100644 --- a/src/components/Ticket/index.ts +++ b/src/components/Ticket/index.ts @@ -48,6 +48,8 @@ const getTicketTitle = (type: TTicketType, itemType = '', sub_type = '') => { return parse(translate('components.ticket.ticketsTitle.transfer')); case 'contract': return sub_type === 'cascade_multi_volume_metadata' ? parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')) : parse(translate('pages.tickets.inferenceTicketType')); + case 'cascade_multi_volume': + return parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')); default: return ''; } diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.tsx index 4ad7e6f8..428765ef 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.tsx @@ -296,7 +296,7 @@ const CascadeDetails = () => { setOpenSnackbar(false)} > diff --git a/src/pages/Details/PastelIdDetails/Overview.tsx b/src/pages/Details/PastelIdDetails/Overview.tsx index 324c31d8..dfe3c956 100644 --- a/src/pages/Details/PastelIdDetails/Overview.tsx +++ b/src/pages/Details/PastelIdDetails/Overview.tsx @@ -37,8 +37,13 @@ const Overview: React.FC = ({ const renderTicketsType = () => { const tickets = ticketsTypeList.filter(i => i.total > 0); return tickets.map(item => { - const ticket = TICKET_TYPE_OPTIONS.find(i => i.value === item.type); - const total = getTicketTypeTotal(item.type, ticketsTypeList); + let ticket = TICKET_TYPE_OPTIONS.find(i => i.value === item.type); + let total = getTicketTypeTotal(item.type, ticketsTypeList); + if (item.sub_type === 'cascade_multi_volume') { + ticket = TICKET_TYPE_OPTIONS.find(i => i.value === item.sub_type); + const newTicketsTypeList = ticketsTypeList.find((t) => t.sub_type === 'cascade_multi_volume'); + total = newTicketsTypeList?.total || 0; + } return ( {parse(translate(ticket?.name || ''))} ({total}{' '} diff --git a/src/pages/Details/PastelIdDetails/PastelIdDetails.helpers.tsx b/src/pages/Details/PastelIdDetails/PastelIdDetails.helpers.tsx index c82357ad..2821c9de 100644 --- a/src/pages/Details/PastelIdDetails/PastelIdDetails.helpers.tsx +++ b/src/pages/Details/PastelIdDetails/PastelIdDetails.helpers.tsx @@ -1,6 +1,7 @@ export type TTicketsTypeProps = { type: string; total: number; + sub_type?: string; }; export const TICKET_TYPE_OPTIONS = [ @@ -56,6 +57,10 @@ export const TICKET_TYPE_OPTIONS = [ name: 'pages.pastelIdDetails.transfer', value: 'transfer', }, + { + name: 'components.ticket.ticketsTitle.cascadeMultiVolume', + value: 'cascade_multi_volume', + }, ]; export const getTicketTypeTotal = (type: string, ticketsTypeList: TTicketsTypeProps[]) => { diff --git a/src/pages/Details/PastelIdDetails/TicketList.tsx b/src/pages/Details/PastelIdDetails/TicketList.tsx index 6944eaa3..b22a1bfe 100644 --- a/src/pages/Details/PastelIdDetails/TicketList.tsx +++ b/src/pages/Details/PastelIdDetails/TicketList.tsx @@ -26,6 +26,8 @@ import { TTicketType, TSenseRequests, ICascadeApiTicket, + IInferenceAPICreditPackTicket, + IMultiVolumeTicket, } from '@utils/types/ITransactions'; import { PastelIDRegistrationTicket, @@ -37,6 +39,8 @@ import { NFTRoyaltyTicket, ActionActivationTicket, ActionRegistrationTicket, + InferenceAPICreditPackTicket, + CascadeMultiVolumeTicket, OfferTicket, AcceptTicket, TransferTicket, @@ -224,8 +228,11 @@ const TicketsList: React.FC = ({ | IActionActivationTicket | IOfferTicket | IAcceptTicket + | IInferenceAPICreditPackTicket + | IMultiVolumeTicket | ITransferTicket, transactionHash: string, + sub_type?: string, ) => { switch (type) { case 'username-change': @@ -264,6 +271,13 @@ const TicketsList: React.FC = ({ return ; case 'transfer': return ; + case 'contract': + if (sub_type === 'cascade_multi_volume_metadata') { + return + } + return ( + + ); default: return ; } @@ -357,11 +371,12 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, + ticket.sub_type )} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash)} + {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} ))} {!data.length && !isLoading ? ( diff --git a/src/pages/Movement/Movement.helpers.tsx b/src/pages/Movement/Movement.helpers.tsx index e7d71ccd..db8d15f9 100644 --- a/src/pages/Movement/Movement.helpers.tsx +++ b/src/pages/Movement/Movement.helpers.tsx @@ -33,7 +33,11 @@ export const getTicketsTypeList = (tickets: string) => { const newTickets = JSON.parse(tickets); const counts: TCounts = {}; for (let i = 0; i < newTickets.length; i += 1) { - counts[newTickets[i].type] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + if (newTickets[i].sub_type === 'cascade_multi_volume_metadata') { + counts['cascade_multi_volume'] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + } else { + counts[newTickets[i].type] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + } } const keys = Object.keys(counts); diff --git a/src/utils/types/ITransactions.ts b/src/utils/types/ITransactions.ts index 5b8d0e70..fe35470d 100644 --- a/src/utils/types/ITransactions.ts +++ b/src/utils/types/ITransactions.ts @@ -486,6 +486,7 @@ export type TTicketType = | 'offer' | 'accept' | 'contract' + | 'cascade_multi_volume' | 'transfer'; export type TAlternativeNsfwScores = { From 9d6c0ef1a2db189217a57a16c556ff529c0635a4 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 26 Aug 2024 14:09:41 +0700 Subject: [PATCH 09/13] fix issues --- src/pages/Details/CascadeDetails/CascadeDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.tsx index 428765ef..a1a1fb3b 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.tsx @@ -296,7 +296,7 @@ const CascadeDetails = () => { setOpenSnackbar(false)} > From 8c5351bbe748e2d712488e29034317bd88692c07 Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 26 Aug 2024 14:43:34 +0700 Subject: [PATCH 10/13] fix eslints --- .../Ticket/CascadeMultiVolumeTicket.tsx | 16 ++++++++++------ src/components/Ticket/Ticket.helpers.ts | 10 ++++++++++ .../CascadeDetails/CascadeDetails.helpers.tsx | 2 +- src/pages/Movement/Movement.helpers.tsx | 2 +- src/pages/Tickets/Tickets.helpers.tsx | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/components/Ticket/CascadeMultiVolumeTicket.tsx b/src/components/Ticket/CascadeMultiVolumeTicket.tsx index de021769..6ccb8647 100644 --- a/src/components/Ticket/CascadeMultiVolumeTicket.tsx +++ b/src/components/Ticket/CascadeMultiVolumeTicket.tsx @@ -22,8 +22,9 @@ import { formatBytes, formatAddress } from '@utils/helpers/format'; import { fakeFilesData } from '@pages/Details/CascadeDetails/mockup'; import * as CascadeDetailsStyles from '@pages/Details/CascadeDetails/CascadeDetails.styles'; import * as TicketStyles from '@components/Ticket/Ticket.styles'; +import { useUsdPrice } from '@hooks/useTransactionDetails'; -import { useStorageFee } from './Ticket.helpers'; +import { getStorageFee } from './Ticket.helpers'; import * as Styles from './Ticket.styles'; interface ICascadeMultiVolumeTicketProps { @@ -33,6 +34,7 @@ interface ICascadeMultiVolumeTicketProps { const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { const [isExpanded, setIsExpanded] = useState(false); + const { usdPrice } = useUsdPrice(); return ( { {files?.map((item) => { - const reqBurnTxnAmount = useStorageFee(item.req_burn_txn_amount); - const reqAmount = useStorageFee(item.req_amount); + const reqBurnTxnAmount = getStorageFee(item.req_burn_txn_amount, usdPrice); + const reqAmount = getStorageFee(item.req_amount, usdPrice); return ( @@ -121,7 +123,7 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { {parse(translate('components.ticket.multiVolume.reqBurnTxnAmount'))} - {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {reqBurnTxnAmount.storageFee} + {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {reqBurnTxnAmount} @@ -142,7 +144,7 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { {parse(translate('components.ticket.multiVolume.reqAmount'))} - {formatNumber(item.req_amount)} {getCurrencyName()} {reqAmount.storageFee} + {formatNumber(item.req_amount)} {getCurrencyName()} {reqAmount} @@ -212,11 +214,13 @@ const CascadeMultiVolumeTicket: React.FC = ({ ticket, showFull = false, }) => { + const { usdPrice } = useUsdPrice(); + if (!ticket?.tx_info) { return null; } const txInfo = JSON.parse(ticket.tx_info); - const { storageFee } = useStorageFee(txInfo.multisig_tx_total_fee); + const storageFee = getStorageFee(txInfo.multisig_tx_total_fee, usdPrice); if (showFull) { return ( diff --git a/src/components/Ticket/Ticket.helpers.ts b/src/components/Ticket/Ticket.helpers.ts index 665af7c4..2c699d8b 100644 --- a/src/components/Ticket/Ticket.helpers.ts +++ b/src/components/Ticket/Ticket.helpers.ts @@ -24,3 +24,13 @@ export const useStorageFee = (pslPrice: number) => { storageFee, }; }; + + +export const getStorageFee = (pslPrice: number, usdPrice: number) => { + if (pslPrice && usdPrice) { + return ` (${formatNumber(pslPrice * usdPrice, { decimalsLength: 2 })} ${translateDropdown( + 'common.usd', + )})` + } + return ` (0 ${translateDropdown('common.usd')})` +} diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx index 4a58c4f9..07117a52 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx @@ -89,7 +89,7 @@ export const getCascadeVolumeIcon = (file_name: string) => { return mov; case 'zip': case 'tar': - case 'tar': + case 'rar': case 'gz': case '7z': return zip; diff --git a/src/pages/Movement/Movement.helpers.tsx b/src/pages/Movement/Movement.helpers.tsx index db8d15f9..ce9d19ed 100644 --- a/src/pages/Movement/Movement.helpers.tsx +++ b/src/pages/Movement/Movement.helpers.tsx @@ -34,7 +34,7 @@ export const getTicketsTypeList = (tickets: string) => { const counts: TCounts = {}; for (let i = 0; i < newTickets.length; i += 1) { if (newTickets[i].sub_type === 'cascade_multi_volume_metadata') { - counts['cascade_multi_volume'] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + counts.cascade_multi_volume = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; } else { counts[newTickets[i].type] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; } diff --git a/src/pages/Tickets/Tickets.helpers.tsx b/src/pages/Tickets/Tickets.helpers.tsx index a2fea546..48d60ca6 100644 --- a/src/pages/Tickets/Tickets.helpers.tsx +++ b/src/pages/Tickets/Tickets.helpers.tsx @@ -161,7 +161,7 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = {parse(translate('pages.tickets.cascadeOutput'))} - {parse(translate('pages.tickets.fileSize'))}: {formatBytes(contractTicket?.size_of_original_file_mb * 1000000 || 0)} + {parse(translate('pages.tickets.fileSize'))}: {contractTicket?.size_of_original_file_mb ? formatBytes(contractTicket.size_of_original_file_mb * 1000000) : 0} {' - '} Date: Mon, 26 Aug 2024 18:23:15 +0700 Subject: [PATCH 11/13] add check valid pastel address --- .../AddressDetails/AddressDetails.helpers.tsx | 14 ++++ .../AddressDetails/AddressDetails.styles.ts | 15 +++++ .../Details/AddressDetails/AddressDetails.tsx | 40 +++++++---- .../Details/AddressDetails/BalanceHistory.tsx | 66 ++++++++++--------- .../Details/AddressDetails/DirectionChart.tsx | 42 +++++++----- 5 files changed, 117 insertions(+), 60 deletions(-) diff --git a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx index 70cc60be..c623784d 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx @@ -140,3 +140,17 @@ export const transformDirectionChartData = (data: TChartStatisticsResponse[] | n dataY, }; }; + +export const isValidAddress = (address: string) => { + const defaultCurrencyName = process.env.REACT_APP_EXPLORER_DEFAULT_CURRENCY_NAME; + if (defaultCurrencyName === 'PSL' && address?.length === 35 && address.startsWith('Pt')) { + return true; + } + if (defaultCurrencyName === 'LSP' && address?.length === 35 && address.startsWith('tP')) { + return true; + } + if (defaultCurrencyName === 'DEV' && address?.length === 36 && address.startsWith('44o')) { + return true; + } + return false; +} diff --git a/src/pages/Details/AddressDetails/AddressDetails.styles.ts b/src/pages/Details/AddressDetails/AddressDetails.styles.ts index 0cd39fb6..1f9f92a1 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.styles.ts +++ b/src/pages/Details/AddressDetails/AddressDetails.styles.ts @@ -1,5 +1,6 @@ import styled from 'styled-components'; import { Grid } from '@mui/material'; +import { Typography as MuiTypography } from '@mui/material'; import themeVariant from '@theme/variants'; @@ -248,6 +249,10 @@ export const TitleWrapper = styled('div')` border-radius: 10px 10px 0 0; overflow: hidden; + &.large-padding { + padding: 14px 16px; + } + h4 { margin: 0; } @@ -554,3 +559,13 @@ export const FireIcon = styled.div` width: 18px; } `; + +export const NoData = styled(MuiTypography) ` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + font-size: 24px; + font-weight: 600; +` diff --git a/src/pages/Details/AddressDetails/AddressDetails.tsx b/src/pages/Details/AddressDetails/AddressDetails.tsx index 7c66a876..44c95cc0 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.tsx @@ -23,6 +23,7 @@ import { DATA_FETCH_LIMIT, DATA_DEFAULT_SORT, generateLatestTransactions, + isValidAddress, } from './AddressDetails.helpers'; import { ADDRESS_TRANSACTION_TIMESTAMP_KEY, columns } from './AddressDetails.columns'; import BalanceHistory from './BalanceHistory'; @@ -82,7 +83,8 @@ const AddressDetails = () => { !swrData.isLoading && !swrData?.data?.totalReceived && !swrData?.data?.totalSent && - !swrData?.data?.balance?.length + !swrData?.data?.balance?.length && + !isValidAddress(id || '') ) { navigate(ROUTES.NOT_FOUND); } @@ -140,17 +142,19 @@ const AddressDetails = () => { const generateTitle = () => { return ( - +

{parse(translate('pages.addressDetails.latestTransactions'))}

- - {status === 'downloading' - ? parse(translate('pages.addressDetails.downloading')) - : parse(translate('pages.addressDetails.downloadCSV'))} - + {addresses?.length ? + + {status === 'downloading' + ? parse(translate('pages.addressDetails.downloading')) + : parse(translate('pages.addressDetails.downloadCSV'))} + : null + }
); }; @@ -173,7 +177,7 @@ const AddressDetails = () => { - {addresses ? ( + {addresses?.length ? ( { customLoading={isLoading} /> ) : null} - {isLoading && !addresses?.length ? ( + {isLoading ? ( {generateTitle()} @@ -202,6 +206,16 @@ const AddressDetails = () => { ) : null} + {!isLoading && !addresses?.length ? ( + + {generateTitle()} + + + {parse(translate('common.noData'))} + + + + ) : null} diff --git a/src/pages/Details/AddressDetails/BalanceHistory.tsx b/src/pages/Details/AddressDetails/BalanceHistory.tsx index 6ff901df..d5104079 100644 --- a/src/pages/Details/AddressDetails/BalanceHistory.tsx +++ b/src/pages/Details/AddressDetails/BalanceHistory.tsx @@ -160,23 +160,25 @@ const BalanceHistory: React.FC = ({ id }) => { outgoingSum={swrData?.data?.totalSent} incomingSum={swrData?.data?.totalReceived} /> - - {parse(translate('pages.historicalStatistics.period'))}: -
- {periods[1].map(_period => ( - handlePeriodFilterChange(_period)} - type="button" - key={`button-filter-${_period}`} - > - {_period} - - ))} -
-
+ {chartData?.dataX?.length ? + + {parse(translate('pages.historicalStatistics.period'))}: +
+ {periods[1].map(_period => ( + handlePeriodFilterChange(_period)} + type="button" + key={`button-filter-${_period}`} + > + {_period} + + ))} +
+
: null + } {isLoading || swrData.isLoading ? ( @@ -185,19 +187,23 @@ const BalanceHistory: React.FC = ({ id }) => { {parse(translate('common.loadingData'))} ) : ( - + <> + {chartData?.dataX?.length ? + : {parse(translate('common.noData'))} + } + )} diff --git a/src/pages/Details/AddressDetails/DirectionChart.tsx b/src/pages/Details/AddressDetails/DirectionChart.tsx index 585c9bf5..7312479f 100644 --- a/src/pages/Details/AddressDetails/DirectionChart.tsx +++ b/src/pages/Details/AddressDetails/DirectionChart.tsx @@ -90,7 +90,7 @@ const DirectionItem: React.FC = ({ setChartData(currentCache[selectedPeriod].parseData as TLineChartData); setLoading(false); } else { - setLoading(true); + setLoading(swrData.isLoading); } if (!swrData.isLoading && swrData.data) { const parseData = transformDirectionChartData( @@ -137,19 +137,21 @@ const DirectionItem: React.FC = ({ {title} - - {parse(translate('pages.historicalStatistics.period'))}: - {periods[10].map(_period => ( - handlePeriodChange(_period)} - type="button" - key={`${chartName}-filter-${_period}`} - > - {_period} - - ))} - + {chartData?.dataX?.length ? + + {parse(translate('pages.historicalStatistics.period'))}: + {periods[10].map(_period => ( + handlePeriodChange(_period)} + type="button" + key={`${chartName}-filter-${_period}`} + > + {_period} + + ))} + : null + } @@ -158,7 +160,8 @@ const DirectionItem: React.FC = ({ {parse(translate('common.loadingData'))} - ) : ( + ) : null} + {!isLoading && chartData?.dataX?.length ? = ({ className="line-chart" seriesName={seriesName} chartColor={chartColor} - /> - )} + /> : null + } + {!isLoading && !chartData?.dataX?.length ? + + {parse(translate('common.noData'))} + : null + } ); From 38ec7ba0ce9fa9ac128b8654bdfbd84c9d5d47af Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Mon, 26 Aug 2024 18:27:41 +0700 Subject: [PATCH 12/13] fix eslint --- .../LinearProgress/LinearProgress.styles.ts | 5 +- .../LinearProgress/LinearProgress.tsx | 2 +- .../Ticket/CascadeMultiVolumeTicket.tsx | 118 +++++++----------- src/components/Ticket/Ticket.helpers.ts | 7 +- src/components/Ticket/index.ts | 4 +- .../AddressDetails/AddressDetails.helpers.tsx | 2 +- .../AddressDetails/AddressDetails.styles.ts | 7 +- .../Details/AddressDetails/AddressDetails.tsx | 8 +- .../Details/AddressDetails/BalanceHistory.tsx | 20 +-- .../Details/AddressDetails/DirectionChart.tsx | 38 +++--- src/pages/Details/BlockDetails/Tickets.tsx | 13 +- .../CascadeDetails/CascadeDetails.helpers.tsx | 4 +- .../CascadeDetails/CascadeDetails.styles.ts | 6 +- .../Details/CascadeDetails/CascadeDetails.tsx | 29 +++-- src/pages/Details/CascadeDetails/FileInfo.tsx | 52 ++++---- .../Details/PastelIdDetails/Overview.tsx | 2 +- .../Details/PastelIdDetails/TicketList.tsx | 11 +- src/pages/Movement/Movement.helpers.tsx | 8 +- src/pages/Tickets/Tickets.helpers.tsx | 79 +++++++----- src/pages/TicketsType/TicketList.tsx | 13 +- src/utils/helpers/__test__/encryption.test.ts | 16 +-- src/utils/types/ITransactions.ts | 3 +- 22 files changed, 220 insertions(+), 227 deletions(-) diff --git a/src/components/Progress/LinearProgress/LinearProgress.styles.ts b/src/components/Progress/LinearProgress/LinearProgress.styles.ts index 1512fa8b..f508c48d 100644 --- a/src/components/Progress/LinearProgress/LinearProgress.styles.ts +++ b/src/components/Progress/LinearProgress/LinearProgress.styles.ts @@ -1,8 +1,5 @@ import styled from 'styled-components'; -import { - LinearProgress as MuiLinearProgress, - Typography as MuiTypography, -} from '@mui/material'; +import { LinearProgress as MuiLinearProgress, Typography as MuiTypography } from '@mui/material'; export const LinearProgress = styled(MuiLinearProgress)` height: 14px; diff --git a/src/components/Progress/LinearProgress/LinearProgress.tsx b/src/components/Progress/LinearProgress/LinearProgress.tsx index 4311c3b7..355c7d65 100644 --- a/src/components/Progress/LinearProgress/LinearProgress.tsx +++ b/src/components/Progress/LinearProgress/LinearProgress.tsx @@ -19,6 +19,6 @@ const LinearProgressComponent: React.FC = ({ value, descrip LinearProgressComponent.defaultProps = { description: '', -} +}; export default LinearProgressComponent; diff --git a/src/components/Ticket/CascadeMultiVolumeTicket.tsx b/src/components/Ticket/CascadeMultiVolumeTicket.tsx index 6ccb8647..9e1b0e25 100644 --- a/src/components/Ticket/CascadeMultiVolumeTicket.tsx +++ b/src/components/Ticket/CascadeMultiVolumeTicket.tsx @@ -12,10 +12,7 @@ import { getCurrencyName } from '@utils/appInfo'; import RouterLink from '@components/RouterLink/RouterLink'; import { formattedDate, formatFullDate } from '@utils/helpers/date/date'; import { formatNumber } from '@utils/helpers/formatNumbers/formatNumbers'; -import { - IMultiVolumeTicket, - IMultiVolumeFIle, -} from '@utils/types/ITransactions'; +import { IMultiVolumeTicket, IMultiVolumeFIle } from '@utils/types/ITransactions'; import * as ROUTES from '@utils/constants/routes'; import { translate } from '@utils/helpers/i18n'; import { formatBytes, formatAddress } from '@utils/helpers/format'; @@ -32,7 +29,7 @@ interface ICascadeMultiVolumeTicketProps { showFull?: boolean; } -const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { +const Files = ({ files }: { files: IMultiVolumeFIle[] }) => { const [isExpanded, setIsExpanded] = useState(false); const { usdPrice } = useUsdPrice(); @@ -44,9 +41,7 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { - - {parse(translate('pages.tickets.files'))}: - + {parse(translate('pages.tickets.files'))}: @@ -62,19 +57,17 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { - {files?.map((item) => { + {files?.map(item => { const reqBurnTxnAmount = getStorageFee(item.req_burn_txn_amount, usdPrice); const reqAmount = getStorageFee(item.req_amount, usdPrice); return ( - + {parse(translate('components.ticket.multiVolume.fileID'))} - - {item.file_id} - + {item.file_id} @@ -88,9 +81,7 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { {parse(translate('components.ticket.multiVolume.taskID'))} - - {item.task_id} - + {item.task_id} @@ -123,7 +114,8 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { {parse(translate('components.ticket.multiVolume.reqBurnTxnAmount'))} - {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {reqBurnTxnAmount} + {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()}{' '} + {reqBurnTxnAmount} @@ -202,13 +194,13 @@ const Files = ({ files }: { files: IMultiVolumeFIle[]}) => { - ) + ); })}
- ) -} + ); +}; const CascadeMultiVolumeTicket: React.FC = ({ ticket, @@ -264,12 +256,12 @@ const CascadeMultiVolumeTicket: React.FC = ({ - - {parse(translate('pages.cascade.dataHash'))} - + {parse(translate('pages.cascade.dataHash'))} - {ticket.contract_ticket.sha3_256_hash_of_original_file} + + {ticket.contract_ticket.sha3_256_hash_of_original_file} + @@ -285,50 +277,37 @@ const CascadeMultiVolumeTicket: React.FC = ({ - {parse( - translate( - 'components.ticket.multiVolume.totalFee', - ), - )} + {parse(translate('components.ticket.multiVolume.totalFee'))} - {txInfo.multisig_tx_total_fee ? formatNumber(txInfo.multisig_tx_total_fee) : parse(translate('common.na'))} {getCurrencyName()} {storageFee} + {txInfo.multisig_tx_total_fee + ? formatNumber(txInfo.multisig_tx_total_fee) + : parse(translate('common.na'))}{' '} + {getCurrencyName()} {storageFee} - {parse( - translate( - 'components.ticket.multiVolume.totalFile', - ), - )} + {parse(translate('components.ticket.multiVolume.totalFile'))} - - {txInfo.multisig_outputs_count} - + {txInfo.multisig_outputs_count} - - {parse( - translate( - 'pages.cascade.fileSize', - ), - )} - + {parse(translate('pages.cascade.fileSize'))} - {ticket.contract_ticket.size_of_original_file_mb ? - formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) : parse(translate('common.na')) - } + {ticket.contract_ticket.size_of_original_file_mb + ? formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) + : parse(translate('common.na'))} @@ -392,12 +371,12 @@ const CascadeMultiVolumeTicket: React.FC = ({ - - {parse(translate('pages.cascade.dataHash'))} - + {parse(translate('pages.cascade.dataHash'))} - {ticket.contract_ticket.sha3_256_hash_of_original_file} + + {ticket.contract_ticket.sha3_256_hash_of_original_file} + @@ -413,50 +392,37 @@ const CascadeMultiVolumeTicket: React.FC = ({ - {parse( - translate( - 'components.ticket.multiVolume.totalFee', - ), - )} + {parse(translate('components.ticket.multiVolume.totalFee'))} - {txInfo.multisig_tx_total_fee ? formatNumber(txInfo.multisig_tx_total_fee) : parse(translate('common.na'))} {getCurrencyName()} {storageFee} + {txInfo.multisig_tx_total_fee + ? formatNumber(txInfo.multisig_tx_total_fee) + : parse(translate('common.na'))}{' '} + {getCurrencyName()} {storageFee} - {parse( - translate( - 'components.ticket.multiVolume.totalFile', - ), - )} + {parse(translate('components.ticket.multiVolume.totalFile'))} - - {txInfo.multisig_outputs_count} - + {txInfo.multisig_outputs_count} - - {parse( - translate( - 'pages.cascade.fileSize', - ), - )} - + {parse(translate('pages.cascade.fileSize'))} - {ticket.contract_ticket.size_of_original_file_mb ? - formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) : parse(translate('common.na')) - } + {ticket.contract_ticket.size_of_original_file_mb + ? formatBytes(ticket.contract_ticket.size_of_original_file_mb * 1000000) + : parse(translate('common.na'))} diff --git a/src/components/Ticket/Ticket.helpers.ts b/src/components/Ticket/Ticket.helpers.ts index 2c699d8b..12f7f62c 100644 --- a/src/components/Ticket/Ticket.helpers.ts +++ b/src/components/Ticket/Ticket.helpers.ts @@ -25,12 +25,11 @@ export const useStorageFee = (pslPrice: number) => { }; }; - export const getStorageFee = (pslPrice: number, usdPrice: number) => { if (pslPrice && usdPrice) { return ` (${formatNumber(pslPrice * usdPrice, { decimalsLength: 2 })} ${translateDropdown( 'common.usd', - )})` + )})`; } - return ` (0 ${translateDropdown('common.usd')})` -} + return ` (0 ${translateDropdown('common.usd')})`; +}; diff --git a/src/components/Ticket/index.ts b/src/components/Ticket/index.ts index 949fbf18..0ba0ba3b 100644 --- a/src/components/Ticket/index.ts +++ b/src/components/Ticket/index.ts @@ -47,7 +47,9 @@ const getTicketTitle = (type: TTicketType, itemType = '', sub_type = '') => { case 'transfer': return parse(translate('components.ticket.ticketsTitle.transfer')); case 'contract': - return sub_type === 'cascade_multi_volume_metadata' ? parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')) : parse(translate('pages.tickets.inferenceTicketType')); + return sub_type === 'cascade_multi_volume_metadata' + ? parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')) + : parse(translate('pages.tickets.inferenceTicketType')); case 'cascade_multi_volume': return parse(translate('components.ticket.ticketsTitle.cascadeMultiVolume')); default: diff --git a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx index c623784d..28b24d47 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx @@ -153,4 +153,4 @@ export const isValidAddress = (address: string) => { return true; } return false; -} +}; diff --git a/src/pages/Details/AddressDetails/AddressDetails.styles.ts b/src/pages/Details/AddressDetails/AddressDetails.styles.ts index 1f9f92a1..d65dbed9 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.styles.ts +++ b/src/pages/Details/AddressDetails/AddressDetails.styles.ts @@ -1,6 +1,5 @@ import styled from 'styled-components'; -import { Grid } from '@mui/material'; -import { Typography as MuiTypography } from '@mui/material'; +import { Grid, Typography as MuiTypography } from '@mui/material'; import themeVariant from '@theme/variants'; @@ -560,7 +559,7 @@ export const FireIcon = styled.div` } `; -export const NoData = styled(MuiTypography) ` +export const NoData = styled(MuiTypography)` display: flex; align-items: center; justify-content: center; @@ -568,4 +567,4 @@ export const NoData = styled(MuiTypography) ` width: 100%; font-size: 24px; font-weight: 600; -` +`; diff --git a/src/pages/Details/AddressDetails/AddressDetails.tsx b/src/pages/Details/AddressDetails/AddressDetails.tsx index 44c95cc0..9616c590 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.tsx @@ -142,9 +142,9 @@ const AddressDetails = () => { const generateTitle = () => { return ( - +

{parse(translate('pages.addressDetails.latestTransactions'))}

- {addresses?.length ? + {addresses?.length ? ( { {status === 'downloading' ? parse(translate('pages.addressDetails.downloading')) : parse(translate('pages.addressDetails.downloadCSV'))} - : null - } + + ) : null}
); }; diff --git a/src/pages/Details/AddressDetails/BalanceHistory.tsx b/src/pages/Details/AddressDetails/BalanceHistory.tsx index d5104079..a49f80ed 100644 --- a/src/pages/Details/AddressDetails/BalanceHistory.tsx +++ b/src/pages/Details/AddressDetails/BalanceHistory.tsx @@ -160,7 +160,7 @@ const BalanceHistory: React.FC = ({ id }) => { outgoingSum={swrData?.data?.totalSent} incomingSum={swrData?.data?.totalReceived} /> - {chartData?.dataX?.length ? + {chartData?.dataX?.length ? ( {parse(translate('pages.historicalStatistics.period'))}:
@@ -177,8 +177,8 @@ const BalanceHistory: React.FC = ({ id }) => { ))}
-
: null - } + + ) : null} {isLoading || swrData.isLoading ? ( @@ -188,7 +188,7 @@ const BalanceHistory: React.FC = ({ id }) => { ) : ( <> - {chartData?.dataX?.length ? + {chartData?.dataX?.length ? ( = ({ id }) => { className="line-chart" period={selectedPeriod} seriesName={`pages.addressDetails.balanceHistory.${ - isBurnAddress && selectedChartType === 'received' ? 'totalBurned' : selectedChartType + isBurnAddress && selectedChartType === 'received' + ? 'totalBurned' + : selectedChartType }`} chartColor={getChartColor()} - /> : {parse(translate('common.noData'))} - } + /> + ) : ( + + {parse(translate('common.noData'))} + + )} )} diff --git a/src/pages/Details/AddressDetails/DirectionChart.tsx b/src/pages/Details/AddressDetails/DirectionChart.tsx index 7312479f..d6e44a9f 100644 --- a/src/pages/Details/AddressDetails/DirectionChart.tsx +++ b/src/pages/Details/AddressDetails/DirectionChart.tsx @@ -137,21 +137,21 @@ const DirectionItem: React.FC = ({ {title} - {chartData?.dataX?.length ? + {chartData?.dataX?.length ? ( {parse(translate('pages.historicalStatistics.period'))}: - {periods[10].map(_period => ( - handlePeriodChange(_period)} - type="button" - key={`${chartName}-filter-${_period}`} - > - {_period} - - ))} - : null - } + {periods[10].map(_period => ( + handlePeriodChange(_period)} + type="button" + key={`${chartName}-filter-${_period}`} + > + {_period} + + ))} + + ) : null} @@ -161,7 +161,7 @@ const DirectionItem: React.FC = ({ {parse(translate('common.loadingData'))} ) : null} - {!isLoading && chartData?.dataX?.length ? + {!isLoading && chartData?.dataX?.length ? ( = ({ className="line-chart" seriesName={seriesName} chartColor={chartColor} - /> : null - } - {!isLoading && !chartData?.dataX?.length ? + /> + ) : null} + {!isLoading && !chartData?.dataX?.length ? ( {parse(translate('common.noData'))} - : null - } + + ) : null} ); diff --git a/src/pages/Details/BlockDetails/Tickets.tsx b/src/pages/Details/BlockDetails/Tickets.tsx index 0b0b0729..7374bfd5 100644 --- a/src/pages/Details/BlockDetails/Tickets.tsx +++ b/src/pages/Details/BlockDetails/Tickets.tsx @@ -413,7 +413,7 @@ const TicketsList: React.FC = ({ return ; case 'contract': if (sub_type === 'cascade_multi_volume_metadata') { - return + return ; } return ( @@ -433,7 +433,7 @@ const TicketsList: React.FC = ({ data[0].type as TTicketType, (data[0].data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, - data[0].sub_type + data[0].sub_type, )} @@ -475,7 +475,7 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket) ?.collection_ticket?.item_type, - ticket.sub_type + ticket.sub_type, )}
@@ -483,7 +483,12 @@ const TicketsList: React.FC = ({ ) : null} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} + {renderContent( + ticket.type, + ticket.data.ticket, + ticket.transactionHash, + ticket.sub_type, + )} ))}
diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx index 07117a52..4b654cb0 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.helpers.tsx @@ -56,8 +56,8 @@ export const getCascadeVolumeIcon = (file_name: string) => { if (!file_name) { return unknown; } - const parseFileName = file_name.split('.') - const fileExtension = parseFileName[parseFileName.length - 1] + const parseFileName = file_name.split('.'); + const fileExtension = parseFileName[parseFileName.length - 1]; switch (fileExtension) { case 'jpg': return jpg; diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts index 05684de0..86505964 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts +++ b/src/pages/Details/CascadeDetails/CascadeDetails.styles.ts @@ -270,15 +270,15 @@ export const FileItem = styled.div` height: 20px; &.completed { - color: #00D097; + color: #00d097; } &.fail { - color: #FF754C; + color: #ff754c; } &.downloading { - color: #BAA806; + color: #baa806; } } diff --git a/src/pages/Details/CascadeDetails/CascadeDetails.tsx b/src/pages/Details/CascadeDetails/CascadeDetails.tsx index a1a1fb3b..1dd87a0b 100644 --- a/src/pages/Details/CascadeDetails/CascadeDetails.tsx +++ b/src/pages/Details/CascadeDetails/CascadeDetails.tsx @@ -97,7 +97,10 @@ const CascadeDetails = () => { dataDownloadedMegabytes: res.data.data_downloaded_megabytes, details: res.data.details, }); - if ((res.data.task_status === 'Completed' || res.data.task_status === 'Failed') && timeout) { + if ( + (res.data.task_status === 'Completed' || res.data.task_status === 'Failed') && + timeout + ) { clearInterval(timeout); } } @@ -108,7 +111,7 @@ const CascadeDetails = () => { clearInterval(timeout); } }); - } + }; useEffect(() => { window.addEventListener('beforeunload', handleReloadPage); @@ -121,9 +124,9 @@ const CascadeDetails = () => { if (fileId) { timeout = setInterval(() => { checkDownloadStatus(fileId); - }, 5000) // ~ 1s + }, 5000); // ~ 1s } - }, [fileId]) + }, [fileId]); const decodeApiTicket = (apiTicket: string) => { let data = null; @@ -195,7 +198,7 @@ const CascadeDetails = () => { // link.click(); // setStatus('done'); if (res?.data?.file_id) { - setFileId(res.data.file_id) + setFileId(res.data.file_id); } }) .catch(() => { @@ -288,7 +291,7 @@ const CascadeDetails = () => { @@ -305,16 +308,20 @@ const CascadeDetails = () => { {parse(translate('pages.cascade.totalVolumes'))}: {downloadStatus.totalVolumes}
- {parse(translate('pages.cascade.downloadedVolumes'))}: {downloadStatus.downloadedVolumes} + {parse(translate('pages.cascade.downloadedVolumes'))}:{' '} + {downloadStatus.downloadedVolumes}
- {parse(translate('pages.cascade.volumesDownloadInProgress'))}: {downloadStatus.volumesDownloadInProgress} + {parse(translate('pages.cascade.volumesDownloadInProgress'))}:{' '} + {downloadStatus.volumesDownloadInProgress}
- {parse(translate('pages.cascade.volumesPendingDownload'))}: {downloadStatus.volumesPendingDownload} + {parse(translate('pages.cascade.volumesPendingDownload'))}:{' '} + {downloadStatus.volumesPendingDownload}
- {parse(translate('pages.cascade.volumesDownloadFailed'))}: {downloadStatus.volumesDownloadFailed} + {parse(translate('pages.cascade.volumesDownloadFailed'))}:{' '} + {downloadStatus.volumesDownloadFailed}
@@ -335,7 +342,7 @@ const CascadeDetails = () => { - ) + ); } return cascadeData ? ( diff --git a/src/pages/Details/CascadeDetails/FileInfo.tsx b/src/pages/Details/CascadeDetails/FileInfo.tsx index 6b06ce03..5fa16d2d 100644 --- a/src/pages/Details/CascadeDetails/FileInfo.tsx +++ b/src/pages/Details/CascadeDetails/FileInfo.tsx @@ -23,7 +23,7 @@ import * as TicketStyles from '@components/Ticket/Ticket.styles'; import RqIds from './RqIds'; import { getFileIcon, getCascadeVolumeIcon } from './CascadeDetails.helpers'; import * as Styles from './CascadeDetails.styles'; -import { fakeFilesData } from './mockup' +import { fakeFilesData } from './mockup'; type TCascadeData = { data_hash: string; @@ -109,9 +109,9 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('pages.cascade.fileSize'))}: - {data.size_of_original_file_mb ? - formatBytes(data.size_of_original_file_mb * 1000000) : parse(translate('common.na')) - } + {data.size_of_original_file_mb + ? formatBytes(data.size_of_original_file_mb * 1000000) + : parse(translate('common.na'))} @@ -121,11 +121,13 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('pages.cascade.dataHash'))}: - {data.sha3_256_hash_of_original_file ? + {data.sha3_256_hash_of_original_file ? ( {formatAddress(data.sha3_256_hash_of_original_file, 30, -5)} - : parse(translate('common.na')) - } + + ) : ( + parse(translate('common.na')) + )} @@ -170,7 +172,9 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('pages.tickets.multisigOutputsCount'))}: - {data?.tx_info?.multisig_outputs_count ? formatNumber(data?.tx_info.multisig_outputs_count) : parse(translate('common.na'))} + {data?.tx_info?.multisig_outputs_count + ? formatNumber(data?.tx_info.multisig_outputs_count) + : parse(translate('common.na'))} @@ -180,11 +184,13 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('pages.tickets.key'))}: - {data?.ticket?.key ? + {data?.ticket?.key ? ( {formatAddress(getDataHash(data?.ticket?.key), 30, -5)} - : parse(translate('common.na')) - } + + ) : ( + parse(translate('common.na')) + )} @@ -195,22 +201,18 @@ const FileInfo: React.FC = ({ data }) => { - - {parse(translate('pages.tickets.files'))} - + {parse(translate('pages.tickets.files'))} - {fakeFilesData?.map((item) => ( - + {fakeFilesData?.map(item => ( + {parse(translate('components.ticket.multiVolume.fileID'))} - - {item.file_id} - + {item.file_id} @@ -224,9 +226,7 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('components.ticket.multiVolume.taskID'))} - - {item.task_id} - + {item.task_id} @@ -259,7 +259,8 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('components.ticket.multiVolume.reqBurnTxnAmount'))} - {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()} {getStorageFee(item.req_burn_txn_amount, usdPrice)} + {formatNumber(item.req_burn_txn_amount)} {getCurrencyName()}{' '} + {getStorageFee(item.req_burn_txn_amount, usdPrice)} @@ -280,7 +281,8 @@ const FileInfo: React.FC = ({ data }) => { {parse(translate('components.ticket.multiVolume.reqAmount'))} - {formatNumber(item.req_amount)} {getCurrencyName()} {getStorageFee(item.req_amount, usdPrice)} + {formatNumber(item.req_amount)} {getCurrencyName()}{' '} + {getStorageFee(item.req_amount, usdPrice)} @@ -342,7 +344,7 @@ const FileInfo: React.FC = ({ data }) => { - ) + ); } return ( diff --git a/src/pages/Details/PastelIdDetails/Overview.tsx b/src/pages/Details/PastelIdDetails/Overview.tsx index dfe3c956..0b674890 100644 --- a/src/pages/Details/PastelIdDetails/Overview.tsx +++ b/src/pages/Details/PastelIdDetails/Overview.tsx @@ -41,7 +41,7 @@ const Overview: React.FC = ({ let total = getTicketTypeTotal(item.type, ticketsTypeList); if (item.sub_type === 'cascade_multi_volume') { ticket = TICKET_TYPE_OPTIONS.find(i => i.value === item.sub_type); - const newTicketsTypeList = ticketsTypeList.find((t) => t.sub_type === 'cascade_multi_volume'); + const newTicketsTypeList = ticketsTypeList.find(t => t.sub_type === 'cascade_multi_volume'); total = newTicketsTypeList?.total || 0; } return ( diff --git a/src/pages/Details/PastelIdDetails/TicketList.tsx b/src/pages/Details/PastelIdDetails/TicketList.tsx index b22a1bfe..114831ba 100644 --- a/src/pages/Details/PastelIdDetails/TicketList.tsx +++ b/src/pages/Details/PastelIdDetails/TicketList.tsx @@ -273,7 +273,7 @@ const TicketsList: React.FC = ({ return ; case 'contract': if (sub_type === 'cascade_multi_volume_metadata') { - return + return ; } return ( @@ -371,12 +371,17 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, - ticket.sub_type + ticket.sub_type, )} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} + {renderContent( + ticket.type, + ticket.data.ticket, + ticket.transactionHash, + ticket.sub_type, + )} ))} {!data.length && !isLoading ? ( diff --git a/src/pages/Movement/Movement.helpers.tsx b/src/pages/Movement/Movement.helpers.tsx index ce9d19ed..8f372b09 100644 --- a/src/pages/Movement/Movement.helpers.tsx +++ b/src/pages/Movement/Movement.helpers.tsx @@ -34,9 +34,13 @@ export const getTicketsTypeList = (tickets: string) => { const counts: TCounts = {}; for (let i = 0; i < newTickets.length; i += 1) { if (newTickets[i].sub_type === 'cascade_multi_volume_metadata') { - counts.cascade_multi_volume = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + counts.cascade_multi_volume = counts[newTickets[i].type] + ? counts[newTickets[i].type] + 1 + : 1; } else { - counts[newTickets[i].type] = counts[newTickets[i].type] ? counts[newTickets[i].type] + 1 : 1; + counts[newTickets[i].type] = counts[newTickets[i].type] + ? counts[newTickets[i].type] + 1 + : 1; } } const keys = Object.keys(counts); diff --git a/src/pages/Tickets/Tickets.helpers.tsx b/src/pages/Tickets/Tickets.helpers.tsx index 48d60ca6..28f9f2d8 100644 --- a/src/pages/Tickets/Tickets.helpers.tsx +++ b/src/pages/Tickets/Tickets.helpers.tsx @@ -22,7 +22,10 @@ import { TAppTheme } from '@theme/index'; import breakpoints from '@theme/breakpoints'; import * as TicketsStyles from '@components/Ticket/Ticket.styles'; import { translate, translateDropdown } from '@utils/helpers/i18n'; -import { getFileIcon, getCascadeVolumeIcon } from '@pages/Details/CascadeDetails/CascadeDetails.helpers'; +import { + getFileIcon, + getCascadeVolumeIcon, +} from '@pages/Details/CascadeDetails/CascadeDetails.helpers'; import noImagePlaceholder from '@assets/images/no-image-placeholder.svg'; import { @@ -132,44 +135,45 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = {parse(translate('pages.tickets.fileHash'))} - {contractTicket?.sha3_256_hash_of_original_file ? - + {contractTicket?.sha3_256_hash_of_original_file ? ( + {formatAddress(contractTicket?.sha3_256_hash_of_original_file, 5, -5)} - : - <> - {parse(translate('common.na'))} - - } + + ) : ( + <>{parse(translate('common.na'))} + )} {parse(translate('pages.tickets.fileName'))} - {contractTicket?.name_of_original_file ? + {contractTicket?.name_of_original_file ? ( {contractTicket?.name_of_original_file} - : parse(translate('common.na')) - } + + ) : ( + parse(translate('common.na')) + )} {parse(translate('pages.tickets.cascadeOutput'))} - {parse(translate('pages.tickets.fileSize'))}: {contractTicket?.size_of_original_file_mb ? formatBytes(contractTicket.size_of_original_file_mb * 1000000) : 0} - - {' - '} - + {parse(translate('pages.tickets.fileSize'))}:{' '} + {contractTicket?.size_of_original_file_mb + ? formatBytes(contractTicket.size_of_original_file_mb * 1000000) + : 0} + + {' - '} + @@ -180,9 +184,13 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = - {parse(translate('pages.tickets.multisigOutputsCount'))} + + {parse(translate('pages.tickets.multisigOutputsCount'))} + - {tx_info?.multisig_outputs_count ? formatNumber(tx_info.multisig_outputs_count) : parse(translate('common.na'))} + {tx_info?.multisig_outputs_count + ? formatNumber(tx_info.multisig_outputs_count) + : parse(translate('common.na'))} @@ -195,11 +203,14 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = {parse(translate('pages.tickets.fee'))} - {tx_info ? + {tx_info ? ( <> - {formatNumber(tx_info.multisig_tx_total_fee)} {getCurrencyName()} {getStorageFee(tx_info.multisig_tx_total_fee, usdPrice)} - : parse(translate('common.na')) - } + {formatNumber(tx_info.multisig_tx_total_fee)} {getCurrencyName()}{' '} + {getStorageFee(tx_info.multisig_tx_total_fee, usdPrice)} + + ) : ( + parse(translate('common.na')) + )} @@ -209,11 +220,11 @@ export const transformCascadeData = (cascade: TicketsList[], usdPrice: number) = - - - {getCascadeVolumeIcon(contractTicket?.name_of_original_file || '')} - - + + + {getCascadeVolumeIcon(contractTicket?.name_of_original_file || '')} + + ), diff --git a/src/pages/TicketsType/TicketList.tsx b/src/pages/TicketsType/TicketList.tsx index 16ed26fb..bfbfd8a2 100644 --- a/src/pages/TicketsType/TicketList.tsx +++ b/src/pages/TicketsType/TicketList.tsx @@ -311,7 +311,7 @@ const TicketsList: React.FC = ({ return ; case 'contract': if (sub_type === 'cascade_multi_volume_metadata') { - return + return ; } return ( @@ -341,7 +341,7 @@ const TicketsList: React.FC = ({ return parse(translate('pages.ticketsType.senseAndNFTCollectionTickets')); } if (ticketType === 'contract') { - return parse(translate('pages.ticketsType.inferenceAPICreditPack')); + return parse(translate('pages.ticketsType.inferenceAPICreditPack')); } const ticket = TICKET_TYPE_OPTIONS.find(t => t.value === ticketType); return ( @@ -546,12 +546,17 @@ const TicketsList: React.FC = ({ ticket.type as TTicketType, (ticket.data.ticket as INftCollectionRegistrationTicket)?.collection_ticket ?.item_type, - ticket.sub_type + ticket.sub_type, )} - {renderContent(ticket.type, ticket.data.ticket, ticket.transactionHash, ticket.sub_type)} + {renderContent( + ticket.type, + ticket.data.ticket, + ticket.transactionHash, + ticket.sub_type, + )} ))} {!data.length && !isLoading ? ( diff --git a/src/utils/helpers/__test__/encryption.test.ts b/src/utils/helpers/__test__/encryption.test.ts index 381db80d..7e994919 100644 --- a/src/utils/helpers/__test__/encryption.test.ts +++ b/src/utils/helpers/__test__/encryption.test.ts @@ -18,21 +18,7 @@ describe('utils/helpers/encryption', () => { .spyOn(fzstd1, 'decompress') .mockReturnValue( new Uint8Array([ - 0x28, - 0xb5, - 0x2f, - 0xfd, - 0x24, - 0x02, - 0x11, - 0x00, - 0x00, - 0x4f, - 0x6b, - 0x64, - 0x50, - 0xa9, - 0x5a, + 0x28, 0xb5, 0x2f, 0xfd, 0x24, 0x02, 0x11, 0x00, 0x00, 0x4f, 0x6b, 0x64, 0x50, 0xa9, 0x5a, ]), ); expect(decompress_zstd_compressed_data_func('KLUv/SQCEQAAe33RlPJ6')).toEqual(initial); diff --git a/src/utils/types/ITransactions.ts b/src/utils/types/ITransactions.ts index fe35470d..d5ec0140 100644 --- a/src/utils/types/ITransactions.ts +++ b/src/utils/types/ITransactions.ts @@ -867,7 +867,6 @@ export type TTransfer = { transactionTime: number; }; - interface IRegistrationAttempt { id: number; file_id: string; @@ -905,7 +904,7 @@ export interface IMultiVolumeFIle { size_of_original_big_file: number; start_block: number; done_block: number; - registration_attempts: IRegistrationAttempt[], + registration_attempts: IRegistrationAttempt[]; activation_attempts: IActivationAttempt; } From dcdc0599ead1ccd3a73d39c10d0f977bf2ba55ee Mon Sep 17 00:00:00 2001 From: Tuan Nguyen Date: Fri, 30 Aug 2024 15:58:12 +0700 Subject: [PATCH 13/13] add Coin Supply And Inflation Stats page --- public/locales/en/translation.json | 38 +++ src/components/Sidebar/Sidebar.styles.ts | 20 ++ src/components/Sidebar/Sidebar.tsx | 2 + src/declarations.d.ts | 2 +- src/hooks/useCoinSupplyAndInflationStats.ts | 16 ++ .../CoinSupplyAndInflationStats.helpers.ts | 88 +++++++ .../CoinSupplyAndInflationStats.styles.ts | 145 ++++++++++++ .../CoinSupplyAndInflationStats.tsx | 220 ++++++++++++++++++ .../AddressDetails/AddressDetails.helpers.tsx | 14 -- .../Details/AddressDetails/AddressDetails.tsx | 4 +- .../Details/AddressDetails/BalanceHistory.tsx | 67 +++--- .../Chart/EChartsLineChart.tsx | 74 +++--- .../CirculatingSupply/index.tsx | 67 ++++++ .../StatisticsOvertime.styles.ts | 21 ++ .../TotalSupply/index.tsx | 64 +++++ src/routes/index.tsx | 20 ++ src/utils/constants/routes.ts | 1 + src/utils/constants/statistics.ts | 3 + src/utils/constants/types.ts | 2 + src/utils/constants/urls.ts | 2 + src/utils/helpers/chartOptions.ts | 192 +++++++++++++++ src/utils/helpers/statisticsLib.ts | 45 +++- src/utils/types/IStatistics.ts | 6 + 23 files changed, 1027 insertions(+), 86 deletions(-) create mode 100644 src/hooks/useCoinSupplyAndInflationStats.ts create mode 100644 src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.helpers.ts create mode 100644 src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.styles.ts create mode 100644 src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.tsx diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index bb2a5123..986cc522 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -4345,6 +4345,40 @@ "message": "Report", "description": "Used for the Network Challenges And Self Healing page" } + }, + "coinSupplyAndInflationStats": { + "pageTitle": { + "message": "Coin Supply And Inflation Stats", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "month": { + "message": "{{month}} Month Ago ({{date}})", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "months": { + "message": "{{months}} Months Ago ({{date}})", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "year": { + "message": "{{year}} Year Ago ({{date}})", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "today": { + "message": "Today ({{date}})", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "coinSupply": { + "message": "Coin Supply", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "circulatingSupply": { + "message": "Circulating Supply", + "description": "Used for the Coin Supply And Inflation Stats page" + }, + "start": { + "message": "From start ({{date}})", + "description": "Used for the Coin Supply And Inflation Stats page" + } } }, "routes": { @@ -4555,6 +4589,10 @@ "networkChallengesAndSelfHealing": { "message": "Network Challenges and Self Healing", "description": "Used for the routes" + }, + "coinSupplyAndInflationStats": { + "message": "Coin Supply and Inflation Stats", + "description": "Used for the routes" } }, "chartOptions": { diff --git a/src/components/Sidebar/Sidebar.styles.ts b/src/components/Sidebar/Sidebar.styles.ts index 2ae3b416..62cfb442 100644 --- a/src/components/Sidebar/Sidebar.styles.ts +++ b/src/components/Sidebar/Sidebar.styles.ts @@ -128,6 +128,17 @@ export const Category = styled(ListItem)` font-weight: ${props => props.theme.typography.fontWeightRegular}; transition: all 0.3s ease-in-out; + &.has-sub { + ${props => props.theme.breakpoints.down(960)} { + flex-direction: column; + width: 100%; + + .sub-menu { + width: 100%; + } + } + } + ${props => props.theme.breakpoints.down('md')} { margin: 0; } @@ -274,6 +285,15 @@ export const Category = styled(ListItem)` &.has-sub { padding: 0; border-radius: 0; + + ${props => props.theme.breakpoints.down(960)} { + flex-direction: column; + width: 100%; + + .sub-menu { + width: 100%; + } + } } .menu-text { diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 0ff32f9d..18e0ca7e 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -92,6 +92,7 @@ const SidebarCategory: React.FC = ({ (window.location.pathname === ROUTES.STATISTICS || window.location.pathname === ROUTES.STATISTICS_OVERTIME || window.location.pathname === ROUTES.CASCADE_AND_SENSE_STATISTICS || + window.location.pathname === ROUTES.COIN_SUPPLY_AND_INFLATION_STATS || window.location.pathname.includes(ROUTES.STATISTICS_OVERTIME)) && category?.path === ROUTES.STATISTICS_PARENT ) { @@ -176,6 +177,7 @@ const Sidebar: React.FC = ({ ...rest }) => { (pathName === ROUTES.STATISTICS || pathName === ROUTES.STATISTICS_OVERTIME || pathName === ROUTES.CASCADE_AND_SENSE_STATISTICS || + pathName === ROUTES.COIN_SUPPLY_AND_INFLATION_STATS || pathName.includes(ROUTES.STATISTICS_OVERTIME)) && route.path === ROUTES.STATISTICS_PARENT && width < 960 diff --git a/src/declarations.d.ts b/src/declarations.d.ts index fb57c833..85e085de 100644 --- a/src/declarations.d.ts +++ b/src/declarations.d.ts @@ -1,5 +1,5 @@ // prettier-ignore declare module '*.woff2' { - const content: any; + const content: any; // eslint-disable-line export default content; } diff --git a/src/hooks/useCoinSupplyAndInflationStats.ts b/src/hooks/useCoinSupplyAndInflationStats.ts new file mode 100644 index 00000000..04b9552c --- /dev/null +++ b/src/hooks/useCoinSupplyAndInflationStats.ts @@ -0,0 +1,16 @@ +import useSWRInfinite from 'swr/infinite'; + +import { axiosGet } from '@utils/helpers/useFetch/useFetch'; +import * as URLS from '@utils/constants/urls'; +import { TCoinSupplyAndInflationStats } from '@utils/types/IStatistics'; + +export default function useCoinSupplyAndInflationStats(period: string) { + const { data, isLoading } = useSWRInfinite<{ data: Array }>( + () => `${URLS.GET_STATISTICS_COIN_SUPPLY_AND_CIRCULATING_SUPPLY}?period=${period}`, + axiosGet, + ); + return { + data: data ? data[0].data : null, + isLoading, + }; +} diff --git a/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.helpers.ts b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.helpers.ts new file mode 100644 index 00000000..896b9128 --- /dev/null +++ b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.helpers.ts @@ -0,0 +1,88 @@ +import { subMonths, fromUnixTime, format } from 'date-fns'; + +import { formatNumber } from '@utils/helpers/formatNumbers/formatNumbers'; +import { TCoinSupplyAndInflationStats } from '@utils/types/IStatistics'; + +type TDateRange = { + value: number; + type: string; +}; + +export const dateRange = [ + { + value: 12, + type: 'year', + }, + { + value: 6, + type: 'month', + }, + { + value: 3, + type: 'month', + }, + { + value: 1, + type: 'month', + }, +]; + +export const periods = JSON.parse(JSON.stringify(dateRange)) + .sort((a: TDateRange, b: TDateRange) => a.value - b.value) + .map((date: TDateRange) => date.value.toString()) + .join(','); + +const convertDate = (date: number) => { + return format(fromUnixTime(new Date(date).getTime() / 1000), 'MM/yyyy'); +}; + +export const getCoinSupplyAndInflationStatsData = ( + items: TCoinSupplyAndInflationStats[] | null, +) => { + if (!items?.length) { + return null; + } + const dates: string[] = []; + const circulatingSupply: number[] = []; + const coinSupply: number[] = []; + for (let i = 0; i <= dateRange.length - 1; i += 1) { + const item = dateRange[i]; + const targetDate = subMonths(new Date(), item.value); + const data = items.filter( + c => convertDate(c.time) === convertDate(new Date(targetDate).getTime()), + ); + dates.push(format(fromUnixTime(new Date(targetDate).getTime() / 1000), 'MM/dd/yyyy')); + if (data?.length) { + const targetItem = data[data.length - 1]; + circulatingSupply.push(targetItem.circulatingSupply); + coinSupply.push(targetItem.coinSupply); + } else { + circulatingSupply.push(0); + coinSupply.push(0); + } + } + const latestItem = items[items.length - 1]; + return { + dates, + circulatingSupply, + coinSupply, + today: { + circulatingSupply: latestItem.circulatingSupply, + coinSupply: latestItem.coinSupply, + date: format(fromUnixTime(new Date().getTime() / 1000), 'MM/dd/yyyy'), + }, + start: { + date: format(fromUnixTime(new Date(items[0].time).getTime() / 1000), 'MM/dd/yyyy'), + circulatingSupply: items[0].circulatingSupply, + coinSupply: items[0].coinSupply, + }, + }; +}; + +export const getPercent = (value: number, total: number) => { + if (!value || !total) { + return 0; + } + const newValue = total - value; + return formatNumber(Number(((newValue * 100) / total).toFixed(1)), { decimalsLength: 1 }); +}; diff --git a/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.styles.ts b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.styles.ts new file mode 100644 index 00000000..7024ae61 --- /dev/null +++ b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.styles.ts @@ -0,0 +1,145 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + width: 100%; + + .page-title { + margin-bottom: 10px; + + h3 { + font-size: 1.2rem; + } + + .MuiDivider-root { + display: none; + } + } + + ${props => props.theme.breakpoints.down(1024)} { + .custom-table { + &.responsive { + .table__row { + display: flex; + flex-direction: column; + + td { + padding-top: 6px; + padding-bottom: 6px; + border-bottom: 0; + + &:first-child { + padding-top: 12px; + } + + &:last-child { + padding-bottom: 12px; + } + } + } + } + } + } +`; + +export const MainContent = styled.div` + position: relative; + min-height: calc(100vh - 350px); +`; + +export const TicketTitle = styled.div` + font-weight: 400; +`; + +export const TicketContent = styled.div` + font-weight: 700; +`; + +export const NoData = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + font-size: 24px; + font-weight: 600; +`; + +export const ChartWrapper = styled.div` + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-top: 20px; + + ${props => props.theme.breakpoints.down(1024)} { + grid-template-columns: 1fr; + } +`; +export const TicketSummaryBox = styled.div` + display: block; + width: 100%; + padding: 8px 12px; + box-shadow: 0px 5px 6px rgb(16 16 16 / 6%); + background-color: ${props => props.theme.sidebar.menu.background}; + border-radius: 10px; + text-decoration: none; + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + border: 1px solid #d8d8d8; + + .box-title { + margin-bottom: 10px; + color: ${props => props.theme.card.color}; + font-size: 16px; + font-weight: 700; + line-height: 1.25; + white-space: nowrap; + } + + .coin-supply-item { + margin-bottom: 6px; + } + + .ticket-summary-title { + display: block; + color: ${props => props.theme.sidebar.menu.default}; + font-size: 14px; + font-weight: 500; + line-height: 1.25; + white-space: nowrap; + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + } + + .ticket-summary-value { + margin-top: 3px; + font-size: 16px; + font-weight: 700; + overflow: hidden; + text-overflow: ellipsis; + color: ${props => props.theme.card.color}; + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + white-space: nowrap; + } +`; + +export const BoxWrapper = styled.div` + display: flex; + gap: 12px; + + ${props => props.theme.breakpoints.down(1330)} { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + } + + ${props => props.theme.breakpoints.down(768)} { + display: grid; + grid-template-columns: 1fr 1fr; + } + + ${props => props.theme.breakpoints.down(500)} { + display: grid; + grid-template-columns: 1fr; + } +`; + +export const BoxSection = styled.div` + position: relative; +`; diff --git a/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.tsx b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.tsx new file mode 100644 index 00000000..91fcafbb --- /dev/null +++ b/src/pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats.tsx @@ -0,0 +1,220 @@ +import React from 'react'; +import parse from 'html-react-parser'; +import CircularProgress from '@mui/material/CircularProgress'; +import Box from '@mui/material/Box'; + +import Header from '@components/Header/Header'; +import { formatNumber } from '@utils/helpers/formatNumbers/formatNumbers'; +import useCoinSupplyAndInflationStats from '@hooks/useCoinSupplyAndInflationStats'; +import { getCurrencyName } from '@utils/appInfo'; +import { translate, translateDropdown } from '@utils/helpers/i18n'; +import * as AddressDetailsStyles from '@pages/Details/AddressDetails/AddressDetails.styles'; +import { CirculatingSupplyChart } from '@pages/HistoricalStatistics/CirculatingSupply'; +import { TotalSupplyChart } from '@pages/HistoricalStatistics/TotalSupply'; + +import { + getCoinSupplyAndInflationStatsData, + dateRange, + getPercent, + periods, +} from './CoinSupplyAndInflationStats.helpers'; +import * as Styles from './CoinSupplyAndInflationStats.styles'; + +const CoinSupplyAndInflationStats: React.FC = () => { + const { data, isLoading } = useCoinSupplyAndInflationStats(periods); + const coinSupplyAndInflationStatsData = getCoinSupplyAndInflationStatsData(data); + + const renderHeaderCell = (type: string, value: number, date: string) => { + if (type === 'year') { + return parse( + translate('pages.coinSupplyAndInflationStats.year', { + year: 1, + date, + }), + ); + } + + if (value === 1) { + return parse( + translate('pages.coinSupplyAndInflationStats.month', { + month: 1, + date, + }), + ); + } + + return parse( + translate('pages.coinSupplyAndInflationStats.months', { + months: value, + date, + }), + ); + }; + + const renderContent = () => { + if (isLoading) { + return null; + } + if (!coinSupplyAndInflationStatsData) { + return {parse(translate('common.noData'))}; + } + let startCirculatingSupply = coinSupplyAndInflationStatsData.start.circulatingSupply; + let startCoinSupply = coinSupplyAndInflationStatsData.start.coinSupply; + + return ( + + +
+ {parse( + translate('pages.coinSupplyAndInflationStats.start', { + date: coinSupplyAndInflationStatsData.start.date, + }), + )} +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.coinSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData.start.coinSupply, { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} + +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.circulatingSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData?.start.circulatingSupply, { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} + +
+
+ + {dateRange?.map((item, index) => { + if (!coinSupplyAndInflationStatsData.circulatingSupply[index]) { + return null; + } + const tmpStartCirculatingSupply = startCirculatingSupply; + const tmpStartCoinSupply = startCoinSupply; + startCirculatingSupply = coinSupplyAndInflationStatsData.circulatingSupply[index]; + startCoinSupply = coinSupplyAndInflationStatsData.coinSupply[index]; + + return ( + +
+ {renderHeaderCell( + item.type, + item.value, + coinSupplyAndInflationStatsData.dates[index], + )} +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.coinSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData.coinSupply[index], { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} ( + {getPercent( + tmpStartCoinSupply, + coinSupplyAndInflationStatsData.coinSupply[index], + )} + %) + +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.circulatingSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData.circulatingSupply[index], { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} ( + {getPercent( + tmpStartCirculatingSupply, + coinSupplyAndInflationStatsData.circulatingSupply[index], + )} + %) + +
+
+ ); + })} + + +
+ {parse( + translate('pages.coinSupplyAndInflationStats.today', { + date: coinSupplyAndInflationStatsData?.today.date, + }), + )} +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.coinSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData.today.coinSupply, { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} ( + {getPercent(startCoinSupply, coinSupplyAndInflationStatsData?.today.coinSupply)}%) + +
+
+ + {parse(translate('pages.coinSupplyAndInflationStats.circulatingSupply'))}: + + + {formatNumber(coinSupplyAndInflationStatsData?.today.circulatingSupply, { + decimalsLength: 2, + })}{' '} + {getCurrencyName()} ( + {getPercent( + startCirculatingSupply, + coinSupplyAndInflationStatsData?.today.circulatingSupply, + )} + %) + +
+
+
+ ); + }; + + return ( + +
+ + {isLoading ? ( + + + + + {parse(translate('common.loadingData'))} + + + + ) : null} + {renderContent()} + + + + + + + ); +}; + +export default CoinSupplyAndInflationStats; diff --git a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx index 28b24d47..70cc60be 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.helpers.tsx @@ -140,17 +140,3 @@ export const transformDirectionChartData = (data: TChartStatisticsResponse[] | n dataY, }; }; - -export const isValidAddress = (address: string) => { - const defaultCurrencyName = process.env.REACT_APP_EXPLORER_DEFAULT_CURRENCY_NAME; - if (defaultCurrencyName === 'PSL' && address?.length === 35 && address.startsWith('Pt')) { - return true; - } - if (defaultCurrencyName === 'LSP' && address?.length === 35 && address.startsWith('tP')) { - return true; - } - if (defaultCurrencyName === 'DEV' && address?.length === 36 && address.startsWith('44o')) { - return true; - } - return false; -}; diff --git a/src/pages/Details/AddressDetails/AddressDetails.tsx b/src/pages/Details/AddressDetails/AddressDetails.tsx index 9616c590..1f4c5951 100644 --- a/src/pages/Details/AddressDetails/AddressDetails.tsx +++ b/src/pages/Details/AddressDetails/AddressDetails.tsx @@ -23,7 +23,6 @@ import { DATA_FETCH_LIMIT, DATA_DEFAULT_SORT, generateLatestTransactions, - isValidAddress, } from './AddressDetails.helpers'; import { ADDRESS_TRANSACTION_TIMESTAMP_KEY, columns } from './AddressDetails.columns'; import BalanceHistory from './BalanceHistory'; @@ -83,8 +82,7 @@ const AddressDetails = () => { !swrData.isLoading && !swrData?.data?.totalReceived && !swrData?.data?.totalSent && - !swrData?.data?.balance?.length && - !isValidAddress(id || '') + !swrData?.data?.balance?.length ) { navigate(ROUTES.NOT_FOUND); } diff --git a/src/pages/Details/AddressDetails/BalanceHistory.tsx b/src/pages/Details/AddressDetails/BalanceHistory.tsx index a49f80ed..12520be6 100644 --- a/src/pages/Details/AddressDetails/BalanceHistory.tsx +++ b/src/pages/Details/AddressDetails/BalanceHistory.tsx @@ -149,6 +149,40 @@ const BalanceHistory: React.FC = ({ id }) => { return '#5470c6'; } }; + + const renderContent = () => { + if (isLoading || swrData.isLoading) { + return ( + + + {parse(translate('common.loadingData'))} + + ); + } + + if (chartData?.dataX?.length) { + return ( + + ); + } + + return ( + {parse(translate('common.noData'))} + ); + }; + return ( @@ -180,38 +214,7 @@ const BalanceHistory: React.FC = ({ id }) => { ) : null} - - {isLoading || swrData.isLoading ? ( - - - {parse(translate('common.loadingData'))} - - ) : ( - <> - {chartData?.dataX?.length ? ( - - ) : ( - - {parse(translate('common.noData'))} - - )} - - )} - + {renderContent()} ); }; diff --git a/src/pages/HistoricalStatistics/Chart/EChartsLineChart.tsx b/src/pages/HistoricalStatistics/Chart/EChartsLineChart.tsx index 65dc3c57..2ca46c30 100644 --- a/src/pages/HistoricalStatistics/Chart/EChartsLineChart.tsx +++ b/src/pages/HistoricalStatistics/Chart/EChartsLineChart.tsx @@ -48,6 +48,8 @@ export const EChartsLineChart = (props: TLineChartProps): JSX.Element => { subTitle, customHtml, isLoading = false, + hideChangeColor = false, + hideDownloadButton = false, } = props; const { height, width } = useWindowDimensions(); const { darkMode } = useSelector(getThemeState); @@ -102,7 +104,9 @@ export const EChartsLineChart = (props: TLineChartProps): JSX.Element => { chartName === 'averageTransactionsPerBlock' || chartName === 'accounts' || chartName === 'circulatingSupply' || + chartName === 'circulatingSupplySmallChart' || chartName === 'totalSupply' || + chartName === 'totalSupplySmallChart' || chartName === 'difficulty' ) { const result = generateMinMaxChartData(min, max, 0, 5, selectedPeriodButton); @@ -311,7 +315,7 @@ export const EChartsLineChart = (props: TLineChartProps): JSX.Element => { ) : null} - + {subTitle ? ( {subTitle} @@ -334,37 +338,43 @@ export const EChartsLineChart = (props: TLineChartProps): JSX.Element => { /> )} - -
- {themes.map((theme, index) => ( - handleThemeButtonClick(theme, index)} - style={{ - backgroundColor: `${theme.backgroundColor}`, - }} - type="button" - key={`button-filter-${theme.name}`} - > - {' '} - - ))} -
-
- - {parse(translate('pages.historicalStatistics.downloadPNG'))} - - - {parse(translate('pages.historicalStatistics.downloadCSV'))} - -
-
+ {!hideChangeColor || !hideDownloadButton ? ( + + {!hideChangeColor ? ( +
+ {themes.map((theme, index) => ( + handleThemeButtonClick(theme, index)} + style={{ + backgroundColor: `${theme.backgroundColor}`, + }} + type="button" + key={`button-filter-${theme.name}`} + > + {' '} + + ))} +
+ ) : null} + {!hideDownloadButton ? ( +
+ + {parse(translate('pages.historicalStatistics.downloadPNG'))} + + + {parse(translate('pages.historicalStatistics.downloadCSV'))} + +
+ ) : null} +
+ ) : null} ); }; diff --git a/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx b/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx index 73d76b91..44e8d18c 100644 --- a/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx +++ b/src/pages/HistoricalStatistics/CirculatingSupply/index.tsx @@ -13,6 +13,8 @@ import { translate } from '@utils/helpers/i18n'; import { EChartsLineChart } from '../Chart/EChartsLineChart'; +import * as Styles from '../StatisticsOvertime.styles'; + function CirculatingSupply() { const [chartData, setChartData] = useState(null); const [currentBgColor, handleBgColorChange] = useBackgroundChart(); @@ -84,3 +86,68 @@ function CirculatingSupply() { } export default CirculatingSupply; + +export function CirculatingSupplyChart() { + const [chartData, setChartData] = useState(null); + const [currentBgColor, handleBgColorChange] = useBackgroundChart(); + const [period, setPeriod] = useState(periods[12][0]); + const [isLoading, setLoading] = useState(false); + const swrData = useCirculatingSupply(period); + + useEffect(() => { + let currentCache = readCacheValue(cacheList.circulatingSupply) || {}; + if (currentCache[period]) { + setChartData(currentCache[period].parseData as TLineChartData); + setLoading(false); + } else { + setLoading(true); + } + if (!swrData.isLoading && swrData.data) { + const parseData = transformStatisticsChart(swrData.data, period, ''); + setChartData(parseData); + currentCache = { + ...currentCache, + [period]: { + parseData, + }, + }; + setCacheValue( + cacheList.circulatingSupply, + JSON.stringify({ + currentCache, + lastDate: Date.now(), + }), + ); + setLoading(false); + } + }, [period, swrData.isLoading, swrData.data]); + + const handlePeriodFilterChange = (value: PeriodTypes) => { + setPeriod(value); + }; + + return ( + + + + ); +} diff --git a/src/pages/HistoricalStatistics/StatisticsOvertime.styles.ts b/src/pages/HistoricalStatistics/StatisticsOvertime.styles.ts index 4d8d1d08..600e874e 100644 --- a/src/pages/HistoricalStatistics/StatisticsOvertime.styles.ts +++ b/src/pages/HistoricalStatistics/StatisticsOvertime.styles.ts @@ -40,3 +40,24 @@ export const BlockTitle = styled.h4` export const ChartImage = styled.div` padding: 12px; `; + +export const SmallChartWrapper = styled.div` + position: relative; + border-radius: 10px; + border: 1px solid #d8d8d8; + box-shadow: 0px 5px 6px rgb(16 16 16 / 6%); + + .line-chart-header { + margin: 0 0 16px; + padding: 13px 16px; + } + + .line-chart-main-content { + margin-bottom: 10px; + padding: 0 16px; + } + + .line-chart-footer { + padding: 0 16px 16px; + } +`; diff --git a/src/pages/HistoricalStatistics/TotalSupply/index.tsx b/src/pages/HistoricalStatistics/TotalSupply/index.tsx index 9bea459c..412db86d 100644 --- a/src/pages/HistoricalStatistics/TotalSupply/index.tsx +++ b/src/pages/HistoricalStatistics/TotalSupply/index.tsx @@ -12,6 +12,7 @@ import useTotalSupply from '@hooks/useTotalSupply'; import { translate } from '@utils/helpers/i18n'; import { EChartsLineChart } from '../Chart/EChartsLineChart'; +import * as Styles from '../StatisticsOvertime.styles'; function TotalSupply() { const [chartData, setChartData] = useState(null); @@ -80,3 +81,66 @@ function TotalSupply() { } export default TotalSupply; + +export function TotalSupplyChart() { + const [chartData, setChartData] = useState(null); + const [currentBgColor, handleBgColorChange] = useBackgroundChart(); + const [period, setPeriod] = useState(periods[12][0]); + const [isLoading, setLoading] = useState(false); + const swrData = useTotalSupply(period); + + useEffect(() => { + let currentCache = readCacheValue(cacheList.totalSupply) || {}; + if (currentCache[period]) { + setChartData(currentCache[period].parseData as TLineChartData); + setLoading(false); + } else { + setLoading(true); + } + if (!swrData.isLoading && swrData.data) { + const parseData = transformTotalSupplyDataChart(swrData.data, period); + setChartData(parseData); + currentCache = { + ...currentCache, + [period]: { + parseData, + }, + }; + setCacheValue( + cacheList.totalSupply, + JSON.stringify({ + currentCache, + lastDate: Date.now(), + }), + ); + setLoading(false); + } + }, [period, swrData.isLoading, swrData.data]); + + const handlePeriodFilterChange = (value: PeriodTypes) => { + setPeriod(value); + }; + + return ( + + + + ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index d75d5013..1be10bba 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -76,6 +76,9 @@ const Page404 = loadable(() => import('@pages/404/404')); const NetworkChallengesAndSelfHealing = loadable( () => import('@pages/NetworkChallengesAndSelfHealing/NetworkChallengesAndSelfHealing'), ); +const CoinSupplyAndInflationStats = loadable( + () => import('@pages/CoinSupplyAndInflationStats/CoinSupplyAndInflationStats'), +); const explorerRoutes = { id: 'routes.explorer', @@ -167,6 +170,12 @@ const statisticsRoutes = { component: CascadeAndSenseStatistics, seoTitle: 'routes.cascadeAndSenseStatistics', }, + { + path: ROUTES.COIN_SUPPLY_AND_INFLATION_STATS, + name: 'routes.coinSupplyAndInflationStats', + component: CoinSupplyAndInflationStats, + seoTitle: 'routes.coinSupplyAndInflationStats', + }, // { // path: ROUTES.NETWORK_CHALLENGE_AND_SELF_HEALING, // name: 'routes.networkChallengesAndSelfHealing', @@ -207,6 +216,16 @@ const cascadeAndSenseStatisticsRoutes = { exact: false, }; +const coinSupplyAndInflationStatsRoutes = { + id: 'routes.coinSupplyAndInflationStats', + path: ROUTES.COIN_SUPPLY_AND_INFLATION_STATS, + component: CoinSupplyAndInflationStats, + icon: , + seoTitle: 'routes.coinSupplyAndInflationStats', + children: null, + exact: false, +}; + const difficultyStatisticsRoutes = { id: 'routes.networkDifficulty', path: ROUTES.STATISTICS_DIFFICULTY, @@ -552,6 +571,7 @@ export const pageRoutes = [ percentOfPSLStakedStatisticsRoutes, accountsStatisticsRoutes, cascadeAndSenseStatisticsRoutes, + coinSupplyAndInflationStatsRoutes, senseDetailsRoutes, pastelIdDetailsRoutes, ticketsTypeRoutes, diff --git a/src/utils/constants/routes.ts b/src/utils/constants/routes.ts index cb86f916..df620ce2 100644 --- a/src/utils/constants/routes.ts +++ b/src/utils/constants/routes.ts @@ -37,6 +37,7 @@ export const STATISTICS_PERCENT_OF_PSL_STAKED = `${STATISTICS_OVERTIME}/percent- export const STATISTICS_ACCOUNTS = `${STATISTICS_OVERTIME}/accounts`; export const STATISTICS_FEE_SCHEDULE = `${STATISTICS_OVERTIME}/fee-schedule`; export const STATISTICS_PSL_BURNT = `${STATISTICS_OVERTIME}/psl-burnt`; +export const COIN_SUPPLY_AND_INFLATION_STATS = `/coin-supply-and-inflation-stats`; export const CASCADE_AND_SENSE_STATISTICS = '/cascade-and-sense-statistics'; export const SENSE_DETAILS = '/sense'; export const PASTEL_ID_DETAILS = '/pastelid'; diff --git a/src/utils/constants/statistics.ts b/src/utils/constants/statistics.ts index 6a663a7a..0562ef07 100644 --- a/src/utils/constants/statistics.ts +++ b/src/utils/constants/statistics.ts @@ -207,7 +207,9 @@ export const csvHeaders: TCsvHeaderType = { totalTransactionFees: commonCsvFields, totalTransactionsPerDay: commonCsvFields, circulatingSupply: commonCsvFields, + circulatingSupplySmallChart: commonCsvFields, totalSupply: commonCsvFields, + totalSupplySmallChart: commonCsvFields, percentOfPSLStaked: commonCsvFields, pslBurnt: commonCsvFields, networktotals: [ @@ -248,6 +250,7 @@ export const periods: PeriodTypes[][] = [ ['7d', '14d'], ['1y', '2y', 'max'], ['7d', '14d', '30d', '90d', '180d', '1y', 'max'], + ['24h', '30d', '180d', '1y', 'max'], ]; export const CHART_THEME_BACKGROUND_DEFAULT_COLOR = '#2D3748'; diff --git a/src/utils/constants/types.ts b/src/utils/constants/types.ts index 2bcc2b85..ea116c81 100644 --- a/src/utils/constants/types.ts +++ b/src/utils/constants/types.ts @@ -70,6 +70,8 @@ export type TLineChartProps = { showLegend?: boolean; symbol?: string; symbol1?: string; + hideChangeColor?: boolean; + hideDownloadButton?: boolean; }; type LabelKeyObject = { diff --git a/src/utils/constants/urls.ts b/src/utils/constants/urls.ts index bf7eebd9..dcb40944 100644 --- a/src/utils/constants/urls.ts +++ b/src/utils/constants/urls.ts @@ -62,6 +62,8 @@ export const GET_SENSE_TRANSFERS = 'v1/sense/transfers'; export const GET_STATISTICS_STORAGE_CHALLENGES = 'storage_challenges/detailed_logs'; export const GET_STATISTICS_HEALTH_CHECK_CHALLENGES = 'healthcheck_challenge/detailed_logs'; export const GET_STATISTICS_SELF_HEALING_CHALLENGES = 'self_healing/detailed_logs'; +export const GET_STATISTICS_COIN_SUPPLY_AND_CIRCULATING_SUPPLY = + 'v1/stats/coin-supply-and-circulating-supply'; // External URLS export const TWITTER_URL = 'https://twitter.com/pastelnetwork'; export const TELEGRAM_URL = 'https://t.me/PastelNetwork'; diff --git a/src/utils/helpers/chartOptions.ts b/src/utils/helpers/chartOptions.ts index 9a8f925e..273768c6 100644 --- a/src/utils/helpers/chartOptions.ts +++ b/src/utils/helpers/chartOptions.ts @@ -15,6 +15,7 @@ import { TGranularity, getYAxisLabel, convertYAxisLabel, + generateXAxisIntervalSmallChart, } from '@utils/helpers/statisticsLib'; import { TChartParams } from '@utils/types/IStatistics'; import { translateDropdown } from '@utils/helpers/i18n'; @@ -1675,6 +1676,97 @@ export function getThemeInitOption(args: TThemeInitOption): EChartsOption { }, animation: false, }, + totalSupplySmallChart: { + backgroundColor: theme?.backgroundColor, + textStyle: { + color: theme?.color, + }, + color: ['#cd6661'], + grid: { + top: 8, + right: 40, + bottom: 70, + left: 60, + show: false, + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100, + }, + { + start: 0, + end: 100, + }, + ], + tooltip: { + trigger: 'axis', + formatter(params: TChartParams[]) { + return ` +
+
${generateTooltipLabel( + new Date(params[0].axisValue), + granularity, + )}
+
${params[0].marker} ${formatNumber(params[0].value)}
+
+ `; + }, + }, + xAxis: { + type: 'category', + data: dataX, + boundaryGap: false, + axisLabel: { + formatter(value: string, index: number) { + let isShowMinutesFor24h = false; + if (period === '24h' && dataX && (index === 0 || dataX.length - 1 === index)) { + isShowMinutesFor24h = true; + } + return generateXAxisLabel(new Date(value), period, isShowMinutesFor24h); + }, + showMaxLabel: true, + interval: generateXAxisIntervalSmallChart('1d', period, dataX, width), + }, + }, + yAxis: { + type: 'value', + min: minY, + max: maxY, + interval: (maxY - minY) / 5, + splitLine: { + show: false, + }, + axisLabel: { + formatter(value: string) { + return getYAxisLabel(Number(value), minY, maxY); + }, + }, + axisLine: { + show: true, + }, + }, + series: { + type: 'line', + name: translateDropdown('chartOptions.totalSupply', { currency: getCurrencyName() }), + data: dataY, + showSymbol: false, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: '#cd6661', + }, + { + offset: 1, + color: theme?.backgroundColor ?? '#F4F4F4', + }, + ]), + }, + }, + animation: false, + }, circulatingSupply: { backgroundColor: theme?.backgroundColor, textStyle: { @@ -1775,6 +1867,106 @@ export function getThemeInitOption(args: TThemeInitOption): EChartsOption { }, animation: false, }, + circulatingSupplySmallChart: { + backgroundColor: theme?.backgroundColor, + textStyle: { + color: theme?.color, + }, + color: [blueColor], + grid: { + top: 8, + right: 40, + bottom: 70, + left: 60, + show: false, + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100, + }, + { + start: 0, + end: 100, + }, + ], + tooltip: { + trigger: 'axis', + formatter(params: TChartParams[]) { + return `
+
${generateTooltipLabel(new Date(params[0].axisValue), granularity)}
+
${params[0].marker} ${formatNumber(params[0].value)}
+
`; + }, + }, + xAxis: { + type: 'category', + data: dataX, + boundaryGap: false, + axisLabel: { + formatter(value: string, index: number) { + let isShowMinutesFor24h = false; + if (period === '24h' && dataX && (index === 0 || dataX.length - 1 === index)) { + isShowMinutesFor24h = true; + } + if (period && periods[9].indexOf(period) !== -1) { + const date = format(new Date(value), 'MM/dd/yyyy'); + if (firstDay !== date) { + firstDay = date; + return generateXAxisLabel(new Date(value), period, isShowMinutesFor24h); + } + + return null; + } + return value ? generateXAxisLabel(new Date(value), period, isShowMinutesFor24h) : null; + }, + showMaxLabel: true, + interval: generateXAxisIntervalSmallChart('1d', period, dataX, width), + }, + }, + yAxis: { + type: 'value', + min: minY, + max: maxY, + interval: (maxY - minY) / 5, + splitLine: { + show: false, + }, + axisLine: { + show: true, + }, + axisLabel: { + formatter(value: string) { + return getYAxisLabel(Number(value), minY, maxY); + }, + }, + }, + series: { + type: 'line', + name: translateDropdown('chartOptions.circulatingSupply', { currency: getCurrencyName() }), + data: dataY, + showSymbol: false, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: blueColor, + }, + { + offset: 1, + color: theme?.backgroundColor || '#fff', + }, + ]), + }, + emphasis: { + lineStyle: { + width: 2, + }, + }, + }, + animation: false, + }, percentOfPSLStaked: { backgroundColor: theme?.backgroundColor, textStyle: { diff --git a/src/utils/helpers/statisticsLib.ts b/src/utils/helpers/statisticsLib.ts index 578d6424..561f4604 100644 --- a/src/utils/helpers/statisticsLib.ts +++ b/src/utils/helpers/statisticsLib.ts @@ -655,6 +655,45 @@ export const generateXAxisInterval = ( } }; +export const generateXAxisIntervalSmallChart = ( + granularity: TGranularity, + period?: PeriodTypes, + dataX?: string[], + width?: number, +): number | string => { + if (!dataX || !dataX?.length || !period || !width) { + return 'auto'; + } + + if (width > 960 && width < 1200) { + return Math.floor(dataX.length / 5); + } + + if (width <= 960) { + return Math.floor(dataX.length / 3); + } + + switch (period) { + case '24h': + return Math.floor(dataX.length / 5); + case '7d': + case '14d': + if (dataX.length <= 8) { + return 'auto'; + } + return Math.floor(dataX.length / 5); + case '30d': + case '90d': + case '180d': + if (dataX.length !== 31) { + return Math.floor(dataX.length / 5); + } + return 1; + default: + return Math.floor(dataX.length / 5); + } +}; + export const generateMinMaxChartData = ( min: number, max: number, @@ -909,10 +948,8 @@ export function transformFeeSchedule( const dataY: number[] = []; for (let i = 0; i < trans.length; i += 1) { const fee = Number(trans[i].value); - if (fee) { - dataY.push(fee); - dataX.push(new Date(trans[i].time).toLocaleString()); - } + dataY.push(fee); + dataX.push(new Date(trans[i].time).toLocaleString()); } if ( period === '24h' && diff --git a/src/utils/types/IStatistics.ts b/src/utils/types/IStatistics.ts index 7149ef3f..1ffa0d1f 100644 --- a/src/utils/types/IStatistics.ts +++ b/src/utils/types/IStatistics.ts @@ -199,3 +199,9 @@ export type TFeeSchedule = { time: number; value: number; }; + +export type TCoinSupplyAndInflationStats = { + time: number; + circulatingSupply: number; + coinSupply: number; +};