Replace "Hide compose button while scrolling" setting with bottom padding (#4486)
As discussed in our contributors meeting. Advantages: - last element of list never obscured by action button - less code that runs on every scroll - less settings to worry about Additionally: - Added a (smaller) padding to the bottom of lists without action button, I think it looks nice if there is a bit of white space and the nav bar divider and the last list divider don't touch. - The list of filters had no dividers, I added them. - Recyclerviews with fixed height (Drafts, Filters, edits) now have scrollbars - code formatted all touched xml files closes https://github.com/tuskyapp/Tusky/issues/1563 <img src="https://github.com/tuskyapp/Tusky/assets/10157047/cd50199f-e84f-4402-93e4-a5a1beba2a08" width="280"/>
This commit is contained in:
parent
adbe694471
commit
34b53a3c59
27 changed files with 98 additions and 151 deletions
|
|
@ -134,6 +134,10 @@ class TuskyApplication : Application(), Configuration.Provider {
|
|||
editor.remove(PrefKeys.TAB_SHOW_HOME_SELF_BOOSTS)
|
||||
}
|
||||
|
||||
if (oldVersion < 2024060201) {
|
||||
editor.remove(PrefKeys.Deprecated.FAB_HIDE)
|
||||
}
|
||||
|
||||
editor.putInt(PrefKeys.SCHEMA_VERSION, newVersion)
|
||||
editor.apply()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,8 +129,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
private var animateAvatar: Boolean = false
|
||||
private var animateEmojis: Boolean = false
|
||||
|
||||
// fields for scroll animation
|
||||
private var hideFab: Boolean = false
|
||||
// for scroll animation
|
||||
private var oldOffset: Int = 0
|
||||
|
||||
@ColorInt
|
||||
|
|
@ -170,7 +169,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
|
||||
animateAvatar = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)
|
||||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
|
||||
handleWindowInsets()
|
||||
setupToolbar()
|
||||
|
|
@ -364,15 +362,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
if (hideFab && !blocking) {
|
||||
if (verticalOffset > oldOffset) {
|
||||
binding.accountFloatingActionButton.show()
|
||||
}
|
||||
if (verticalOffset < oldOffset) {
|
||||
binding.accountFloatingActionButton.hide()
|
||||
}
|
||||
}
|
||||
|
||||
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
|
||||
|
||||
binding.accountAvatarImageView.scaleX = scaledAvatarSize
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
|||
import com.keylesspalace.tusky.components.account.AccountActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
|
|
@ -86,8 +85,6 @@ class ConversationsFragment :
|
|||
|
||||
private var adapter: ConversationAdapter? = null
|
||||
|
||||
private var hideFab = false
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
|
|
@ -173,24 +170,6 @@ class ConversationsFragment :
|
|||
}
|
||||
})
|
||||
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.conversationFlow.collectLatest { pagingData ->
|
||||
adapter.submitData(pagingData)
|
||||
|
|
@ -407,10 +386,6 @@ class ConversationsFragment :
|
|||
|
||||
private fun onPreferenceChanged(adapter: ConversationAdapter, key: String) {
|
||||
when (key) {
|
||||
PrefKeys.FAB_HIDE -> {
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
}
|
||||
|
||||
PrefKeys.MEDIA_PREVIEW_ENABLED -> {
|
||||
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled
|
||||
val oldMediaPreviewEnabled = adapter.mediaPreviewEnabled
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.os.Bundle
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.ActivityFiltersBinding
|
||||
|
|
@ -51,6 +52,10 @@ class FiltersActivity : BaseActivity(), FiltersListener {
|
|||
|
||||
setTitle(R.string.pref_title_timeline_filters)
|
||||
|
||||
binding.filtersList.addItemDecoration(
|
||||
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
|
||||
)
|
||||
|
||||
observeViewModel()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.paging.LoadState
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
|
@ -24,7 +23,6 @@ import com.keylesspalace.tusky.components.compose.ComposeAutoCompleteAdapter
|
|||
import com.keylesspalace.tusky.databinding.ActivityFollowedTagsBinding
|
||||
import com.keylesspalace.tusky.interfaces.HashtagActionListener
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
import com.keylesspalace.tusky.util.copyToClipboard
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
|
|
@ -85,19 +83,6 @@ class FollowedTagsActivity :
|
|||
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
|
||||
)
|
||||
(binding.followedTagsView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
|
||||
val hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
if (hideFab) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAdapter(): FollowedTagsAdapter {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ import com.keylesspalace.tusky.databinding.NotificationsFilterBinding
|
|||
import com.keylesspalace.tusky.entity.Notification
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
|
|
@ -100,7 +99,6 @@ class NotificationsFragment :
|
|||
|
||||
private var adapter: NotificationsPagingAdapter? = null
|
||||
|
||||
private var hideFab: Boolean = false
|
||||
private var showNotificationsFilterBar: Boolean = true
|
||||
private var readingOrder: ReadingOrder = ReadingOrder.NEWEST_FIRST
|
||||
|
||||
|
|
@ -180,26 +178,8 @@ class NotificationsFragment :
|
|||
DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
|
||||
)
|
||||
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
readingOrder = ReadingOrder.from(preferences.getString(PrefKeys.READING_ORDER, null))
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
adapter.addLoadStateListener { loadState ->
|
||||
if (loadState.refresh != LoadState.Loading && loadState.source.refresh != LoadState.Loading) {
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
|
|
@ -481,10 +461,6 @@ class NotificationsFragment :
|
|||
|
||||
private fun onPreferenceChanged(adapter: NotificationsPagingAdapter, key: String) {
|
||||
when (key) {
|
||||
PrefKeys.FAB_HIDE -> {
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
}
|
||||
|
||||
PrefKeys.MEDIA_PREVIEW_ENABLED -> {
|
||||
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled
|
||||
val oldMediaPreviewEnabled = adapter.mediaPreviewEnabled
|
||||
|
|
|
|||
|
|
@ -170,13 +170,6 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
|||
isSingleLineTitle = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(false)
|
||||
key = PrefKeys.FAB_HIDE
|
||||
setTitle(R.string.pref_title_hide_follow_button)
|
||||
isSingleLineTitle = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(false)
|
||||
key = PrefKeys.ABSOLUTE_TIME_VIEW
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import android.view.View
|
|||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -109,7 +110,6 @@ class TimelineFragment :
|
|||
private var adapter: TimelinePagingAdapter? = null
|
||||
|
||||
private var isSwipeToRefreshEnabled = true
|
||||
private var hideFab = false
|
||||
|
||||
/**
|
||||
* Adapter position of the placeholder that was most recently clicked to "Load more". If null
|
||||
|
|
@ -279,26 +279,6 @@ class TimelineFragment :
|
|||
}
|
||||
}
|
||||
|
||||
if (actionButtonPresent()) {
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
eventHub.events.collect { event ->
|
||||
when (event) {
|
||||
|
|
@ -411,6 +391,14 @@ class TimelineFragment :
|
|||
val divider = DividerItemDecoration(context, RecyclerView.VERTICAL)
|
||||
binding.recyclerView.addItemDecoration(divider)
|
||||
|
||||
val recyclerViewBottomPadding = if ((activity as? ActionButtonActivity?)?.actionButton != null) {
|
||||
resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_actionbutton)
|
||||
} else {
|
||||
resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_no_actionbutton)
|
||||
}
|
||||
|
||||
binding.recyclerView.updatePadding(bottom = recyclerViewBottomPadding)
|
||||
|
||||
// CWs are expanded without animation, buttons animate itself, we don't need it basically
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
|
@ -573,10 +561,6 @@ class TimelineFragment :
|
|||
|
||||
private fun onPreferenceChanged(adapter: TimelinePagingAdapter, key: String) {
|
||||
when (key) {
|
||||
PrefKeys.FAB_HIDE -> {
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
}
|
||||
|
||||
PrefKeys.MEDIA_PREVIEW_ENABLED -> {
|
||||
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled
|
||||
val oldMediaPreviewEnabled = adapter.mediaPreviewEnabled
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ object PrefKeys {
|
|||
|
||||
const val SCHEMA_VERSION: String = "schema_version"
|
||||
const val APP_THEME = "appTheme"
|
||||
const val FAB_HIDE = "fabHide"
|
||||
const val LANGUAGE = "language"
|
||||
const val STATUS_TEXT_SIZE = "statusTextSize"
|
||||
const val READING_ORDER = "readingOrder"
|
||||
|
|
@ -112,4 +111,8 @@ object PrefKeys {
|
|||
|
||||
/** UI text scaling factor, stored as float, 100 = 100% = no scaling */
|
||||
const val UI_TEXT_SCALE_RATIO = "uiTextScaleRatio"
|
||||
|
||||
object Deprecated {
|
||||
const val FAB_HIDE = "fabHide"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue