From 3d56bb574c5924f38bd0abf365a4864e45bcf66d Mon Sep 17 00:00:00 2001 From: Myroslav Sviderok Date: Thu, 17 Oct 2024 11:52:30 +0300 Subject: [PATCH] chore(TransactionFeedV2): Add "No more transactions" toast (#6153) ### Description 7th PR for RET-1207. Add "No more transactions" toast when: - the first page is the only page and there's decent amount of transactions to trigger the scroll - next page returns an empty array This behaviour will be changed in the follow-up PR once connected to the new blockchain-api handler. ### Test plan Adds tests to check the new toast behaviour. ### Related issues - Relates to RET-1207 ### Backwards compatibility Yes ### Network scalability If a new NetworkId and/or Network are added in the future, the changes in this PR will: - [x] Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added) --- .../feed/TransactionFeedV2.test.tsx | 63 ++++++++++++++++++- src/transactions/feed/TransactionFeedV2.tsx | 14 ++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/transactions/feed/TransactionFeedV2.test.tsx b/src/transactions/feed/TransactionFeedV2.test.tsx index 3e2a362ef8..a11abcbbb0 100644 --- a/src/transactions/feed/TransactionFeedV2.test.tsx +++ b/src/transactions/feed/TransactionFeedV2.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, waitFor, within } from '@testing-library/react-native' import { FetchMock } from 'jest-fetch-mock/types' import React from 'react' +import Toast from 'react-native-simple-toast' import { Provider } from 'react-redux' import { ReactTestInstance } from 'react-test-renderer' import { RootState } from 'src/redux/reducers' @@ -21,6 +22,7 @@ import { setupApiStore } from 'src/transactions/apiTestHelpers' import { RecursivePartial } from 'test/utils' jest.mock('src/statsig') +jest.mock('react-native-simple-toast') const STAND_BY_TRANSACTION_SUBTITLE_KEY = 'confirmingTransaction' const mockFetch = fetch as FetchMock @@ -203,7 +205,6 @@ describe('TransactionFeedV2', () => { const { store, ...tree } = renderScreen() await waitFor(() => tree.getByTestId('TransactionList')) - fireEvent(tree.getByTestId('TransactionList'), 'onEndReached') await waitFor(() => expect(mockFetch).toBeCalled()) await waitFor(() => expect(tree.getByTestId('TransactionList/loading')).toBeVisible()) @@ -328,4 +329,64 @@ describe('TransactionFeedV2', () => { expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(1) expect(tree.getByTestId('TransactionList').props.data[1].data.length).toBe(1) }) + + it('should show "no transactions" toast if there is more than MIN_NUM_TRANSACTIONS transactions', async () => { + mockFetch + .mockResponseOnce( + typedResponse({ + transactions: [ + mockTransaction({ transactionHash: '0x01' }), + mockTransaction({ transactionHash: '0x02' }), + mockTransaction({ transactionHash: '0x03' }), + mockTransaction({ transactionHash: '0x04' }), + mockTransaction({ transactionHash: '0x05' }), + mockTransaction({ transactionHash: '0x06' }), + mockTransaction({ transactionHash: '0x07' }), + mockTransaction({ transactionHash: '0x08' }), + mockTransaction({ transactionHash: '0x09' }), + mockTransaction({ transactionHash: '0x10' }), + mockTransaction({ transactionHash: '0x11' }), + ], + }) + ) + .mockResponseOnce(typedResponse({ transactions: [] })) + + const tree = renderScreen() + + await waitFor(() => tree.getByTestId('TransactionList')) + fireEvent(tree.getByTestId('TransactionList'), 'onEndReached') + await waitFor(() => expect(mockFetch).toBeCalled()) + await waitFor(() => expect(tree.getByTestId('TransactionList/loading')).toBeVisible()) + await waitFor(() => expect(tree.queryByTestId('TransactionList/loading')).toBeFalsy()) + + fireEvent(tree.getByTestId('TransactionList'), 'onEndReached') + await waitFor(() => expect(Toast.showWithGravity).toBeCalledTimes(1)) + }) + + it('should not show "no transactions" toast if there is not enough transactions to trigger the toast', async () => { + mockFetch.mockResponseOnce( + typedResponse({ + transactions: [ + mockTransaction({ transactionHash: '0x01', timestamp: 50 }), + mockTransaction({ transactionHash: '0x02', timestamp: 49 }), + mockTransaction({ transactionHash: '0x03', timestamp: 48 }), + mockTransaction({ transactionHash: '0x04', timestamp: 47 }), + mockTransaction({ transactionHash: '0x05', timestamp: 46 }), + mockTransaction({ transactionHash: '0x06', timestamp: 45 }), + mockTransaction({ transactionHash: '0x07', timestamp: 44 }), + mockTransaction({ transactionHash: '0x08', timestamp: 43 }), + mockTransaction({ transactionHash: '0x09', timestamp: 42 }), + ], + }) + ) + + const tree = renderScreen() + + await waitFor(() => tree.getByTestId('TransactionList')) + fireEvent(tree.getByTestId('TransactionList'), 'onEndReached') + await waitFor(() => expect(mockFetch).toBeCalled()) + await waitFor(() => expect(tree.getByTestId('TransactionList/loading')).toBeVisible()) + await waitFor(() => expect(tree.queryByTestId('TransactionList/loading')).toBeFalsy()) + await waitFor(() => expect(Toast.showWithGravity).not.toBeCalled()) + }) }) diff --git a/src/transactions/feed/TransactionFeedV2.tsx b/src/transactions/feed/TransactionFeedV2.tsx index 9838bd5f8f..e73de809a0 100644 --- a/src/transactions/feed/TransactionFeedV2.tsx +++ b/src/transactions/feed/TransactionFeedV2.tsx @@ -1,5 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { ActivityIndicator, SectionList, StyleSheet, View } from 'react-native' +import Toast from 'react-native-simple-toast' import { showError } from 'src/alert/actions' import { ErrorMessages } from 'src/app/ErrorMessages' import SectionHead from 'src/components/SectionHead' @@ -40,9 +42,9 @@ type PaginatedData = { [timestamp: number]: TokenTransaction[] } -// Query poll interval +const MIN_NUM_TRANSACTIONS_NECESSARY_FOR_SCROLL = 10 const POLL_INTERVAL_MS = 10000 // 10 sec -const FIRST_PAGE_TIMESTAMP = 0 +const FIRST_PAGE_TIMESTAMP = 0 // placeholder const TAG = 'transactions/feed/TransactionFeedV2' function getAllowedNetworksForTransfers() { @@ -208,6 +210,7 @@ function renderItem({ item: tx }: { item: TokenTransaction }) { } export default function TransactionFeedV2() { + const { t } = useTranslation() const dispatch = useDispatch() const address = useSelector(walletAddressSelector) const standByTransactions = useStandByTransactions() @@ -345,9 +348,16 @@ export default function TransactionFeedV2() { ) } + // This logic will change once the real api is connected function fetchMoreTransactions() { if (nextCursor) { setEndCursor(nextCursor) + return + } + + const totalTxCount = standByTransactions.pending.length + confirmedTransactions.length + if (totalTxCount > MIN_NUM_TRANSACTIONS_NECESSARY_FOR_SCROLL) { + Toast.showWithGravity(t('noMoreTransactions'), Toast.SHORT, Toast.CENTER) } }