feat(explorer): allow tx types to set their own tx type label

This commit is contained in:
Edd
2023-02-21 11:21:10 +00:00
parent 992aa6007e
commit 073859e5df
4 changed files with 85 additions and 9 deletions
@@ -10,15 +10,30 @@ export type EpochMissingOverviewProps = {
};
/**
* Renders a set of details for an epoch that has no representation in the
* data node. This is primarily for one of two reasons:
*
* 1. The epoch hasn't happened yet
* 2. The epoch happened before a snapshot, and thus the details don't exist
*
* This component is used when the API has responded with no data for an epoch
* by ID, so we already know that we can't display start time/block etc.
*
* We can detect 1 if the epoch is a higher number than the current epoch
* We can detect 2 if the epoch is in the past, but we still get no response.
*/
const EpochMissingOverview = ({
missingEpochId,
}: EpochMissingOverviewProps) => {
const { data, error, loading } = useExplorerFutureEpochQuery();
// This should not happen, but it's easily handled
if (!missingEpochId) {
return <span>-</span>;
}
// No data should also not happen - we've requested the current epoch. This
// could happen at chain restart, but shouldn't. If it does, fallback.
if (!data || loading || error) {
return <span>{missingEpochId}</span>;
}
@@ -27,9 +42,11 @@ const EpochMissingOverview = ({
// Let's assume it is
let isInFuture = true;
// Blank string will be return 0 seconds from getSecondsFromInterval
const epochLength = data.networkParameter?.value || '';
const epochLengthInSeconds = getSeconds(epochLength);
const epochLengthInSeconds = getSecondsFromInterval(epochLength);
// If we have enough information to predict a future or past block time, let's do it
if (missingEpochId && data.epoch.id && data.epoch.timestamps.start) {
const missing = parseInt(missingEpochId);
const current = parseInt(data.epoch.id);
@@ -65,7 +82,15 @@ const EpochMissingOverview = ({
export default EpochMissingOverview;
function getSeconds(str: string) {
/**
* Parses the interval string we get for the epoch length from the
* network parameter API. These are in the format '1D2H3m' for 1 day,
* 2 hours and 3 minutes.
*
* @param str Interval string
* @returns integer the number of seconds the interval represents
*/
export function getSecondsFromInterval(str: string) {
let seconds = 0;
const months = str.match(/(\d+)\s*M/);
const days = str.match(/(\d+)\s*D/);
@@ -17,6 +17,12 @@ export type EpochOverviewProps = {
};
/**
* Displays detailed information about an epoch, given an ID. This
* works for past epochs and current epochs - future epochs, and a
* few other situations (see epoch-missing) will not return us
* enough information to render this.
*
* The details are hidden in a tooltip, behind the epoch number
*/
const EpochOverview = ({ id }: EpochOverviewProps) => {
const { data, error, loading } = useExplorerEpochQuery({
@@ -96,6 +102,11 @@ export type IconForEpochProps = {
end: string;
};
/**
* Chooses an icon to display next to the epoch number, representing
* when the epoch is relative to now (i.e. not yet started, started,
* finished)
*/
function IconForEpoch({ start, end }: IconForEpochProps) {
const startHasPassed = isPast(new Date(start));
const endHasPassed = end ? isPast(new Date(end)) : false;
@@ -14,10 +14,14 @@ interface TxDetailsSharedProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
// A transitional property used in some complex TX types to display more detailed type information
// than the shared component can derive
hideTypeRow?: boolean;
}
// Applied to all header cells
const sharedHeaderProps = {
export const sharedHeaderProps = {
// Ensures that multi line contents still have the header aligned to the first line
className: 'align-top',
};
@@ -31,6 +35,7 @@ export const TxDetailsShared = ({
txData,
pubKey,
blockData,
hideTypeRow = false,
}: TxDetailsSharedProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
@@ -41,10 +46,12 @@ export const TxDetailsShared = ({
return (
<>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{txData.type}</TableCell>
</TableRow>
{hideTypeRow === false ? (
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{txData.type}</TableCell>
</TableRow>
) : null}
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Hash')}</TableCell>
<TableCell>
@@ -1,13 +1,20 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import type {
Block,
TendermintBlocksResponse,
} from '../../../routes/blocks/tendermint-blocks-response';
import { sharedHeaderProps, TxDetailsShared } from './shared/tx-details-shared';
import { TableRow, TableCell, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { PartyLink } from '../../links';
import SizeInAsset from '../../size-in-asset/size-in-asset';
import { TransferRecurring } from './transfer/transfer-recurring';
import {
SPECIAL_CASE_NETWORK,
SPECIAL_CASE_NETWORK_ID,
} from '../../links/party-link/party-link';
type Transfer = components['schemas']['commandsv1Transfer'];
@@ -45,10 +52,15 @@ export const TxDetailsTransfer = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
<TableCell>{getTypeLabelForTransfer(transfer)}</TableCell>
</TableRow>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
hideTypeRow={true}
/>
{from ? (
<TableRow modifier="bordered">
@@ -79,3 +91,24 @@ export const TxDetailsTransfer = ({
</>
);
};
/**
* Gets a string description of this transfer
* @param txData A full transfer
* @returns string Transfer label
*/
export function getTypeLabelForTransfer(tx: Transfer) {
if (tx.to === SPECIAL_CASE_NETWORK || tx.to === SPECIAL_CASE_NETWORK_ID) {
if (tx.recurring && tx.recurring.dispatchStrategy) {
return 'Reward top up transfer';
}
// Else: we don't know that it's a reward transfer, so let's not guess
} else if (tx.recurring) {
return 'Recurring transfer';
} else if (tx.oneOff) {
// Currently redundant, but could be used to indicate something more specific
return 'Transfer';
}
return 'Transfer';
}