Skip to content

Commit 4f8448e

Browse files
authored
feat: search using an id or address (#32)
* feat: search using an id or address
1 parent f1036aa commit 4f8448e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+900
-163
lines changed

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@algorandfoundation/algokit-utils": "^6.0.0-beta.1",
2727
"@radix-ui/react-dialog": "^1.0.5",
2828
"@radix-ui/react-dropdown-menu": "^2.0.6",
29+
"@radix-ui/react-icons": "^1.3.0",
2930
"@radix-ui/react-label": "^2.0.2",
3031
"@radix-ui/react-navigation-menu": "^1.1.4",
3132
"@radix-ui/react-select": "^2.0.0",
@@ -36,6 +37,7 @@
3637
"@tauri-apps/api": "^1.5.3",
3738
"class-variance-authority": "^0.7.0",
3839
"clsx": "^2.1.0",
40+
"cmdk": "^1.0.0",
3941
"date-fns": "^3.5.0",
4042
"decimal.js": "^10.4.3",
4143
"isutf8": "^4.0.0",
@@ -143,4 +145,4 @@
143145
"semantic-release-export-data"
144146
]
145147
}
146-
}
148+
}

src/App.routes.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { GroupPage } from './features/transactions/pages/group-page'
88
import { ErrorPage } from './features/common/pages/error-page'
99
import { BlockPage, blockPageTitle } from './features/blocks/pages/block-page'
1010
import { InnerTransactionPage } from './features/transactions/pages/inner-transaction-page'
11+
import { AccountPage, accountPageTitle } from './features/accounts/pages/account-page'
12+
import { AssetPage, assetPageTitle } from './features/assets/pages/asset-page'
13+
import { ApplicationPage, applicationPageTitle } from './features/applications/pages/application-page'
1114

1215
export const routes = evalTemplates([
1316
{
@@ -59,6 +62,21 @@ export const routes = evalTemplates([
5962
element: <BlockPage />,
6063
errorElement: <ErrorPage title={blockPageTitle} />,
6164
},
65+
{
66+
template: Urls.Explore.Account.ById,
67+
element: <AccountPage />,
68+
errorElement: <ErrorPage title={accountPageTitle} />,
69+
},
70+
{
71+
template: Urls.Explore.Asset.ById,
72+
element: <AssetPage />,
73+
errorElement: <ErrorPage title={assetPageTitle} />,
74+
},
75+
{
76+
template: Urls.Explore.Application.ById,
77+
element: <ApplicationPage />,
78+
errorElement: <ErrorPage title={applicationPageTitle} />,
79+
},
6280
],
6381
},
6482
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { invariant } from '@/utils/invariant'
2+
import { UrlParams } from '../../../routes/urls'
3+
import { useRequiredParam } from '../../common/hooks/use-required-param'
4+
import { cn } from '@/features/common/utils'
5+
import { isAddress } from '@/utils/is-address'
6+
7+
export const accountPageTitle = 'Account'
8+
export const accountInvalidAddressMessage = 'Address is invalid'
9+
10+
export function AccountPage() {
11+
const { address } = useRequiredParam(UrlParams.Address)
12+
invariant(isAddress(address), accountInvalidAddressMessage)
13+
14+
return (
15+
<div>
16+
<h1 className={cn('text-2xl text-primary font-bold')}>{accountPageTitle}</h1>
17+
{address}
18+
</div>
19+
)
20+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { indexer } from '@/features/common/data'
2+
import { JotaiStore } from '@/features/common/data/types'
3+
import { atom, useAtomValue, useStore } from 'jotai'
4+
import { atomEffect } from 'jotai-effect'
5+
import { ApplicationLookupResult } from '@algorandfoundation/algokit-utils/types/indexer'
6+
import { asApplication } from '../mappers'
7+
import { useMemo } from 'react'
8+
import { loadable } from 'jotai/utils'
9+
import { ApplicationId } from './types'
10+
import { applicationResultsAtom } from './core'
11+
12+
const fetchApplicationResultAtomBuilder = (applicationId: ApplicationId) => {
13+
return atom(async (_get) => {
14+
return await indexer
15+
.lookupApplications(applicationId)
16+
.includeAll(true)
17+
.do()
18+
.then((result) => {
19+
return (result as ApplicationLookupResult).application
20+
})
21+
})
22+
}
23+
24+
export const getApplicationAtomBuilder = (store: JotaiStore, applicationId: ApplicationId) => {
25+
const fetchApplicationResultAtom = fetchApplicationResultAtomBuilder(applicationId)
26+
27+
const syncEffect = atomEffect((get, set) => {
28+
;(async () => {
29+
try {
30+
const applicationResult = await get(fetchApplicationResultAtom)
31+
set(applicationResultsAtom, (prev) => {
32+
return prev.set(applicationResult.id, applicationResult)
33+
})
34+
} catch (e) {
35+
// Ignore any errors as there is nothing to sync
36+
}
37+
})()
38+
})
39+
40+
return atom(async (get) => {
41+
const applicationResults = store.get(applicationResultsAtom)
42+
const cachedApplicationResult = applicationResults.get(applicationId)
43+
if (cachedApplicationResult) {
44+
return asApplication(cachedApplicationResult)
45+
}
46+
47+
get(syncEffect)
48+
49+
const applicationResult = await get(fetchApplicationResultAtom)
50+
return asApplication(applicationResult)
51+
})
52+
}
53+
54+
const useApplicationAtom = (applicationId: ApplicationId) => {
55+
const store = useStore()
56+
57+
return useMemo(() => {
58+
return getApplicationAtomBuilder(store, applicationId)
59+
}, [store, applicationId])
60+
}
61+
62+
export const useLoadableApplication = (applicationId: ApplicationId) => {
63+
return useAtomValue(loadable(useApplicationAtom(applicationId)))
64+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ApplicationResult } from '@algorandfoundation/algokit-utils/types/indexer'
2+
import { atom } from 'jotai'
3+
import { ApplicationId } from './types'
4+
5+
export const applicationResultsAtom = atom<Map<ApplicationId, ApplicationResult>>(new Map())
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './application'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type ApplicationId = number
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Application } from '../models'
2+
import { ApplicationResult } from '@algorandfoundation/algokit-utils/types/indexer'
3+
4+
export const asApplication = (application: ApplicationResult): Application => {
5+
return {
6+
id: application.id,
7+
}
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type Application = {
2+
id: number
3+
}

0 commit comments

Comments
 (0)