Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update RequestStepCategory to add Empty and Loading states for category list #41344

Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ const ROUTES = {
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_CATEGORIES: {
route: 'settings/:policyID/categories',
getRoute: (policyID: string) => `settings/${policyID}/categories` as const,
},
MONEY_REQUEST_STEP_CURRENCY: {
route: ':action/:iouType/currency/:transactionID/:reportID/:pageIndex?',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = '', currency = '', backTo = '') =>
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ const SCREENS = {
WAYPOINT: 'Money_Request_Waypoint',
EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint',
RECEIPT: 'Money_Request_Receipt',
CATEGORIES: 'Money_Request_Categories',
STATE_SELECTOR: 'Money_Request_State_Selector',
},

Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,7 @@ export default {
createFailureMessage: 'An error occurred while creating the category, please try again.',
addCategory: 'Add category',
editCategory: 'Edit category',
editCategories: 'Edit categories',
categoryRequiredError: 'Category name is required.',
existingCategoryError: 'A category with this name already exists.',
invalidCategoryName: 'Invalid category name.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,7 @@ export default {
createFailureMessage: 'Se ha producido un error al intentar crear la categoría. Por favor, inténtalo más tarde.',
addCategory: 'Añadir categoría',
editCategory: 'Editar categoría',
editCategories: 'Editar categorías',
categoryRequiredError: 'Lo nombre de la categoría es obligatorio.',
existingCategoryError: 'Ya existe una categoría con este nombre.',
invalidCategoryName: 'Lo nombre de la categoría es invalido.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator<MoneyRequestNa
[SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE]: () => require('@pages/iou/request/step/IOURequestStepDistanceRate').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_MERCHANT]: () => require('../../../../pages/iou/request/step/IOURequestStepMerchant').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_PARTICIPANTS]: () => require('../../../../pages/iou/request/step/IOURequestStepParticipants').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.CATEGORIES]: () => require('../../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_SCAN]: () => require('../../../../pages/iou/request/step/IOURequestStepScan').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_TAG]: () => require('../../../../pages/iou/request/step/IOURequestStepTag').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: () => require('../../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
},
},
},
[SCREENS.MONEY_REQUEST.CATEGORIES]: ROUTES.MONEY_REQUEST_CATEGORIES.route,
[SCREENS.MONEY_REQUEST.STEP_SEND_FROM]: ROUTES.MONEY_REQUEST_STEP_SEND_FROM.route,
[SCREENS.MONEY_REQUEST.STEP_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_AMOUNT.route,
[SCREENS.MONEY_REQUEST.STEP_CATEGORY]: ROUTES.MONEY_REQUEST_STEP_CATEGORY.route,
Expand Down
58 changes: 50 additions & 8 deletions src/pages/iou/request/step/IOURequestStepCategory.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import lodashIsEmpty from 'lodash/isEmpty';
import React, {useEffect} from 'react';
import {ActivityIndicator, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import BlockingView from '@components/BlockingViews/BlockingView';
import Button from '@components/Button';
import CategoryPicker from '@components/CategoryPicker';
import FixedFooter from '@components/FixedFooter';
import * as Illustrations from '@components/Icon/Illustrations';
import type {ListItem} from '@components/SelectionList/types';
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';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
import * as PolicyActions from '@userActions/Policy';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -63,6 +70,7 @@ function IOURequestStepCategory({
session,
}: IOURequestStepCategoryProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const isEditing = action === CONST.IOU.ACTION.EDIT;
const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT;
Expand All @@ -78,7 +86,7 @@ function IOURequestStepCategory({
const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT;
const canEditSplitBill = isSplitBill && reportAction && session?.accountID === reportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction);
// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = !shouldShowCategory || (isEditing && (isSplitBill ? !canEditSplitBill : !ReportUtils.canEditMoneyRequest(reportAction)));
const shouldShowNotFoundPage = isEditing && (isSplitBill ? !canEditSplitBill : !ReportUtils.canEditMoneyRequest(reportAction));

const fetchData = () => {
if (policy && policyCategories) {
Expand All @@ -87,7 +95,9 @@ function IOURequestStepCategory({

PolicyActions.openDraftWorkspaceRequest(report?.policyID ?? '');
};
useNetwork({onReconnect: fetchData});
const {isOffline} = useNetwork({onReconnect: fetchData});
const isLoading = !isOffline && policyCategories === undefined;
const shouldShowEmptyState = !isLoading && !shouldShowCategory;

useEffect(() => {
fetchData();
Expand Down Expand Up @@ -137,12 +147,44 @@ function IOURequestStepCategory({
testID={IOURequestStepCategory.displayName}
includeSafeAreaPaddingBottom={false}
>
<Text style={[styles.ph5, styles.pv3]}>{translate('iou.categorySelection')}</Text>
<CategoryPicker
selectedCategory={transactionCategory}
policyID={report?.policyID ?? ''}
onSubmit={updateCategory}
/>
{isLoading && (
<ActivityIndicator
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
style={[styles.flex1]}
color={theme.spinner}
/>
)}
{shouldShowEmptyState && (
<View style={[styles.flex1]}>
<BlockingView
icon={Illustrations.EmptyStateExpenses}
iconWidth={variables.modalTopIconWidth}
Copy link
Contributor

Choose a reason for hiding this comment

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

Coming from #43064. This caused a regression where the empty folder icon was not centred. This is because the width and height values used here are incorrect for the icon used.

iconHeight={variables.modalTopIconHeight}
title={translate('workspace.categories.emptyCategories.title')}
subtitle={translate('workspace.categories.emptyCategories.subtitle')}
/>
<FixedFooter style={[styles.mtAuto, styles.pt5]}>
<Button
large
success
style={[styles.w100]}
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_CATEGORIES.getRoute(policy?.id ?? ''))}
text={translate('workspace.categories.editCategories')}
pressOnEnter
/>
</FixedFooter>
</View>
)}
{!shouldShowEmptyState && !isLoading && (
<>
<Text style={[styles.ph5, styles.pv3]}>{translate('iou.categorySelection')}</Text>
<CategoryPicker
selectedCategory={transactionCategory}
policyID={report?.policyID ?? ''}
onSubmit={updateCategory}
/>
</>
)}
</StepScreenWrapper>
);
}
Expand Down
11 changes: 9 additions & 2 deletions src/pages/workspace/categories/CategoryForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ type CategoryFormProps = {

/** Function to validate the edited values of the form */
validateEdit?: (values: FormOnyxValues<typeof ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM>) => FormInputErrors<typeof ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM>;

/** Should go back after submitting the form */
isShouldGoBackOnSubmit?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prefixing should is enough to understand it's a boolean.

Suggested change
isShouldGoBackOnSubmit?: boolean;
shouldGoBackOnSubmit?: boolean;

};

function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit}: CategoryFormProps) {
function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit, isShouldGoBackOnSubmit}: CategoryFormProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {inputCallbackRef} = useAutoFocusInput();
Expand Down Expand Up @@ -60,9 +63,13 @@ function CategoryForm({onSubmit, policyCategories, categoryName, validateEdit}:
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM>) => {
onSubmit(values);
Keyboard.dismiss();
if (isShouldGoBackOnSubmit) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

in which scenarios would we not go back?

Copy link
Contributor Author

@ZhenjaHorbach ZhenjaHorbach May 5, 2024

Choose a reason for hiding this comment

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

Actually these changes are not necessary
I added other changes related to navigation
Because categories are tied to workspace screen
And after reloading the page with open categories we always navigate to workspace screen

[SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS, SCREENS.WORKSPACE.CATEGORY_EDIT],

Navigation.goBack();
return;
}
Navigation.dismissModal();
},
[onSubmit],
[isShouldGoBackOnSubmit, onSubmit],
);

return (
Expand Down
1 change: 1 addition & 0 deletions src/pages/workspace/categories/CreateCategoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps)
onBackButtonPress={Navigation.goBack}
/>
<CategoryForm
isShouldGoBackOnSubmit
onSubmit={createCategory}
policyCategories={policyCategories}
/>
Expand Down
20 changes: 10 additions & 10 deletions src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import localeCompare from '@libs/LocaleCompare';
Expand All @@ -46,7 +46,7 @@ type PolicyOption = ListItem & {
type WorkspaceCategoriesPageProps = StackScreenProps<WorkspacesCentralPaneNavigatorParamList, typeof SCREENS.WORKSPACE.CATEGORIES>;

function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
const {isSmallScreenWidth} = useWindowDimensions();
const {isInModal} = useResponsiveLayout();
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
Expand Down Expand Up @@ -210,35 +210,35 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
customText={translate('workspace.common.selected', {selectedNumber: selectedCategoriesArray.length})}
options={options}
isSplitButton={false}
style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]}
style={[isInModal && styles.flexGrow1, isInModal && styles.mb3]}
/>
);
}

