Skip to content

Commit 9f919b4

Browse files
authored
Cache invalidation (#78)
* feat: store last accessed timestamp alongside atom * chore: fix route url naming * feat: remove data when it's stale and hasn't been recently used * feat: expire live transactions
1 parent 66531cc commit 9f919b4

Some content is hidden

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

42 files changed

+301
-170
lines changed

src/App.routes.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Urls } from './routes/urls'
44
import { evalTemplates } from './routes/templated-route'
55
import { TransactionPage, transactionPageTitle } from './features/transactions/pages/transaction-page'
66
import { ExplorePage, explorePageTitle } from './features/explore/pages/explore-page'
7-
import { GroupPage } from './features/groups/pages/group-page'
7+
import { GroupPage, groupPageTitle } from './features/groups/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'
@@ -55,21 +55,22 @@ export const routes = evalTemplates([
5555
],
5656
},
5757
{
58-
template: Urls.Explore.Block.ById,
59-
errorElement: <ErrorPage title={blockPageTitle} />,
58+
template: Urls.Explore.Block.ByRound,
6059
children: [
6160
{
62-
template: Urls.Explore.Block.ById,
61+
template: Urls.Explore.Block.ByRound,
62+
errorElement: <ErrorPage title={blockPageTitle} />,
6363
element: <BlockPage />,
6464
},
6565
{
66-
template: Urls.Explore.Block.ById.Group.ById,
66+
template: Urls.Explore.Block.ByRound.Group.ById,
67+
errorElement: <ErrorPage title={groupPageTitle} />,
6768
element: <GroupPage />,
6869
},
6970
],
7071
},
7172
{
72-
template: Urls.Explore.Account.ById,
73+
template: Urls.Explore.Account.ByAddress,
7374
element: <AccountPage />,
7475
errorElement: <ErrorPage title={accountPageTitle} />,
7576
},

