diff --git a/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt b/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt index 912e209c0..c9b19c1ab 100644 --- a/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt +++ b/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt @@ -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 @@ -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) + } } } diff --git a/app/src/main/java/app/pachli/components/notifications/NotificationsViewModel.kt b/app/src/main/java/app/pachli/components/notifications/NotificationsViewModel.kt index 0af09a6c4..50a469fdd 100644 --- a/app/src/main/java/app/pachli/components/notifications/NotificationsViewModel.kt +++ b/app/src/main/java/app/pachli/components/notifications/NotificationsViewModel.kt @@ -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 @@ -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 */ @@ -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, ) } } @@ -384,6 +389,7 @@ class NotificationsViewModel @Inject constructor( account.lastNotificationId = "0" accountManager.saveAccount(account) reload.getAndUpdate { it + 1 } + repository.invalidate() } } @@ -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, @@ -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) } } diff --git a/app/src/main/java/app/pachli/components/preference/PreferencesFragment.kt b/app/src/main/java/app/pachli/components/preference/PreferencesFragment.kt index d364becab..f5d0a3550 100644 --- a/app/src/main/java/app/pachli/components/preference/PreferencesFragment.kt +++ b/app/src/main/java/app/pachli/components/preference/PreferencesFragment.kt @@ -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 @@ -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) @@ -285,6 +277,24 @@ 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 @@ -292,11 +302,10 @@ class PreferencesFragment : PreferenceFragmentCompat() { isSingleLineTitle = false } - switchPreference { - setDefaultValue(false) - key = PrefKeys.SHOW_STATS_INLINE - setTitle(R.string.pref_title_show_stat_inline) - isSingleLineTitle = false + enumListPreference { + setDefaultValue(TabTapBehaviour.JUMP_TO_NEXT_PAGE) + setTitle(app.pachli.core.preferences.R.string.pref_title_tab_tap) + key = PrefKeys.TAB_TAP_BEHAVIOUR } } diff --git a/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt b/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt index 31afb7615..47d66e371 100644 --- a/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt @@ -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 @@ -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) + } } } diff --git a/app/src/main/java/app/pachli/components/timeline/viewmodel/TimelineViewModel.kt b/app/src/main/java/app/pachli/components/timeline/viewmodel/TimelineViewModel.kt index 71c4e8a00..8927baaf9 100644 --- a/app/src/main/java/app/pachli/components/timeline/viewmodel/TimelineViewModel.kt +++ b/app/src/main/java/app/pachli/components/timeline/viewmodel/TimelineViewModel.kt @@ -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 @@ -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 @@ -406,13 +410,18 @@ 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, @@ -420,6 +429,7 @@ abstract class TimelineViewModel( initialValue = UiState( showFabWhileScrolling = !sharedPreferencesRepository.getBoolean(PrefKeys.FAB_HIDE, false), reverseTimeline = sharedPreferencesRepository.getBoolean(PrefKeys.LAB_REVERSE_TIMELINE, false), + tabTapBehaviour = sharedPreferencesRepository.tabTapBehaviour, ), ) diff --git a/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestUiState.kt b/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestUiState.kt index d51e8241f..6467c04af 100644 --- a/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestUiState.kt +++ b/app/src/test/java/app/pachli/components/notifications/NotificationsViewModelTestUiState.kt @@ -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 @@ -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 diff --git a/core/preferences/src/main/kotlin/app/pachli/core/preferences/SettingsConstants.kt b/core/preferences/src/main/kotlin/app/pachli/core/preferences/SettingsConstants.kt index 176cdb9e1..2a433b769 100644 --- a/core/preferences/src/main/kotlin/app/pachli/core/preferences/SettingsConstants.kt +++ b/core/preferences/src/main/kotlin/app/pachli/core/preferences/SettingsConstants.kt @@ -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 { diff --git a/core/preferences/src/main/kotlin/app/pachli/core/preferences/SharedPreferencesRepository.kt b/core/preferences/src/main/kotlin/app/pachli/core/preferences/SharedPreferencesRepository.kt index 557553776..e28ae1327 100644 --- a/core/preferences/src/main/kotlin/app/pachli/core/preferences/SharedPreferencesRepository.kt +++ b/core/preferences/src/main/kotlin/app/pachli/core/preferences/SharedPreferencesRepository.kt @@ -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 diff --git a/core/preferences/src/main/kotlin/app/pachli/core/preferences/TabTapBehaviour.kt b/core/preferences/src/main/kotlin/app/pachli/core/preferences/TabTapBehaviour.kt new file mode 100644 index 000000000..ce4dc5442 --- /dev/null +++ b/core/preferences/src/main/kotlin/app/pachli/core/preferences/TabTapBehaviour.kt @@ -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 . + */ + +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), +} diff --git a/core/preferences/src/main/res/values/strings.xml b/core/preferences/src/main/res/values/strings.xml index 1e6d7d424..d31bc36b1 100644 --- a/core/preferences/src/main/res/values/strings.xml +++ b/core/preferences/src/main/res/values/strings.xml @@ -21,9 +21,15 @@ Downloads folder Per-account folders, in Downloads folder Per-sender folders, in Downloads folder + Light Black Automatic at sunset Dark Use system design + + Tabs + Action when tapping a tab + Jump to next page + Jump to newest content