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
52 changes: 32 additions & 20 deletions src/hooks/useLoadReportActions.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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[];

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -94,48 +99,55 @@ 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 ||
Comment thread
blimpich marked this conversation as resolved.
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);
} else if (newestReportAction) {
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 {
Expand Down
6 changes: 4 additions & 2 deletions src/libs/API/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,13 @@ function paginate<TRequestType extends ApiRequestType, TCommand extends CommandO

switch (type) {
case CONST.API_REQUEST_TYPE.WRITE:
return processRequest(request, type);
processRequest(request, type);
return;
case CONST.API_REQUEST_TYPE.MAKE_REQUEST_WITH_SIDE_EFFECTS:
return processRequest(request, type);
case CONST.API_REQUEST_TYPE.READ:
return waitForWrites(command as ReadCommand).then(() => processRequest(request, type));
waitForWrites(command as ReadCommand).then(() => processRequest(request, type));
return;
default:
throw new Error('Unknown API request type');
}
Expand Down
1 change: 0 additions & 1 deletion src/libs/API/parameters/OpenReportParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ type OpenReportParams = {
*/
moneyRequestPreviewReportActionID?: string;
includePartiallySetupBankAccounts?: boolean;
useLastUnreadReportAction?: boolean;
};

export default OpenReportParams;
7 changes: 3 additions & 4 deletions src/libs/actions/Report/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
/** @deprecated This value is deprecated and will be removed soon after migration. Use the email from useCurrentUserPersonalDetails hook instead. */
let deprecatedCurrentUserLogin: string | undefined;

Onyx.connect({

Check warning on line 319 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -330,7 +330,7 @@
},
});

Onyx.connect({

Check warning on line 333 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => (conciergeReportIDOnyxConnect = value),
});
Expand All @@ -338,7 +338,7 @@
// map of reportID to all reportActions for that report
const allReportActions: OnyxCollection<ReportActions> = {};

Onyx.connect({

Check warning on line 341 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
callback: (actions, key) => {
if (!key || !actions) {
Expand All @@ -350,7 +350,7 @@
});

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 353 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -359,7 +359,7 @@
});

let allPersonalDetails: OnyxEntry<PersonalDetailsList> = {};
Onyx.connect({

Check warning on line 362 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand All @@ -374,7 +374,7 @@
});

let onboarding: OnyxEntry<Onboarding>;
Onyx.connect({

Check warning on line 377 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_ONBOARDING,
callback: (val) => {
if (Array.isArray(val)) {
Expand All @@ -385,7 +385,7 @@
});

let deprecatedIntroSelected: OnyxEntry<IntroSelected> = {};
Onyx.connect({

Check warning on line 388 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_INTRO_SELECTED,
callback: (val) => (deprecatedIntroSelected = val),
});
Expand Down Expand Up @@ -1287,7 +1287,6 @@
parentReportActionID,
transactionID: transaction?.transactionID,
includePartiallySetupBankAccounts: true,
useLastUnreadReportAction: true,
};

if (optimisticSelfDMReport) {
Expand Down Expand Up @@ -2028,7 +2027,7 @@
*/
function getOlderActions(reportID: string | undefined, reportActionID: string | undefined) {
if (!reportID || !reportActionID) {
return Promise.resolve();
return;
}

const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_METADATA>> = [
Expand Down Expand Up @@ -2068,7 +2067,7 @@
reportActionID,
};

return API.paginate(
API.paginate(
CONST.API_REQUEST_TYPE.READ,
READ_COMMANDS.GET_OLDER_ACTIONS,
parameters,
Expand Down Expand Up @@ -2126,7 +2125,7 @@
reportActionID,
};

return API.paginate(
API.paginate(
CONST.API_REQUEST_TYPE.READ,
READ_COMMANDS.GET_NEWER_ACTIONS,
parameters,
Expand Down
24 changes: 19 additions & 5 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent} from 'r
import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {renderScrollComponent as renderActionSheetAwareScrollView} from '@components/ActionSheetAwareScrollView';
import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/FlatList/hooks/useFlatListScrollKey';
import InvertedFlatList from '@components/FlatList/InvertedFlatList';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
Expand Down Expand Up @@ -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({
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/pages/inbox/report/ReportActionsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ function ReportActionsView({

const {loadOlderChats, loadNewerChats} = useLoadReportActions({
reportID,
reportActionID,
reportActions,
allReportActionIDs,
transactionThreadReport,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type ChatContentScrollViewPlatformStyles from './types';

const chatContentScrollViewPlatformStyles: ChatContentScrollViewPlatformStyles = {
overflow: 'clip',
overflow: 'hidden',
};

export default chatContentScrollViewPlatformStyles;
11 changes: 5 additions & 6 deletions tests/ui/PaginationTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'});
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions tests/unit/useLoadReportActionsTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('useLoadReportActions', () => {
const props = {
...baseProps,
hasNewerActions: true,
reportActionID: 'EXISTING_ACTION_ID',
};

const {result} = renderHook(() => useLoadReportActions(props));
Expand Down
Loading