Skip to content
Closed
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
21 changes: 17 additions & 4 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ function MoneyRequestView({
// 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 && hasEnabledOptions(policyCategories ?? {}));
// 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 @@ -307,6 +307,8 @@ function MoneyRequestView({
amountTitle = translate('iou.receiptStatusTitle');
}

const shouldNavigateToUpgradePath = !policyForMovingExpenses && !shouldSelectPolicy;

const updatedTransactionDescription = useMemo(() => {
if (!updatedTransaction) {
return undefined;
Expand Down Expand Up @@ -742,17 +744,28 @@ function MoneyRequestView({
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 @@ -893,7 +906,7 @@ function MoneyRequestView({
if (!canEditReport) {
return;
}
if (!policyForMovingExpenses && !shouldSelectPolicy) {
if (shouldNavigateToUpgradePath) {
Navigation.navigate(
ROUTES.MONEY_REQUEST_UPGRADE.getRoute({
iouType,
Expand Down
50 changes: 38 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 @@ -12,6 +12,7 @@ import type {ReportActionListItemType, SearchListItem, SelectionListHandle, Tran
import SearchRowSkeleton from '@components/Skeletons/SearchRowSkeleton';
import {WideRHPContext} from '@components/WideRHPContextProvider';
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 @@ -29,6 +30,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, selectArchivedReportsIdSet, selectFilteredReportActions} from '@libs/ReportUtils';
import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils';
Expand Down Expand Up @@ -90,6 +92,7 @@ function mapTransactionItemToSelectedEntry(
item: TransactionListItemType,
reportActions: ReportAction[],
outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue,
canMoveExpense = true,
): [string, SelectedTransactionInfo] {
return [
item.keyForList,
Expand All @@ -105,7 +108,7 @@ function mapTransactionItemToSelectedEntry(
undefined,
undefined,
outstandingReportsByPolicyID,
true,
canMoveExpense,
),
action: item.action,
reportID: item.reportID,
Expand Down Expand Up @@ -166,6 +169,7 @@ function prepareTransactionsList(
selectedTransactions: SelectedTransactions,
reportActions: ReportAction[],
outstandingReportsByPolicyID?: OutstandingReportsByPolicyIDDerivedValue,
canMoveExpense = true,
) {
if (selectedTransactions[item.keyForList]?.isSelected) {
const {[item.keyForList]: omittedTransaction, ...transactions} = selectedTransactions;
Expand All @@ -187,7 +191,7 @@ function prepareTransactionsList(
undefined,
undefined,
outstandingReportsByPolicyID,
true,
canMoveExpense,
),
action: item.action,
reportID: item.reportID,
Expand All @@ -201,6 +205,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 @@ -235,6 +243,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 @@ -445,7 +461,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 @@ -478,7 +494,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 @@ -500,7 +516,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 @@ -560,7 +576,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 @@ -582,13 +598,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 @@ -796,7 +812,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 @@ -809,11 +825,21 @@ 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, validGroupBy, reportActionsArray, selectedTransactions, setSelectedTransactions, outstandingReportsByPolicyID, isExpenseReportType]);
}, [
validGroupBy,
isExpenseReportType,
selectedTransactions,
setSelectedTransactions,
data,
clearSelectedTransactions,
reportActionsArray,
outstandingReportsByPolicyID,
isUserWorkspaceMember,
]);

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 @@ -206,6 +207,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 @@ -918,6 +923,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 @@ -1111,6 +1111,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 @@ -1213,6 +1213,12 @@ type NewReportWorkspaceSelectionNavigatorParamList = {
};
};

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

type ReportDetailsNavigatorParamList = {
[SCREENS.REPORT_DETAILS.ROOT]: {
reportID: string;
Expand Down Expand Up @@ -1819,6 +1825,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 @@ -2457,6 +2464,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 @@ -4389,7 +4389,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 @@ -4474,13 +4474,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