From 2af495217b9efbf3774872806c5d4caea432ecee Mon Sep 17 00:00:00 2001 From: Kenny Lei <3003853+kennyzlei@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:06:29 -0800 Subject: [PATCH 01/15] fix: correct typo for website in english translation (#1101) ## High Level Overview of Change Fix typo for website in english translation ### Context of Change Bug introduced in https://github.com/ripple/explorer/pull/1056 ### Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactor (non-breaking change that only restructures code) - [ ] Tests (You added tests for code that already exists, or your new feature included in this PR) - [ ] Documentation Updates - [ ] Translation Updates - [ ] Release ### TypeScript/Hooks Update - [ ] Updated files to React Hooks - [ ] Updated files to TypeScript --- public/locales/en-US/translations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 83fb30d98..d1ff3ff1d 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -542,7 +542,7 @@ "deleted": "Deleted", "holders": "HOLDERS: {{holders}}", "trustlines": " TRUSTLINES: {{trustlines}}", - "website": "Wesbite", + "website": "Website", "mpt_issuance_id": "MPT Issuance ID", "asset_scale": "Asset Scale", "metadata": "Metadata", From a6a1ee8cbe621f1e45d4f61cd3f13dc75ef9ecae Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 15:14:18 -0400 Subject: [PATCH 02/15] initial commit --- .../Accounts/AMM/AMMAccounts/index.tsx | 2 +- src/containers/App/index.tsx | 3 + src/containers/App/routes.ts | 11 +- src/containers/Entry/Simple/index.tsx | 29 +++ src/containers/Entry/SimpleTab.tsx | 109 ++++++++++++ src/containers/Entry/entry.scss | 60 +++++++ src/containers/Entry/index.tsx | 168 ++++++++++++++++++ src/containers/Entry/simpleTab.scss | 163 +++++++++++++++++ src/containers/Header/Search.tsx | 16 +- src/rippled/lib/rippled.js | 2 +- 10 files changed, 555 insertions(+), 8 deletions(-) create mode 100644 src/containers/Entry/Simple/index.tsx create mode 100644 src/containers/Entry/SimpleTab.tsx create mode 100644 src/containers/Entry/entry.scss create mode 100644 src/containers/Entry/index.tsx create mode 100644 src/containers/Entry/simpleTab.scss diff --git a/src/containers/Accounts/AMM/AMMAccounts/index.tsx b/src/containers/Accounts/AMM/AMMAccounts/index.tsx index b3361d61a..d4dc4e2b5 100644 --- a/src/containers/Accounts/AMM/AMMAccounts/index.tsx +++ b/src/containers/Accounts/AMM/AMMAccounts/index.tsx @@ -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) diff --git a/src/containers/App/index.tsx b/src/containers/App/index.tsx index e55445c8e..fda41d200 100644 --- a/src/containers/App/index.tsx +++ b/src/containers/App/index.tsx @@ -25,6 +25,7 @@ import { AMENDMENTS_ROUTE, AMENDMENT_ROUTE, MPT_ROUTE, + ENTRY_ROUTE, } from './routes' import { LedgersPage as Ledgers } from '../Ledgers' import { Ledger } from '../Ledger' @@ -37,6 +38,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' @@ -64,6 +66,7 @@ export const AppWrapper = () => { [LEDGERS_ROUTE, Ledgers], [LEDGER_ROUTE, Ledger], [ACCOUNT_ROUTE, AccountsRouter], + [ENTRY_ROUTE, Entry], [TRANSACTION_ROUTE, Transaction], [NETWORK_ROUTE, Network], [AMENDMENTS_ROUTE, Amendments], diff --git a/src/containers/App/routes.ts b/src/containers/App/routes.ts index 5c401b93e..98d446137 100644 --- a/src/containers/App/routes.ts +++ b/src/containers/App/routes.ts @@ -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 = { @@ -41,7 +41,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<{ diff --git a/src/containers/Entry/Simple/index.tsx b/src/containers/Entry/Simple/index.tsx new file mode 100644 index 000000000..72554848e --- /dev/null +++ b/src/containers/Entry/Simple/index.tsx @@ -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 '../../shared/components/Transaction/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 ( + +
{t('component_error')}
+
{t('try_detailed_raw')}
+ + } + > + +
+ ) + } + return +} diff --git a/src/containers/Entry/SimpleTab.tsx b/src/containers/Entry/SimpleTab.tsx new file mode 100644 index 000000000..6f22553e3 --- /dev/null +++ b/src/containers/Entry/SimpleTab.tsx @@ -0,0 +1,109 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { localizeDate, localizeNumber, BREAKPOINTS } from '../shared/utils' +import { Account } from '../shared/components/Account' +import { Sequence } from '../shared/components/Sequence' +import { Simple } from './Simple' + +import { useLanguage } from '../shared/hooks' +import { RouteLink } from '../shared/routing' +import { CURRENCY_OPTIONS, XRP_BASE } from '../shared/transactionUtils' +import { SimpleRow } from '../shared/components/Transaction/SimpleRow' +import '../shared/css/simpleTab.scss' +import './simpleTab.scss' +import { LEDGER_ROUTE } from '../App/routes' + +const TIME_ZONE = 'UTC' +const DATE_OPTIONS = { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour12: true, + timeZone: TIME_ZONE, +} + +export const SimpleTab: FC<{ data: any; width: number }> = ({ + data, + width, +}) => { + const { t } = useTranslation() + const language = useLanguage() + + const renderRowIndex = ( + time, + ledgerIndex, + fee, + account, + sequence, + ticketSequence, + isHook, + ) => ( + <> + + {time} + + + + {ledgerIndex} + + + {account && ( + + + + )} + + + + + {fee} + + + ) + + const { processed } = data + const numberOptions = { ...CURRENCY_OPTIONS, currency: 'XRP' } + const time = localizeDate(new Date(processed.date), language, DATE_OPTIONS) + const ledgerIndex = processed.ledger_index + const fee = processed.tx.Fee + ? localizeNumber( + Number.parseFloat(processed.tx.Fee) / XRP_BASE, + language, + numberOptions, + ) + : 0 + + const rowIndex = renderRowIndex( + time, + ledgerIndex, + fee, + processed.tx.Account, + processed.tx.Sequence, + processed.tx.TicketSequence, + !!processed.tx.EmitDetails, + ) + + return ( +
+
+ + {width < BREAKPOINTS.landscape && rowIndex} +
+ {width >= BREAKPOINTS.landscape && ( +
{rowIndex}
+ )} +
+
+ ) +} diff --git a/src/containers/Entry/entry.scss b/src/containers/Entry/entry.scss new file mode 100644 index 000000000..45997a886 --- /dev/null +++ b/src/containers/Entry/entry.scss @@ -0,0 +1,60 @@ +@use '../shared/css/variables' as *; + +.transaction { + position: relative; + max-width: 912px; + min-height: 600px; + + @include for-size(tablet-landscape-up) { + width: 584px; + margin: 0 auto; + } + + @include for-size(desktop-up) { + width: 820px; + } + + @include for-size(big-desktop-up) { + width: 912px; + } + + .loader { + position: absolute; + z-index: 1; + top: 0px; + } + + .summary { + padding: 0 16px 87px 0; + + .type { + display: inline-block; + margin-bottom: 24px; + color: $white; + font-size: 42px; + @include bold; + } + + .txid { + .title { + @include semibold; + } + + display: flex; + overflow: hidden; + margin-top: 8px; + color: $black-40; + font-size: 12px; + letter-spacing: 0px; + line-height: 20px; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; + @include medium; + } + + .tx-status { + margin-left: 15px; + } + } +} diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx new file mode 100644 index 000000000..4e0eda89b --- /dev/null +++ b/src/containers/Entry/index.tsx @@ -0,0 +1,168 @@ +import { useContext, useEffect } from 'react' +import { Helmet } from 'react-helmet-async' +import { useTranslation } from 'react-i18next' +import { useQuery } from 'react-query' +import { useWindowSize } from 'usehooks-ts' +import NoMatch from '../NoMatch' +import { Loader } from '../shared/components/Loader' +import { Tabs } from '../shared/components/Tabs' +import { + NOT_FOUND, + BAD_REQUEST, + HASH256_REGEX, + CTID_REGEX, +} from '../shared/utils' +import { SimpleTab } from './SimpleTab' +import './entry.scss' +import { AnalyticsFields, useAnalytics } from '../shared/analytics' +import SocketContext from '../shared/SocketContext' +import { TxStatus } from '../shared/components/TxStatus' +import { getAction, getCategory } from '../shared/components/Transaction' +import { buildPath, useRouteParams } from '../shared/routing' +import { SUCCESSFUL_TRANSACTION } from '../shared/transactionUtils' +import { ENTRY_ROUTE } from '../App/routes' +import { JsonView } from '../shared/components/JsonView' +import { getLedgerEntry } from '../../rippled/lib/rippled' + +const WRONG_NETWORK = 406 + +const ERROR_MESSAGES: Record = {} +ERROR_MESSAGES[NOT_FOUND] = { + title: 'transaction_not_found', + hints: ['server_ledgers_hint', 'check_transaction_hash'], +} +ERROR_MESSAGES[BAD_REQUEST] = { + title: 'invalid_transaction_hash', + hints: ['check_transaction_hash'], +} +ERROR_MESSAGES[WRONG_NETWORK] = { + title: 'wrong_network', + hints: ['check_transaction_hash'], +} +ERROR_MESSAGES.default = { + title: 'generic_error', + hints: ['not_your_fault'], +} + +const getErrorMessage = (error) => + ERROR_MESSAGES[error] || ERROR_MESSAGES.default + +export const Entry = () => { + const { id = '', tab = 'simple' } = useRouteParams(ENTRY_ROUTE) + const { t } = useTranslation() + const rippledSocket = useContext(SocketContext) + const { trackException, trackScreenLoaded } = useAnalytics() + const { isLoading, data, error, isError } = useQuery(['entry', id], () => { + if (id === '') { + return undefined + } + if (HASH256_REGEX.test(id) || CTID_REGEX.test(id)) { + return getLedgerEntry(rippledSocket, { index: id }).catch( + (ledgerEntryRequestError) => { + const status = ledgerEntryRequestError.code + trackException( + `entry ${id} --- ${JSON.stringify( + ledgerEntryRequestError.message, + )}`, + ) + + return Promise.reject(status) + }, + ) + } + + return Promise.reject(BAD_REQUEST) + }) + const { width } = useWindowSize() + + useEffect(() => { + if (!data?.processed) return + + const type = data?.processed.tx.TransactionType + const status = data?.processed.meta.TransactionResult + + const transactionProperties: AnalyticsFields = { + transaction_action: getAction(type), + transaction_category: getCategory(type), + transaction_type: type, + } + + if (status !== SUCCESSFUL_TRANSACTION) { + transactionProperties.tec_code = status + } + + trackScreenLoaded(transactionProperties) + }, [id, data?.processed, tab, trackScreenLoaded]) + + function renderSummary() { + const type = data?.processed.tx.TransactionType + return ( +
+
{type}
+ +
+
{t('hash')}:
+ {data?.processed.hash} +
+ {data?.processed.tx.ctid && ( +
+
CTID:
+ {data.processed.tx.ctid} +
+ )} +
+ ) + } + + function renderTabs() { + const tabs = ['simple', 'raw'] + const mainPath = buildPath(ENTRY_ROUTE, { id }) + return + } + + function renderTransaction() { + if (!data) return undefined + + let body + + switch (tab) { + case 'raw': + body = + break + default: + body = + break + } + return ( + <> + {renderSummary()} + {renderTabs()} +
{body}
+ + ) + } + + let body + + if (isError) { + const message = getErrorMessage(error) + body = + } else if (data?.processed && data?.processed.hash) { + body = renderTransaction() + } else if (!id) { + body = ( + + ) + } + return ( +
+ + {isLoading && } + {body} +
+ ) +} diff --git a/src/containers/Entry/simpleTab.scss b/src/containers/Entry/simpleTab.scss new file mode 100644 index 000000000..e29bc8c0a --- /dev/null +++ b/src/containers/Entry/simpleTab.scss @@ -0,0 +1,163 @@ +@use '../shared/css/variables' as *; + +$subdued-color: $black-40; + +.simple-body-tx { + .rows { + padding-top: 0px; + + .partial-row { + display: flex; + overflow: hidden; + flex-grow: 1; + flex-wrap: wrap; + padding-top: 20px; + padding-left: 5px; + color: $red-dark; + float: right; + font-size: 14px; + font-style: italic; + text-align: right; + vertical-align: middle; + @include regular; + } + + .row { + .value { + text-align: right; + + .grant { + .account { + padding-bottom: 8px; + font-size: 11px; + text-align: right; + @include medium; + } + } + + .amount { + text-align: right; + + .amount-localized { + display: block; + } + + .one-line { + display: flex; + } + + .currency { + display: block; + } + + .currency, + .one-line { + margin: -1px 0px -16px; + color: $subdued-color; + font-size: 11px; + text-align: right; + @include medium; + + .account { + font-size: inherit; + line-height: inherit; + vertical-align: bottom; + } + } + + &.list { + margin-bottom: 12px; + + .one-line { + justify-content: flex-end; + margin: -1px 0px 0px; + } + } + } + + ul { + padding: 0; + margin: 0; + list-style: none; + } + + &.partial, + &.closed, + &.unset { + color: $red-dark; + font-size: 14px; + font-style: italic; + @include regular; + } + + &.flag { + color: $blue-purple-30; + font-size: 14px; + font-style: italic; + @include regular; + } + + &.condition, + &.fulfillment, + &.tx, + &.channel { + width: calc(100% - 120px); + color: $subdued-color; + font-size: 14px; + white-space: normal; + word-break: break-all; + @include regular; + } + } + + &:first-child { + padding-top: 40px; + } + } + + .error { + padding: 50px; + color: $orange-40; + font-size: 14px; + text-align: center; + + .type { + margin: 0 5px; + @include bold; + } + } + + .group { + margin: 16px 0 16px -16px; + background: rgba($black-80, 0.7); + gap: 15px; + + &:first-child { + margin-top: 0px; + } + + &:last-child { + margin-bottom: 0px; + } + + .group-title { + display: block; + flex-direction: row; + padding: 16px 0 0 16px; + color: $black-40; + font-size: 14px; + font-weight: 600; + text-transform: uppercase; + } + + .row { + padding: 16px 28px 12px 16px; + + &:last-child { + padding-bottom: 16px; + border: none; + } + } + } + } +} diff --git a/src/containers/Header/Search.tsx b/src/containers/Header/Search.tsx index 3b1032be3..686c77208 100644 --- a/src/containers/Header/Search.tsx +++ b/src/containers/Header/Search.tsx @@ -27,10 +27,11 @@ import { HASH192_REGEX, } from '../shared/utils' import './search.scss' -import { getTransaction } from '../../rippled/lib/rippled' +import { getNFTInfo, getTransaction } from '../../rippled/lib/rippled' import { buildPath } from '../shared/routing' import { ACCOUNT_ROUTE, + ENTRY_ROUTE, LEDGER_ROUTE, NFT_ROUTE, TOKEN_ROUTE, @@ -42,10 +43,15 @@ import TokenSearchResults from '../shared/components/TokenSearchResults/TokenSea const determineHashType = async (id: string, rippledContext: XrplClient) => { try { - await getTransaction(rippledContext, id) - return 'transactions' - } catch (e) { + await getNFTInfo(rippledContext, id) return 'nft' + } catch (e) { + try { + await getTransaction(rippledContext, id) + return 'transactions' + } catch (e2) { + return 'entry' + } } } @@ -77,6 +83,8 @@ const getRoute = async ( path = buildPath(TRANSACTION_ROUTE, { identifier: id.toUpperCase() }) } else if (type === 'nft') { path = buildPath(NFT_ROUTE, { id: id.toUpperCase() }) + } else if (type === 'entry') { + path = buildPath(ENTRY_ROUTE, { id: id.toUpperCase() }) } return { diff --git a/src/rippled/lib/rippled.js b/src/rippled/lib/rippled.js index 89f47ad6e..03ffa4649 100644 --- a/src/rippled/lib/rippled.js +++ b/src/rippled/lib/rippled.js @@ -76,7 +76,7 @@ const getLedger = (rippledSocket, parameters) => { } // get ledger_entry -const getLedgerEntry = (rippledSocket, { index }) => { +const getLedgerEntry = (rippledSocket, index) => { const request = { command: 'ledger_entry', index, From 1129c90c220c1a3528e05ec3b0ce4a13792463b0 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 15:38:42 -0400 Subject: [PATCH 03/15] get something working --- src/containers/Entry/Simple/DefaultSimple.tsx | 196 ++++++++++++++++++ src/containers/Entry/Simple/index.tsx | 2 +- src/containers/Entry/SimpleTab.tsx | 50 +++-- src/containers/Entry/index.tsx | 59 ++---- src/containers/Header/Search.tsx | 13 +- 5 files changed, 246 insertions(+), 74 deletions(-) create mode 100644 src/containers/Entry/Simple/DefaultSimple.tsx diff --git a/src/containers/Entry/Simple/DefaultSimple.tsx b/src/containers/Entry/Simple/DefaultSimple.tsx new file mode 100644 index 000000000..d187e5c6c --- /dev/null +++ b/src/containers/Entry/Simple/DefaultSimple.tsx @@ -0,0 +1,196 @@ +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' +import { TransactionSimpleProps } from '../../shared/components/Transaction/types' + +const DEFAULT_ENTRY_ELEMENTS = [ + 'Account', + 'Owner', + 'index', + 'LedgerEntryType', + 'PreviousTxnID', + 'PreviousTxnLgrSeq', + 'Flags', + 'LedgerIndex', +] + +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 + } + if (value.length > 300) { + return `${value.substring(0, 300)}...` + } + if (value === '') { + return {''} + } + 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
{processed}
+ } + const processed = processValue(childValue) + return
{processed}
+ }) + } + + if (typeof value === 'object') { + return ( +
+ {Object.entries(value).map(([childKey, childValue]) => ( +
+ {`${childKey}: `} + {processValue(childValue)} +
+ ))} +
+ ) + } + + return JSON.stringify(value) +} + +const getRowNested = (key: any, value: any, uniqueKey: string = '') => { + if (key === 'Amount') { + return ( + + + + ) + } + + if (isCurrency(value)) { + return ( + + + + ) + } + + if (isAmount(value, key)) { + return ( + + + + ) + } + return ( + + {processValue(value)} + + ) +} + +const getRow = (key: any, value: any) => { + if (Array.isArray(value)) { + return ( +
+ {value.map((innerValue, index) => { + if ( + typeof innerValue === 'object' && + Object.keys(innerValue).length === 1 + ) { + const innerKey = Object.keys(innerValue)[0] + return ( + + {Object.entries(innerValue[innerKey]).map( + ([childKey, childValue], index2) => + getRowNested(childKey, childValue, index2.toString()), + )} + + ) + } + return getRowNested(index.toString(), innerValue, index.toString()) + })} +
+ ) + } + + if ( + typeof value === 'object' && + !isCurrency(value) && + !isAmount(value, key) + ) { + return ( + + {Object.entries(value).map(([childKey, childValue], index) => + getRowNested(childKey, childValue, index.toString()), + )} + + ) + } + + return getRowNested(key, value) +} + +export interface EntrySimpleProps { + data: { + node: I + } +} + +export const DefaultSimple = ({ data }: EntrySimpleProps) => { + console.log(data) + 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))} + ) +} diff --git a/src/containers/Entry/Simple/index.tsx b/src/containers/Entry/Simple/index.tsx index 72554848e..ed5e56d64 100644 --- a/src/containers/Entry/Simple/index.tsx +++ b/src/containers/Entry/Simple/index.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import { ErrorBoundary } from 'react-error-boundary' import { useTranslation } from 'react-i18next' import { transactionTypes } from '../../shared/components/Transaction' -import { DefaultSimple } from '../../shared/components/Transaction/DefaultSimple' +import { DefaultSimple } from './DefaultSimple' export const Simple: FC<{ data: any diff --git a/src/containers/Entry/SimpleTab.tsx b/src/containers/Entry/SimpleTab.tsx index 6f22553e3..00f4d96f1 100644 --- a/src/containers/Entry/SimpleTab.tsx +++ b/src/containers/Entry/SimpleTab.tsx @@ -1,13 +1,11 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { localizeDate, localizeNumber, BREAKPOINTS } from '../shared/utils' import { Account } from '../shared/components/Account' import { Sequence } from '../shared/components/Sequence' import { Simple } from './Simple' import { useLanguage } from '../shared/hooks' import { RouteLink } from '../shared/routing' -import { CURRENCY_OPTIONS, XRP_BASE } from '../shared/transactionUtils' import { SimpleRow } from '../shared/components/Transaction/SimpleRow' import '../shared/css/simpleTab.scss' import './simpleTab.scss' @@ -72,37 +70,37 @@ export const SimpleTab: FC<{ data: any; width: number }> = ({ ) - const { processed } = data - const numberOptions = { ...CURRENCY_OPTIONS, currency: 'XRP' } - const time = localizeDate(new Date(processed.date), language, DATE_OPTIONS) - const ledgerIndex = processed.ledger_index - const fee = processed.tx.Fee - ? localizeNumber( - Number.parseFloat(processed.tx.Fee) / XRP_BASE, - language, - numberOptions, - ) - : 0 + const { node } = data + // const numberOptions = { ...CURRENCY_OPTIONS, currency: 'XRP' } + // const time = localizeDate(new Date(node.date), language, DATE_OPTIONS) + // const ledgerIndex = node.ledger_index + // const fee = node.tx.Fee + // ? localizeNumber( + // Number.parseFloat(node.tx.Fee) / XRP_BASE, + // language, + // numberOptions, + // ) + // : 0 - const rowIndex = renderRowIndex( - time, - ledgerIndex, - fee, - processed.tx.Account, - processed.tx.Sequence, - processed.tx.TicketSequence, - !!processed.tx.EmitDetails, - ) + // const rowIndex = renderRowIndex( + // time, + // ledgerIndex, + // fee, + // node.tx.Account, + // node.tx.Sequence, + // node.tx.TicketSequence, + // !!node.tx.EmitDetails, + // ) return (
- - {width < BREAKPOINTS.landscape && rowIndex} + + {/* {width < BREAKPOINTS.landscape && rowIndex} */}
- {width >= BREAKPOINTS.landscape && ( + {/* {width >= BREAKPOINTS.landscape && (
{rowIndex}
- )} + )} */}
) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 4e0eda89b..cb34d5573 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -6,20 +6,12 @@ import { useWindowSize } from 'usehooks-ts' import NoMatch from '../NoMatch' import { Loader } from '../shared/components/Loader' import { Tabs } from '../shared/components/Tabs' -import { - NOT_FOUND, - BAD_REQUEST, - HASH256_REGEX, - CTID_REGEX, -} from '../shared/utils' +import { NOT_FOUND, BAD_REQUEST, HASH256_REGEX } from '../shared/utils' import { SimpleTab } from './SimpleTab' import './entry.scss' -import { AnalyticsFields, useAnalytics } from '../shared/analytics' +import { useAnalytics } from '../shared/analytics' import SocketContext from '../shared/SocketContext' -import { TxStatus } from '../shared/components/TxStatus' -import { getAction, getCategory } from '../shared/components/Transaction' import { buildPath, useRouteParams } from '../shared/routing' -import { SUCCESSFUL_TRANSACTION } from '../shared/transactionUtils' import { ENTRY_ROUTE } from '../App/routes' import { JsonView } from '../shared/components/JsonView' import { getLedgerEntry } from '../../rippled/lib/rippled' @@ -52,12 +44,13 @@ export const Entry = () => { const { t } = useTranslation() const rippledSocket = useContext(SocketContext) const { trackException, trackScreenLoaded } = useAnalytics() + const { isLoading, data, error, isError } = useQuery(['entry', id], () => { if (id === '') { return undefined } - if (HASH256_REGEX.test(id) || CTID_REGEX.test(id)) { - return getLedgerEntry(rippledSocket, { index: id }).catch( + if (HASH256_REGEX.test(id)) { + return getLedgerEntry(rippledSocket, id).catch( (ledgerEntryRequestError) => { const status = ledgerEntryRequestError.code trackException( @@ -76,40 +69,22 @@ export const Entry = () => { const { width } = useWindowSize() useEffect(() => { - if (!data?.processed) return - - const type = data?.processed.tx.TransactionType - const status = data?.processed.meta.TransactionResult + trackScreenLoaded() - const transactionProperties: AnalyticsFields = { - transaction_action: getAction(type), - transaction_category: getCategory(type), - transaction_type: type, + return () => { + window.scrollTo(0, 0) } - - if (status !== SUCCESSFUL_TRANSACTION) { - transactionProperties.tec_code = status - } - - trackScreenLoaded(transactionProperties) - }, [id, data?.processed, tab, trackScreenLoaded]) + }, [tab, trackScreenLoaded]) function renderSummary() { - const type = data?.processed.tx.TransactionType + const type = data?.node.LedgerEntryType return (
{type}
- -
+
{t('hash')}:
- {data?.processed.hash} + {data?.index}
- {data?.processed.tx.ctid && ( -
-
CTID:
- {data.processed.tx.ctid} -
- )}
) } @@ -120,19 +95,21 @@ export const Entry = () => { return } - function renderTransaction() { + function renderEntry() { if (!data) return undefined + console.log('renderEntry', data) let body switch (tab) { case 'raw': - body = + body = break default: body = break } + console.log(body) return ( <> {renderSummary()} @@ -147,8 +124,8 @@ export const Entry = () => { if (isError) { const message = getErrorMessage(error) body = - } else if (data?.processed && data?.processed.hash) { - body = renderTransaction() + } else if (data != null) { + body = renderEntry() } else if (!id) { body = ( { try { - await getNFTInfo(rippledContext, id) - return 'nft' + await getTransaction(rippledContext, id) + return 'transactions' } catch (e) { try { - await getTransaction(rippledContext, id) - return 'transactions' - } catch (e2) { + await getLedgerEntry(rippledContext, id) return 'entry' + } catch (e2) { + // TODO: better error message here + return 'nft' } } } From adbd35cf7fc90c510a623e966606ec9be4b86fc6 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 15:44:35 -0400 Subject: [PATCH 04/15] get sidebar working --- src/containers/Entry/Simple/DefaultSimple.tsx | 1 - src/containers/Entry/SimpleTab.tsx | 96 +++++-------------- src/containers/Entry/index.tsx | 2 - 3 files changed, 23 insertions(+), 76 deletions(-) diff --git a/src/containers/Entry/Simple/DefaultSimple.tsx b/src/containers/Entry/Simple/DefaultSimple.tsx index d187e5c6c..86432fa57 100644 --- a/src/containers/Entry/Simple/DefaultSimple.tsx +++ b/src/containers/Entry/Simple/DefaultSimple.tsx @@ -183,7 +183,6 @@ export interface EntrySimpleProps { } export const DefaultSimple = ({ data }: EntrySimpleProps) => { - console.log(data) const uniqueData = Object.fromEntries( Object.entries(data.node).filter( ([key, value]) => !DEFAULT_ENTRY_ELEMENTS.includes(key) && value != null, diff --git a/src/containers/Entry/SimpleTab.tsx b/src/containers/Entry/SimpleTab.tsx index 00f4d96f1..da72fc71d 100644 --- a/src/containers/Entry/SimpleTab.tsx +++ b/src/containers/Entry/SimpleTab.tsx @@ -1,106 +1,56 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import { Account } from '../shared/components/Account' -import { Sequence } from '../shared/components/Sequence' import { Simple } from './Simple' -import { useLanguage } from '../shared/hooks' import { RouteLink } from '../shared/routing' import { SimpleRow } from '../shared/components/Transaction/SimpleRow' import '../shared/css/simpleTab.scss' import './simpleTab.scss' -import { LEDGER_ROUTE } from '../App/routes' - -const TIME_ZONE = 'UTC' -const DATE_OPTIONS = { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour12: true, - timeZone: TIME_ZONE, -} +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 language = useLanguage() - const renderRowIndex = ( - time, - ledgerIndex, - fee, - account, - sequence, - ticketSequence, - isHook, - ) => ( + const renderRowIndex = (owner, prevLedgerIndex, prevTx) => ( <> - - {time} - - - - {ledgerIndex} - - - {account && ( - - + {owner && ( + + )} - - + + + {prevLedgerIndex} + - - {fee} + + + {prevTx} + ) - const { node } = data - // const numberOptions = { ...CURRENCY_OPTIONS, currency: 'XRP' } - // const time = localizeDate(new Date(node.date), language, DATE_OPTIONS) - // const ledgerIndex = node.ledger_index - // const fee = node.tx.Fee - // ? localizeNumber( - // Number.parseFloat(node.tx.Fee) / XRP_BASE, - // language, - // numberOptions, - // ) - // : 0 - - // const rowIndex = renderRowIndex( - // time, - // ledgerIndex, - // fee, - // node.tx.Account, - // node.tx.Sequence, - // node.tx.TicketSequence, - // !!node.tx.EmitDetails, - // ) + const rowIndex = renderRowIndex( + data?.node?.Owner ?? data?.node?.Account, + data?.node?.PreviousTxnLgrSeq, + data?.node?.PreviousTxnID, + ) return (
- - {/* {width < BREAKPOINTS.landscape && rowIndex} */} + + {width < BREAKPOINTS.landscape && rowIndex}
- {/* {width >= BREAKPOINTS.landscape && ( + {width >= BREAKPOINTS.landscape && (
{rowIndex}
- )} */} + )}
) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index cb34d5573..de53fab0d 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -97,7 +97,6 @@ export const Entry = () => { function renderEntry() { if (!data) return undefined - console.log('renderEntry', data) let body @@ -109,7 +108,6 @@ export const Entry = () => { body = break } - console.log(body) return ( <> {renderSummary()} From ed6d9ef7b898daf6ad6ce88d015156262d30e6c3 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:04:27 -0400 Subject: [PATCH 05/15] link from metadata --- public/locales/en-US/translations.json | 2 +- src/containers/Entry/index.tsx | 1 + .../Transactions/DetailTab/Meta/index.tsx | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 5badb6f88..387e53941 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -257,7 +257,7 @@ "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", + "node_meta_type": "It {{action}} a node with type", "transaction_balance_line_one": "It <1><0>{{action}} a <3><0>{{currency}} RippleState node between <5><0>{{account}} and <7><0>{{counterAccount}}", "transaction_balance_line_two": "Balance changed by <1><0>{{change}} from <3><0>{{previousBalance}} to <5><0>{{finalBalance}}", "transaction_outstanding_balance_line_two": "Outstanding balance changed by <1><0>{{change}} from <3><0>{{previousBalance}} to <5><0>{{finalBalance}}", diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index de53fab0d..1067e1194 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -116,6 +116,7 @@ export const Entry = () => { ) } + console.log(data) let body diff --git a/src/containers/Transactions/DetailTab/Meta/index.tsx b/src/containers/Transactions/DetailTab/Meta/index.tsx index a7a6cc226..a6e3368f8 100644 --- a/src/containers/Transactions/DetailTab/Meta/index.tsx +++ b/src/containers/Transactions/DetailTab/Meta/index.tsx @@ -1,5 +1,5 @@ import { FC } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import renderAccountRoot from './AccountRoot' import renderDirectoryNode from './DirectoryNode' import renderOffer from './Offer' @@ -9,10 +9,23 @@ import renderMPToken from './MPToken' import renderMPTokenIssuance from './MPTokenIssuance' import { groupAffectedNodes } from '../../../shared/transactionUtils' import { useLanguage } from '../../../shared/hooks' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const renderDefault = (t, action, node, index) => (
  • - {t('node_meta_type', { action })} {node.LedgerEntryType} + + {/* The inner text will be replaced by the content of in the JSON */} + + ), + }} + />{' '} + {node.LedgerEntryType}
  • ) @@ -20,7 +33,7 @@ export const TransactionMeta: FC<{ data: any }> = ({ data }) => { const language = useLanguage() const { t } = useTranslation() - const renderNodesMeta = (action, list, tx) => { + const renderNodesMeta = (action: string, list: any[], tx: any) => { const meta = list.map((node, index) => { switch (node.LedgerEntryType) { case 'AccountRoot': From 7997ddb65938c79d7dfe6c95a36b1594af4d4e43 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:12:53 -0400 Subject: [PATCH 06/15] basic deleted object support --- src/containers/Entry/Simple/DefaultSimple.tsx | 2 +- src/containers/Entry/index.tsx | 3 ++- src/rippled/lib/rippled.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/containers/Entry/Simple/DefaultSimple.tsx b/src/containers/Entry/Simple/DefaultSimple.tsx index 86432fa57..990e813a6 100644 --- a/src/containers/Entry/Simple/DefaultSimple.tsx +++ b/src/containers/Entry/Simple/DefaultSimple.tsx @@ -5,7 +5,6 @@ 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' -import { TransactionSimpleProps } from '../../shared/components/Transaction/types' const DEFAULT_ENTRY_ELEMENTS = [ 'Account', @@ -16,6 +15,7 @@ const DEFAULT_ENTRY_ELEMENTS = [ 'PreviousTxnLgrSeq', 'Flags', 'LedgerIndex', + 'OwnerNode', ] const displayKey = (key: string) => key.replace(/([a-z])([A-Z])/g, '$1 $2') diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 1067e1194..5cccf424c 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -50,7 +50,7 @@ export const Entry = () => { return undefined } if (HASH256_REGEX.test(id)) { - return getLedgerEntry(rippledSocket, id).catch( + return getLedgerEntry(rippledSocket, id, true).catch( (ledgerEntryRequestError) => { const status = ledgerEntryRequestError.code trackException( @@ -81,6 +81,7 @@ export const Entry = () => { return (
    {type}
    + {data?.deleted_ledger_index && 'DELETED'}
    {t('hash')}:
    {data?.index} diff --git a/src/rippled/lib/rippled.js b/src/rippled/lib/rippled.js index 03ffa4649..c05f8292e 100644 --- a/src/rippled/lib/rippled.js +++ b/src/rippled/lib/rippled.js @@ -76,11 +76,12 @@ const getLedger = (rippledSocket, parameters) => { } // get ledger_entry -const getLedgerEntry = (rippledSocket, index) => { +const getLedgerEntry = (rippledSocket, index, includeDeleted = false) => { const request = { command: 'ledger_entry', index, ledger_index: 'validated', + include_deleted: includeDeleted, } return query(rippledSocket, request).then((resp) => { From b05c038d6af0206fb5cd702cb560bf4572d5bc35 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:20:51 -0400 Subject: [PATCH 07/15] get "no entry exists" working --- src/containers/Entry/index.tsx | 4 ++-- src/containers/Transactions/index.tsx | 1 + src/rippled/lib/rippled.js | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 5cccf424c..1f540a550 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -117,14 +117,14 @@ export const Entry = () => { ) } - console.log(data) + console.log(data, isError, error) let body if (isError) { const message = getErrorMessage(error) body = - } else if (data != null) { + } else if (data?.index != null) { body = renderEntry() } else if (!id) { body = ( diff --git a/src/containers/Transactions/index.tsx b/src/containers/Transactions/index.tsx index 2918e7674..893780705 100644 --- a/src/containers/Transactions/index.tsx +++ b/src/containers/Transactions/index.tsx @@ -53,6 +53,7 @@ export const Transaction = () => { const { t } = useTranslation() const rippledSocket = useContext(SocketContext) const { trackException, trackScreenLoaded } = useAnalytics() + const { isLoading, data, error, isError } = useQuery( ['transaction', identifier], () => { diff --git a/src/rippled/lib/rippled.js b/src/rippled/lib/rippled.js index c05f8292e..3a50e0f63 100644 --- a/src/rippled/lib/rippled.js +++ b/src/rippled/lib/rippled.js @@ -85,55 +85,55 @@ const getLedgerEntry = (rippledSocket, index, includeDeleted = false) => { } return query(rippledSocket, request).then((resp) => { - if (resp.error_message === 'entryNotFound') { + if (resp.error === 'entryNotFound') { throw new Error('ledger entry not found', 404) } - if (resp.error_message === 'invalidParams') { + if (resp.error === 'invalidParams') { throw new Error('invalidParams for ledger_entry', 404) } - if (resp.error_message === 'lgrNotFound') { + if (resp.error === 'lgrNotFound') { throw new Error('invalid ledger index/hash', 400) } - if (resp.error_message === 'malformedAddress') { + if (resp.error === 'malformedAddress') { throw new Error( 'The ledger_entry request improperly specified an Address field.', 404, ) } - if (resp.error_message === 'malformedCurrency') { + if (resp.error === 'malformedCurrency') { throw new Error( 'The ledger_entry request improperly specified a Currency Code field.', 404, ) } - if (resp.error_message === 'malformedOwner') { + if (resp.error === 'malformedOwner') { throw new Error( 'The ledger_entry request improperly specified the escrow.owner sub-field.', 404, ) } - if (resp.error_message === 'malformedRequest') { + if (resp.error === 'malformedRequest') { throw new Error( 'The ledger_entry request provided an invalid combination of fields, or provided the wrong type for one or more fields.', 404, ) } - if (resp.error_message === 'unknownOption') { + if (resp.error === 'unknownOption') { throw new Error( 'The fields provided in the ledger_entry request did not match any of the expected request formats.', 404, ) } - if (resp.error_message) { - throw new Error(resp.error_message, 500) + if (resp.error) { + throw new Error(resp.error, 500) } return resp From 6d66b2847ccaef587ecf3b02c9d9ad77b16157ba Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:26:42 -0400 Subject: [PATCH 08/15] clean up translations --- public/locales/en-US/translations.json | 10 +++++++++- src/containers/Entry/index.tsx | 22 ++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 387e53941..a55055e41 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -565,5 +565,13 @@ "expiration": "Expiration", "domain_id": "Domain ID", "accepted_credentials": "Accepted Credentials", - "expected_date": "Expected Date" + "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" } diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 1f540a550..6c8aa5218 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -16,20 +16,14 @@ import { ENTRY_ROUTE } from '../App/routes' import { JsonView } from '../shared/components/JsonView' import { getLedgerEntry } from '../../rippled/lib/rippled' -const WRONG_NETWORK = 406 - const ERROR_MESSAGES: Record = {} ERROR_MESSAGES[NOT_FOUND] = { - title: 'transaction_not_found', - hints: ['server_ledgers_hint', 'check_transaction_hash'], + title: 'entry_not_found', + hints: ['check_entry_id'], } ERROR_MESSAGES[BAD_REQUEST] = { - title: 'invalid_transaction_hash', - hints: ['check_transaction_hash'], -} -ERROR_MESSAGES[WRONG_NETWORK] = { - title: 'wrong_network', - hints: ['check_transaction_hash'], + title: 'invalid_entry_id', + hints: ['check_entry_id'], } ERROR_MESSAGES.default = { title: 'generic_error', @@ -129,15 +123,15 @@ export const Entry = () => { } else if (!id) { body = ( ) } return ( -
    - +
    + {isLoading && } {body}
    From 68af77e22dbc3ad70a735a5143176b7c3a007714 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:34:27 -0400 Subject: [PATCH 09/15] clean up css --- public/locales/en-US/translations.json | 3 ++- src/containers/Entry/entry.scss | 4 ++-- src/containers/Entry/index.tsx | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index a55055e41..386dad835 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -573,5 +573,6 @@ "check_entry_id": "Please check your entry ID.", "entry_short": "Entry", "entry": "Entry", - "invalid_entry_id": "The entry ID is invalid" + "invalid_entry_id": "The entry ID is invalid", + "id": "ID" } diff --git a/src/containers/Entry/entry.scss b/src/containers/Entry/entry.scss index 45997a886..99b8dab2a 100644 --- a/src/containers/Entry/entry.scss +++ b/src/containers/Entry/entry.scss @@ -1,6 +1,6 @@ @use '../shared/css/variables' as *; -.transaction { +.entry { position: relative; max-width: 912px; min-height: 600px; @@ -35,7 +35,7 @@ @include bold; } - .txid { + .id { .title { @include semibold; } diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 6c8aa5218..efeb7ec60 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -76,8 +76,11 @@ export const Entry = () => {
    {type}
    {data?.deleted_ledger_index && 'DELETED'} -
    -
    {t('hash')}:
    +
    +
    + {t('id')} + {': '}{' '} +
    {data?.index}
    From a774d6b67e46d52adc8a93932f27add3b3bc7a78 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:35:07 -0400 Subject: [PATCH 10/15] redirect accounts --- src/containers/Entry/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index efeb7ec60..888aafcb7 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -3,6 +3,7 @@ import { Helmet } from 'react-helmet-async' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' import { useWindowSize } from 'usehooks-ts' +import { useNavigate } from 'react-router' import NoMatch from '../NoMatch' import { Loader } from '../shared/components/Loader' import { Tabs } from '../shared/components/Tabs' @@ -12,7 +13,7 @@ import './entry.scss' import { useAnalytics } from '../shared/analytics' import SocketContext from '../shared/SocketContext' import { buildPath, useRouteParams } from '../shared/routing' -import { ENTRY_ROUTE } from '../App/routes' +import { ACCOUNT_ROUTE, ENTRY_ROUTE } from '../App/routes' import { JsonView } from '../shared/components/JsonView' import { getLedgerEntry } from '../../rippled/lib/rippled' @@ -38,6 +39,7 @@ export const Entry = () => { const { t } = useTranslation() const rippledSocket = useContext(SocketContext) const { trackException, trackScreenLoaded } = useAnalytics() + const navigate = useNavigate() const { isLoading, data, error, isError } = useQuery(['entry', id], () => { if (id === '') { @@ -122,6 +124,10 @@ export const Entry = () => { const message = getErrorMessage(error) body = } else if (data?.index != null) { + if (data.node.LedgerEntryType === 'AccountRoot') { + const path = buildPath(ACCOUNT_ROUTE, { id: data.node.Account }) + navigate(path) + } body = renderEntry() } else if (!id) { body = ( From 7e1845bdc743a2395040250db88ec5eee6221661 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 16:39:40 -0400 Subject: [PATCH 11/15] fix linting issues --- src/containers/Entry/index.tsx | 1 - src/containers/Transactions/DetailTab/Meta/index.tsx | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 888aafcb7..954f3d417 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -116,7 +116,6 @@ export const Entry = () => { ) } - console.log(data, isError, error) let body diff --git a/src/containers/Transactions/DetailTab/Meta/index.tsx b/src/containers/Transactions/DetailTab/Meta/index.tsx index a6e3368f8..a496bcf74 100644 --- a/src/containers/Transactions/DetailTab/Meta/index.tsx +++ b/src/containers/Transactions/DetailTab/Meta/index.tsx @@ -12,7 +12,7 @@ import { useLanguage } from '../../../shared/hooks' import { RouteLink } from '../../../shared/routing' import { ENTRY_ROUTE } from '../../../App/routes' -const renderDefault = (t, action, node, index) => ( +const renderDefault = (action, node, index) => (
  • ( components={{ Link: ( - {/* The inner text will be replaced by the content of in the JSON */} + {/* The inner text will be replaced by the content of in translations.json */} ), }} @@ -51,7 +51,7 @@ export const TransactionMeta: FC<{ data: any }> = ({ data }) => { case 'MPToken': return renderMPToken(t, language, action, node, index) default: - return renderDefault(t, action, node, index) + return renderDefault(action, node, index) } }) From b4d3eff7fef9ff169e9842d797a4b86bb80546bc Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 17:10:22 -0400 Subject: [PATCH 12/15] add links to all metadata objects --- public/locales/en-US/translations.json | 12 ++++----- .../DetailTab/Meta/DirectoryNode.jsx | 26 +++++++++++++------ .../Transactions/DetailTab/Meta/MPToken.jsx | 8 +++++- .../DetailTab/Meta/MPTokenIssuance.jsx | 8 +++++- .../Transactions/DetailTab/Meta/Offer.jsx | 7 ++++- .../DetailTab/Meta/PayChannel.jsx | 8 +++++- .../DetailTab/Meta/RippleState.jsx | 8 +++++- 7 files changed, 58 insertions(+), 19 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 386dad835..59707e628 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -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}} a PayChannel node from <3><0>{{account}} to <5><0>{{counterAccount}}", + "paychannel_node_line1": "It <1><0>{{action}} a PayChannel <3><0>{{node}} from <5><0>{{account}} to <7><0>{{counterAccount}}", "paychannel_amount_changed": "Amount changed by <1><0>{{difference}}<1><0>{{currency}} from <3><0>{{previous}}<1><0>{{currency}} to <5><0>{{final}}<1><0>{{currency}}", "paychannel_balance_changed": "Balance changed by <1><0>{{difference}}<1><0>{{currency}} from <3><0>{{previous}}<1><0>{{currency}} to <5><0>{{final}}<1><0>{{currency}}", "setfee_fees_description": "Future transactions will require a minimum fee of .", @@ -258,19 +258,19 @@ "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}} a <3><0>{{currency}} RippleState node between <5><0>{{account}} and <7><0>{{counterAccount}}", + "transaction_balance_line_one": "It <1><0>{{action}} a <3><0>{{currency}} RippleState <5><0>{{node}} between <7><0>{{account}} and <9><0>{{counterAccount}}", "transaction_balance_line_two": "Balance changed by <1><0>{{change}} from <3><0>{{previousBalance}} to <5><0>{{finalBalance}}", "transaction_outstanding_balance_line_two": "Outstanding balance changed by <1><0>{{change}} from <3><0>{{previousBalance}} to <5><0>{{finalBalance}}", - "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 node owned by", + "transaction_unowned_directory": "It {{action}} a DirectoryNode node", "transaction_mptoken_line_one": "It <1><0>{{action}} an MPToken node of <3><0>{{account}}", - "transaction_mpt_issuance_line_one": "It <1><0>{{action}} an MPTokenIssuance node of <3><0>{{account}}", + "transaction_mpt_issuance_line_one": "It <1><0>{{action}} an MPTokenIssuance <3><0>node of <5><0>{{account}}", "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}}<1><0>{{currency}} from <3><0>{{previous}}<1><0>{{currency}} to <5><0>{{final}}<1><0>{{currency}}", "account_balance_decreased": "Balance decreased by <1><0>{{difference}}<1><0>{{currency}} from <3><0>{{previous}}<1><0>{{currency}} to <5><0>{{final}}<1><0>{{currency}}", "decreased_from_to": "decreased by <1><0>{{change}} from <3><0>{{previous}} to <5><0>{{final}}", - "offer_node_meta": "It <1><0>{{action}} a <3><0>{{pair}} offer node owned by <5><0>{{account}} with sequence # <7><0>{{sequence}}", + "offer_node_meta": "It <1><0>{{action}} a <3><0>{{pair}} offer <5><0>{{node}} owned by <7><0>{{account}} with sequence # <9><0>{{sequence}}", "offer_replaces": "This offer replaces the existing offer #", "offer_partially_filled": "The offer was partially filled", "offer_filled": "The offer was filled", diff --git a/src/containers/Transactions/DetailTab/Meta/DirectoryNode.jsx b/src/containers/Transactions/DetailTab/Meta/DirectoryNode.jsx index 62c6ec23f..50bd96c6d 100644 --- a/src/containers/Transactions/DetailTab/Meta/DirectoryNode.jsx +++ b/src/containers/Transactions/DetailTab/Meta/DirectoryNode.jsx @@ -1,17 +1,27 @@ +import { Trans } from 'react-i18next' import { Account } from '../../../shared/components/Account' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const render = (t, action, node, index) => { const fields = node.FinalFields || node.NewFields return (
  • - {t( - fields.Owner - ? 'transaction_owned_directory' - : 'transaction_unowned_directory', - { - action, - }, - )} + + {/* The inner text will be replaced by the content of in translations.json */} + + ), + }} + /> {fields.Owner && ( {' '} diff --git a/src/containers/Transactions/DetailTab/Meta/MPToken.jsx b/src/containers/Transactions/DetailTab/Meta/MPToken.jsx index b94856e40..604dab78f 100644 --- a/src/containers/Transactions/DetailTab/Meta/MPToken.jsx +++ b/src/containers/Transactions/DetailTab/Meta/MPToken.jsx @@ -1,6 +1,8 @@ import { Trans } from 'react-i18next' import { Account } from '../../../shared/components/Account' import { computeMPTokenBalanceChange } from '../../../shared/utils' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const render = (t, language, action, node, index) => { const { previousBalance, finalBalance, account, change } = @@ -11,7 +13,11 @@ const render = (t, language, action, node, index) => { const line1 = ( - It {action} an MPToken node of + It {action} an MPToken + + node + + of ) diff --git a/src/containers/Transactions/DetailTab/Meta/MPTokenIssuance.jsx b/src/containers/Transactions/DetailTab/Meta/MPTokenIssuance.jsx index 99305b59c..79bdee270 100644 --- a/src/containers/Transactions/DetailTab/Meta/MPTokenIssuance.jsx +++ b/src/containers/Transactions/DetailTab/Meta/MPTokenIssuance.jsx @@ -1,6 +1,8 @@ import { Trans } from 'react-i18next' import { Account } from '../../../shared/components/Account' import { computeMPTIssuanceBalanceChange } from '../../../shared/utils' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const render = (t, language, action, node, index) => { const { previousBalance, finalBalance, account, change } = @@ -11,7 +13,11 @@ const render = (t, language, action, node, index) => { const line1 = ( - It {action} an MPTokenIssuance node of + It {action} an MPTokenIssuance + + node + + of ) diff --git a/src/containers/Transactions/DetailTab/Meta/Offer.jsx b/src/containers/Transactions/DetailTab/Meta/Offer.jsx index 5131c4868..89099d5a8 100644 --- a/src/containers/Transactions/DetailTab/Meta/Offer.jsx +++ b/src/containers/Transactions/DetailTab/Meta/Offer.jsx @@ -7,6 +7,8 @@ import { } from '../../../shared/transactionUtils' import { localizeNumber } from '../../../shared/utils' import { Account } from '../../../shared/components/Account' +import { ENTRY_ROUTE } from '../../../App/routes' +import { RouteLink } from '../../../shared/routing' const normalize = (value, currency) => currency === 'XRP' ? (value / XRP_BASE).toString() : value @@ -176,7 +178,10 @@ const render = (t, language, action, node, index, tx) => { return (
  • - It {action} a {pair} + It {action} a {pair} offer + + node + owned by with sequence # {{ sequence: fields.Sequence }} diff --git a/src/containers/Transactions/DetailTab/Meta/PayChannel.jsx b/src/containers/Transactions/DetailTab/Meta/PayChannel.jsx index 47e1a7235..906d2b27d 100644 --- a/src/containers/Transactions/DetailTab/Meta/PayChannel.jsx +++ b/src/containers/Transactions/DetailTab/Meta/PayChannel.jsx @@ -2,6 +2,8 @@ import { Trans } from 'react-i18next' import { CURRENCY_OPTIONS } from '../../../shared/transactionUtils' import { localizeNumber } from '../../../shared/utils' import { Account } from '../../../shared/components/Account' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const MILLION = 1000000 @@ -14,7 +16,11 @@ const render = (t, language, action, node, index) => { const line1 = ( - It {action} a PayChannel node from + It {action} a PayChannel + + node + + from to diff --git a/src/containers/Transactions/DetailTab/Meta/RippleState.jsx b/src/containers/Transactions/DetailTab/Meta/RippleState.jsx index 2ea9539eb..be0a717e3 100644 --- a/src/containers/Transactions/DetailTab/Meta/RippleState.jsx +++ b/src/containers/Transactions/DetailTab/Meta/RippleState.jsx @@ -4,6 +4,8 @@ import { localizeNumber, computeRippleStateBalanceChange, } from '../../../shared/utils' +import { RouteLink } from '../../../shared/routing' +import { ENTRY_ROUTE } from '../../../App/routes' const render = (t, language, action, node, index) => { const { @@ -19,7 +21,11 @@ const render = (t, language, action, node, index) => { const line1 = ( It {action} a {currency} - ripplestate node between + ripplestate + + node + + between and From 12145f6226ae5d3ccb5b49c426f35df5a2f4aa4f Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 31 Mar 2025 18:03:28 -0400 Subject: [PATCH 13/15] add more redirects and links --- src/containers/Entry/index.tsx | 6 +++++- src/containers/NFT/NFTTabs/Offers.tsx | 6 ++++-- .../components/Transaction/NFTokenAcceptOffer/Simple.tsx | 6 +++++- .../components/Transaction/NFTokenCancelOffer/Simple.tsx | 6 +++++- .../components/Transaction/NFTokenCreateOffer/Simple.tsx | 6 +++++- .../components/Transaction/PaymentChannelCreate/Simple.tsx | 6 +++++- 6 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 954f3d417..079362cc3 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -13,7 +13,7 @@ import './entry.scss' import { useAnalytics } from '../shared/analytics' import SocketContext from '../shared/SocketContext' import { buildPath, useRouteParams } from '../shared/routing' -import { ACCOUNT_ROUTE, ENTRY_ROUTE } from '../App/routes' +import { ACCOUNT_ROUTE, ENTRY_ROUTE, MPT_ROUTE } from '../App/routes' import { JsonView } from '../shared/components/JsonView' import { getLedgerEntry } from '../../rippled/lib/rippled' @@ -127,6 +127,10 @@ export const Entry = () => { const path = buildPath(ACCOUNT_ROUTE, { id: data.node.Account }) navigate(path) } + if (data.node.LedgerEntryType === 'MPTokenIssuance') { + const path = buildPath(MPT_ROUTE, { id: data.node.mpt_issuance_id }) + navigate(path) + } body = renderEntry() } else if (!id) { body = ( diff --git a/src/containers/NFT/NFTTabs/Offers.tsx b/src/containers/NFT/NFTTabs/Offers.tsx index 4a55cfedf..698ba5879 100644 --- a/src/containers/NFT/NFTTabs/Offers.tsx +++ b/src/containers/NFT/NFTTabs/Offers.tsx @@ -10,7 +10,7 @@ import { Amount } from '../../shared/components/Amount' import '../../shared/components/TransactionTable/styles.scss' // Reuse load-more-btn import { formatAmount } from '../../../rippled/lib/txSummary/formatAmount' import { LoadMoreButton } from '../../shared/LoadMoreButton' -import { ACCOUNT_ROUTE } from '../../App/routes' +import { ACCOUNT_ROUTE, ENTRY_ROUTE } from '../../App/routes' import { RouteLink } from '../../shared/routing' interface Props { @@ -55,7 +55,9 @@ export const Offers = (props: Props) => { return ( - {offerIndex} + + {offerIndex} + diff --git a/src/containers/shared/components/Transaction/NFTokenAcceptOffer/Simple.tsx b/src/containers/shared/components/Transaction/NFTokenAcceptOffer/Simple.tsx index c1ccfacef..2a49dadfa 100644 --- a/src/containers/shared/components/Transaction/NFTokenAcceptOffer/Simple.tsx +++ b/src/containers/shared/components/Transaction/NFTokenAcceptOffer/Simple.tsx @@ -5,6 +5,8 @@ import { Amount } from '../../Amount' import { NFTokenAcceptOfferInstructions } from './types' import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' import { NFTokenLink } from '../../NFTokenLink' +import { ENTRY_ROUTE } from '../../../../App/routes' +import { RouteLink } from '../../../routing' export const Simple: TransactionSimpleComponent = ({ data, @@ -16,7 +18,9 @@ export const Simple: TransactionSimpleComponent = ({ <> {acceptedOfferIDs.map((offer) => ( - {offer} + + {offer} + ))} {amount && seller && buyer && tokenID && ( diff --git a/src/containers/shared/components/Transaction/NFTokenCancelOffer/Simple.tsx b/src/containers/shared/components/Transaction/NFTokenCancelOffer/Simple.tsx index 044c0316f..ee73b552a 100644 --- a/src/containers/shared/components/Transaction/NFTokenCancelOffer/Simple.tsx +++ b/src/containers/shared/components/Transaction/NFTokenCancelOffer/Simple.tsx @@ -5,6 +5,8 @@ import { Amount } from '../../Amount' import { NFTokenCancelOfferInstructions } from './types' import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' import { NFTokenLink } from '../../NFTokenLink' +import { ENTRY_ROUTE } from '../../../../App/routes' +import { RouteLink } from '../../../routing' export const Simple: TransactionSimpleComponent = ({ data, @@ -21,7 +23,9 @@ export const Simple: TransactionSimpleComponent = ({ className="dt" data-test="offer-id" > - {offerID} + + {offerID} + diff --git a/src/containers/shared/components/Transaction/NFTokenCreateOffer/Simple.tsx b/src/containers/shared/components/Transaction/NFTokenCreateOffer/Simple.tsx index 14e8a2bd7..021c94189 100644 --- a/src/containers/shared/components/Transaction/NFTokenCreateOffer/Simple.tsx +++ b/src/containers/shared/components/Transaction/NFTokenCreateOffer/Simple.tsx @@ -5,6 +5,8 @@ import { SimpleRow } from '../SimpleRow' import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' import { NFTokenCreateOfferInstructions } from './types' import { NFTokenLink } from '../../NFTokenLink' +import { ENTRY_ROUTE } from '../../../../App/routes' +import { RouteLink } from '../../../routing' export const Simple: TransactionSimpleComponent = ({ data, @@ -17,7 +19,9 @@ export const Simple: TransactionSimpleComponent = ({ <> {offerID && ( - {offerID} + + {offerID} + )} - {channel} + + {channel} + )} From c8133d5ea7d260241cf4e69d79ba0c2b6f62576d Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 1 Apr 2025 17:23:51 -0400 Subject: [PATCH 14/15] redirect AMM --- src/containers/Entry/index.tsx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/containers/Entry/index.tsx b/src/containers/Entry/index.tsx index 079362cc3..c0cce6ef2 100644 --- a/src/containers/Entry/index.tsx +++ b/src/containers/Entry/index.tsx @@ -72,6 +72,21 @@ export const Entry = () => { } }, [tab, trackScreenLoaded]) + useEffect(() => { + if (id != null && data?.index != null) { + if (data.node.LedgerEntryType === 'AccountRoot') { + const path = buildPath(ACCOUNT_ROUTE, { id: data.node.Account }) + navigate(path) + } else if (data.node.LedgerEntryType === 'MPTokenIssuance') { + const path = buildPath(MPT_ROUTE, { id: data.node.mpt_issuance_id }) + navigate(path) + } else if (data.node.LedgerEntryType === 'AMM') { + const path = buildPath(ACCOUNT_ROUTE, { id: data.node.Account }) + navigate(path) + } + } + }, [id, data, navigate]) + function renderSummary() { const type = data?.node.LedgerEntryType return ( @@ -123,14 +138,6 @@ export const Entry = () => { const message = getErrorMessage(error) body = } else if (data?.index != null) { - if (data.node.LedgerEntryType === 'AccountRoot') { - const path = buildPath(ACCOUNT_ROUTE, { id: data.node.Account }) - navigate(path) - } - if (data.node.LedgerEntryType === 'MPTokenIssuance') { - const path = buildPath(MPT_ROUTE, { id: data.node.mpt_issuance_id }) - navigate(path) - } body = renderEntry() } else if (!id) { body = ( From ff7e79c1338c1610737a4c19065e5884f0c0425d Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Tue, 21 Oct 2025 15:16:50 -0400 Subject: [PATCH 15/15] run linter --- src/containers/Accounts/AccountAsset/styles.scss | 1 + src/containers/Accounts/AccountSummary/styles.scss | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/containers/Accounts/AccountAsset/styles.scss b/src/containers/Accounts/AccountAsset/styles.scss index ef3792562..7434c1211 100644 --- a/src/containers/Accounts/AccountAsset/styles.scss +++ b/src/containers/Accounts/AccountAsset/styles.scss @@ -164,6 +164,7 @@ backdrop-filter: blur( 100px ); /* As table rows scroll under the header, they appear blurred behind it */ + color: $black-50; font-size: 12px; text-align: left; diff --git a/src/containers/Accounts/AccountSummary/styles.scss b/src/containers/Accounts/AccountSummary/styles.scss index 79fca4b17..77723eada 100644 --- a/src/containers/Accounts/AccountSummary/styles.scss +++ b/src/containers/Accounts/AccountSummary/styles.scss @@ -397,9 +397,11 @@ padding: 12px; gap: 8px; } + .details-label { font-size: 12px; } + .details-value { font-size: 14px; } @@ -466,6 +468,7 @@ .properties { .properties-header { gap: 8px; + .properties-toggle { padding: 4px; } @@ -489,6 +492,7 @@ .flags-card { grid-column: auto; } + .signers-card { grid-column: auto; }