tuskyapp / Tusky

An Android client for the microblogging server Mastodon
https://tusky.app
GNU General Public License v3.0
2.42k stars 386 forks source link

Thead view does not show loading animation #3435

Closed Lakoja closed 1 year ago

Lakoja commented 1 year ago

Currently my instance is slow in loading conversations. So if a I tap a status in the home timeline it instantly shows that status but nothing more. After some seconds then the remaining status below are shown.

If you (initially) scroll the screen up you will see that this is possible and then at the bottom a loading animation line is shown. The screen is by a measure of the title bar higher than it needs to be.

(For most status that is. If it has a high image or much text it is probably not too "high" because it of course extends below the fold.)


nikclayton commented 1 year ago

The intended effect is:

  1. Delay the loading animation for 500ms, try and fetch the missing data. If the data is not loaded within 500ms then show the progress bar.
  2. In parallel, fetch the statuses above/below the thread. This is a single API call. Again, there is a loading progress bar for this (it's https://github.com/tuskyapp/Tusky/blob/develop/app/src/main/res/layout/fragment_view_thread.xml#L26), but it's delayed for 500ms.

Are you sure it's taking "some seconds" to show the statuses without any progress indication?

Lakoja commented 1 year ago

The intended effect is:

Ah, now I get it. That code (getProgressBarJob) is rather new.

You can see parts of the problem when opening a single status (not too long) with not replies: The resulting view can be moved some pixels upwards (to move the thread title out of view). Only in the now visible space was there the indeterminate progress bar line.

I'm not really sure what exactly was slow as I have not yet found out how to simulate it. If I add a delay in the async "contextCall" in ViewThreadViewModel#loadThread the gui hangs for that period (or is very slugish). Which surprises me because I would have guessed that the api call in there is sometimes slow.

nikclayton commented 1 year ago

What did you try in the contextCall code?

I just modified it to this to add a 10 second delay, and tested:

val contextCall = async { delay(10000); api.statusContext(id) }

That doesn't delay the UI for me, but it does demonstrate that the loading animation isn't shown. Hmm

nikclayton commented 1 year ago

Which is odd, because that used to work, and there have been no relevant changes to the layout.

nikclayton commented 1 year ago

As a quick fix, try this as fragment_view_thread.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipeRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="?android:attr/colorBackground"
                android:scrollbars="vertical" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

        <com.google.android.material.progressindicator.LinearProgressIndicator
            android:id="@+id/threadProgressBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            android:indeterminate="true"
            android:contentDescription="@string/a11y_label_loading_thread" />
    </LinearLayout>

    <ProgressBar
        android:id="@+id/initialProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:indeterminate="true"
        android:layout_gravity="center"
        android:contentDescription="@string/a11y_label_loading_thread" />

    <com.keylesspalace.tusky.view.BackgroundMessageView
        android:id="@+id/statusView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

If that looks good to you then please submit a PR (I'm going to have limited internet for the next couple of weeks, hence the handoff).

The better fix is probably to use https://developer.android.com/reference/kotlin/androidx/paging/LoadStateAdapter (there's an example of that in the notifications code). However, I'm not sure if that's tied to the paging3 library or not, so might be a much more involved change.

Lakoja commented 1 year ago

What did you try in the contextCall code?

I just modified it to this to add a 10 second delay, and tested:

val contextCall = async { delay(10000); api.statusContext(id) }

That doesn't delay the UI for me, but it does demonstrate that the loading animation isn't shown. Hmm

Yes, that works (good for testing).

I tried Thread.sleep which seems to block everything.

nikclayton commented 1 year ago

Yes. A coroutine is not a thread.

So if you tell the thread to sleep then everything in that thread will sleep. That's bad if it's the UI thread.

https://www.baeldung.com/kotlin/threads-coroutines might be helpful.