From ca1dbc809ed85277cd47cac7c2d173ebee6bebfe Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 19 Sep 2023 15:27:57 +0700 Subject: [PATCH 1/3] fix: 27252 Profile pic gets removed in offline mode --- src/components/Avatar.js | 6 +++--- src/components/AvatarWithImagePicker.js | 2 +- src/components/AvatarWithIndicator.js | 7 ++++++- src/components/LHNOptionsList/OptionRowLHNData.js | 1 + src/components/MentionSuggestions.js | 1 + src/components/MultipleAvatars.js | 6 +++++- src/components/RoomHeaderAvatars.js | 2 ++ src/components/SelectionList/UserListItem.js | 1 + src/components/SubscriptAvatar.js | 2 ++ src/components/UserDetailsTooltip/index.web.js | 1 + src/components/avatarPropTypes.js | 1 + src/components/menuItemPropTypes.js | 2 +- src/libs/ReportUtils.js | 8 ++++++++ src/libs/actions/PersonalDetails.js | 1 + src/pages/DetailsPage.js | 1 + src/pages/ProfilePage.js | 2 ++ .../home/report/ReportActionCompose/SuggestionMention.js | 1 + src/pages/home/report/ReportActionItemSingle.js | 3 ++- src/pages/home/sidebar/PressableAvatarWithIndicator.js | 1 + src/pages/settings/InitialSettingsPage.js | 1 + src/pages/settings/Profile/LoungeAccessPage.js | 1 + src/pages/settings/Profile/ProfilePage.js | 3 ++- 22 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index b96e60dd56d1..ac206b971a6e 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -40,7 +40,7 @@ const propTypes = { /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. * If the avatar is type === workspace, this fallback icon will be ignored and decided based on the name prop. */ - fallbackIcon: PropTypes.func, + fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), /** Denotes whether it is an avatar or a workspace avatar */ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), @@ -88,7 +88,7 @@ function Avatar(props) { pointerEvents="none" style={props.containerStyles} > - {_.isFunction(props.source) || imageError ? ( + {_.isFunction(props.source) || (imageError && _.isFunction(fallbackAvatar)) ? ( setImageError(true)} /> diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 3a43ede5a8f4..2b72e7600dc3 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -60,7 +60,7 @@ const propTypes = { size: PropTypes.oneOf([CONST.AVATAR_SIZE.LARGE, CONST.AVATAR_SIZE.DEFAULT]), /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.func, + fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), /** Denotes whether it is an avatar or a workspace avatar */ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js index b59f67a45b7b..72f09ce7cfc2 100644 --- a/src/components/AvatarWithIndicator.js +++ b/src/components/AvatarWithIndicator.js @@ -6,6 +6,7 @@ import styles from '../styles/styles'; import Tooltip from './Tooltip'; import * as UserUtils from '../libs/UserUtils'; import Indicator from './Indicator'; +import * as Expensicons from './Icon/Expensicons'; const propTypes = { /** URL for the avatar */ @@ -13,17 +14,21 @@ const propTypes = { /** To show a tooltip on hover */ tooltipText: PropTypes.string, + + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ + fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), }; const defaultProps = { tooltipText: '', + fallbackIcon: Expensicons.FallbackAvatar, }; function AvatarWithIndicator(props) { return ( - + diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 2c51d6332946..fe0506c7f55b 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -156,6 +156,7 @@ const personalDetailsSelector = (personalDetails) => firstName: personalData.firstName, status: personalData.status, avatar: UserUtils.getAvatar(personalData.avatar, personalData.accountID), + fallbackIcon: personalData.fallbackIcon }; return finalPersonalDetails; }, diff --git a/src/components/MentionSuggestions.js b/src/components/MentionSuggestions.js index 11df8a597ded..b3374279f66b 100644 --- a/src/components/MentionSuggestions.js +++ b/src/components/MentionSuggestions.js @@ -81,6 +81,7 @@ function MentionSuggestions(props) { name={item.icons[0].name} type={item.icons[0].type} fill={themeColors.success} + fallbackIcon={item.icons[0].fallbackIcon} /> @@ -184,6 +185,7 @@ function MultipleAvatars(props) { size={props.size} name={icon.name} type={icon.type} + fallbackIcon={icon.fallbackIcon} /> @@ -249,6 +251,7 @@ function MultipleAvatars(props) { imageStyles={[singleAvatarStyle]} name={props.icons[0].name} type={props.icons[0].type} + fallbackIcon={props.icons[0].fallbackIcon} /> @@ -270,6 +273,7 @@ function MultipleAvatars(props) { imageStyles={[singleAvatarStyle]} name={props.icons[1].name} type={props.icons[1].type} + fallbackIcon={props.icons[1].fallbackIcon} /> diff --git a/src/components/RoomHeaderAvatars.js b/src/components/RoomHeaderAvatars.js index af594dc2415a..92f294f056c7 100644 --- a/src/components/RoomHeaderAvatars.js +++ b/src/components/RoomHeaderAvatars.js @@ -49,6 +49,7 @@ function RoomHeaderAvatars(props) { size={CONST.AVATAR_SIZE.LARGE} name={props.icons[0].name} type={props.icons[0].type} + fallbackIcon={props.icons[0].fallbackIcon} /> )} @@ -93,6 +94,7 @@ function RoomHeaderAvatars(props) { containerStyles={[...iconStyle, StyleUtils.getAvatarBorderRadius(CONST.AVATAR_SIZE.LARGE_BORDERED, icon.type)]} name={icon.name} type={icon.type} + fallbackIcon={icon.fallbackIcon} /> )} diff --git a/src/components/SelectionList/UserListItem.js b/src/components/SelectionList/UserListItem.js index 014e0cf879a5..511dc16be636 100644 --- a/src/components/SelectionList/UserListItem.js +++ b/src/components/SelectionList/UserListItem.js @@ -25,6 +25,7 @@ function UserListItem({item, isFocused = false, showTooltip, onSelectRow, onDism source={lodashGet(item, 'avatar.source', '')} name={lodashGet(item, 'avatar.name', item.text)} type={lodashGet(item, 'avatar.type', CONST.ICON_TYPE_AVATAR)} + fallbackIcon={lodashGet(item, 'avatar.fallbackIcon')} /> ); diff --git a/src/components/SubscriptAvatar.js b/src/components/SubscriptAvatar.js index 038484e3f42d..81864d6e5af2 100644 --- a/src/components/SubscriptAvatar.js +++ b/src/components/SubscriptAvatar.js @@ -60,6 +60,7 @@ function SubscriptAvatar(props) { size={props.size || CONST.AVATAR_SIZE.DEFAULT} name={props.mainAvatar.name} type={props.mainAvatar.type} + fallbackIcon={props.mainAvatar.fallbackIcon} /> @@ -83,6 +84,7 @@ function SubscriptAvatar(props) { fill={themeColors.iconSuccessFill} name={props.secondaryAvatar.name} type={props.secondaryAvatar.type} + fallbackIcon={props.secondaryAvatar.fallbackIcon} /> diff --git a/src/components/UserDetailsTooltip/index.web.js b/src/components/UserDetailsTooltip/index.web.js index 5fdae15184ac..b6a987316141 100644 --- a/src/components/UserDetailsTooltip/index.web.js +++ b/src/components/UserDetailsTooltip/index.web.js @@ -48,6 +48,7 @@ function UserDetailsTooltip(props) { source={props.icon ? props.icon.source : UserUtils.getAvatar(userAvatar, userAccountID)} type={props.icon ? props.icon.type : CONST.ICON_TYPE_AVATAR} name={props.icon ? props.icon.name : userLogin} + fallbackIcon={lodashGet(props.icon,'fallbackIcon')} /> {title} diff --git a/src/components/avatarPropTypes.js b/src/components/avatarPropTypes.js index 12ee5c622b4f..a07de54ff481 100644 --- a/src/components/avatarPropTypes.js +++ b/src/components/avatarPropTypes.js @@ -6,4 +6,5 @@ export default PropTypes.shape({ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), name: PropTypes.string, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), }); diff --git a/src/components/menuItemPropTypes.js b/src/components/menuItemPropTypes.js index 53216ab7cdc7..b233f8c951b1 100644 --- a/src/components/menuItemPropTypes.js +++ b/src/components/menuItemPropTypes.js @@ -98,7 +98,7 @@ const propTypes = { interactive: PropTypes.bool, /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.func, + fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), /** Avatars to show on the right of the menu item */ floatRightAvatars: PropTypes.arrayOf(avatarPropTypes), diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 4b6512a543b0..8d1b5e4e18fe 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -924,6 +924,7 @@ function getIconsForParticipants(participants, personalDetails) { lodashGet(personalDetails, [accountID, 'displayName']) || lodashGet(personalDetails, [accountID, 'login'], ''), lodashGet(personalDetails, [accountID, 'firstName'], ''), avatarSource, + lodashGet(personalDetails, [accountID, 'fallBackIcon']), ]); } @@ -938,6 +939,7 @@ function getIconsForParticipants(participants, personalDetails) { source: sortedParticipantDetails[i][3], type: CONST.ICON_TYPE_AVATAR, name: sortedParticipantDetails[i][1], + fallBackIcon: sortedParticipantDetails[i][4] }; avatars.push(userIcon); } @@ -995,6 +997,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: parentReportAction.actorAccountID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'displayName'], ''), + fallbackIcon:lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']) }; return [memberIcon, workspaceIcon]; @@ -1009,6 +1012,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [actorAccountID, 'avatar']), actorAccountID), name: actorDisplayName, type: CONST.ICON_TYPE_AVATAR, + fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']) }; if (isWorkspaceThread(report)) { @@ -1023,6 +1027,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) }; if (isWorkspaceTaskReport(report)) { @@ -1055,6 +1060,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: report.ownerAccountID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) }; return isExpenseReport(report) ? [memberIcon, workspaceIcon] : [workspaceIcon, memberIcon]; } @@ -1064,6 +1070,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: report.managerID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.managerID, 'displayName'], ''), + fallbackIcon: lodashGet(personalDetails, [report.managerID, 'fallbackIcon']) }; const ownerIcon = { @@ -1071,6 +1078,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) }; return isPayer ? [managerIcon, ownerIcon] : [ownerIcon, managerIcon]; diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index dd80992e64a4..1c81f2d1be51 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -429,6 +429,7 @@ function updateAvatar(file) { avatar: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, originalFileName: null, }, + fallbackIcon: file.uri }, }, }, diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 7873c4daa00c..59724c973510 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -160,6 +160,7 @@ function DetailsPage(props) { imageStyles={[styles.avatarLarge]} source={UserUtils.getAvatar(details.avatar, details.accountID)} size={CONST.AVATAR_SIZE.LARGE} + fallbackIcon={details.fallbackIcon} /> diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 19f2b1fdc0c6..4dafdfc82d35 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -110,6 +110,7 @@ function ProfilePage(props) { const displayName = details.displayName ? details.displayName : props.translate('common.hidden'); const avatar = lodashGet(details, 'avatar', UserUtils.getDefaultAvatar()); + const fallbackIcon = lodashGet(details, 'fallbackIcon', ''); const originalFileName = lodashGet(details, 'originalFileName', ''); const login = lodashGet(details, 'login', ''); const timezone = lodashGet(details, 'timezone', {}); @@ -175,6 +176,7 @@ function ProfilePage(props) { imageStyles={[styles.avatarLarge]} source={UserUtils.getAvatar(avatar, accountID)} size={CONST.AVATAR_SIZE.LARGE} + fallbackIcon={fallbackIcon} /> diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index a76025b67b1e..93cc4a12dd81 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -169,6 +169,7 @@ function SuggestionMention({ name: detail.login, source: UserUtils.getAvatar(detail.avatar, detail.accountID), type: 'avatar', + fallbackIcon: detail.fallbackIcon }, ], }); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index bfbce8aed336..319dc94edc3e 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -90,7 +90,7 @@ const showWorkspaceDetails = (reportID) => { function ReportActionItemSingle(props) { const actorAccountID = props.action.actorAccountID; let {displayName} = props.personalDetailsList[actorAccountID] || {}; - const {avatar, login, pendingFields, status} = props.personalDetailsList[actorAccountID] || {}; + const {avatar, login, pendingFields, status, fallbackIcon} = props.personalDetailsList[actorAccountID] || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport, [props.action.actionName, props.iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(props.report) && (!actorAccountID || displayAllActors); @@ -198,6 +198,7 @@ function ReportActionItemSingle(props) { source={icon.source} type={icon.type} name={icon.name} + fallbackIcon={fallbackIcon} /> diff --git a/src/pages/home/sidebar/PressableAvatarWithIndicator.js b/src/pages/home/sidebar/PressableAvatarWithIndicator.js index ef6e663ce705..7b240c108e4e 100644 --- a/src/pages/home/sidebar/PressableAvatarWithIndicator.js +++ b/src/pages/home/sidebar/PressableAvatarWithIndicator.js @@ -52,6 +52,7 @@ function PressableAvatarWithIndicator({isCreateMenuOpen, currentUserPersonalDeta diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index a67e7cbc122e..080ade2680fb 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -353,6 +353,7 @@ function InitialSettingsPage(props) { imageStyles={[styles.avatarLarge]} source={UserUtils.getAvatar(props.currentUserPersonalDetails.avatar, props.session.accountID)} size={CONST.AVATAR_SIZE.LARGE} + fallbackIcon={props.currentUserPersonalDetails.fallbackIcon} /> diff --git a/src/pages/settings/Profile/LoungeAccessPage.js b/src/pages/settings/Profile/LoungeAccessPage.js index ce047bd9ccae..bbea526ca29c 100644 --- a/src/pages/settings/Profile/LoungeAccessPage.js +++ b/src/pages/settings/Profile/LoungeAccessPage.js @@ -74,6 +74,7 @@ function LoungeAccessPage(props) { imageStyles={[styles.avatarLarge]} source={UserUtils.getAvatar(props.currentUserPersonalDetails.avatar, props.session.accountID)} size={CONST.AVATAR_SIZE.LARGE} + fallbackIcon={props.currentUserPersonalDetails.fallbackIcon} /> { App.openProfile(props.currentUserPersonalDetails); }, [props.currentUserPersonalDetails]); - + return ( {_.map(profileSettingsOptions, (detail, index) => ( From dee6a5405e523a34b57f6347e741a6a316ebe80f Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 19 Sep 2023 15:40:58 +0700 Subject: [PATCH 2/3] fix lint --- src/components/Avatar.js | 4 ++-- src/components/AvatarWithImagePicker.js | 2 +- src/components/AvatarWithIndicator.js | 7 +++++-- src/components/LHNOptionsList/OptionRowLHNData.js | 2 +- src/components/MultipleAvatars.js | 2 +- src/components/UserDetailsTooltip/index.web.js | 2 +- src/components/avatarPropTypes.js | 2 +- src/components/menuItemPropTypes.js | 2 +- src/libs/ReportUtils.js | 14 +++++++------- src/libs/actions/PersonalDetails.js | 2 +- .../ReportActionCompose/SuggestionMention.js | 2 +- src/pages/settings/Profile/ProfilePage.js | 2 +- 12 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/components/Avatar.js b/src/components/Avatar.js index ac206b971a6e..a27073be15f0 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -40,7 +40,7 @@ const propTypes = { /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. * If the avatar is type === workspace, this fallback icon will be ignored and decided based on the name prop. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** Denotes whether it is an avatar or a workspace avatar */ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), @@ -106,7 +106,7 @@ function Avatar(props) { ) : ( setImageError(true)} /> diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 2b72e7600dc3..e633125812d6 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -60,7 +60,7 @@ const propTypes = { size: PropTypes.oneOf([CONST.AVATAR_SIZE.LARGE, CONST.AVATAR_SIZE.DEFAULT]), /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** Denotes whether it is an avatar or a workspace avatar */ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js index 72f09ce7cfc2..5e7b8d1ee632 100644 --- a/src/components/AvatarWithIndicator.js +++ b/src/components/AvatarWithIndicator.js @@ -16,7 +16,7 @@ const propTypes = { tooltipText: PropTypes.string, /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), }; const defaultProps = { @@ -28,7 +28,10 @@ function AvatarWithIndicator(props) { return ( - + diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index fe0506c7f55b..e8e1f8e85978 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -156,7 +156,7 @@ const personalDetailsSelector = (personalDetails) => firstName: personalData.firstName, status: personalData.status, avatar: UserUtils.getAvatar(personalData.avatar, personalData.accountID), - fallbackIcon: personalData.fallbackIcon + fallbackIcon: personalData.fallbackIcon, }; return finalPersonalDetails; }, diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index ec6edd4d5f65..1cdacb5fc1cc 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -25,7 +25,7 @@ const propTypes = { secondAvatarStyle: PropTypes.arrayOf(PropTypes.object), /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** Prop to identify if we should load avatars vertically instead of diagonally */ shouldStackHorizontally: PropTypes.bool, diff --git a/src/components/UserDetailsTooltip/index.web.js b/src/components/UserDetailsTooltip/index.web.js index b6a987316141..99ab6d18c7b4 100644 --- a/src/components/UserDetailsTooltip/index.web.js +++ b/src/components/UserDetailsTooltip/index.web.js @@ -48,7 +48,7 @@ function UserDetailsTooltip(props) { source={props.icon ? props.icon.source : UserUtils.getAvatar(userAvatar, userAccountID)} type={props.icon ? props.icon.type : CONST.ICON_TYPE_AVATAR} name={props.icon ? props.icon.name : userLogin} - fallbackIcon={lodashGet(props.icon,'fallbackIcon')} + fallbackIcon={lodashGet(props.icon, 'fallbackIcon')} /> {title} diff --git a/src/components/avatarPropTypes.js b/src/components/avatarPropTypes.js index a07de54ff481..915eac995fcb 100644 --- a/src/components/avatarPropTypes.js +++ b/src/components/avatarPropTypes.js @@ -6,5 +6,5 @@ export default PropTypes.shape({ type: PropTypes.oneOf([CONST.ICON_TYPE_AVATAR, CONST.ICON_TYPE_WORKSPACE]), name: PropTypes.string, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), }); diff --git a/src/components/menuItemPropTypes.js b/src/components/menuItemPropTypes.js index b233f8c951b1..6272a7a2ef7d 100644 --- a/src/components/menuItemPropTypes.js +++ b/src/components/menuItemPropTypes.js @@ -98,7 +98,7 @@ const propTypes = { interactive: PropTypes.bool, /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon: PropTypes.oneOfType([PropTypes.func,PropTypes.string]), + fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** Avatars to show on the right of the menu item */ floatRightAvatars: PropTypes.arrayOf(avatarPropTypes), diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 8d1b5e4e18fe..054f32654f11 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -939,7 +939,7 @@ function getIconsForParticipants(participants, personalDetails) { source: sortedParticipantDetails[i][3], type: CONST.ICON_TYPE_AVATAR, name: sortedParticipantDetails[i][1], - fallBackIcon: sortedParticipantDetails[i][4] + fallBackIcon: sortedParticipantDetails[i][4], }; avatars.push(userIcon); } @@ -997,7 +997,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: parentReportAction.actorAccountID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'displayName'], ''), - fallbackIcon:lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']), }; return [memberIcon, workspaceIcon]; @@ -1012,7 +1012,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [actorAccountID, 'avatar']), actorAccountID), name: actorDisplayName, type: CONST.ICON_TYPE_AVATAR, - fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']), }; if (isWorkspaceThread(report)) { @@ -1027,7 +1027,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), - fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']), }; if (isWorkspaceTaskReport(report)) { @@ -1060,7 +1060,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: report.ownerAccountID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), - fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']), }; return isExpenseReport(report) ? [memberIcon, workspaceIcon] : [workspaceIcon, memberIcon]; } @@ -1070,7 +1070,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, id: report.managerID, type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.managerID, 'displayName'], ''), - fallbackIcon: lodashGet(personalDetails, [report.managerID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [report.managerID, 'fallbackIcon']), }; const ownerIcon = { @@ -1078,7 +1078,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false, source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), type: CONST.ICON_TYPE_AVATAR, name: lodashGet(personalDetails, [report.ownerAccountID, 'displayName'], ''), - fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']) + fallbackIcon: lodashGet(personalDetails, [report.ownerAccountID, 'fallbackIcon']), }; return isPayer ? [managerIcon, ownerIcon] : [ownerIcon, managerIcon]; diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 1c81f2d1be51..201898324d07 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -429,7 +429,7 @@ function updateAvatar(file) { avatar: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, originalFileName: null, }, - fallbackIcon: file.uri + fallbackIcon: file.uri, }, }, }, diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 93cc4a12dd81..5dcef285bbe2 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -169,7 +169,7 @@ function SuggestionMention({ name: detail.login, source: UserUtils.getAvatar(detail.avatar, detail.accountID), type: 'avatar', - fallbackIcon: detail.fallbackIcon + fallbackIcon: detail.fallbackIcon, }, ], }); diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 6dfa8ee8eaba..50678972a90e 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -103,7 +103,7 @@ function ProfilePage(props) { useEffect(() => { App.openProfile(props.currentUserPersonalDetails); }, [props.currentUserPersonalDetails]); - + return ( Date: Fri, 22 Sep 2023 13:38:17 +0700 Subject: [PATCH 3/3] fix: infiniti loading --- src/components/AttachmentModal.js | 1 + .../AttachmentView/AttachmentViewImage/index.js | 3 ++- src/components/Attachments/AttachmentView/index.js | 11 ++++++++++- src/components/Avatar.js | 8 ++++++-- src/components/AvatarWithImagePicker.js | 1 + src/components/ImageView/index.js | 7 ++++++- src/libs/actions/PersonalDetails.js | 2 ++ src/pages/ProfilePage.js | 1 + 8 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 946b5e2ddec9..6a250c05e805 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -395,6 +395,7 @@ function AttachmentModal(props) { file={file} onToggleKeyboard={updateConfirmButtonVisibility} isWorkspaceAvatar={props.isWorkspaceAvatar} + fallbackSource={props.fallbackSource} /> ) )} diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js index 7de4417a4efc..48ac954ced7f 100755 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js @@ -12,13 +12,14 @@ const propTypes = { ...withLocalizePropTypes, }; -function AttachmentViewImage({source, file, isAuthTokenRequired, loadComplete, onPress, isImage, onScaleChanged, translate}) { +function AttachmentViewImage({source, file, isAuthTokenRequired, loadComplete, onPress, isImage, onScaleChanged, translate, onError}) { const children = ( ); return onPress ? ( diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.js index 47353d915060..1fc579977c9d 100755 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.js @@ -17,6 +17,7 @@ import AttachmentViewPdf from './AttachmentViewPdf'; import addEncryptedAuthTokenToURL from '../../../libs/addEncryptedAuthTokenToURL'; import * as StyleUtils from '../../../styles/StyleUtils'; import {attachmentViewPropTypes, attachmentViewDefaultProps} from './propTypes'; +import useNetwork from '../../../hooks/useNetwork'; const propTypes = { ...attachmentViewPropTypes, @@ -62,9 +63,14 @@ function AttachmentView({ translate, isFocused, isWorkspaceAvatar, + fallbackSource, }) { const [loadComplete, setLoadComplete] = useState(false); + const [imageError, setImageError] = useState(false); + + useNetwork({onReconnect: () => setImageError(false)}); + // Handles case where source is a component (ex: SVG) if (_.isFunction(source)) { let iconFillColor = ''; @@ -113,7 +119,7 @@ function AttachmentView({ if (isImage || (file && Str.isImage(file.name))) { return ( { + setImageError(true); + }} /> ); } diff --git a/src/components/Avatar.js b/src/components/Avatar.js index a27073be15f0..4f0eb60eb2e0 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; @@ -66,6 +66,10 @@ function Avatar(props) { useNetwork({onReconnect: () => setImageError(false)}); + useEffect(() => { + setImageError(false); + }, [props.source]); + if (!props.source) { return null; } @@ -81,7 +85,7 @@ function Avatar(props) { const iconStyle = props.imageStyles && props.imageStyles.length ? [StyleUtils.getAvatarStyle(props.size), styles.bgTransparent, ...props.imageStyles] : undefined; const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(props.name).fill : props.fill; - const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon; + const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(props.name) : props.fallbackIcon || Expensicons.FallbackAvatar; return ( {({show}) => ( diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index e0dce180043b..1d90a5723016 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -22,13 +22,16 @@ const propTypes = { /** image file name */ fileName: PropTypes.string.isRequired, + + onError: PropTypes.func, }; const defaultProps = { isAuthTokenRequired: false, + onError: () => {}, }; -function ImageView({isAuthTokenRequired, url, fileName}) { +function ImageView({isAuthTokenRequired, url, fileName, onError}) { const [isLoading, setIsLoading] = useState(true); const [containerHeight, setContainerHeight] = useState(0); const [containerWidth, setContainerWidth] = useState(0); @@ -238,6 +241,7 @@ function ImageView({isAuthTokenRequired, url, fileName}) { resizeMode={zoomScale > 1 ? Image.resizeMode.center : Image.resizeMode.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} + onError={onError} /> {(isLoading || zoomScale === 0) && } @@ -268,6 +272,7 @@ function ImageView({isAuthTokenRequired, url, fileName}) { resizeMode={Image.resizeMode.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} + onError={onError} /> diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 201898324d07..69cf05b89b34 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -480,6 +480,7 @@ function deleteAvatar() { value: { [currentUserAccountID]: { avatar: defaultAvatar, + fallbackIcon: null, }, }, }, @@ -491,6 +492,7 @@ function deleteAvatar() { value: { [currentUserAccountID]: { avatar: allPersonalDetails[currentUserAccountID].avatar, + fallbackIcon: allPersonalDetails[currentUserAccountID].fallbackIcon, }, }, }, diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index b0caac92e6c4..60d9d46f64b2 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -162,6 +162,7 @@ function ProfilePage(props) { source={UserUtils.getFullSizeAvatar(avatar, accountID)} isAuthTokenRequired originalFileName={originalFileName} + fallbackSource={fallbackIcon} > {({show}) => (