diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index cc0de763f515..7e5820f425c5 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -57,7 +57,7 @@ type NoIcon = { type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { /** Function to fire when component is pressed */ - onPress?: (event: GestureResponderEvent | KeyboardEvent) => void; + onPress?: (event: GestureResponderEvent | KeyboardEvent) => void | Promise; /** Whether the menu item should be interactive at all */ interactive?: boolean; diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 6ec386679a32..a8c918bc5def 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -176,8 +176,8 @@ function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSourc * @param avatarURL - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ -function getAvatarUrl(avatarURL: string, accountID: number): string { - return isDefaultAvatar(avatarURL) ? getDefaultAvatarURL(accountID) : avatarURL; +function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number): AvatarSource { + return isDefaultAvatar(avatarSource) ? getDefaultAvatarURL(accountID) : avatarSource; } /** diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js deleted file mode 100644 index 3f0ef6ca138e..000000000000 --- a/src/pages/ShareCodePage.js +++ /dev/null @@ -1,144 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {ScrollView, View} from 'react-native'; -import _ from 'underscore'; -import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png'; -import ContextMenuItem from '@components/ContextMenuItem'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItem from '@components/MenuItem'; -import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; -import ScreenWrapper from '@components/ScreenWrapper'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withEnvironment from '@components/withEnvironment'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import Clipboard from '@libs/Clipboard'; -import compose from '@libs/compose'; -import getPlatform from '@libs/getPlatform'; -import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as Url from '@libs/Url'; -import * as UserUtils from '@libs/UserUtils'; -import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; -import reportPropTypes from './reportPropTypes'; - -const propTypes = { - /** The report currently being looked at */ - report: reportPropTypes, - - /** The string value representing the URL of the current environment */ - environmentURL: PropTypes.string.isRequired, - - ...withLocalizePropTypes, - ...withCurrentUserPersonalDetailsPropTypes, - ...withThemeStylesPropTypes, - ...windowDimensionsPropTypes, -}; - -const defaultProps = { - report: undefined, - ...withCurrentUserPersonalDetailsDefaultProps, -}; - -// eslint-disable-next-line react/prefer-stateless-function -class ShareCodePage extends React.Component { - qrCodeRef = React.createRef(); - - /** - * @param {Boolean} isReport - * @return {String|string|*} - */ - getSubtitle(isReport) { - if (ReportUtils.isExpenseReport(this.props.report)) { - return ReportUtils.getPolicyName(this.props.report); - } - if (ReportUtils.isMoneyRequestReport(this.props.report)) { - // generate subtitle from participants - return _.map(ReportUtils.getVisibleMemberIDs(this.props.report), (accountID) => ReportUtils.getDisplayNameForParticipant(accountID)).join(' & '); - } - - if (isReport) { - return ReportUtils.getParentNavigationSubtitle(this.props.report).workspaceName || ReportUtils.getChatRoomSubtitle(this.props.report); - } - - return this.props.formatPhoneNumber(this.props.session.email); - } - - render() { - const isReport = this.props.report != null && this.props.report.reportID != null; - const title = isReport ? ReportUtils.getReportName(this.props.report) : this.props.currentUserPersonalDetails.displayName; - const subtitle = this.getSubtitle(isReport); - const urlWithTrailingSlash = Url.addTrailingForwardSlash(this.props.environmentURL); - const url = isReport - ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(this.props.report.reportID)}` - : `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(this.props.session.accountID)}`; - - const platform = getPlatform(); - const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; - - return ( - - Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS)} - shouldShowBackButton={isReport || this.props.isSmallScreenWidth} - /> - - - - - - - - Clipboard.setString(url)} - shouldLimitWidth={false} - /> - - {isNative && ( - this.qrCodeRef.current?.download()} - /> - )} - - Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} - /> - - - - ); - } -} - -ShareCodePage.propTypes = propTypes; -ShareCodePage.defaultProps = defaultProps; -ShareCodePage.displayName = 'ShareCodePage'; - -export default compose(withEnvironment, withLocalize, withCurrentUserPersonalDetails, withThemeStyles, withWindowDimensions)(ShareCodePage); diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx new file mode 100644 index 000000000000..831f0eb8f1d8 --- /dev/null +++ b/src/pages/ShareCodePage.tsx @@ -0,0 +1,129 @@ +import React, {useRef} from 'react'; +import {ScrollView, View} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png'; +import ContextMenuItem from '@components/ContextMenuItem'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; +import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; +import ScreenWrapper from '@components/ScreenWrapper'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import useEnvironment from '@hooks/useEnvironment'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import Clipboard from '@libs/Clipboard'; +import getPlatform from '@libs/getPlatform'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as Url from '@libs/Url'; +import * as UserUtils from '@libs/UserUtils'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import type {Report, Session} from '@src/types/onyx'; + +type ShareCodePageOnyxProps = WithCurrentUserPersonalDetailsProps & { + /** Session info for the currently logged in user. */ + session: OnyxEntry; + + /** The report currently being looked at */ + report?: OnyxEntry; +}; + +type ShareCodePageProps = ShareCodePageOnyxProps; + +function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodePageProps) { + const themeStyles = useThemeStyles(); + const {translate} = useLocalize(); + const {environmentURL} = useEnvironment(); + const qrCodeRef = useRef(null); + const {isSmallScreenWidth} = useWindowDimensions(); + + const isReport = !!report?.reportID; + + const getSubtitle = () => { + if (isReport) { + if (ReportUtils.isExpenseReport(report)) { + return ReportUtils.getPolicyName(report); + } + if (ReportUtils.isMoneyRequestReport(report)) { + // generate subtitle from participants + return ReportUtils.getVisibleMemberIDs(report) + .map((accountID) => ReportUtils.getDisplayNameForParticipant(accountID)) + .join(' & '); + } + + return ReportUtils.getParentNavigationSubtitle(report).workspaceName ?? ReportUtils.getChatRoomSubtitle(report); + } + + return session?.email; + }; + + const title = isReport ? ReportUtils.getReportName(report) : currentUserPersonalDetails.displayName ?? ''; + const subtitle = getSubtitle(); + const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); + const url = isReport ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(report.reportID)}` : `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(session?.accountID ?? '')}`; + const platform = getPlatform(); + const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; + + return ( + + Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID) : ROUTES.SETTINGS)} + shouldShowBackButton={isReport || isSmallScreenWidth} + /> + + + + + + + Clipboard.setString(url)} + shouldLimitWidth={false} + /> + + {isNative && ( + + )} + + Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} + /> + + + + ); +} + +ShareCodePage.displayName = 'ShareCodePage'; + +export default withCurrentUserPersonalDetails(ShareCodePage); diff --git a/src/pages/home/report/ReportDetailsShareCodePage.js b/src/pages/home/report/ReportDetailsShareCodePage.js deleted file mode 100644 index 0ef2d46029ea..000000000000 --- a/src/pages/home/report/ReportDetailsShareCodePage.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import reportPropTypes from '@pages/reportPropTypes'; -import ShareCodePage from '@pages/ShareCodePage'; -import withReportOrNotFound from './withReportOrNotFound'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen */ - params: PropTypes.shape({ - reportID: PropTypes.string, - }).isRequired, - }).isRequired, - - /** The report currently being looked at */ - report: reportPropTypes, -}; - -const defaultProps = { - report: undefined, -}; - -function ReportDetailsShareCodePage(props) { - return ; -} - -ReportDetailsShareCodePage.propTypes = propTypes; -ReportDetailsShareCodePage.defaultProps = defaultProps; - -export default withReportOrNotFound()(ReportDetailsShareCodePage); diff --git a/src/pages/home/report/ReportDetailsShareCodePage.tsx b/src/pages/home/report/ReportDetailsShareCodePage.tsx new file mode 100644 index 000000000000..28b1d5cd71d7 --- /dev/null +++ b/src/pages/home/report/ReportDetailsShareCodePage.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ShareCodePage from '@pages/ShareCodePage'; +import type {WithReportOrNotFoundProps} from './withReportOrNotFound'; +import withReportOrNotFound from './withReportOrNotFound'; + +type ReportDetailsShareCodePageProps = WithReportOrNotFoundProps; + +function ReportDetailsShareCodePage({report}: ReportDetailsShareCodePageProps) { + return ; +} + +export default withReportOrNotFound()(ReportDetailsShareCodePage); diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index 7214e6b6f3ea..15bccb900077 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -16,10 +16,13 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WithReportOrNotFoundOnyxProps = { /** The report currently being looked at */ report: OnyxEntry; + /** The policies which the user has access to */ policies: OnyxCollection; + /** Beta features list */ betas: OnyxEntry; + /** Indicated whether the report data is loading */ isLoadingReportData: OnyxEntry; };