-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TS migration] Migrate 'Profile' page to TypeScript #34974
Changes from 15 commits
769d6d4
bca8c01
f944f30
5a368a8
a25fc45
f710944
27480fe
3955283
3173fa0
910072a
025b609
d0d42d5
8c7d5e9
b7f8dae
23a576a
8749ad1
8a0af88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -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, {useEffect} from 'react'; | ||||||||||||||
import {ScrollView, View} from 'react-native'; | ||||||||||||||
import {withOnyx} from 'react-native-onyx'; | ||||||||||||||
import _ from 'underscore'; | ||||||||||||||
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; | ||||||||||||||
import AutoUpdateTime from '@components/AutoUpdateTime'; | ||||||||||||||
import Avatar from '@components/Avatar'; | ||||||||||||||
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; | ||||||||||||||
|
@@ -19,132 +18,118 @@ 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 Navigation from '@libs/Navigation/Navigation'; | ||||||||||||||
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; | ||||||||||||||
import {parsePhoneNumber} from '@libs/PhoneNumber'; | ||||||||||||||
import * as ReportUtils from '@libs/ReportUtils'; | ||||||||||||||
import * as UserUtils from '@libs/UserUtils'; | ||||||||||||||
import * as ValidationUtils from '@libs/ValidationUtils'; | ||||||||||||||
import * as PersonalDetails from '@userActions/PersonalDetails'; | ||||||||||||||
import * as Report from '@userActions/Report'; | ||||||||||||||
import * as Session from '@userActions/Session'; | ||||||||||||||
import type {ProfileNavigatorParamList} from '@navigation/types'; | ||||||||||||||
import * as PersonalDetailsActions from '@userActions/PersonalDetails'; | ||||||||||||||
import * as ReportActions from '@userActions/Report'; | ||||||||||||||
import * as SessionActions from '@userActions/Session'; | ||||||||||||||
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's turn this imports back as they was, and update OnyxTypes import to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is better to import only the needed types There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pasyukevich I agree with @VickyStash comment in this case, by doing your way you had to change the other imports to avoid conflicts, we don't need that if we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For me using |
||||||||||||||
import CONST from '@src/CONST'; | ||||||||||||||
import type {TranslationPaths} from '@src/languages/types'; | ||||||||||||||
import ONYXKEYS from '@src/ONYXKEYS'; | ||||||||||||||
import ROUTES from '@src/ROUTES'; | ||||||||||||||
import personalDetailsPropType from './personalDetailsPropType'; | ||||||||||||||
import type SCREENS from '@src/SCREENS'; | ||||||||||||||
import type {PersonalDetails, PersonalDetailsList, Report, Session} from '@src/types/onyx'; | ||||||||||||||
import {isEmptyObject} from '@src/types/utils/EmptyObject'; | ||||||||||||||
import type {EmptyObject} from '@src/types/utils/EmptyObject'; | ||||||||||||||
|
||||||||||||||
const matchType = PropTypes.shape({ | ||||||||||||||
params: PropTypes.shape({ | ||||||||||||||
/** accountID passed via route /a/:accountID */ | ||||||||||||||
accountID: PropTypes.string, | ||||||||||||||
type ProfilePageOnyxProps = { | ||||||||||||||
/** The personal details of the person who is logged in */ | ||||||||||||||
personalDetails: OnyxEntry<PersonalDetailsList>; | ||||||||||||||
|
||||||||||||||
/** report ID passed */ | ||||||||||||||
reportID: PropTypes.string, | ||||||||||||||
}), | ||||||||||||||
}); | ||||||||||||||
/** The report currently being looked at */ | ||||||||||||||
report: OnyxEntry<Report>; | ||||||||||||||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
const propTypes = { | ||||||||||||||
/* Onyx Props */ | ||||||||||||||
|
||||||||||||||
/** The personal details of all users */ | ||||||||||||||
personalDetails: PropTypes.objectOf(personalDetailsPropType), | ||||||||||||||
|
||||||||||||||
/** Route params */ | ||||||||||||||
route: matchType.isRequired, | ||||||||||||||
/** The list of all reports | ||||||||||||||
* ONYXKEYS.COLLECTION.REPORT is needed for report key function | ||||||||||||||
*/ | ||||||||||||||
// eslint-disable-next-line react/no-unused-prop-types | ||||||||||||||
reports: OnyxCollection<Report>; | ||||||||||||||
|
||||||||||||||
/** Session info for the currently logged in user. */ | ||||||||||||||
session: PropTypes.shape({ | ||||||||||||||
/** Currently logged in user accountID */ | ||||||||||||||
accountID: PropTypes.number, | ||||||||||||||
}), | ||||||||||||||
|
||||||||||||||
...withLocalizePropTypes, | ||||||||||||||
session: OnyxEntry<Session>; | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
const defaultProps = { | ||||||||||||||
// When opening someone else's profile (via deep link) before login, this is empty | ||||||||||||||
personalDetails: {}, | ||||||||||||||
session: { | ||||||||||||||
accountID: 0, | ||||||||||||||
}, | ||||||||||||||
}; | ||||||||||||||
type ProfilePageProps = ProfilePageOnyxProps & StackScreenProps<ProfileNavigatorParamList, typeof SCREENS.PROFILE_ROOT>; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* 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) => { | ||||||||||||||
// If the user hasn't set a displayName, it is set to their phone number, so use that | ||||||||||||||
const displayName = lodashGet(details, 'displayName', ''); | ||||||||||||||
const getPhoneNumber = ({login = '', displayName = ''}: PersonalDetails | EmptyObject): string | undefined => { | ||||||||||||||
// If the user hasn't set a displayName, it is set to their phone number | ||||||||||||||
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 ProfilePage(props) { | ||||||||||||||
function ProfilePage({personalDetails, route, session, report}: ProfilePageProps) { | ||||||||||||||
const styles = useThemeStyles(); | ||||||||||||||
const accountID = Number(lodashGet(props.route.params, 'accountID', 0)); | ||||||||||||||
const isCurrentUser = props.session.accountID === accountID; | ||||||||||||||
const {translate, formatPhoneNumber} = useLocalize(); | ||||||||||||||
const accountID = Number(route.params?.accountID ?? 0); | ||||||||||||||
const isCurrentUser = session?.accountID === accountID; | ||||||||||||||
const details: PersonalDetails | EmptyObject = personalDetails?.[accountID] ?? (ValidationUtils.isValidAccountRoute(accountID) ? {} : {isLoading: false, accountID: 0, avatar: ''}); | ||||||||||||||
|
||||||||||||||
const details = lodashGet(props.personalDetails, accountID, ValidationUtils.isValidAccountRoute(accountID) ? {} : {isloading: false}); | ||||||||||||||
const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(details, undefined, undefined, isCurrentUser); | ||||||||||||||
const avatar = lodashGet(details, 'avatar', UserUtils.getDefaultAvatar()); | ||||||||||||||
const fallbackIcon = lodashGet(details, 'fallbackIcon', ''); | ||||||||||||||
const login = lodashGet(details, 'login', ''); | ||||||||||||||
const timezone = lodashGet(details, 'timezone', {}); | ||||||||||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | ||||||||||||||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
const avatar = details?.avatar || UserUtils.getDefaultAvatar(); // we can have an empty string and in this case, we need to show the default avatar | ||||||||||||||
const fallbackIcon = details?.fallbackIcon ?? ''; | ||||||||||||||
const login = details?.login ?? ''; | ||||||||||||||
const timezone = details?.timezone; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a fallback value here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so since we have some statements in code that rely on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pasyukevich We pass the timezone to AutoUpdateTime Component as an Object. If timezone is undefined, we will get the error "can't read the attribute of undefined" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DylanDylann It is not a problem, since we will render There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pasyukevich Also please help to resolve conflict. This PR has been delayed for a long time. Let's move forward |
||||||||||||||
|
||||||||||||||
// If we have a reportID param this means that we | ||||||||||||||
// arrived here via the ParticipantsPage and should be allowed to navigate back to it | ||||||||||||||
const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([accountID]) && !_.isEmpty(timezone); | ||||||||||||||
let pronouns = lodashGet(details, 'pronouns', ''); | ||||||||||||||
if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { | ||||||||||||||
const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([accountID]) && !isEmptyObject(timezone); | ||||||||||||||
let pronouns = details?.pronouns ?? ''; | ||||||||||||||
if (pronouns?.startsWith(CONST.PRONOUNS.PREFIX)) { | ||||||||||||||
const localeKey = pronouns.replace(CONST.PRONOUNS.PREFIX, ''); | ||||||||||||||
pronouns = props.translate(`pronouns.${localeKey}`); | ||||||||||||||
pronouns = translate(`pronouns.${localeKey}` as TranslationPaths); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const isSMSLogin = Str.isSMSLogin(login); | ||||||||||||||
const phoneNumber = getPhoneNumber(details); | ||||||||||||||
const phoneOrEmail = isSMSLogin ? getPhoneNumber(details) : login; | ||||||||||||||
|
||||||||||||||
const hasMinimumDetails = !_.isEmpty(details.avatar); | ||||||||||||||
const isLoading = lodashGet(details, 'isLoading', false) || _.isEmpty(details); | ||||||||||||||
const hasMinimumDetails = !isEmptyObject(details.avatar); | ||||||||||||||
const isLoading = Boolean(details?.isLoading) || isEmptyObject(details); | ||||||||||||||
|
||||||||||||||
// If the API returns an error for some reason there won't be any details and isLoading will get set to false, so we want to show a blocking screen | ||||||||||||||
const shouldShowBlockingView = !hasMinimumDetails && !isLoading; | ||||||||||||||
|
||||||||||||||
const statusEmojiCode = lodashGet(details, 'status.emojiCode', ''); | ||||||||||||||
const statusText = lodashGet(details, 'status.text', ''); | ||||||||||||||
const statusEmojiCode = details?.status?.emojiCode ?? ''; | ||||||||||||||
const statusText = details?.status?.text ?? ''; | ||||||||||||||
const hasStatus = !!statusEmojiCode; | ||||||||||||||
const statusContent = `${statusEmojiCode} ${statusText}`; | ||||||||||||||
|
||||||||||||||
const navigateBackTo = lodashGet(props.route, 'params.backTo'); | ||||||||||||||
const navigateBackTo = route?.params?.backTo; | ||||||||||||||
|
||||||||||||||
const shouldShowNotificationPreference = !_.isEmpty(props.report) && !isCurrentUser && props.report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; | ||||||||||||||
const notificationPreference = shouldShowNotificationPreference ? props.translate(`notificationPreferencesPage.notificationPreferences.${props.report.notificationPreference}`) : ''; | ||||||||||||||
const shouldShowNotificationPreference = !isEmptyObject(report) && !isCurrentUser && report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might have caused #39677 |
||||||||||||||
const notificationPreference = shouldShowNotificationPreference | ||||||||||||||
? translate(`notificationPreferencesPage.notificationPreferences.${report.notificationPreference}` as TranslationPaths) | ||||||||||||||
: ''; | ||||||||||||||
Comment on lines
+118
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Assertion can be escaped if we narrow down the type of
Suggested change
But I'm not sure if it's a better approach There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the current is better cause we have a better visualisation of the logic |
||||||||||||||
|
||||||||||||||
// eslint-disable-next-line rulesdir/prefer-early-return | ||||||||||||||
useEffect(() => { | ||||||||||||||
if (ValidationUtils.isValidAccountRoute(accountID)) { | ||||||||||||||
PersonalDetails.openPublicProfilePage(accountID); | ||||||||||||||
PersonalDetailsActions.openPublicProfilePage(accountID); | ||||||||||||||
} | ||||||||||||||
}, [accountID]); | ||||||||||||||
|
||||||||||||||
return ( | ||||||||||||||
<ScreenWrapper testID={ProfilePage.displayName}> | ||||||||||||||
<FullPageNotFoundView shouldShow={shouldShowBlockingView || CONST.RESTRICTED_ACCOUNT_IDS.includes(accountID)}> | ||||||||||||||
<HeaderWithBackButton | ||||||||||||||
title={props.translate('common.profile')} | ||||||||||||||
title={translate('common.profile')} | ||||||||||||||
onBackButtonPress={() => Navigation.goBack(navigateBackTo)} | ||||||||||||||
/> | ||||||||||||||
<View style={[styles.containerWithSpaceBetween, styles.pointerEventsBoxNone]}> | ||||||||||||||
|
@@ -154,10 +139,10 @@ function ProfilePage(props) { | |||||||||||||
<PressableWithoutFocus | ||||||||||||||
style={[styles.noOutline]} | ||||||||||||||
onPress={() => Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} | ||||||||||||||
accessibilityLabel={props.translate('common.profile')} | ||||||||||||||
accessibilityLabel={translate('common.profile')} | ||||||||||||||
accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} | ||||||||||||||
> | ||||||||||||||
<OfflineWithFeedback pendingAction={lodashGet(details, 'pendingFields.avatar', null)}> | ||||||||||||||
<OfflineWithFeedback pendingAction={details?.pendingFields?.avatar}> | ||||||||||||||
<Avatar | ||||||||||||||
containerStyles={[styles.avatarXLarge, styles.mb3]} | ||||||||||||||
imageStyles={[styles.avatarXLarge]} | ||||||||||||||
|
@@ -181,7 +166,7 @@ function ProfilePage(props) { | |||||||||||||
style={[styles.textLabelSupporting, styles.mb1]} | ||||||||||||||
numberOfLines={1} | ||||||||||||||
> | ||||||||||||||
{props.translate('statusPage.status')} | ||||||||||||||
{translate('statusPage.status')} | ||||||||||||||
</Text> | ||||||||||||||
<Text>{statusContent}</Text> | ||||||||||||||
</View> | ||||||||||||||
|
@@ -193,11 +178,11 @@ function ProfilePage(props) { | |||||||||||||
style={[styles.textLabelSupporting, styles.mb1]} | ||||||||||||||
numberOfLines={1} | ||||||||||||||
> | ||||||||||||||
{props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} | ||||||||||||||
{translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')} | ||||||||||||||
</Text> | ||||||||||||||
<CommunicationsLink value={phoneOrEmail}> | ||||||||||||||
<CommunicationsLink value={phoneOrEmail ?? ''}> | ||||||||||||||
<UserDetailsTooltip accountID={details.accountID}> | ||||||||||||||
<Text numberOfLines={1}>{isSMSLogin ? props.formatPhoneNumber(phoneNumber) : login}</Text> | ||||||||||||||
<Text numberOfLines={1}>{isSMSLogin ? formatPhoneNumber(phoneNumber ?? '') : login}</Text> | ||||||||||||||
</UserDetailsTooltip> | ||||||||||||||
</CommunicationsLink> | ||||||||||||||
</View> | ||||||||||||||
|
@@ -208,7 +193,7 @@ function ProfilePage(props) { | |||||||||||||
style={[styles.textLabelSupporting, styles.mb1]} | ||||||||||||||
numberOfLines={1} | ||||||||||||||
> | ||||||||||||||
{props.translate('profilePage.preferredPronouns')} | ||||||||||||||
{translate('profilePage.preferredPronouns')} | ||||||||||||||
</Text> | ||||||||||||||
<Text numberOfLines={1}>{pronouns}</Text> | ||||||||||||||
</View> | ||||||||||||||
|
@@ -219,30 +204,30 @@ function ProfilePage(props) { | |||||||||||||
<MenuItemWithTopDescription | ||||||||||||||
shouldShowRightIcon | ||||||||||||||
title={notificationPreference} | ||||||||||||||
description={props.translate('notificationPreferencesPage.label')} | ||||||||||||||
onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(props.report.reportID))} | ||||||||||||||
description={translate('notificationPreferencesPage.label')} | ||||||||||||||
onPress={() => Navigation.navigate(ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES.getRoute(report.reportID))} | ||||||||||||||
wrapperStyle={[styles.mtn6, styles.mb5]} | ||||||||||||||
/> | ||||||||||||||
)} | ||||||||||||||
{!isCurrentUser && !Session.isAnonymousUser() && ( | ||||||||||||||
{!isCurrentUser && !SessionActions.isAnonymousUser() && ( | ||||||||||||||
<MenuItem | ||||||||||||||
title={`${props.translate('common.message')}${displayName}`} | ||||||||||||||
title={`${translate('common.message')}${displayName}`} | ||||||||||||||
titleStyle={styles.flex1} | ||||||||||||||
icon={Expensicons.ChatBubble} | ||||||||||||||
onPress={() => Report.navigateToAndOpenReportWithAccountIDs([accountID])} | ||||||||||||||
onPress={() => ReportActions.navigateToAndOpenReportWithAccountIDs([accountID])} | ||||||||||||||
wrapperStyle={styles.breakAll} | ||||||||||||||
shouldShowRightIcon | ||||||||||||||
/> | ||||||||||||||
)} | ||||||||||||||
{!_.isEmpty(props.report) && !isCurrentUser && ( | ||||||||||||||
{!isEmptyObject(report) && !isCurrentUser && ( | ||||||||||||||
<MenuItem | ||||||||||||||
title={`${props.translate('privateNotes.title')}`} | ||||||||||||||
title={`${translate('privateNotes.title')}`} | ||||||||||||||
titleStyle={styles.flex1} | ||||||||||||||
icon={Expensicons.Pencil} | ||||||||||||||
onPress={() => ReportUtils.navigateToPrivateNotes(props.report, props.session)} | ||||||||||||||
onPress={() => ReportUtils.navigateToPrivateNotes(report, session)} | ||||||||||||||
wrapperStyle={styles.breakAll} | ||||||||||||||
shouldShowRightIcon | ||||||||||||||
brickRoadIndicator={Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} | ||||||||||||||
brickRoadIndicator={ReportActions.hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we fall back to empty string? /** The type of brick road indicator to show. */
brickRoadIndicator: PropTypes.oneOf([CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR, CONST.BRICK_ROAD_INDICATOR_STATUS.INFO, '']) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, with TS updates we can have one of the |
||||||||||||||
/> | ||||||||||||||
)} | ||||||||||||||
</ScrollView> | ||||||||||||||
|
@@ -254,49 +239,44 @@ function ProfilePage(props) { | |||||||||||||
); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
ProfilePage.propTypes = propTypes; | ||||||||||||||
ProfilePage.defaultProps = defaultProps; | ||||||||||||||
ProfilePage.displayName = 'ProfilePage'; | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* This function narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering | ||||||||||||||
* and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI. | ||||||||||||||
* @param {Object} [report] | ||||||||||||||
* @returns {Object|undefined} | ||||||||||||||
*/ | ||||||||||||||
const chatReportSelector = (report) => | ||||||||||||||
report && { | ||||||||||||||
const chatReportSelector = (report: OnyxEntry<Report>): Report => | ||||||||||||||
(report && { | ||||||||||||||
reportID: report.reportID, | ||||||||||||||
participantAccountIDs: report.participantAccountIDs, | ||||||||||||||
parentReportID: report.parentReportID, | ||||||||||||||
parentReportActionID: report.parentReportActionID, | ||||||||||||||
type: report.type, | ||||||||||||||
chatType: report.chatType, | ||||||||||||||
isPolicyExpenseChat: report.isPolicyExpenseChat, | ||||||||||||||
}; | ||||||||||||||
}) as Report; | ||||||||||||||
|
||||||||||||||
export default compose( | ||||||||||||||
withLocalize, | ||||||||||||||
withOnyx({ | ||||||||||||||
reports: { | ||||||||||||||
key: ONYXKEYS.COLLECTION.REPORT, | ||||||||||||||
selector: chatReportSelector, | ||||||||||||||
}, | ||||||||||||||
personalDetails: { | ||||||||||||||
key: ONYXKEYS.PERSONAL_DETAILS_LIST, | ||||||||||||||
}, | ||||||||||||||
session: { | ||||||||||||||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
key: ONYXKEYS.SESSION, | ||||||||||||||
}, | ||||||||||||||
report: { | ||||||||||||||
key: ({route, session, reports}) => { | ||||||||||||||
const accountID = Number(lodashGet(route.params, 'accountID', 0)); | ||||||||||||||
const reportID = lodashGet(ReportUtils.getChatByParticipants([accountID], reports), 'reportID', ''); | ||||||||||||||
if ((session && Number(session.accountID) === accountID) || Session.isAnonymousUser() || !reportID) { | ||||||||||||||
return null; | ||||||||||||||
} | ||||||||||||||
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; | ||||||||||||||
}, | ||||||||||||||
export default withOnyx<ProfilePageProps, ProfilePageOnyxProps>({ | ||||||||||||||
reports: { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pasyukevich We improved the const chatReportSelector = (report: OnyxEntry<Report>): Report =>
(report && {
reportID: report.reportID,
participantAccountIDs: report.participantAccountIDs,
parentReportID: report.parentReportID,
parentReportActionID: report.parentReportActionID,
type: report.type,
chatType: report.chatType,
isPolicyExpenseChat: report.isPolicyExpenseChat,
}) as Report; cc @youssef-lr There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, many thanks @fabioh8010! |
||||||||||||||
key: ONYXKEYS.COLLECTION.REPORT, | ||||||||||||||
selector: chatReportSelector, | ||||||||||||||
}, | ||||||||||||||
personalDetails: { | ||||||||||||||
key: ONYXKEYS.PERSONAL_DETAILS_LIST, | ||||||||||||||
}, | ||||||||||||||
session: { | ||||||||||||||
key: ONYXKEYS.SESSION, | ||||||||||||||
}, | ||||||||||||||
report: { | ||||||||||||||
key: ({route, session, reports}) => { | ||||||||||||||
const accountID = Number(route.params?.accountID ?? 0); | ||||||||||||||
const reportID = ReportUtils.getChatByParticipants([accountID], reports)?.reportID ?? ''; | ||||||||||||||
|
||||||||||||||
if ((Boolean(session) && Number(session?.accountID) === accountID) || SessionActions.isAnonymousUser() || !reportID) { | ||||||||||||||
return `${ONYXKEYS.COLLECTION.REPORT}0`; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The key should be always a key according to our types The key was updated from Before - |
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; | ||||||||||||||
}, | ||||||||||||||
}), | ||||||||||||||
)(ProfilePage); | ||||||||||||||
}, | ||||||||||||||
})(ProfilePage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit confused. Because we also have '@userActions/ReportActions' import in some other places
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that using a Report is even more confusing since it does not describe what it is (especially when we are using the ts and have types)