fix: Show the FAB according to the user's preferences (#29)

The previous code didn't collect the uiState, so it was fixed at the
default value, ignoring any changes that happened over the life of
the viewmodel.

Fix that, so that the FAB will hide/show on scroll according to the
user's preferences.

While I'm here simplify the show/hide logic. The previous code would
ignore the user's preference if scrolling up. There doesn't seem to
be a good reason for that, and spelunking 6+ years back through the
history didn't find a justification for that behaviour in the original
commit.

Fixes #15
This commit is contained in:
Nik Clayton 2023-09-11 21:19:45 +02:00 committed by GitHub
parent ecd81e80b0
commit ec66942ae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 38 additions and 90 deletions

View File

@ -2599,7 +2599,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/conversation/ConversationsFragment.kt"
line="154"
line="155"
column="25"/>
</issue>
@ -2610,7 +2610,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/conversation/ConversationsFragment.kt"
line="156"
line="157"
column="33"/>
</issue>
@ -2661,45 +2661,12 @@
<issue
id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `FollowedTagsActivity` requires synthetic accessor"
errorLine1=" if (dy > 0 &amp;&amp; binding.fab.isShown) {"
errorLine2=" ~~~~~~~">
errorLine1=" binding.fab.visible(dy == 0)"
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/followedtags/FollowedTagsActivity.kt"
line="92"
column="39"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `FollowedTagsActivity` requires synthetic accessor"
errorLine1=" binding.fab.hide()"
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/followedtags/FollowedTagsActivity.kt"
line="93"
column="29"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `FollowedTagsActivity` requires synthetic accessor"
errorLine1=" } else if (dy &lt; 0 &amp;&amp; !binding.fab.isShown) {"
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/followedtags/FollowedTagsActivity.kt"
line="94"
column="47"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `FollowedTagsActivity` requires synthetic accessor"
errorLine1=" binding.fab.show()"
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/components/followedtags/FollowedTagsActivity.kt"
line="95"
column="29"/>
column="25"/>
</issue>
<issue
@ -2709,7 +2676,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/components/followedtags/FollowedTagsActivity.kt"
line="204"
line="200"
column="21"/>
</issue>
@ -3750,12 +3717,12 @@
<issue
id="SyntheticAccessor"
message="Access to `private` method `getViewModel` of class `NotificationsFragment` requires synthetic accessor"
errorLine1=" if (!viewModel.uiState.value.showFabWhileScrolling) {"
errorLine2=" ~~~~~~~~~">
errorLine1=" actionButton?.visible(viewModel.uiState.value.showFabWhileScrolling || dy == 0)"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/app/pachli/components/notifications/NotificationsFragment.kt"
line="176"
column="30"/>
column="43"/>
</issue>
<issue
@ -3838,12 +3805,12 @@
<issue
id="SyntheticAccessor"
message="Access to `private` method `getViewModel` of class `TimelineFragment` requires synthetic accessor"
errorLine1=" if (!viewModel.uiState.value.showFabWhileScrolling) {"
errorLine2=" ~~~~~~~~~">
errorLine1=" actionButton?.visible(viewModel.uiState.value.showFabWhileScrolling || dy == 0)"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/app/pachli/components/timeline/TimelineFragment.kt"
line="177"
column="34"/>
line="178"
column="47"/>
</issue>
<issue

View File

@ -54,6 +54,7 @@ import app.pachli.util.StatusDisplayOptions
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.util.visible
import app.pachli.viewdata.AttachmentViewData
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.color.MaterialColors
@ -87,7 +88,7 @@ class ConversationsFragment :
private lateinit var adapter: ConversationAdapter
private var hideFab = false
private var showFabWhileScrolling = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_timeline, container, false)
@ -161,22 +162,13 @@ class ConversationsFragment :
},
)
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
showFabWhileScrolling = !preferences.getBoolean(PrefKeys.FAB_HIDE, false)
binding.recyclerView.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
val actionButton = (activity as? ActionButtonActivity)?.actionButton
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
val composeButton = (activity as ActionButtonActivity).actionButton
if (composeButton != null) {
if (hideFab) {
if (dy > 0 && composeButton.isShown) {
composeButton.hide() // hides the button if we're scrolling down
} else if (dy < 0 && !composeButton.isShown) {
composeButton.show() // shows it if we are scrolling up
}
} else if (!composeButton.isShown) {
composeButton.show()
}
}
actionButton?.visible(showFabWhileScrolling || dy == 0)
}
},
)
@ -375,7 +367,7 @@ class ConversationsFragment :
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
when (key) {
PrefKeys.FAB_HIDE -> {
hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false)
showFabWhileScrolling = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false)
}
PrefKeys.MEDIA_PREVIEW_ENABLED -> {
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled

View File

@ -89,11 +89,7 @@ class FollowedTagsActivity :
binding.followedTagsView.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0 && binding.fab.isShown) {
binding.fab.hide()
} else if (dy < 0 && !binding.fab.isShown) {
binding.fab.show()
}
binding.fab.visible(dy == 0)
}
},
)

View File

@ -65,6 +65,7 @@ import app.pachli.util.hide
import app.pachli.util.openLink
import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.util.visible
import app.pachli.viewdata.AttachmentViewData.Companion.list
import app.pachli.viewdata.NotificationViewData
import at.connyduck.sparkbutton.helpers.Utils
@ -169,26 +170,18 @@ class NotificationsFragment :
binding.recyclerView.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
val actionButton = (activity as ActionButtonActivity).actionButton
val actionButton = (activity as? ActionButtonActivity)?.actionButton
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
actionButton?.let { fab ->
if (!viewModel.uiState.value.showFabWhileScrolling) {
if (dy > 0 && fab.isShown) {
fab.hide() // Hide when scrolling down
} else if (dy < 0 && !fab.isShown) {
fab.show() // Show when scrolling up
}
} else if (!fab.isShown) {
fab.show()
}
}
actionButton?.visible(viewModel.uiState.value.showFabWhileScrolling || dy == 0)
}
@Suppress("SyntheticAccessor")
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
newState != SCROLL_STATE_IDLE && return
actionButton?.show()
// Save the ID of the first notification visible in the list, so the user's
// reading position is always restorable.
layoutManager.findFirstVisibleItemPosition().takeIf { it != NO_POSITION }?.let { position ->

View File

@ -71,6 +71,7 @@ import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.unsafeLazy
import app.pachli.util.viewBinding
import app.pachli.util.visible
import app.pachli.util.withPresentationState
import app.pachli.viewdata.AttachmentViewData
import app.pachli.viewdata.StatusViewData
@ -171,23 +172,17 @@ class TimelineFragment :
if (actionButtonPresent()) {
binding.recyclerView.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
val actionButton = (activity as? ActionButtonActivity)?.actionButton
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
val composeButton = (activity as ActionButtonActivity).actionButton
if (composeButton != null) {
if (!viewModel.uiState.value.showFabWhileScrolling) {
if (dy > 0 && composeButton.isShown) {
composeButton.hide() // hides the button if we're scrolling down
} else if (dy < 0 && !composeButton.isShown) {
composeButton.show() // shows it if we are scrolling up
}
} else if (!composeButton.isShown) {
composeButton.show()
}
}
actionButton?.visible(viewModel.uiState.value.showFabWhileScrolling || dy == 0)
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
newState != SCROLL_STATE_IDLE && return
actionButton?.show()
saveVisibleId()
}
},
@ -310,6 +305,11 @@ class TimelineFragment :
}
}
// Collect the uiState. Nothing is done with it, but if you don't collect it then
// accessing viewModel.uiState.value (e.g., to check whether the FAB should be
// hidden) always returns the initial state.
launch { viewModel.uiState.collect() }
// Update status display from statusDisplayOptions. If the new options request
// relative time display collect the flow to periodically re-bind the UI.
launch {