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

Only debounce saving draft when user is typing #11552

Merged
merged 3 commits into from
Oct 4, 2022

Conversation

youssef-lr
Copy link
Contributor

@youssef-lr youssef-lr commented Oct 3, 2022

Details

We were debouncing the call to Report.saveReportComment even when the change in comment is not coming from the user typing. This caused a race condition between these two calls:

  • componentDidUpdate calls debounced Report.saveReportComment (through updateComment) with previous Onyx state which contains the draft.
  • submitForm calls debounced Report.saveReportComment (through updateComment) with new empty comment to clear the composer.

Fixed Issues

$ #11526

Tests

Test 1

  • Open a blank conversation with no past messages.
  • Type in something, hit send.
  • Verify the text sent does not reappear in the composer.

Test 2

  • Open any conversation.
  • Type something in the composer but do not hit send, refresh the page (On mobile, close and then re-open the app).
  • Hit send.
  • Verify the text sent does not reappear in the composer.

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 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.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

PR Reviewer Checklist

The Contributor+ will copy/paste it into a new comment and complete it after the author checklist is completed

  • I have verified the author checklist is complete (all boxes are checked off).
  • 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
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • 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 other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • 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.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

QA Steps

Test 1

  • Open a blank conversation with no past messages.
  • Type in something, hit send.
  • Verify the text sent does not reappear in the composer.

Test 2

  • Open any conversation.
  • Type something in the composer but do not hit send, refresh the page (On mobile, close and then re-open the app).
  • Hit send.
  • Verify the text sent does not reappear in the composer.

Screenshots

Web

SR.-.Web.mov

Mobile Web - Chrome

Mobile Web - Safari

Desktop

iOS

Android

@youssef-lr youssef-lr self-assigned this Oct 3, 2022
@youssef-lr youssef-lr requested a review from a team as a code owner October 3, 2022 21:32
@github-actions
Copy link
Contributor

github-actions bot commented Oct 3, 2022

⚠️ ⚠️ Heads up! This pull request has the CP Staging label. ⚠️ ⚠️
Merging it will cause it to be immediately deployed to staging, even if the open StagingDeployCash deploy checklist is locked.

@melvin-bot melvin-bot bot requested review from techievivek and removed request for a team October 3, 2022 21:33
@youssef-lr youssef-lr requested a review from a team October 3, 2022 21:33
@melvin-bot melvin-bot bot requested review from deetergp and removed request for a team October 3, 2022 21:33
tgolen
tgolen previously approved these changes Oct 3, 2022
Copy link
Contributor

@tgolen tgolen left a comment

Choose a reason for hiding this comment

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

This looks OK, but I've got this nagging feeling that I don't know that componentDidUpdate() should be calling updateComment() directly. It feels like there is a lot of logic in updateComment() that is unnecessary in that context, but maybe I am wrong. Same thing goes for componentDidMount() and all the other methods calling updateComment(). It's like updateComment() is just a catch-all for a bunch of different things, and I think race conditions like this are a good indication that this is a bad pattern.

@tgolen
Copy link
Contributor

tgolen commented Oct 3, 2022

Can you please add videos from the different platforms to the description?

@tgolen
Copy link
Contributor

tgolen commented Oct 3, 2022

  • I have verified the author checklist is complete (all boxes are checked off).
  • 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
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • 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 other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • 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.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

@tgolen
Copy link
Contributor

tgolen commented Oct 3, 2022

BUG - WEB: While doing test 1, I can still reproduce the problem.

  1. Type a new comment
  2. Press enter or send very quickly after finished typing

Actually happening: Draft message disappears, but then reappears after a couple of seconds

Expected to happen: Draft message disappear forever

BUG: - WEB: While doing test 2, when I refresh the page, the draft message is no longer in the composer, it is blank, so the test cannot be performed.

  1. Type a new comment
  2. Refresh the page

Actually happening: Draft message disappears

Expected to happen: Draft message is still there

@youssef-lr
Copy link
Contributor Author

youssef-lr commented Oct 3, 2022

BUG - WEB: While doing test 1, I can still reproduce the problem.

Type a new comment
Press enter or send very quickly after finished typing
Actually happening: Draft message disappears, but then reappears after a couple of seconds

Good catch, I just pushed a fix for this. Basically when you type something and then quickly press enter, a few ms later the debounced saveReportComment will save to Onyx what you were typing. So I went with cancelling it right before we submit the form.

BUG: - WEB: While doing test 2, when I refresh the page, the draft message is no longer in the composer, it is blank, so the test cannot be performed.

Sorry should have been more clear, but you need to wait a second or two for Onyx state to be saved before refreshing. If you refresh right away the state won't be saved as it's debounced.

Adding recordings shortly. Let me know if the last commit fixes the issues!

deetergp
deetergp previously approved these changes Oct 3, 2022
Copy link
Contributor

@deetergp deetergp left a comment

Choose a reason for hiding this comment

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

Code looks good to me and test well on desktop, web, & mobile web. I'm assuming this can't be tested in native mobile since the testing steps require a refresh?

@youssef-lr
Copy link
Contributor Author

I'm assuming this can't be tested in native mobile since the testing steps require a refresh?

Good catch. For mobile native we can close/reopen the app to simulate the same scenario. Will update the tests in the issue.

@deetergp
Copy link
Contributor

deetergp commented Oct 3, 2022

Confirmed tests pass on both native mobile platforms 👍

@youssef-lr
Copy link
Contributor Author

youssef-lr commented Oct 4, 2022

It feels like there is a lot of logic in updateComment() that is unnecessary in that context, but maybe I am wrong. Same thing goes for componentDidMount() and all the other methods calling updateComment(). It's like updateComment() is just a catch-all for a bunch of different things, and I think race conditions like this are a good indication that this is a bad pattern.

@tgolen I totally agree. I think we may need to rethink how we're using updateComment in that file and potentially refactor it to be more predictable. I'll create a separate issue for this.

@tgolen
Copy link
Contributor

tgolen commented Oct 4, 2022

OK, I've completed testing and the checklist, so I will merge this.

@tgolen tgolen merged commit 1a42cf8 into main Oct 4, 2022
@tgolen tgolen deleted the youssef_fix_message_reappearing branch October 4, 2022 21:49
@melvin-bot melvin-bot bot added the Emergency label Oct 4, 2022
@melvin-bot
Copy link

melvin-bot bot commented Oct 4, 2022

@tgolen looks like this was merged without passing tests. Please add a note explaining why this was done and remove the Emergency label if this is not an emergency.

@youssef-lr
Copy link
Contributor Author

Tests passed Melvin.

@mallenexpensify
Copy link
Contributor

What's status on this? I see the CP Staging label, should this be fixed on staging now?

@tgolen
Copy link
Contributor

tgolen commented Oct 5, 2022

It should be... but it looks like it wasn't CPed for some reason. @AndrewGable are you seeing issues with the CP staging at all this week?

@OSBotify
Copy link
Contributor

OSBotify commented Oct 6, 2022

🚀 Deployed to staging by @tgolen in version: 1.2.12-0 🚀

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

@OSBotify
Copy link
Contributor

🚀 Deployed to production by @AndrewGable in version: 1.2.12-4 🚀

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

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.

5 participants