Skip to content

Commit

Permalink
fix: Remove progressbar from status timelines (#208)
Browse files Browse the repository at this point in the history
Previously the middle-of-screen progress spinner and the spinner that
appears on a swipe-to-refresh could get out of sync.

Fix this by removing the middle-of-screen progress spinner from relevant
fragments, as the swipe-to-refresh spinner shows the user that an
operation is in progress, and also clues them in to the fact that a
swipe-to-refresh is possible (by using the common UX control).

Fixes #75
  • Loading branch information
nikclayton authored Oct 30, 2023
1 parent c3f68ba commit 6aa4eab
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 80 deletions.
4 changes: 2 additions & 2 deletions app/lint-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2555,7 +2555,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/conversation/ConversationsFragment.kt"
line="148"
line="152"
column="29"/>
</issue>

Expand All @@ -2566,7 +2566,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/conversation/ConversationsFragment.kt"
line="150"
line="154"
column="37"/>
</issue>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState
import androidx.recyclerview.widget.GridLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
import app.pachli.ViewMediaActivity
import app.pachli.databinding.FragmentTimelineBinding
Expand Down Expand Up @@ -59,6 +60,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class AccountMediaFragment :
Fragment(R.layout.fragment_timeline),
OnRefreshListener,
RefreshableFragment,
MenuProvider {

Expand Down Expand Up @@ -99,7 +101,7 @@ class AccountMediaFragment :
binding.recyclerView.adapter = adapter

binding.swipeRefreshLayout.isEnabled = false
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeColors(MaterialColors.getColor(binding.root, androidx.appcompat.R.attr.colorPrimary))

binding.statusView.visibility = View.GONE
Expand All @@ -112,7 +114,6 @@ class AccountMediaFragment :

adapter.addLoadStateListener { loadState ->
binding.statusView.hide()
binding.progressBar.hide()

if (loadState.refresh != LoadState.Loading && loadState.source.refresh != LoadState.Loading) {
binding.swipeRefreshLayout.isRefreshing = false
Expand All @@ -128,11 +129,11 @@ class AccountMediaFragment :
}
is LoadState.Error -> {
binding.statusView.show()
binding.statusView.setup((loadState.refresh as LoadState.Error).error)
}
is LoadState.Loading -> {
binding.progressBar.show()
binding.statusView.setup((loadState.refresh as LoadState.Error).error) {
refreshContent()
}
}
is LoadState.Loading -> { /* nothing to do */ }
}
}
}
Expand All @@ -151,7 +152,6 @@ class AccountMediaFragment :
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_refresh -> {
binding.swipeRefreshLayout.isRefreshing = true
refreshContent()
true
}
Expand Down Expand Up @@ -191,7 +191,18 @@ class AccountMediaFragment :
}
}

/** Refresh the displayed content, as if the user had swiped on the SwipeRefreshLayout */
override fun refreshContent() {
binding.swipeRefreshLayout.isRefreshing = true
onRefresh()
}

/**
* Listener for the user swiping on the SwipeRefreshLayout. The SwipeRefreshLayout has
* handled displaying the animated spinner.
*/
override fun onRefresh() {
binding.statusView.hide()
adapter.refresh()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.paging.LoadState
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
import app.pachli.StatusListActivity
import app.pachli.adapter.StatusBaseViewHolder
Expand Down Expand Up @@ -71,6 +72,7 @@ import kotlin.time.toDuration
@AndroidEntryPoint
class ConversationsFragment :
SFragment(),
OnRefreshListener,
StatusActionListener,
ReselectableFragment,
MenuProvider {
Expand Down Expand Up @@ -114,11 +116,11 @@ class ConversationsFragment :
}

binding.statusView.hide()
binding.progressBar.hide()

if (adapter.itemCount == 0) {
when (loadState.refresh) {
is LoadState.NotLoading -> {
binding.swipeRefreshLayout.isRefreshing = false
if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) {
binding.statusView.show()
binding.statusView.setup(
Expand All @@ -130,13 +132,14 @@ class ConversationsFragment :
}

is LoadState.Error -> {
binding.swipeRefreshLayout.isRefreshing = false
binding.statusView.show()
binding.statusView.setup((loadState.refresh as LoadState.Error).error) { refreshContent() }
binding.statusView.setup((loadState.refresh as LoadState.Error).error) {
refreshContent()
}
}

is LoadState.Loading -> {
binding.progressBar.show()
}
is LoadState.Loading -> { /* nothing to do */ }
}
}
}
Expand Down Expand Up @@ -208,7 +211,6 @@ class ConversationsFragment :
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_refresh -> {
binding.swipeRefreshLayout.isRefreshing = true
refreshContent()
true
}
Expand All @@ -229,13 +231,24 @@ class ConversationsFragment :
binding.recyclerView.adapter = adapter.withLoadStateFooter(ConversationLoadStateAdapter(adapter::retry))
}

