Skip to content

[Insight] Refactored Tx Data with Boxes and Underlines #3929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/insight/src/assets/images/circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion packages/insight/src/components/block-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {routerFadeIn} from '../utilities/animations';
import {
getApiRoot,
getConvertedValue,
getDifficultyFromBits,
getFee,
getFormattedDate,
normalizeParams,
Expand Down Expand Up @@ -147,7 +148,7 @@ const BlockDetails: FC<BlockDetailsProps> = ({currency, network, block}) => {
<SharedTile title='Merkle Root' description={summary.merkleRoot} />
<SharedTile
title='Difficulty'
description={(0x1d00ffff / summary.bits).toString()}
description={getDifficultyFromBits(summary.bits).toString()}
/>
<SharedTile title='Bits' description={summary.bits} />
<SharedTile title='Size (bytes)' description={summary.size} />
Expand Down
223 changes: 136 additions & 87 deletions packages/insight/src/components/transaction-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
isRBF,
getLib,
} from '../utilities/helper-methods';
import {useState, useEffect, FC, memo} from 'react';
import {useState, useEffect, FC, memo, ReactNode, Children} from 'react';
import {
TransactionBodyCol,
TransactionTile,
Expand All @@ -24,8 +24,9 @@ import {
import {Tile, TileDescription} from '../assets/styles/tile';
import ArrowSvg from '../assets/images/arrow.svg';
import BlueArrowSvg from '../assets/images/arrow-blue.svg';
import CircleSvg from '../assets/images/circle.svg';
import {useNavigate, createSearchParams} from 'react-router-dom';
import styled from 'styled-components';
import styled, { useTheme } from 'styled-components';
import {Slate, SlateDark} from '../assets/styles/colors';

const TextElipsis = styled(ScriptText)`
Expand All @@ -46,6 +47,41 @@ const SelectedPill = styled.div`
font-size: 16px;
`;

const TxAddressLink = styled.span`
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
width: 100%;
margin-right: 7px;
&:hover {
cursor: pointer;
}
`

const BorderBoxLabel: FC<{children: ReactNode, label: string}> = ({children, label}) => {
const theme = useTheme();
const modifiedChildren = typeof children === 'object'
? Children.map(children as JSX.Element, (child: JSX.Element) => {
return <span {...child.props} style={{margin: 0}}></span>;
})
: children;

return (
<fieldset style={{
border: `1.5px solid ${theme.dark ? '#5f5f5f' : '#ccc'}`,
borderRadius: '5px',
padding: '0.1rem 0.4rem',
wordBreak: 'break-all',
whiteSpace: 'normal',
width: 'fit-content',
height: 'fit-content'
}}>
<legend style={{margin: '-0.2rem 0.1rem'}}>{label}</legend>
{modifiedChildren}
</fieldset>
);
}

interface TransactionDetailsProps {
transaction: Transaction;
currency: string;
Expand Down Expand Up @@ -157,8 +193,11 @@ const TransactionDetails: FC<TransactionDetailsProps> = ({
{vi.items.map((item: any, itemIndex: number) => (
<div key={item.mintTxid + itemIndex}>
{isInputSelected(item) ? <SelectedPill>Selected</SelectedPill> : null}

<Tile invertedBorderColor={arr.length > 1 && arr.length !== i + 1}>
<div style={{
display: 'flex',
marginTop: '1rem',
...(showDetails && {borderBottom: '2px solid', paddingBottom: '0.25rem'})
}}>
<ArrowDiv margin='auto .5rem auto 0'>
<img
src={BlueArrowSvg}
Expand All @@ -168,53 +207,61 @@ const TransactionDetails: FC<TransactionDetailsProps> = ({
onClick={() => goToTx(item.mintTxid, undefined, item.mintIndex)}
/>
</ArrowDiv>

<TileDescription padding='0 1rem 0 0' value>
{getAddress(vi) !== 'Unparsed address' ? (
<SpanLink onClick={() => goToAddress(getAddress(vi))}>
{getAddress(vi)}
</SpanLink>
) : (
<span>Unparsed address</span>
)}

{showDetails && (
<>
<TextElipsis>
<b>Tx ID </b>
<SpanLink
onClick={() =>
goToTx(item.mintTxid, undefined, item.mintIndex)
}>
{item.mintTxid}
</SpanLink>
</TextElipsis>

<TextElipsis>
<b>Tx Index</b> {item.mintIndex}
</TextElipsis>

{item.uiConfirmations && confirmations > 0 ? (
<ScriptText>
<b>Confirmations</b> {item.uiConfirmations + confirmations}
</ScriptText>
) : null}
{getAddress(vi) !== 'Unparsed address' ? (
<TxAddressLink onClick={() => goToAddress(getAddress(vi))} style={{wordBreak: showDetails ? 'break-all' : 'unset'}}>
{getAddress(vi)}
</TxAddressLink>
) : (
<span style={{textAlign: 'left', width: '100%'}}>
Unparsed address
</span>
)}
<div style={{minInlineSize: 'fit-content'}}>
{getConvertedValue(item.value, currency)} {currency}
</div>
</div>

<Tile invertedBorderColor={arr.length > 1 && arr.length !== i + 1} padding={showDetails ? undefined : '0.4rem'}>
{showDetails &&
<>

<TileDescription padding='0 1rem 0 0' value>
<BorderBoxLabel label='Tx ID'>
<TextElipsis>
<SpanLink
onClick={() =>
goToTx(item.mintTxid, undefined, item.mintIndex)
}>
{item.mintTxid}
</SpanLink>
</TextElipsis>
</BorderBoxLabel>

<span style={{display: 'flex'}}>
<BorderBoxLabel label='Tx Index'>
<TextElipsis>
{item.mintIndex}
</TextElipsis>
</BorderBoxLabel>

{item.uiConfirmations && confirmations > 0 ? (
<BorderBoxLabel label='Confirmations'>
<ScriptText>
{item.uiConfirmations + confirmations}
</ScriptText>
</BorderBoxLabel>
) : null}
</span>

{item.script && (
<>
<b>Script Hex</b>
<ScriptText>{item.script}</ScriptText>
<b>Script ASM</b>
<ScriptText>{new lib.Script(item.script).toASM()}</ScriptText>
<BorderBoxLabel label='Script Hex'>{item.script}</BorderBoxLabel>
<BorderBoxLabel label='Script ASM'>{new lib.Script(item.script).toASM()}</BorderBoxLabel>
</>
)}
</>
)}
</TileDescription>

<TileDescription value textAlign='right'>
{getConvertedValue(item.value, currency)} {currency}
</TileDescription>
</TileDescription>
</>
}
</Tile>
</div>
))}
Expand All @@ -234,56 +281,58 @@ const TransactionDetails: FC<TransactionDetailsProps> = ({
return (
<div key={i}>
{isOutputSelected(i) ? <SelectedPill>Selected</SelectedPill> : null}
<Tile invertedBorderColor={outputsLength > 1 && outputsLength !== i + 1}>
<TileDescription padding='0 1rem 0 0' value>
{getAddress(vo) !== 'Unparsed address' ? (
<SpanLink onClick={() => goToAddress(getAddress(vo))}>
{getAddress(vo)}
</SpanLink>
) : (
<span>{isOpReturn(vo) ? 'OP_RETURN' : 'Unparsed address'}</span>
)}

{showDetails && (
<>
{vo.spentTxid && (
<TextElipsis>
<b>Spent By </b>
<SpanLink onClick={() => goToTx(vo.spentTxid, transaction.txid, i)}>
{vo.spentTxid}
</SpanLink>
</TextElipsis>
)}
{isOpReturn(vo) && <ScriptText>{getOpReturnText(vo)}</ScriptText>}
{vo.script && (
<>
<b>Script Hex</b>
<ScriptText>{new lib.Script(vo.script).toHex()}</ScriptText>
<b>Script ASM</b>
<ScriptText>{new lib.Script(vo.script).toASM()}</ScriptText>
</>
)}
</>
)}
</TileDescription>

<TileDescription value textAlign='right'>
<div style={{
display: 'flex',
marginTop: '1rem',
...(showDetails && {borderBottom: '2px solid', paddingBottom: '0.25rem'})
}}>
{getAddress(vo) !== 'Unparsed address' ? (
<TxAddressLink onClick={() => goToAddress(getAddress(vo))} style={{wordBreak: showDetails ? 'break-all' : 'unset'}}>
{getAddress(vo)}
</TxAddressLink>
) : (
<span style={{textAlign: 'left', width: '100%'}}>
{isOpReturn(vo) ? 'OP_RETURN' : 'Unparsed address'}
</span>
)}
<div style={{minInlineSize: 'fit-content', display: 'flex'}}>
{getConvertedValue(vo.value, currency)} {currency}{' '}
</TileDescription>
<ArrowDiv margin='auto 0 auto .5rem'>
<img
src={vo.spentTxid ? BlueArrowSvg : ArrowSvg}
src={vo.spentTxid ? BlueArrowSvg : (isOpReturn(vo) ? CircleSvg : ArrowSvg)}
width={17}
height={17}
alt='Spent'
title={vo.spentTxid ? 'Spent' : 'Unspent'}
style={{
visibility: (isOpReturn(vo) ? 'hidden' : 'visible'),
margin: '0px 5px'
}}
title={vo.spentTxid ? 'Spent' : (isOpReturn(vo) ? 'Unspendable' : 'Unspent')}
style={{margin: `0px ${isOpReturn(vo) ? '4px' : '5px'}`}}
onClick={() => vo.spentTxid && goToTx(vo.spentTxid, transaction.txid, i)}
/>
</ArrowDiv>
</div>
</div>
<Tile invertedBorderColor={outputsLength > 1 && outputsLength !== i + 1} padding={showDetails ? undefined : '0.4rem'}>
{showDetails &&
<>
<TileDescription padding='0 1rem 0 0' value>
{vo.spentTxid && (
<BorderBoxLabel label='Spent By'>
<TextElipsis>
<SpanLink onClick={() => goToTx(vo.spentTxid, transaction.txid, i)}>
{vo.spentTxid}
</SpanLink>
</TextElipsis>
</BorderBoxLabel>
)}
{isOpReturn(vo) && <ScriptText>{getOpReturnText(vo)}</ScriptText>}
{vo.script && (
<>
<BorderBoxLabel label='Script Hex'>{new lib.Script(vo.script).toHex()}</BorderBoxLabel>
<BorderBoxLabel label='Script ASM'>{new lib.Script(vo.script).toASM()}</BorderBoxLabel>
</>
)}
</TileDescription>
</>
}
</Tile>
</div>
);
Expand Down
6 changes: 6 additions & 0 deletions packages/insight/src/utilities/helper-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,9 @@ export const getLib = (currency: string) => {
return BitcoreLibLtc;
}
};

export const getDifficultyFromBits = (bits: number) => {
const maxBody = Math.log(0x00ffff);
const scaland = Math.log(256);
return Math.exp(maxBody - Math.log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24)));
}