Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/frontend/src/eth/services/eth-transactions.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export const reloadEthereumTransactions = (params: {
silent?: boolean;
}): Promise<ResultSuccess> => loadEthereumTransactions({ ...params, updateOnly: true });

const hasStoredEthTransactions = (tokenId: TokenId): boolean =>
(get(ethTransactionsStore)?.[tokenId] ?? []).length > 0;

const maxEthNativeBlockNumberInStore = (tokenId: TokenId): number | undefined => {
const rows = get(ethTransactionsStore)?.[tokenId];

Expand Down Expand Up @@ -183,8 +186,10 @@ const loadEthTransactions = async ({
? await loadEthUserTransactions({ identity, tokenId: transactionTokenId })
: undefined;

// Left alone on a reload: the timer must not rewind pages the user has already scrolled past.
if (!updateOnly) {
// Only while the list is being built from scratch. The periodic refresh comes through here too,
// so resetting the cursor unconditionally would send the next scroll back over pages the user
// already has.
if (!hasStoredEthTransactions(tokenId)) {
setEthBackendPaginationCursor({ tokenId, nextStart: stored?.nextStart });
}

Expand Down Expand Up @@ -215,7 +220,10 @@ const loadEthTransactions = async ({
ethTransactionsStore.update({ tokenId, transaction })
);
} else {
ethTransactionsStore.set({ tokenId, transactions: certifiedTransactions });
// Prepended rather than set, because this batch is not the whole history: it is the newest
// stored page plus whatever is newer than it. Replacing the slot would throw away every older
// page the user scrolled in - and the periodic refresh runs through here every 30 seconds.
ethTransactionsStore.prepend({ tokenId, transactions: certifiedTransactions });
}

// Save newly finalized transactions to backend (fire-and-forget).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,14 @@ describe('eth-transactions.services', () => {
});
});

it('should leave the backend cursor alone on a reload', async () => {
it('should leave the backend cursor alone once the list has been built', async () => {
// The periodic refresh runs the same path, so it must not send the next scroll back over
// pages the user already has.
ethTransactionsStore.set({
tokenId: mockTokenId,
transactions: createMockEthTransactions(3).map((data) => ({ data, certified: false }))
});

vi.mocked(loadEthUserTransactions).mockResolvedValue({
transactions: createMockEthTransactions(2),
newestBlockIndex: 100n,
Expand All @@ -574,13 +581,53 @@ describe('eth-transactions.services', () => {
networkId: mockNetworkId,
tokenId: mockTokenId,
chainId: mockChainId,
standard: mockStandard,
updateOnly: true
standard: mockStandard
});

expect(setEthBackendPaginationCursor).not.toHaveBeenCalled();
});

it('should keep pages already scrolled in when it refreshes', async () => {
const pagedIn = createMockEthTransactions(4);

ethTransactionsStore.set({
tokenId: mockTokenId,
transactions: pagedIn.map((data) => ({ data, certified: false }))
});

// A refresh only ever returns the newest stored page plus anything newer than it.
const newestPage = createMockEthTransactions(2);

Comment thread
sbpublic marked this conversation as resolved.
vi.mocked(loadEthUserTransactions).mockResolvedValue({
transactions: newestPage,
newestBlockIndex: 100n,
oldestBlockIndex: 50n,
nextStart: 60n,
totalStored: 30n
});

infuraMocks.mockInfuraGetBlockNumber.mockResolvedValueOnce(150);
mockEthTransactionsProvider.mockResolvedValueOnce([]);

await loadEthereumTransactions({
identity: mockIdentity,
networkId: mockNetworkId,
tokenId: mockTokenId,
chainId: mockChainId,
standard: mockStandard
});

const rows = get(ethTransactionsStore)?.[mockTokenId];

assertNonNullish(rows);

expect(rows).toHaveLength(pagedIn.length + newestPage.length);

expect(rows.map(({ data: { hash } }) => hash)).toEqual(
expect.arrayContaining(pagedIn.map(({ hash }) => hash))
);
});

it('should use update method when updateOnly is true', async () => {
vi.mocked(loadEthUserTransactions).mockResolvedValue(undefined);

Expand Down
Loading