restore-feed-order (#223)
* fix: possible duplicate saved search restored if query or filters is null * refactor: change backup structure so it allows keeping feeds' order
This commit is contained in:
parent
d5844286b9
commit
c43015439d
8 changed files with 78 additions and 65 deletions
|
|
@ -14,6 +14,6 @@ class FeedBackupCreator(
|
|||
* Backup global Popular/Latest feeds
|
||||
*/
|
||||
suspend fun backupFeeds(): List<BackupFeed> {
|
||||
return handler.awaitList { feed_saved_searchQueries.selectAllGlobalNonSavedSearch(backupFeedMapper) }
|
||||
return handler.awaitList { feed_saved_searchQueries.selectAllFeedWithSavedSearch(backupFeedMapper) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<BackupSavedSearch> {
|
||||
// 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) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<BackupFeed> = emptyList(),
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
val backupSavedSearchMapper =
|
||||
{ _: Long, source: Long, name: String, query: String?, filtersJson: String?, backupFeeds: List<BackupFeed> ->
|
||||
{ _: Long, source: Long, name: String, query: String?, filtersJson: String? ->
|
||||
BackupSavedSearch(
|
||||
source = source,
|
||||
name = name,
|
||||
query = query.orEmpty(),
|
||||
filterList = filtersJson ?: "[]",
|
||||
// KMK -->
|
||||
backupFeeds = backupFeeds,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,10 +146,10 @@ class BackupRestorer(
|
|||
// KMK <--
|
||||
) = launch {
|
||||
ensureActive()
|
||||
savedSearchRestorer.restoreSavedSearches(backupSavedSearches)
|
||||
// KMK -->
|
||||
feedRestorer.restoreFeeds(backupFeeds)
|
||||
// KMK <--
|
||||
savedSearchRestorer.restoreSavedSearches(backupSavedSearches)
|
||||
|
||||
restoreProgress += 1
|
||||
notifier.showRestoreProgress(
|
||||
|
|
|
|||
|
|
@ -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<BackupFeed>, savedSearchId: Long? = null) {
|
||||
suspend fun restoreFeeds(backupFeeds: List<BackupFeed>) {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<BackupSavedSearch>) {
|
||||
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 == "[]" },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue