From 7c63f84f5fe515ef54dc0c7ab80c80807f79527e Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Wed, 3 Jul 2024 13:01:51 +0700 Subject: [PATCH] feat: Add support for backing up and storing source's feeds along with saved searches --- .../creators/SavedSearchBackupCreator.kt | 20 +++++++++- .../data/backup/models/BackupFeed.kt | 3 ++ .../data/backup/models/BackupSavedSearch.kt | 8 +++- .../backup/restore/restorers/FeedRestorer.kt | 16 +++----- .../restore/restorers/SavedSearchRestorer.kt | 38 +++++++++++++------ .../tachiyomi/data/feed_saved_search.sq | 5 ++- .../domain/source/model/FeedSavedSearch.kt | 2 +- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SavedSearchBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SavedSearchBackupCreator.kt index 22941e799..748e52fe7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SavedSearchBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SavedSearchBackupCreator.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.backup.create.creators import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch +import eu.kanade.tachiyomi.data.backup.models.backupFeedMapper import eu.kanade.tachiyomi.data.backup.models.backupSavedSearchMapper import tachiyomi.data.DatabaseHandler import uy.kohesive.injekt.Injekt @@ -11,6 +12,23 @@ class SavedSearchBackupCreator( ) { suspend fun backupSavedSearches(): List { - return handler.awaitList { saved_searchQueries.selectAll(backupSavedSearchMapper) } + // KMK --> + // return handler.awaitList { saved_searchQueries.selectAll(backupSavedSearchMapper) } + val savedSearches = handler.awaitList { saved_searchQueries.selectAll() } + val feedSavedSearches = + handler.awaitList { feed_saved_searchQueries.selectAllFeedHasSavedSearch() } + + return savedSearches.map { savedSearch -> + val feeds = feedSavedSearches.filter { it.saved_search == savedSearch._id } + backupSavedSearchMapper( + savedSearch._id, + savedSearch.source, + savedSearch.name, + savedSearch.query, + savedSearch.filters_json, + feeds.map { it.backupFeedMapper() }, + ) + } + // KMK <-- } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupFeed.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupFeed.kt index 478797692..f534dd83c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupFeed.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupFeed.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.models import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber +import tachiyomi.data.SelectAllFeedHasSavedSearch @Serializable data class BackupFeed( @@ -16,3 +17,5 @@ val backupFeedMapper = global = global, ) } + +fun SelectAllFeedHasSavedSearch.backupFeedMapper() = backupFeedMapper(_id, source, saved_search, global) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupSavedSearch.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupSavedSearch.kt index 4486b6bfd..581426cf9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupSavedSearch.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupSavedSearch.kt @@ -12,14 +12,20 @@ data class BackupSavedSearch( @ProtoNumber(2) val query: String = "", @ProtoNumber(3) val filterList: String = "", @ProtoNumber(4) val source: Long = 0, + // KMK --> + @ProtoNumber(9) var backupFeeds: List = emptyList(), + // KMK <-- ) val backupSavedSearchMapper = - { _: Long, source: Long, name: String, query: String?, filtersJson: String? -> + { _: Long, source: Long, name: String, query: String?, filtersJson: String?, backupFeeds: List -> BackupSavedSearch( source = source, name = name, query = query.orEmpty(), filterList = filtersJson ?: "[]", + // KMK --> + backupFeeds = backupFeeds, + // KMK <-- ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt index 5378c4648..b49c7f146 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt @@ -8,18 +8,14 @@ import uy.kohesive.injekt.api.get class FeedRestorer( private val handler: DatabaseHandler = Injekt.get(), ) { - - /** - * Restore global Popular/Latest feeds - */ - suspend fun restoreFeeds(backupFeeds: List) { + suspend fun restoreFeeds(backupFeeds: List, savedSearchId: Long? = null) { if (backupFeeds.isEmpty()) return - val currentFeeds = handler.awaitList { - feed_saved_searchQueries.selectAllGlobalNonSavedSearch() - } + handler.await(true) { + val currentFeeds = handler.awaitList { + feed_saved_searchQueries.selectAllGlobalNonSavedSearch() + } - handler.await { backupFeeds.filter { backupFeed -> // Filter if source's global Popular/Latest feed already existed currentFeeds.none { @@ -28,7 +24,7 @@ class FeedRestorer( }.forEach { feed_saved_searchQueries.insert( sourceId = it.source, - savedSearch = null, + savedSearch = savedSearchId, global = it.global, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt index 66430abfe..12cda7227 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt @@ -8,25 +8,39 @@ import uy.kohesive.injekt.api.get class SavedSearchRestorer( private val handler: DatabaseHandler = Injekt.get(), + // KMK --> + private val feedRestorer: FeedRestorer = FeedRestorer(), + // KMK <-- ) { suspend fun restoreSavedSearches(backupSavedSearches: List) { if (backupSavedSearches.isEmpty()) return - val currentSavedSearches = handler.awaitList { - saved_searchQueries.selectNamesAndSources() - } + // KMK --> + handler.await(true) { + // KMK <-- + val currentSavedSearches = handler.awaitList { + saved_searchQueries.selectNamesAndSources() + } - handler.await { backupSavedSearches.filter { backupSavedSearch -> currentSavedSearches.none { it.source == backupSavedSearch.source && it.name == backupSavedSearch.name } - }.forEach { - saved_searchQueries.insert( - source = it.source, - name = it.name, - query = it.query.nullIfBlank(), - filtersJson = it.filterList.nullIfBlank() - ?.takeUnless { it == "[]" }, - ) + }.forEach { backupSavedSearch -> + // KMK --> + val savedSearchId = handler.awaitOneExecutable(true) { + // KMK <-- + saved_searchQueries.insert( + source = backupSavedSearch.source, + name = backupSavedSearch.name, + query = backupSavedSearch.query.nullIfBlank(), + filtersJson = backupSavedSearch.filterList.nullIfBlank() + ?.takeUnless { it == "[]" }, + ) + // KMK --> + saved_searchQueries.selectLastInsertedRowId() + } + + feedRestorer.restoreFeeds(backupSavedSearch.backupFeeds, savedSearchId) + // KMK <-- } } } diff --git a/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq b/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq index ce42d5e99..be4d77df8 100644 --- a/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq +++ b/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq @@ -57,4 +57,7 @@ selectLastInsertedRowId: SELECT last_insert_rowid(); selectAllGlobalNonSavedSearch: -SELECT * FROM feed_saved_search WHERE global = 1 AND saved_search IS NULL; \ No newline at end of file +SELECT * FROM feed_saved_search WHERE global = 1 AND saved_search IS NULL; + +selectAllFeedHasSavedSearch: +SELECT * FROM feed_saved_search WHERE saved_search IS NOT NULL; \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearch.kt b/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearch.kt index 658c13ab6..aae516d51 100644 --- a/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearch.kt +++ b/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearch.kt @@ -10,6 +10,6 @@ data class FeedSavedSearch( // If null then get latest/popular, if set get the saved search val savedSearch: Long?, - // If the feed is a global or source specific feed + // If the feed is a global (FeedScreen) or source specific feed (SourceFeedScreen) val global: Boolean, )