pachli / pachli-android

The Pachli Android app
https://pachli.app
GNU General Public License v3.0
126 stars 15 forks source link

Async actions (favorite/boost buttons) should show spinner while waiting. #632

Open ericlathrop opened 6 months ago

ericlathrop commented 6 months ago

Is your feature request related to a problem? Please describe. Sometimes when I tap the favorite or boost buttons it takes a while to highlight. I'm guessing this be because it's waiting on the network.

Describe the solution you'd like I think the app should replace the button wigh a loading spinner or something so I can know if its actually doing something.

Describe alternatives you've considered There could be a global spinner, but I think each button having a separate spinner would be more clear.

nikclayton commented 6 months ago

I've got a slightly different idea in mind (some of it already works like this).

Tapping a button (e.g., bookmark) should toggle the state immediately, and send an async network request to make the change on the backend.

If the backend request fails (possibly after some number of retries) the user is shown a "Retry" snackbar, and the button state resets to its previous value.

Although there's a couple of things that make this a bit more complicated in the UX:

  1. Suppose you bookmark a post, then scroll up several pages, and then the bookmark operation fails. The error message has to give you enough context to know what post is having a problem.

  2. Suppose you bookmark a post on your home timeline, then open your bookmarks timeline before the bookmark operation completes. The user is surprised that the post they just bookmarked doesn't appear in that list. And the error message about the bookmark operation failing won't appear, because it's tied to the previous screen...

I have thought about maintaining a persistent queue of outbound operations; bookmark a post and the network operation starts immediately, and if it fails the request is put in a queue to retry later.

But that opens thorny questions like "Should the user be able to see the queue?", "If they can see the queue, can they change it (e..g, delete a pending request)?", and "Should the queue be checked when displaying posts to indicate if a post has a pending operation associated with it?".

ericlathrop commented 6 months ago

I don't like that idea of "lying" and showing the button highlighted when it's not actually completed on the server because it's not reliable. Sometimes I'll favorite something, the button highlights, then open the photo on the same post, and when I close the photo the favorite button is dimmed out, so I tap it again. No idea if it sends it twice or what happens.

I like the idea of a queue of all pending async actions and the buttons checking those for determining their state. I don't think users need to see the queue, because requests will drain naturally and if they error the current popup message is fine.

nikclayton commented 6 months ago

I don't like that idea of "lying" and showing the button highlighted when it's not actually completed on the server because it's not reliable

I disagree with this a bit because I think it's a good idea for the UI to reflect the common case. Most of the time these operations are going to succeed. If every UI interaction that required a network request replaced the button with a spinning circle there'd be a lot of them in the UI. And users might start looking for ways to cancel those operations.

There are mitigations that can be taken; e.g., perform the request, and then wait for up to 500ms

That's still a lot more complexity in the code for a (hopefully) rare case, which is why I haven't prioritised that sort of work yet.

Sometimes I'll favorite something, the button highlights, then open the photo on the same post, and when I close the photo the favorite button is dimmed out

That's a bug. If you didn't get an error message then it should show as favourited because the operation succeeded or is in progress. There are definitely places in the code where that doesn't happen properly yet.

Background

Pachli inherits a Tusky design choice that I haven't fixed yet. Only one timeline is cached to local storage, the home timeline. Everything else (notifications, local, federated, bookmarks, etc) is ephemeral, and re-fetched every time. So all the code for interacting with a post and maintaining local state has to be duplicated, and some of it's not in sync. That's why, for example, translations only work on the home timeline at the moment.

mileskrell commented 6 months ago

Tapping a button (e.g., bookmark) should toggle the state immediately, and send an async network request to make the change on the backend.

I generally see this "update the UI to reflect success immediately and then do the request in the background" pattern referred to as "optimistic UI", and I know a lot of folks are fans of it. My concern is that implementing this without also adding a request queue would make it hard to know when a request had failed. Users would need to wait for an unclear period of time before they could assume that that favoriting or boosting a post had (probably) succeeded. This would be a problem in situations with poor network connectivity.

