Skip to content

Commit

Permalink
feat: Allow the user to chose behaviour when tapping a tab (#955)
Browse files Browse the repository at this point in the history
Previously, tapping a tab would jump to the top of the loaded content,
which might trigger a load of a fresh page.

Provide a preference to control this; the default is the current
behaviour, the user can also choose to discard the current content and
load the newest content.

Fixes #939
  • Loading branch information
nikclayton authored Sep 27, 2024
1 parent 90537da commit 9755866
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status
import app.pachli.core.preferences.TabTapBehaviour
import app.pachli.core.ui.ActionButtonScrollListener
import app.pachli.core.ui.BackgroundMessage
import app.pachli.core.ui.extensions.getErrorString
Expand Down Expand Up @@ -671,7 +672,10 @@ class NotificationsFragment :

override fun onReselect() {
if (isAdded) {
layoutManager.scrollToPosition(0)
when (viewModel.uiState.value.tabTapBehaviour) {
TabTapBehaviour.JUMP_TO_NEXT_PAGE -> layoutManager.scrollToPosition(0)
TabTapBehaviour.JUMP_TO_NEWEST -> viewModel.accept(InfallibleUiAction.LoadNewest)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Poll
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.core.preferences.TabTapBehaviour
import app.pachli.network.FilterModel
import app.pachli.usecase.TimelineCases
import app.pachli.util.deserialize
Expand Down Expand Up @@ -82,6 +83,9 @@ data class UiState(

/** True if the FAB should be shown while scrolling */
val showFabWhileScrolling: Boolean = true,

/** User's preference for behaviour when tapping a tab. */
val tabTapBehaviour: TabTapBehaviour = TabTapBehaviour.JUMP_TO_NEXT_PAGE,
)

/** Preferences the UI reacts to */
Expand All @@ -92,6 +96,7 @@ data class UiPrefs(
/** Relevant preference keys. Changes to any of these trigger a display update */
val prefKeys = setOf(
PrefKeys.FAB_HIDE,
PrefKeys.TAB_TAP_BEHAVIOUR,
)
}
}
Expand Down Expand Up @@ -384,6 +389,7 @@ class NotificationsViewModel @Inject constructor(
account.lastNotificationId = "0"
accountManager.saveAccount(account)
reload.getAndUpdate { it + 1 }
repository.invalidate()
}
}

Expand Down Expand Up @@ -506,10 +512,11 @@ class NotificationsViewModel @Inject constructor(
getNotifications(filters = action.filter, initialKey = getInitialKey())
}.cachedIn(viewModelScope)

uiState = combine(notificationFilter, getUiPrefs()) { filter, prefs ->
uiState = combine(notificationFilter, getUiPrefs()) { filter, _ ->
UiState(
activeFilter = filter.filter,
showFabWhileScrolling = prefs.showFabWhileScrolling,
showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false),
tabTapBehaviour = sharedPreferencesRepository.tabTapBehaviour,
)
}.stateIn(
scope = viewModelScope,
Expand Down Expand Up @@ -557,10 +564,5 @@ class NotificationsViewModel @Inject constructor(
*/
private fun getUiPrefs() = sharedPreferencesRepository.changes
.filter { UiPrefs.prefKeys.contains(it) }
.map { toPrefs() }
.onStart { emit(toPrefs()) }

private fun toPrefs() = UiPrefs(
showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false),
)
.onStart { emit(null) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import app.pachli.core.preferences.AppTheme
import app.pachli.core.preferences.DownloadLocation
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.core.preferences.TabTapBehaviour
import app.pachli.core.ui.extensions.await
import app.pachli.core.ui.makeIcon
import app.pachli.databinding.AccountNotificationDetailsListItemBinding
Expand Down Expand Up @@ -195,15 +196,6 @@ class PreferencesFragment : PreferenceFragmentCompat() {
icon = makeIcon(GoogleMaterial.Icon.gmd_format_size)
}

listPreference {
setDefaultValue("top")
setEntries(R.array.pref_main_nav_position_options)
setEntryValues(R.array.pref_main_nav_position_values)
key = PrefKeys.MAIN_NAV_POSITION
setSummaryProvider { entry }
setTitle(R.string.pref_main_nav_position)
}

listPreference {
setDefaultValue("disambiguate")
setEntries(R.array.pref_show_self_username_names)
Expand Down Expand Up @@ -285,18 +277,35 @@ class PreferencesFragment : PreferenceFragmentCompat() {
isSingleLineTitle = false
}

switchPreference {
setDefaultValue(false)
key = PrefKeys.SHOW_STATS_INLINE
setTitle(R.string.pref_title_show_stat_inline)
isSingleLineTitle = false
}
}

preferenceCategory(app.pachli.core.preferences.R.string.pref_category_tabs) {
listPreference {
setDefaultValue("top")
setEntries(R.array.pref_main_nav_position_options)
setEntryValues(R.array.pref_main_nav_position_values)
key = PrefKeys.MAIN_NAV_POSITION
setSummaryProvider { entry }
setTitle(R.string.pref_main_nav_position)
}

switchPreference {
setDefaultValue(true)
key = PrefKeys.ENABLE_SWIPE_FOR_TABS
setTitle(R.string.pref_title_enable_swipe_for_tabs)
isSingleLineTitle = false
}

switchPreference {
setDefaultValue(false)
key = PrefKeys.SHOW_STATS_INLINE
setTitle(R.string.pref_title_show_stat_inline)
isSingleLineTitle = false
enumListPreference<TabTapBehaviour> {
setDefaultValue(TabTapBehaviour.JUMP_TO_NEXT_PAGE)
setTitle(app.pachli.core.preferences.R.string.pref_title_tab_tap)
key = PrefKeys.TAB_TAP_BEHAVIOUR
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status
import app.pachli.core.preferences.TabTapBehaviour
import app.pachli.core.ui.ActionButtonScrollListener
import app.pachli.core.ui.BackgroundMessage
import app.pachli.core.ui.extensions.getErrorString
Expand Down Expand Up @@ -799,9 +800,15 @@ class TimelineFragment :

override fun onReselect() {
if (isAdded) {
binding.recyclerView.scrollToPosition(0)
binding.recyclerView.stopScroll()
saveVisibleId()
when (viewModel.uiState.value.tabTapBehaviour) {
TabTapBehaviour.JUMP_TO_NEXT_PAGE -> {
binding.recyclerView.scrollToPosition(0)
binding.recyclerView.stopScroll()
saveVisibleId()
}

TabTapBehaviour.JUMP_TO_NEWEST -> viewModel.accept(InfallibleUiAction.LoadNewest)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.core.preferences.TabTapBehaviour
import app.pachli.network.FilterModel
import app.pachli.usecase.TimelineCases
import app.pachli.viewdata.StatusViewData
Expand Down Expand Up @@ -85,6 +86,9 @@ data class UiState(

/** True if the timeline should be shown in reverse order (oldest first) */
val reverseTimeline: Boolean,

/** User's preference for behaviour when tapping a tab. */
val tabTapBehaviour: TabTapBehaviour = TabTapBehaviour.JUMP_TO_NEXT_PAGE,
)

// TODO: Ui* classes are copied from NotificationsViewModel. Not yet sure whether these actions
Expand Down Expand Up @@ -406,20 +410,26 @@ abstract class TimelineViewModel(
}
}

val watchedPrefs = setOf(PrefKeys.FAB_HIDE, PrefKeys.LAB_REVERSE_TIMELINE)
val watchedPrefs = setOf(
PrefKeys.FAB_HIDE,
PrefKeys.LAB_REVERSE_TIMELINE,
PrefKeys.TAB_TAP_BEHAVIOUR,
)
uiState = sharedPreferencesRepository.changes
.filter { watchedPrefs.contains(it) }
.map {
UiState(
showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false),
reverseTimeline = sharedPreferencesRepository.getBoolean(PrefKeys.LAB_REVERSE_TIMELINE, false),
tabTapBehaviour = sharedPreferencesRepository.tabTapBehaviour,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),
initialValue = UiState(
showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false),
reverseTimeline = sharedPreferencesRepository.getBoolean(PrefKeys.LAB_REVERSE_TIMELINE, false),
tabTapBehaviour = sharedPreferencesRepository.tabTapBehaviour,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.core.content.edit
import app.cash.turbine.test
import app.pachli.core.network.model.Notification
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.TabTapBehaviour
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
Expand All @@ -36,6 +37,7 @@ class NotificationsViewModelTestUiState : NotificationsViewModelTestBase() {
private val initialUiState = UiState(
activeFilter = setOf(Notification.Type.FOLLOW),
showFabWhileScrolling = true,
tabTapBehaviour = TabTapBehaviour.JUMP_TO_NEXT_PAGE,
)

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ object PrefKeys {
const val USE_PREVIOUS_UNIFIED_PUSH_DISTRIBUTOR = "usePreviousUnifiedPushDistributor"

const val DOWNLOAD_LOCATION = "downloadLocation"
const val TAB_TAP_BEHAVIOUR = "tabTapBehaviour"

/** Keys that are no longer used (e.g., the preference has been removed */
object Deprecated {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class SharedPreferencesRepository @Inject constructor(
val downloadLocation: DownloadLocation
get() = getEnum(PrefKeys.DOWNLOAD_LOCATION, DownloadLocation.DOWNLOADS)

val tabTapBehaviour: TabTapBehaviour
get() = getEnum(PrefKeys.TAB_TAP_BEHAVIOUR, TabTapBehaviour.JUMP_TO_NEXT_PAGE)

// Ensure the listener is retained during minification. If you do not do this the
// field is removed and eventually garbage collected (because registering it as a
// change listener does not create a strong reference to it) and then no more
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 Pachli Association
*
* This file is a part of Pachli.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/

package app.pachli.core.preferences

/** Behaviour when the user taps on a tab. */
enum class TabTapBehaviour(override val displayResource: Int, override val value: String? = null) :
PreferenceEnum {
/** Jump the user's position to the top, fetching the next page of content. */
JUMP_TO_NEXT_PAGE(R.string.tab_tap_behaviour_jump_to_next_page),

/** Fetch the newest page of content and jump to that. */
JUMP_TO_NEWEST(R.string.tab_tap_behaviour_jump_to_newest),
}
6 changes: 6 additions & 0 deletions core/preferences/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@
<string name="download_location_downloads">Downloads folder</string>
<string name="download_location_per_account">Per-account folders, in Downloads folder</string>
<string name="download_location_per_sender">Per-sender folders, in Downloads folder</string>

<string name="app_theme_light">Light</string>
<string name="app_theme_black">Black</string>
<string name="app_theme_auto">Automatic at sunset</string>
<string name="app_theme_dark">Dark</string>
<string name="app_theme_system">Use system design</string>

<string name="pref_category_tabs">Tabs</string>
<string name="pref_title_tab_tap">Action when tapping a tab</string>
<string name="tab_tap_behaviour_jump_to_next_page">Jump to next page</string>
<string name="tab_tap_behaviour_jump_to_newest">Jump to newest content</string>
</resources>

0 comments on commit 9755866

Please sign in to comment.