}>
-
+
{vertical.associatedAccounts.map((associatedAccount, i) => (
@@ -54,7 +54,7 @@ export function VerticalTitle({ vertical }: { vertical: Vertical }) {
leftComponent={
}
rightComponent={
}
>
-
+
{vertical.associatedAccounts.map((associatedAccount, i) => (
diff --git a/src/features/transactions/components/latest-transactions.tsx b/src/features/transactions/components/latest-transactions.tsx
index 251608667..b00c5344f 100644
--- a/src/features/transactions/components/latest-transactions.tsx
+++ b/src/features/transactions/components/latest-transactions.tsx
@@ -7,6 +7,11 @@ import { DescriptionList } from '@/features/common/components/description-list'
import { ArrowRightLeft, Info } from 'lucide-react'
import { Badge } from '@/features/common/components/badge'
import { TransactionSummary } from '@/features/transactions/models'
+import { useLoadableNfd } from '@/features/nfd/data/nfd'
+import { RenderLoadable } from '@/features/common/components/render-loadable'
+import { useMemo } from 'react'
+import { Loadable } from 'jotai/vanilla/utils/loadable'
+import { NfdResult } from '@/features/nfd/data/types'
export const latestTransactionsTitle = 'Latest Transactions'
@@ -14,6 +19,58 @@ type Props = {
latestTransactions: TransactionSummary[]
}
+function useConditionalLoadableNfdResult(address: string | number | undefined) {
+ const [loadableNfd] = useLoadableNfd(typeof address === 'string' ? address : '')
+ return useMemo(() => ({ loadableNfd, isString: typeof address === 'string' }), [address, loadableNfd]) as {
+ loadableNfd: Loadable
+ isString: boolean
+ }
+}
+
+function Transaction({ transaction }: { transaction: TransactionSummary }) {
+ const { loadableNfd: loadableNfdFrom } = useConditionalLoadableNfdResult(transaction.from)
+ const { loadableNfd: loadableNfdTo, isString: isTransactionToString } = useConditionalLoadableNfdResult(transaction.to)
+
+ return (
+
+
+
+
+
{ellipseId(transaction.id)}
+
+ {(nfd) => <>{nfd?.name ?? ellipseAddress(transaction.from)}>}
+
+ ),
+ },
+ {
+ dt: 'To:',
+ dd: isTransactionToString ? (
+
+ {(nfd) => <>{nfd?.name ?? ellipseAddress(transaction.to as string)}>}
+
+ ) : (
+ <>{transaction.to}>
+ ),
+ },
+ ]}
+ />
+
+
+ {transaction.type}
+
+
+
+ )
+}
+
export function LatestTransactions({ latestTransactions }: Props) {
return (
@@ -22,29 +79,7 @@ export function LatestTransactions({ latestTransactions }: Props) {
{latestTransactions.length > 0 && (
{latestTransactions.map((transaction) => (
- -
-
-
-
-
{ellipseId(transaction.id)}
-
-
-
- {transaction.type}
-
-
-
+
))}
)}
diff --git a/src/features/transactions/pages/transaction-page.test.tsx b/src/features/transactions/pages/transaction-page.test.tsx
index ad8bd0541..2dd3e19aa 100644
--- a/src/features/transactions/pages/transaction-page.test.tsx
+++ b/src/features/transactions/pages/transaction-page.test.tsx
@@ -86,6 +86,7 @@ import { AppInterfaceEntity, dbConnectionAtom } from '@/features/common/data/ind
import { genesisHashAtom } from '@/features/blocks/data'
import { writeAppInterface } from '@/features/app-interfaces/data'
import { algod } from '@/features/common/data/algo-client'
+import { useLoadableNfd } from '@/features/nfd/data/nfd'
vi.mock('@/features/common/data/algo-client', async () => {
const original = await vi.importActual('@/features/common/data/algo-client')
@@ -159,6 +160,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -218,6 +221,7 @@ describe('transaction-page', () => {
beforeEach(() => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
})
it('should show the multisig information', () => {
@@ -259,6 +263,7 @@ describe('transaction-page', () => {
beforeEach(() => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
})
it('should show 2 tabs with the logicsig base64 as default', () => {
@@ -320,6 +325,7 @@ describe('transaction-page', () => {
beforeEach(() => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
})
it('should show 2 tabs with the note base64 as default', () => {
@@ -370,6 +376,7 @@ describe('transaction-page', () => {
beforeEach(() => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
})
it('should show 3 tabs with the note json as default', () => {
@@ -437,6 +444,7 @@ describe('transaction-page', () => {
beforeEach(() => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
})
it('should show 3 tabs with the note arc-2 as default', () => {
@@ -504,6 +512,7 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
@@ -565,6 +574,7 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
@@ -590,6 +600,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
@@ -615,6 +627,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
@@ -648,6 +662,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(
@@ -747,6 +763,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id, '*': '2' }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(
@@ -877,6 +895,7 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id, '*': '2/1' }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(
@@ -914,6 +933,7 @@ describe('transaction-page', () => {
it('should be rendered without error', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(
@@ -945,6 +965,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -978,6 +1000,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1039,6 +1063,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1069,6 +1095,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
@@ -1132,6 +1160,8 @@ describe('transaction-page', () => {
it('should be rendered correctly', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
return executeComponentTest(
@@ -1161,6 +1191,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1197,6 +1229,8 @@ describe('transaction-page', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1229,6 +1263,8 @@ describe('when rendering a rekey transaction', () => {
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1257,6 +1293,7 @@ describe('when rendering an app call transaction with ARC-32 app spec loaded', (
it('should be rendered with the correct data', async () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
const myStore = createStore()
myStore.set(genesisHashAtom, 'some-hash')
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
@@ -1301,6 +1338,8 @@ describe('when rendering an app call transaction with ARC-4 app spec loaded', ()
it('should be rendered with the correct data', async () => {
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
+ vi.mocked(useLoadableNfd).mockReturnValue([{ state: 'loading' }])
+
const myStore = createStore()
myStore.set(genesisHashAtom, 'some-hash')
myStore.set(transactionResultsAtom, new Map([[transaction.id, createReadOnlyAtomAndTimestamp(transaction)]]))
diff --git a/src/features/wallet/components/connect-wallet-button.tsx b/src/features/wallet/components/connect-wallet-button.tsx
index f17372ce7..6ff1589ba 100644
--- a/src/features/wallet/components/connect-wallet-button.tsx
+++ b/src/features/wallet/components/connect-wallet-button.tsx
@@ -2,7 +2,7 @@ import { Button } from '@/features/common/components/button'
import { cn } from '@/features/common/utils'
import { Account, PROVIDER_ID, Provider, useWallet } from '@txnlab/use-wallet'
import { Dialog, DialogContent, DialogHeader, SmallSizeDialogBody } from '@/features/common/components/dialog'
-import { ellipseAddress } from '@/utils/ellipse-address'
+import { ellipseAddress, ellipseNfd } from '@/utils/ellipse-address'
import { AccountLink } from '@/features/accounts/components/account-link'
import { Loader2 as Loader, CircleMinus, Wallet } from 'lucide-react'
import { useNetworkConfig } from '@/features/network/data'
@@ -20,6 +20,8 @@ import { walletDialogOpenAtom } from '../data/wallet-dialog'
import { clearAvailableWallets } from '../utils/clear-available-wallets'
import { useDisconnectWallet } from '../hooks/use-disconnect-wallet'
import { CopyButton } from '@/features/common/components/copy-button'
+import { useLoadableNfd } from '@/features/nfd/data/nfd'
+import { RenderLoadable } from '@/features/common/components/render-loadable'
export const connectWalletLabel = 'Connect Wallet'
export const disconnectWalletLabel = 'Disconnect Wallet'
@@ -68,7 +70,7 @@ function ConnectedWallet({ activeAddress, connectedActiveAccounts, providers }:
},
[activeProvider]
)
-
+ const [loadableNfd] = useLoadableNfd(activeAddress)
return (
@@ -84,7 +86,11 @@ function ConnectedWallet({ activeAddress, connectedActiveAccounts, providers }:
/>
))}
- {ellipseAddress(activeAddress)}
+ {loadableNfd.state === 'hasData' && loadableNfd.data !== null ? (
+ {(nfd) => {ellipseNfd(nfd?.name)}}
+ ) : (
+ <>{ellipseAddress(activeAddress)}>
+ )}
diff --git a/src/tests/builders/nfd-result-builder.ts b/src/tests/builders/nfd-result-builder.ts
new file mode 100644
index 000000000..0656114e7
--- /dev/null
+++ b/src/tests/builders/nfd-result-builder.ts
@@ -0,0 +1,18 @@
+import { NfdResult } from '@/features/nfd/data/types'
+import { DataBuilder, dossierProxy, randomString } from '@makerx/ts-dossier'
+
+export class NfdResultBuilder extends DataBuilder {
+ constructor(initialState?: NfdResult) {
+ super(
+ initialState
+ ? initialState
+ : {
+ name: randomString(5, 20),
+ depositAccount: randomString(52, 52),
+ caAlgo: [randomString(52, 52), randomString(52, 52)],
+ }
+ )
+ }
+}
+
+export const nfdResultBuilder = dossierProxy(NfdResultBuilder)
diff --git a/src/tests/object-mother/account-result.ts b/src/tests/object-mother/account-result.ts
index 6e645038b..7c79dddaa 100644
--- a/src/tests/object-mother/account-result.ts
+++ b/src/tests/object-mother/account-result.ts
@@ -279,4 +279,29 @@ export const accountResultMother = {
'total-created-assets': 984393,
})
},
+ ['mainnet-DHMCHBN4W5MBO72C3L3ZP6GGJHQ4OR6SW2EP3VDEJ5VHT4MERQLCTVW6PU']: () => {
+ return new AccountResultBuilder({
+ address: 'DHMCHBN4W5MBO72C3L3ZP6GGJHQ4OR6SW2EP3VDEJ5VHT4MERQLCTVW6PU',
+ amount: 1915706350,
+ 'amount-without-pending-rewards': 1915706350,
+ 'apps-local-state': [],
+ 'apps-total-schema': {
+ 'num-byte-slice': 0,
+ 'num-uint': 0,
+ },
+ assets: [],
+ 'created-apps': [],
+ 'created-assets': [],
+ 'min-balance': 100000,
+ 'pending-rewards': 0,
+ 'reward-base': 218288,
+ rewards: 19424,
+ round: 43483662,
+ status: AccountStatus.Offline,
+ 'total-apps-opted-in': 0,
+ 'total-assets-opted-in': 0,
+ 'total-created-apps': 0,
+ 'total-created-assets': 0,
+ } satisfies AccountResult)
+ },
}
diff --git a/src/tests/object-mother/nfd-result.ts b/src/tests/object-mother/nfd-result.ts
new file mode 100644
index 000000000..17133e13c
--- /dev/null
+++ b/src/tests/object-mother/nfd-result.ts
@@ -0,0 +1,15 @@
+import { NfdResult } from '@/features/nfd/data/types'
+import { NfdResultBuilder, nfdResultBuilder } from '../builders/nfd-result-builder'
+
+export const nfdResultMother = {
+ basic: () => {
+ return nfdResultBuilder()
+ },
+ ['mainnet-datamuseum.algo']: () => {
+ return new NfdResultBuilder({
+ name: 'datamuseum.algo',
+ depositAccount: 'DHMCHBN4W5MBO72C3L3ZP6GGJHQ4OR6SW2EP3VDEJ5VHT4MERQLCTVW6PU',
+ caAlgo: ['DHMCHBN4W5MBO72C3L3ZP6GGJHQ4OR6SW2EP3VDEJ5VHT4MERQLCTVW6PU'],
+ } satisfies NfdResult)
+ },
+}
diff --git a/src/tests/setup/mocks/index.ts b/src/tests/setup/mocks/index.ts
index 7f8370993..d87cd9287 100644
--- a/src/tests/setup/mocks/index.ts
+++ b/src/tests/setup/mocks/index.ts
@@ -83,3 +83,12 @@ vi.mock('@auth0/auth0-react', async () => {
})
window.HTMLElement.prototype.hasPointerCapture = vi.fn()
+
+vi.mock('@/features/nfd/data/nfd', async () => {
+ const original = await vi.importActual('@/features/nfd/data/nfd')
+ return {
+ ...original,
+ useLoadableNfd: vi.fn(),
+ useLoadableNfdResult: vi.fn(),
+ }
+})
diff --git a/src/tests/test-platform-provider.tsx b/src/tests/test-platform-provider.tsx
index f127a2875..c6ba5dd48 100644
--- a/src/tests/test-platform-provider.tsx
+++ b/src/tests/test-platform-provider.tsx
@@ -12,6 +12,7 @@ type Props = PropsWithChildren<{
export function TestPlatformProvider({ children, store }: Props) {
const networkConfig = {
id: localnetId,
+ nfdApiUrl: 'http://not-used',
...defaultNetworkConfigs.localnet,
}
useTheme()
diff --git a/src/utils/ellipse-address.ts b/src/utils/ellipse-address.ts
index b17fffafe..f38d4851d 100644
--- a/src/utils/ellipse-address.ts
+++ b/src/utils/ellipse-address.ts
@@ -1,3 +1,10 @@
export function ellipseAddress(address = '', width = 4): string {
return address ? `${address.slice(0, width)}…${address.slice(-width)}` : address
}
+
+export function ellipseNfd(address = '', width = 5): string {
+ if (address.length <= width * 2) {
+ return address
+ }
+ return address ? `${address.slice(0, width)}…${address.slice(-width)}` : address
+}