Skip to content

Commit 485a851

Browse files
authored
Merge pull request Expensify#26166 from margelo/feat/#Expensify#23220-bidirectional-pagination
Bidirectional pagination
2 parents 4a7709f + c9f0620 commit 485a851

File tree

14 files changed

+319
-84
lines changed

14 files changed

+319
-84
lines changed

src/CONST.ts

+7
Original file line numberDiff line numberDiff line change
@@ -2779,13 +2779,20 @@ const CONST = {
27792779
SCROLLING: 'scrolling',
27802780
},
27812781

2782+
CHAT_HEADER_LOADER_HEIGHT: 36,
2783+
27822784
HORIZONTAL_SPACER: {
27832785
DEFAULT_BORDER_BOTTOM_WIDTH: 1,
27842786
DEFAULT_MARGIN_VERTICAL: 8,
27852787
HIDDEN_MARGIN_VERTICAL: 0,
27862788
HIDDEN_BORDER_BOTTOM_WIDTH: 0,
27872789
},
27882790

2791+
LIST_COMPONENTS: {
2792+
HEADER: 'header',
2793+
FOOTER: 'footer',
2794+
},
2795+
27892796
GLOBAL_NAVIGATION_OPTION: {
27902797
HOME: 'home',
27912798
CHATS: 'chats',

src/ONYXKEYS.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ const ONYXKEYS = {
244244
POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
245245
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
246246
REPORT: 'report_',
247-
// REPORT_METADATA is a perf optimization used to hold loading states (isLoadingReportActions, isLoadingMoreReportActions).
247+
// REPORT_METADATA is a perf optimization used to hold loading states (isLoadingInitialReportActions, isLoadingOlderReportActions, isLoadingNewerReportActions).
248248
// A lot of components are connected to the Report entity and do not care about the actions. Setting the loading state
249249
// directly on the report caused a lot of unnecessary re-renders
250250
REPORT_METADATA: 'reportMetadata_',

src/components/InvertedFlatList/BaseInvertedFlatList.js

+3
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ function BaseInvertedFlatList(props) {
133133
// Web requires that items be measured or else crazy things happen when scrolling.
134134
getItemLayout={shouldMeasureItems ? getItemLayout : undefined}
135135
windowSize={15}
136+
maintainVisibleContentPosition={{
137+
minIndexForVisible: 0,
138+
}}
136139
inverted
137140
/>
138141
);

src/components/ReportActionsSkeletonView/index.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@ import CONST from '../../CONST';
77
const propTypes = {
88
/** Whether to animate the skeleton view */
99
shouldAnimate: PropTypes.bool,
10+
11+
/** Number of possible visible content items */
12+
possibleVisibleContentItems: PropTypes.number,
1013
};
1114

1215
const defaultProps = {
1316
shouldAnimate: true,
17+
possibleVisibleContentItems: 0,
1418
};
1519

16-
function ReportActionsSkeletonView(props) {
17-
// Determines the number of content items based on container height
18-
const possibleVisibleContentItems = Math.ceil(Dimensions.get('window').height / CONST.CHAT_SKELETON_VIEW.AVERAGE_ROW_HEIGHT);
20+
function ReportActionsSkeletonView({shouldAnimate, possibleVisibleContentItems}) {
21+
const contentItems = possibleVisibleContentItems || Math.ceil(Dimensions.get('window').height / CONST.CHAT_SKELETON_VIEW.AVERAGE_ROW_HEIGHT);
1922
const skeletonViewLines = [];
20-
for (let index = 0; index < possibleVisibleContentItems; index++) {
23+
for (let index = 0; index < contentItems; index++) {
2124
const iconIndex = (index + 1) % 4;
2225
switch (iconIndex) {
2326
case 2:
2427
skeletonViewLines.push(
2528
<SkeletonViewLines
26-
shouldAnimate={props.shouldAnimate}
29+
shouldAnimate={shouldAnimate}
2730
numberOfRows={2}
2831
key={`skeletonViewLines${index}`}
2932
/>,
@@ -32,7 +35,7 @@ function ReportActionsSkeletonView(props) {
3235
case 0:
3336
skeletonViewLines.push(
3437
<SkeletonViewLines
35-
shouldAnimate={props.shouldAnimate}
38+
shouldAnimate={shouldAnimate}
3639
numberOfRows={3}
3740
key={`skeletonViewLines${index}`}
3841
/>,
@@ -41,7 +44,7 @@ function ReportActionsSkeletonView(props) {
4144
default:
4245
skeletonViewLines.push(
4346
<SkeletonViewLines
44-
shouldAnimate={props.shouldAnimate}
47+
shouldAnimate={shouldAnimate}
4548
numberOfRows={1}
4649
key={`skeletonViewLines${index}`}
4750
/>,

src/libs/actions/Policy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs) {
365365
onyxMethod: Onyx.METHOD.MERGE,
366366
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${optimisticReport.reportID}`,
367367
value: {
368-
isLoadingReportActions: false,
368+
isLoadingInitialReportActions: false,
369369
},
370370
});
371371
});

src/libs/actions/Report.js

+63-14
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,9 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p
477477
onyxMethod: Onyx.METHOD.MERGE,
478478
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
479479
value: {
480-
isLoadingReportActions: true,
481-
isLoadingMoreReportActions: false,
480+
isLoadingInitialReportActions: true,
481+
isLoadingOlderReportActions: false,
482+
isLoadingNewerReportActions: false,
482483
},
483484
},
484485
];
@@ -501,7 +502,7 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p
501502
onyxMethod: Onyx.METHOD.MERGE,
502503
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
503504
value: {
504-
isLoadingReportActions: false,
505+
isLoadingInitialReportActions: false,
505506
},
506507
},
507508
];
@@ -511,7 +512,7 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p
511512
onyxMethod: Onyx.METHOD.MERGE,
512513
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
513514
value: {
514-
isLoadingReportActions: false,
515+
isLoadingInitialReportActions: false,
515516
},
516517
},
517518
];
@@ -737,8 +738,9 @@ function reconnect(reportID) {
737738
onyxMethod: Onyx.METHOD.MERGE,
738739
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
739740
value: {
740-
isLoadingReportActions: true,
741-
isLoadingMoreReportActions: false,
741+
isLoadingInitialReportActions: true,
742+
isLoadingNewerReportActions: false,
743+
isLoadingOlderReportActions: false,
742744
},
743745
},
744746
],
@@ -747,7 +749,7 @@ function reconnect(reportID) {
747749
onyxMethod: Onyx.METHOD.MERGE,
748750
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
749751
value: {
750-
isLoadingReportActions: false,
752+
isLoadingInitialReportActions: false,
751753
},
752754
},
753755
],
@@ -756,7 +758,7 @@ function reconnect(reportID) {
756758
onyxMethod: Onyx.METHOD.MERGE,
757759
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
758760
value: {
759-
isLoadingReportActions: false,
761+
isLoadingInitialReportActions: false,
760762
},
761763
},
762764
],
@@ -771,9 +773,9 @@ function reconnect(reportID) {
771773
* @param {String} reportID
772774
* @param {String} reportActionID
773775
*/
774-
function readOldestAction(reportID, reportActionID) {
776+
function getOlderActions(reportID, reportActionID) {
775777
API.read(
776-
'ReadOldestAction',
778+
'GetOlderActions',
777779
{
778780
reportID,
779781
reportActionID,
@@ -784,7 +786,7 @@ function readOldestAction(reportID, reportActionID) {
784786
onyxMethod: Onyx.METHOD.MERGE,
785787
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
786788
value: {
787-
isLoadingMoreReportActions: true,
789+
isLoadingOlderReportActions: true,
788790
},
789791
},
790792
],
@@ -793,7 +795,7 @@ function readOldestAction(reportID, reportActionID) {
793795
onyxMethod: Onyx.METHOD.MERGE,
794796
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
795797
value: {
796-
isLoadingMoreReportActions: false,
798+
isLoadingOlderReportActions: false,
797799
},
798800
},
799801
],
@@ -802,7 +804,53 @@ function readOldestAction(reportID, reportActionID) {
802804
onyxMethod: Onyx.METHOD.MERGE,
803805
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
804806
value: {
805-
isLoadingMoreReportActions: false,
807+
isLoadingOlderReportActions: false,
808+
},
809+
},
810+
],
811+
},
812+
);
813+
}
814+
815+
/**
816+
* Gets the newer actions that have not been read yet.
817+
* Normally happens when you are not located at the bottom of the list and scroll down on a chat.
818+
*
819+
* @param {String} reportID
820+
* @param {String} reportActionID
821+
*/
822+
function getNewerActions(reportID, reportActionID) {
823+
API.read(
824+
'GetNewerActions',
825+
{
826+
reportID,
827+
reportActionID,
828+
},
829+
{
830+
optimisticData: [
831+
{
832+
onyxMethod: Onyx.METHOD.MERGE,
833+
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
834+
value: {
835+
isLoadingNewerReportActions: true,
836+
},
837+
},
838+
],
839+
successData: [
840+
{
841+
onyxMethod: Onyx.METHOD.MERGE,
842+
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
843+
value: {
844+
isLoadingNewerReportActions: false,
845+
},
846+
},
847+
],
848+
failureData: [
849+
{
850+
onyxMethod: Onyx.METHOD.MERGE,
851+
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
852+
value: {
853+
isLoadingNewerReportActions: false,
806854
},
807855
},
808856
],
@@ -2411,7 +2459,6 @@ export {
24112459
expandURLPreview,
24122460
markCommentAsUnread,
24132461
readNewestAction,
2414-
readOldestAction,
24152462
openReport,
24162463
openReportFromDeepLink,
24172464
navigateToAndOpenReport,
@@ -2436,6 +2483,8 @@ export {
24362483
getReportPrivateNote,
24372484
clearPrivateNotesError,
24382485
hasErrorInPrivateNotes,
2486+
getOlderActions,
2487+
getNewerActions,
24392488
openRoomMembersPage,
24402489
savePrivateNotesDraft,
24412490
getDraftPrivateNote,

src/pages/home/ReportScreen.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ const defaultProps = {
106106
hasOutstandingIOU: false,
107107
},
108108
reportMetadata: {
109-
isLoadingReportActions: true,
110-
isLoadingMoreReportActions: false,
109+
isLoadingInitialReportActions: true,
110+
isLoadingOlderReportActions: false,
111+
isLoadingNewerReportActions: false,
111112
},
112113
isComposerFullSize: false,
113114
betas: [],
@@ -165,7 +166,7 @@ function ReportScreen({
165166
const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}];
166167

167168
// There are no reportActions at all to display and we are still in the process of loading the next set of actions.
168-
const isLoadingInitialReportActions = _.isEmpty(reportActions) && reportMetadata.isLoadingReportActions;
169+
const isLoadingInitialReportActions = _.isEmpty(reportActions) && reportMetadata.isLoadingInitialReportActions;
169170

170171
const isOptimisticDelete = lodashGet(report, 'statusNum') === CONST.REPORT.STATUS.CLOSED;
171172

@@ -260,6 +261,13 @@ function ReportScreen({
260261
const onSubmitComment = useCallback(
261262
(text) => {
262263
Report.addComment(getReportID(route), text);
264+
265+
// We need to scroll to the bottom of the list after the comment is added
266+
const refID = setTimeout(() => {
267+
flatListRef.current.scrollToOffset({animated: false, offset: 0});
268+
}, 10);
269+
270+
return () => clearTimeout(refID);
263271
},
264272
[route],
265273
);
@@ -372,7 +380,7 @@ function ReportScreen({
372380

373381
// eslint-disable-next-line rulesdir/no-negated-variables
374382
const shouldShowNotFoundPage = useMemo(
375-
() => (!firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata.isLoadingReportActions && !isLoading && !userLeavingStatus) || shouldHideReport,
383+
() => (!firstRenderRef.current && !report.reportID && !isOptimisticDelete && !reportMetadata.isLoadingInitialReportActions && !isLoading && !userLeavingStatus) || shouldHideReport,
376384
[report, reportMetadata, isLoading, shouldHideReport, isOptimisticDelete, userLeavingStatus],
377385
);
378386

@@ -428,8 +436,9 @@ function ReportScreen({
428436
<ReportActionsView
429437
reportActions={reportActions}
430438
report={report}
431-
isLoadingReportActions={reportMetadata.isLoadingReportActions}
432-
isLoadingMoreReportActions={reportMetadata.isLoadingMoreReportActions}
439+
isLoadingInitialReportActions={reportMetadata.isLoadingInitialReportActions}
440+
isLoadingNewerReportActions={reportMetadata.isLoadingNewerReportActions}
441+
isLoadingOlderReportActions={reportMetadata.isLoadingOlderReportActions}
433442
isComposerFullSize={isComposerFullSize}
434443
policy={policy}
435444
/>
@@ -488,8 +497,9 @@ export default compose(
488497
reportMetadata: {
489498
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`,
490499
initialValue: {
491-
isLoadingReportActions: true,
492-
isLoadingMoreReportActions: false,
500+
isLoadingInitialReportActions: true,
501+
isLoadingOlderReportActions: false,
502+
isLoadingNewerReportActions: false,
493503
},
494504
},
495505
isComposerFullSize: {

0 commit comments

Comments
 (0)