src/features/accounts/components/account-link.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const AccountLink = fixedForwardRef(
1616
return (
1717
<TemplatedNavLink
1818
className={cn(!children && 'text-primary underline', className)}
19-
urlTemplate={Urls.Explore.Account.ById}
19+
urlTemplate={Urls.Explore.Account.ByAddress}
2020
urlParams={{ address }}
2121
ref={ref}
2222
{...rest}

src/features/accounts/data/account-result.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { atom } from 'jotai'
22
import { AccountResult, Address } from './types'
3-
import { algod } from '@/features/common/data'
3+
import { createAtomAndTimestamp } from '@/features/common/data'
44
import { atomsInAtom } from '@/features/common/data'
55
import { assetResultsAtom } from '@/features/assets/data'
66
import { applicationResultsAtom } from '@/features/applications/data'
7+
import { algod } from '@/features/common/data/algo-client'
78

89
const getAccountResult = (address: Address) =>
910
algod
@@ -24,7 +25,7 @@ const syncAssociatedDataAndReturnAccountResultAtom = atom(null, async (get, set,
2425
const next = new Map(prev)
2526
assetsToAdd.forEach((asset) => {
2627
if (!next.has(asset.index)) {
27-
next.set(asset.index, atom(asset))
28+
next.set(asset.index, createAtomAndTimestamp(asset))
2829
}
2930
})
3031
return next
@@ -36,7 +37,7 @@ const syncAssociatedDataAndReturnAccountResultAtom = atom(null, async (get, set,
3637
const next = new Map(prev)
3738
applicationsToAdd.forEach((application) => {
3839
if (!next.has(application.id)) {
39-
next.set(application.id, atom(application))
40+
next.set(application.id, createAtomAndTimestamp(application))
4041
}
4142
})
4243
return next

src/features/accounts/data/account-transaction-history.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Address } from '../data/types'
2-
import { indexer } from '@/features/common/data'
2+
import { createAtomAndTimestamp } from '@/features/common/data'
33
import { TransactionResult, TransactionSearchResults } from '@algorandfoundation/algokit-utils/types/indexer'
44
import { createTransactionsAtom, transactionResultsAtom } from '@/features/transactions/data'
55
import { atomEffect } from 'jotai-effect'
66
import { atom } from 'jotai'
77
import { createLoadableViewModelPageAtom } from '@/features/common/data/lazy-load-pagination'
88
import { DEFAULT_FETCH_SIZE } from '@/features/common/constants'
9+
import { indexer } from '@/features/common/data/algo-client'
910

1011
const getAccountTransactionResults = async (address: Address, nextPageToken?: string) => {
1112
const results = (await indexer
@@ -28,7 +29,7 @@ const createSyncEffect = (transactionResults: TransactionResult[]) => {
2829
const next = new Map(prev)
2930
transactionResults.forEach((transactionResult) => {
3031
if (!next.has(transactionResult.id)) {
31-
next.set(transactionResult.id, atom(transactionResult))
32+
next.set(transactionResult.id, createAtomAndTimestamp(transactionResult))
3233
}
3334
})
3435
return next

src/features/accounts/pages/account-page.test.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { render, waitFor } from '@/tests/testing-library'
33
import { useParams } from 'react-router-dom'
44
import { describe, expect, it, vi } from 'vitest'
55
import { AccountPage, accountFailedToLoadMessage } from './account-page'
6-
import { algod } from '@/features/common/data'
6+
import { createAtomAndTimestamp } from '@/features/common/data'
77
import { accountResultMother } from '@/tests/object-mother/account-result'
8-
import { atom, createStore } from 'jotai'
8+
import { createStore } from 'jotai'
99
import { descriptionListAssertion } from '@/tests/assertions/description-list-assertion'
1010
import { accountResultsAtom } from '../data'
1111
import {
@@ -24,6 +24,7 @@ import {
2424
import { assetResultsAtom } from '@/features/assets/data'
2525
import { assetResultMother } from '@/tests/object-mother/asset-result'
2626
import { refreshButtonLabel } from '@/features/common/components/refresh-button'
27+
import { algod } from '@/features/common/data/algo-client'
2728

2829
describe('account-page', () => {
2930
describe('when rendering an account using a invalid address', () => {
@@ -58,14 +59,14 @@ describe('account-page', () => {
5859
describe('when rendering an account', () => {
5960
const accountResult = accountResultMother['mainnet-BIQXAK67KSCKN3EJXT4S3RVXUBFOLZ45IQOBTSOQWOSR4LLULBTD54S5IA']().build()
6061
const assetResults = new Map([
61-
[924268058, atom(assetResultMother['mainnet-924268058']().build())],
62-
[1010208883, atom(assetResultMother['mainnet-1010208883']().build())],
63-
[1096015467, atom(assetResultMother['mainnet-1096015467']().build())],
62+
[924268058, createAtomAndTimestamp(assetResultMother['mainnet-924268058']().build())],
63+
[1010208883, createAtomAndTimestamp(assetResultMother['mainnet-1010208883']().build())],
64+
[1096015467, createAtomAndTimestamp(assetResultMother['mainnet-1096015467']().build())],
6465
])
6566

6667
it('should be rendered with the correct data', () => {
6768
const myStore = createStore()
68-
myStore.set(accountResultsAtom, new Map([[accountResult.address, atom(accountResult)]]))
69+
myStore.set(accountResultsAtom, new Map([[accountResult.address, createAtomAndTimestamp(accountResult)]]))
6970
myStore.set(assetResultsAtom, assetResults)
7071

7172
vi.mocked(useParams).mockImplementation(() => ({ address: accountResult.address }))
@@ -100,17 +101,17 @@ describe('account-page', () => {
100101
describe('when rendering an account with assets and applications', () => {
101102
const accountResult = accountResultMother['mainnet-ORANGESCU7XMR2TFXSFTOHCUHNP6OYEPIKZW3JZANTCDHVQYMGQFYFIDDA']().build()
102103
const assetResults = new Map([
103-
[1336655079, atom(accountResult['created-assets']![0])],
104-
[1284444444, atom(assetResultMother['mainnet-1284444444']().build())],
105-
[1162292622, atom(assetResultMother['mainnet-1162292622']().build())],
106-
[1294765516, atom(assetResultMother['mainnet-1294765516']().build())],
107-
[1355858325, atom(assetResultMother['mainnet-1355858325']().build())],
108-
[1355898842, atom(assetResultMother['mainnet-1355898842']().build())],
104+
[1336655079, createAtomAndTimestamp(accountResult['created-assets']![0])],
105+
[1284444444, createAtomAndTimestamp(assetResultMother['mainnet-1284444444']().build())],
106+
[1162292622, createAtomAndTimestamp(assetResultMother['mainnet-1162292622']().build())],
107+
[1294765516, createAtomAndTimestamp(assetResultMother['mainnet-1294765516']().build())],
108+
[1355858325, createAtomAndTimestamp(assetResultMother['mainnet-1355858325']().build())],
109+
[1355898842, createAtomAndTimestamp(assetResultMother['mainnet-1355898842']().build())],
109110
])
110111

111112
it('should be rendered with the correct data', () => {
112113
const myStore = createStore()
113-
myStore.set(accountResultsAtom, new Map([[accountResult.address, atom(accountResult)]]))
114+
myStore.set(accountResultsAtom, new Map([[accountResult.address, createAtomAndTimestamp(accountResult)]]))
114115
myStore.set(assetResultsAtom, assetResults)
115116

116117
vi.mocked(useParams).mockImplementation(() => ({ address: accountResult.address }))
@@ -147,7 +148,7 @@ describe('account-page', () => {
147148

148149
it('should be rendered with the correct data', () => {
149150
const myStore = createStore()
150-
myStore.set(accountResultsAtom, new Map([[accountResult.address, atom(accountResult)]]))
151+
myStore.set(accountResultsAtom, new Map([[accountResult.address, createAtomAndTimestamp(accountResult)]]))
151152

152153
vi.mocked(useParams).mockImplementation(() => ({ address: accountResult.address }))
153154

@@ -182,14 +183,14 @@ describe('account-page', () => {
182183
describe('when rendering an account that becomes stale', () => {
183184
const accountResult = accountResultMother['mainnet-BIQXAK67KSCKN3EJXT4S3RVXUBFOLZ45IQOBTSOQWOSR4LLULBTD54S5IA']().build()
184185
const assetResults = new Map([
185-
[924268058, atom(assetResultMother['mainnet-924268058']().build())],
186-
[1010208883, atom(assetResultMother['mainnet-1010208883']().build())],
187-
[1096015467, atom(assetResultMother['mainnet-1096015467']().build())],
186+
[924268058, createAtomAndTimestamp(assetResultMother['mainnet-924268058']().build())],
187+
[1010208883, createAtomAndTimestamp(assetResultMother['mainnet-1010208883']().build())],
188+
[1096015467, createAtomAndTimestamp(assetResultMother['mainnet-1096015467']().build())],
188189
])
189190

190191
it('should be rendered with the refresh button', () => {
191192
const myStore = createStore()
192-
myStore.set(accountResultsAtom, new Map([[accountResult.address, atom(accountResult)]]))
193+
myStore.set(accountResultsAtom, new Map([[accountResult.address, createAtomAndTimestamp(accountResult)]]))
193194
myStore.set(assetResultsAtom, assetResults)
194195

195196
vi.mocked(useParams).mockImplementation(() => ({ address: accountResult.address }))

src/features/applications/components/application-program.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, expect, it, vi } from 'vitest'
22
import { getByRole, render, waitFor } from '../../../tests/testing-library'
33
import { ApplicationProgram, base64ProgramTabLabel, tealProgramTabLabel } from './application-program'
4-
import { algod } from '@/features/common/data'
54
import { executeComponentTest } from '@/tests/test-component'
5+
import { algod } from '@/features/common/data/algo-client'
66

77
describe('application-program', () => {
88
describe('when rendering an application program', () => {

src/features/applications/data/application-boxes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { ApplicationId } from './types'
2-
import { indexer } from '@/features/common/data'
32
import { useMemo } from 'react'
43
import { atom, useAtomValue } from 'jotai'
54
import { ApplicationBox, ApplicationBoxSummary } from '../models'
65
import { Buffer } from 'buffer'
76
import { loadable } from 'jotai/utils'
87
import { createLoadableViewModelPageAtom } from '@/features/common/data/lazy-load-pagination'
98
import { DEFAULT_FETCH_SIZE } from '@/features/common/constants'
9+
import { indexer } from '@/features/common/data/algo-client'
1010

1111
const getApplicationBoxes = async (applicationId: ApplicationId, nextPageToken?: string) => {
1212
const results = await indexer

src/features/applications/data/application-metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { atomsInAtom } from '@/features/common/data'
22
import { ApplicationMetadataResult, ApplicationResult } from './types'
3-
import { indexer } from '@/features/common/data'
43
import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
54
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
65
import { TransactionType } from 'algosdk'
76
import { base64ToUtf8 } from '@/utils/base64-to-utf8'
87
import { parseArc2 } from '@/features/transactions/mappers/arc-2'
98
import { parseJson } from '@/utils/parse-json'
9+
import { indexer } from '@/features/common/data/algo-client'
1010

1111
const getApplicationMetadataResult = async (applicationResult: ApplicationResult): Promise<ApplicationMetadataResult> => {
1212
// We only need to fetch the first page to find the application creation transaction

src/features/applications/data/application-result.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { algod, indexer } from '@/features/common/data'
21
import { ApplicationId, ApplicationResult } from './types'
32
import { atomsInAtom } from '@/features/common/data'
3+
import { algod, indexer } from '@/features/common/data/algo-client'
44
import { asError, is404 } from '@/utils/error'
55

66
const getApplicationResult = async (applicationId: ApplicationId) => {

src/features/applications/data/application-transaction-history.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { ApplicationId } from './types'
2-
import { indexer } from '@/features/common/data'
2+
import { createAtomAndTimestamp } from '@/features/common/data'
33
import { TransactionResult, TransactionSearchResults } from '@algorandfoundation/algokit-utils/types/indexer'
44
import { createTransactionsAtom, transactionResultsAtom } from '@/features/transactions/data'
55
import { atomEffect } from 'jotai-effect'
66
import { atom } from 'jotai'
77
import { createLoadableViewModelPageAtom } from '@/features/common/data/lazy-load-pagination'
88
import { DEFAULT_FETCH_SIZE } from '@/features/common/constants'
9+
import { indexer } from '@/features/common/data/algo-client'
910

1011
const getApplicationTransactionResults = async (applicationID: ApplicationId, nextPageToken?: string) => {
1112
const results = (await indexer
@@ -28,7 +29,7 @@ const createSyncEffect = (transactionResults: TransactionResult[]) => {
2829
const next = new Map(prev)
2930
transactionResults.forEach((transactionResult) => {
3031
if (!next.has(transactionResult.id)) {
31-
next.set(transactionResult.id, atom(transactionResult))
32+
next.set(transactionResult.id, createAtomAndTimestamp(transactionResult))
3233
}
3334
})
3435
return next

0 commit comments

Comments
 (0)