private fun initSwipeToRefresh() {
binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.swipeRefreshLayout.setColorSchemeColors(MaterialColors.getColor(binding.root, androidx.appcompat.R.attr.colorPrimary))
}

/** Refresh the displayed content, as if the user had swiped on the SwipeRefreshLayout */
private fun refreshContent() {
adapter.refresh()
binding.swipeRefreshLayout.isRefreshing = true
onRefresh()
}

private fun initSwipeToRefresh() {
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
binding.swipeRefreshLayout.setColorSchemeColors(MaterialColors.getColor(binding.root, androidx.appcompat.R.attr.colorPrimary))
/**
* Listener for the user swiping on the SwipeRefreshLayout. The SwipeRefreshLayout has
* handled displaying the animated spinner.
*/
override fun onRefresh() {
binding.statusView.hide()
adapter.refresh()
}

override fun onReblog(reblog: Boolean, position: Int) {
Expand Down Expand Up @@ -375,6 +388,8 @@ class ConversationsFragment :
}

companion object {
@Suppress("unused")
private const val TAG = "ConversationsFragment"
fun newInstance() = ConversationsFragment()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -391,20 +391,13 @@ class TimelineFragment :
}
}

// Manage the display of progress bars. Rather than hide them as soon as the
// Refresh portion completes, hide them when then first Prepend completes. This
// is a better signal to the user that it is now possible to scroll up and see
// new content.
// Manage the progress display. Rather than hide as soon as the Refresh portion
// completes, hide when then first Prepend completes. This is a better signal to
// the user that it is now possible to scroll up and see new content.
launch {
refreshState.collect {
when (it) {
UserRefreshState.ACTIVE -> {
if (adapter.itemCount == 0 && !binding.swipeRefreshLayout.isRefreshing) {
binding.progressBar.show()
}
}
UserRefreshState.COMPLETE, UserRefreshState.ERROR -> {
binding.progressBar.hide()
binding.swipeRefreshLayout.isRefreshing = false
}
else -> { /* nothing to do */ }
Expand Down Expand Up @@ -555,10 +548,19 @@ class TimelineFragment :
)
}

/** Refresh the displayed content, as if the user had swiped on the SwipeRefreshLayout */
override fun refreshContent() {
binding.swipeRefreshLayout.isRefreshing = true
onRefresh()
}

/**
* Listener for the user swiping on the SwipeRefreshLayout. The SwipeRefreshLayout has
* handled displaying the animated spinner.
*/
override fun onRefresh() {
binding.statusView.hide()
snackbar?.dismiss()

adapter.refresh()
}

Expand Down Expand Up @@ -754,11 +756,6 @@ class TimelineFragment :
}
}

override fun refreshContent() {
binding.swipeRefreshLayout.isRefreshing = true
onRefresh()
}

companion object {
private const val TAG = "TimelineFragment" // logging tag
private const val KIND_ARG = "kind"
Expand Down
22 changes: 0 additions & 22 deletions app/src/main/res/layout-sw640dp/fragment_timeline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

Expand Down Expand Up @@ -33,26 +32,5 @@
android:visibility="gone" />
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
23 changes: 0 additions & 23 deletions app/src/main/res/layout/fragment_timeline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

Expand All @@ -27,26 +26,4 @@
android:visibility="gone" />
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

0 comments on commit 6aa4eab

Please sign in to comment.