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

#3293 update last message details on message deletion #3526

Conversation

dklymenk
Copy link
Contributor

@dklymenk dklymenk commented Jun 10, 2021

Details

Fixes incorrect display of last message on LHN after deleting last message of the chat.

Fixed Issues

Fixes #3293

Tests/QA Steps

  1. Log in to the app
  2. Navigate to a conversation
  3. Send a message
  4. Delete the message
  5. Message in LHN should be updated in a few moments.
  6. Refresh the page
  7. Message shown on LHN should be the last one you have in that chat.

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

screen.mp4

Mobile Web

screen1.mp4

Desktop

Screen.Recording.2021-06-10.at.18.24.04.mov

iOS

Simulator.Screen.Recording.-.iPhone.12.-.2021-06-10.at.18.20.53.mp4

Android

3293.mp4

@dklymenk dklymenk marked this pull request as ready for review June 10, 2021 21:52
@dklymenk dklymenk requested a review from a team as a code owner June 10, 2021 21:52
@MelvinBot MelvinBot requested review from Beamanator and removed request for a team June 10, 2021 21:52
@Beamanator
Copy link
Contributor

I'm sorry if I'm missing lots of the context behind why you did this a certain way, but why can't we just let the pusher event (Pusher.TYPE.REPORT_COMMENT_EDIT) update the LHN when deleting a comment, as it does when we edit a comment, since they're basically the same thing & call the same API?

@dklymenk
Copy link
Contributor Author

@Beamanator This particular MR and the linked issue are about deletion on sender's screen, and not on participants screens.

There is an issue with deleting messages on other people screens which I have reported here: #3524

Or do you think that once that issue is resolved we will be able to move this whole logic into updateReportActionMessage (function that ultimately handlesPusher.TYPE.REPORT_COMMENT_EDIT action)?

@dklymenk
Copy link
Contributor Author

Actually, I agree. Since we are not doing an optimistic update here, there is no reason to do it the way I did it.

However, I would assume down the line you would want every API action to have an optimistic update. This a quote from @NikkiWines from the original issue:

we typically prefer an optimistic approach but it looks like updateReportActionMessage isn't being used optimistically at the moment. You can go with the simpler non-optimistic approach

Not really sure if that implies, that down the line the behavior of updateReportActionMessage might be changed.

Anyway, I do not mind moving my code to updateReportActionMessage, however I will only be able to do it after #3524 is resolved, because at the moment sequenceNumber client gets from BE on comment deletion over WS is incorrect.

@Beamanator
Copy link
Contributor

However, I would assume down the line you would want every API action to have an optimistic update. This a quote from @NikkiWines from the original issue:

we typically prefer an optimistic approach but it looks like updateReportActionMessage isn't being used optimistically at the moment. You can go with the simpler non-optimistic approach

Not really sure if that implies, that down the line the behavior of updateReportActionMessage might be changed.

Yeah I believe you're right - for now I think we can go the simpler way as long as we still get the necessary fix we need 😅

Anyway, I do not mind moving my code to updateReportActionMessage, however I will only be able to do it after #3524 is resolved, because at the moment sequenceNumber client gets from BE on comment deletion over WS is incorrect.

Oof yeah good point, I've been seeing those weird -1 sequenceNumber things so I was wondering where that came from, good catch!

Copy link
Contributor

@NikkiWines NikkiWines left a comment

Choose a reason for hiding this comment

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

Couple of small comments

