diff --git a/Mobile-Expensify b/Mobile-Expensify
index d2def548c474..adaf7330ca03 160000
--- a/Mobile-Expensify
+++ b/Mobile-Expensify
@@ -1 +1 @@
-Subproject commit d2def548c474b3e4e2b5ac46f54b1607c735eef1
+Subproject commit adaf7330ca036168a42ba07f04d9ebeb560c7b40
diff --git a/android/app/build.gradle b/android/app/build.gradle
index dbd3919fc9a0..5652168ef4f5 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -111,8 +111,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009032701
- versionName "9.3.27-1"
+ versionCode 1009032702
+ versionName "9.3.27-2"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index f365ffc872f6..4fbe195f9072 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -44,7 +44,7 @@
CFBundleVersion
- 9.3.27.1
+ 9.3.27.2
FullStory
OrgId
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 5e423722d598..a418179d0723 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.3.27
CFBundleVersion
- 9.3.27.1
+ 9.3.27.2
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist
index b2378c8f3474..da017c09829d 100644
--- a/ios/ShareViewController/Info.plist
+++ b/ios/ShareViewController/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.3.27
CFBundleVersion
- 9.3.27.1
+ 9.3.27.2
NSExtension
NSExtensionAttributes
diff --git a/package-lock.json b/package-lock.json
index c0107c7422e9..d37383113a79 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.3.27-1",
+ "version": "9.3.27-2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.3.27-1",
+ "version": "9.3.27-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index c10d466f03dd..fbc33299cf72 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.3.27-1",
+ "version": "9.3.27-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/hooks/useLoadReportActions.ts b/src/hooks/useLoadReportActions.ts
index 6eb00bcec4af..14755d8655c2 100644
--- a/src/hooks/useLoadReportActions.ts
+++ b/src/hooks/useLoadReportActions.ts
@@ -1,5 +1,5 @@
import {useIsFocused} from '@react-navigation/native';
-import {useCallback, useMemo} from 'react';
+import {useCallback, useMemo, useRef} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {getNewerActions, getOlderActions} from '@userActions/Report';
import CONST from '@src/CONST';
@@ -11,6 +11,9 @@ type UseLoadReportActionsArguments = {
/** The id of the current report */
reportID: string;
+ /** The id of the reportAction (if specific action was linked to */
+ reportActionID?: string;
+
/** The list of reportActions linked to the current report */
reportActions: ReportAction[];
@@ -31,14 +34,16 @@ type UseLoadReportActionsArguments = {
* Provides reusable logic to get the functions for loading older/newer reportActions.
* Used in the report displaying components
*/
-function useLoadReportActions({reportID, reportActions, allReportActionIDs, transactionThreadReport, hasOlderActions, hasNewerActions}: UseLoadReportActionsArguments) {
+function useLoadReportActions({reportID, reportActionID, reportActions, allReportActionIDs, transactionThreadReport, hasOlderActions, hasNewerActions}: UseLoadReportActionsArguments) {
+ const didLoadOlderChats = useRef(false);
+ const didLoadNewerChats = useRef(false);
+
const {isOffline} = useNetwork();
const isFocused = useIsFocused();
+
const newestReportAction = useMemo(() => reportActions?.at(0), [reportActions]);
const oldestReportAction = useMemo(() => reportActions?.at(-1), [reportActions]);
- const isTransactionThreadReport = !isEmptyObject(transactionThreadReport);
-
// Track oldest/newest actions per report in a single pass
const {currentReportOldest, currentReportNewest, transactionThreadOldest, transactionThreadNewest} = useMemo(() => {
let currentReportNewestAction = null;
@@ -61,7 +66,7 @@ function useLoadReportActions({reportID, reportActions, allReportActionIDs, tran
}
// Oldest = last matching action we encounter
currentReportOldestAction = action;
- } else if (isTransactionThreadReport && transactionThreadReport?.reportID === targetReportID) {
+ } else if (!isEmptyObject(transactionThreadReport) && transactionThreadReport?.reportID === targetReportID) {
// Same logic for transaction thread
if (!transactionThreadNewestAction) {
transactionThreadNewestAction = action;
@@ -76,7 +81,7 @@ function useLoadReportActions({reportID, reportActions, allReportActionIDs, tran
transactionThreadOldest: transactionThreadOldestAction,
transactionThreadNewest: transactionThreadNewestAction,
};
- }, [allReportActionIDs, isTransactionThreadReport, reportActions, reportID, transactionThreadReport?.reportID]);
+ }, [reportActions, allReportActionIDs, reportID, transactionThreadReport]);
/**
* Retrieves the next set of reportActions for the chat once we are nearing the end of what we are currently
@@ -94,40 +99,37 @@ function useLoadReportActions({reportID, reportActions, allReportActionIDs, tran
return;
}
- if (isTransactionThreadReport) {
+ didLoadOlderChats.current = true;
+
+ if (!isEmptyObject(transactionThreadReport)) {
getOlderActions(reportID, currentReportOldest?.reportActionID);
- getOlderActions(transactionThreadReport?.reportID, transactionThreadOldest?.reportActionID);
+ getOlderActions(transactionThreadReport.reportID, transactionThreadOldest?.reportActionID);
} else {
getOlderActions(reportID, currentReportOldest?.reportActionID);
}
},
- [
- currentReportOldest?.reportActionID,
- hasOlderActions,
- isOffline,
- isTransactionThreadReport,
- oldestReportAction,
- reportID,
- transactionThreadOldest?.reportActionID,
- transactionThreadReport?.reportID,
- ],
+ [isOffline, oldestReportAction, hasOlderActions, transactionThreadReport, reportID, currentReportOldest?.reportActionID, transactionThreadOldest?.reportActionID],
);
const loadNewerChats = useCallback(
(force = false) => {
if (
!force &&
- (!isFocused ||
+ (!reportActionID ||
+ !isFocused ||
!newestReportAction ||
!hasNewerActions ||
isOffline ||
// If there was an error only try again once on initial mount. We should also still load
// more in case we have cached messages.
+ didLoadNewerChats.current ||
newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
) {
return;
}
+ didLoadNewerChats.current = true;
+
if (!isEmptyObject(transactionThreadReport)) {
getNewerActions(reportID, currentReportNewest?.reportActionID);
getNewerActions(transactionThreadReport.reportID, transactionThreadNewest?.reportActionID);
@@ -135,7 +137,17 @@ function useLoadReportActions({reportID, reportActions, allReportActionIDs, tran
getNewerActions(reportID, newestReportAction.reportActionID);
}
},
- [currentReportNewest?.reportActionID, hasNewerActions, isFocused, isOffline, newestReportAction, reportID, transactionThreadNewest?.reportActionID, transactionThreadReport],
+ [
+ reportActionID,
+ isFocused,
+ newestReportAction,
+ hasNewerActions,
+ isOffline,
+ transactionThreadReport,
+ reportID,
+ currentReportNewest?.reportActionID,
+ transactionThreadNewest?.reportActionID,
+ ],
);
return {
diff --git a/src/libs/API/index.ts b/src/libs/API/index.ts
index 50c959552f08..e1b9419a0f62 100644
--- a/src/libs/API/index.ts
+++ b/src/libs/API/index.ts
@@ -311,11 +311,13 @@ function paginate processRequest(request, type));
+ waitForWrites(command as ReadCommand).then(() => processRequest(request, type));
+ return;
default:
throw new Error('Unknown API request type');
}
diff --git a/src/libs/API/parameters/OpenReportParams.ts b/src/libs/API/parameters/OpenReportParams.ts
index 7c3ab73bab06..aa58f4c9f677 100644
--- a/src/libs/API/parameters/OpenReportParams.ts
+++ b/src/libs/API/parameters/OpenReportParams.ts
@@ -26,7 +26,6 @@ type OpenReportParams = {
*/
moneyRequestPreviewReportActionID?: string;
includePartiallySetupBankAccounts?: boolean;
- useLastUnreadReportAction?: boolean;
};
export default OpenReportParams;
diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts
index b5828dac4da8..65c295ba9147 100644
--- a/src/libs/actions/Report/index.ts
+++ b/src/libs/actions/Report/index.ts
@@ -1287,7 +1287,6 @@ function openReport(
parentReportActionID,
transactionID: transaction?.transactionID,
includePartiallySetupBankAccounts: true,
- useLastUnreadReportAction: true,
};
if (optimisticSelfDMReport) {
@@ -2028,7 +2027,7 @@ function explain(
*/
function getOlderActions(reportID: string | undefined, reportActionID: string | undefined) {
if (!reportID || !reportActionID) {
- return Promise.resolve();
+ return;
}
const optimisticData: Array> = [
@@ -2068,7 +2067,7 @@ function getOlderActions(reportID: string | undefined, reportActionID: string |
reportActionID,
};
- return API.paginate(
+ API.paginate(
CONST.API_REQUEST_TYPE.READ,
READ_COMMANDS.GET_OLDER_ACTIONS,
parameters,
@@ -2126,7 +2125,7 @@ function getNewerActions(reportID: string | undefined, reportActionID: string |
reportActionID,
};
- return API.paginate(
+ API.paginate(
CONST.API_REQUEST_TYPE.READ,
READ_COMMANDS.GET_NEWER_ACTIONS,
parameters,
diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx
index cbf9ff2875d4..77ebc4e914f1 100644
--- a/src/pages/inbox/report/ReportActionsList.tsx
+++ b/src/pages/inbox/report/ReportActionsList.tsx
@@ -11,6 +11,7 @@ import InvertedFlatList from '@components/InvertedFlatList';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
+import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@hooks/useFlatListScrollKey';
import useIsAnonymousUser from '@hooks/useIsAnonymousUser';
import useLocalize from '@hooks/useLocalize';
import useNetworkWithOfflineStatus from '@hooks/useNetworkWithOfflineStatus';
@@ -344,10 +345,13 @@ function ReportActionsList({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lastAction?.created]);
+ const lastActionIndex = lastAction?.reportActionID;
+ const reportActionSize = useRef(sortedVisibleReportActions.length);
const lastVisibleActionCreated = getReportLastVisibleActionCreated(report, transactionThreadReport);
const hasNewestReportAction = lastAction?.created === lastVisibleActionCreated || isReportPreviewAction(lastAction);
const hasNewestReportActionRef = useRef(hasNewestReportAction);
hasNewestReportActionRef.current = hasNewestReportAction;
+ const previousLastIndex = useRef(lastActionIndex);
const sortedVisibleReportActionsRef = useRef(sortedVisibleReportActions);
const {isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible, trackVerticalScrolling, onViewableItemsChanged} = useReportUnreadMessageScrollTracking({
@@ -366,6 +370,20 @@ function ReportActionsList({
hasOnceLoadedReportActions: !!reportMetadata?.hasOnceLoadedReportActions,
});
+ useEffect(() => {
+ if (
+ scrollOffsetRef.current < AUTOSCROLL_TO_TOP_THRESHOLD &&
+ previousLastIndex.current !== lastActionIndex &&
+ reportActionSize.current !== sortedVisibleReportActions.length &&
+ hasNewestReportAction
+ ) {
+ setIsFloatingMessageCounterVisible(false);
+ reportScrollManager.scrollToBottom();
+ }
+ previousLastIndex.current = lastActionIndex;
+ reportActionSize.current = sortedVisibleReportActions.length;
+ }, [lastActionIndex, sortedVisibleReportActions.length, reportScrollManager, hasNewestReportAction, linkedReportActionID, setIsFloatingMessageCounterVisible, scrollOffsetRef]);
+
useEffect(() => {
const shouldTriggerScroll = shouldFocusToTopOnMount && prevHasCreatedActionAdded && !hasCreatedActionAdded;
if (!shouldTriggerScroll) {
@@ -526,13 +544,9 @@ function ReportActionsList({
if (action?.reportActionID) {
setActionIdToHighlight(action.reportActionID);
}
- } else if (Navigation.getReportRHPActiveRoute()) {
+ } else {
setIsFloatingMessageCounterVisible(false);
reportScrollManager.scrollToBottom();
- } else {
- Navigation.setNavigationActionToMicrotaskQueue(() => {
- Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID));
- });
}
setIsScrollToBottomEnabled(true);
diff --git a/src/pages/inbox/report/ReportActionsView.tsx b/src/pages/inbox/report/ReportActionsView.tsx
index da9bb5d5f1ff..8202baf851d8 100755
--- a/src/pages/inbox/report/ReportActionsView.tsx
+++ b/src/pages/inbox/report/ReportActionsView.tsx
@@ -265,6 +265,7 @@ function ReportActionsView({
const {loadOlderChats, loadNewerChats} = useLoadReportActions({
reportID,
+ reportActionID,
reportActions,
allReportActionIDs,
transactionThreadReport,
diff --git a/src/styles/utils/chatContentScrollViewPlatformStyles/index.ts b/src/styles/utils/chatContentScrollViewPlatformStyles/index.ts
index 6ef204b0acd7..cef1d5e70e6e 100644
--- a/src/styles/utils/chatContentScrollViewPlatformStyles/index.ts
+++ b/src/styles/utils/chatContentScrollViewPlatformStyles/index.ts
@@ -1,7 +1,7 @@
import type ChatContentScrollViewPlatformStyles from './types';
const chatContentScrollViewPlatformStyles: ChatContentScrollViewPlatformStyles = {
- overflow: 'clip',
+ overflow: 'hidden',
};
export default chatContentScrollViewPlatformStyles;
diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx
index 07474755fdc1..d2bebf6fca7a 100644
--- a/tests/ui/PaginationTest.tsx
+++ b/tests/ui/PaginationTest.tsx
@@ -376,9 +376,6 @@ describe('Pagination', () => {
// Here we have 5 messages from the initial OpenReport and 5 from the initial GetNewerActions.
expect(getReportActions()).toHaveLength(10);
- // Simulate the backend returning no new messages to simulate reaching the start of the chat.
- mockGetNewerActions(0);
-
// There is 1 extra call here because of the comment linking report.
TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 3);
TestHelper.expectAPICommandToHaveBeenCalledWith('OpenReport', 1, {reportID: REPORT_ID, reportActionID: '5'});
@@ -393,20 +390,22 @@ describe('Pagination', () => {
TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 3);
TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
- TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 2);
+ TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 1);
// We now have 10 messages. 5 from the initial OpenReport and 5 from the GetNewerActions call.
expect(getReportActions()).toHaveLength(10);
+ // Simulate the backend returning no new messages to simulate reaching the start of the chat.
+ mockGetNewerActions(0);
+
scrollToOffset(500);
await waitForBatchedUpdatesWithAct();
scrollToOffset(0);
await waitForBatchedUpdatesWithAct();
- // When there are no newer actions, we don't want to trigger GetNewerActions again.
TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 3);
TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
- TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 2);
+ TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 1);
// We still have 15 messages. 5 from the initial OpenReport and 5 from the GetNewerActions call.
expect(getReportActions()).toHaveLength(10);
diff --git a/tests/unit/useLoadReportActionsTest.tsx b/tests/unit/useLoadReportActionsTest.tsx
index 871d57c808a1..2f743e78d0cd 100644
--- a/tests/unit/useLoadReportActionsTest.tsx
+++ b/tests/unit/useLoadReportActionsTest.tsx
@@ -124,6 +124,7 @@ describe('useLoadReportActions', () => {
const props = {
...baseProps,
hasNewerActions: true,
+ reportActionID: 'EXISTING_ACTION_ID',
};
const {result} = renderHook(() => useLoadReportActions(props));