Make hiding empty groups toggleable (#921)

This commit is contained in:
ymcx 2025-01-24 04:53:44 +02:00 committed by GitHub
parent 39dc3bceee
commit 1ffdb2f4f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 79 additions and 4 deletions

View File

@ -0,0 +1,48 @@
package me.ash.reader.infrastructure.preference
import android.content.Context
import androidx.compose.runtime.compositionLocalOf
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.ash.reader.ui.ext.DataStoreKey
import me.ash.reader.ui.ext.DataStoreKey.Companion.hideEmptyGroups
import me.ash.reader.ui.ext.dataStore
import me.ash.reader.ui.ext.put
val LocalHideEmptyGroups =
compositionLocalOf<HideEmptyGroupsPreference> { HideEmptyGroupsPreference.default }
sealed class HideEmptyGroupsPreference(val value: Boolean) : Preference() {
data object ON : HideEmptyGroupsPreference(true)
data object OFF : HideEmptyGroupsPreference(false)
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
context.dataStore.put(
hideEmptyGroups,
value
)
}
}
fun toggle(context: Context, scope: CoroutineScope) = scope.launch {
context.dataStore.put(
hideEmptyGroups,
!value
)
}
companion object {
val default = ON
val values = listOf(ON, OFF)
fun fromPreferences(preferences: Preferences) =
when (preferences[DataStoreKey.keys[hideEmptyGroups]?.key as Preferences.Key<Boolean>]) {
true -> ON
false -> OFF
else -> default
}
}
}

View File

