diff --git a/app/src/main/java/me/ash/reader/domain/repository/ArticleDao.kt b/app/src/main/java/me/ash/reader/domain/repository/ArticleDao.kt index 703820271..eb7541b17 100644 --- a/app/src/main/java/me/ash/reader/domain/repository/ArticleDao.kt +++ b/app/src/main/java/me/ash/reader/domain/repository/ArticleDao.kt @@ -286,11 +286,13 @@ interface ArticleDao { before: Date, ) + @Transaction @Query( """ UPDATE article SET isUnread = :isUnread WHERE accountId = :accountId AND date < :before + AND isUnread != :isUnread """ ) suspend fun markAllAsRead( @@ -299,6 +301,7 @@ interface ArticleDao { before: Date, ) + @Transaction @Query( """ UPDATE article SET isUnread = :isUnread @@ -307,6 +310,7 @@ interface ArticleDao { WHERE groupId = :groupId ) AND accountId = :accountId + AND isUnread != :isUnread AND date < :before """ ) @@ -317,11 +321,13 @@ interface ArticleDao { before: Date, ) + @Transaction @Query( """ UPDATE article SET isUnread = :isUnread WHERE feedId = :feedId AND accountId = :accountId + AND isUnread != :isUnread AND date < :before """ ) @@ -332,6 +338,7 @@ interface ArticleDao { before: Date, ) + @Transaction @Query( """ UPDATE article SET isUnread = :isUnread @@ -632,6 +639,17 @@ interface ArticleDao { ) fun queryMetadataAll(accountId: Int): List + @Transaction + @Query( + """ + SELECT id, isUnread, isStarred FROM article + WHERE accountId = :accountId + AND isUnread = :isUnread + ORDER BY date DESC + """ + ) + fun queryMetadataAll(accountId: Int, isUnread: Boolean): List + @Transaction @Query( """ @@ -643,16 +661,29 @@ interface ArticleDao { ) fun queryMetadataAll(accountId: Int, before: Date): List + @Transaction + @Query( + """ + SELECT id, isUnread, isStarred FROM article + WHERE accountId = :accountId + AND isUnread = :isUnread + AND date < :before + ORDER BY date DESC + """ + ) + fun queryMetadataAll(accountId: Int, isUnread: Boolean, before: Date): List + @Transaction @Query( """ SELECT id, isUnread, isStarred FROM article WHERE accountId = :accountId AND feedId = :feedId + AND isUnread = :isUnread ORDER BY date DESC """ ) - fun queryMetadataByFeedId(accountId: Int, feedId: String): List + fun queryMetadataByFeedId(accountId: Int, feedId: String, isUnread: Boolean): List @Transaction @Query( @@ -660,11 +691,12 @@ interface ArticleDao { SELECT id, isUnread, isStarred FROM article WHERE accountId = :accountId AND feedId = :feedId + AND isUnread = :isUnread AND date < :before ORDER BY date DESC """ ) - fun queryMetadataByFeedId(accountId: Int, feedId: String, before: Date): List + fun queryMetadataByFeedId(accountId: Int, feedId: String, isUnread: Boolean, before: Date): List @Transaction @Query( @@ -675,10 +707,11 @@ interface ArticleDao { LEFT JOIN `group` AS c ON c.id = b.groupId WHERE c.id = :groupId AND a.accountId = :accountId + AND a.isUnread = :isUnread ORDER BY a.date DESC """ ) - fun queryMetadataByGroupId(accountId: Int, groupId: String): List + fun queryMetadataByGroupIdWhenIsUnread(accountId: Int, groupId: String, isUnread: Boolean): List @Transaction @Query( @@ -689,11 +722,12 @@ interface ArticleDao { LEFT JOIN `group` AS c ON c.id = b.groupId WHERE c.id = :groupId AND a.accountId = :accountId + AND a.isUnread = :isUnread AND a.date < :before ORDER BY a.date DESC """ ) - fun queryMetadataByGroupId(accountId: Int, groupId: String, before: Date): List + fun queryMetadataByGroupIdWhenIsUnread(accountId: Int, groupId: String, isUnread: Boolean, before: Date): List @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(vararg article: Article) diff --git a/app/src/main/java/me/ash/reader/domain/service/AbstractRssRepository.kt b/app/src/main/java/me/ash/reader/domain/service/AbstractRssRepository.kt index b76055c1f..9bb207498 100644 --- a/app/src/main/java/me/ash/reader/domain/service/AbstractRssRepository.kt +++ b/app/src/main/java/me/ash/reader/domain/service/AbstractRssRepository.kt @@ -140,7 +140,7 @@ abstract class AbstractRssRepository( ) } - feedId != null && articleId == null -> { + feedId != null -> { articleDao.markAllAsReadByFeedId( accountId = accountId, feedId = feedId, diff --git a/app/src/main/java/me/ash/reader/domain/service/GoogleReaderRssService.kt b/app/src/main/java/me/ash/reader/domain/service/GoogleReaderRssService.kt index 5df5e6853..717e5c622 100644 --- a/app/src/main/java/me/ash/reader/domain/service/GoogleReaderRssService.kt +++ b/app/src/main/java/me/ash/reader/domain/service/GoogleReaderRssService.kt @@ -184,16 +184,16 @@ class GoogleReaderRssService @Inject constructor( } /** - * This is a reference to Reeder's synchronization logic, + * This is improved from Reeder's synchronization strategy, * which syncs well across multiple devices. * * 1. Fetch tags (not supported yet) * 2. Fetch folder and subscription list * 3. Fetch all unread item id list * 4. Fetch all starred item id list - * 5. Fetch unread contents of items with differences + * 5. Fetch unread contents of items with differences (up to 10k items per sync process) * 6. Fetch starred contents of items with differences - * 7. Fetch read contents of items with differences + * 7. Fetch read contents of items with differences (up to one month old) * 8. Remove orphaned groups and feeds, after synchronizing the starred/un-starred * * The following link contains other great synchronization logic, @@ -277,16 +277,16 @@ class GoogleReaderRssService @Inject constructor( googleReaderAPI.getUnreadItemIds(continuationId = it) }.toSet() Log.i("RLog", "sync unreadIds size: ${unreadIds.size}") - val toBeUnread = unreadIds - localUnreadIds + val toBeUnread = (unreadIds - localUnreadIds).run { + if (size > 10000) take(10000).toSet() else this + } Log.i("RLog", "sync toBeUnread size: ${toBeUnread.size}") - if (toBeUnread.isNotEmpty()) { - toBeUnread.chunked(999).forEach { - articleDao.markAsReadByIdSet( - accountId = accountId, - ids = it.toSet(), - isUnread = true, - ) - } + toBeUnread.takeIf { it.isNotEmpty() }?.chunked(500)?.forEach { + articleDao.markAsReadByIdSet( + accountId = accountId, + ids = it.toSet(), + isUnread = true, + ) } // 4. Fetch all starred item id list @@ -296,17 +296,15 @@ class GoogleReaderRssService @Inject constructor( Log.i("RLog", "sync starredIds size: ${starredIds.size}") val toBeStarred = starredIds - localStarredIds Log.i("RLog", "sync toBeStarred size: ${toBeStarred.size}") - if (toBeStarred.isNotEmpty()) { - toBeStarred.chunked(999).forEach { - articleDao.markAsStarredByIdSet( - accountId = accountId, - ids = it.toSet(), - isStarred = true, - ) - } + toBeStarred.takeIf { it.isNotEmpty() }?.chunked(500)?.forEach { + articleDao.markAsStarredByIdSet( + accountId = accountId, + ids = it.toSet(), + isStarred = true, + ) } - // 5. Fetch unread contents of items with differences + // 5. Fetch unread contents of items with differences (up to 10k items per sync process) fetchItemsContents( itemIds = toBeUnread, googleReaderAPI = googleReaderAPI, @@ -328,14 +326,14 @@ class GoogleReaderRssService @Inject constructor( preDate = preDate, ) - // 7. Fetch read contents of items with differences + // 7. Fetch read contents of items with differences (up to one month old) val readIds = fetchItemIdsAndContinue { googleReaderAPI.getReadItemIds(since = lastMonthAt, continuationId = it) }.toSet() Log.i("RLog", "sync readIds size: ${readIds.size}") val localReadIds = articleDao.queryMetadataAll(accountId).filter { !it.isUnread } .map { it.id.dollarLast() }.toSet() - val toBeRead = readIds - unreadIds - localReadIds + var toBeRead = readIds - unreadIds - localReadIds Log.i("RLog", "sync toBeRead size: ${toBeRead.size}") if (toBeRead.isNotEmpty()) { fetchItemsContents( @@ -348,6 +346,16 @@ class GoogleReaderRssService @Inject constructor( preDate = preDate, ) } + // Sync the read status of articles prior to last month + toBeRead = localUnreadIds - unreadIds + Log.i("RLog", "sync toBeRead (last month) size: ${toBeRead.size}") + toBeRead.takeIf { it.isNotEmpty() }?.chunked(500)?.forEach { + articleDao.markAsReadByIdSet( + accountId = accountId, + ids = it.toSet(), + isUnread = false, + ) + } // 8. Remove orphaned groups and feeds, after synchronizing the starred/un-starred groupDao.queryAll(accountId) @@ -428,23 +436,25 @@ class GoogleReaderRssService @Inject constructor( before: Date?, isUnread: Boolean, ) { - super.markAsRead(groupId, feedId, articleId, before, isUnread) val accountId = context.currentAccountId val googleReaderAPI = getGoogleReaderAPI() val markList: List = when { groupId != null -> { if (before == null) { - articleDao.queryMetadataByGroupId(accountId, groupId) + articleDao.queryMetadataByGroupIdWhenIsUnread(accountId, groupId, !isUnread) } else { - articleDao.queryMetadataByGroupId(accountId, groupId, before) + articleDao.queryMetadataByGroupIdWhenIsUnread(accountId, + groupId, + !isUnread, + before) }.map { it.id.dollarLast() } } feedId != null -> { if (before == null) { - articleDao.queryMetadataByFeedId(accountId, feedId) + articleDao.queryMetadataByFeedId(accountId, feedId, !isUnread) } else { - articleDao.queryMetadataByFeedId(accountId, feedId, before) + articleDao.queryMetadataByFeedId(accountId, feedId, !isUnread, before) }.map { it.id.dollarLast() } } @@ -454,17 +464,21 @@ class GoogleReaderRssService @Inject constructor( else -> { if (before == null) { - articleDao.queryMetadataAll(accountId) + articleDao.queryMetadataAll(accountId, !isUnread) } else { - articleDao.queryMetadataAll(accountId, before) + articleDao.queryMetadataAll(accountId, !isUnread, before) }.map { it.id.dollarLast() } } } - if (markList.isNotEmpty()) googleReaderAPI.editTag( - itemIds = markList, - mark = if (!isUnread) GoogleReaderAPI.Stream.READ.tag else null, - unmark = if (isUnread) GoogleReaderAPI.Stream.READ.tag else null, - ) + super.markAsRead(groupId, feedId, articleId, before, isUnread) + markList.takeIf { it.isNotEmpty() }?.chunked(500)?.forEach { + Log.d("RLog", "sync markAsRead: ${it.size} num") + googleReaderAPI.editTag( + itemIds = it, + mark = if (!isUnread) GoogleReaderAPI.Stream.READ.tag else null, + unmark = if (isUnread) GoogleReaderAPI.Stream.READ.tag else null, + ) + } } override suspend fun markAsStarred(articleId: String, isStarred: Boolean) { diff --git a/app/src/main/java/me/ash/reader/infrastructure/rss/provider/greader/GoogleReaderAPI.kt b/app/src/main/java/me/ash/reader/infrastructure/rss/provider/greader/GoogleReaderAPI.kt index 6a614ae96..ac0f8754c 100644 --- a/app/src/main/java/me/ash/reader/infrastructure/rss/provider/greader/GoogleReaderAPI.kt +++ b/app/src/main/java/me/ash/reader/infrastructure/rss/provider/greader/GoogleReaderAPI.kt @@ -256,7 +256,7 @@ class GoogleReaderAPI private constructor( retryablePostRequest( query = "reader/api/0/edit-tag", form = mutableListOf>().apply { - itemIds.forEach { add(Pair("i", it.ofItemIdToStreamId())) } + itemIds.forEach { add(Pair("i", it.ofItemIdToHexId())) } mark?.let { add(Pair("a", mark)) } unmark?.let { add(Pair("r", unmark)) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt index 83ac2b61a..55e536aff 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt @@ -1,7 +1,6 @@ package me.ash.reader.ui.page.home import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData @@ -10,7 +9,13 @@ import androidx.work.WorkManager import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import me.ash.reader.domain.model.article.ArticleFlowItem import me.ash.reader.domain.model.article.mapPagingFlowItem @@ -18,8 +23,8 @@ import me.ash.reader.domain.model.feed.Feed import me.ash.reader.domain.model.general.Filter import me.ash.reader.domain.model.group.Group import me.ash.reader.domain.service.RssService -import me.ash.reader.infrastructure.android.AndroidStringsHelper import me.ash.reader.domain.service.SyncWorker +import me.ash.reader.infrastructure.android.AndroidStringsHelper import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.IODispatcher import javax.inject.Inject @@ -44,7 +49,7 @@ class HomeViewModel @Inject constructor( val syncWorkLiveData = workManager.getWorkInfosByTagLiveData(SyncWorker.WORK_NAME) fun sync() { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { rssService.get().doSync() } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt index f87ac2f92..8952c605b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.withContext import me.ash.reader.domain.model.feed.Feed import me.ash.reader.domain.model.group.Group import me.ash.reader.domain.service.RssService +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.IODispatcher import me.ash.reader.infrastructure.di.MainDispatcher import javax.inject.Inject @@ -29,6 +30,8 @@ class FeedOptionViewModel @Inject constructor( private val mainDispatcher: CoroutineDispatcher, @IODispatcher private val ioDispatcher: CoroutineDispatcher, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _feedOptionUiState = MutableStateFlow(FeedOptionUiState()) @@ -87,7 +90,7 @@ class FeedOptionViewModel @Inject constructor( fun addNewGroup() { if (_feedOptionUiState.value.newGroupContent.isNotBlank()) { - viewModelScope.launch { + applicationScope.launch { selectedGroup(rssService.get().addGroup( destFeed = _feedOptionUiState.value.feed, newGroupName = _feedOptionUiState.value.newGroupContent)) @@ -97,7 +100,7 @@ class FeedOptionViewModel @Inject constructor( } fun selectedGroup(groupId: String) { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { _feedOptionUiState.value.feed?.let { rssService.get().moveFeed( originGroupId = it.groupId, @@ -128,7 +131,7 @@ class FeedOptionViewModel @Inject constructor( fun delete(callback: () -> Unit = {}) { _feedOptionUiState.value.feed?.let { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { rssService.get().deleteFeed(it) withContext(mainDispatcher) { callback() @@ -166,7 +169,7 @@ class FeedOptionViewModel @Inject constructor( fun renameFeed() { _feedOptionUiState.value.feed?.let { - viewModelScope.launch { + applicationScope.launch { rssService.get().renameFeed(it.copy(name = _feedOptionUiState.value.newName)) _feedOptionUiState.update { it.copy(renameDialogVisible = false) } } @@ -219,7 +222,7 @@ class FeedOptionViewModel @Inject constructor( fun changeFeedUrl() { _feedOptionUiState.value.feed?.let { - viewModelScope.launch { + applicationScope.launch { rssService.get().changeFeedUrl(it.copy(url = _feedOptionUiState.value.newUrl)) _feedOptionUiState.update { it.copy(changeUrlDialogVisible = false) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt index 20bd78cd7..1ada59dfb 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import me.ash.reader.domain.model.group.Group import me.ash.reader.domain.service.RssService +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.IODispatcher import me.ash.reader.infrastructure.di.MainDispatcher import javax.inject.Inject @@ -28,6 +29,8 @@ class GroupOptionViewModel @Inject constructor( private val mainDispatcher: CoroutineDispatcher, @IODispatcher private val ioDispatcher: CoroutineDispatcher, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _groupOptionUiState = MutableStateFlow(GroupOptionUiState()) @@ -92,7 +95,7 @@ class GroupOptionViewModel @Inject constructor( fun delete(callback: () -> Unit = {}) { _groupOptionUiState.value.group?.let { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { rssService.get().deleteGroup(it) withContext(mainDispatcher) { callback() @@ -161,7 +164,7 @@ class GroupOptionViewModel @Inject constructor( fun rename() { _groupOptionUiState.value.group?.let { - viewModelScope.launch { + applicationScope.launch { rssService.get().renameGroup(it.copy(name = _groupOptionUiState.value.newName)) _groupOptionUiState.update { it.copy(renameDialogVisible = false) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt index d735f3bdc..5d27a838b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt @@ -5,14 +5,21 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.rometools.rome.feed.synd.SyndFeed import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.domain.model.group.Group import me.ash.reader.domain.service.OpmlService import me.ash.reader.domain.service.RssService import me.ash.reader.infrastructure.android.AndroidStringsHelper +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.rss.RssHelper import me.ash.reader.ui.ext.formatUrl import java.io.InputStream @@ -24,6 +31,8 @@ class SubscribeViewModel @Inject constructor( val rssService: RssService, private val rssHelper: RssHelper, private val androidStringsHelper: AndroidStringsHelper, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _subscribeUiState = MutableStateFlow(SubscribeUiState()) @@ -48,7 +57,7 @@ class SubscribeViewModel @Inject constructor( } fun importFromInputStream(inputStream: InputStream) { - viewModelScope.launch { + applicationScope.launch { try { opmlService.saveToDatabase(inputStream) rssService.get().doSync() @@ -64,7 +73,7 @@ class SubscribeViewModel @Inject constructor( fun addNewGroup() { if (_subscribeUiState.value.newGroupContent.isNotBlank()) { - viewModelScope.launch { + applicationScope.launch { // TODO: How to add a single group without no feeds via Google Reader API? selectedGroup(rssService.get().addGroup(null, _subscribeUiState.value.newGroupContent)) hideNewGroupDialog() @@ -141,7 +150,7 @@ class SubscribeViewModel @Inject constructor( } fun subscribe() { - viewModelScope.launch { + applicationScope.launch { rssService.get().subscribe( searchedFeed = _subscribeUiState.value.searchedFeed ?: return@launch, feedLink = _subscribeUiState.value.linkContent, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt index 3459f78bf..ac6309a71 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt @@ -2,15 +2,16 @@ package me.ash.reader.ui.page.home.flow import androidx.compose.foundation.lazy.LazyListState import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import me.ash.reader.domain.model.general.MarkAsReadConditions import me.ash.reader.domain.service.RssService +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.IODispatcher import javax.inject.Inject @@ -19,13 +20,15 @@ class FlowViewModel @Inject constructor( private val rssService: RssService, @IODispatcher private val ioDispatcher: CoroutineDispatcher, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _flowUiState = MutableStateFlow(FlowUiState()) val flowUiState: StateFlow = _flowUiState.asStateFlow() fun sync() { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { rssService.get().doSync() } } @@ -36,7 +39,7 @@ class FlowViewModel @Inject constructor( articleId: String?, conditions: MarkAsReadConditions, ) { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { rssService.get().markAsRead( groupId = groupId, feedId = feedId, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt index 90669f9b1..3f06d6760 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import androidx.paging.ItemSnapshotList import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,9 +18,10 @@ import me.ash.reader.domain.model.article.ArticleFlowItem import me.ash.reader.domain.model.article.ArticleWithFeed import me.ash.reader.domain.model.feed.Feed import me.ash.reader.domain.service.RssService +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.IODispatcher import me.ash.reader.infrastructure.rss.RssHelper -import java.util.* +import java.util.Date import javax.inject.Inject @HiltViewModel @@ -28,6 +30,8 @@ class ReadingViewModel @Inject constructor( private val rssHelper: RssHelper, @IODispatcher private val ioDispatcher: CoroutineDispatcher, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _readingUiState = MutableStateFlow(ReadingUiState()) @@ -110,7 +114,7 @@ class ReadingViewModel @Inject constructor( fun updateReadStatus(isUnread: Boolean) { currentArticle?.run { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { _readingUiState.update { it.copy(isUnread = isUnread) } rssService.get().markAsRead( groupId = null, @@ -128,7 +132,7 @@ class ReadingViewModel @Inject constructor( fun markAsUnread() = updateReadStatus(isUnread = true) fun updateStarredStatus(isStarred: Boolean) { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { _readingUiState.update { it.copy(isStarred = isStarred) } currentArticle?.let { rssService.get().markAsStarred( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/accounts/AccountViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/settings/accounts/AccountViewModel.kt index 3f7e18eb4..6296a1df4 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/accounts/AccountViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/accounts/AccountViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -18,6 +19,7 @@ import me.ash.reader.domain.model.account.Account import me.ash.reader.domain.service.AccountService import me.ash.reader.domain.service.OpmlService import me.ash.reader.domain.service.RssService +import me.ash.reader.infrastructure.di.ApplicationScope import me.ash.reader.infrastructure.di.DefaultDispatcher import me.ash.reader.infrastructure.di.IODispatcher import me.ash.reader.infrastructure.di.MainDispatcher @@ -34,6 +36,8 @@ class AccountViewModel @Inject constructor( private val defaultDispatcher: CoroutineDispatcher, @MainDispatcher private val mainDispatcher: CoroutineDispatcher, + @ApplicationScope + private val applicationScope: CoroutineScope, ) : ViewModel() { private val _accountUiState = MutableStateFlow(AccountUiState()) @@ -48,7 +52,7 @@ class AccountViewModel @Inject constructor( } fun update(accountId: Int, block: Account.() -> Unit) { - viewModelScope.launch(ioDispatcher) { + applicationScope.launch(ioDispatcher) { accountService.update(accountId, block) rssService.get(accountId).clearAuthorization() } @@ -101,7 +105,7 @@ class AccountViewModel @Inject constructor( fun addAccount(account: Account, callback: (account: Account?, exception: Exception?) -> Unit) { setLoading(true) - addAccountJob = viewModelScope.launch(ioDispatcher) { + addAccountJob = applicationScope.launch(ioDispatcher) { val addAccount = accountService.addAccount(account) try { if (rssService.get(addAccount.type.id).validCredentials(account)) {