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
21 changes: 17 additions & 4 deletions src/components/ParentNavigationSubtitle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useRoute} from '@react-navigation/native';
import React from 'react';
import React, {useMemo} from 'react';
import type {ColorValue, StyleProp, TextStyle, ViewStyle} from 'react-native';
import {View} from 'react-native';
import {StyleSheet, View} from 'react-native';
import useHover from '@hooks/useHover';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
Expand Down Expand Up @@ -59,6 +59,9 @@ type ParentNavigationSubtitleProps = {

/** The number of lines for the subtitle */
subtitleNumberOfLines?: number;

/** Whether to show the "from" text prefix */
shouldShowFrom?: boolean;
};

function ParentNavigationSubtitle({
Expand All @@ -74,6 +77,7 @@ function ParentNavigationSubtitle({
statusTextColor,
statusTextContainerStyles,
subtitleNumberOfLines = 1,
shouldShowFrom = true,
}: ParentNavigationSubtitleProps) {
const currentRoute = useRoute();
const styles = useThemeStyles();
Expand Down Expand Up @@ -110,6 +114,15 @@ function ParentNavigationSubtitle({
};
});

// Extract color from textStyles to apply only to non-link text, preserving link color
const textStylesWithoutColor = useMemo(() => {
if (!textStyles) {
return textStyles;
}
const {color, ...rest} = StyleSheet.flatten(textStyles);
return rest;
}, [textStyles]);

// We should not display the parent navigation subtitle if the user does not have access to the parent chat (the reportName is empty in this case)
if (!reportName) {
return;
Expand Down Expand Up @@ -208,7 +221,7 @@ function ParentNavigationSubtitle({
>
{!!reportName && (
<>
<Text style={[styles.optionAlternateText, styles.textLabelSupporting, textStyles]}>{`${translate('threads.from')} `}</Text>
{shouldShowFrom && <Text style={[styles.optionAlternateText, styles.textLabelSupporting, textStyles]}>{`${translate('threads.from')} `}</Text>}
{hasAccessToParentReport ? (
<TextLink
testID="parent-navigation-subtitle-link"
Expand All @@ -221,7 +234,7 @@ function ParentNavigationSubtitle({
styles.optionAlternateText,
styles.textLabelSupporting,
hovered ? StyleUtils.getColorStyle(theme.linkHover) : styles.link,
textStyles,
textStylesWithoutColor,
]}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
>
Expand Down
113 changes: 39 additions & 74 deletions src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import DisplayNames from '@components/DisplayNames';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import {ModalActions} from '@components/Modal/Global/ModalContext';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import type {PromotedAction} from '@components/PromotedActionsBar';
import PromotedActionsBar, {PromotedActions} from '@components/PromotedActionsBar';
import ReportActionAvatars from '@components/ReportActionAvatars';
Expand All @@ -40,13 +38,13 @@ import usePreferredPolicy from '@hooks/usePreferredPolicy';
import useReportAttributes from '@hooks/useReportAttributes';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getBase62ReportID from '@libs/getBase62ReportID';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {ReportDetailsNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types';
import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils';
import Parser from '@libs/Parser';
import Permissions from '@libs/Permissions';
import {isPaidGroupPolicy, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, shouldShowPolicy} from '@libs/PolicyUtils';
Expand All @@ -62,7 +60,6 @@ import {
createDraftTransactionAndNavigateToParticipantSelector,
getAvailableReportFields,
getChatRoomSubtitle,
getDisplayNamesWithTooltips,
getIcons,
getOriginalReportID,
getParentNavigationSubtitle,
Expand Down Expand Up @@ -104,10 +101,10 @@ import {
navigateBackOnDeleteTransaction,
navigateToPrivateNotes,
shouldDisableRename as shouldDisableRenameUtil,
shouldUseFullTitleToDisplay,
} from '@libs/ReportUtils';
import StringUtils from '@libs/StringUtils';
import {isDemoTransaction} from '@libs/TransactionUtils';
import variables from '@styles/variables';
import {deleteTrackExpense, getNavigationUrlAfterTrackExpenseDelete, getNavigationUrlOnMoneyRequestDelete} from '@userActions/IOU';
import {
clearAvatarErrors,
Expand Down Expand Up @@ -156,9 +153,10 @@ const CASES = {
type CaseID = ValueOf<typeof CASES>;

function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetailsPageProps) {
const {translate, localeCompare, formatPhoneNumber} = useLocalize();
const {translate, formatPhoneNumber} = useLocalize();
const {isOffline} = useNetwork();
const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy();
const theme = useTheme();
const activePolicy = useActivePolicy();
const styles = useThemeStyles();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['Users', 'Gear', 'Send', 'Folder', 'UserPlus', 'Pencil', 'Checkmark', 'Building', 'Exit', 'Bug', 'Camera', 'Trashcan']);
Expand Down Expand Up @@ -197,7 +195,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
const isPolicyAdmin = useMemo(() => isPolicyAdminUtil(policy), [policy]);
const isPolicyEmployee = useMemo(() => isPolicyEmployeeUtil(report?.policyID, policy), [report?.policyID, policy]);
const isPolicyExpenseChat = useMemo(() => isPolicyExpenseChatUtil(report), [report]);
const shouldUseFullTitle = useMemo(() => shouldUseFullTitleToDisplay(report), [report]);

const isChatRoom = useMemo(() => isChatRoomUtil(report), [report]);
const isUserCreatedPolicyRoom = useMemo(() => isUserCreatedPolicyRoomUtil(report), [report]);
const isDefaultRoom = useMemo(() => isDefaultRoomUtil(report), [report]);
Expand Down Expand Up @@ -225,6 +223,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
const parentNavigationSubtitleData = getParentNavigationSubtitle(report, isParentReportArchived);
const base62ReportID = getBase62ReportID(Number(report.reportID));
const ancestors = useAncestors(report);
const parentNavigationSubtitleTextStyle = useMemo(() => ({fontSize: variables.fontSizeNormal, color: theme.heading}), [theme.heading]);
// eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx
const chatRoomSubtitle = useMemo(() => {
const subtitle = getChatRoomSubtitle(report, false, isReportArchived);
Expand Down Expand Up @@ -630,26 +629,11 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
userBillingGraceEndPeriodCollection,
]);

const displayNamesWithTooltips = useMemo(() => {
const hasMultipleParticipants = participants.length > 1;
return getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(participants, personalDetails), hasMultipleParticipants, localeCompare, formatPhoneNumber);
}, [participants, personalDetails, localeCompare, formatPhoneNumber]);

const icons = useMemo(
() => getIcons(report, formatPhoneNumber, personalDetails, null, '', -1, policy, undefined, isReportArchived),
[report, formatPhoneNumber, personalDetails, policy, isReportArchived],
);

const chatRoomSubtitleText = chatRoomSubtitle ? (
<DisplayNames
fullTitle={chatRoomSubtitle}
tooltipEnabled
numberOfLines={1}
textStyles={[styles.sidebarLinkText, styles.textLabelSupporting, styles.pre, styles.mt1, styles.textAlignCenter]}
shouldUseFullTitle
/>
) : null;

const renderedAvatar = useMemo(() => {
if (isChatRoom && !isThread) {
return (
Expand Down Expand Up @@ -737,61 +721,36 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
}, [canJoin, report, backTo, currentUserPersonalDetails.accountID]);

const nameSectionExpenseIOU = (
<View style={[styles.reportDetailsRoomInfo, styles.mw100]}>
{shouldDisableRename && (
<>
<View style={[styles.alignSelfCenter, styles.w100, styles.mt1]}>
<DisplayNames
fullTitle={reportName}
displayNamesWithTooltips={displayNamesWithTooltips}
shouldParseFullTitle={!isGroupChat}
tooltipEnabled
numberOfLines={isChatRoom && !isChatThread ? 0 : 1}
textStyles={[styles.textHeadline, styles.textAlignCenter, isChatRoom && !isChatThread ? undefined : styles.pre]}
shouldUseFullTitle={shouldUseFullTitle}
/>
</View>
{isPolicyAdmin ? (
<PressableWithoutFeedback
style={[styles.w100]}
disabled={policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
role={CONST.ROLE.BUTTON}
accessibilityLabel={chatRoomSubtitle}
accessible
sentryLabel={CONST.SENTRY_LABEL.REPORT_DETAILS.WORKSPACE_LINK}
onPress={() => {
let policyID = report?.policyID;

if (!policyID) {
policyID = '';
}

Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID));
}}
>
{chatRoomSubtitleText}
</PressableWithoutFeedback>
) : (
chatRoomSubtitleText
)}
</>
)}
<>
<MenuItemWithTopDescription
shouldShowRightIcon={false}
interactive={false}
title={reportName}
titleStyle={styles.newKansasLarge}
shouldCheckActionAllowedOnPress={false}
description={translate('common.title')}
/>
{!isEmptyObject(parentNavigationSubtitleData) && (isMoneyRequestReport || isInvoiceReport || isMoneyRequest || isTaskReport) && (
<View style={[styles.w100, styles.mt1, styles.alignItemsCenter]}>
<View style={styles.mw100}>
<MenuItemWithTopDescription
shouldShowRightIcon={false}
interactive={false}
titleComponent={
<ParentNavigationSubtitle
parentNavigationSubtitleData={parentNavigationSubtitleData}
reportID={report?.reportID}
parentReportID={report?.parentReportID}
parentReportActionID={report?.parentReportActionID}
pressableStyles={[styles.mt1, styles.mw100]}
textStyles={[styles.textAlignCenter]}
subtitleNumberOfLines={2}
shouldShowFrom={false}
textStyles={parentNavigationSubtitleTextStyle}

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.

Suggested change
textStyles={parentNavigationSubtitleTextStyle}
textStyles={parentNavigationSubtitleTextStyle}
descriptionTextStyle={[styles.mutedNormalTextLabel, styles.mb1]}

We need to set descriptionTextStyle to make it consistent with other description labels.

/>
</View>
</View>
}
description={translate('threads.from')}
descriptionTextStyle={[styles.mutedNormalTextLabel, styles.mb1]}
shouldCheckActionAllowedOnPress={false}
/>
)}
</View>
</>
);

const nameSectionGroupWorkspace = (
Expand Down Expand Up @@ -839,6 +798,8 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
parentReportActionID={report?.parentReportActionID}
pressableStyles={[styles.mt1, styles.mw100]}
subtitleNumberOfLines={2}
shouldShowFrom={false}
textStyles={parentNavigationSubtitleTextStyle}
/>
);

Expand Down Expand Up @@ -867,7 +828,13 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail

Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report.reportID, policyID, CONST.REPORT_FIELD_TITLE_FIELD_ID, backTo));
}}
furtherDetailsComponent={nameSectionFurtherDetailsContent}
/>
<MenuItemWithTopDescription
shouldShowRightIcon={false}
interactive={false}
titleComponent={nameSectionFurtherDetailsContent}
description={translate('common.from')}

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.

Suggested change
description={translate('common.from')}
description={translate('common.from')}
descriptionTextStyle={[styles.mutedNormalTextLabel, styles.mb1]}

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.

Done 👍

descriptionTextStyle={[styles.mutedNormalTextLabel, styles.mb1]}
/>
</View>
</OfflineWithFeedback>
Expand Down Expand Up @@ -1026,11 +993,9 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail
onBackButtonPress={() => Navigation.goBack(backTo)}
/>
<ScrollView contentContainerStyle={[styles.flexGrow1]}>
<View style={[styles.reportDetailsTitleContainer, styles.pb0]}>
{renderedAvatar}
{isExpenseReport && !shouldShowEditableTitleField && nameSectionExpenseIOU}
</View>
{isExpenseReport && shouldShowEditableTitleField && nameSectionTitleField}
<View style={[styles.reportDetailsTitleContainer, styles.pb0]}>{renderedAvatar}</View>
{isExpenseReport && (!shouldShowEditableTitleField || !titleField) && nameSectionExpenseIOU}
{isExpenseReport && shouldShowEditableTitleField && titleField && nameSectionTitleField}

{!isExpenseReport && nameSectionGroupWorkspace}

Expand Down
Loading