From dec45f7851fda572209228dd3de8744d223c9303 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:42:23 +0000 Subject: [PATCH] feat: Add saved filters to backup and restore This commit adds support for backing up and restoring saved filters. - Added a new `SAVED_FILTERS` section to the backup process. - Implemented the logic to read filters from SharedPreferences during backup and write them back during restore. --- .../kotatsu/backups/data/BackupRepository.kt | 34 +++++++++++++++++++ .../backups/data/model/SavedFilterBackup.kt | 34 +++++++++++++++++++ .../kotatsu/backups/domain/BackupSection.kt | 1 + 3 files changed, 69 insertions(+) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/SavedFilterBackup.kt diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt index ee2eda0c8..fd99b82e8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt @@ -26,6 +26,7 @@ import org.koitharu.kotatsu.backups.data.model.CategoryBackup import org.koitharu.kotatsu.backups.data.model.FavouriteBackup import org.koitharu.kotatsu.backups.data.model.HistoryBackup import org.koitharu.kotatsu.backups.data.model.MangaBackup +import org.koitharu.kotatsu.backups.data.model.SavedFilterBackup import org.koitharu.kotatsu.backups.data.model.ScrobblingBackup import org.koitharu.kotatsu.backups.data.model.SourceBackup import org.koitharu.kotatsu.backups.data.model.StatisticBackup @@ -34,6 +35,8 @@ import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.CompositeResult import org.koitharu.kotatsu.core.util.progress.Progress +import org.koitharu.kotatsu.explore.data.MangaSourcesRepository +import org.koitharu.kotatsu.filter.data.SavedFiltersRepository import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.reader.data.TapGridSettings import java.io.InputStream @@ -48,6 +51,8 @@ class BackupRepository @Inject constructor( private val database: MangaDatabase, private val settings: AppSettings, private val tapGridSettings: TapGridSettings, + private val mangaSourcesRepository: MangaSourcesRepository, + private val savedFiltersRepository: SavedFiltersRepository, ) { private val json = Json { @@ -123,6 +128,18 @@ class BackupRepository @Inject constructor( data = database.getStatsDao().dumpEnabled().map { StatisticBackup(it) }, serializer = serializer(), ) + + BackupSection.SAVED_FILTERS -> { + val sources = mangaSourcesRepository.getEnabledSources() + val filters = sources.flatMap { source -> + savedFiltersRepository.getAll(source) + } + output.writeJsonArray( + section = BackupSection.SAVED_FILTERS, + data = filters.asFlow().map { SavedFilterBackup(it) }, + serializer = serializer(), + ) + } } progress?.emit(commonProgress) commonProgress++ @@ -185,6 +202,15 @@ class BackupRepository @Inject constructor( getStatsDao().upsert(it.toEntity()) } + BackupSection.SAVED_FILTERS -> input.readJsonArray(serializer()) + .restoreWithoutTransaction { + savedFiltersRepository.save( + source = it.source, + name = it.name, + filter = it.filter, + ) + } + null -> CompositeResult.EMPTY // skip unknown entries } progress?.emit(commonProgress) @@ -281,4 +307,12 @@ class BackupRepository @Inject constructor( } } } + + private suspend inline fun Sequence.restoreWithoutTransaction(crossinline block: suspend (T) -> Unit): CompositeResult { + return fold(CompositeResult.EMPTY) { result, item -> + result + runCatchingCancellable { + block(item) + } + } + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/SavedFilterBackup.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/SavedFilterBackup.kt new file mode 100644 index 000000000..6099e2de4 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/SavedFilterBackup.kt @@ -0,0 +1,34 @@ +package org.koitharu.kotatsu.backups.data.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.koitharu.kotatsu.core.model.MangaSourceSerializer +import org.koitharu.kotatsu.filter.data.MangaListFilterSerializer +import org.koitharu.kotatsu.filter.data.PersistableFilter +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaSource + +@Serializable +data class SavedFilterBackup( + @SerialName("name") + val name: String, + @Serializable(with = MangaSourceSerializer::class) + @SerialName("source") + val source: MangaSource, + @Serializable(with = MangaListFilterSerializer::class) + @SerialName("filter") + val filter: MangaListFilter, +) { + + constructor(persistableFilter: PersistableFilter) : this( + name = persistableFilter.name, + source = persistableFilter.source, + filter = persistableFilter.filter, + ) + + fun toPersistableFilter() = PersistableFilter( + name = name, + source = source, + filter = filter, + ) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt index 82aaf9e79..7a3c8777b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt @@ -17,6 +17,7 @@ enum class BackupSection( SOURCES("sources"), SCROBBLING("scrobbling"), STATS("statistics"), + SAVED_FILTERS("saved_filters"), ; companion object {