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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user