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
5 changes: 5 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,11 @@ const ROUTES = {
return getUrlWithBackToParam(baseRoute, backTo);
},
},
SET_DEFAULT_WORKSPACE: {
route: 'set-default-workspace',
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (backTo?: string) => getUrlWithBackToParam('set-default-workspace', backTo),
},
REPORT: 'r',
REPORT_WITH_ID: {
route: 'r/:reportID?/:reportActionID?',
Expand Down
5 changes: 5 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ const SCREENS = {
DETAILS: 'Details',
PROFILE: 'Profile',
NEW_REPORT_WORKSPACE_SELECTION: 'New_Report_Workspace_Selection',
SET_DEFAULT_WORKSPACE: 'Set_Default_Workspace',
REPORT_DETAILS: 'Report_Details',
REPORT_CHANGE_WORKSPACE: 'ReportChangeWorkspace',
WORKSPACE_CONFIRMATION: 'Workspace_Confirmation',
Expand Down Expand Up @@ -400,6 +401,10 @@ const SCREENS = {
ROOT: 'NewReportWorkspaceSelection_Root',
},

SET_DEFAULT_WORKSPACE: {
ROOT: 'SetDefaultWorkspace_Root',
},

REPORT_DETAILS: {
ROOT: 'Report_Details_Root',
SHARE_CODE: 'Report_Details_Share_Code',
Expand Down
22 changes: 18 additions & 4 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@
// Flags for showing categories and tags
// transactionCategory can be an empty string
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const shouldShowCategory = isPolicyExpenseChat && (categoryForDisplay || hasEnabledOptions(policyCategories ?? {}));
const shouldShowCategory =
(isPolicyExpenseChat && (categoryForDisplay || hasEnabledOptions(policyCategories ?? {}))) || (isExpenseUnreported && (!policyForMovingExpenses || policy?.areCategoriesEnabled));
// transactionTag can be an empty string
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const shouldShowTag = isPolicyExpenseChat && (transactionTag || hasEnabledTags(policyTagLists));
Expand Down Expand Up @@ -308,6 +309,8 @@
amountTitle = translate('iou.receiptStatusTitle');
}

const shouldNavigateToUpgradePath = !policyForMovingExpenses && !shouldSelectPolicy;

const updatedTransactionDescription = useMemo(() => {
if (!updatedTransaction) {
return undefined;
Expand Down Expand Up @@ -542,7 +545,7 @@

const getAttendeesTitle = useMemo(() => {
return Array.isArray(actualAttendees) ? actualAttendees.map((item) => item?.displayName ?? item?.login).join(', ') : '';
}, [transactionAttendees]);

Check warning on line 548 in src/components/ReportActionItem/MoneyRequestView.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useMemo has a missing dependency: 'actualAttendees'. Either include it or remove the dependency array
const attendeesCopyValue = !canEdit ? getAttendeesTitle : undefined;

const previousTagLength = getLengthOfTag(previousTag ?? '');
Expand Down Expand Up @@ -754,17 +757,28 @@
shouldShowRightIcon={canEdit}
titleStyle={styles.flex1}
onPress={() => {
if (!policy) {
if (shouldNavigateToUpgradePath) {
Navigation.navigate(
ROUTES.MONEY_REQUEST_UPGRADE.getRoute({
action: CONST.IOU.ACTION.EDIT,
iouType,
transactionID: transaction.transactionID,
reportID: report.reportID,
backTo: getReportRHPActiveRoute(),
upgradePath: CONST.UPGRADE_PATHS.CATEGORIES,
}),
);
} else if (!policy && shouldSelectPolicy) {
Navigation.navigate(
ROUTES.SET_DEFAULT_WORKSPACE.getRoute(
ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(
CONST.IOU.ACTION.EDIT,
iouType,
transaction.transactionID,
report.reportID,
getReportRHPActiveRoute(),
),
),
);
} else {
Navigation.navigate(
ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction.transactionID, report.reportID, getReportRHPActiveRoute()),
Expand Down Expand Up @@ -905,7 +919,7 @@
if (!canEditReport) {
return;
}
if (!policyForMovingExpenses && !shouldSelectPolicy) {
if (shouldNavigateToUpgradePath) {
Navigation.navigate(
ROUTES.MONEY_REQUEST_UPGRADE.getRoute({
iouType,
Expand Down
40 changes: 28 additions & 12 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {accountIDSelector} from '@selectors/Session';
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Animated, {FadeIn, FadeOut, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import FullPageErrorView from '@components/BlockingViews/FullPageErrorView';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
Expand All @@ -13,6 +13,7 @@ import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
import {WideRHPContext} from '@components/WideRHPContextProvider';
import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet';
import useCardFeedsForDisplay from '@hooks/useCardFeedsForDisplay';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
Expand All @@ -30,6 +31,7 @@ import Log from '@libs/Log';
import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types';
import Performance from '@libs/Performance';
import {getPolicyRole, isPaidGroupPolicy as isPaidGroupPolicyPolicyUtils} from '@libs/PolicyUtils';
import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils';
import {canEditFieldOfMoneyRequest, selectFilteredReportActions} from '@libs/ReportUtils';
import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils';
Expand Down Expand Up @@ -91,6 +93,7 @@ function mapTransactionItemToSelectedEntry(
item: TransactionListItemType,
reportActions: ReportAction[],
outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue,
canMoveExpense = true,
): [string, SelectedTransactionInfo] {
return [
item.keyForList,
Expand All @@ -106,7 +109,7 @@ function mapTransactionItemToSelectedEntry(
undefined,
undefined,
outstandingReportsByPolicyID,
true,
canMoveExpense,
),
action: item.action,
reportID: item.reportID,
Expand Down Expand Up @@ -167,6 +170,7 @@ function prepareTransactionsList(
selectedTransactions: SelectedTransactions,
reportActions: ReportAction[],
outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue,
canMoveExpense = true,
) {
if (selectedTransactions[item.keyForList]?.isSelected) {
const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions;
Expand All @@ -188,7 +192,7 @@ function prepareTransactionsList(
undefined,
undefined,
outstandingReportsByPolicyID,
true,
canMoveExpense,
),
action: item.action,
reportID: item.reportID,
Expand All @@ -202,6 +206,10 @@ function prepareTransactionsList(
};
}

const activePaidPoliciesSelector = (allPolicies: OnyxCollection<Policy>): OnyxCollection<Policy> => {
return Object.fromEntries(Object.entries(allPolicies ?? {}).filter(([, userPolicy]) => isPaidGroupPolicyPolicyUtils(userPolicy)));
};

function Search({queryJSON, searchResults, onSearchListScroll, contentContainerStyle, handleSearch, isMobileSelectionModeEnabled, onSortPressedCallback}: SearchProps) {
const {type, status, sortBy, sortOrder, hash, similarSearchHash, groupBy} = queryJSON;
const {isOffline} = useNetwork();
Expand Down Expand Up @@ -237,6 +245,14 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
const [outstandingReportsByPolicyID] = useOnyx(ONYXKEYS.DERIVED.OUTSTANDING_REPORTS_BY_POLICY_ID, {canBeMissing: true});
const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true});

const [activePaidPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
selector: activePaidPoliciesSelector,
canBeMissing: true,
});

const {login: currentUserLogin} = useCurrentUserPersonalDetails();
const isUserWorkspaceMember = !!Object.values(activePaidPolicies ?? {}).filter((userPolicy) => getPolicyRole(userPolicy, currentUserLogin)).length;

// Filter violations based on user visibility
const filteredViolations = useMemo(() => {
if (!violations || !searchResults?.data) {
Expand Down Expand Up @@ -444,7 +460,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
undefined,
undefined,
outstandingReportsByPolicyID,
true,
isUserWorkspaceMember,
),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isSelected: areAllMatchingItemsSelected || selectedTransactions[transaction.transactionID].isSelected,
Expand Down Expand Up @@ -477,7 +493,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
undefined,
undefined,
outstandingReportsByPolicyID,
true,
isUserWorkspaceMember,
),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isSelected: areAllMatchingItemsSelected || selectedTransactions[transaction.transactionID].isSelected,
Expand All @@ -499,7 +515,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS

isRefreshingSelection.current = true;
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [data, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID]);
}, [data, setSelectedTransactions, areAllMatchingItemsSelected, isFocused, outstandingReportsByPolicyID, isUserWorkspaceMember]);

useEffect(() => {
if (!isSearchResultsEmpty || prevIsSearchResultEmpty) {
Expand Down Expand Up @@ -559,7 +575,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
if (isTransactionPendingDelete(item)) {
return;
}
setSelectedTransactions(prepareTransactionsList(item, selectedTransactions, reportActionsArray, outstandingReportsByPolicyID), data);
setSelectedTransactions(prepareTransactionsList(item, selectedTransactions, reportActionsArray, outstandingReportsByPolicyID, isUserWorkspaceMember), data);
return;
}

Expand All @@ -581,13 +597,13 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
...Object.fromEntries(
currentTransactions
.filter((t) => !isTransactionPendingDelete(t))
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID)),
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID, isUserWorkspaceMember)),
),
},
data,
);
},
[data, reportActionsArray, selectedTransactions, outstandingReportsByPolicyID, setSelectedTransactions],
[setSelectedTransactions, selectedTransactions, data, reportActionsArray, outstandingReportsByPolicyID, isUserWorkspaceMember],
);

const onSelectRow = useCallback(
Expand Down Expand Up @@ -794,7 +810,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
(data as TransactionGroupListItemType[]).flatMap((item) =>
item.transactions
.filter((t) => !isTransactionPendingDelete(t))
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID)),
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID, isUserWorkspaceMember)),
),
),
data,
Expand All @@ -807,11 +823,11 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
Object.fromEntries(
(data as TransactionListItemType[])
.filter((t) => !isTransactionPendingDelete(t))
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID)),
.map((transactionItem) => mapTransactionItemToSelectedEntry(transactionItem, reportActionsArray, outstandingReportsByPolicyID, isUserWorkspaceMember)),
),
data,
);
}, [clearSelectedTransactions, data, groupBy, reportActionsArray, selectedTransactions, setSelectedTransactions, outstandingReportsByPolicyID]);
}, [clearSelectedTransactions, data, groupBy, isUserWorkspaceMember, reportActionsArray, selectedTransactions, setSelectedTransactions, outstandingReportsByPolicyID]);