I somewhat agree with the sentiment that optimistic UI is "lying" to users. I prefer for the UI to reflect reality, even though I know that many non-technical users will simply assume that apps that implement optimistic UI are faster than the ones that don't and may therefore prefer apps with optimistic UI. However, I would be okay with optimistic UI if we also added a queue of pending requests, such that the user has a guarantee that the "success" button state at least indicates that the request will definitely eventually succeed.

But if our primary goal is just to add immediate feedback, then loading indicators do seem like they would accomplish that while requiring less work to build. We would still want to have a reasonably short timeout, after which point the button would revert to its initial state and we would show an error modal/toast/snackbar.

nikclayton commented 4 months ago

@ericlathrop If you get the time, can you let me know what you think of the UX in the new "Suggested accounts" feature. I'm experimenting with a handful of small changes there that, if they work well, I'll incrementally roll out in to the rest of the app.

The changes are:

  1. At the top of the screen, below the title bar, a progress bar will appear while there's any ongoing network operation (either because you've hit the "Dismiss" or "Follow" buttons, or because you've triggered a refresh.

So if you quickly tap "Follow" on three different accounts the progress bar will persist until all three network operations are complete.

  1. While an operation is in-progress on one of the suggested accounts the account in the list is disabled; the UI changes, and you can't click on any of the buttons for that account.

If you have a fast network / server this is probably difficult to see. Easiest way to see it in action is to open the list of suggested accounts, then turn on "Airplane mode", then try dismissing (or following) an account.

  1. If you swipe to refresh the pull-down-spinny thing appears to show that the swipe operation has been registered. But it's then replaced with the progress bar from (1) above. So there's only one (consistent) indicator of ongoing network operations.

All of this works for "Suggested accounts" because the operations you can take on a suggestion ("Dismiss" or "Follow") are mutually exclusive, so disabling the suggestion while the network operation is in progress makes sense.

It'll need to be more nuanced for posts, where there might be multiple operations in flight at once, so this is just an early experiment at the moment.

ericlathrop commented 4 months ago

Sure, how do I try it? I can't seem to find it in version 2.5.2.

On June 18, 2024 5:49:05 AM EDT, Nik Clayton @.***> wrote:

@ericlathrop If you get the time, can you let me know what you think of the UX in the new "Suggested accounts" feature. I'm experimenting with a handful of small changes there that, if they work well, I'll incrementally roll out in to the rest of the app.

The changes are:

  1. At the top of the screen, below the title bar, a progress bar will appear while there's any ongoing network operation (either because you've hit the "Dismiss" or "Follow" buttons, or because you've triggered a refresh.

So if you quickly tap "Follow" on three different accounts the progress bar will persist until all three network operations are complete.

  1. While an operation is in-progress on one of the suggested accounts the account in the list is disabled; the UI changes, and you can't click on any of the buttons for that account.

If you have a fast network / server this is probably difficult to see. Easiest way to see it in action is to open the list of suggested accounts, then turn on "Airplane mode", then try dismissing (or following) an account.

  1. If you swipe to refresh the pull-down-spinny thing appears to show that the swipe operation has been registered. But it's then replaced with the progress bar from (1) above. So there's only one (consistent) indicator of ongoing network operations.

All of this works for "Suggested accounts" because the operations you can take on a suggestion ("Dismiss" or "Follow") are mutually exclusive, so disabling the suggestion while the network operation is in progress makes sense.

It'll need to be more nuanced for posts, where there might be multiple operations in flight at once, so this is just an early experiment at the moment.

-- Reply to this email directly or view it on GitHub: https://github.com/pachli/pachli-android/issues/632#issuecomment-2175679914 You are receiving this because you were mentioned.

Message ID: @.***>

nikclayton commented 4 months ago

It's in Pachli Current at the moment (https://play.google.com/store/apps/details?id=app.pachli.current). The next release of Pachli will be at the end of this month.