-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Refactor the WorkspaceSettingsPage #6033
Conversation
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.
This is ready for a review! Thank you very much for your time reviewing this.
I prefer this flow when the avatar image is saved immediately and users does not have to click the Save
button in order to save the image. I think this is better UX as will more likely be times when users adds the avatar and notices it being uploaded and exits the settings without saving just to be confused that the image has actually not been saved.
On the other hand, I cannot imagine situation when users would be confused that the picture has been uploaded before they clicked Save
.
However, I am open for discussion and happy to change this if you will think this approach is worse.
src/libs/actions/Policy.js
Outdated
.then((avatarURL) => { | ||
// Update the policy with the new avatarURL as soon as we get it | ||
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {avatarURL, isAvatarUploading: false}); | ||
update(policyID, {avatarURL}, true); |
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 am now updating the policy immediately when we get the s3bucket url so the avatar is saved straight away.
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 love this! I think this is a much better UX as well. When I first tested out this UI I thought it was a bug that the avatar wasn't saving on upload.
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.
Perfect, I felt the same!
src/libs/actions/Policy.js
Outdated
.then((response) => { | ||
if (response.jsonCode !== 200) { | ||
// Show the user error feedback | ||
throw new Error(); |
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.
Since I am now chaining two .then()
together, I could not just simply return
here as before (that would be supplied to the next then statement), so I have decided to throw new Error
, which will be then caught in the .catch()
.
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 was considering this... and I think it's mostly OK, except I don't really like that there are multiple .then()
s being chained together. This makes the code harder to follow and more complex than it needs to be. I think this can all be rewritten like this and it's more simple:
API.User_UploadAvatar({file})
.then((response) => {
if (response.jsonCode === 200) {
// Update the policy with the new avatarURL as soon as we get it
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {avatarURL: response.s3url, isAvatarUploading: false});
update(policyID, {avatarURL: response.s3url}, true);
return;
}
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {isAvatarUploading: false});
const errorMessage = translateLocal('workspace.editor.avatarUploadFailureMessage');
Growl.error(errorMessage, 5000);
});
The only change this makes is that there is no more catch()
so there won't be any HTTP errors caught either (but I'm not sure we really want to be catching those here anyway, and most API calls don't catch those in the action layer).
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.
Oh, I really like this approach, why have I not seen it before 😅 Thanks for bringing this up, it is way clearer!
Please hold, one of my PR is going to be merged soon #5271 which handles multiple issues. It's on hold since N6. |
@parasharrajat Thank you for heads up! I will need to resolve the merge conflicts first then. I guess this can wait for a review :) |
@mountiny Sorry, it is just merged. 🤣 |
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.
This looks much better, Vit! Thank you.
})).catch(() => { | ||
Growl.error(this.props.translate('workspace.editor.avatarUploadFailureMessage')); | ||
}).finally(() => Policy.updateLocalPolicyValues(this.props.policy.id, {isAvatarUploading: false})); | ||
Policy.updateLocalPolicyValues(this.props.policy.id, {isAvatarUploading: true}); |
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.
It looks like the three instances we have of Policy.updateLocalPolicyValues()
are all immediately before calling something that updates the policy. I think that this is confusing and also error-prone. What I would suggest is see about making Policy.updateLocalPolicyValues()
a local method that is not exported, and then just reference it internally inside the action (inside of Policy.update()
and Policy.uploadAvatar()
).
I think that keeping it internal will make the view code more simple (only have to call one method) and less error prone (don't have to worry about forgetting to call one of the methods).
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.
That is also a good point and I agree that it will be clearer if we keep it in the Policy actions. I have refactored it accordingly :)
src/libs/actions/Policy.js
Outdated
.then((response) => { | ||
if (response.jsonCode !== 200) { | ||
// Show the user error feedback | ||
throw new Error(); |
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 was considering this... and I think it's mostly OK, except I don't really like that there are multiple .then()
s being chained together. This makes the code harder to follow and more complex than it needs to be. I think this can all be rewritten like this and it's more simple:
API.User_UploadAvatar({file})
.then((response) => {
if (response.jsonCode === 200) {
// Update the policy with the new avatarURL as soon as we get it
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {avatarURL: response.s3url, isAvatarUploading: false});
update(policyID, {avatarURL: response.s3url}, true);
return;
}
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {isAvatarUploading: false});
const errorMessage = translateLocal('workspace.editor.avatarUploadFailureMessage');
Growl.error(errorMessage, 5000);
});
The only change this makes is that there is no more catch()
so there won't be any HTTP errors caught either (but I'm not sure we really want to be catching those here anyway, and most API calls don't catch those in the action layer).
Some pullerbear bug here, sorry Joel for notification, unassigning you. |
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.
@tgolen Thank you very much for your time reviewing this. I have addressed your comments and made the improvements. This is ready for another look 🙌
componentWillUnmount() { | ||
Policy.updateLocalPolicyValues(this.props.policy.id, {isAvatarUploading: false}); | ||
} | ||
|
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.
We won't need this block, because the entire upload process is now component agnostic and the picture or workspace details will upload even if user quits the component.
} | ||
|
||
submit() { | ||
if (this.props.policy.isAvatarUploading || !this.validate()) { | ||
if (this.props.policy.isPolicyUpdating || !this.validate()) { |
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.
Since I have separated the image upload flow from the other policy details, we need to use isPolicyUpdating
here.
if (this.props.policy.isAvatarUploading) { | ||
return; | ||
} | ||
this.setState({previewAvatarURL: image.uri}); |
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.
Additionally, since we are now uploading and saving the ne picture immediately after users selects it from Desktop, we need to make sure they won't select new image while the previous one is still being uploaded.
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.
Awesome, this looks really great now!! Nice work
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
@mountiny Can you confirm if this is |
@roryabraham This should be QA'ed for any regressions, I have forgot to add mention to the QA part that it should be same as tests. Fixed it now. |
🚀 Deployed to production by @roryabraham in version: 1.1.11-1 🚀
|
cc @HorusGoul @tgolen
Details
This PR refactors some bad smells:
WorkspaceSettingsPage.js
toactions/Policy.js
Additionally, I have added a small change in the flow (which we can discuss if we want to keep) which is the avatar being saved in the policy when it is uploaded. Currently, user needs to upload the avatar, wait for it to load and then click
Save
to actually save the avatar into the policy.After this refactoring, the avatar flow is independent on the name/currency flow, so the new picture is saved to our database once it is uploaded.
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/180546
Tests
This is a refactoring of the Workspace settings editor. We need to make sure uploading a new avatar for workspace or changing name/currency still works as expected.
General settings
.Save
.QA Steps
Same as tests above.
Tested On
Screenshots
Web
Screen.Recording.2021-10-25.at.19.28.09.mov
Mobile Web
Desktop
iOS
Android