Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
"react-native-localize": "^3.5.4",
"react-native-nitro-modules": "0.29.4",
"react-native-nitro-sqlite": "9.2.0",
"react-native-onyx": "3.0.45",
"react-native-onyx": "3.0.46",
"react-native-pager-view": "8.0.0",
"react-native-pdf": "7.0.2",
"react-native-permissions": "^5.4.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useFocusEffect} from '@react-navigation/native';
import {useIsFocused} from '@react-navigation/native';
import {hasSeenTourSelector} from '@selectors/Onboarding';
import {FlashList} from '@shopify/flash-list';
import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list';
Expand Down Expand Up @@ -551,31 +551,40 @@ function MoneyRequestReportPreviewContent({
carouselTransactionsRef.current = carouselTransactions;
}, [carouselTransactions]);

useFocusEffect(
useCallback(() => {
const index = carouselTransactions.findIndex((transaction) => newTransactionIDs?.has(transaction.transactionID));
const isFocused = useIsFocused();
const isFocusedRef = useRef(isFocused);

if (index < 0) {
useEffect(() => {
isFocusedRef.current = isFocused;
}, [isFocused]);

useEffect(() => {
const index = carouselTransactions.findIndex((transaction) => newTransactionIDs?.has(transaction.transactionID));

if (index < 0) {
return;
}
const newTransaction = carouselTransactions.at(index);
setTimeout(() => {
if (!isFocusedRef.current) {
return;
}
// If the new transaction is not available at the index it was on before the delay, avoid the scrolling
// because we are scrolling to either a wrong or unavailable transaction (which can cause crash).
if (newTransaction?.transactionID !== carouselTransactionsRef.current.at(index)?.transactionID) {
return;
}
const newTransaction = carouselTransactions.at(index);
setTimeout(() => {
// If the new transaction is not available at the index it was on before the delay, avoid the scrolling
// because we are scrolling to either a wrong or unavailable transaction (which can cause crash).
if (newTransaction?.transactionID !== carouselTransactionsRef.current.at(index)?.transactionID) {
return;
}

carouselRef.current?.scrollToIndex({
index,
viewOffset: -2 * styles.gap2.gap,
animated: true,
});
}, CONST.ANIMATED_TRANSITION);
carouselRef.current?.scrollToIndex({
index,
viewOffset: -2 * styles.gap2.gap,
animated: true,
});
}, CONST.ANIMATED_TRANSITION);

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [newTransactionIDs]),
);
// We only want to scroll to a new transaction when the set of new transaction IDs changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [newTransactionIDs]);

const onViewableItemsChanged = useRef(({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => {
const newIndex = viewableItems.at(0)?.index;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {useIsFocused} from '@react-navigation/native';
import type {ListRenderItem} from '@shopify/flash-list';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import type {LayoutChangeEvent} from 'react-native';
Expand Down Expand Up @@ -122,9 +121,8 @@ function MoneyRequestReportPreview({
selector: hasOnceLoadedReportActionsSelector,
});
const newTransactions = useNewTransactions(hasOnceLoadedReportActions, transactions);
const isFocused = useIsFocused();
// We only want to highlight the new expenses if the screen is focused.
const newTransactionIDs = isFocused ? new Set(newTransactions.map((transaction) => transaction.transactionID)) : undefined;
const newTransactionIDs = new Set(newTransactions.map((transaction) => transaction.transactionID));

const transactionPreviewContainerStyles = [styles.h100, reportPreviewStyles.transactionPreviewCarouselStyle];

Expand Down
9 changes: 6 additions & 3 deletions src/pages/workspace/withPolicy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {ComponentType} from 'react';
import React from 'react';
import React, {useEffect} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import useOnyx from '@hooks/useOnyx';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
Expand Down Expand Up @@ -90,9 +90,12 @@ export default function <TProps extends WithPolicyProps>(WrappedComponent: Compo
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */
const isLoadingPolicy = !hasLoadedApp || (!!policyID && isLoadingOnyxValue(policyResults, policyDraftResults));

if (policyID && policyID.length > 0) {
useEffect(() => {
if (!policyID) {
return;
}
updateLastAccessedWorkspace(policyID);
}
}, [policyID]);

return (
<WrappedComponent
Expand Down
8 changes: 8 additions & 0 deletions tests/actions/ReportTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,8 @@ describe('actions/Report', () => {
currentUserAccountID: TEST_USER_ACCOUNT_ID,
});

await waitForBatchedUpdates();

// Need the reportActionID to delete the comments
const newComment = PersistedRequests.getAll().at(1);
const reportActionID = newComment?.data?.reportActionID as string | undefined;
Expand Down Expand Up @@ -1984,6 +1986,9 @@ describe('actions/Report', () => {
const newComment = PersistedRequests.getAll().at(0);
const reportActionID = newComment?.data?.reportActionID as string | undefined;
const reportAction = TestHelper.buildTestReportComment(created, TEST_USER_ACCOUNT_ID, reportActionID);

await waitForBatchedUpdates();

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

// wait for Onyx.connect execute the callback and start processing the queue
Expand Down Expand Up @@ -2177,6 +2182,7 @@ describe('actions/Report', () => {
expect(requests?.at(0)?.data?.reportComment).toBe('value3');

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});
await waitForBatchedUpdates();

TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.UPDATE_COMMENT, 1);
});
Expand Down Expand Up @@ -2275,6 +2281,8 @@ describe('actions/Report', () => {
expect(requests?.at(0)?.data?.reportComment).toBe('value3');

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});
await waitForBatchedUpdates();

TestHelper.expectAPICommandToHaveBeenCalled(WRITE_COMMANDS.UPDATE_COMMENT, 1);
});

Expand Down
4 changes: 4 additions & 0 deletions tests/actions/SessionTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ describe('Session', () => {

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

await waitForBatchedUpdates();

expect(getAllPersistedRequests().length).toBe(0);
});

Expand Down Expand Up @@ -226,6 +228,8 @@ describe('Session', () => {

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

await waitForBatchedUpdates();

expect(getAllPersistedRequests().length).toBe(0);
});

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/APITest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ describe('APITests', () => {
});

Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});
expect(NetworkStore.isOffline()).toBe(false);
expect(NetworkStore.isOffline()).toBe(true);
expect(NetworkStore.isAuthenticating()).toBe(false);
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -551,7 +551,7 @@ describe('APITests', () => {
API.write('MockCommandThree' as WriteCommand, {});

// THEN the retryable requests should immediately be added to the persisted requests
expect(PersistedRequests.getAll().length).toBe(2);
expect(PersistedRequests.getLength()).toBe(2);

// WHEN we wait for the queue to run and finish processing
return waitForBatchedUpdates();
Expand Down
25 changes: 12 additions & 13 deletions tests/unit/OptionsListUtilsTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3526,7 +3526,7 @@ describe('OptionsListUtils', () => {
expect(canCreate).toBe(false);
});

it('createOptionList() localization', () => {
it('createOptionList() localization', async () => {
renderLocaleContextProvider();
// Given a set of reports and personal details
// When we call createOptionList and extract the reports
Expand All @@ -3535,18 +3535,15 @@ describe('OptionsListUtils', () => {
// Then the returned reports should match the expected values
expect(reports.at(10)?.subtitle).toBe(`Submits to Mister Fantastic`);

return (
waitForBatchedUpdates()
// When we set the preferred locale to Spanish
.then(() => Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES))
.then(() => {
// When we call createOptionList again
const newReports = createOptionList(PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS).reports;
// Then the returned reports should change to Spanish
// cspell:disable-next-line
expect(newReports.at(10)?.subtitle).toBe('Se envía a Mister Fantastic');
})
);
await Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.ES);

await waitForBatchedUpdates();

// When we call createOptionList again
const newReports = createOptionList(PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, EMPTY_PRIVATE_IS_ARCHIVED_MAP, REPORTS).reports;
// Then the returned reports should change to Spanish
// cspell:disable-next-line
expect(newReports.at(10)?.subtitle).toBe('Se envía a Mister Fantastic');
});
});

Expand Down Expand Up @@ -3620,6 +3617,8 @@ describe('OptionsListUtils', () => {
'1': getFakeAdvancedReportAction(CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT),
},
});
await waitForBatchedUpdates();

// When we call createOptionList with report 10 marked as archived
const archivedMap: PrivateIsArchivedMap = {
[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}10`]: reportNameValuePairs.private_isArchived,
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/ReportSecondaryActionUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ describe('getSecondaryAction', () => {
beforeAll(() => {
Onyx.init({
keys: ONYXKEYS,
initialKeyStates: {
[ONYXKEYS.SESSION]: SESSION,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: {[EMPLOYEE_ACCOUNT_ID]: PERSONAL_DETAILS, [APPROVER_ACCOUNT_ID]: {accountID: APPROVER_ACCOUNT_ID, login: APPROVER_EMAIL}},
},
});
});

beforeEach(async () => {
jest.clearAllMocks();
Onyx.clear();
await Onyx.merge(ONYXKEYS.SESSION, SESSION);
await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {[EMPLOYEE_ACCOUNT_ID]: PERSONAL_DETAILS, [APPROVER_ACCOUNT_ID]: {accountID: APPROVER_ACCOUNT_ID, login: APPROVER_EMAIL}});
});

it('should always return default options', () => {
Expand Down
21 changes: 14 additions & 7 deletions tests/unit/SequentialQueueTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ describe('SequentialQueue', () => {
};
SequentialQueue.push(requestWithConflictResolution);
expect(getLength()).toBe(1);
// We know there is only one request in the queue, so we can get the first one and verify
// that the persisted request is the second one.
const persistedRequest = getAll().at(0);
expect(persistedRequest?.data?.accountID).toBe(56789);
// We know there is only one request and it's ongoing.
// We can get it and verify that the ongoing request is the second one.
const ongoingRequest = getOngoingRequest();
expect(ongoingRequest?.data?.accountID).toBe(56789);
});

it('should push two requests with conflict resolution and push', () => {
Expand Down Expand Up @@ -109,7 +109,9 @@ describe('SequentialQueue', () => {
};

SequentialQueue.push(requestWithConflictResolution);
expect(getLength()).toBe(2);

const ongoingRequest = getOngoingRequest();
expect(ongoingRequest?.data?.accountID).toBe(56789);
});

it('should replace request request in queue while a similar one is ongoing', async () => {
Expand Down Expand Up @@ -175,9 +177,14 @@ describe('SequentialQueue', () => {

expect(getLength()).toBe(4);
const persistedRequests = getAll();
// We know ReconnectApp is at index 1 in the queue, so we can get it to verify
const ongoingRequest = getOngoingRequest();

// The first OpenReport call is ongoing
expect(ongoingRequest?.command).toBe('OpenReport');

// We know ReconnectApp is at index 0 in the queue now, so we can get it to verify
// that was replaced by the new request.
expect(persistedRequests.at(1)?.data?.accountID).toBe(56789);
expect(persistedRequests.at(0)?.data?.accountID).toBe(56789);
});

// need to test a rance condition between processing the next request and then pushing a new request with conflict resolver
Expand Down
Loading