From 816dc0cbbc6e264ea3fd138d5214b8d1a3d9a2a3 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Wed, 1 Mar 2023 20:00:19 +0100 Subject: [PATCH] make sure all timeline database operations run on a background thread (#3391) --- .../tusky/appstore/CacheUpdater.kt | 51 +++++++++++-------- .../com/keylesspalace/tusky/db/TimelineDao.kt | 30 +++++------ 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt index 66ae898b..df551e10 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt @@ -3,45 +3,52 @@ package com.keylesspalace.tusky.appstore import com.google.gson.Gson import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase -import io.reactivex.rxjava3.disposables.Disposable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import kotlinx.coroutines.rx3.asFlow import javax.inject.Inject class CacheUpdater @Inject constructor( eventHub: EventHub, - private val accountManager: AccountManager, + accountManager: AccountManager, appDatabase: AppDatabase, gson: Gson ) { - private val disposable: Disposable + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) init { val timelineDao = appDatabase.timelineDao() - disposable = eventHub.events.subscribe { event -> - val accountId = accountManager.activeAccount?.id ?: return@subscribe - when (event) { - is FavoriteEvent -> - timelineDao.setFavourited(accountId, event.statusId, event.favourite) - is ReblogEvent -> - timelineDao.setReblogged(accountId, event.statusId, event.reblog) - is BookmarkEvent -> - timelineDao.setBookmarked(accountId, event.statusId, event.bookmark) - is UnfollowEvent -> - timelineDao.removeAllByUser(accountId, event.accountId) - is StatusDeletedEvent -> - timelineDao.delete(accountId, event.statusId) - is PollVoteEvent -> { - val pollString = gson.toJson(event.poll) - timelineDao.setVoted(accountId, event.statusId, pollString) + scope.launch { + eventHub.events.asFlow().collect { event -> + val accountId = accountManager.activeAccount?.id ?: return@collect + when (event) { + is FavoriteEvent -> + timelineDao.setFavourited(accountId, event.statusId, event.favourite) + is ReblogEvent -> + timelineDao.setReblogged(accountId, event.statusId, event.reblog) + is BookmarkEvent -> + timelineDao.setBookmarked(accountId, event.statusId, event.bookmark) + is UnfollowEvent -> + timelineDao.removeAllByUser(accountId, event.accountId) + is StatusDeletedEvent -> + timelineDao.delete(accountId, event.statusId) + is PollVoteEvent -> { + val pollString = gson.toJson(event.poll) + timelineDao.setVoted(accountId, event.statusId, pollString) + } + is PinEvent -> + timelineDao.setPinned(accountId, event.statusId, event.pinned) } - is PinEvent -> - timelineDao.setPinned(accountId, event.statusId, event.pinned) } } } fun stop() { - this.disposable.dispose() + this.scope.cancel() } } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index d8423600..18f2f4d4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -36,7 +36,7 @@ SELECT s.serverId, s.url, s.timelineUserId, s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.editedAt, s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive, s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId, -s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, +s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId', a.localUsername as 'a_localUsername', a.username as 'a_username', a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar', @@ -59,7 +59,7 @@ SELECT s.serverId, s.url, s.timelineUserId, s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.editedAt, s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive, s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId, -s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, +s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId', a.localUsername as 'a_localUsername', a.username as 'a_username', a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar', @@ -71,7 +71,7 @@ rb.emojis as 'rb_emojis', rb.bot as 'rb_bot' FROM TimelineStatusEntity s LEFT JOIN TimelineAccountEntity a ON (s.timelineUserId = a.timelineUserId AND s.authorServerId = a.serverId) LEFT JOIN TimelineAccountEntity rb ON (s.timelineUserId = rb.timelineUserId AND s.reblogAccountId = rb.serverId) -WHERE (s.serverId = :statusId OR s.reblogServerId = :statusId) +WHERE (s.serverId = :statusId OR s.reblogServerId = :statusId) AND s.authorServerId IS NOT NULL""" ) abstract suspend fun getStatus(statusId: String): TimelineStatusWithAccount? @@ -89,25 +89,25 @@ AND """UPDATE TimelineStatusEntity SET favourited = :favourited WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setFavourited(accountId: Long, statusId: String, favourited: Boolean) + abstract suspend fun setFavourited(accountId: Long, statusId: String, favourited: Boolean) @Query( """UPDATE TimelineStatusEntity SET bookmarked = :bookmarked WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setBookmarked(accountId: Long, statusId: String, bookmarked: Boolean) + abstract suspend fun setBookmarked(accountId: Long, statusId: String, bookmarked: Boolean) @Query( """UPDATE TimelineStatusEntity SET reblogged = :reblogged WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setReblogged(accountId: Long, statusId: String, reblogged: Boolean) + abstract suspend fun setReblogged(accountId: Long, statusId: String, reblogged: Boolean) @Query( """DELETE FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND (authorServerId = :userId OR reblogAccountId = :userId)""" ) - abstract fun removeAllByUser(accountId: Long, userId: String) + abstract suspend fun removeAllByUser(accountId: Long, userId: String) /** * Removes everything in the TimelineStatusEntity and TimelineAccountEntity tables for one user account @@ -128,7 +128,7 @@ WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = """DELETE FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND serverId = :statusId""" ) - abstract fun delete(accountId: Long, statusId: String) + abstract suspend fun delete(accountId: Long, statusId: String) /** * Cleans the TimelineStatusEntity and TimelineAccountEntity tables from old entries. @@ -158,8 +158,8 @@ AND serverId = :statusId""" */ @Query( """DELETE FROM TimelineAccountEntity WHERE timelineUserId = :accountId AND serverId NOT IN - (SELECT authorServerId FROM TimelineStatusEntity WHERE timelineUserId = :accountId) - AND serverId NOT IN + (SELECT authorServerId FROM TimelineStatusEntity WHERE timelineUserId = :accountId) + AND serverId NOT IN (SELECT reblogAccountId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND reblogAccountId IS NOT NULL)""" ) abstract suspend fun cleanupAccounts(accountId: Long) @@ -168,31 +168,31 @@ AND serverId = :statusId""" """UPDATE TimelineStatusEntity SET poll = :poll WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setVoted(accountId: Long, statusId: String, poll: String) + abstract suspend fun setVoted(accountId: Long, statusId: String, poll: String) @Query( """UPDATE TimelineStatusEntity SET expanded = :expanded WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setExpanded(accountId: Long, statusId: String, expanded: Boolean) + abstract suspend fun setExpanded(accountId: Long, statusId: String, expanded: Boolean) @Query( """UPDATE TimelineStatusEntity SET contentShowing = :contentShowing WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setContentShowing(accountId: Long, statusId: String, contentShowing: Boolean) + abstract suspend fun setContentShowing(accountId: Long, statusId: String, contentShowing: Boolean) @Query( """UPDATE TimelineStatusEntity SET contentCollapsed = :contentCollapsed WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setContentCollapsed(accountId: Long, statusId: String, contentCollapsed: Boolean) + abstract suspend fun setContentCollapsed(accountId: Long, statusId: String, contentCollapsed: Boolean) @Query( """UPDATE TimelineStatusEntity SET pinned = :pinned WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract fun setPinned(accountId: Long, statusId: String, pinned: Boolean) + abstract suspend fun setPinned(accountId: Long, statusId: String, pinned: Boolean) @Query( """DELETE FROM TimelineStatusEntity