return (
<View style={[styles.w100, styles.flexRow, isSmallScreenWidth && styles.mb3]}>
<View style={[styles.w100, styles.flexRow, isInModal && styles.mb3]}>
{!PolicyUtils.hasAccountingConnections(policy) && (
<Button
medium
success
onPress={navigateToCreateCategoryPage}
icon={Expensicons.Plus}
text={translate('workspace.categories.addCategory')}
style={[styles.mr3, isSmallScreenWidth && styles.w50]}
style={[styles.mr3, isInModal && styles.w50]}
/>
)}
<Button
medium
onPress={navigateToCategoriesSettings}
icon={Expensicons.Gear}
text={translate('common.settings')}
style={[isSmallScreenWidth && styles.w50]}
style={[isInModal && styles.w50]}
/>
</View>
);
};

const isLoading = !isOffline && policyCategories === undefined;
const isLoading = !isOffline && policyCategories === null;
Copy link
Contributor

Choose a reason for hiding this comment

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

@ZhenjaHorbach why did you change it? Right now loading state is not working properly. See here. Reverting this change fixes the issue

Copy link
Contributor Author

@ZhenjaHorbach ZhenjaHorbach Jul 2, 2024

Choose a reason for hiding this comment

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

This is a known issue
But yes, I may have tested some functionality
But then I forgot to remove these changes 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Coming from #44224. This caused a regression that a loader is not displayed
To fix this we need to use undefined in case of an empty onyx state as in other places


const shouldShowEmptyState = !categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) && !isLoading;

Expand All @@ -258,9 +258,9 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
<HeaderWithBackButton
icon={Illustrations.FolderOpen}
title={translate('workspace.common.categories')}
shouldShowBackButton={isSmallScreenWidth}
shouldShowBackButton={isInModal}
>
{!isSmallScreenWidth && getHeaderButtons()}
{!isInModal && getHeaderButtons()}
</HeaderWithBackButton>
<ConfirmModal
isVisible={deleteCategoriesConfirmModalVisible}
Expand All @@ -272,7 +272,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
cancelText={translate('common.cancel')}
danger
/>
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
{isInModal && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
{Object.keys(policy?.connections ?? {}).length > 0 ? (
<Text>
Expand Down
Loading