@ -81,6 +81,7 @@ fun Preferences.toSettings(): Settings {
swipeStartAction = SwipeStartActionPreference.fromPreferences(this), swipeStartAction = SwipeStartActionPreference.fromPreferences(this),
swipeEndAction = SwipeEndActionPreference.fromPreferences(this), swipeEndAction = SwipeEndActionPreference.fromPreferences(this),
markAsReadOnScroll = MarkAsReadOnScrollPreference.fromPreferences(this), markAsReadOnScroll = MarkAsReadOnScrollPreference.fromPreferences(this),
hideEmptyGroups = HideEmptyGroupsPreference.fromPreferences(this),
pullToSwitchArticle = PullToSwitchArticlePreference.fromPreference(this), pullToSwitchArticle = PullToSwitchArticlePreference.fromPreference(this),
openLink = OpenLinkPreference.fromPreferences(this), openLink = OpenLinkPreference.fromPreferences(this),
openLinkSpecificBrowser = OpenLinkSpecificBrowserPreference.fromPreferences(this), openLinkSpecificBrowser = OpenLinkSpecificBrowserPreference.fromPreferences(this),

View File

@ -79,6 +79,7 @@ data class Settings(
val swipeStartAction: SwipeStartActionPreference = SwipeStartActionPreference.default, val swipeStartAction: SwipeStartActionPreference = SwipeStartActionPreference.default,
val swipeEndAction: SwipeEndActionPreference = SwipeEndActionPreference.default, val swipeEndAction: SwipeEndActionPreference = SwipeEndActionPreference.default,
val markAsReadOnScroll: MarkAsReadOnScrollPreference = MarkAsReadOnScrollPreference.default, val markAsReadOnScroll: MarkAsReadOnScrollPreference = MarkAsReadOnScrollPreference.default,
val hideEmptyGroups: HideEmptyGroupsPreference = HideEmptyGroupsPreference.default,
val pullToSwitchArticle: PullToSwitchArticlePreference = PullToSwitchArticlePreference.default, val pullToSwitchArticle: PullToSwitchArticlePreference = PullToSwitchArticlePreference.default,
val openLink: OpenLinkPreference = OpenLinkPreference.default, val openLink: OpenLinkPreference = OpenLinkPreference.default,
val openLinkSpecificBrowser: OpenLinkSpecificBrowserPreference = OpenLinkSpecificBrowserPreference.default, val openLinkSpecificBrowser: OpenLinkSpecificBrowserPreference = OpenLinkSpecificBrowserPreference.default,
@ -170,6 +171,7 @@ fun SettingsProvider(
LocalArticleListSwipeStartAction provides settings.swipeStartAction, LocalArticleListSwipeStartAction provides settings.swipeStartAction,
LocalArticleListSwipeEndAction provides settings.swipeEndAction, LocalArticleListSwipeEndAction provides settings.swipeEndAction,
LocalMarkAsReadOnScroll provides settings.markAsReadOnScroll, LocalMarkAsReadOnScroll provides settings.markAsReadOnScroll,
LocalHideEmptyGroups provides settings.hideEmptyGroups,
LocalPullToSwitchArticle provides settings.pullToSwitchArticle, LocalPullToSwitchArticle provides settings.pullToSwitchArticle,
LocalOpenLink provides settings.openLink, LocalOpenLink provides settings.openLink,
LocalOpenLinkSpecificBrowser provides settings.openLinkSpecificBrowser, LocalOpenLinkSpecificBrowser provides settings.openLinkSpecificBrowser,

View File

@ -162,6 +162,7 @@ data class DataStoreKey<T>(
const val swipeStartAction = "swipeStartAction" const val swipeStartAction = "swipeStartAction"
const val swipeEndAction = "swipeEndAction" const val swipeEndAction = "swipeEndAction"
const val markAsReadOnScroll = "markAsReadOnScroll" const val markAsReadOnScroll = "markAsReadOnScroll"
const val hideEmptyGroups = "hideEmptyGroups"
const val pullToSwitchArticle = "pullToSwitchArticle" const val pullToSwitchArticle = "pullToSwitchArticle"
const val openLink = "openLink" const val openLink = "openLink"
const val openLinkAppSpecificBrowser = "openLinkAppSpecificBrowser" const val openLinkAppSpecificBrowser = "openLinkAppSpecificBrowser"
@ -239,6 +240,7 @@ data class DataStoreKey<T>(
booleanPreferencesKey(markAsReadOnScroll), booleanPreferencesKey(markAsReadOnScroll),
Boolean::class.java Boolean::class.java
), ),
hideEmptyGroups to DataStoreKey(booleanPreferencesKey(hideEmptyGroups), Boolean::class.java),
pullToSwitchArticle to DataStoreKey(booleanPreferencesKey(pullToSwitchArticle), Boolean::class.java), pullToSwitchArticle to DataStoreKey(booleanPreferencesKey(pullToSwitchArticle), Boolean::class.java),
openLink to DataStoreKey(intPreferencesKey(openLink), Int::class.java), openLink to DataStoreKey(intPreferencesKey(openLink), Int::class.java),
openLinkAppSpecificBrowser to DataStoreKey(stringPreferencesKey(openLinkAppSpecificBrowser), String::class.java), openLinkAppSpecificBrowser to DataStoreKey(stringPreferencesKey(openLinkAppSpecificBrowser), String::class.java),

View File

@ -66,6 +66,7 @@ import me.ash.reader.infrastructure.preference.LocalFeedsFilterBarTonalElevation
import me.ash.reader.infrastructure.preference.LocalFeedsGroupListExpand import me.ash.reader.infrastructure.preference.LocalFeedsGroupListExpand
import me.ash.reader.infrastructure.preference.LocalFeedsGroupListTonalElevation import me.ash.reader.infrastructure.preference.LocalFeedsGroupListTonalElevation
import me.ash.reader.infrastructure.preference.LocalFeedsTopBarTonalElevation import me.ash.reader.infrastructure.preference.LocalFeedsTopBarTonalElevation
import me.ash.reader.infrastructure.preference.LocalHideEmptyGroups
import me.ash.reader.infrastructure.preference.LocalNewVersionNumber import me.ash.reader.infrastructure.preference.LocalNewVersionNumber
import me.ash.reader.infrastructure.preference.LocalSkipVersionNumber import me.ash.reader.infrastructure.preference.LocalSkipVersionNumber
import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.FilterBar
@ -198,9 +199,11 @@ fun FeedsPage(
feedsViewModel.fetchAccount() feedsViewModel.fetchAccount()
} }
val hideEmptyGroups = LocalHideEmptyGroups.current.value
LaunchedEffect(filterUiState, isSyncing) { LaunchedEffect(filterUiState, isSyncing) {
snapshotFlow { filterUiState }.collect { snapshotFlow { filterUiState }.collect {
feedsViewModel.pullFeeds(it) feedsViewModel.pullFeeds(it, hideEmptyGroups)
} }
} }

View File

@ -49,7 +49,7 @@ class FeedsViewModel @Inject constructor(
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
fun pullFeeds(filterState: FilterState) { fun pullFeeds(filterState: FilterState, hideEmptyGroups: Boolean) {
val isStarred = filterState.filter.isStarred() val isStarred = filterState.filter.isStarred()
val isUnread = filterState.filter.isUnread() val isUnread = filterState.filter.isUnread()
_feedsUiState.update { _feedsUiState.update {
@ -77,7 +77,7 @@ class FeedsViewModel @Inject constructor(
while (groupIterator.hasNext()) { while (groupIterator.hasNext()) {
val groupWithFeed = groupIterator.next() val groupWithFeed = groupIterator.next()
val groupImportant = importantMap[groupWithFeed.group.id] ?: 0 val groupImportant = importantMap[groupWithFeed.group.id] ?: 0
if ((isStarred || isUnread) && groupImportant == 0) { if (hideEmptyGroups && (isStarred || isUnread) && groupImportant == 0) {
groupIterator.remove() groupIterator.remove()
continue continue
} }
@ -87,7 +87,7 @@ class FeedsViewModel @Inject constructor(
val feed = feedIterator.next() val feed = feedIterator.next()
val feedImportant = importantMap[feed.id] ?: 0 val feedImportant = importantMap[feed.id] ?: 0
groupWithFeed.group.feeds++ groupWithFeed.group.feeds++
if ((isStarred || isUnread) && feedImportant == 0) { if (hideEmptyGroups && (isStarred || isUnread) && feedImportant == 0) {
feedIterator.remove() feedIterator.remove()
continue continue
} }

View File

@ -26,6 +26,7 @@ import me.ash.reader.infrastructure.preference.InitialFilterPreference
import me.ash.reader.infrastructure.preference.InitialPagePreference import me.ash.reader.infrastructure.preference.InitialPagePreference
import me.ash.reader.infrastructure.preference.LocalArticleListSwipeEndAction import me.ash.reader.infrastructure.preference.LocalArticleListSwipeEndAction
import me.ash.reader.infrastructure.preference.LocalArticleListSwipeStartAction import me.ash.reader.infrastructure.preference.LocalArticleListSwipeStartAction
import me.ash.reader.infrastructure.preference.LocalHideEmptyGroups
import me.ash.reader.infrastructure.preference.LocalInitialFilter import me.ash.reader.infrastructure.preference.LocalInitialFilter
import me.ash.reader.infrastructure.preference.LocalInitialPage import me.ash.reader.infrastructure.preference.LocalInitialPage
import me.ash.reader.infrastructure.preference.LocalMarkAsReadOnScroll import me.ash.reader.infrastructure.preference.LocalMarkAsReadOnScroll
@ -58,6 +59,7 @@ fun InteractionPage(
val swipeToStartAction = LocalArticleListSwipeStartAction.current val swipeToStartAction = LocalArticleListSwipeStartAction.current
val swipeToEndAction = LocalArticleListSwipeEndAction.current val swipeToEndAction = LocalArticleListSwipeEndAction.current
val markAsReadOnScroll = LocalMarkAsReadOnScroll.current val markAsReadOnScroll = LocalMarkAsReadOnScroll.current
val hideEmptyGroups = LocalHideEmptyGroups.current
val pullToSwitchArticle = LocalPullToSwitchArticle.current val pullToSwitchArticle = LocalPullToSwitchArticle.current
val openLink = LocalOpenLink.current val openLink = LocalOpenLink.current
val openLinkSpecificBrowser = LocalOpenLinkSpecificBrowser.current val openLinkSpecificBrowser = LocalOpenLinkSpecificBrowser.current
@ -112,6 +114,22 @@ fun InteractionPage(
) {} ) {}
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.feeds_page),
)
SettingItem(
title = stringResource(R.string.hide_empty_groups),
onClick = {
hideEmptyGroups.toggle(context, scope)
},
) {
RYSwitch(activated = hideEmptyGroups.value) {
hideEmptyGroups.toggle(context, scope)
}
}
Spacer(modifier = Modifier.height(24.dp))
Subtitle( Subtitle(
modifier = Modifier.padding(horizontal = 24.dp), modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.article_list), text = stringResource(R.string.article_list),

View File

@ -459,6 +459,7 @@
<string name="bionic_reading_domain" translatable="false">bionic-reading.com</string> <string name="bionic_reading_domain" translatable="false">bionic-reading.com</string>
<string name="bionic_reading_link" translatable="false">https://bionic-reading.com</string> <string name="bionic_reading_link" translatable="false">https://bionic-reading.com</string>
<string name="mark_as_read_on_scroll">Mark as read on scroll</string> <string name="mark_as_read_on_scroll">Mark as read on scroll</string>
<string name="hide_empty_groups">Hide empty groups</string>
<string name="become_a_sponsor">Become a sponsor</string> <string name="become_a_sponsor">Become a sponsor</string>
<string name="sponsor_desc">We build and upkeep this free, open-source app in our off-hours. If you enjoy it, please consider supporting us with a small donation! ☕️</string> <string name="sponsor_desc">We build and upkeep this free, open-source app in our off-hours. If you enjoy it, please consider supporting us with a small donation! ☕️</string>
<string name="toolbars">Toolbars</string> <string name="toolbars">Toolbars</string>