Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
244 commits
Select commit Hold shift + click to select a range
4a9ef9f
Open reports at first unread action
janicduplessis Oct 23, 2024
d3458ed
Remove not needed code
janicduplessis Feb 26, 2025
c49865c
Merge remote-tracking branch 'upstream/main' into @janic/open-at-new
janicduplessis Feb 26, 2025
866106c
Merge remote-tracking branch 'upstream/main' into @janic/open-at-new
janicduplessis Feb 28, 2025
4442d2d
Fix loading at unread when no cache
janicduplessis Mar 15, 2025
87e1d6e
Merge branch 'main' into @janic/open-at-new
chrispader Mar 18, 2025
57b4a7e
fix: `ReportUtils.isUnread` signature changed
chrispader Mar 18, 2025
d3f1c53
Merge branch 'main' into @janic/open-at-new
chrispader Mar 25, 2025
21340a5
fix: ts error
chrispader Mar 25, 2025
aa6d447
fix: just check whether report is initially loading rather than wheth…
chrispader Mar 26, 2025
38e084d
Merge branch 'main' into @janic/open-at-new
chrispader Apr 16, 2025
3b89ab7
fix: add `canBeMissing` options to `useOnyx`
chrispader Apr 16, 2025
2f02420
fix: default string id lint errors
chrispader Apr 16, 2025
30eada3
fix: make canBeMissing false
chrispader Apr 16, 2025
b9b1cbe
fix: add condition for when user is offline
chrispader Apr 16, 2025
44b8528
fix: scroll to end if first message is unread
chrispader Apr 20, 2025
1c75d7a
fix: only scroll to top once
chrispader Apr 22, 2025
6a2419e
fix: improve scrolling code
chrispader Apr 22, 2025
55dd1f4
Merge branch 'main' into @janic/open-at-new
chrispader Apr 23, 2025
6540565
Merge branch 'main' into @janic/open-at-new
chrispader Apr 24, 2025
7dd1ba9
fix: typecheck
chrispader Apr 24, 2025
ab7fefa
fix: update unread indicator test
chrispader Apr 24, 2025
d9899db
Merge branch 'main' into @janic/open-at-new
chrispader Apr 25, 2025
4c9768b
Merge branch 'main' into @janic/open-at-new
chrispader Apr 29, 2025
73174dc
fix: don't immediately mark last message as read
chrispader Apr 29, 2025
d424e78
fix: improve scrolling logic
chrispader Apr 29, 2025
eac544d
fix: scrolling logic
chrispader Apr 29, 2025
5b7d8ce
fix: only start render queue when it's not already rendering
chrispader Apr 29, 2025
6d7d226
fix: initial loading when offline
chrispader Apr 29, 2025
9277cd8
fix: mock new RenderTaskQueue function
chrispader Apr 29, 2025
a5f2338
fix: RenderTaskQueue mock
chrispader Apr 29, 2025
531972f
Merge branch 'main' into pr/51366
chrispader May 8, 2025
ee538c7
Merge branch 'main' into @janic/open-at-new
chrispader May 17, 2025
0d2e982
fix: initial scrolling in BaseInvertedFlatList
chrispader May 18, 2025
a3bda66
fix: TS error in jest test setup
chrispader May 18, 2025
7d7f0a3
fix: only mark report as read when no newer messages
chrispader May 18, 2025
0df5d06
fix: allow fetching new/old messages multiple times
chrispader May 18, 2025
d6ad10e
Merge branch 'main' into @janic/open-at-new
chrispader May 20, 2025
7ab75e8
fix: TS and ESLint errors
chrispader May 20, 2025
f28a353
fix: more TS errors
chrispader May 20, 2025
6a3f4bf
Merge branch 'main' into @janic/open-at-new
chrispader May 23, 2025
4252442
fix: unread marker
chrispader May 23, 2025
b2e35b9
fix: only show floating message counter when we have an unread message
chrispader May 23, 2025
6080aec
fix: prevent multiple calls to GetOlderActions/GetNewerActions at the…
chrispader May 23, 2025
40c5a92
fix: prevent readNewestAction when report has not been unread
chrispader May 23, 2025
ddfcf16
fix: better variable names
chrispader May 23, 2025
bbba7ca
fix: update PaginationTest accordingly
chrispader May 23, 2025
bf15082
fix: remove test timeout
chrispader May 23, 2025
8c4565c
fix: wait for list to emit initial load event before expecting in tests
chrispader May 23, 2025
f4f8db6
fix: improve unread indicator test
chrispader May 23, 2025
fd5ab55
Update CONST.ts
chrispader May 23, 2025
c706843
fix: better mock BaseInvertedFlatList
chrispader May 23, 2025
efc32e1
revert testing changes
chrispader May 23, 2025
f555e31
Merge branch 'main' into @janic/open-at-new
chrispader May 23, 2025
bee7bf7
fix: imports
chrispader May 23, 2025
aa3d0df
fix: readActionSkipped fix
chrispader May 23, 2025
82db3c0
fix: wait for ReadNewestAction in test
chrispader May 26, 2025
236e7a0
fix: remove timeout
chrispader May 26, 2025
5aa401e
Merge branch 'main' into @janic/open-at-new
chrispader May 28, 2025
cc5a5b1
fix: eslint
chrispader May 29, 2025
3fd5c41
Merge branch 'main' into @janic/open-at-new
chrispader Jun 3, 2025
59e2475
fix: only call `onInitiallyLoaded` once and improve naming of variable
chrispader Jun 3, 2025
fbb0355
fix: add a max waiting time to GetNewerMessages/GetOlderMessages trigger
chrispader Jun 3, 2025
a42da60
fix: firefox chat scroll content cut off
chrispader Jun 4, 2025
77305fe
Merge branch 'main' into @janic/open-at-new
chrispader Jun 4, 2025
c14e819
Merge branch 'main' into @janic/open-at-new
chrispader Jun 11, 2025
917cf42
fix: add waitFor to Test
chrispader Jun 11, 2025
056c6f6
remove unused import
chrispader Jun 11, 2025
73b8633
fix: report screen not loading
chrispader Jun 11, 2025
a2958bd
Merge branch 'main' into @janic/open-at-new
chrispader Jun 16, 2025
87e73dd
rename variable
chrispader Jun 17, 2025
b2bce90
feat: add oldestUnreadReportActionID to OpenReport
chrispader Jun 25, 2025
5d9bee1
fix: initial unread message correct on empty cache
chrispader Jun 25, 2025
d17dae6
Update ReportScreen.tsx
chrispader Jun 25, 2025
7947454
fix: ref types
chrispader Jun 25, 2025
40d2814
fix: move Onyx operation to user action
chrispader Jun 25, 2025
181fa61
fix: reset oldestUnreadReportActionID on new openReport
chrispader Jun 25, 2025
8366ce9
refactor: simplify
chrispader Jun 25, 2025
914223f
Merge branch 'main' into @janic/open-at-new
chrispader Jun 25, 2025
2a51d0f
Merge branch 'main' into @janic/open-at-new
chrispader Jul 15, 2025
4abee92
fix: spelling
chrispader Jul 15, 2025
968788a
fix: use CONST.DEFAULT_NUMBER_ID instead of -1
chrispader Jul 15, 2025
96aebf1
fix: eslint
chrispader Jul 15, 2025
9762eda
pass down oldestUnreadReportActionID from backend
chrispader Jul 15, 2025
d14c710
fix: extract not found id
chrispader Jul 15, 2025
80adb6f
Merge branch 'main' into @janic/open-at-new
chrispader Jul 24, 2025
ed7faec
Merge branch 'main' into @janic/open-at-new
chrispader Aug 4, 2025
e31d69a
fix: loading chats in offline mode
chrispader Aug 4, 2025
c5925c3
fix: remove console log
chrispader Aug 4, 2025
9affe08
refactor: add default boolean value
chrispader Aug 5, 2025
4ce35d6
refactor: remove unused code
chrispader Aug 5, 2025
9bf5488
refactor: rename variable
chrispader Aug 5, 2025
18ceb72
fix: add comments
chrispader Aug 5, 2025
55a18d1
fix: readNewestAction logic after merge
chrispader Aug 8, 2025
6b0097a
fix: simplify if statement
chrispader Aug 8, 2025
5eee0da
Merge branch 'main' into @janic/open-at-new
chrispader Aug 8, 2025
22539e6
refactor: simplify report ready conditions
chrispader Aug 8, 2025
04a2615
Update Pagination.ts
chrispader Aug 8, 2025
1167533
fix: update oldestUnreadReportActionID type
chrispader Aug 8, 2025
5f02df7
fix: don't reset oldestUnreadReportActionID on every openReport call
chrispader Aug 11, 2025
5297ea0
fix: use onyx value if available on first render
chrispader Aug 11, 2025
6262c53
fix: isReportReady condition
chrispader Aug 11, 2025
5c9188e
Update PaginationTest.tsx
chrispader Aug 11, 2025
db7bd71
Merge branch 'main' into @janic/open-at-new
chrispader Aug 11, 2025
45e0f66
Update UnreadIndicatorsTest.tsx
chrispader Aug 11, 2025
16fb1a1
Update useReportUnreadMessageScrollTracking.ts
chrispader Aug 11, 2025
742bcb3
fix: prettier
chrispader Aug 11, 2025
1c11181
refactor: simplify maintainVisibleContentPosition variable
chrispader Aug 11, 2025
85093ee
refactor: improve props
chrispader Aug 13, 2025
b70a043
fix: animate container rather then children in `ActionSheetAwareScrol…
chrispader Aug 18, 2025
db95f6f
Revert "fix: animate container rather then children in `ActionSheetAw…
chrispader Aug 18, 2025
28606ca
fix: animate container rather then children in `ActionSheetAwareScrol…
chrispader Aug 18, 2025
2d82c4e
fix: list not loading smoothly
chrispader Aug 18, 2025
1f15c70
fix: onyx value not cleared
chrispader Aug 18, 2025
96df2d6
Update ReportScreen.tsx
chrispader Aug 18, 2025
91aa0d8
fix: clear after setting the value
chrispader Aug 18, 2025
d76bcfa
fix: tests
chrispader Aug 18, 2025
95921c8
remove console.log
chrispader Aug 18, 2025
8709b9f
Merge branch 'main' into @janic/open-at-new
chrispader Aug 19, 2025
6aaad85
dummy commit
chrispader Aug 19, 2025
ac3ace2
another dummy commit for triggering checks
chrispader Aug 19, 2025
997f637
Merge branch 'main' into @janic/open-at-new
chrispader Sep 11, 2025
6c206dd
fix: extract comment linking code to useFlatListScrollKey
chrispader Sep 11, 2025
8167bc5
fix: improve naming of some variables
chrispader Sep 11, 2025
1706d2e
feat: implement useWithFallbackRef hook
chrispader Sep 11, 2025
9551c73
fix: extract more logic to useFlatListScrollKey and useFlatListHandle
chrispader Sep 11, 2025
afac60f
fix: deprecation warning
chrispader Sep 11, 2025
2f9a92b
fix: invalid import
chrispader Sep 11, 2025
7e9b9b2
fix: render content immediately in FlatListWithScrollKey
chrispader Sep 11, 2025
93c298a
fix: typo
chrispader Sep 11, 2025
eeeae6b
revert: useFLatListScrollKey PR changes
chrispader Sep 16, 2025
1c0cfc9
Merge branch 'main' into @janic/open-at-new
chrispader Sep 16, 2025
e5a7ffd
fix: rename hook useRefWithFallback for more clarity
chrispader Sep 16, 2025
ec423af
feat: make use of useFlatListHandle and useWithFallbackRef
chrispader Sep 16, 2025
22b67e3
fix: missing property in useReportUnreadMessageScrollTracking call
chrispader Sep 16, 2025
5d9aa08
fix: add missing property in ReportActionsList
chrispader Sep 16, 2025
9161912
Merge branch 'main' into @janic/open-at-new
chrispader Sep 29, 2025
c8bd9dc
Merge branch 'main' into @janic/open-at-new
chrispader Oct 2, 2025
b2a5d6e
refactor: extract oldestUnreadReportActionID logic into separate hook
chrispader Oct 2, 2025
109da8f
docs: add comments around the new hook
chrispader Oct 2, 2025
6a73219
Merge branch 'main' into pr/51366
chrispader Oct 7, 2025
8d24e0e
fix: recursive ref calling loop
chrispader Oct 10, 2025
a9545d9
Merge branch 'main' into @janic/open-at-new
chrispader Oct 16, 2025
48ab5b8
refactor: move local constant PAGINATION_SIZE out of CONST
chrispader Oct 16, 2025
34395f9
fix: invalid index in `scrollToIndex`
chrispader Oct 16, 2025
8418480
Merge branch 'main' into @janic/open-at-new
chrispader Oct 17, 2025
3f5c76f
Merge branch 'main' into @janic/open-at-new
chrispader Oct 21, 2025
ffb3c71
fix: isReportArchived missing in `isUnread` util function call
chrispader Oct 21, 2025
4f30632
refactor: move Onyx.set call
chrispader Oct 21, 2025
4bed196
fix: UnreadIndicatorsTest
chrispader Oct 21, 2025
8321452
fix: disable deprecation warning to pass tests for now
chrispader Oct 21, 2025
c2e13cf
fix: deprecation warning
chrispader Oct 21, 2025
67d7982
fix: another new isUnread function usage
chrispader Oct 21, 2025
41c0073
Merge branch 'main' into @janic/open-at-new
chrispader Oct 22, 2025
15cf348
Merge branch 'main' into @janic/open-at-new
chrispader Oct 23, 2025
bb2abba
Merge branch 'main' into pr/51366
chrispader Oct 30, 2025
2fadbf4
Merge branch 'main' into pr/51366
chrispader Oct 31, 2025
11390d3
docs: add comment about `REPORT_OLDEST_UNREAD_REPORT_ACTION_ID` colle…
chrispader Oct 31, 2025
947134d
fix: eslint deprecation warning
chrispader Oct 31, 2025
f822b79
fix: remove unnecessary `eslint-disable-next` comments
chrispader Oct 31, 2025
d1a151f
fix: exit early if reportID is nullish
chrispader Oct 31, 2025
3ea2db9
fix: remove race condition and use `Promise.finally`
chrispader Oct 31, 2025
22e4a8a
fix: improve chat loading finalisation
chrispader Oct 31, 2025
aa76a22
fix: remove promises
chrispader Oct 31, 2025
0358bf6
refactor: remove unused type
chrispader Oct 31, 2025
4c30692
Merge branch 'main' into pr/51366
chrispader Oct 31, 2025
9507ca6
fix: remove deprecation rule `eslint-disable` comments
chrispader Oct 31, 2025
d1fbb1e
fix: stop loading if no report action is loaded
chrispader Nov 4, 2025
9a872ba
Merge branch 'main' into pr/51366
chrispader Nov 4, 2025
8ab1d8f
Merge branch 'main' into pr/51366
chrispader Nov 4, 2025
a48582c
Merge branch 'main' into pr/51366
chrispader Nov 6, 2025
e311169
fix: report loading indicator on every open
chrispader Nov 6, 2025
58e49a7
Merge branch 'main' into pr/51366
chrispader Nov 10, 2025
3ecaeac
Merge branch 'main' into pr/51366
chrispader Nov 13, 2025
b28acad
feat: implement new search for unread report action ID in `Pagination…
chrispader Nov 13, 2025
1e86e77
add comments and rename property
chrispader Nov 13, 2025
a53719e
feat: return found item from `getContinousChain`
chrispader Nov 13, 2025
a24520a
fix: remove `oldestUnreadReportActionID` changes
chrispader Nov 13, 2025
8b05d99
fix: remove more old changes
chrispader Nov 13, 2025
70fa0c7
unnecessary change
chrispader Nov 13, 2025
d52f129
fix: improve `usePaginatedReportActions` function signature
chrispader Nov 13, 2025
80b980c
fix: remove unused `oldestUnreadReportActionID` prop in list componetns
chrispader Nov 13, 2025
a0c5210
fix: simplify PaginationUtils and return `resourceItem` restul
chrispader Nov 13, 2025
c70a883
refactor: use `resourceItem` for `linkedAction` and `oldestUnreadRepo…
chrispader Nov 13, 2025
970609f
fix: invalid condition for `oldestUnreadReportActionID`
chrispader Nov 14, 2025
c6930d3
Merge branch 'main' into pr/51366
chrispader Nov 14, 2025
766870f
fix: rename `usePaginatedReportActions ` option and fix linked/unread…
chrispader Nov 14, 2025
136e1a7
remove log
chrispader Nov 14, 2025
1f5c9fb
feat: show skeleton if report actions don't fill page
chrispader Nov 14, 2025
eba6789
fix: search oldest unread report action by initial `report.lastReadTime`
chrispader Nov 14, 2025
7bf2e3c
feat: implement loading state for loading initial report actions
chrispader Nov 14, 2025
a5e7fb9
fix: search with `resourceItemPredicate` for last index
chrispader Nov 14, 2025
a0fae0d
remove empty line
chrispader Nov 14, 2025
2b04286
remove logs
chrispader Nov 14, 2025
7a076d2
fix: improve report ready conditions
chrispader Nov 14, 2025
f2bca6e
refactor: improve screen filling condition
chrispader Nov 14, 2025
af20a30
Merge branch 'main' into pr/51366
chrispader Nov 17, 2025
594570a
refactor: add comments and minor refactors
chrispader Nov 17, 2025
f7ef85d
fix: remove redundant conditional
chrispader Nov 17, 2025
7f1cd9b
fix: add missing `isInitiallyLoaded` dependency
chrispader Nov 17, 2025
8f5f094
refactor: move variable
chrispader Nov 17, 2025
ffbb946
fix: start render task queue with delay
chrispader Nov 17, 2025
ec7c41b
fix: remove null for `currentDataID` instead of empty string
chrispader Nov 17, 2025
9073cb0
refactor: remove unused `useEffect` deps
chrispader Nov 17, 2025
7f3b5b0
fix: dependency array
chrispader Nov 17, 2025
f5d8323
fix: maintainVisibleContentPosition
chrispader Nov 17, 2025
96f9672
fix: Expensicons deprecated
chrispader Nov 17, 2025
c70056c
fix: spell check
chrispader Nov 17, 2025
e7858f5
fix: RenderTaskQueue tests
chrispader Nov 17, 2025
a091b4f
fix: reduce Pagina`getContinuousChain` to one resource ID param
chrispader Nov 17, 2025
06834f3
fix: add back external change
chrispader Nov 17, 2025
641914c
fix: simplify `getContinousChain` logic
chrispader Nov 17, 2025
7b8dbbb
Update usePaginatedReportActions.ts
chrispader Nov 17, 2025
83c3b7c
Merge branch 'main' into pr/51366
chrispader Nov 26, 2025
6cd8f46
fix: simplify
chrispader Nov 26, 2025
357e222
fix: rename Expensicons variable
chrispader Nov 26, 2025
a22aec9
update comments
chrispader Nov 26, 2025
ea13a27
Merge branch 'main' into pr/51366
chrispader Nov 26, 2025
482a607
refactor: extract changes to separate PRs
chrispader Nov 26, 2025
618e933
refactor: extract more changes to separate PRs
chrispader Nov 26, 2025
290df1e
fix: missing condition
chrispader Nov 26, 2025
99d8152
refactor: move more changes
chrispader Nov 26, 2025
a38b82f
refactor: extract changes
chrispader Nov 26, 2025
262cf44
Merge branch 'main' into @janic/open-at-new
chrispader Mar 25, 2026
b2d705d
Update Mobile-Expensify
chrispader Mar 25, 2026
9d06d14
fix: TS and ESLint errors
chrispader Mar 25, 2026
deca4e5
fix: ESLint
chrispader Mar 25, 2026
6ecce8c
revert: API changes
chrispader Mar 25, 2026
1eca499
fix: prevent page merging when opening unread report action
chrispader Mar 26, 2026
44a9efe
Merge branch 'main' into pr/51366
chrispader Apr 3, 2026
80a7396
fix: missing `shouldLinkToOldestUnreadReportAction` option in `usePag…
chrispader Apr 3, 2026
3f6c320
fix: keep condition for initial request load
chrispader Apr 3, 2026
d75a1af
fix: missing `shouldLinkToOldestUnreadReportAction` call
chrispader Apr 3, 2026
c296025
Merge branch 'main' into pr/51366
chrispader Apr 10, 2026
56fbca9
Merge branch 'main' into pr/51366
chrispader Apr 13, 2026
9d1f19c
fix: scrolling to bottom on new message snet
chrispader Apr 4, 2026
a898c50
Merge branch 'main' into pr/51366
chrispader Apr 13, 2026
2841ff7
Merge branch 'main' into pr/51366
chrispader Apr 14, 2026
0d9fc51
fix: invalid import
chrispader Apr 14, 2026
a4dc08d
Merge branch 'main' into pr/51366
chrispader Apr 14, 2026
120aaf5
fix: remove unused backend response param
chrispader Apr 14, 2026
82599b9
Merge branch 'main' into pr/51366
chrispader Apr 14, 2026
dab912d
Merge branch 'main' into pr/51366
chrispader Apr 14, 2026
bd52a44
Merge branch '@janic/open-at-new' of https://github.com/janicduplessi…
chrispader Apr 14, 2026
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
2 changes: 1 addition & 1 deletion __mocks__/react-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jest.doMock('react-native', () => {
NativeModules: {
...ReactNative.NativeModules,
BootSplash: {
hide: jest.fn(),
hide: jest.fn().mockResolvedValue(undefined),
logoSizeRatio: 1,
navigationBarHeight: 0,
},
Expand Down
3 changes: 1 addition & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5369,8 +5369,7 @@ const CONST = {

REPORT_FIELD_TITLE_FIELD_ID: 'text_title',

MOBILE_PAGINATION_SIZE: 15,
WEB_PAGINATION_SIZE: 30,
PAGINATION_SIZE: 15,

/** Dimensions for illustration shown in Confirmation Modal */
CONFIRM_CONTENT_SVG_SIZE: {
Expand Down

This file was deleted.

This file was deleted.

23 changes: 21 additions & 2 deletions src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo,
import type {FlatListProps, ListRenderItem, ListRenderItemInfo, FlatList as RNFlatList, ScrollViewProps} from 'react-native';
import FlatList from '@components/FlatList';
import usePrevious from '@hooks/usePrevious';
import getInitialPaginationSize from './getInitialPaginationSize';
import CONST from '@src/CONST';
import RenderTaskQueue from './RenderTaskQueue';

// Adapted from https://github.com/facebook/react-native/blob/29a0d7c3b201318a873db0d1b62923f4ce720049/packages/virtualized-lists/Lists/VirtualizeUtils.js#L237
Expand Down Expand Up @@ -46,7 +46,7 @@ function BaseInvertedFlatList<T>(props: BaseInvertedFlatListProps<T>, ref: Forwa
if (currentDataIndex <= 0) {
return data;
}
return data.slice(Math.max(0, currentDataIndex - (isInitialData ? 0 : getInitialPaginationSize)));
return data.slice(Math.max(0, currentDataIndex - (isInitialData ? 0 : CONST.PAGINATION_SIZE)));
}, [currentDataIndex, data, isInitialData]);

const isLoadingData = data.length > displayedData.length;
Expand Down Expand Up @@ -111,13 +111,32 @@ function BaseInvertedFlatList<T>(props: BaseInvertedFlatListProps<T>, ref: Forwa
});
};

const scrollToIndexFn: RNFlatList['scrollToIndex'] = (params) => {
const actualIndex = params.index - dataIndexDifference;
try {
listRef.current?.scrollToIndex({...params, index: actualIndex});
} catch (ex) {
// It is possible that scrolling fails since the item we are trying to scroll to
// has not been rendered yet. In this case, we call the onScrollToIndexFailed.
props.onScrollToIndexFailed?.({
index: actualIndex,
// These metrics are not implemented.
averageItemLength: 0,
highestMeasuredFrameIndex: 0,
});
}
};

return new Proxy(
{},
{
get: (_target, prop) => {
if (prop === 'scrollToOffset') {
return scrollToOffsetFn;
}
if (prop === 'scrollToIndex') {
return scrollToIndexFn;
}
return listRef.current?.[prop as keyof RNFlatList];
},
},
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/OpenReportParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type OpenReportParams = {
optimisticAccountIDList?: string;
file?: File | CustomRNImageManipulatorResult;
guidedSetupData?: string;
useLastUnreadReportAction?: boolean;
};

export default OpenReportParams;
10 changes: 8 additions & 2 deletions src/libs/Middleware/Pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const Pagination: Middleware = (requestResponse, request) => {

const newPage = sortedPageItems.map((item) => getItemID(item));

if (response.hasNewerActions === false || (type === 'initial' && !cursorID)) {
if (response.hasNewerActions === false) {
newPage.unshift(CONST.PAGINATION_START_ID);
}
if (response.hasOlderActions === false) {
Expand All @@ -119,8 +119,14 @@ const Pagination: Middleware = (requestResponse, request) => {

const pagesCollections = pages.get(pageCollectionKey) ?? {};
const existingPages = pagesCollections[pageKey] ?? [];
const mergedPages = PaginationUtils.mergeAndSortContinuousPages(sortedAllItems, [...existingPages, newPage], getItemID);

// When loading the first page of data, make sure to remove the start maker if the backend returns
// that there is new data.
const firstPage = existingPages.at(0);
if (type === 'initial' && !cursorID && firstPage?.at(0) === CONST.PAGINATION_START_ID && response.hasNewerActions === true) {
firstPage.shift();
}
const mergedPages = PaginationUtils.mergeAndSortContinuousPages(sortedAllItems, [...existingPages, newPage], getItemID);
response.onyxData.push({
key: pageKey,
onyxMethod: Onyx.METHOD.SET,
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ function openReport(
emailList: participantLoginList ? participantLoginList.join(',') : '',
accountIDList: participantAccountIDList ? participantAccountIDList.join(',') : '',
parentReportActionID,
useLastUnreadReportAction: true,
};

const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;
Expand Down
33 changes: 27 additions & 6 deletions src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ function ReportActionsList({
/**
* The reportActionID the unread marker should display above
*/
const unreadMarkerReportActionID = useMemo(() => {
const {unreadMarkerReportActionID, unreadMarkerReportActionIndex} = useMemo(() => {
const shouldDisplayNewMarker = (message: OnyxTypes.ReportAction, index: number): boolean => {
const nextMessage = sortedVisibleReportActions.at(index + 1);
const isNextMessageUnread = !!nextMessage && isReportActionUnread(nextMessage, unreadMarkerTime);
Expand Down Expand Up @@ -332,11 +332,11 @@ function ReportActionsList({

// eslint-disable-next-line react-compiler/react-compiler
if (reportAction && shouldDisplayNewMarker(reportAction, index)) {
return reportAction.reportActionID;
return {unreadMarkerReportActionID: reportAction.reportActionID, unreadMarkerReportActionIndex: index};
}
}

return null;
return {unreadMarkerReportActionID: null, unreadMarkerReportActionIndex: -1};
}, [accountID, earliestReceivedOfflineMessageIndex, prevSortedVisibleReportActionsObjects, sortedVisibleReportActions, unreadMarkerTime]);
prevUnreadMarkerReportActionID.current = unreadMarkerReportActionID;

Expand Down Expand Up @@ -446,7 +446,7 @@ function ReportActionsList({
}, [report.lastVisibleActionCreated, report.reportID, isVisible]);

useEffect(() => {
if (linkedReportActionID) {
if (linkedReportActionID || unreadMarkerReportActionID) {
return;
}
InteractionManager.runAfterInteractions(() => {
Expand Down Expand Up @@ -491,7 +491,7 @@ function ReportActionsList({
useEffect(() => {
// Why are we doing this, when in the cleanup of the useEffect we are already calling the unsubscribe function?
// Answer: On web, when navigating to another report screen, the previous report screen doesn't get unmounted,
// meaning that the cleanup might not get called. When we then open a report we had open already previosuly, a new
// meaning that the cleanup might not get called. When we then open a report we had open already previously, a new
// ReportScreen will get created. Thus, we have to cancel the earlier subscription of the previous screen,
// because the two subscriptions could conflict!
// In case we return to the previous screen (e.g. by web back navigation) the useEffect for that screen would
Expand Down Expand Up @@ -639,6 +639,27 @@ function ReportActionsList({
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isFocused, isVisible]);

// Handles scrolling to the unread marker.
// If we have an unread marker initially we do not need to scroll to it as this
// will be handled by the list `initialScrollKey`.
const didScrollToUnreadMarker = useRef(unreadMarkerReportActionIndex >= 0);
useEffect(() => {
if (unreadMarkerReportActionIndex === -1 || didScrollToUnreadMarker.current) {
return;
}
didScrollToUnreadMarker.current = true;
InteractionManager.runAfterInteractions(() => {
reportScrollManager.ref?.current?.scrollToIndex({
index: unreadMarkerReportActionIndex,
animated: true,
// This scrolls the unread action at the top of the screen.
viewPosition: 1,
// This makes sure that the unread indicator doesn't get cut off.
viewOffset: -64,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly adjusting the scroll position so the newest message is near the top ended up very difficult with the way we handle opening the list in the middle of a chat.

how is this relevant if the last unread message is show at the bottom not the top?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From Flatlist's scrollToIndex() docs:

Scrolls to the item at the specified index such that it is positioned in the viewable area such that viewPosition 0 places it at the top, 1 at the bottom, and 0.5 centered in the middle.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually not needed anymore.

});
});
}, [reportScrollManager, unreadMarkerReportActionIndex]);

const renderItem = useCallback(
({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => (
<ReportActionsListItemRenderer
Expand Down Expand Up @@ -813,7 +834,7 @@ function ReportActionsList({
extraData={extraData}
key={listID}
shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScrollToTopThreshold}
initialScrollKey={reportActionID}
initialScrollKey={reportActionID ?? unreadMarkerReportActionID}
/>
</View>
</>
Expand Down
4 changes: 1 addition & 3 deletions src/pages/home/report/ReportActionsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,7 @@ function ReportActionsView({
(force = false) => {
if (
!force &&
(!reportActionID ||
!isFocused ||
(!isFocused ||
!newestReportAction ||
isLoadingInitialReportActions ||
isLoadingNewerReportActions ||
Expand Down Expand Up @@ -361,7 +360,6 @@ function ReportActionsView({
}
},
[
reportActionID,
isFocused,
newestReportAction,
isLoadingInitialReportActions,
Expand Down
81 changes: 16 additions & 65 deletions tests/ui/PaginationTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import {act, fireEvent, render, screen, within} from '@testing-library/react-nat
import {addSeconds, format, subMinutes} from 'date-fns';
import React from 'react';
import Onyx from 'react-native-onyx';
import * as Localize from '@libs/Localize';
import * as SequentialQueue from '@libs/Network/SequentialQueue';
import * as AppActions from '@userActions/App';
import * as User from '@userActions/User';
import {setSidebarLoaded} from '@libs/actions/App';
import {subscribeToUserEvents} from '@libs/actions/User';
import {translateLocal} from '@libs/Localize';
import {waitForIdle} from '@libs/Network/SequentialQueue';
import App from '@src/App';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ReportAction} from '@src/types/onyx';
import type {NativeNavigationMock} from '../../__mocks__/@react-navigation/native';
import PusherHelper from '../utils/PusherHelper';
import {getReportScreen, LIST_CONTENT_SIZE, navigateToSidebarOption, REPORT_ID, scrollToOffset, triggerListLayout} from '../utils/ReportTestUtils';
import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
Expand All @@ -30,75 +31,23 @@ jest.mock('@src/components/Navigation/TopLevelBottomTabBar/useIsBottomTabVisible
TestHelper.setupApp();
const fetchMock = TestHelper.setupGlobalFetchMock();

const LIST_SIZE = {
width: 300,
height: 400,
};
const LIST_CONTENT_SIZE = {
width: 300,
height: 600,
};
const TEN_MINUTES_AGO = subMinutes(new Date(), 10);

const REPORT_ID = '1';
const COMMENT_LINKING_REPORT_ID = '2';
const USER_A_ACCOUNT_ID = 1;
const USER_A_EMAIL = 'user_a@test.com';
const USER_B_ACCOUNT_ID = 2;
const USER_B_EMAIL = 'user_b@test.com';

function getReportScreen(reportID = REPORT_ID) {
return screen.getByTestId(`report-screen-${reportID}`);
}

function scrollToOffset(offset: number) {
const hintText = Localize.translateLocal('sidebarScreen.listOfChatMessages');
fireEvent.scroll(within(getReportScreen()).getByLabelText(hintText), {
nativeEvent: {
contentOffset: {
y: offset,
},
contentSize: LIST_CONTENT_SIZE,
layoutMeasurement: LIST_SIZE,
},
});
}

function triggerListLayout(reportID?: string) {
const report = getReportScreen(reportID);
fireEvent(within(report).getByTestId('report-actions-view-wrapper'), 'onLayout', {
nativeEvent: {
layout: {
x: 0,
y: 0,
...LIST_SIZE,
},
},
});

fireEvent(within(report).getByTestId('report-actions-list'), 'onContentSizeChange', LIST_CONTENT_SIZE.width, LIST_CONTENT_SIZE.height);
}

function getReportActions(reportID?: string) {
const report = getReportScreen(reportID);
return [
...within(report).queryAllByLabelText(Localize.translateLocal('accessibilityHints.chatMessage')),
...within(report).queryAllByLabelText(translateLocal('accessibilityHints.chatMessage')),
// Created action has a different accessibility label.
...within(report).queryAllByLabelText(Localize.translateLocal('accessibilityHints.chatWelcomeMessage')),
...within(report).queryAllByLabelText(translateLocal('accessibilityHints.chatWelcomeMessage')),
];
}

async function navigateToSidebarOption(reportID: string): Promise<void> {
const optionRow = screen.getByTestId(reportID);
fireEvent(optionRow, 'press');
await act(() => {
(NativeNavigation as NativeNavigationMock).triggerTransitionEnd();
});
// ReportScreen relies on the onLayout event to receive updates from onyx.
triggerListLayout(reportID);
await waitForBatchedUpdatesWithAct();
}

function buildCreatedAction(reportActionID: string, created: string) {
return {
reportActionID,
Expand Down Expand Up @@ -129,7 +78,7 @@ function buildReportComments(count: number, initialID: string, reverse = false)
}

function mockOpenReport(messageCount: number, initialID: string) {
fetchMock.mockAPICommand('OpenReport', ({reportID}) => {
fetchMock.mockAPICommand('OpenReport', ({reportID, reportActionID}) => {
const comments = buildReportComments(messageCount, initialID);
return {
onyxData:
Expand All @@ -143,7 +92,7 @@ function mockOpenReport(messageCount: number, initialID: string) {
]
: [],
hasOlderActions: !comments['1'],
hasNewerActions: !!reportID,
hasNewerActions: !!reportActionID,
};
});
}
Expand Down Expand Up @@ -192,7 +141,7 @@ async function signInAndGetApp(): Promise<void> {
// Render the App and sign in as a test user.
render(<App />);
await waitForBatchedUpdatesWithAct();
const hintText = Localize.translateLocal('loginForm.loginForm');
const hintText = translateLocal('loginForm.loginForm');
const loginForm = await screen.findAllByLabelText(hintText);
expect(loginForm).toHaveLength(1);

Expand All @@ -202,16 +151,17 @@ async function signInAndGetApp(): Promise<void> {

await waitForBatchedUpdatesWithAct();

User.subscribeToUserEvents();
subscribeToUserEvents();

await waitForBatchedUpdates();

await act(async () => {
// Simulate setting an unread report and personal details
// Simulate setting a report and personal details
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, {
reportID: REPORT_ID,
reportName: CONST.REPORT.DEFAULT_REPORT_NAME,
lastMessageText: 'Test',
lastReadTime: format(new Date(), CONST.DATE.FNS_DB_FORMAT_STRING),
participants: {
[USER_B_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS},
[USER_A_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS},
Expand All @@ -229,6 +179,7 @@ async function signInAndGetApp(): Promise<void> {
reportID: COMMENT_LINKING_REPORT_ID,
reportName: CONST.REPORT.DEFAULT_REPORT_NAME,
lastMessageText: 'Test',
lastReadTime: format(new Date(), CONST.DATE.FNS_DB_FORMAT_STRING),
participants: {[USER_A_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}},
lastActorAccountID: USER_A_ACCOUNT_ID,
type: CONST.REPORT.TYPE.CHAT,
Expand All @@ -253,15 +204,15 @@ async function signInAndGetApp(): Promise<void> {
});

// We manually setting the sidebar as loaded since the onLayout event does not fire in tests
AppActions.setSidebarLoaded();
setSidebarLoaded();
});

await waitForBatchedUpdatesWithAct();
}

describe('Pagination', () => {
afterEach(async () => {
await SequentialQueue.waitForIdle();
await waitForIdle();
await act(async () => {
await Onyx.clear();

Expand Down
Loading