Skip to content
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

Added a fallback icon when getting error on loading image #8852

Merged

Conversation

jayeshmangwani
Copy link
Contributor

Details

Fixed Issues

$ #8291

Tests

  1. Put your phone on airplane mode and turn off wifi
  2. Close the app if it's already open.
  3. Open or reopen the app
  4. If avatar images are not cached then it will fall back on the default

Another way to test

  1. Put your phone on airplane mode and turn off wifi
  2. Close the app if it's already open.
  3. Open or reopen the app
  4. press search from the header and go to search and search the user from the list and check the avatar for user
  • Verify that no errors appear in the JS console

PR Review Checklist

Contributor (PR Author) Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

PR Reviewer Checklist

  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

QA Steps

  1. Put your phone on airplane mode and turn off wifi
  2. Close the app if it's already open.
  3. Open or reopen the app
  4. If avatar images are not cached then it will fall back on the default

Another way to test

  1. Put your phone on airplane mode and turn off wifi
  2. Close the app if it's already open.
  3. Open or reopen the app
  4. press search from the header and go to search and search the user from the list and check the avatar for user
  • Verify that no errors appear in the JS console

Screenshots

Web

8291-web.mov

Mobile Web

8291-mWeb.mov

Desktop

8291-Desktop.mov

iOS

8291-ios.mov

Android

8291-Android.1.mp4

@jayeshmangwani jayeshmangwani requested a review from a team as a code owner May 2, 2022 08:26
@melvin-bot melvin-bot bot requested review from AndrewGable and Santhosh-Sellavel and removed request for a team May 2, 2022 08:26
@shawnborton
Copy link
Contributor

I also wanted to make sure you are addressing this issue here:
image

Where the avatar circles do not have a default background color set - regardless of whether or not we are able to load them. I think this can easily be fixed by simply adding a background color to the rounded view that wraps each avatar image.

@Santhosh-Sellavel
Copy link
Collaborator

I also wanted to make sure you are addressing this issue here: image

Where the avatar circles do not have a default background color set - regardless of whether or not we are able to load them. I think this can easily be fixed by simply adding a background color to the rounded view that wraps each avatar image.

@jayeshmangwani can you address this too!

import Connect from '../../../assets/images/connect.svg';

