Skip to content

Commit

Permalink
Merge pull request #40610 from janicduplessis/@janic/pagination-errors
Browse files Browse the repository at this point in the history
Handle pagination errors in chats
  • Loading branch information
mountiny authored May 13, 2024
2 parents 9083c3e + 350c7e2 commit d796699
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 87 deletions.
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,10 @@ export default {
offline:
"You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.",
},
listBoundary: {
errorMessage: 'There was an error loading more messages.',
tryAgain: 'Try again',
},
systemMessage: {
mergedWithCashTransaction: 'matched a receipt to this transaction.',
},
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3534,6 +3534,10 @@ export default {
offline:
'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.',
},
listBoundary: {
errorMessage: 'Se produjo un error al cargar más mensajes.',
tryAgain: 'Inténtalo de nuevo',
},
systemMessage: {
mergedWithCashTransaction: 'encontró un recibo para esta transacción.',
},
Expand Down
6 changes: 6 additions & 0 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,9 @@ function openReport(
value: {
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: false,
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: false,
lastVisitTime: DateUtils.getDBTime(),
},
},
Expand Down Expand Up @@ -1038,6 +1040,7 @@ function getOlderActions(reportID: string, reportActionID: string) {
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
value: {
isLoadingOlderReportActions: true,
hasLoadingOlderReportActionsError: false,
},
},
];
Expand All @@ -1058,6 +1061,7 @@ function getOlderActions(reportID: string, reportActionID: string) {
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
value: {
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: true,
},
},
];
Expand All @@ -1081,6 +1085,7 @@ function getNewerActions(reportID: string, reportActionID: string) {
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
value: {
isLoadingNewerReportActions: true,
hasLoadingNewerReportActionsError: false,
},
},
];
Expand All @@ -1101,6 +1106,7 @@ function getNewerActions(reportID: string, reportActionID: string) {
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`,
value: {
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: true,
},
},
];
Expand Down
6 changes: 6 additions & 0 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ function ReportScreen({
reportMetadata = {
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: false,
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: false,
},
parentReportActions,
accountManagerReportID,
Expand Down Expand Up @@ -709,7 +711,9 @@ function ReportScreen({
parentReportAction={parentReportAction}
isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
isLoadingNewerReportActions={reportMetadata?.isLoadingNewerReportActions}
hasLoadingNewerReportActionsError={reportMetadata?.hasLoadingNewerReportActionsError}
isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
hasLoadingOlderReportActionsError={reportMetadata?.hasLoadingOlderReportActionsError}
isReadyForCommentLinking={!shouldShowSkeleton}
transactionThreadReportID={transactionThreadReportID}
/>
Expand Down Expand Up @@ -766,7 +770,9 @@ export default withCurrentReportID(
initialValue: {
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
hasLoadingOlderReportActionsError: false,
isLoadingNewerReportActions: false,
hasLoadingNewerReportActionsError: false,
},
},
isComposerFullSize: {
Expand Down
49 changes: 47 additions & 2 deletions src/pages/home/report/ListBoundaryLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import React, {useEffect} from 'react';
import {ActivityIndicator, View} from 'react-native';
import type {ValueOf} from 'type-fest';
import Button from '@components/Button';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -22,6 +25,12 @@ type ListBoundaryLoaderProps = {

/** Name of the last report action */
lastReportActionName?: string;

/** Shows if there was an error when loading report actions */
hasError?: boolean;

/** Function to retry if there was an error */
onRetry?: () => void;
};

function ListBoundaryLoader({
Expand All @@ -30,11 +39,47 @@ function ListBoundaryLoader({
isLoadingInitialReportActions = false,
lastReportActionName = '',
isLoadingNewerReportActions = false,
hasError = false,
onRetry,
}: ListBoundaryLoaderProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {isOffline} = useNetwork();
const {translate} = useLocalize();

// When retrying we want to show the loading state in the retry button so we
// have this separate state to handle that.
const [isRetrying, setIsRetrying] = React.useState(false);

const retry = () => {
setIsRetrying(true);
onRetry?.();
};

// Reset the retrying state once loading is done.
useEffect(() => {
if (isLoadingNewerReportActions || isLoadingOlderReportActions) {
return;
}

setIsRetrying(false);
}, [isLoadingNewerReportActions, isLoadingOlderReportActions]);

if (hasError || isRetrying) {
return (
<View style={[styles.alignItemsCenter, styles.justifyContentCenter, styles.listBoundaryError]}>
<Text style={styles.listBoundaryErrorText}>{translate('listBoundary.errorMessage')}</Text>
{!isOffline && (
<Button
small
onPress={retry}
text={translate('listBoundary.tryAgain')}
isLoading={isRetrying}
/>
)}
</View>
);
}
// We use two different loading components for the header and footer
// to reduce the jumping effect when the user is scrolling to the newer report actions
if (type === CONST.LIST_COMPONENTS.FOOTER) {
Expand All @@ -55,7 +100,7 @@ function ListBoundaryLoader({
// applied for a header of the list, i.e. when you scroll to the bottom of the list
// the styles for android and the rest components are different that's why we use two different components
return (
<View style={[styles.alignItemsCenter, styles.justifyContentCenter, styles.chatBottomLoader]}>
<View style={[styles.alignItemsCenter, styles.justifyContentCenter, styles.listBoundaryLoader]}>
<ActivityIndicator
color={theme.spinner}
size="small"
Expand Down
52 changes: 41 additions & 11 deletions src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,26 @@ type ReportActionsListProps = WithCurrentUserPersonalDetailsProps & {
/** Are we loading more report actions? */
isLoadingOlderReportActions?: boolean;

/** Was there an error when loading older report actions? */
hasLoadingOlderReportActionsError?: boolean;

/** Are we loading newer report actions? */
isLoadingNewerReportActions?: boolean;

/** Was there an error when loading newer report actions? */
hasLoadingNewerReportActionsError?: boolean;

/** Callback executed on list layout */
onLayout: (event: LayoutChangeEvent) => void;

/** Callback executed on scroll */
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;

/** Function to load more chats */
loadOlderChats: () => void;
loadOlderChats: (force?: boolean) => void;

/** Function to load newer chats */
loadNewerChats: () => void;
loadNewerChats: (force?: boolean) => void;

/** Whether the composer is in full size */
isComposerFullSize?: boolean;
Expand Down Expand Up @@ -139,7 +145,9 @@ function ReportActionsList({
parentReportAction,
isLoadingInitialReportActions = false,
isLoadingOlderReportActions = false,
hasLoadingOlderReportActionsError = false,
isLoadingNewerReportActions = false,
hasLoadingNewerReportActionsError = false,
sortedReportActions,
onScroll,
mostRecentIOUReportActionID = '',
Expand Down Expand Up @@ -615,11 +623,16 @@ function ReportActionsList({

const lastReportAction: OnyxTypes.ReportAction | EmptyObject = useMemo(() => sortedReportActions.at(-1) ?? {}, [sortedReportActions]);

const listFooterComponent = useCallback(() => {
const retryLoadOlderChatsError = useCallback(() => {
loadOlderChats(true);
}, [loadOlderChats]);

const listFooterComponent = useMemo(() => {
// Skip this hook on the first render (when online), as we are not sure if more actions are going to be loaded,
// Therefore showing the skeleton on footer might be misleading.
// When offline, there should be no second render, so we should show the skeleton if the corresponding loading prop is present
if (!isOffline && !hasFooterRendered.current) {
// When offline, there should be no second render, so we should show the skeleton if the corresponding loading prop is present.
// In case of an error we want to display the footer no matter what.
if (!isOffline && !hasFooterRendered.current && !hasLoadingOlderReportActionsError) {
hasFooterRendered.current = true;
return null;
}
Expand All @@ -630,9 +643,11 @@ function ReportActionsList({
isLoadingOlderReportActions={isLoadingOlderReportActions}
isLoadingInitialReportActions={isLoadingInitialReportActions}
lastReportActionName={lastReportAction.actionName}
hasError={hasLoadingOlderReportActionsError}
onRetry={retryLoadOlderChatsError}
/>
);
}, [isLoadingInitialReportActions, isLoadingOlderReportActions, lastReportAction.actionName, isOffline]);
}, [isLoadingInitialReportActions, isLoadingOlderReportActions, lastReportAction.actionName, isOffline, hasLoadingOlderReportActionsError, retryLoadOlderChatsError]);

const onLayoutInner = useCallback(
(event: LayoutChangeEvent) => {
Expand All @@ -647,8 +662,13 @@ function ReportActionsList({
[onContentSizeChange],
);

const listHeaderComponent = useCallback(() => {
if (!canShowHeader) {
const retryLoadNewerChatsError = useCallback(() => {
loadNewerChats(true);
}, [loadNewerChats]);

const listHeaderComponent = useMemo(() => {
// In case of an error we want to display the header no matter what.
if (!canShowHeader && !hasLoadingNewerReportActionsError) {
hasHeaderRendered.current = true;
return null;
}
Expand All @@ -657,9 +677,19 @@ function ReportActionsList({
<ListBoundaryLoader
type={CONST.LIST_COMPONENTS.HEADER}
isLoadingNewerReportActions={isLoadingNewerReportActions}
hasError={hasLoadingNewerReportActionsError}
onRetry={retryLoadNewerChatsError}
/>
);
}, [isLoadingNewerReportActions, canShowHeader]);
}, [isLoadingNewerReportActions, canShowHeader, hasLoadingNewerReportActionsError, retryLoadNewerChatsError]);

const onStartReached = useCallback(() => {
loadNewerChats(false);
}, [loadNewerChats]);

const onEndReached = useCallback(() => {
loadOlderChats(false);
}, [loadOlderChats]);

// When performing comment linking, initially 25 items are added to the list. Subsequent fetches add 15 items from the cache or 50 items from the server.
// This is to ensure that the user is able to see the 'scroll to newer comments' button when they do comment linking and have not reached the end of the list yet.
Expand All @@ -681,9 +711,9 @@ function ReportActionsList({
contentContainerStyle={contentContainerStyle}
keyExtractor={keyExtractor}
initialNumToRender={initialNumToRender}
onEndReached={loadOlderChats}
onEndReached={onEndReached}
onEndReachedThreshold={0.75}
onStartReached={loadNewerChats}
onStartReached={onStartReached}
onStartReachedThreshold={0.75}
ListFooterComponent={listFooterComponent}
ListHeaderComponent={listHeaderComponent}
Expand Down
Loading

0 comments on commit d796699

Please sign in to comment.