fix(avatars): when Undefined an error promise will return #4502
Conversation
|
Thanks for this PR @divyabiyani! I left a giant comment inline about what I believe the correct fix should be. |
| @@ -163,6 +163,9 @@ define(function (require, exports, module) { | |||
| }, | |||
|
|
|||
| deleteDisplayedAccountProfileImage (account) { | |||
| if ( ! this._displayedProfileImage ) { | |||
shane-tomlinson
Dec 14, 2016
Member
It seems to me this solution sidesteps the problem but does not fix the cause of the problem, which seems to be inconsistent state. deleteDisplayedAccountProfileImage is called from removeAvatar in avatar_change.js. removeAvatar can only be called if we believe the account has a profile image. hasProfileImage is set if the account has a profileImageUrl.
So, we believe the account has a profileImageUrl, but this._displayedProfileImage is undefined, which means the assignment to this._displayedProfileImage has not been made.
This can happen because displayAccountProfileImage is called in avatar_change.js->afterVisible. afterVisible is called after the view is already visible (with the Clear button shown to the user), this means there is a window in which the user can click the Clear button before the profile image is fetched, and before this._displayedProfileImage is set.
This behavior was introduced in #3850 to fix #3543.
We are using the cached data in this._displayedProfileImage to get the profile image ID. The image ID should already be available on the account model, which is passed in to this function.
I believe the correct fix here is to not log and display the error you added here, but rather:
- Use the image ID from the account model. If there is no image ID on the account model, THEN log and display an error.
OR
- Ensure
this._displayedProfileImage is set by calling this.displayAccountProfileImage(account) and then deleting the avatar whenever that function call completes.
We might have to try both approaches to see which works best.
It seems to me this solution sidesteps the problem but does not fix the cause of the problem, which seems to be inconsistent state. deleteDisplayedAccountProfileImage is called from removeAvatar in avatar_change.js. removeAvatar can only be called if we believe the account has a profile image. hasProfileImage is set if the account has a profileImageUrl.
So, we believe the account has a profileImageUrl, but this._displayedProfileImage is undefined, which means the assignment to this._displayedProfileImage has not been made.
This can happen because displayAccountProfileImage is called in avatar_change.js->afterVisible. afterVisible is called after the view is already visible (with the Clear button shown to the user), this means there is a window in which the user can click the Clear button before the profile image is fetched, and before this._displayedProfileImage is set.
This behavior was introduced in #3850 to fix #3543.
We are using the cached data in this._displayedProfileImage to get the profile image ID. The image ID should already be available on the account model, which is passed in to this function.
I believe the correct fix here is to not log and display the error you added here, but rather:
- Use the image ID from the account model. If there is no image ID on the account model, THEN log and display an error.
OR
- Ensure
this._displayedProfileImageis set by callingthis.displayAccountProfileImage(account)and then deleting the avatar whenever that function call completes.
We might have to try both approaches to see which works best.
| @@ -45,6 +45,10 @@ define(function (require, exports, module) { | |||
| UNEXPECTED_ERROR: { | |||
| errno: 999, | |||
| message: t('Unexpected error') | |||
| }, | |||
| NO_DISPLAYED_PROFILEIMAGE_FOUND: { | |||
| errno: 1049, | |||
shane-tomlinson
Dec 14, 2016
Member
For any error that you add to a module, please use consecutive numbers, so this would be 1000. Errno's in each module are namespaced appropriately in our logs so we can tell the difference between errno 1000 in profile-errors and errno 1000 in auth-errors.
For any error that you add to a module, please use consecutive numbers, so this would be 1000. Errno's in each module are namespaced appropriately in our logs so we can tell the difference between errno 1000 in profile-errors and errno 1000 in auth-errors.
shane-tomlinson
Dec 14, 2016
Member
Also, I imagine in the end this won't be the error used. Instead it'll be something like NO_PROFILE_IMAGE_ID
Also, I imagine in the end this won't be the error used. Instead it'll be something like NO_PROFILE_IMAGE_ID
| }, | ||
| NO_DISPLAYED_PROFILEIMAGE_FOUND: { | ||
| errno: 1049, | ||
| message: t('No Displayed Profile Image') |
shane-tomlinson
Dec 14, 2016
Member
Please sentence case any errors, so the only word with a capital letter will be No
Please sentence case any errors, so the only word with a capital letter will be No
|
There were the following issues with your Pull Request
Guidelines are available at https://github.com/mozilla/fxa-content-server/blob/master/CONTRIBUTING.md#git-commit-guidelines This message was auto-generated by https://gitcop.com |
| @@ -96,7 +97,7 @@ define(function (require, exports, module) { | |||
| avatarWrapperEl.addClass('with-default'); | |||
| }, | |||
|
|
|||
| // Makes sure the account has an up-to-date image cache. | |||
| // Makes sure the account has an up-to-date image casche. | |||
vladikoff
Dec 14, 2016
Contributor
what happened here?
what happened here?
|
@divyabiyani if we go with using Also do we only call it when |
I'd say yes, or rather, a similar approach using the image ID from the account. I'd use the image ID from the account and only fetch the profile image if the account doesn't have an image ID already. Rationale is - something about storing and relying on ➜ scripts git:(master) pwd
/Users/stomlinson/development/fxa-content-server/app/scripts
➜ scripts git:(master) rgrep hasDisplayedAccountProfileImage .
./views/mixins/avatar-mixin.js: hasDisplayedAccountProfileImage () {
➜ scripts git:(master)To me it seems the optimal fix/cleanup is:
|
|
@divyabiyani see comment above ^, let us know if something is not clear. Try running |
|
There were the following issues with your Pull Request
Guidelines are available at https://github.com/mozilla/fxa-content-server/blob/master/CONTRIBUTING.md#git-commit-guidelines This message was auto-generated by https://gitcop.com |
| this.updateProfileImage(new ProfileImage(), account); | ||
| }); | ||
| if ( ! account.get('profileImageId') ) { | ||
| return account.fetchCurrentProfileImage() |
shane-tomlinson
Dec 16, 2016
•
Member
This looks like the proper solution. I'm going to propose a slight simplification that reworks lines 161-182 to use promise chaining and eliminate duplicate code but still Do The Right Thing.
return p().then(() => {
if (! account.get('profileImageId')) {
return account.fetchCurrentProfileImage()
.then((profileImage) => {
// Cache the result to make sure we don't flash the default
// image while fetching the latest profile image
// this should set the profileImageId on the account.
this._updateCachedProfileImage(profileImage, account);
});
}
// if we reach here, the account has a profile image ID already.
})
// by this point, the account will have an avatar and a profileImageId
.then(() => account.deleteAvatar(account.get('profileImageId')))
.then(() => this.updateProfileImage(new ProfileImage(), account));
Do you have much experience with promises? If not, they are a bit strange to get used to, but once you do, they are pretty great. A good intro is: https://developers.google.com/web/fundamentals/getting-started/primers/promises
This looks like the proper solution. I'm going to propose a slight simplification that reworks lines 161-182 to use promise chaining and eliminate duplicate code but still Do The Right Thing.
return p().then(() => {
if (! account.get('profileImageId')) {
return account.fetchCurrentProfileImage()
.then((profileImage) => {
// Cache the result to make sure we don't flash the default
// image while fetching the latest profile image
// this should set the profileImageId on the account.
this._updateCachedProfileImage(profileImage, account);
});
}
// if we reach here, the account has a profile image ID already.
})
// by this point, the account will have an avatar and a profileImageId
.then(() => account.deleteAvatar(account.get('profileImageId')))
.then(() => this.updateProfileImage(new ProfileImage(), account));Do you have much experience with promises? If not, they are a bit strange to get used to, but once you do, they are pretty great. A good intro is: https://developers.google.com/web/fundamentals/getting-started/primers/promises
divyabiyani
Dec 16, 2016
Author
Member
@shane-tomlinson Hi, I have been studying about promises from past few days. @vladikoff gave me the intro link. 😄
Also, is the code fine now?
@shane-tomlinson Hi, I have been studying about promises from past few days. @vladikoff gave me the intro link.
Also, is the code fine now?
70b0bc1
to
8dd2e2c
| return account.deleteAvatar(this._displayedProfileImage.get('id')) | ||
| return p() | ||
| .then(() => { | ||
| if ( ! account.get('profileImageId') ) { |
vladikoff
Dec 16, 2016
Contributor
review nit: no space before ! or here account.get('profileImageId') ) .
Remove the space before the last parenthesis.
@pdehaan is there an ESlint rule that can help us track these?
review nit: no space before ! or here account.get('profileImageId') ) .
Remove the space before the last parenthesis.
@pdehaan is there an ESlint rule that can help us track these?
| @@ -87,10 +86,6 @@ define(function (require, exports, module) { | |||
| }); | |||
| }, | |||
|
|
|||
| hasDisplayedAccountProfileImage () { | |||
| return this._displayedProfileImage && ! this._displayedProfileImage.isDefault(); | |||
| }, | |||
|
Great work @divyabiyani ! |
fixes #4386.