src/libs/actions/Report.js Show resolved Hide resolved
src/libs/actions/Report.js Show resolved Hide resolved
@dklymenk dklymenk requested a review from NikkiWines June 15, 2021 10:25
@dklymenk dklymenk requested a review from NikkiWines June 17, 2021 11:13
// Update last message information in the report object
Onyx.connect({
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
callback: _.once((reportActionList) => {
Copy link
Member

Choose a reason for hiding this comment

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

Sorry but the use of _.once is very bad here. You are creating n number of subscriptions without releasing them. Where n === no of deleted comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sorry but _.once is required there. If you remove _.once and put console.log inside that callback, you'll see that the callback is called 2-3 times per message delete call. Can you please explain what exactly is the big downside to this approach? Is it using extra memory?

Copy link
Member

Choose a reason for hiding this comment

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

So you can call Onyx.disconnect here after the operation is done. Otherwise, this subscription will remain, and On the next delete operation, both old ones and the new ones will be called. And I believe that this _.once will not be required then. But it is possible that while you are listening to this key, other factors update the Store, and thus this callback fire multiple times.
So yeah just Onyx.disconnect will be fine. _.once has not drawbacks just I was thinking that it won't be required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I'll check that out and update PR accordingly if that helps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That actually works flawlessly. Thanks.

PR is updated.

Comment on lines 1090 to 1094
// Update last message information in the report object
Onyx.connect({
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
callback: _.once((reportActionList) => {
// Find last non-empty message
Copy link
Member

Choose a reason for hiding this comment

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

Sorry to pitch in very late but this solution does not seem to follow the optimistic approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The decision to ditch optimistic approach is validated with Expensify engineers. The tl;dr is since the edit comment doesn't update last message optimistically, there is no reason the delete comment should.

Copy link
Member

@parasharrajat parasharrajat Jun 20, 2021

Choose a reason for hiding this comment

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

https://github.com/Expensify/Expensify.cash/blob/3c54c020bfab47a66f75d3dd8a8cc158b2337e42/src/libs/actions/Report.js#L1061-L1072
This is happening optimistically. In simpler words, we delete the comment from the Onyx even though we have not called the API yet. and then based on API response if there was a failure we revert it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sir, how is that code snippet you linked relevant to what I said?

the edit comment doesn't update last message optimistically

The code snippet you sent doesn't have anything to do with edit comment action or LHN.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, just saw a post from Nikki about this. It's fine then. But can't we do this before the API call. and revert those changes if the response is not 200?

Copy link
Contributor Author

@dklymenk dklymenk Jun 20, 2021

Choose a reason for hiding this comment

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

Yes, we can. That's actually what I was going to do initially, but since Nikki said there is no need to do it optimistically, I happily abandoned that idea. Anyway I didn't want to think about any potential race conditions with both optimistic update and then revert update happening in async manner, deleting multiple comments in a quick succession, etc.

lastMessageDetails.lastActorEmail = '';
}

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, lastMessageDetails);
Copy link
Member

Choose a reason for hiding this comment

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

This statement causes a recursion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you please explain how does it cause a recursion? Is there some specific edge case I didn't see. The code runs only once after the response from BE arrives.

Copy link
Member

@parasharrajat parasharrajat Jun 20, 2021

Choose a reason for hiding this comment

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

Oh sorry didn't notice at first sight that this is a different subscription key.

lastMessageDetails.lastActorEmail = '';
}

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, lastMessageDetails);
Copy link
Member

Choose a reason for hiding this comment

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

Please release the connection here before this statement. And remove _.once. Could you please try to think of your solution in an optimistic way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am aware this is not the best approach. As I stated in one of the messages above, ideally we move this entire code to updateReportActionMessage function and have the handling for delete action next to edit action. However, until #3524 is resolved this cannot be done for delete action.

@dklymenk
Copy link
Contributor Author

Hello, @NikkiWines.

Pinging you just in case you forgot about this PR. There has been no activity from Expensify team members here for more than 5 days now.

Please let me know if this is blocked because of some other PR and if I need to do any more changes.

Thanks.

@NikkiWines
Copy link
Contributor

NikkiWines commented Jun 23, 2021

hi @dklymenk, apologies for the delay on our end.

Just so you're aware, it looks like #3524 has been resolved, so I'm not sure if you'd like to make additional modifications based on that.

Additionally, I would recommend posting to #expensify-open-source (as recommended here) regarding the usage of _.once here to get some more insight on whether or not this is the best strategy to handle multiple callbacks.

@dklymenk
Copy link
Contributor Author

Hello

Additionally, I would recommend posting to #expensify-open-source (as recommended here) regarding the usage of _.once here to get some more insight on whether or not this is the best strategy to handle multiple callbacks.

Actually, thanks to a suggestion by @parasharrajat, there is no more _.once in this PR. So actually it is good as is.

Just so you're aware, it looks like #3524 has been resolved, so I'm not sure if you'd like to make additional modifications based on that.

A change that I would have made here is taking all the code this PR adds to deleteReportComment and move it to updateReportActionMessage. However, I have an issue with that function: if you delete a message, reportMaxSequenceNumbers is not updated.

This means that if you delete last message and then edit the next last message, the LHN is not updated because you are editing a message that does not have maxSequenceNumber according to reportMaxSequenceNumbers even though it is technically the last message.

So, I suggest we merge this PR as is. There is no bugs and it doesn't do anything sub-optimal. I, however, think that moving that code to updateReportActionMessage would be cleaner. So I'll create a separate issue with proposal on how we can get a reliable maxSequenceNumber in there and if my proposal gets accepted I'll move the code to updateReportActionMessage in the new PR, because as things are now, moving the code to updateReportActionMessage will only make things worse.

@dklymenk
Copy link
Contributor Author

@NikkiWines I have created a new issue #3743 for updateReportActionMessage changes. As you can see, my proposal there already has a part of code from this PR. So in the end it should look very clean after solutions for both issues are merged.

@NikkiWines
Copy link
Contributor

Thanks for the update @dklymenk, that sounds like a good plan.

Copy link
Contributor

@NikkiWines NikkiWines left a comment

Choose a reason for hiding this comment

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

Works well, @Beamanator final review is all yours

const connectionID = Onyx.connect({
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
callback: (reportActionList) => {
// To make sure the callback is only called once
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB: // Disconnect to ensure the callback is only called once

@Beamanator Beamanator self-requested a review June 25, 2021 10:39
Copy link
Contributor

@Beamanator Beamanator left a comment

Choose a reason for hiding this comment

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

Hmm I'm getting some pretty unexpected behavior while testing on Web, are either of you getting this?

My first test was:

  1. Send a new message. Notice LHN updates correctly
  2. Refresh the page, LHN shows the previous message (not good!)
  3. Refresh again, the LHN now shows the latest message correctly

The second test:

  1. Deleting a message I just sent shows the phone number in the LHN (bad)
  2. Deleting a previous message shows the first message I deleted (bad)
Screen.Recording.2021-06-25.at.1.02.49.PM.mov

@dklymenk
Copy link
Contributor Author

dklymenk commented Jun 25, 2021

I have merged main into this branch yesterday to test how the updateReportActionMessage works now after a fix for #3524 is merged. And the behavior you see in the second test is basically this code running for any message update regardless of whether it's an edit or deletion.

    // If this is the most recent message, update the lastMessageText in the report object as well
    if (sequenceNumber === reportMaxSequenceNumbers[reportID]) {
        Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
            lastMessageText: message.html,
        });
    }

There a lot wrong with this function now and it is a cause of multiple issues at this point. That's why I created issue #3743 with a proposal for it's refactor, that should solve everything.

I didn't realize at the time that #3524 broke this PR, so I think it would be better for me to first implement the proposal for #3743, and then update this PR accordingly. What do you think?

As for your first test, I would assume it caused by some some code interruption issue, where you see an old version of localstorage because new one didn't persist because you've refreshed the page too fast. I actually cannot reproduce that one. The timing must be very precise by the looks of it.

@dklymenk
Copy link
Contributor Author

I will move the PR state to draft. Good thing I have merged main into this branch and you have noticed the new issues now and not after merge.

@dklymenk dklymenk marked this pull request as draft June 25, 2021 12:00
@NikkiWines
Copy link
Contributor

@dklymenk does this still need to be open?

@dklymenk dklymenk closed this Jul 23, 2021
@dklymenk
Copy link
Contributor Author

@NikkiWines sorry, I have completely forgot about this one. Closed.

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