Skip to content

Commit

Permalink
Merge pull request #27757 from tienifr/fix/27252
Browse files Browse the repository at this point in the history
fix: 27252 Profile pic gets removed in offline mode
  • Loading branch information
srikarparsi authored Sep 26, 2023
2 parents e3f32a0 + 2d2aeee commit a63199e
Show file tree
Hide file tree
Showing 26 changed files with 76 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ function AttachmentModal(props) {
file={file}
onToggleKeyboard={updateConfirmButtonVisibility}
isWorkspaceAvatar={props.isWorkspaceAvatar}
fallbackSource={props.fallbackSource}
/>
)
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
<ImageView
onScaleChanged={onScaleChanged}
url={source}
fileName={file.name}
isAuthTokenRequired={isImage && isAuthTokenRequired}
onError={onError}
/>
);
return onPress ? (
Expand Down
11 changes: 10 additions & 1 deletion src/components/Attachments/AttachmentView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = '';
Expand Down Expand Up @@ -113,7 +119,7 @@ function AttachmentView({
if (isImage || (file && Str.isImage(file.name))) {
return (
<AttachmentViewImage
source={source}
source={imageError ? fallbackSource : source}
file={file}
isAuthTokenRequired={isAuthTokenRequired}
isUsedInCarousel={isUsedInCarousel}
Expand All @@ -122,6 +128,9 @@ function AttachmentView({
isImage={isImage}
onPress={onPress}
onScaleChanged={onScaleChanged}
onError={() => {
setImageError(true);
}}
/>
);
}
Expand Down
14 changes: 9 additions & 5 deletions src/components/Avatar.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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]),
Expand All @@ -66,6 +66,10 @@ function Avatar(props) {

useNetwork({onReconnect: () => setImageError(false)});

useEffect(() => {
setImageError(false);
}, [props.source]);

if (!props.source) {
return null;
}
Expand All @@ -81,14 +85,14 @@ 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 (
<View
pointerEvents="none"
style={props.containerStyles}
>
{_.isFunction(props.source) || imageError ? (
{_.isFunction(props.source) || (imageError && _.isFunction(fallbackAvatar)) ? (
<View style={iconStyle}>
<Icon
src={imageError ? fallbackAvatar : props.source}
Expand All @@ -106,7 +110,7 @@ function Avatar(props) {
) : (
<View style={[iconStyle, StyleUtils.getAvatarBorderStyle(props.size, props.type), ...props.iconAdditionalStyles]}>
<Image
source={{uri: props.source}}
source={{uri: imageError ? fallbackAvatar : props.source}}
style={imageStyle}
onError={() => setImageError(true)}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/components/AvatarWithImagePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,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]),
Expand Down Expand Up @@ -296,6 +296,7 @@ class AvatarWithImagePicker extends React.Component {
headerTitle={this.props.headerTitle}
source={this.props.previewSource}
originalFileName={this.props.originalFileName}
fallbackSource={this.props.fallbackIcon}
>
{({show}) => (
<AttachmentPicker type={CONST.ATTACHMENT_PICKER_TYPE.IMAGE}>
Expand Down
10 changes: 9 additions & 1 deletion src/components/AvatarWithIndicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@ 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 */
source: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,

/** 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 (
<Tooltip text={props.tooltipText}>
<View style={[styles.sidebarAvatar]}>
<Avatar source={UserUtils.getSmallSizeAvatar(props.source)} />
<Avatar
source={UserUtils.getSmallSizeAvatar(props.source)}
fallbackIcon={props.fallbackIcon}
/>
<Indicator />
</View>
</Tooltip>
Expand Down
7 changes: 6 additions & 1 deletion src/components/ImageView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) && <FullscreenLoadingIndicator style={[styles.opacity1, styles.bgTransparent]} />}
</View>
Expand Down Expand Up @@ -268,6 +272,7 @@ function ImageView({isAuthTokenRequired, url, fileName}) {
resizeMode={Image.resizeMode.contain}
onLoadStart={imageLoadingStart}
onLoad={imageLoad}
onError={onError}
/>
</PressableWithoutFeedback>

Expand Down
1 change: 1 addition & 0 deletions src/components/LHNOptionsList/OptionRowLHNData.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},
Expand Down
1 change: 1 addition & 0 deletions src/components/MentionSuggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
</View>
<Text
Expand Down
6 changes: 5 additions & 1 deletion src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.func,
fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),

/** Prop to identify if we should load avatars vertically instead of diagonally */
shouldStackHorizontally: PropTypes.bool,
Expand Down Expand Up @@ -134,6 +134,7 @@ function MultipleAvatars(props) {
fill={themeColors.iconSuccessFill}
name={props.icons[0].name}
type={props.icons[0].type}
fallbackIcon={props.icons[0].fallbackIcon}
/>
</View>
</UserDetailsTooltip>
Expand Down Expand Up @@ -184,6 +185,7 @@ function MultipleAvatars(props) {
size={props.size}
name={icon.name}
type={icon.type}
fallbackIcon={icon.fallbackIcon}
/>
</View>
</UserDetailsTooltip>
Expand Down Expand Up @@ -249,6 +251,7 @@ function MultipleAvatars(props) {
imageStyles={[singleAvatarStyle]}
name={props.icons[0].name}
type={props.icons[0].type}
fallbackIcon={props.icons[0].fallbackIcon}
/>
</View>
</UserDetailsTooltip>
Expand All @@ -270,6 +273,7 @@ function MultipleAvatars(props) {
imageStyles={[singleAvatarStyle]}
name={props.icons[1].name}
type={props.icons[1].type}
fallbackIcon={props.icons[1].fallbackIcon}
/>
</View>
</UserDetailsTooltip>
Expand Down
2 changes: 2 additions & 0 deletions src/components/RoomHeaderAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
</PressableWithoutFocus>
)}
Expand Down Expand Up @@ -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}
/>
</PressableWithoutFocus>
)}
Expand Down
1 change: 1 addition & 0 deletions src/components/SelectionList/UserListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')}
/>
);

Expand Down
2 changes: 2 additions & 0 deletions src/components/SubscriptAvatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
</View>
</UserDetailsTooltip>
Expand All @@ -83,6 +84,7 @@ function SubscriptAvatar(props) {
fill={themeColors.iconSuccessFill}
name={props.secondaryAvatar.name}
type={props.secondaryAvatar.type}
fallbackIcon={props.secondaryAvatar.fallbackIcon}
/>
</View>
</UserDetailsTooltip>
Expand Down
1 change: 1 addition & 0 deletions src/components/UserDetailsTooltip/index.web.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')}
/>
</View>
<Text style={[styles.mt2, styles.textMicroBold, styles.textReactionSenders, styles.textAlignCenter]}>{title}</Text>
Expand Down
1 change: 1 addition & 0 deletions src/components/avatarPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]),
});
2 changes: 1 addition & 1 deletion src/components/menuItemPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
9 changes: 8 additions & 1 deletion src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ function getIconsForParticipants(participants, personalDetails) {
const accountID = participantsList[i];
const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar'], ''), accountID);
const displayNameLogin = lodashGet(personalDetails, [accountID, 'displayName']) || lodashGet(personalDetails, [accountID, 'login'], '');
participantDetails.push([accountID, displayNameLogin, avatarSource]);
participantDetails.push([accountID, displayNameLogin, avatarSource, lodashGet(personalDetails, [accountID, 'fallBackIcon'])]);
}

const sortedParticipantDetails = _.chain(participantDetails)
Expand All @@ -975,6 +975,7 @@ function getIconsForParticipants(participants, personalDetails) {
source: sortedParticipantDetails[i][2],
type: CONST.ICON_TYPE_AVATAR,
name: sortedParticipantDetails[i][1],
fallBackIcon: sortedParticipantDetails[i][3],
};
avatars.push(userIcon);
}
Expand Down Expand Up @@ -1031,6 +1032,7 @@ function getIcons(report, personalDetails, defaultIcon = null, defaultName = '',
id: parentReportAction.actorAccountID,
type: CONST.ICON_TYPE_AVATAR,
name: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'displayName'], ''),
fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']),
};

return [memberIcon, workspaceIcon];
Expand All @@ -1045,6 +1047,7 @@ function getIcons(report, personalDetails, defaultIcon = null, defaultName = '',
source: UserUtils.getAvatar(lodashGet(personalDetails, [actorAccountID, 'avatar']), actorAccountID),
name: actorDisplayName,
type: CONST.ICON_TYPE_AVATAR,
fallbackIcon: lodashGet(personalDetails, [parentReportAction.actorAccountID, 'fallbackIcon']),
};

if (isWorkspaceThread(report)) {
Expand All @@ -1059,6 +1062,7 @@ function getIcons(report, personalDetails, defaultIcon = null, defaultName = '',
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)) {
Expand Down Expand Up @@ -1091,6 +1095,7 @@ function getIcons(report, personalDetails, defaultIcon = null, defaultName = '',
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];
}
Expand All @@ -1100,12 +1105,14 @@ function getIcons(report, personalDetails, defaultIcon = null, defaultName = '',
id: report.managerID,
type: CONST.ICON_TYPE_AVATAR,
name: lodashGet(personalDetails, [report.managerID, 'displayName'], ''),
fallbackIcon: lodashGet(personalDetails, [report.managerID, 'fallbackIcon']),
};
const ownerIcon = {
id: report.ownerAccountID,
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']),
};
const isPayer = currentUserAccountID === report.managerID;

Expand Down
Loading

0 comments on commit a63199e

Please sign in to comment.