Improve pull feeds
This commit is contained in:
parent
eda76f4445
commit
518dd6b59c
|
@ -72,7 +72,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
fun pullFeeds(): Flow<MutableList<GroupWithFeed>> {
|
fun pullFeeds(): Flow<MutableList<GroupWithFeed>> {
|
||||||
return groupDao.queryAllGroupWithFeed(
|
return groupDao.queryAllGroupWithFeed(
|
||||||
context.dataStore.get(DataStoreKeys.CurrentAccountId) ?: 0
|
context.dataStore.get(DataStoreKeys.CurrentAccountId) ?: 0
|
||||||
)
|
)//.flowOn(Dispatchers.IO)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pullArticles(
|
fun pullArticles(
|
||||||
|
@ -81,6 +81,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
isStarred: Boolean = false,
|
isStarred: Boolean = false,
|
||||||
isUnread: Boolean = false,
|
isUnread: Boolean = false,
|
||||||
): PagingSource<Int, ArticleWithFeed> {
|
): PagingSource<Int, ArticleWithFeed> {
|
||||||
|
Log.i("RLog", "thread:pullArticles ${Thread.currentThread().name}")
|
||||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId) ?: 0
|
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId) ?: 0
|
||||||
Log.i(
|
Log.i(
|
||||||
"RLog",
|
"RLog",
|
||||||
|
@ -116,6 +117,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
isUnread: Boolean = false,
|
isUnread: Boolean = false,
|
||||||
): Flow<List<ImportantCount>> {
|
): Flow<List<ImportantCount>> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
|
Log.i("RLog", "thread:pullImportant ${Thread.currentThread().name}")
|
||||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!!
|
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)!!
|
||||||
Log.i(
|
Log.i(
|
||||||
"RLog",
|
"RLog",
|
||||||
|
@ -128,7 +130,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
.queryImportantCountWhenIsUnread(accountId, isUnread)
|
||||||
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
else -> articleDao.queryImportantCountWhenIsAll(accountId)
|
||||||
}
|
}
|
||||||
}
|
}//.flowOn(Dispatchers.IO)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findArticleById(id: String): ArticleWithFeed? {
|
suspend fun findArticleById(id: String): ArticleWithFeed? {
|
||||||
|
@ -176,7 +178,6 @@ class SyncWorker @AssistedInject constructor(
|
||||||
15, TimeUnit.MINUTES
|
15, TimeUnit.MINUTES
|
||||||
).setConstraints(
|
).setConstraints(
|
||||||
Constraints.Builder()
|
Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
||||||
.build()
|
.build()
|
||||||
).addTag(WORK_NAME).build()
|
).addTag(WORK_NAME).build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,8 @@ class LocalRssRepository @Inject constructor(
|
||||||
|
|
||||||
override suspend fun sync() {
|
override suspend fun sync() {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
val preTime = System.currentTimeMillis()
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
val preTime = System.currentTimeMillis()
|
||||||
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
|
val accountId = context.dataStore.get(DataStoreKeys.CurrentAccountId)
|
||||||
?: return@withContext
|
?: return@withContext
|
||||||
val feeds = async { feedDao.queryAll(accountId) }
|
val feeds = async { feedDao.queryAll(accountId) }
|
||||||
|
@ -98,6 +98,7 @@ class LocalRssRepository @Inject constructor(
|
||||||
feedCount = feed.size,
|
feedCount = feed.size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Log.i("RLog", "thread:sync ${Thread.currentThread().name}")
|
||||||
}.map { feed ->
|
}.map { feed ->
|
||||||
async {
|
async {
|
||||||
val articles = syncFeed(accountId, feed)
|
val articles = syncFeed(accountId, feed)
|
||||||
|
@ -132,36 +133,37 @@ class LocalRssRepository @Inject constructor(
|
||||||
private suspend fun syncFeed(
|
private suspend fun syncFeed(
|
||||||
accountId: Int,
|
accountId: Int,
|
||||||
feed: Feed
|
feed: Feed
|
||||||
): MutableList<Article> {
|
): List<Article> {
|
||||||
val articles = mutableListOf<Article>()
|
|
||||||
val latest = articleDao.queryLatestByFeedId(accountId, feed.id)
|
val latest = articleDao.queryLatestByFeedId(accountId, feed.id)
|
||||||
articles.addAll(
|
val articles = rssHelper.queryRssXml(
|
||||||
rssHelper.queryRssXml(
|
|
||||||
rssNetworkDataSource,
|
rssNetworkDataSource,
|
||||||
accountId,
|
accountId,
|
||||||
feed,
|
feed,
|
||||||
latest?.title,
|
latest?.link,
|
||||||
).also {
|
).also {
|
||||||
if (feed.icon == null && it.isNotEmpty()) {
|
if (feed.icon == null && it.isNotEmpty()) {
|
||||||
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
|
rssHelper.queryRssIcon(feedDao, feed, it.first().link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
Log.i("RLog", "thread:syncFeed ${Thread.currentThread().name}")
|
||||||
updateSyncState {
|
updateSyncState {
|
||||||
it.copy(
|
it.copy(
|
||||||
syncedCount = it.syncedCount + 1,
|
syncedCount = it.syncedCount + 1,
|
||||||
currentFeedName = feed.name
|
currentFeedName = feed.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (articles.isNotEmpty()) {
|
||||||
articleDao.insertList(articles)
|
articleDao.insertList(articles)
|
||||||
if (feed.isNotification) {
|
if (feed.isNotification) {
|
||||||
notify(articles)
|
notify(articles)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return articles
|
return articles
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notify(
|
private fun notify(
|
||||||
articles: MutableList<Article>,
|
articles: List<Article>,
|
||||||
) {
|
) {
|
||||||
articles.forEach { article ->
|
articles.forEach { article ->
|
||||||
val builder = NotificationCompat.Builder(
|
val builder = NotificationCompat.Builder(
|
||||||
|
|
|
@ -92,13 +92,13 @@ class RssHelper @Inject constructor(
|
||||||
rssNetworkDataSource: RssNetworkDataSource,
|
rssNetworkDataSource: RssNetworkDataSource,
|
||||||
accountId: Int,
|
accountId: Int,
|
||||||
feed: Feed,
|
feed: Feed,
|
||||||
latestTitle: String? = null,
|
latestLink: String? = null,
|
||||||
): List<Article> {
|
): List<Article> {
|
||||||
val a = mutableListOf<Article>()
|
val a = mutableListOf<Article>()
|
||||||
try {
|
try {
|
||||||
val parseRss = rssNetworkDataSource.parseRss(feed.url)
|
val parseRss = rssNetworkDataSource.parseRss(feed.url)
|
||||||
parseRss.items.forEach {
|
parseRss.items.forEach {
|
||||||
if (latestTitle != null && latestTitle == it.title) return a
|
if (latestLink != null && latestLink == it.link) return a
|
||||||
Log.i("RLog", "request rss ${feed.name}: ${it.title}")
|
Log.i("RLog", "request rss ${feed.name}: ${it.title}")
|
||||||
a.add(
|
a.add(
|
||||||
Article(
|
Article(
|
||||||
|
|
50
app/src/main/java/me/ash/reader/ui/page/home/FilterBar2.kt
Normal file
50
app/src/main/java/me/ash/reader/ui/page/home/FilterBar2.kt
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package me.ash.reader.ui.page.home
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import me.ash.reader.data.constant.Filter
|
||||||
|
import me.ash.reader.ui.extension.getName
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun FilterBar2(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
filter: Filter,
|
||||||
|
onSelected: (Filter) -> Unit = {},
|
||||||
|
) {
|
||||||
|
NavigationBar(
|
||||||
|
tonalElevation = 0.dp,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.width(60.dp))
|
||||||
|
listOf(
|
||||||
|
Filter.Starred,
|
||||||
|
Filter.Unread,
|
||||||
|
Filter.All,
|
||||||
|
).forEach { item ->
|
||||||
|
NavigationBarItem(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (filter == item) item.filledIcon else item.icon,
|
||||||
|
contentDescription = item.getName()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// label = { Text(text = item.getName()) },
|
||||||
|
selected = filter == item,
|
||||||
|
onClick = { onSelected(item) },
|
||||||
|
colors = NavigationBarItemDefaults.colors(
|
||||||
|
selectedIconColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
|
unselectedIconColor = MaterialTheme.colorScheme.outline,
|
||||||
|
selectedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
indicatorColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(60.dp))
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,7 +113,12 @@ fun HomeBottomNavBar(
|
||||||
.alpha(1 - readerBarAlpha),
|
.alpha(1 - readerBarAlpha),
|
||||||
) {
|
) {
|
||||||
Log.i("RLog", "AppNavigationBar: ${readerBarAlpha}, ${1f - readerBarAlpha}")
|
Log.i("RLog", "AppNavigationBar: ${readerBarAlpha}, ${1f - readerBarAlpha}")
|
||||||
FilterBar(
|
// FilterBar(
|
||||||
|
// modifier = modifier,
|
||||||
|
// filter = filter,
|
||||||
|
// onSelected = filterOnClick,
|
||||||
|
// )
|
||||||
|
FilterBar2(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
filter = filter,
|
filter = filter,
|
||||||
onSelected = filterOnClick,
|
onSelected = filterOnClick,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.data.account.Account
|
import me.ash.reader.data.account.Account
|
||||||
|
@ -67,6 +68,7 @@ class FeedsViewModel @Inject constructor(
|
||||||
rssRepository.get().pullFeeds(),
|
rssRepository.get().pullFeeds(),
|
||||||
rssRepository.get().pullImportant(isStarred, isUnread),
|
rssRepository.get().pullImportant(isStarred, isUnread),
|
||||||
) { groupWithFeedList, importantList ->
|
) { groupWithFeedList, importantList ->
|
||||||
|
Log.i("RLog", "thread:combine ${Thread.currentThread().name}")
|
||||||
val groupImportantMap = mutableMapOf<String, Int>()
|
val groupImportantMap = mutableMapOf<String, Int>()
|
||||||
val feedImportantMap = mutableMapOf<String, Int>()
|
val feedImportantMap = mutableMapOf<String, Int>()
|
||||||
importantList.groupBy { it.groupId }.forEach { (i, list) ->
|
importantList.groupBy { it.groupId }.forEach { (i, list) ->
|
||||||
|
@ -101,6 +103,8 @@ class FeedsViewModel @Inject constructor(
|
||||||
}.onStart {
|
}.onStart {
|
||||||
|
|
||||||
}.onEach { groupWithFeedList ->
|
}.onEach { groupWithFeedList ->
|
||||||
|
Log.i("RLog", "thread:onEach ${Thread.currentThread().name}")
|
||||||
|
|
||||||
_viewState.update {
|
_viewState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
filter = when {
|
filter = when {
|
||||||
|
@ -116,7 +120,7 @@ class FeedsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}.catch {
|
}.catch {
|
||||||
Log.e("RLog", "catch in articleRepository.pullFeeds(): $this")
|
Log.e("RLog", "catch in articleRepository.pullFeeds(): $this")
|
||||||
}.collect()
|
}.flowOn(Dispatchers.Default).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scrollToItem(index: Int) {
|
private fun scrollToItem(index: Int) {
|
||||||
|
|
|
@ -19,12 +19,11 @@ import me.ash.reader.ui.page.home.read.ReadViewModel
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
fun LazyListScope.generateArticleList(
|
fun LazyListScope.generateArticleList(
|
||||||
context: Context,
|
context: Context,
|
||||||
pagingItems: LazyPagingItems<ArticleWithFeed>?,
|
pagingItems: LazyPagingItems<ArticleWithFeed>,
|
||||||
readViewModel: ReadViewModel,
|
readViewModel: ReadViewModel,
|
||||||
homeViewModel: HomeViewModel,
|
homeViewModel: HomeViewModel,
|
||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
) {
|
) {
|
||||||
pagingItems ?: return
|
|
||||||
var lastItemDay: String? = null
|
var lastItemDay: String? = null
|
||||||
for (itemIndex in 0 until pagingItems.itemCount) {
|
for (itemIndex in 0 until pagingItems.itemCount) {
|
||||||
val currentItem = pagingItems.peek(itemIndex) ?: continue
|
val currentItem = pagingItems.peek(itemIndex) ?: continue
|
||||||
|
|
|
@ -22,7 +22,6 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.ui.extension.collectAsStateValue
|
import me.ash.reader.ui.extension.collectAsStateValue
|
||||||
import me.ash.reader.ui.extension.getName
|
import me.ash.reader.ui.extension.getName
|
||||||
|
@ -46,7 +45,7 @@ fun FlowPage(
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val viewState = viewModel.viewState.collectAsStateValue()
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||||
val pagingItems = viewState.pagingData?.collectAsLazyPagingItems()
|
val pagingItems = viewState.pagingData.collectAsLazyPagingItems()
|
||||||
|
|
||||||
LaunchedEffect(homeViewModel.filterState) {
|
LaunchedEffect(homeViewModel.filterState) {
|
||||||
homeViewModel.filterState.collect { state ->
|
homeViewModel.filterState.collect { state ->
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package me.ash.reader.ui.page.home.flow
|
package me.ash.reader.ui.page.home.flow
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
@ -8,6 +9,7 @@ import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.ash.reader.data.article.ArticleWithFeed
|
import me.ash.reader.data.article.ArticleWithFeed
|
||||||
|
@ -53,13 +55,14 @@ class FlowViewModel @Inject constructor(
|
||||||
_viewState.update {
|
_viewState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
pagingData = Pager(PagingConfig(pageSize = 10)) {
|
pagingData = Pager(PagingConfig(pageSize = 10)) {
|
||||||
|
Log.i("RLog", "thread:Pager ${Thread.currentThread().name}")
|
||||||
rssRepository.get().pullArticles(
|
rssRepository.get().pullArticles(
|
||||||
groupId = filterState.group?.id,
|
groupId = filterState.group?.id,
|
||||||
feedId = filterState.feed?.id,
|
feedId = filterState.feed?.id,
|
||||||
isStarred = filterState.filter.isStarred(),
|
isStarred = filterState.filter.isStarred(),
|
||||||
isUnread = filterState.filter.isUnread(),
|
isUnread = filterState.filter.isUnread(),
|
||||||
)
|
)
|
||||||
}.flow.cachedIn(viewModelScope)
|
}.flow.flowOn(Dispatchers.IO).cachedIn(viewModelScope)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +84,7 @@ data class ArticleViewState(
|
||||||
val filterImportant: Int = 0,
|
val filterImportant: Int = 0,
|
||||||
val listState: LazyListState = LazyListState(),
|
val listState: LazyListState = LazyListState(),
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = false,
|
||||||
val pagingData: Flow<PagingData<ArticleWithFeed>>? = null,
|
val pagingData: Flow<PagingData<ArticleWithFeed>> = emptyFlow(),
|
||||||
val syncWorkInfo: String = "",
|
val syncWorkInfo: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user