Skip to content

Commit

Permalink
[feature] Support mark all articles of a Group, Feed as read (#50); s…
Browse files Browse the repository at this point in the history
…upport refresh Feed via a button on top bar (#56)
  • Loading branch information
SkyD666 committed Jun 6, 2024
1 parent 7e0a2c2 commit e4028b9
Show file tree
Hide file tree
Showing 22 changed files with 334 additions and 123 deletions.
4 changes: 4 additions & 0 deletions app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.skyd.anivu.model.preference.appearance.ThemePreference
import com.skyd.anivu.model.preference.appearance.article.ArticleItemTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ArticleListTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ArticleTopBarTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ShowArticlePullRefreshPreference
import com.skyd.anivu.model.preference.appearance.article.ShowArticleTopBarRefreshPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference
Expand Down Expand Up @@ -46,6 +48,8 @@ fun Preferences.toSettings(): Settings {
articleItemTonalElevation = ArticleItemTonalElevationPreference.fromPreferences(this),
searchListTonalElevation = SearchListTonalElevationPreference.fromPreferences(this),
searchTopBarTonalElevation = SearchTopBarTonalElevationPreference.fromPreferences(this),
showArticleTopBarRefresh = ShowArticleTopBarRefreshPreference.fromPreferences(this),
showArticlePullRefresh = ShowArticlePullRefreshPreference.fromPreferences(this),

// Update
ignoreUpdateVersion = IgnoreUpdateVersionPreference.fromPreferences(this),
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/skyd/anivu/ext/SnackbarExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ fun SnackbarHostState.showSnackbar(
@Composable
fun SnackbarHostState.showSnackbarWithLaunchedEffect(
message: String,
key1: Any? = this,
key1: Any? = null,
key2: Any? = null,
key3: Any? = null,
key3: Any? = this,
actionLabel: String? = null,
withDismissAction: Boolean = true,
duration: SnackbarDuration = if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/skyd/anivu/model/db/dao/ArticleDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,26 @@ interface ArticleDao {
"""
)
fun readArticle(articleId: String, read: Boolean)

@Transaction
@Query(
"""
UPDATE $ARTICLE_TABLE_NAME SET ${ArticleBean.IS_READ_COLUMN} = 1
WHERE ${ArticleBean.IS_READ_COLUMN} = 0 AND ${ArticleBean.FEED_URL_COLUMN} = :feedUrl
"""
)
fun readAllInFeed(feedUrl: String): Int

@Transaction
@Query(
"""
UPDATE $ARTICLE_TABLE_NAME SET ${ArticleBean.IS_READ_COLUMN} = 1
WHERE ${ArticleBean.IS_READ_COLUMN} = 0 AND ${ArticleBean.FEED_URL_COLUMN} IN (
SELECT DISTINCT ${FeedBean.URL_COLUMN} FROM $FEED_TABLE_NAME
WHERE ${FeedBean.GROUP_ID_COLUMN} = :groupId OR
:groupId IS NULL AND ${FeedBean.GROUP_ID_COLUMN} IS NULL
)
"""
)
fun readAllInGroup(groupId: String?): Int
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/skyd/anivu/model/preference/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import com.skyd.anivu.model.preference.appearance.ThemePreference
import com.skyd.anivu.model.preference.appearance.article.ArticleItemTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ArticleListTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ArticleTopBarTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.article.ShowArticlePullRefreshPreference
import com.skyd.anivu.model.preference.appearance.article.ShowArticleTopBarRefreshPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedGroupExpandPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedListTonalElevationPreference
import com.skyd.anivu.model.preference.appearance.feed.FeedTopBarTonalElevationPreference
Expand Down Expand Up @@ -58,6 +60,8 @@ import com.skyd.anivu.ui.local.LocalPlayerShow85sButton
import com.skyd.anivu.ui.local.LocalPlayerShowScreenshotButton
import com.skyd.anivu.ui.local.LocalSearchListTonalElevation
import com.skyd.anivu.ui.local.LocalSearchTopBarTonalElevation
import com.skyd.anivu.ui.local.LocalShowArticlePullRefresh
import com.skyd.anivu.ui.local.LocalShowArticleTopBarRefresh
import com.skyd.anivu.ui.local.LocalTextFieldStyle
import com.skyd.anivu.ui.local.LocalTheme
import com.skyd.anivu.ui.local.LocalUseAutoDelete
Expand All @@ -79,6 +83,8 @@ data class Settings(
val articleItemTonalElevation: Float = ArticleItemTonalElevationPreference.default,
val searchListTonalElevation: Float = SearchListTonalElevationPreference.default,
val searchTopBarTonalElevation: Float = SearchTopBarTonalElevationPreference.default,
val showArticleTopBarRefresh: Boolean = ShowArticleTopBarRefreshPreference.default,
val showArticlePullRefresh: Boolean = ShowArticlePullRefreshPreference.default,
// Update
val ignoreUpdateVersion: Long = IgnoreUpdateVersionPreference.default,
// Behavior
Expand Down Expand Up @@ -122,6 +128,8 @@ fun SettingsProvider(
LocalArticleItemTonalElevation provides settings.articleItemTonalElevation,
LocalSearchListTonalElevation provides settings.searchListTonalElevation,
LocalSearchTopBarTonalElevation provides settings.searchTopBarTonalElevation,
LocalShowArticleTopBarRefresh provides settings.showArticleTopBarRefresh,
LocalShowArticlePullRefresh provides settings.showArticlePullRefresh,
// Update
LocalIgnoreUpdateVersion provides settings.ignoreUpdateVersion,
// Behavior
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.skyd.anivu.model.preference.appearance.article

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import com.skyd.anivu.base.BasePreference
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.put
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

object ShowArticlePullRefreshPreference : BasePreference<Boolean> {
private const val SHOW_ARTICLE_PULL_REFRESH = "showArticlePullRefresh"
override val default = true

val key = booleanPreferencesKey(SHOW_ARTICLE_PULL_REFRESH)

fun put(context: Context, scope: CoroutineScope, value: Boolean) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(key, value)
}
}

override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.skyd.anivu.model.preference.appearance.article

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import com.skyd.anivu.base.BasePreference
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.put
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

object ShowArticleTopBarRefreshPreference : BasePreference<Boolean> {
private const val SHOW_ARTICLE_TOP_BAR_REFRESH = "showArticleTopBarRefresh"
override val default = false

val key = booleanPreferencesKey(SHOW_ARTICLE_TOP_BAR_REFRESH)

fun put(context: Context, scope: CoroutineScope, value: Boolean) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(key, value)
}
}

override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.skyd.anivu.ext.isLocal
import com.skyd.anivu.ext.isNetwork
import com.skyd.anivu.model.bean.FeedBean
import com.skyd.anivu.model.bean.GroupBean
import com.skyd.anivu.model.db.dao.ArticleDao
import com.skyd.anivu.model.db.dao.FeedDao
import com.skyd.anivu.model.db.dao.GroupDao
import kotlinx.coroutines.Dispatchers
Expand All @@ -23,8 +24,9 @@ import java.util.UUID
import javax.inject.Inject

class FeedRepository @Inject constructor(
private val feedDao: FeedDao,
private val groupDao: GroupDao,
private val feedDao: FeedDao,
private val articleDao: ArticleDao,
private val rssHelper: RssHelper,
) : BaseRepository() {
suspend fun requestGroupAnyList(): Flow<List<Any>> {
Expand Down Expand Up @@ -182,6 +184,19 @@ class FeedRepository @Inject constructor(
}
}.flowOn(Dispatchers.IO)
}

suspend fun readAllInGroup(groupId: String?): Flow<Int> {
return flow {
val realGroupId = if (groupId == GroupBean.DEFAULT_GROUP_ID) null else groupId
emit(articleDao.readAllInGroup(realGroupId))
}.flowOn(Dispatchers.IO)
}

suspend fun readAllInFeed(feedUrl: String): Flow<Int> {
return flow {
emit(articleDao.readAllInFeed(feedUrl))
}.flowOn(Dispatchers.IO)
}
}

fun tryDeleteFeedIconFile(path: String?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -19,6 +24,7 @@ import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowUpward
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
Expand Down Expand Up @@ -59,7 +65,7 @@ import com.skyd.anivu.base.BaseComposeFragment
import com.skyd.anivu.base.mvi.getDispatcher
import com.skyd.anivu.ext.plus
import com.skyd.anivu.ext.popBackStackWithLifecycle
import com.skyd.anivu.ext.showSnackbar
import com.skyd.anivu.ext.showSnackbarWithLaunchedEffect
import com.skyd.anivu.model.bean.ArticleWithFeed
import com.skyd.anivu.ui.component.AniVuFloatingActionButton
import com.skyd.anivu.ui.component.AniVuIconButton
Expand All @@ -75,6 +81,8 @@ import com.skyd.anivu.ui.fragment.search.SearchFragment
import com.skyd.anivu.ui.local.LocalArticleListTonalElevation
import com.skyd.anivu.ui.local.LocalArticleTopBarTonalElevation
import com.skyd.anivu.ui.local.LocalNavController
import com.skyd.anivu.ui.local.LocalShowArticlePullRefresh
import com.skyd.anivu.ui.local.LocalShowArticleTopBarRefresh
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -157,6 +165,27 @@ private fun ArticleContentScreen(
),
),
actions = {
if (LocalShowArticleTopBarRefresh.current) {
val angle = if (uiState.articleListState.loading) {
val infiniteTransition =
rememberInfiniteTransition(label = "topBarRefreshTransition")
infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing)
),
label = "topBarRefreshAnimate",
).value
} else 0f
AniVuIconButton(
onClick = { dispatch(ArticleIntent.Refresh(feedUrls)) },
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(id = R.string.refresh),
rotate = angle,
enabled = !uiState.articleListState.loading,
)
}
AniVuIconButton(
onClick = {
navController.navigate(
Expand Down Expand Up @@ -230,16 +259,16 @@ private fun ArticleContentScreen(

when (val event = uiEvent) {
is ArticleEvent.InitArticleListResultEvent.Failed ->
snackbarHostState.showSnackbar(message = event.msg, scope = scope)
snackbarHostState.showSnackbarWithLaunchedEffect(message = event.msg, key1 = event)

is ArticleEvent.RefreshArticleListResultEvent.Failed ->
snackbarHostState.showSnackbar(message = event.msg, scope = scope)
snackbarHostState.showSnackbarWithLaunchedEffect(message = event.msg, key1 = event)

is ArticleEvent.FavoriteArticleResultEvent.Failed ->
snackbarHostState.showSnackbar(message = event.msg, scope = scope)
snackbarHostState.showSnackbarWithLaunchedEffect(message = event.msg, key1 = event)

is ArticleEvent.ReadArticleResultEvent.Failed ->
snackbarHostState.showSnackbar(message = event.msg, scope = scope)
snackbarHostState.showSnackbarWithLaunchedEffect(message = event.msg, key1 = event)

null -> Unit
}
Expand All @@ -264,7 +293,7 @@ private fun Content(
)
Box(
modifier = Modifier
.pullRefresh(state)
.pullRefresh(state = state, enabled = LocalShowArticlePullRefresh.current)
.padding(top = contentPadding.calculateTopPadding()),
) {
Column {
Expand All @@ -291,11 +320,13 @@ private fun Content(
)
}

PullRefreshIndicator(
refreshing = uiState.articleListState.loading,
state = state,
modifier = Modifier.align(Alignment.TopCenter),
)
if (LocalShowArticlePullRefresh.current) {
PullRefreshIndicator(
refreshing = uiState.articleListState.loading,
state = state,
modifier = Modifier.align(Alignment.TopCenter),
)
}
}
}

Expand Down
12 changes: 3 additions & 9 deletions app/src/main/java/com/skyd/anivu/ui/fragment/article/Filter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package com.skyd.anivu.ui.fragment.article
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -65,15 +63,12 @@ internal fun FilterRow(
) {
AnimatedVisibility(
visible = favoriteFilterValue != null || readFilterValue != null,
enter = fadeIn() + expandHorizontally(),
exit = fadeOut() + shrinkHorizontally(),
enter = expandHorizontally(),
exit = shrinkHorizontally(),
) {
FilterSetting(
filterCount = {
listOf(
favoriteFilterValue,
readFilterValue
).map { if (it != null) 1 else 0 }.sum()
listOfNotNull(favoriteFilterValue, readFilterValue).size
},
onClearAllFilters = {
onFilterFavorite(null)
Expand Down Expand Up @@ -252,5 +247,4 @@ private fun FavoriteReadFilter(
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Image
import androidx.compose.material.icons.outlined.Link
Expand Down Expand Up @@ -78,6 +79,7 @@ fun EditFeedSheet(
onDismissRequest: () -> Unit,
feed: FeedBean,
groups: List<GroupBean>,
onReadAll: (String) -> Unit,
onRefresh: (String) -> Unit,
onDelete: (String) -> Unit,
onUrlChange: (String) -> Unit,
Expand Down Expand Up @@ -118,6 +120,7 @@ fun EditFeedSheet(

// Options
OptionArea(
onReadAll = { onReadAll(feed.url) },
onRefresh = { onRefresh(feed.url) },
onDelete = {
onDelete(feed.url)
Expand Down Expand Up @@ -342,6 +345,7 @@ private fun LinkArea(link: String, onLinkClick: () -> Unit) {
internal fun OptionArea(
deleteEnabled: Boolean = true,
deleteWarningText: String = stringResource(id = R.string.feed_screen_delete_feed_warning),
onReadAll: () -> Unit,
onRefresh: () -> Unit,
onDelete: () -> Unit,
) {
Expand All @@ -358,6 +362,11 @@ internal fun OptionArea(
verticalArrangement = Arrangement.spacedBy(8.dp),
overflow = FlowRowOverflow.Visible
) {
SheetChip(
icon = Icons.Outlined.DoneAll,
text = stringResource(id = R.string.read_all),
onClick = onReadAll,
)
SheetChip(
icon = Icons.Outlined.Refresh,
text = stringResource(id = R.string.refresh),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fun EditGroupSheet(
onDismissRequest: () -> Unit,
group: GroupBean,
groups: List<GroupBean>,
onReadAll: (String) -> Unit,
onRefresh: (String) -> Unit,
onDelete: (String) -> Unit,
onNameChange: (String) -> Unit,
Expand Down Expand Up @@ -67,6 +68,7 @@ fun EditGroupSheet(
id = R.string.feed_screen_delete_group_warning,
group.name,
),
onReadAll = { onReadAll(group.groupId) },
onRefresh = { onRefresh(group.groupId) },
onDelete = {
onDelete(group.groupId)
Expand Down
Loading

0 comments on commit e4028b9

Please sign in to comment.