From c95398cc15aa1903886806d7ca59308edb85313d Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 19 Apr 2024 15:39:05 -0400 Subject: [PATCH 1/3] Handle pagination errors in chats --- src/languages/en.ts | 4 + src/languages/es.ts | 4 + src/libs/API/types.ts | 2 +- src/libs/actions/Report.ts | 6 + src/pages/home/ReportScreen.tsx | 4 + src/pages/home/report/ListBoundaryLoader.tsx | 49 ++++- src/pages/home/report/ReportActionsList.tsx | 44 +++- src/pages/home/report/ReportActionsView.tsx | 199 ++++++++++++------- src/styles/index.ts | 11 +- src/types/onyx/ReportMetadata.ts | 6 + 10 files changed, 244 insertions(+), 85 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5e2d1360e816..805d3a51de62 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2938,4 +2938,8 @@ 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', + }, } satisfies TranslationBase; diff --git a/src/languages/es.ts b/src/languages/es.ts index d82fabb80799..f3bb47b5afd3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3434,4 +3434,8 @@ 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', + }, } satisfies EnglishTranslation; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index b38ef2b1f7c6..5c8be2dbe8c3 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -193,7 +193,7 @@ const WRITE_COMMANDS = { ACCEPT_JOIN_REQUEST: 'AcceptJoinRequest', DECLINE_JOIN_REQUEST: 'DeclineJoinRequest', CREATE_POLICY_TAX: 'CreatePolicyTax', - UPDATE_POLICY_CONNECTION_CONFIG: 'UpdatePolicyConnectionConfiguration', + UPDATE_POLICY_CONNECTION_CONFIG: 'UpdatePolicyConnectionConfig', REMOVE_POLICY_CONNECTION: 'RemovePolicyConnection', SET_POLICY_TAXES_ENABLED: 'SetPolicyTaxesEnabled', DELETE_POLICY_TAXES: 'DeletePolicyTaxes', diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 34335c797212..00f3935fb3d8 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -696,7 +696,9 @@ function openReport( value: { isLoadingInitialReportActions: true, isLoadingOlderReportActions: false, + hasLoadingOlderReportActionsError: false, isLoadingNewerReportActions: false, + hasLoadingNewerReportActionsError: false, lastVisitTime: DateUtils.getDBTime(), }, }, @@ -988,6 +990,7 @@ function getOlderActions(reportID: string, reportActionID: string) { key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, value: { isLoadingOlderReportActions: true, + hasLoadingOlderReportActionsError: false, }, }, ]; @@ -1008,6 +1011,7 @@ function getOlderActions(reportID: string, reportActionID: string) { key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, value: { isLoadingOlderReportActions: false, + hasLoadingOlderReportActionsError: true, }, }, ]; @@ -1031,6 +1035,7 @@ function getNewerActions(reportID: string, reportActionID: string) { key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, value: { isLoadingNewerReportActions: true, + hasLoadingNewerReportActionsError: false, }, }, ]; @@ -1051,6 +1056,7 @@ function getNewerActions(reportID: string, reportActionID: string) { key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, value: { isLoadingNewerReportActions: false, + hasLoadingNewerReportActionsError: true, }, }, ]; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index aa7c623ac2b4..e02a8d1ee115 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -137,7 +137,9 @@ function ReportScreen({ reportMetadata = { isLoadingInitialReportActions: true, isLoadingOlderReportActions: false, + hasLoadingOlderReportActionsError: false, isLoadingNewerReportActions: false, + hasLoadingNewerReportActionsError: false, }, parentReportActions, accountManagerReportID, @@ -696,7 +698,9 @@ function ReportScreen({ parentReportAction={parentReportAction} isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions} isLoadingNewerReportActions={reportMetadata?.isLoadingNewerReportActions} + hasLoadingNewerReportActionsError={reportMetadata?.hasLoadingNewerReportActionsError} isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions} + hasLoadingOlderReportActionsError={reportMetadata?.hasLoadingOlderReportActionsError} isReadyForCommentLinking={!shouldShowSkeleton} transactionThreadReportID={transactionThreadReportID} /> diff --git a/src/pages/home/report/ListBoundaryLoader.tsx b/src/pages/home/report/ListBoundaryLoader.tsx index a359606b9ed5..8081e18bb668 100644 --- a/src/pages/home/report/ListBoundaryLoader.tsx +++ b/src/pages/home/report/ListBoundaryLoader.tsx @@ -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'; @@ -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({ @@ -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 ( + + {translate('listBoundary.errorMessage')} + {!isOffline && ( +