diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index 2c41864564a3..ba9ce9858d03 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -24,7 +24,7 @@ import MessagesRow from './MessagesRow'; type OfflineWithFeedbackProps = ChildrenProps & { /** The type of action that's pending */ - pendingAction?: OnyxCommon.PendingAction; + pendingAction?: OnyxCommon.PendingAction | null; /** Determine whether to hide the component's children if deletion is pending */ shouldHideOnDelete?: boolean; diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 52c3ecef156c..55a6c81f0417 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -184,7 +184,7 @@ function getAvatarUrl(avatarSource: AvatarSource | undefined, accountID: number) * Avatars uploaded by users will have a _128 appended so that the asset server returns a small version. * This removes that part of the URL so the full version of the image can load. */ -function getFullSizeAvatar(avatarSource: AvatarSource, accountID: number): AvatarSource { +function getFullSizeAvatar(avatarSource: AvatarSource | undefined, accountID: number): AvatarSource { const source = getAvatar(avatarSource, accountID); if (typeof source !== 'string') { return source; diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.tsx similarity index 72% rename from src/pages/DetailsPage.js rename to src/pages/DetailsPage.tsx index a4cafd59cb73..973fd5d4aba6 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.tsx @@ -1,10 +1,9 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import AutoUpdateTime from '@components/AutoUpdateTime'; import Avatar from '@components/Avatar'; @@ -18,77 +17,51 @@ import PressableWithoutFocus from '@components/Pressable/PressableWithoutFocus'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; +import type {DetailsNavigatorParamList} from '@navigation/types'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import personalDetailsPropType from './personalDetailsPropType'; - -const matchType = PropTypes.shape({ - params: PropTypes.shape({ - /** login passed via route /details/:login */ - login: PropTypes.string, - - /** report ID passed */ - reportID: PropTypes.string, - }), -}); - -const propTypes = { - /* Onyx Props */ +import type SCREENS from '@src/SCREENS'; +import type {PersonalDetails, PersonalDetailsList, Session} from '@src/types/onyx'; +type DetailsPageOnyxProps = { /** The personal details of the person who is logged in */ - personalDetails: personalDetailsPropType, - - /** Route params */ - route: matchType.isRequired, + personalDetails: OnyxEntry; /** Session info for the currently logged in user. */ - session: PropTypes.shape({ - /** Currently logged in user accountID */ - accountID: PropTypes.number, - }), - - ...withLocalizePropTypes, + session: OnyxEntry; }; -const defaultProps = { - // When opening someone else's profile (via deep link) before login, this is empty - personalDetails: {}, - session: { - accountID: 0, - }, -}; +type DetailsPageProps = DetailsPageOnyxProps & StackScreenProps; /** * Gets the phone number to display for SMS logins - * - * @param {Object} details - * @param {String} details.login - * @param {String} details.displayName - * @returns {String} */ -const getPhoneNumber = (details) => { +const getPhoneNumber = ({login = '', displayName = ''}: PersonalDetails): string | undefined => { // If the user hasn't set a displayName, it is set to their phone number, so use that - const parsedPhoneNumber = parsePhoneNumber(details.displayName); + const parsedPhoneNumber = parsePhoneNumber(displayName); if (parsedPhoneNumber.possible) { - return parsedPhoneNumber.number.e164; + return parsedPhoneNumber?.number?.e164; } // If the user has set a displayName, get the phone number from the SMS login - return details.login ? Str.removeSMSDomain(details.login) : ''; + return login ? Str.removeSMSDomain(login) : ''; }; -function DetailsPage(props) { +function DetailsPage({personalDetails, route, session}: DetailsPageProps) { const styles = useThemeStyles(); - const login = lodashGet(props.route.params, 'login', ''); - let details = _.find(props.personalDetails, (detail) => detail.login === login.toLowerCase()); + const {translate, formatPhoneNumber} = useLocalize(); + const login = route.params?.login ?? ''; + const sessionAccountID = session?.accountID ?? 0; + + let details = Object.values(personalDetails ?? {}).find((personalDetail) => personalDetail?.login === login.toLowerCase()); if (!details) { if (login === CONST.EMAIL.CONCIERGE) { @@ -116,44 +89,44 @@ function DetailsPage(props) { if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { const localeKey = pronouns.replace(CONST.PRONOUNS.PREFIX, ''); - pronouns = props.translate(`pronouns.${localeKey}`); + pronouns = translate(`pronouns.${localeKey}` as TranslationPaths); } const phoneNumber = getPhoneNumber(details); const phoneOrEmail = isSMSLogin ? getPhoneNumber(details) : details.login; const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, '', false); - const isCurrentUser = props.session.accountID === details.accountID; + const isCurrentUser = sessionAccountID === details.accountID; return ( - - + + {details ? ( {({show}) => ( - + @@ -173,11 +146,11 @@ function DetailsPage(props) { style={[styles.textLabelSupporting, styles.mb1]} numberOfLines={1} > - {props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} + {translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} - {isSMSLogin ? props.formatPhoneNumber(phoneNumber) : details.login} + {isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : details.login} @@ -188,16 +161,16 @@ function DetailsPage(props) { style={[styles.textLabelSupporting, styles.mb1]} numberOfLines={1} > - {props.translate('profilePage.preferredPronouns')} + {translate('profilePage.preferredPronouns')} {pronouns} ) : null} - {shouldShowLocalTime && } + {shouldShowLocalTime && } {!isCurrentUser && ( Report.navigateToAndOpenReport([login])} @@ -213,18 +186,13 @@ function DetailsPage(props) { ); } -DetailsPage.propTypes = propTypes; -DetailsPage.defaultProps = defaultProps; DetailsPage.displayName = 'DetailsPage'; -export default compose( - withLocalize, - withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - }), -)(DetailsPage); +export default withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + session: { + key: ONYXKEYS.SESSION, + }, +})(DetailsPage);