diff --git a/src/components/TextWithCopy.tsx b/src/components/TextWithCopy.tsx new file mode 100644 index 000000000000..a969edf0dece --- /dev/null +++ b/src/components/TextWithCopy.tsx @@ -0,0 +1,57 @@ +import React, {useRef} from 'react'; +import type {GestureResponderEvent, View} from 'react-native'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {showContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; +import CONST from '@src/CONST'; +import PressableWithSecondaryInteraction from './PressableWithSecondaryInteraction'; +import Text from './Text'; +import type {TextProps} from './Text'; + +type TextWithCopyProps = TextProps & { + /** The value to copy when the text component is pressed */ + copyValue: string; +}; + +/** + * A text component that copies the text to the clipboard when pressed. + * This is different from the `copyValueToClipboard` component in that + * here the copy functionality is incorporated into the text itself. + * Long press this text to toggle the context menu containing the copy option. + */ +function TextWithCopy({children, copyValue, ...rest}: TextWithCopyProps) { + const popoverAnchor = useRef(null); + const styles = useThemeStyles(); + + const showCopyContextMenu = (event: GestureResponderEvent | MouseEvent) => { + if (!copyValue) { + return; + } + showContextMenu({ + type: CONST.CONTEXT_MENU_TYPES.TEXT, + event, + selection: copyValue, + contextMenuAnchor: popoverAnchor.current, + }); + }; + + return ( + + + {children} + + + ); +} + +export default TextWithCopy; diff --git a/src/languages/en.ts b/src/languages/en.ts index 48a9842a7cd9..e092223d5119 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -499,6 +499,7 @@ const translations = { filterLogs: 'Filter Logs', network: 'Network', reportID: 'Report ID', + longID: 'Long ID', bankAccounts: 'Bank accounts', chooseFile: 'Choose file', dropTitle: 'Let it go', diff --git a/src/languages/es.ts b/src/languages/es.ts index 47ac36ce01bf..a3832b9f9e5a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -492,6 +492,7 @@ const translations = { filterLogs: 'Registros de filtrado', network: 'La red', reportID: 'ID del informe', + longID: 'ID largo', bankAccounts: 'Cuentas bancarias', chooseFile: 'Elegir archivo', dropTitle: 'Suéltalo', diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index ee01f9aa7e93..782bec1b00b5 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -11,6 +11,7 @@ import ConfirmModal from '@components/ConfirmModal'; import DecisionModal from '@components/DecisionModal'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import DisplayNames from '@components/DisplayNames'; +import FixedFooter from '@components/FixedFooter'; import Header from '@components/Header'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; @@ -29,6 +30,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import {useSearchContext} from '@components/Search/SearchContext'; import Text from '@components/Text'; +import TextWithCopy from '@components/TextWithCopy'; import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -37,6 +39,7 @@ import usePermissions from '@hooks/usePermissions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import getBase62ReportID from '@libs/getBase62ReportID'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; @@ -231,6 +234,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const isSelfDMTrackExpenseReport = isTrackExpenseReport && isSelfDMUtil(parentReport); const shouldDisableRename = useMemo(() => shouldDisableRenameUtil(report), [report]); const parentNavigationSubtitleData = getParentNavigationSubtitle(report); + const base62ReportID = getBase62ReportID(Number(report.reportID)); // eslint-disable-next-line react-compiler/react-compiler, 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); @@ -1009,7 +1013,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta title={translate('common.details')} onBackButtonPress={() => Navigation.goBack(backTo)} /> - + {renderedAvatar} {isExpenseReport && (!shouldShowTitleField || !titleField) && nameSectionExpenseIOU} @@ -1063,6 +1067,23 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta onPress={() => setIsDeleteModalVisible(true)} /> )} + + + + + {`${translate('common.reportID')}: ${base62ReportID}`} + + + {`${translate('common.longID')}: ${report.reportID}`} + + +