diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/FeedBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/FeedBackupCreator.kt index 77f8b6fd2..1a2372673 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/FeedBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/FeedBackupCreator.kt @@ -14,6 +14,6 @@ class FeedBackupCreator( * Backup global Popular/Latest feeds */ suspend fun backupFeeds(): List { - return handler.awaitList { feed_saved_searchQueries.selectAllGlobalNonSavedSearch(backupFeedMapper) } + return handler.awaitList { feed_saved_searchQueries.selectAllFeedWithSavedSearch(backupFeedMapper) } } } 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 748e52fe7..22941e799 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,7 +1,6 @@ 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 @@ -12,23 +11,6 @@ class SavedSearchBackupCreator( ) { suspend fun backupSavedSearches(): List { - // 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 <-- + return handler.awaitList { saved_searchQueries.selectAll(backupSavedSearchMapper) } } } 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 eba8c1f6e..735943ad4 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,20 +2,23 @@ package eu.kanade.tachiyomi.data.backup.models import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber -import tachiyomi.data.SelectAllFeedHasSavedSearch @Serializable data class BackupFeed( @ProtoNumber(1) val source: Long = 0, @ProtoNumber(2) val global: Boolean = true, + @ProtoNumber(9) val savedSearch: BackupSavedSearch? = null, ) val backupFeedMapper = - { _: Long, source: Long, _: Long?, global: Boolean, _: Long -> + { source: Long, global: Boolean, savedSearch: Long?, name: String?, query: String?, filtersJson: String? -> BackupFeed( source = source, global = global, + savedSearch = if (savedSearch != null && name != null) { + backupSavedSearchMapper(savedSearch, source, name, query, filtersJson) + } else { + null + }, ) } - -fun SelectAllFeedHasSavedSearch.backupFeedMapper() = backupFeedMapper(_id, source, saved_search, global, feed_order) 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 581426cf9..4486b6bfd 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,20 +12,14 @@ 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?, backupFeeds: List -> + { _: Long, source: Long, name: String, query: String?, filtersJson: String? -> 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/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index a3e39cc7a..ff5078185 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -146,10 +146,10 @@ class BackupRestorer( // KMK <-- ) = launch { ensureActive() + savedSearchRestorer.restoreSavedSearches(backupSavedSearches) // KMK --> feedRestorer.restoreFeeds(backupFeeds) // KMK <-- - savedSearchRestorer.restoreSavedSearches(backupSavedSearches) restoreProgress += 1 notifier.showRestoreProgress( 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 b49c7f146..cac304d9f 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 @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import eu.kanade.tachiyomi.data.backup.models.BackupFeed +import exh.util.nullIfBlank import tachiyomi.data.DatabaseHandler import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -8,24 +9,58 @@ import uy.kohesive.injekt.api.get class FeedRestorer( private val handler: DatabaseHandler = Injekt.get(), ) { - suspend fun restoreFeeds(backupFeeds: List, savedSearchId: Long? = null) { + suspend fun restoreFeeds(backupFeeds: List) { if (backupFeeds.isEmpty()) return handler.await(true) { val currentFeeds = handler.awaitList { - feed_saved_searchQueries.selectAllGlobalNonSavedSearch() + feed_saved_searchQueries.selectAllFeedWithSavedSearch() + } + val currentSavedSearches = handler.awaitList { + saved_searchQueries.selectAll() } backupFeeds.filter { backupFeed -> - // Filter if source's global Popular/Latest feed already existed - currentFeeds.none { - it.source == backupFeed.source && backupFeed.global + // Filter out source's global Popular/Latest feed already existed + backupFeed.savedSearch == null && + currentFeeds.none { currentFeed -> + currentFeed.source == backupFeed.source && backupFeed.global + } || + // Filter out feed with saveSearch already existed (both global/non-global) + backupFeed.savedSearch != null && + currentFeeds.none { currentFeed -> + currentFeed.source == backupFeed.source && + currentFeed.global == backupFeed.global && + currentFeed.name == backupFeed.savedSearch.name && + currentFeed.query.orEmpty() == backupFeed.savedSearch.query && + (currentFeed.filters_json ?: "[]") == backupFeed.savedSearch.filterList + } + }.forEach { backupFeed -> + val savedSearchId = backupFeed.savedSearch?.let { + val existedSavedSearchId = currentSavedSearches.find { currentSavedSearch -> + currentSavedSearch.source == backupFeed.source && + currentSavedSearch.name == backupFeed.savedSearch.name && + currentSavedSearch.query.orEmpty() == backupFeed.savedSearch.query && + (currentSavedSearch.filters_json ?: "[]") == backupFeed.savedSearch.filterList + }?._id + + existedSavedSearchId ?: handler.awaitOneExecutable(true) { + // Just in case, trying to create the associated saved_search + saved_searchQueries.insert( + source = backupFeed.source, + name = backupFeed.savedSearch.name, + query = backupFeed.savedSearch.query.nullIfBlank(), + filtersJson = backupFeed.savedSearch.filterList.nullIfBlank() + ?.takeUnless { it == "[]" }, + ) + saved_searchQueries.selectLastInsertedRowId() + } } - }.forEach { + feed_saved_searchQueries.insert( - sourceId = it.source, + sourceId = backupFeed.source, savedSearch = savedSearchId, - global = it.global, + global = backupFeed.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 1e9428bd9..8e4735a6f 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,9 +8,6 @@ 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 @@ -19,32 +16,29 @@ class SavedSearchRestorer( handler.await(true) { // KMK <-- val currentSavedSearches = handler.awaitList { + // KMK --> + // saved_searchQueries.selectNamesAndSources() saved_searchQueries.selectAll() + // KMK <-- } - backupSavedSearches.forEach { backupSavedSearch -> - val existedSavedSearchId = currentSavedSearches.find { - it.source == backupSavedSearch.source && - it.name == backupSavedSearch.name && - it.query == backupSavedSearch.query && - it.filters_json == backupSavedSearch.filterList - }?._id - // KMK --> - val savedSearchId = existedSavedSearchId ?: handler.awaitOneExecutable(true) { + backupSavedSearches.filter { backupSavedSearch -> + currentSavedSearches.none { currentSavedSearch -> + currentSavedSearch.source == backupSavedSearch.source && + currentSavedSearch.name == backupSavedSearch.name && + // KMK --> + currentSavedSearch.query.orEmpty() == backupSavedSearch.query && + (currentSavedSearch.filters_json ?: "[]") == backupSavedSearch.filterList // 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 <-- + }.forEach { backupSavedSearch -> + saved_searchQueries.insert( + source = backupSavedSearch.source, + name = backupSavedSearch.name, + query = backupSavedSearch.query.nullIfBlank(), + filtersJson = backupSavedSearch.filterList.nullIfBlank() + ?.takeUnless { it == "[]" }, + ) } } } 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 85b8b159e..e5b0e7958 100644 --- a/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq +++ b/data/src/main/sqldelight/tachiyomi/data/feed_saved_search.sq @@ -58,13 +58,18 @@ ON saved_search._id = M.saved_search; selectLastInsertedRowId: SELECT last_insert_rowid(); +-- KMK --> selectAllGlobalNonSavedSearch: SELECT * FROM feed_saved_search WHERE global = 1 AND saved_search IS NULL ORDER BY feed_order; selectAllFeedHasSavedSearch: SELECT * FROM feed_saved_search WHERE saved_search IS NOT NULL ORDER BY feed_order; --- KMK --> +selectAllFeedWithSavedSearch: +SELECT coalesce(f.source, s.source) source, f.global, f.saved_search, s.name, s.query, s.filters_json + FROM feed_saved_search AS f LEFT JOIN saved_search AS s + ON f.saved_search = s._id ORDER BY f.feed_order; + setOrder: UPDATE feed_saved_search SET feed_order = :order WHERE _id = :id;