From 02a7174cd877c65fdb9810af6438b535897b2851 Mon Sep 17 00:00:00 2001 From: SkyD666 Date: Fri, 27 Dec 2024 16:44:17 +0800 Subject: [PATCH] [feature] Support #65 --- app/build.gradle.kts | 2 +- .../java/com/skyd/anivu/ext/PreferenceExt.kt | 2 + .../com/skyd/anivu/model/db/dao/FeedDao.kt | 19 +- .../com/skyd/anivu/model/db/dao/GroupDao.kt | 2 +- .../skyd/anivu/model/preference/Settings.kt | 4 + .../feed/FeedNumberBadgePreference.kt | 44 +++++ .../model/repository/ArticleRepository.kt | 2 +- .../model/repository/feed/FeedRepository.kt | 178 +++++++++--------- .../importexport/ImportExportRepository.kt | 2 +- .../com/skyd/anivu/ui/local/LocalValue.kt | 2 + .../anivu/ui/screen/feed/EditFeedSheet.kt | 14 +- .../skyd/anivu/ui/screen/feed/FeedEvent.kt | 11 +- .../ui/screen/feed/FeedPartialStateChange.kt | 12 +- .../skyd/anivu/ui/screen/feed/FeedScreen.kt | 52 +++-- .../anivu/ui/screen/feed/FeedViewModel.kt | 44 +++-- .../anivu/ui/screen/feed/item/Feed1Item.kt | 72 +++++-- .../appearance/feed/FeedStyleScreen.kt | 38 ++++ app/src/main/res/values-tr/strings.xml | 4 - app/src/main/res/values-zh-rCN/strings.xml | 3 - app/src/main/res/values-zh-rTW/strings.xml | 3 - app/src/main/res/values/strings.xml | 8 +- 21 files changed, 330 insertions(+), 188 deletions(-) create mode 100644 app/src/main/java/com/skyd/anivu/model/preference/appearance/feed/FeedNumberBadgePreference.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2de35933..7869273c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { minSdk = 24 targetSdk = 35 versionCode = 24 - versionName = "2.1-beta17" + versionName = "2.1-rc01" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt index 09f1101b..8e82f0ee 100644 --- a/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt +++ b/app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt @@ -17,6 +17,7 @@ import com.skyd.anivu.model.preference.appearance.article.ShowArticlePullRefresh import com.skyd.anivu.model.preference.appearance.article.ShowArticleTopBarRefreshPreference import com.skyd.anivu.model.preference.appearance.feed.FeedDefaultGroupExpandPreference import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference +import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference import com.skyd.anivu.model.preference.appearance.media.MediaShowThumbnailPreference import com.skyd.anivu.model.preference.appearance.read.ReadContentTonalElevationPreference @@ -87,6 +88,7 @@ fun Preferences.toSettings(): Settings { readTextSize = ReadTextSizePreference.fromPreferences(this), readContentTonalElevation = ReadContentTonalElevationPreference.fromPreferences(this), readTopBarTonalElevation = ReadTopBarTonalElevationPreference.fromPreferences(this), + feedNumberBadge = FeedNumberBadgePreference.fromPreferences(this), // Update ignoreUpdateVersion = IgnoreUpdateVersionPreference.fromPreferences(this), diff --git a/app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt b/app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt index 753a9b27..1c31966b 100644 --- a/app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt +++ b/app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt @@ -139,17 +139,16 @@ interface FeedDao { fun getFeedPagingSource(): PagingSource @Transaction - @Query("SELECT * FROM $FEED_TABLE_NAME WHERE ${FeedBean.URL_COLUMN} = :feedUrl") - suspend fun getFeed(feedUrl: String): FeedBean + @Query("SELECT * FROM $FEED_VIEW_NAME WHERE ${FeedBean.URL_COLUMN} = :feedUrl") + suspend fun getFeed(feedUrl: String): FeedViewBean @Transaction - @Query( - """ - SELECT * FROM $FEED_VIEW_NAME - WHERE ${FeedBean.GROUP_ID_COLUMN} IN (:groupIds) - """ - ) - suspend fun getFeedsIn(groupIds: List): List + @Query("SELECT * FROM $FEED_VIEW_NAME WHERE ${FeedBean.URL_COLUMN} IN (:feedUrls)") + suspend fun getFeedsIn(feedUrls: List): List + + @Transaction + @Query("SELECT * FROM $FEED_VIEW_NAME WHERE ${FeedBean.GROUP_ID_COLUMN} IN (:groupIds)") + suspend fun getFeedsInGroup(groupIds: List): List @Transaction @Query( @@ -159,7 +158,7 @@ interface FeedDao { ${FeedBean.GROUP_ID_COLUMN} NOT IN (:groupIds) """ ) - suspend fun getFeedsNotIn(groupIds: List): List + suspend fun getFeedsNotInGroup(groupIds: List): List @Transaction @Query( diff --git a/app/src/main/java/com/skyd/anivu/model/db/dao/GroupDao.kt b/app/src/main/java/com/skyd/anivu/model/db/dao/GroupDao.kt index d7708641..b94374d0 100644 --- a/app/src/main/java/com/skyd/anivu/model/db/dao/GroupDao.kt +++ b/app/src/main/java/com/skyd/anivu/model/db/dao/GroupDao.kt @@ -130,7 +130,7 @@ interface GroupDao { removeGroupIdFromList(groupId) innerRemoveGroup(groupId) return EntryPointAccessors.fromApplication(appContext, GroupDaoEntryPoint::class.java).run { - feedDao.getFeedsIn(listOf(groupId)).forEach { + feedDao.getFeedsInGroup(listOf(groupId)).forEach { it.feed.customIcon?.let { icon -> tryDeleteFeedIconFile(icon) } } feedDao.removeFeedByGroupId(groupId) diff --git a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt index dea849aa..275dad29 100644 --- a/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt +++ b/app/src/main/java/com/skyd/anivu/model/preference/Settings.kt @@ -22,6 +22,7 @@ import com.skyd.anivu.model.preference.appearance.article.ShowArticlePullRefresh import com.skyd.anivu.model.preference.appearance.article.ShowArticleTopBarRefreshPreference import com.skyd.anivu.model.preference.appearance.feed.FeedDefaultGroupExpandPreference import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference +import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference import com.skyd.anivu.model.preference.appearance.media.MediaShowThumbnailPreference import com.skyd.anivu.model.preference.appearance.read.ReadContentTonalElevationPreference @@ -84,6 +85,7 @@ import com.skyd.anivu.ui.local.LocalDateStyle import com.skyd.anivu.ui.local.LocalDeduplicateTitleInDesc import com.skyd.anivu.ui.local.LocalFeedDefaultGroupExpand import com.skyd.anivu.ui.local.LocalFeedListTonalElevation +import com.skyd.anivu.ui.local.LocalFeedNumberBadge import com.skyd.anivu.ui.local.LocalFeedTopBarTonalElevation import com.skyd.anivu.ui.local.LocalHardwareDecode import com.skyd.anivu.ui.local.LocalHideEmptyDefault @@ -152,6 +154,7 @@ data class Settings( val readTextSize: Float = ReadTextSizePreference.default, val readContentTonalElevation: Float = ReadContentTonalElevationPreference.default, val readTopBarTonalElevation: Float = ReadTopBarTonalElevationPreference.default, + val feedNumberBadge: Int = FeedNumberBadgePreference.default, // Update val ignoreUpdateVersion: Long = IgnoreUpdateVersionPreference.default, // Behavior @@ -229,6 +232,7 @@ fun SettingsProvider( LocalReadTextSize provides settings.readTextSize, LocalReadContentTonalElevation provides settings.readContentTonalElevation, LocalReadTopBarTonalElevation provides settings.readTopBarTonalElevation, + LocalFeedNumberBadge provides settings.feedNumberBadge, // Update LocalIgnoreUpdateVersion provides settings.ignoreUpdateVersion, // Behavior diff --git a/app/src/main/java/com/skyd/anivu/model/preference/appearance/feed/FeedNumberBadgePreference.kt b/app/src/main/java/com/skyd/anivu/model/preference/appearance/feed/FeedNumberBadgePreference.kt new file mode 100644 index 00000000..5d42ee60 --- /dev/null +++ b/app/src/main/java/com/skyd/anivu/model/preference/appearance/feed/FeedNumberBadgePreference.kt @@ -0,0 +1,44 @@ +package com.skyd.anivu.model.preference.appearance.feed + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.intPreferencesKey +import com.skyd.anivu.R +import com.skyd.anivu.base.BasePreference +import com.skyd.anivu.ext.dataStore +import com.skyd.anivu.ext.getOrDefault +import com.skyd.anivu.ext.put +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +object FeedNumberBadgePreference : BasePreference { + private const val FEED_NUMBER_BADGE = "feedNumberBadge" + + const val UNREAD = 1 + const val ALL = 1 shl 1 + const val UNREAD_ALL = UNREAD + ALL + val values = arrayOf(UNREAD, ALL, UNREAD_ALL) + + override val default = ALL + + val key = intPreferencesKey(FEED_NUMBER_BADGE) + + fun put(context: Context, scope: CoroutineScope, value: Int) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(key, value) + } + } + + override fun fromPreferences(preferences: Preferences): Int = preferences[key] ?: default + + fun toDisplayName( + context: Context, + value: Int = context.dataStore.getOrDefault(this), + ): String = when (value) { + UNREAD -> context.getString(R.string.feed_number_badge_unread) + ALL -> context.getString(R.string.feed_number_badge_all) + UNREAD_ALL -> context.getString(R.string.feed_number_badge_unread_all) + else -> context.getString(R.string.unknown) + } +} diff --git a/app/src/main/java/com/skyd/anivu/model/repository/ArticleRepository.kt b/app/src/main/java/com/skyd/anivu/model/repository/ArticleRepository.kt index f82ddb44..ab98be8e 100644 --- a/app/src/main/java/com/skyd/anivu/model/repository/ArticleRepository.kt +++ b/app/src/main/java/com/skyd/anivu/model/repository/ArticleRepository.kt @@ -102,7 +102,7 @@ class ArticleRepository @Inject constructor( requests += async { val articleBeanList = runCatching { rssHelper.queryRssXml( - feed = feedDao.getFeed(feedUrl), + feed = feedDao.getFeed(feedUrl).feed, latestLink = articleDao.queryLatestByFeedUrl(feedUrl)?.link, )?.also { feedWithArticle -> feedDao.updateFeed(feedWithArticle.feed) diff --git a/app/src/main/java/com/skyd/anivu/model/repository/feed/FeedRepository.kt b/app/src/main/java/com/skyd/anivu/model/repository/feed/FeedRepository.kt index 946fe4e2..39aecb45 100644 --- a/app/src/main/java/com/skyd/anivu/model/repository/feed/FeedRepository.kt +++ b/app/src/main/java/com/skyd/anivu/model/repository/feed/FeedRepository.kt @@ -10,7 +10,7 @@ import com.skyd.anivu.ext.dataStore import com.skyd.anivu.ext.isLocal import com.skyd.anivu.ext.isNetwork import com.skyd.anivu.ext.put -import com.skyd.anivu.model.bean.feed.FeedBean +import com.skyd.anivu.model.bean.feed.FeedViewBean import com.skyd.anivu.model.bean.group.GroupVo import com.skyd.anivu.model.db.dao.ArticleDao import com.skyd.anivu.model.db.dao.FeedDao @@ -35,29 +35,25 @@ class FeedRepository @Inject constructor( private val reorderGroupRepository: ReorderGroupRepository, private val rssHelper: RssHelper, ) : BaseRepository() { - fun requestGroupAnyList(): Flow> { - return combine( - groupDao.getGroupWithFeeds(), - groupDao.getGroupIds(), - ) { groupList, groupIds -> - groupList to feedDao.getFeedsNotIn(groupIds) - }.map { (groupList, defaultFeeds) -> - mutableListOf().apply { - add(GroupVo.DefaultGroup) - addAll(defaultFeeds) - reorderGroupRepository.sortGroupWithFeed(groupList).forEach { group -> - add(group.group.toVo()) - addAll(group.feeds) - } + fun requestGroupAnyList(): Flow> = combine( + groupDao.getGroupWithFeeds(), + groupDao.getGroupIds(), + ) { groupList, groupIds -> + groupList to feedDao.getFeedsNotInGroup(groupIds) + }.map { (groupList, defaultFeeds) -> + mutableListOf().apply { + add(GroupVo.DefaultGroup) + addAll(defaultFeeds) + reorderGroupRepository.sortGroupWithFeed(groupList).forEach { group -> + add(group.group.toVo()) + addAll(group.feeds) } - }.flowOn(Dispatchers.IO) - } - - suspend fun clearGroupArticles(groupId: String): Flow { - return flow { - val realGroupId = if (groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId - emit(articleDao.deleteArticlesInGroup(realGroupId)) } + }.flowOn(Dispatchers.IO) + + fun clearGroupArticles(groupId: String): Flow = flow { + val realGroupId = if (groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId + emit(articleDao.deleteArticlesInGroup(realGroupId)) } suspend fun deleteGroup(groupId: String): Flow { @@ -65,7 +61,7 @@ class FeedRepository @Inject constructor( else flowOf(groupDao.removeGroupWithFeed(groupId)).flowOn(Dispatchers.IO) } - suspend fun renameGroup(groupId: String, name: String): Flow { + fun renameGroup(groupId: String, name: String): Flow { return if (groupId == GroupVo.DEFAULT_GROUP_ID) flow { emit(GroupVo.DefaultGroup) } else flow { @@ -81,83 +77,91 @@ class FeedRepository @Inject constructor( .flowOn(Dispatchers.IO) } - suspend fun setFeed( + fun getFeedViewsByUrls(urls: List) = flow { + emit(feedDao.getFeedsIn(urls)) + }.flowOn(Dispatchers.IO) + + fun getFeedViewsByGroupId(groupId: String?) = flow { + emit(feedDao.getFeedsByGroupId(groupId)) + }.flowOn(Dispatchers.IO) + + fun setFeed( url: String, groupId: String?, nickname: String?, - ): Flow { - return flow { - val realNickname = if (nickname.isNullOrBlank()) null else nickname - val realGroupId = - if (groupId.isNullOrBlank() || groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId - val feedWithArticleBean = rssHelper.searchFeed(url = url).run { - copy( - feed = feed.copy( - groupId = realGroupId, - nickname = realNickname, - ) + ): Flow = flow { + val realNickname = if (nickname.isNullOrBlank()) null else nickname + val realGroupId = + if (groupId.isNullOrBlank() || groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId + val feedWithArticleBean = rssHelper.searchFeed(url = url).run { + copy( + feed = feed.copy( + groupId = realGroupId, + nickname = realNickname, ) - } - feedDao.setFeedWithArticle(feedWithArticleBean) - emit(feedWithArticleBean.feed) - }.flowOn(Dispatchers.IO) - } + ) + } + feedDao.setFeedWithArticle(feedWithArticleBean) + emit(feedDao.getFeed(url)) + }.flowOn(Dispatchers.IO) - suspend fun editFeedUrl( + fun editFeedUrl( oldUrl: String, newUrl: String, - ): Flow = flow { + ): Flow = flow { val oldFeed = feedDao.getFeed(oldUrl) var newFeed = oldFeed if (oldUrl != newUrl) { val feedWithArticleBean = rssHelper.searchFeed(url = newUrl).run { - newFeed = feed.copy( - groupId = oldFeed.groupId, - nickname = oldFeed.nickname, - customDescription = oldFeed.customDescription, - customIcon = oldFeed.customIcon, + copy( + feed = feed.copy( + groupId = oldFeed.feed.groupId, + nickname = oldFeed.feed.nickname, + customDescription = oldFeed.feed.customDescription, + customIcon = oldFeed.feed.customIcon, + ) ) - copy(feed = newFeed) } feedDao.removeFeed(oldUrl) feedDao.setFeedWithArticle(feedWithArticleBean) + newFeed = feedDao.getFeed(newUrl) } emit(newFeed) }.flowOn(Dispatchers.IO) - suspend fun editFeedNickname( + fun editFeedNickname( url: String, nickname: String?, - ): Flow = flow { + ): Flow = flow { val realNickname = if (nickname.isNullOrBlank()) null else nickname - feedDao.updateFeed(feedDao.getFeed(url).copy(nickname = realNickname)) + feedDao.updateFeed(feedDao.getFeed(url).feed.copy(nickname = realNickname)) emit(feedDao.getFeed(url)) }.flowOn(Dispatchers.IO) - suspend fun editFeedGroup( + fun editFeedGroup( url: String, groupId: String?, - ): Flow = flow { + ): Flow = flow { val realGroupId = if (groupId.isNullOrBlank() || groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId - feedDao.updateFeed(feedDao.getFeed(url).copy(groupId = realGroupId)) + feedDao.updateFeed(feedDao.getFeed(url).feed.copy(groupId = realGroupId)) emit(feedDao.getFeed(url)) }.flowOn(Dispatchers.IO) - suspend fun editFeedCustomDescription( + fun editFeedCustomDescription( url: String, customDescription: String?, - ): Flow = flow { - feedDao.updateFeed(feedDao.getFeed(url).copy(customDescription = customDescription)) + ): Flow = flow { + feedDao.updateFeed(feedDao.getFeed(url).feed.copy(customDescription = customDescription)) emit(feedDao.getFeed(url)) }.flowOn(Dispatchers.IO) - suspend fun editFeedCustomIcon( + fun editFeedCustomIcon( url: String, customIcon: Uri?, - ): Flow = flow { + ): Flow = flow { var filePath: String? = null if (customIcon != null) { if (customIcon.isLocal()) { @@ -171,43 +175,39 @@ class FeedRepository @Inject constructor( } } val oldFeed = feedDao.getFeed(url) - oldFeed.customIcon?.let { icon -> tryDeleteFeedIconFile(icon) } - feedDao.updateFeed(oldFeed.copy(customIcon = filePath)) + oldFeed.feed.customIcon?.let { icon -> tryDeleteFeedIconFile(icon) } + feedDao.updateFeed(oldFeed.feed.copy(customIcon = filePath)) emit(feedDao.getFeed(url)) }.flowOn(Dispatchers.IO) - suspend fun editFeedSortXmlArticlesOnUpdate( + fun editFeedSortXmlArticlesOnUpdate( url: String, sort: Boolean, - ): Flow = flow { + ): Flow = flow { feedDao.updateFeedSortXmlArticlesOnUpdate(feedUrl = url, sort = sort) emit(feedDao.getFeed(url)) }.flowOn(Dispatchers.IO) - suspend fun removeFeed(url: String): Flow { + fun removeFeed(url: String): Flow { return flow { - feedDao.getFeed(url).customIcon?.let { icon -> tryDeleteFeedIconFile(icon) } + feedDao.getFeed(url).feed.customIcon?.let { icon -> tryDeleteFeedIconFile(icon) } emit(feedDao.removeFeed(url)) }.flowOn(Dispatchers.IO) } - suspend fun clearFeedArticles(url: String): Flow { - return flow { - emit(articleDao.deleteArticleInFeed(url)) - }.flowOn(Dispatchers.IO) - } + fun clearFeedArticles(url: String): Flow = flow { + emit(articleDao.deleteArticleInFeed(url)) + }.flowOn(Dispatchers.IO) - suspend fun createGroup(group: GroupVo): Flow { - return flow { - if (groupDao.containsByName(group.name) == 0) { - emit(groupDao.setGroup(group.toPo())) - } else { - emit(Unit) - } - }.flowOn(Dispatchers.IO) - } + fun createGroup(group: GroupVo): Flow = flow { + if (groupDao.containsByName(group.name) == 0) { + emit(groupDao.setGroup(group.toPo())) + } else { + emit(Unit) + } + }.flowOn(Dispatchers.IO) - suspend fun changeGroupExpanded(groupId: String?, expanded: Boolean): Flow { + fun changeGroupExpanded(groupId: String?, expanded: Boolean): Flow { return flow { if (groupId == null || groupId == GroupVo.DEFAULT_GROUP_ID) { appContext.dataStore.put( @@ -221,18 +221,14 @@ class FeedRepository @Inject constructor( }.flowOn(Dispatchers.IO) } - suspend fun readAllInGroup(groupId: String?): Flow { - return flow { - val realGroupId = if (groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId - emit(articleDao.readAllInGroup(realGroupId)) - }.flowOn(Dispatchers.IO) - } + fun readAllInGroup(groupId: String?): Flow = flow { + val realGroupId = if (groupId == GroupVo.DEFAULT_GROUP_ID) null else groupId + emit(articleDao.readAllInGroup(realGroupId)) + }.flowOn(Dispatchers.IO) - suspend fun readAllInFeed(feedUrl: String): Flow { - return flow { - emit(articleDao.readAllInFeed(feedUrl)) - }.flowOn(Dispatchers.IO) - } + fun readAllInFeed(feedUrl: String): Flow = flow { + emit(articleDao.readAllInFeed(feedUrl)) + }.flowOn(Dispatchers.IO) } fun tryDeleteFeedIconFile(path: String?) { diff --git a/app/src/main/java/com/skyd/anivu/model/repository/importexport/ImportExportRepository.kt b/app/src/main/java/com/skyd/anivu/model/repository/importexport/ImportExportRepository.kt index ce4bf261..5b64a5c0 100644 --- a/app/src/main/java/com/skyd/anivu/model/repository/importexport/ImportExportRepository.kt +++ b/app/src/main/java/com/skyd/anivu/model/repository/importexport/ImportExportRepository.kt @@ -42,7 +42,7 @@ class ImportExportRepository @Inject constructor( private suspend fun defaultGroupFeeds(): Flow> { return groupDao.getGroupIds().map { groupIds -> - feedDao.getFeedsNotIn(groupIds) + feedDao.getFeedsNotInGroup(groupIds) }.flowOn(Dispatchers.IO) } diff --git a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt index 9e429140..b9f22e01 100644 --- a/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt +++ b/app/src/main/java/com/skyd/anivu/ui/local/LocalValue.kt @@ -18,6 +18,7 @@ import com.skyd.anivu.model.preference.appearance.article.ShowArticlePullRefresh import com.skyd.anivu.model.preference.appearance.article.ShowArticleTopBarRefreshPreference import com.skyd.anivu.model.preference.appearance.feed.FeedDefaultGroupExpandPreference import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference +import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference import com.skyd.anivu.model.preference.appearance.media.MediaShowThumbnailPreference import com.skyd.anivu.model.preference.appearance.read.ReadContentTonalElevationPreference @@ -103,6 +104,7 @@ val LocalReadContentTonalElevation = compositionLocalOf { ReadContentTonalElevationPreference.default } val LocalReadTopBarTonalElevation = compositionLocalOf { ReadTopBarTonalElevationPreference.default } +val LocalFeedNumberBadge = compositionLocalOf { FeedNumberBadgePreference.default } // Update val LocalIgnoreUpdateVersion = compositionLocalOf { IgnoreUpdateVersionPreference.default } diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/EditFeedSheet.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/EditFeedSheet.kt index 9ab25fa3..3d63452e 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/EditFeedSheet.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/EditFeedSheet.kt @@ -67,15 +67,15 @@ import com.skyd.anivu.R import com.skyd.anivu.ext.copy import com.skyd.anivu.ext.openBrowser import com.skyd.anivu.ext.readable -import com.skyd.anivu.model.bean.feed.FeedBean +import com.skyd.anivu.model.bean.feed.FeedViewBean import com.skyd.anivu.model.bean.group.GroupVo import com.skyd.anivu.ui.component.AniVuIconButton import com.skyd.anivu.ui.component.dialog.AniVuDialog import com.skyd.anivu.ui.component.dialog.DeleteWarningDialog import com.skyd.anivu.ui.component.dialog.TextFieldDialog -import com.skyd.anivu.ui.screen.article.FeedIcon import com.skyd.anivu.ui.component.showToast import com.skyd.anivu.ui.local.LocalNavController +import com.skyd.anivu.ui.screen.article.FeedIcon import com.skyd.anivu.ui.screen.feed.requestheaders.openRequestHeadersScreen import com.skyd.anivu.util.launchImagePicker import com.skyd.anivu.util.rememberImagePicker @@ -83,7 +83,7 @@ import com.skyd.anivu.util.rememberImagePicker @Composable fun EditFeedSheet( onDismissRequest: () -> Unit, - feed: FeedBean, + feedView: FeedViewBean, groups: List, onReadAll: (String) -> Unit, onRefresh: (String) -> Unit, @@ -98,6 +98,7 @@ fun EditFeedSheet( openCreateGroupDialog: () -> Unit, ) { val navController = LocalNavController.current + val feed = feedView.feed var openUrlDialog by rememberSaveable { mutableStateOf(false) } var url by rememberSaveable(feed.url) { mutableStateOf(feed.url) } var openNicknameDialog by rememberSaveable { mutableStateOf(false) } @@ -116,7 +117,7 @@ fun EditFeedSheet( .padding(horizontal = 20.dp) ) { InfoArea( - feed = feed, + feedView = feedView, onCustomIconChange = onCustomIconChange, onNicknameChanged = { openNicknameDialog = true }, onCustomDescriptionChanged = { openCustomDescriptionDialog = true }, @@ -234,11 +235,12 @@ fun EditFeedSheet( @Composable private fun InfoArea( - feed: FeedBean, + feedView: FeedViewBean, onCustomIconChange: (Uri?) -> Unit, onNicknameChanged: () -> Unit, onCustomDescriptionChanged: () -> Unit ) { + val feed = feedView.feed Row { val pickStickerLauncher = rememberImagePicker(multiple = false) { result -> result.firstOrNull()?.let { uri -> onCustomIconChange(uri) } @@ -271,7 +273,7 @@ private fun InfoArea( .fillMaxWidth() .clip(RoundedCornerShape(6.dp)) .clickable(onClick = onNicknameChanged), - text = feed.nickname ?: feed.title.orEmpty(), + text = feedView.feed.nickname ?: feed.title.orEmpty(), style = MaterialTheme.typography.titleLarge, maxLines = 2, overflow = TextOverflow.Ellipsis, diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedEvent.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedEvent.kt index 8ad43075..55d6c37c 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedEvent.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedEvent.kt @@ -2,6 +2,7 @@ package com.skyd.anivu.ui.screen.feed import com.skyd.anivu.base.mvi.MviSingleEvent import com.skyd.anivu.model.bean.feed.FeedBean +import com.skyd.anivu.model.bean.feed.FeedViewBean import com.skyd.anivu.model.bean.group.GroupVo sealed interface FeedEvent : MviSingleEvent { @@ -10,12 +11,12 @@ sealed interface FeedEvent : MviSingleEvent { } sealed interface AddFeedResultEvent : FeedEvent { - data class Success(val feed: FeedBean) : AddFeedResultEvent + data class Success(val feed: FeedViewBean) : AddFeedResultEvent data class Failed(val msg: String) : AddFeedResultEvent } sealed interface EditFeedResultEvent : FeedEvent { - data class Success(val feed: FeedBean) : EditFeedResultEvent + data class Success(val feed: FeedViewBean) : EditFeedResultEvent data class Failed(val msg: String) : EditFeedResultEvent } @@ -25,12 +26,12 @@ sealed interface FeedEvent : MviSingleEvent { } sealed interface ClearFeedArticlesResultEvent : FeedEvent { - data object Success : ClearFeedArticlesResultEvent + data class Success(val feed: FeedViewBean) : ClearFeedArticlesResultEvent data class Failed(val msg: String) : ClearFeedArticlesResultEvent } sealed interface RefreshFeedResultEvent : FeedEvent { - data object Success : RefreshFeedResultEvent + data class Success(val feeds: List) : RefreshFeedResultEvent data class Failed(val msg: String) : RefreshFeedResultEvent } @@ -60,7 +61,7 @@ sealed interface FeedEvent : MviSingleEvent { } sealed interface ReadAllResultEvent : FeedEvent { - data class Success(val count: Int) : ReadAllResultEvent + data class Success(val feeds: List) : ReadAllResultEvent data class Failed(val msg: String) : ReadAllResultEvent } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedPartialStateChange.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedPartialStateChange.kt index f80380f9..90548b27 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedPartialStateChange.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedPartialStateChange.kt @@ -1,6 +1,6 @@ package com.skyd.anivu.ui.screen.feed -import com.skyd.anivu.model.bean.feed.FeedBean +import com.skyd.anivu.model.bean.feed.FeedViewBean import com.skyd.anivu.model.bean.group.GroupVo @@ -26,7 +26,7 @@ internal sealed interface FeedPartialStateChange { } } - data class Success(val feed: FeedBean) : AddFeed + data class Success(val feed: FeedViewBean) : AddFeed data class Failed(val msg: String) : AddFeed } @@ -43,7 +43,7 @@ internal sealed interface FeedPartialStateChange { } } - data class Success(val feed: FeedBean) : EditFeed + data class Success(val feed: FeedViewBean) : EditFeed data class Failed(val msg: String) : EditFeed } @@ -60,7 +60,7 @@ internal sealed interface FeedPartialStateChange { } } - data object Success : ClearFeedArticles + data class Success(val feed: FeedViewBean) : ClearFeedArticles data class Failed(val msg: String) : ClearFeedArticles } @@ -94,7 +94,7 @@ internal sealed interface FeedPartialStateChange { } } - data class Success(val count: Int) : ReadAll + data class Success(val feeds: List) : ReadAll data class Failed(val msg: String) : ReadAll } @@ -111,7 +111,7 @@ internal sealed interface FeedPartialStateChange { } } - data object Success : RefreshFeed + data class Success(val feeds: List) : RefreshFeed data class Failed(val msg: String) : RefreshFeed } diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedScreen.kt index f12c285f..8f163155 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedScreen.kt @@ -68,7 +68,6 @@ import com.skyd.anivu.base.mvi.MviEventListener import com.skyd.anivu.base.mvi.getDispatcher import com.skyd.anivu.ext.isCompact import com.skyd.anivu.ext.plus -import com.skyd.anivu.model.bean.feed.FeedBean import com.skyd.anivu.model.bean.feed.FeedBean.Companion.isDefaultGroup import com.skyd.anivu.model.bean.feed.FeedViewBean import com.skyd.anivu.model.bean.group.GroupVo @@ -179,7 +178,7 @@ private fun FeedList( var openMoreMenu by rememberSaveable { mutableStateOf(false) } var openAddDialog by rememberSaveable { mutableStateOf(false) } var addDialogUrl by rememberSaveable { mutableStateOf("") } - var openEditFeedDialog by rememberSaveable { mutableStateOf(null) } + var openEditFeedDialog by rememberSaveable { mutableStateOf(null) } var openEditGroupDialog by rememberSaveable { mutableStateOf(value = null) } var openCreateGroupDialog by rememberSaveable { mutableStateOf(false) } @@ -306,21 +305,29 @@ private fun FeedList( if (openEditGroupDialog != null) openEditGroupDialog = event.group } - is FeedEvent.AddFeedResultEvent.Success -> openEditFeedDialog = event.feed + is FeedEvent.ClearFeedArticlesResultEvent.Success -> { + if (openEditFeedDialog != null) openEditFeedDialog = event.feed + } - is FeedEvent.ReadAllResultEvent.Success -> snackbarHostState.showSnackbar( - context.resources.getQuantityString( - R.plurals.feed_screen_read_all_result, - event.count, - event.count, - ), - ) + is FeedEvent.ReadAllResultEvent.Success -> if (openEditFeedDialog != null) { + val newFeed = event.feeds.firstOrNull { + it.feed.url == openEditFeedDialog?.feed?.url + } + if (newFeed != null) openEditFeedDialog = newFeed + } + + is FeedEvent.RefreshFeedResultEvent.Success -> if (openEditFeedDialog != null) { + val newFeed = event.feeds.firstOrNull { + it.feed.url == openEditFeedDialog?.feed?.url + } + if (newFeed != null) openEditFeedDialog = newFeed + } + + is FeedEvent.AddFeedResultEvent.Success -> openEditFeedDialog = event.feed FeedEvent.RemoveFeedResultEvent.Success, - is FeedEvent.RefreshFeedResultEvent.Success, FeedEvent.CreateGroupResultEvent.Success, FeedEvent.MoveFeedsToGroupResultEvent.Success, - FeedEvent.ClearFeedArticlesResultEvent.Success, FeedEvent.ClearGroupArticlesResultEvent.Success, FeedEvent.DeleteGroupResultEvent.Success -> Unit } @@ -351,19 +358,24 @@ private fun FeedList( } EditFeedSheet( onDismissRequest = { openEditFeedDialog = null }, - feed = openEditFeedDialog!!, + feedView = openEditFeedDialog!!, groups = groups, onReadAll = { dispatch(FeedIntent.ReadAllInFeed(it)) }, onRefresh = { dispatch(FeedIntent.RefreshFeed(it)) }, onClear = { dispatch(FeedIntent.ClearFeedArticles(it)) }, onDelete = { dispatch(FeedIntent.RemoveFeed(it)) }, onUrlChange = { - dispatch(FeedIntent.EditFeedUrl(oldUrl = openEditFeedDialog!!.url, newUrl = it)) + dispatch( + FeedIntent.EditFeedUrl( + oldUrl = openEditFeedDialog!!.feed.url, + newUrl = it + ) + ) }, onNicknameChange = { dispatch( FeedIntent.EditFeedNickname( - url = openEditFeedDialog!!.url, + url = openEditFeedDialog!!.feed.url, nickname = it ) ) @@ -371,28 +383,28 @@ private fun FeedList( onCustomDescriptionChange = { dispatch( FeedIntent.EditFeedCustomDescription( - url = openEditFeedDialog!!.url, customDescription = it, + url = openEditFeedDialog!!.feed.url, customDescription = it, ) ) }, onCustomIconChange = { dispatch( FeedIntent.EditFeedCustomIcon( - url = openEditFeedDialog!!.url, customIcon = it, + url = openEditFeedDialog!!.feed.url, customIcon = it, ) ) }, onSortXmlArticlesOnUpdateChanged = { dispatch( FeedIntent.EditFeedSortXmlArticlesOnUpdate( - url = openEditFeedDialog!!.url, sort = it, + url = openEditFeedDialog!!.feed.url, sort = it, ) ) }, onGroupChange = { dispatch( FeedIntent.EditFeedGroup( - url = openEditFeedDialog!!.url, + url = openEditFeedDialog!!.feed.url, groupId = it.groupId ) ) @@ -550,7 +562,7 @@ private fun FeedList( selectedFeedUrls: List? = null, onShowArticleList: (List) -> Unit, onExpandChanged: (GroupVo, Boolean) -> Unit, - onEditFeed: (FeedBean) -> Unit, + onEditFeed: (FeedViewBean) -> Unit, onEditGroup: (GroupVo) -> Unit, ) { val hideEmptyDefault = LocalHideEmptyDefault.current diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedViewModel.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedViewModel.kt index 320c6d04..adc34c78 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedViewModel.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/FeedViewModel.kt @@ -1,6 +1,5 @@ package com.skyd.anivu.ui.screen.feed -import android.util.Log import com.skyd.anivu.base.mvi.AbstractMviViewModel import com.skyd.anivu.ext.catchMap import com.skyd.anivu.ext.startWith @@ -58,7 +57,7 @@ class FeedViewModel @Inject constructor( FeedEvent.EditFeedResultEvent.Failed(change.msg) is FeedPartialStateChange.ClearFeedArticles.Success -> - FeedEvent.ClearFeedArticlesResultEvent.Success + FeedEvent.ClearFeedArticlesResultEvent.Success(change.feed) is FeedPartialStateChange.ClearFeedArticles.Failed -> FeedEvent.ClearFeedArticlesResultEvent.Failed(change.msg) @@ -70,7 +69,7 @@ class FeedViewModel @Inject constructor( FeedEvent.RemoveFeedResultEvent.Failed(change.msg) is FeedPartialStateChange.RefreshFeed.Success -> - FeedEvent.RefreshFeedResultEvent.Success + FeedEvent.RefreshFeedResultEvent.Success(change.feeds) is FeedPartialStateChange.RefreshFeed.Failed -> FeedEvent.RefreshFeedResultEvent.Failed(change.msg) @@ -109,7 +108,7 @@ class FeedViewModel @Inject constructor( FeedEvent.EditGroupResultEvent.Failed(change.msg) is FeedPartialStateChange.ReadAll.Success -> - FeedEvent.ReadAllResultEvent.Success(change.count) + FeedEvent.ReadAllResultEvent.Success(change.feeds) is FeedPartialStateChange.ReadAll.Failed -> FeedEvent.ReadAllResultEvent.Failed(change.msg) @@ -164,8 +163,10 @@ class FeedViewModel @Inject constructor( .catchMap { FeedPartialStateChange.EditFeed.Failed(it.message.toString()) } }, filterIsInstance().flatMapConcat { intent -> - feedRepo.clearFeedArticles(intent.url).map { - FeedPartialStateChange.ClearFeedArticles.Success + feedRepo.clearFeedArticles(intent.url).flatMapConcat { + feedRepo.getFeedViewsByUrls(listOf(intent.url)) + }.map { + FeedPartialStateChange.ClearFeedArticles.Success(it.first()) }.startWith(FeedPartialStateChange.LoadingDialog.Show) .catchMap { FeedPartialStateChange.ClearFeedArticles.Failed(it.message.toString()) } }, @@ -176,22 +177,35 @@ class FeedViewModel @Inject constructor( }.startWith(FeedPartialStateChange.LoadingDialog.Show) }, merge( - filterIsInstance() - .map { intent -> feedRepo.readAllInGroup(intent.groupId) }, - filterIsInstance() - .map { intent -> feedRepo.readAllInFeed(intent.feedUrl) }, + filterIsInstance().map { intent -> + feedRepo.readAllInGroup(intent.groupId).flatMapConcat { + feedRepo.getFeedViewsByGroupId(intent.groupId) + } + }, + filterIsInstance().map { intent -> + feedRepo.readAllInFeed(intent.feedUrl).flatMapConcat { + feedRepo.getFeedViewsByUrls(listOf(intent.feedUrl)) + } + }, ).flatMapConcat { flow -> flow.map { FeedPartialStateChange.ReadAll.Success(it) } .startWith(FeedPartialStateChange.LoadingDialog.Show) .catchMap { FeedPartialStateChange.ReadAll.Failed(it.message.toString()) } }, merge( - filterIsInstance() - .map { intent -> articleRepo.refreshArticleList(listOf(intent.url)) }, - filterIsInstance() - .map { intent -> articleRepo.refreshGroupArticles(intent.groupId) }, + filterIsInstance().map { intent -> + val urls = listOf(intent.url) + articleRepo.refreshArticleList(urls).flatMapConcat { + feedRepo.getFeedViewsByUrls(urls) + } + }, + filterIsInstance().map { intent -> + articleRepo.refreshGroupArticles(intent.groupId).flatMapConcat { + feedRepo.getFeedViewsByGroupId(intent.groupId) + } + }, ).flatMapConcat { flow -> - flow.map { FeedPartialStateChange.RefreshFeed.Success } + flow.map { FeedPartialStateChange.RefreshFeed.Success(it) } .startWith(FeedPartialStateChange.LoadingDialog.Show) .catchMap { FeedPartialStateChange.RefreshFeed.Failed(it.message.toString()) } }, diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/feed/item/Feed1Item.kt b/app/src/main/java/com/skyd/anivu/ui/screen/feed/item/Feed1Item.kt index 423d1af7..368247ac 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/feed/item/Feed1Item.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/feed/item/Feed1Item.kt @@ -9,11 +9,11 @@ import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Badge import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -27,6 +27,8 @@ import androidx.compose.ui.unit.dp import com.skyd.anivu.ext.readable import com.skyd.anivu.model.bean.feed.FeedBean import com.skyd.anivu.model.bean.feed.FeedViewBean +import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference +import com.skyd.anivu.ui.local.LocalFeedNumberBadge import com.skyd.anivu.ui.local.LocalNavController import com.skyd.anivu.ui.screen.article.FeedIcon import com.skyd.anivu.ui.screen.article.openArticleScreen @@ -39,7 +41,7 @@ fun Feed1Item( inGroup: Boolean = false, isEnd: Boolean = false, onClick: ((FeedBean) -> Unit)? = null, - onEdit: ((FeedBean) -> Unit)? = null, + onEdit: ((FeedViewBean) -> Unit)? = null, ) { val navController = LocalNavController.current val feed = data.feed @@ -66,7 +68,7 @@ fun Feed1Item( ) .combinedClickable( onLongClick = if (onEdit != null) { - { onEdit(feed) } + { onEdit(data) } } else null, onClick = { if (onClick == null) { @@ -94,20 +96,7 @@ fun Feed1Item( maxLines = 2, overflow = TextOverflow.Ellipsis, ) - val feedCount = data.articleCount - if (feedCount > 0) { - Spacer(modifier = Modifier.width(8.dp)) - Badge( - containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, - contentColor = MaterialTheme.colorScheme.outline, - content = { - Text( - text = feedCount.toString(), - style = MaterialTheme.typography.labelSmall - ) - }, - ) - } + FeedNumberBadge(data) } val description = rememberSaveable(feed.customDescription, feed.description) { if (feed.customDescription == null) { @@ -128,4 +117,53 @@ fun Feed1Item( } } } +} + +@Composable +private fun FeedNumberBadge(feedView: FeedViewBean) { + if (feedView.articleCount > 0) { + val startEndPadding = 3.dp + val midPadding = 2.dp + val feedNumberBadge = LocalFeedNumberBadge.current + var allCountContent: (@Composable RowScope.() -> Unit)? = null + var unreadCountContent: (@Composable RowScope.() -> Unit)? = null + val showUnread = feedNumberBadge and FeedNumberBadgePreference.UNREAD != 0 + && feedView.unreadArticleCount > 0 + val showAll = feedNumberBadge and FeedNumberBadgePreference.ALL != 0 + if (showUnread) { + unreadCountContent = { + Text( + modifier = Modifier + .background(MaterialTheme.colorScheme.error) + .padding( + start = startEndPadding, + end = if (showAll) midPadding else startEndPadding, + ), + text = feedView.unreadArticleCount.toString(), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onError, + ) + } + } + if (showAll) { + allCountContent = { + Text( + modifier = Modifier + .background(MaterialTheme.colorScheme.surfaceContainerHighest) + .padding( + start = if (showUnread) midPadding else startEndPadding, + end = startEndPadding, + ), + text = feedView.articleCount.toString(), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.outline, + ) + } + } + Spacer(modifier = Modifier.width(8.dp)) + Row(modifier = Modifier.clip(RoundedCornerShape(20.dp))) { + unreadCountContent?.invoke(this) + allCountContent?.invoke(this) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/anivu/ui/screen/settings/appearance/feed/FeedStyleScreen.kt b/app/src/main/java/com/skyd/anivu/ui/screen/settings/appearance/feed/FeedStyleScreen.kt index ce5bc536..82acd9ef 100644 --- a/app/src/main/java/com/skyd/anivu/ui/screen/settings/appearance/feed/FeedStyleScreen.kt +++ b/app/src/main/java/com/skyd/anivu/ui/screen/settings/appearance/feed/FeedStyleScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Pin import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.Tonality import androidx.compose.material3.Icon @@ -18,6 +19,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -29,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.skyd.anivu.R import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference +import com.skyd.anivu.model.preference.appearance.feed.FeedNumberBadgePreference import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference import com.skyd.anivu.model.preference.appearance.feed.TonalElevationPreferenceUtil import com.skyd.anivu.ui.component.AniVuIconButton @@ -36,8 +39,10 @@ import com.skyd.anivu.ui.component.AniVuTopBar import com.skyd.anivu.ui.component.AniVuTopBarStyle import com.skyd.anivu.ui.component.BaseSettingsItem import com.skyd.anivu.ui.component.CategorySettingsItem +import com.skyd.anivu.ui.component.CheckableListMenu import com.skyd.anivu.ui.component.dialog.SliderDialog import com.skyd.anivu.ui.local.LocalFeedListTonalElevation +import com.skyd.anivu.ui.local.LocalFeedNumberBadge import com.skyd.anivu.ui.local.LocalFeedTopBarTonalElevation @@ -60,6 +65,7 @@ fun FeedStyleScreen() { ) { paddingValues -> var openTopBarTonalElevationDialog by rememberSaveable { mutableStateOf(false) } var openGroupListTonalElevationDialog by rememberSaveable { mutableStateOf(false) } + var expandFeedNumberBadgeMenu by rememberSaveable { mutableStateOf(false) } LazyColumn( modifier = Modifier @@ -93,6 +99,22 @@ fun FeedStyleScreen() { onClick = { openGroupListTonalElevationDialog = true } ) } + item { + BaseSettingsItem( + icon = rememberVectorPainter(Icons.Outlined.Pin), + text = stringResource(id = R.string.feed_style_screen_number_badge), + descriptionText = FeedNumberBadgePreference.toDisplayName( + context, LocalFeedNumberBadge.current, + ), + extraContent = { + FeedNumberBadgeMenu( + expanded = expandFeedNumberBadgeMenu, + onDismissRequest = { expandFeedNumberBadgeMenu = false }, + ) + }, + onClick = { expandFeedNumberBadgeMenu = true } + ) + } } if (openTopBarTonalElevationDialog) { @@ -167,4 +189,20 @@ internal fun TonalElevationDialog( } } ) +} + +@Composable +private fun FeedNumberBadgeMenu(expanded: Boolean, onDismissRequest: () -> Unit) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val feedNumberBadge = LocalFeedNumberBadge.current + + CheckableListMenu( + expanded = expanded, + current = feedNumberBadge, + values = remember { FeedNumberBadgePreference.values.toList() }, + displayName = { FeedNumberBadgePreference.toDisplayName(context, it) }, + onChecked = { FeedNumberBadgePreference.put(context, scope, it) }, + onDismissRequest = onDismissRequest, + ) } \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 4df2c0f6..c6e604bb 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -330,10 +330,6 @@ Oynatma geçmişini silmek istediğinizden emin misiniz? %d öğe silindi Klasör - - %d öğeyi okundu - %d öğe okundu - %d öğe içe aktarıldı, %.2f saniye sürdü %d öğe içe aktarıldı, %.2f saniye sürdü diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1af01572..3993ae59 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -361,9 +361,6 @@ 播放器 播放器通知栏控制 后台播放 - - 已读 %d 项 - 导入了 %d 项,花费 %.2f 秒 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 6dc3654c..8c28f563 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -278,9 +278,6 @@ 切換已讀狀態 切換收藏狀態 - - 已讀 %d 項 - 匯入 %d 項, 耗時 %.2f 秒 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 825621ac..05ef0bee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -370,10 +370,10 @@ Background playback Expand Collapse - - Read %d item - Read %d items - + Unread + All + Number badge + Imported %d item, takes %.2f seconds Imported %d items, takes %.2f seconds