export {
ActiveRoomAvatar,
AdminRoomAvatar,
Android,
AnnounceRoomAvatar,
FallbackAvatar,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to alphabetical order, thanks!
i.e it should be added belowExpensifyCard

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Santhosh-Sellavel I have changed the export order for ExpensifyCard and also changed the import order for the same

constructor(props) {
super(props);
this.state = {
isErrorFetchingImage: false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isErrorFetchingImage can we rename it to imageError or showFallbackAvatar ?
@jayeshmangwani
cc: @AndrewGable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced isErrorFetchingImage with imageError

@jayeshmangwani
Copy link
Contributor Author

I also wanted to make sure you are addressing this issue here: image

Where the avatar circles do not have a default background color set - regardless of whether or not we are able to load them. I think this can easily be fixed by simply adding a background color to the rounded view that wraps each avatar image.

if we are adding background color to wrapper view then it will look like this screenshot, so I have added a background color to the image, and its working fine , grey color will apear untill image load

Screenshot 2022-05-02 at 11 21 21 PM

@Santhosh-Sellavel
Copy link
Collaborator

@shawnborton

@shawnborton
Copy link
Contributor

Isn't there a view that wraps the avatar image to give it the correct rounded crop? Can we add the background color to them?

@jayeshmangwani
Copy link
Contributor Author

Isn't there a view that wraps the avatar image to give it the correct rounded crop? Can we add the background color to them?

@shawnborton no, we are doing a rounded crop in image styling, let me send you code

function getAvatarStyle(size) {
const avatarSize = getAvatarSize(size);
return {
height: avatarSize,
width: avatarSize,
borderRadius: avatarSize,
};
}

getAvatarStyle is do the rounded crop and we are using this at Avatar

const imageStyle = [
StyleUtils.getAvatarStyle(this.props.size),
...this.props.imageStyles,
];

and imageStyle value using here

_.isFunction(this.props.source)
? <Icon src={this.props.source} fill={this.props.fill} height={iconSize} width={iconSize} />
: <Image source={{uri: this.props.source}} style={imageStyle} />

if you want me to add the background color at getAvatarStyle function then we can do that

@shawnborton
Copy link
Contributor

shawnborton commented May 2, 2022

Got it - are we able to set a background color on the image in some kind of way so we don't get a blank area while it loads? I know we can do something like this via CSS where you just give the image a background color and it shows even if the img src doesn't work or hasn't loaded yet. Otherwise we should wrap them so that we can achieve that.

@jayeshmangwani
Copy link
Contributor Author

if I am not wrong then this is already completed, I have added backgroundcolor to the image, and works like this,
the grey color will be visible until the image load

@shawnborton
Copy link
Contributor

Okay great! Are you able to show it in action via a screenshot so we can confirm?

@jayeshmangwani
Copy link
Contributor Author

grey-placeholder.mov

Yes sure, please check this video profile picture is taking time to load and default grey background is visible

@shawnborton
Copy link
Contributor

Perfect, thank you for sharing the video! Looks good to me.

@Santhosh-Sellavel
Copy link
Collaborator

All good then!

@Santhosh-Sellavel
Copy link
Collaborator

Screenshot 2022-05-03 at 3 38 19 AM

I think the avatar has a weird fill here, Is this the expected avatar @shawnborton can you confirm?

@shawnborton
Copy link
Contributor

Yeah, I believe the shape inside of the circle should be white.

@Santhosh-Sellavel
Copy link
Collaborator

@jayeshmangwani Can you check this out there could be some unnecessary fill while showing the fallback avatar thanks!

Comment on lines 62 to 63
// eslint-disable-next-line react/jsx-props-no-spreading
{...(!this.state.imageError ? {fill: this.props.fill} : {})}
Copy link
Collaborator

@Santhosh-Sellavel Santhosh-Sellavel May 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this is messy, can't we simplify it?

Let avoid this unnecessary comments
// eslint-disable-next-line react/jsx-props-no-spreading

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have to pass fill for the fallback avatar?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you trying to do here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if getting error then not passing the fill prop otherwise fill will be pass

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Santhosh-Sellavel

                           // eslint-disable-next-line react/jsx-props-no-spreading
                            {...(!this.state.imageError ? {fill: this.props.fill} : {})}

these lines were added for conditional fill prop for Icon component,
Please let me know that do we have to remove the fill prop for the fallback avatar? then I need to add a condition for the Icon component also, here default file is themeColor.icon

<IconToRender
width={width}
height={height}
fill={this.props.fill}
/>

fill: themeColors.icon,

@jayeshmangwani
Copy link
Contributor Author

Screenshot 2022-05-03 at 3 37 22 PM
@Santhosh-Sellavel added a condition for fill in fallback avtar, even though I am not able repoduce this issue on my side, please check latest code on your side, if issue is still solved or not

@Santhosh-Sellavel
Copy link
Collaborator

@jayeshmangwani
Sorry for the unnecessary confusion revert the last commit, if needed we can add it later. It seems to be working fine with old changes itself, I'm not sure what went wrong yesterday.

@Santhosh-Sellavel
Copy link
Collaborator

Santhosh-Sellavel commented May 3, 2022

@jayeshmangwani
Since this Avatar is a generic component used in some other views I tested those components. Seems it affects few view check below

1. Details page

Avatar size is broken
Screenshot 2022-05-03 at 11 32 42 PM

2. In App Settings page Workspace icon

@shawnborton do we have fallback image for workspace icon, or what should be do here.
Screenshot 2022-05-03 at 11 27 50 PM

3. In Workspaces Settings

Size also broken, same as 2
Screenshot 2022-05-03 at 11 28 01 PM

4. Workspace view - General Settings

Size also broken & same as 2
Screenshot 2022-05-03 at 11 27 56 PM

cc: @shawnborton @AndrewGable

@shawnborton
Copy link
Contributor

Good catch, let's fix all of those and ideally they all behave the same way - they have a background color applied to fill the space while the image loads, and we use a fallback icon if we can't fetch an image.

For the workspace icon, we could fall back on the default workspace icon (green background with a building) or we could take the same approach where we supply a gray version of the icon? Thoughts?

@Santhosh-Sellavel
Copy link
Collaborator

A gray version of the icon yes sounds good so that all fallbacks will have the same theme style. But I'm fine with the default green fill with the building also.

@shawnborton
Copy link
Contributor

Here is a gray fallback version of the workspace avatar:
fallback-workspace-avatar.svg.zip

@Santhosh-Sellavel
Copy link
Collaborator

Can you resolve those conflicts @jayeshmangwani

@jayeshmangwani
Copy link
Contributor Author

Can you resolve those conflicts @jayeshmangwani

@Santhosh-Sellavel I have merged the latest main to the current branch, and changes have been made that you have suggested except for 1 change, I have commented on the reason for the change let me give you the URL of the change
#8852 (comment)

please let me know what conflicts have to be solved,
Thanks

Copy link
Collaborator

@Santhosh-Sellavel Santhosh-Sellavel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jayeshmangwani
Need to update the comment otherwise looks good. Tests well for me.

Also @AndrewGable, can you have a look at this when you have time.

@@ -23,6 +24,9 @@ const propTypes = {

/** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue' */
fill: PropTypes.string,

/** Function for using fallback avatar */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t make any sense, can we add something more meaningful!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Santhosh-Sellavel does this looks good ?

A fallback avatar component to display when there is an error on loading remote URL

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A fallback avatar icon to display when there is an error on loading avatar from remote URL.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndrewGable Any thoughts?

@jayeshmangwani
Copy link
Contributor Author

@Santhosh-Sellavel should I update the comment if this comment message is final?
A fallback avatar icon to display when there is an error on loading avatar from remote URL.

@Santhosh-Sellavel
Copy link
Collaborator

Santhosh-Sellavel commented May 16, 2022

Looks good! @jayeshmangwani

@AndrewGable
Copy link
Contributor

@Santhosh-Sellavel - Can you approve? Then I will review! Thanks.

@Santhosh-Sellavel
Copy link
Collaborator

Santhosh-Sellavel commented May 17, 2022

PR Reviewer Checklist

  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

Copy link
Collaborator

@Santhosh-Sellavel Santhosh-Sellavel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good & tests well for me!

@AndrewGable AndrewGable merged commit 9a9e5a5 into Expensify:main May 17, 2022
@OSBotify
Copy link
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify
Copy link
Contributor

🚀 Deployed to staging by @AndrewGable in version: 1.1.63-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 failure ❌
🕸 web 🕸 success ✅

@OSBotify
Copy link
Contributor

🚀 Deployed to staging by @AndrewGable in version: 1.1.63-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@OSBotify
Copy link
Contributor

🚀 Deployed to production by @AndrewGable in version: 1.1.63-2 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@parasharrajat
Copy link
Member

Heads up! This PR was not carefully tested again style issues which caused this #12645

@parasharrajat
Copy link
Member

Heads up! This bug #12712 was caused due to missing offline tests on this PR.

@Santhosh-Sellavel
Copy link
Collaborator

Santhosh-Sellavel commented Jan 11, 2023

Heads up! This bug #12712 was caused due to missing offline tests on this PR.

I'm not sure how this caused that, here we have added only fallback if image was not available, can you brief @parasharrajat so we could understand what exactly went wrong, thanks!

@parasharrajat
Copy link
Member

parasharrajat commented Jan 11, 2023

So this PR added logic to handle image load failure via this.state.imageError. When the user comes online this state is not reset and the image is never loaded. More information can be present on the tagged issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants