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
63 changes: 63 additions & 0 deletions src/components/BlockingViews/FullPageErrorView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {View} from 'react-native';
import type {StyleProp, TextStyle} from 'react-native';
import * as Illustrations from '@components/Icon/Illustrations';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import BlockingView from './BlockingView';
import ForceFullScreenView from './ForceFullScreenView';

type FullPageErrorViewProps = {
/** TestID for test */
testID?: string;

/** Child elements */
children?: React.ReactNode;

/** If true, child components are replaced with a blocking "error page" view */
shouldShow?: boolean;

/** The title text to be displayed */
title?: string;

/** The subtitle text to be displayed */
subtitle?: string;

/** Whether we should force the full page view */
shouldForceFullScreen?: boolean;

/** The style of the subtitle message */
subtitleStyle?: StyleProp<TextStyle>;
};

// eslint-disable-next-line rulesdir/no-negated-variables
function FullPageErrorView({testID, children = null, shouldShow = false, title = '', subtitle = '', shouldForceFullScreen = false, subtitleStyle}: FullPageErrorViewProps) {
const styles = useThemeStyles();

if (shouldShow) {
return (
<ForceFullScreenView shouldForceFullScreen={shouldForceFullScreen}>
<View
style={[styles.flex1, styles.blockingErrorViewContainer]}
testID={testID}
>
<BlockingView
icon={Illustrations.BrokenMagnifyingGlass}
iconWidth={variables.errorPageIconWidth}
iconHeight={variables.errorPageIconHeight}
title={title}
subtitle={subtitle}
subtitleStyle={subtitleStyle}
/>
</View>
</ForceFullScreenView>
);
}

return children;
}

FullPageErrorView.displayName = 'FullPageErrorView';

export type {FullPageErrorViewProps};
export default FullPageErrorView;
10 changes: 10 additions & 0 deletions src/components/Search/SearchPageHeader/SearchStatusBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {useSearchContext} from '@components/Search/SearchContext';
import type {ChatSearchStatus, ExpenseSearchStatus, InvoiceSearchStatus, SearchQueryJSON, TripSearchStatus} from '@components/Search/types';
import SearchStatusSkeleton from '@components/Skeletons/SearchStatusSkeleton';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSingleExecution from '@hooks/useSingleExecution';
import useStyleUtils from '@hooks/useStyleUtils';
Expand Down Expand Up @@ -179,10 +180,19 @@ function SearchStatusBar({queryJSON, onStatusChange, headerButtonsOptions}: Sear
const scrollRef = useRef<RNScrollView>(null);
const isScrolledRef = useRef(false);
const {shouldShowStatusBarLoading} = useSearchContext();
const {hash} = queryJSON;
const [currentSearchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);
const {isOffline} = useNetwork();

const selectedTransactionsKeys = useMemo(() => Object.keys(selectedTransactions ?? {}), [selectedTransactions]);
const shouldShowSelectedDropdown = headerButtonsOptions.length > 0 && (!shouldUseNarrowLayout || (!!selectionMode && selectionMode.isEnabled));

const hasErrors = Object.keys(currentSearchResults?.errors ?? {}).length > 0 && !isOffline;

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.

@huult QQ: Why are we not showing errors when we are offline?

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.

We don't run any search offline, so I'm not sure there's a use case for this

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.

@shubham1206agra This error is from the API response, so offline mode cannot be used for this.

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.

@huult What's the issue here? Can you write the steps here.

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.

I'm not sure that I understand @shubham1206agra. As @huult pointed out, we only show this error when the API response fails, so we need to be online to do so.


if (hasErrors) {
return null;
}

if (shouldShowStatusBarLoading) {
return <SearchStatusSkeleton shouldAnimate />;
}
Expand Down
18 changes: 18 additions & 0 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle, ViewToken} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FullPageErrorView from '@components/BlockingViews/FullPageErrorView';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import SearchTableHeader from '@components/SelectionList/SearchTableHeader';
import type {ReportActionListItemType, ReportListItemType, TransactionListItemType} from '@components/SelectionList/types';
import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
import useLocalize from '@hooks/useLocalize';
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
import useNetwork from '@hooks/useNetwork';
import usePermissions from '@hooks/usePermissions';
Expand Down Expand Up @@ -140,6 +142,7 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS
const previousTransactions = usePrevious(transactions);
const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS);
const previousReportActions = usePrevious(reportActions);
const {translate} = useLocalize();
const shouldGroupByReports = groupBy === CONST.SEARCH.GROUP_BY.REPORTS;

const {canUseTableReportView} = usePermissions();
Expand Down Expand Up @@ -386,6 +389,21 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS
return mapToItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple, shouldAnimateInHighlight);
});

const hasErrors = Object.keys(searchResults?.errors ?? {}).length > 0 && !isOffline;

if (hasErrors) {
return (
<View style={[shouldUseNarrowLayout ? styles.searchListContentContainerStyles : styles.mt3, styles.flex1]}>
<FullPageErrorView
shouldShow
subtitleStyle={styles.textSupporting}
title={translate('errorPage.title', {isBreakline: !!shouldUseNarrowLayout})}
subtitle={translate('errorPage.subtitle')}
/>
</View>
);
}

if (shouldShowEmptyState(isDataLoaded, data.length, searchResults.search.type) && isFocused) {
return (
<View style={[shouldUseNarrowLayout ? styles.searchListContentContainerStyles : styles.mt3, styles.flex1]}>
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2026,6 +2026,10 @@ const translations = {
noAccess: "That chat doesn't exist or you don't have access to it. Try using search to find a chat.",
goBackHome: 'Go back to home page',
},
errorPage: {
title: ({isBreakline}: {isBreakline: boolean}) => `Oops... ${isBreakline ? '\n' : ''}Something went wrong`,
subtitle: 'Your request could not be completed. Please try again later.',
},
setPasswordPage: {
enterPassword: 'Enter a password',
setPassword: 'Set password',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,10 @@ const translations = {
noAccess: 'Ese chat no existe o no tienes acceso a él. Intenta usar la búsqueda para encontrar un chat.',
goBackHome: 'Volver a la página principal',
},
errorPage: {
title: ({isBreakline}: {isBreakline: boolean}) => `Ups... ${isBreakline ? '\n' : ''}Algo no ha ido bien`,
subtitle: 'No se ha podido completar la acción. Por favor, inténtalo más tarde.',
},
setPasswordPage: {
enterPassword: 'Escribe una contraseña',
setPassword: 'Configura tu contraseña',
Expand Down
8 changes: 8 additions & 0 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ function getOnyxLoadingData(hash: number, queryJSON?: SearchQueryJSON): {optimis
},
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
errors: null,
},
},
];

const finallyData: OnyxUpdate[] = [
Expand All @@ -143,6 +150,7 @@ function getOnyxLoadingData(hash: number, queryJSON?: SearchQueryJSON): {optimis
status: queryJSON?.status,
type: queryJSON?.type,
},
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
];
Expand Down
6 changes: 6 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2732,6 +2732,12 @@ const styles = (theme: ThemeColors) =>
alignSelf: 'center',
},

blockingErrorViewContainer: {
paddingBottom: variables.contentHeaderHeight,
maxWidth: 475,
alignSelf: 'center',
},

forcedBlockingViewContainer: {
...positioning.pFixed,
top: 0,
Expand Down
3 changes: 3 additions & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ export default {
searchTopBarHeight: 52,
searchRouterInputMargin: 52,

errorPageIconWidth: 116,
errorPageIconHeight: 168,

h20: 20,
h28: 28,
h36: 36,
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ type SearchResults = {

/** Whether search data is being fetched from server */
isLoading?: boolean;

/** Whether search data fetch has failed */
errors?: OnyxCommon.Errors;
};

export default SearchResults;
Expand Down