Skip to content
Draft
Show file tree
Hide file tree
Changes from 21 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
26 changes: 18 additions & 8 deletions public/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
"close_request": "close channel request",
"renew_channel": "renew chanel",
"payment_channel_closed": "payment channel closed",
"paychannel_node_line1": "It <1><0>{{action}}</0></1> a PayChannel node from <3><0>{{account}}</0></3> to <5><0>{{counterAccount}}</0></5>",
"paychannel_node_line1": "It <1><0>{{action}}</0></1> a PayChannel <3><0>{{node}}</0></3> from <5><0>{{account}}</0></5> to <7><0>{{counterAccount}}</0></7>",
"paychannel_amount_changed": "Amount changed by <1><0>{{difference}}</0><1><0>{{currency}}</0></1></1> from <3><0>{{previous}}</0><1><0>{{currency}}</0></1></3> to <5><0>{{final}}</0><1><0>{{currency}}</0></1></5>",
"paychannel_balance_changed": "Balance changed by <1><0>{{difference}}</0><1><0>{{currency}}</0></1></1> from <3><0>{{previous}}</0><1><0>{{currency}}</0></1></3> to <5><0>{{final}}</0><1><0>{{currency}}</0></1></5>",
"setfee_fees_description": "Future transactions will require a minimum fee of <amount />.",
Expand Down Expand Up @@ -257,20 +257,20 @@
"meta": "Meta",
"number_of_affected_node": "It affected {{count}} nodes in the ledger:",
"nodes_type": "{{action}} nodes",
"node_meta_type": "It {{action}} a node with type",
"transaction_balance_line_one": "It <1><0>{{action}}</0></1> a <3><0>{{currency}}</0></3> RippleState node between <5><0>{{account}}</0></5> and <7><0>{{counterAccount}}</0></7>",
"node_meta_type": "It {{action}} a <Link>node</Link> with type",
"transaction_balance_line_one": "It <1><0>{{action}}</0></1> a <3><0>{{currency}}</0></3> RippleState <5><0>{{node}}</0></5> between <7><0>{{account}}</0></7> and <9><0>{{counterAccount}}</0></9>",
"transaction_balance_line_two": "Balance changed by <1><0>{{change}}</0></1> from <3><0>{{previousBalance}}</0></3> to <5><0>{{finalBalance}}</0></5>",
"transaction_outstanding_balance_line_two": "Outstanding balance changed by <1><0>{{change}}</0></1> from <3><0>{{previousBalance}}</0></3> to <5><0>{{finalBalance}}</0></5>",
"transaction_owned_directory": "It {{action}} a DirectoryNode node owned by",
"transaction_unowned_directory": "It {{action}} a DirectoryNode node",
"transaction_owned_directory": "It {{action}} a DirectoryNode <Link>node</Link> owned by",
"transaction_unowned_directory": "It {{action}} a DirectoryNode <Link>node</Link>",
"transaction_mptoken_line_one": "It <1><0>{{action}}</0></1> an MPToken node of <3><0>{{account}}</0></3>",
"transaction_mpt_issuance_line_one": "It <1><0>{{action}}</0></1> an MPTokenIssuance node of <3><0>{{account}}</0></3>",
"transaction_mpt_issuance_line_one": "It <1><0>{{action}}</0></1> an MPTokenIssuance <3><0>node</0></3> of <5><0>{{account}}</0></5>",
"owned_account_root": "It {{action}} the AccountRoot node of",
"unowned_account_root": "It {{action}} the AccountRoot node",
"account_balance_increased": "Balance increased by <1><0>{{difference}}</0><1><0>{{currency}}</0></1></1> from <3><0>{{previous}}</0><1><0>{{currency}}</0></1></3> to <5><0>{{final}}</0><1><0>{{currency}}</0></1></5>",
"account_balance_decreased": "Balance decreased by <1><0>{{difference}}</0><1><0>{{currency}}</0></1></1> from <3><0>{{previous}}</0><1><0>{{currency}}</0></1></3> to <5><0>{{final}}</0><1><0>{{currency}}</0></1></5>",
"decreased_from_to": "decreased by <1><0>{{change}}</0></1> from <3><0>{{previous}}</0></3> to <5><0>{{final}}</0></5>",
"offer_node_meta": "It <1><0>{{action}}</0></1> a <3><0>{{pair}}</0></3> offer node owned by <5><0>{{account}}</0></5> with sequence # <7><0>{{sequence}}</0></7>",
"offer_node_meta": "It <1><0>{{action}}</0></1> a <3><0>{{pair}}</0></3> offer <5><0>{{node}}</0></5> owned by <7><0>{{account}}</0></7> with sequence # <9><0>{{sequence}}</0></9>",
"offer_replaces": "This offer replaces the existing offer #",
"offer_partially_filled": "The offer was partially filled",
"offer_filled": "The offer was filled",
Expand Down Expand Up @@ -576,5 +576,15 @@
"load_fee_description": "Current reference transaction cost (base fee) for this ledger version",
"nUnl_description": "No. of validators in NegativeUNL (nUNL)",
"computation_allowance": "Computation Allowance",
"gas": "Gas"
"gas": "Gas",
"prev_ledger_index": "Previous Ledger Index",
"prev_tx_id": "Previous Modifying Transaction",
"entry_not_found": "Entry not found",
"entry_empty_title": "No entry hash supplied",
"entry_empty_hint": "Enter a entry hash in the search box",
"check_entry_id": "Please check your entry ID.",
"entry_short": "Entry",
"entry": "Entry",
"invalid_entry_id": "The entry ID is invalid",
"id": "ID"
}
2 changes: 1 addition & 1 deletion src/containers/Accounts/AMM/AMMAccounts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const AMMAccounts = () => {
*/
return getAccountInfo(rippledSocket, accountId)
.then((accountInfo) =>
getLedgerEntry(rippledSocket, { index: accountInfo.AMMID })
getLedgerEntry(rippledSocket, accountInfo.AMMID)
.then((ammLedgerEntry) => {
asset1 = formatAsset(ammLedgerEntry.node.Asset)
asset2 = formatAsset(ammLedgerEntry.node.Asset2)
Expand Down
3 changes: 3 additions & 0 deletions src/containers/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
AMENDMENTS_ROUTE,
AMENDMENT_ROUTE,
MPT_ROUTE,
ENTRY_ROUTE,
NODES_ROUTE,
VALIDATORS_ROUTE,
UPGRADE_STATUS_ROUTE,
Expand All @@ -38,6 +39,7 @@ import { NFT } from '../NFT/NFT'
import { legacyRedirect } from './legacyRedirects'
import { useCustomNetworks } from '../shared/hooks'
import { Amendments } from '../Amendments'
import { Entry } from '../Entry'
import { Amendment } from '../Amendment'
import { MPT } from '../MPT/MPT'
import { Nodes } from '../Network/Nodes'
Expand Down Expand Up @@ -68,6 +70,7 @@ export const AppWrapper = () => {
[LEDGERS_ROUTE, Ledgers],
[LEDGER_ROUTE, Ledger],
[ACCOUNT_ROUTE, AccountsRouter],
[ENTRY_ROUTE, Entry],
[TRANSACTION_ROUTE, Transaction],
[NODES_ROUTE, Nodes],
[VALIDATORS_ROUTE, Validators],
Expand Down
11 changes: 9 additions & 2 deletions src/containers/App/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const ACCOUNT_ROUTE: RouteDefinition<{
tab?: 'assets' | 'transactions'
assetType?: 'issued' | 'nfts' | 'mpts'
}> = {
path: '/accounts/:id?/:tab?/:assetType?',
path: '/accounts/:id/:tab?/:assetType?',
}

export const LEDGERS_ROUTE: RouteDefinition = {
Expand Down Expand Up @@ -49,7 +49,14 @@ export const TRANSACTION_ROUTE: RouteDefinition<{
identifier: string
tab?: 'simple' | 'detailed' | 'raw'
}> = {
path: `/transactions/:identifier?/:tab?`,
path: `/transactions/:identifier/:tab?`,
}

export const ENTRY_ROUTE: RouteDefinition<{
id: string
tab?: 'simple' | 'raw'
}> = {
path: `/entry/:id/:tab?`,
}

export const VALIDATOR_ROUTE: RouteDefinition<{
Expand Down
195 changes: 195 additions & 0 deletions src/containers/Entry/Simple/DefaultSimple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { isValidClassicAddress } from 'ripple-address-codec'
import { formatAmount } from '../../../rippled/lib/txSummary/formatAmount'
import { Account } from '../../shared/components/Account'
import { Amount } from '../../shared/components/Amount'
import Currency from '../../shared/components/Currency'
import { SimpleGroup } from '../../shared/components/Transaction/SimpleGroup'
import { SimpleRow } from '../../shared/components/Transaction/SimpleRow'

const DEFAULT_ENTRY_ELEMENTS = [
'Account',
'Owner',
'index',
'LedgerEntryType',
'PreviousTxnID',
'PreviousTxnLgrSeq',
'Flags',
'LedgerIndex',
'OwnerNode',
]

const displayKey = (key: string) => key.replace(/([a-z])([A-Z])/g, '$1 $2')

const isCurrency = (value: any) =>
typeof value === 'object' &&
Object.keys(value).length <= 2 &&
(value.issuer == null || typeof value.issuer === 'string') &&
typeof value.currency === 'string'

const isAmount = (amount: any, key: any = null) =>
key === 'Amount' ||
(typeof amount === 'object' &&
Object.keys(amount).length === 3 &&
typeof amount.issuer === 'string' &&
typeof amount.currency === 'string' &&
typeof amount.value === 'string')

const processValue = (value: any) => {
if (typeof value === 'string') {
if (isValidClassicAddress(value)) {
return <Account account={value} />
}
if (value.length > 300) {
return `${value.substring(0, 300)}...`
}
if (value === '') {
return <em>{'<empty>'}</em>
}
if (typeof value === 'object') {
return JSON.stringify(value)
}
return value
}

if (Array.isArray(value)) {
return value.map((childValue) => {
if (
typeof childValue === 'object' &&
Object.keys(childValue).length === 1
) {
const childKey = Object.keys(childValue)[0]
const processed = processValue(childValue[childKey])
return <div key={JSON.stringify(childValue)}>{processed}</div>
}
const processed = processValue(childValue)
return <div key={JSON.stringify(processed)}>{processed}</div>
})
}

if (typeof value === 'object') {
return (
<div className="subgroup">
{Object.entries(value).map(([childKey, childValue]) => (
<div key={childKey} data-test={childKey}>
{`${childKey}: `}
{processValue(childValue)}
</div>
))}
</div>
)
}

return JSON.stringify(value)
}

const getRowNested = (key: any, value: any, uniqueKey: string = '') => {
if (key === 'Amount') {
return (
<SimpleRow
key={`${key}${uniqueKey}`}
label={displayKey(key)}
data-test={key}
>
<Amount value={formatAmount(value)} />
</SimpleRow>
)
}

if (isCurrency(value)) {
return (
<SimpleRow
key={`${key}${uniqueKey}`}
label={displayKey(key)}
data-test={key}
>
<Currency currency={value.currency} issuer={value.issuer} />
</SimpleRow>
)
}

if (isAmount(value, key)) {
return (
<SimpleRow
key={`${key}${uniqueKey}`}
label={displayKey(key)}
data-test={key}
>
<Amount value={formatAmount(value)} />
</SimpleRow>
)
}
return (
<SimpleRow
key={`${key}${uniqueKey}`}
label={displayKey(key)}
data-test={key}
>
{processValue(value)}
</SimpleRow>
)
}

const getRow = (key: any, value: any) => {
if (Array.isArray(value)) {
return (
<div key={key}>
{value.map((innerValue, index) => {
if (
typeof innerValue === 'object' &&
Object.keys(innerValue).length === 1
) {
const innerKey = Object.keys(innerValue)[0]
return (
<SimpleGroup
// eslint-disable-next-line react/no-array-index-key -- okay here
key={`group_${innerKey}_${index}`}
title={displayKey(innerKey)}
data-test={key}
>
{Object.entries(innerValue[innerKey]).map(
([childKey, childValue], index2) =>
getRowNested(childKey, childValue, index2.toString()),
)}
</SimpleGroup>
)
}
return getRowNested(index.toString(), innerValue, index.toString())
})}
</div>
)
}

if (
typeof value === 'object' &&
!isCurrency(value) &&
!isAmount(value, key)
) {
return (
<SimpleGroup key={key} title={displayKey(key)} data-test={key}>
{Object.entries(value).map(([childKey, childValue], index) =>
getRowNested(childKey, childValue, index.toString()),
)}
</SimpleGroup>
)
}

return getRowNested(key, value)
}

export interface EntrySimpleProps<I = any> {
data: {
node: I
}
}

export const DefaultSimple = ({ data }: EntrySimpleProps) => {
const uniqueData = Object.fromEntries(
Object.entries(data.node).filter(
([key, value]) => !DEFAULT_ENTRY_ELEMENTS.includes(key) && value != null,
),
)

return (
<>{Object.entries(uniqueData).map(([key, value]) => getRow(key, value))}</>
)
}
29 changes: 29 additions & 0 deletions src/containers/Entry/Simple/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FC } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useTranslation } from 'react-i18next'
import { transactionTypes } from '../../shared/components/Transaction'
import { DefaultSimple } from './DefaultSimple'

export const Simple: FC<{
data: any
type: string
}> = ({ data, type }) => {
// Locate the component for the left side of the simple tab that is unique per TransactionType.
const { t } = useTranslation()
const SimpleComponent = transactionTypes[type]?.Simple
if (SimpleComponent) {
return (
<ErrorBoundary
fallback={
<div className="error">
<div>{t('component_error')}</div>
<div>{t('try_detailed_raw')}</div>
</div>
}
>
<SimpleComponent data={data} />
</ErrorBoundary>
)
}
return <DefaultSimple data={data} />
}
57 changes: 57 additions & 0 deletions src/containers/Entry/SimpleTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Account } from '../shared/components/Account'
import { Simple } from './Simple'

import { RouteLink } from '../shared/routing'
import { SimpleRow } from '../shared/components/Transaction/SimpleRow'
import '../shared/css/simpleTab.scss'
import './simpleTab.scss'
import { LEDGER_ROUTE, TRANSACTION_ROUTE } from '../App/routes'
import { BREAKPOINTS } from '../shared/utils'

export const SimpleTab: FC<{ data: any; width: number }> = ({
data,
width,
}) => {
const { t } = useTranslation()

const renderRowIndex = (owner, prevLedgerIndex, prevTx) => (
<>
{owner && (
<SimpleRow label={t('owner')} data-test="owner">
<Account account={owner} />
</SimpleRow>
)}
<SimpleRow label={t('prev_ledger_index')} data-test="prev-ledger-index">
<RouteLink to={LEDGER_ROUTE} params={{ identifier: prevLedgerIndex }}>
{prevLedgerIndex}
</RouteLink>
</SimpleRow>
<SimpleRow label={t('prev_tx_id')} data-test="prev-tx">
<RouteLink to={TRANSACTION_ROUTE} params={{ identifier: prevTx }}>
{prevTx}
</RouteLink>
</SimpleRow>
</>
)

const rowIndex = renderRowIndex(
data?.node?.Owner ?? data?.node?.Account,
data?.node?.PreviousTxnLgrSeq,
data?.node?.PreviousTxnID,
)

return (
<div className="simple-body simple-body-tx">
<div className="rows">
<Simple type={data?.node.LedgerEntryType} data={data} />
{width < BREAKPOINTS.landscape && rowIndex}
</div>
{width >= BREAKPOINTS.landscape && (
<div className="index">{rowIndex}</div>
)}
<div className="clear" />
</div>
)
}
Loading
Loading