const onLayout = useCallback(() => handleSelectionListScroll(sortedSelectedData, searchListRef.current), [handleSelectionListScroll, sortedSelectedData]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import type {
SearchAdvancedFiltersParamList,
SearchReportParamList,
SearchSavedSearchParamList,
SetDefaultWorkspaceNavigatorParamList,
SettingsNavigatorParamList,
ShareNavigatorParamList,
SignInNavigatorParamList,
Expand Down Expand Up @@ -212,6 +213,10 @@ const NewReportWorkspaceSelectionModalStackNavigator = createModalStackNavigator
[SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: () => require<ReactComponentModule>('../../../../pages/NewReportWorkspaceSelectionPage').default,
});

const SetDefaultWorkspaceModalStackNavigator = createModalStackNavigator<SetDefaultWorkspaceNavigatorParamList>({
[SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: () => require<ReactComponentModule>('../../../../pages/SetDefaultWorkspacePage').default,
});

const ReportDetailsModalStackNavigator = createModalStackNavigator<ReportDetailsNavigatorParamList>({
[SCREENS.REPORT_DETAILS.ROOT]: () => require<ReactComponentModule>('../../../../pages/ReportDetailsPage').default,
[SCREENS.REPORT_DETAILS.SHARE_CODE]: () => require<ReactComponentModule>('../../../../pages/home/report/ReportDetailsShareCodePage').default,
Expand Down Expand Up @@ -925,6 +930,7 @@ export {
ReferralModalStackNavigator,
TravelModalStackNavigator,
NewReportWorkspaceSelectionModalStackNavigator,
SetDefaultWorkspaceModalStackNavigator,
ReportDescriptionModalStackNavigator,
ReportDetailsModalStackNavigator,
ReportChangeWorkspaceModalStackNavigator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) {
name={SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION}
component={ModalStackNavigators.NewReportWorkspaceSelectionModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.SET_DEFAULT_WORKSPACE}
component={ModalStackNavigators.SetDefaultWorkspaceModalStackNavigator}
/>
<Stack.Screen
name={SCREENS.RIGHT_MODAL.REPORT_DETAILS}
component={ModalStackNavigators.ReportDetailsModalStackNavigator}
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,11 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: ROUTES.NEW_REPORT_WORKSPACE_SELECTION.route,
},
},
[SCREENS.RIGHT_MODAL.SET_DEFAULT_WORKSPACE]: {
screens: {
[SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: ROUTES.SET_DEFAULT_WORKSPACE.route,
},
},
[SCREENS.RIGHT_MODAL.REPORT_DETAILS]: {
screens: {
[SCREENS.REPORT_DETAILS.ROOT]: ROUTES.REPORT_WITH_ID_DETAILS.route,
Expand Down
8 changes: 8 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,12 @@ type NewReportWorkspaceSelectionNavigatorParamList = {
};
};

type SetDefaultWorkspaceNavigatorParamList = {
[SCREENS.SET_DEFAULT_WORKSPACE.ROOT]: {
backTo?: Routes;
};
};

type ReportDetailsNavigatorParamList = {
[SCREENS.REPORT_DETAILS.ROOT]: {
reportID: string;
Expand Down Expand Up @@ -1820,6 +1826,7 @@ type RightModalNavigatorParamList = {
[SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams<ProfileNavigatorParamList>;
[SCREENS.SETTINGS.SHARE_CODE]: undefined;
[SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION]: NavigatorScreenParams<NewReportWorkspaceSelectionNavigatorParamList>;
[SCREENS.RIGHT_MODAL.SET_DEFAULT_WORKSPACE]: NavigatorScreenParams<SetDefaultWorkspaceNavigatorParamList>;
[SCREENS.RIGHT_MODAL.REPORT_DETAILS]: NavigatorScreenParams<ReportDetailsNavigatorParamList>;
[SCREENS.RIGHT_MODAL.REPORT_CHANGE_WORKSPACE]: NavigatorScreenParams<ReportChangeWorkspaceNavigatorParamList>;
[SCREENS.RIGHT_MODAL.REPORT_SETTINGS]: NavigatorScreenParams<ReportSettingsNavigatorParamList>;
Expand Down Expand Up @@ -2462,6 +2469,7 @@ export type {
ReimbursementAccountNavigatorParamList,
ReimbursementAccountEnterSignerInfoNavigatorParamList,
NewReportWorkspaceSelectionNavigatorParamList,
SetDefaultWorkspaceNavigatorParamList,
ReportDescriptionNavigatorParamList,
ReportDetailsNavigatorParamList,
ReportChangeWorkspaceNavigatorParamList,
Expand Down
10 changes: 2 additions & 8 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4459,7 +4459,7 @@ function canEditFieldOfMoneyRequest(
isDeleteAction?: boolean,
isChatReportArchived = false,
outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue,
isSearchPageOption?: boolean,
canMoveExpense = true,
): boolean {
// A list of fields that cannot be edited by anyone, once an expense has been settled
const restrictedFields: string[] = [
Expand Down Expand Up @@ -4544,13 +4544,7 @@ function canEditFieldOfMoneyRequest(
// Unreported transaction from OldDot can have the reportID as an empty string
const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;

const isUserWorkspaceMember = getActivePolicies(allPolicies ?? {}, currentUserEmail).filter((userPolicy) => isPaidGroupPolicyPolicyUtils(userPolicy)).length;

if (isUnreportedExpense && isSearchPageOption && isUserWorkspaceMember) {
return true;
}

if (isUnreportedExpense && !isSearchPageOption) {
if (isUnreportedExpense && canMoveExpense) {
return true;
}

Expand Down
Loading
Loading