-
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
Handle pagination errors in chats #40610
Handle pagination errors in chats #40610
Conversation
|
||
const loadNewerChats = useCallback(() => { | ||
if (isLoadingInitialReportActions || isLoadingOlderReportActions || network.isOffline || newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { |
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 noticed this was checking isLoadingOlderReportActions
which I assume was an error since this is loadNewerChats
, but maybe someone can confirm that this change is ok.
Is someone familiar with this code? I noticed the loading indicator for newer messages never seem to show even when comment linking in the middle of a chat that has new messages to load. This also causes the error state not to show since I'm implementing it in To test it I am currently bypassing the check. |
@mountiny Do you know if there is an issue related to this already? Should I create one? This is currently coming from this convo on slack. |
Might also need help here with design and texts, not sure who to tag and I can't seem to add the Design label. |
@janicduplessis I dont think there is one, feel free to create it please. Then we can add design label there too to ensure the looks are as we expect |
@dannymcclain would you be able to help with the design on this PR? thanks! |
Going to cc @Expensify/design in here as well. How do you all feel about the A couple thoughts from me:
I realize this is unlikely to happen "in real life" as much as it during me testing this, but still, I think it's pretty weird. I would rather the CleanShot.2024-04-22.at.10.48.07.mp4 |
@dannymcclain Thanks, I like the suggestion, will look at making those changes this week. |
Hmm before diving into the UI details, I would be curious to know what exactly causes this? Whatever it is, why can't we solve the root of the problem instead of dressing up the symptoms with nice UI? That being said, I love everything Danny did. But I am just a bit confused why we would ever need to even show something like this or how it would happen to a real user. I've never seen this happen in any of my other chat apps. @quinthar since you reported this issue - is this how you would expect to handle it? |
Maybe said another way, why exactly does the "Try again" button do to fix the pagination error? Why wouldn't we just do that automatically for the user instead of making them tap a button? |
The network requests can fail for various reasons, whether it be a network issue, or some outage on our end. This happened recently to both @mountiny and @quinthar, with the current behaviour the requests are retried in a loop so this is why I suggested introducing an error ui. I’m pretty sure I’ve seen similar in other chat apps before. |
@shawnborton This is shown in the attached video here. I've been seeing it a lot in the product too—it's super disorienting and there's seemingly no way to make it stop other than quitting the app and reopening it. So this |
Yeah that's fair, but why does the Try again button work and the automatic retry doesn't? |
The main goal is to let the user know there is a problem, and avoid spamming our servers with network requests. Currently it just keeps flashing the loading skeleton while immediately retrying the request. If we show some ui it stops the loop and warn the use about the problem. They can decide to try again and if it fails again then it will show the error state again. |
It would be possible to retry the requests a few times, but at some point we need to stop if it keeps failing and let the user know. That is the main goal of this change. If we want to add some automatic retry behaviour it should probably be implemented somewhere else though, more at the networking layer. |
So just to make sure I understand correctly, in this PR, if the request fails we'll show you a button to retry. But when you retry, it's likely just going to fail again? |
Yes, depending on what the problem was, if it was a random network error it might succeed when retrying, but if it is an outage it probably won’t. At least the user will be warned that an error occurred instead of being stuck in a loading loop. |
The current behaviour that causes a loading loop can also be very bad in case of an outage where all clients will start spamming the backend with network requests. |
Cool, thanks for explaining. I definitely don't want to block on progress here and agree that this is better than what we currently experience. Thanks for hearing me out! |
There is definitely a good case for retrying failed requests automatically, but that would be a different project. It would be more like whenever a request fail we can try to see what the reason for the failure is and if we think it could work if trying again (let’s say its a network error) then we can re-send the request automatically. Those retries could be limited to only a few times and possibly with a delay. Then in case the retries fail it would throw an error which would be handled by the error states of the app (including the one added here). |
If there are no further design comments I can go ahead and implement the improvements tomorrow and this can be ready for final review. |
@janicduplessis, let me know when this is ready for review! |
@janicduplessis Thanks for the changes. I just tested error condition for 40610-new-action-retry-issue.mp4 |
@rojiphil good catch. I think they should behave the same like you're suggesting. |
It currently disappears during loading because canShowHeader is false, which I think shouldn’t be. I’m a bit hesitant to add some workaround here since it seems like canShowHeader shouldn’t be false here and there might be a bug with it that could be addressed separately. What do you think? |
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’m a bit hesitant to add some workaround here
I agree that we should not implement any workaround around canShowHeader
. But we may overcome the problem as mentioned in the review comments.
Would this work?
@@ -1081,6 +1085,7 @@ function getNewerActions(reportID: string, reportActionID: string) { | |||
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, | |||
value: { | |||
isLoadingNewerReportActions: true, | |||
hasLoadingNewerReportActionsError: 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 can prevent setting hasLoadingNewerReportActionsError
to false
optimistically on retry of the failed request. Since we are forcefully getting newer actions on retry, we can do something like this here:
...(!force ? {hasLoadingNewerReportActionsError: false} : {}),
And when the API request succeeds, we can set it to false
in successData
@@ -1038,6 +1040,7 @@ function getOlderActions(reportID: string, reportActionID: string) { | |||
key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, | |||
value: { | |||
isLoadingOlderReportActions: true, | |||
hasLoadingOlderReportActionsError: 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.
And we may want to do the same for getOlderActions
here
I think it would work, but I am still hesitant on adding more complexity. For example we do rely on loading state in a few places like here to not trigger additional requests. I am worried that if we have cases of loading data, but not setting this loading state might cause problems and make it more likely to introduce bugs in the future. My current idea would be to merge it like this, and investigate the canShowHeader issue separately since currently there are just no loading states for new messages so it seems bugged. If canShowHeader is actually working as expected then maybe we can consider another solution. |
I agree we should avoid adding too much complexity. If it comes done to the issue with the button disappearing until new failure comes in, i think its fine for use to merge. Its rare case @rojiphil can you please continue with the checklist and treat that issues as nab? |
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.
LGTM and tests well.
Would be happy to work on this when we do. |
@mountiny Checklist is done. |
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.
Just couple NABs, as this is useful performance / UX-wise, I am going to merge it as it, but I think we havent asked marketing for copy on the error so I will follow up with that
isTransactionThread: !isEmptyObject(transactionThreadReport), | ||
})}`, | ||
); | ||
const loadOlderChats = useCallback( |
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 feel like these names here are misleading since we are not loading older or newer chats/ reports but only messages. NAB
paddingVertical: 15, | ||
paddingHorizontal: 20, | ||
}, | ||
listBoundaryErrorText: { |
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.
listBoundaryErrorText: { | |
listBoundaryErrorText: { |
position: 'absolute', | ||
top: 0, | ||
bottom: 0, | ||
left: 0, | ||
right: 0, | ||
height: CONST.CHAT_HEADER_LOADER_HEIGHT, | ||
}, | ||
listBoundaryError: { |
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.
listBoundaryError: { | |
listBoundaryError: { |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
Thanks @mountiny ! |
🚀 Deployed to staging by https://github.com/mountiny in version: 1.4.74-0 🚀
|
@janicduplessis Can you help us with this step?
|
@janicduplessis @rojiphil @mountiny Can we run only the following step?
|
Well! Running the mentioned steps alone will not help. If we can purposely fail |
Yea I think this might be hard to test on staging without being able to change the code. I will see if I can think of something. |
@rojiphil @janicduplessis It would be great if you can validate this PR internally then. |
@Beamanator Could this PR be validated internally? |
@mountiny @rojiphil @janicduplessis can one of you please test this internally in staging? QA is having trouble 🙏 |
@janicduplessis is that PR required to be CPed to staging or it can go through the normal deploy process? I am not sure how this can be tested without changing code. I think we could however mark this off the checklist and we can monitor this change in the #newdot-quality room |
I think it can just go to normal deploy process. It is hard to test since it requires specific network failures or outage to happen. We can monitor it and make sure we do not have reports of infinite loading after network errors. |
🚀 Deployed to production by https://github.com/chiragsalian in version: 1.4.74-6 🚀
|
Getting old report actions gets stuck in a retry infinite loop, could it be related to this PR: https://expensify.slack.com/archives/C05LX9D6E07/p1717711999602319?thread_ts=1717462134.645329&cid=C05LX9D6E07 ? |
Details
Currently we do not handle errors properly in
getOlderActions
andgetNewerActions
. All we do is set the loading state back to false, but that will cause the listonEndReached
oronStartReached
to be called again and a new network request to load more will be triggered. If we are near any edge of the chat, or even worse if we have a chat with few messages we will trigger these requests in a loop.To solve this we should add an error state to those pagination methods and display an error UI with an option to retry loading. This will prevent this request loop from happening.
Here's the UI I implemented for this error state
Top error:
Bottom error:
Example with comment linking:
Screen.Recording.2024-04-19.at.16.39.20.mov
Fixed Issues
$ #40641
PROPOSAL:
Tests
HttpUtils.ts
hereOffline tests
QA Steps
HttpUtils.ts
here. It might also be possible to simulate this by turning off internet after loading a chat.PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
Screen.Recording.2024-04-19.at.16.39.20.mov
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
MacOS: Desktop