Migrate saved searches to the db
This commit is contained in:
parent
1ebcfc53d4
commit
5d330c4f75
@ -25,7 +25,7 @@ android {
|
||||
applicationId = "eu.kanade.tachiyomi.sy"
|
||||
minSdk = AndroidConfig.minSdk
|
||||
targetSdk = AndroidConfig.targetSdk
|
||||
versionCode = 30
|
||||
versionCode = 31
|
||||
versionName = "1.8.1"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
|
@ -38,13 +38,11 @@ import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
||||
import exh.metadata.metadata.base.insertFlatMetadataAsync
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import exh.source.getMainSource
|
||||
import exh.util.executeOnIO
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import logcat.LogPriority
|
||||
import okio.buffer
|
||||
@ -164,14 +162,12 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
||||
* @return list of [BackupSavedSearch] to be backed up
|
||||
*/
|
||||
private fun backupSavedSearches(): List<BackupSavedSearch> {
|
||||
return preferences.savedSearches().get().mapNotNull {
|
||||
val sourceId = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
val content = Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
return databaseHelper.getSavedSearches().executeAsBlocking().map {
|
||||
BackupSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
content.filters.toString(),
|
||||
sourceId
|
||||
it.name,
|
||||
it.query.orEmpty(),
|
||||
it.filtersJson ?: "[]",
|
||||
it.source
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -431,34 +427,25 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
||||
|
||||
// SY -->
|
||||
internal fun restoreSavedSearches(backupSavedSearches: List<BackupSavedSearch>) {
|
||||
val currentSavedSearches = preferences.savedSearches().get().mapNotNull {
|
||||
val sourceId = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
val content = try {
|
||||
Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
} catch (e: Exception) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
BackupSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
content.filters.toString(),
|
||||
sourceId
|
||||
)
|
||||
}
|
||||
val currentSavedSearches = databaseHelper.getSavedSearches()
|
||||
.executeAsBlocking()
|
||||
|
||||
val newSavedSearches = backupSavedSearches.filter { backupSavedSearch ->
|
||||
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
||||
}.map {
|
||||
"${it.source}:" + Json.encodeToString(
|
||||
JsonSavedSearch(
|
||||
it.name,
|
||||
it.query,
|
||||
Json.decodeFromString(it.filterList)
|
||||
)
|
||||
SavedSearch(
|
||||
id = null,
|
||||
it.source,
|
||||
it.name,
|
||||
it.query.nullIfBlank(),
|
||||
filtersJson = it.filterList.nullIfBlank()
|
||||
?.takeUnless { it == "[]" }
|
||||
)
|
||||
}.toSet()
|
||||
}.ifEmpty { null }
|
||||
|
||||
preferences.savedSearches().set(newSavedSearches + preferences.savedSearches().get())
|
||||
if (newSavedSearches != null) {
|
||||
databaseHelper.insertSavedSearches(newSavedSearches)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,11 +28,16 @@ import eu.kanade.tachiyomi.source.model.toSManga
|
||||
import eu.kanade.tachiyomi.source.online.all.MergedSource
|
||||
import exh.eh.EHentaiThrottleManager
|
||||
import exh.merged.sql.models.MergedMangaReference
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.source.MERGED_SOURCE_ID
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.contextual
|
||||
import kotlin.math.max
|
||||
@ -287,34 +292,26 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
|
||||
internal fun restoreSavedSearches(jsonSavedSearches: String) {
|
||||
val backupSavedSearches = jsonSavedSearches.split("***").toSet()
|
||||
|
||||
val currentSavedSearches = databaseHelper.getSavedSearches().executeAsBlocking()
|
||||
|
||||
val newSavedSearches = backupSavedSearches.mapNotNull {
|
||||
runCatching {
|
||||
val id = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
val content = parser.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
id to content
|
||||
val content = parser.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||
SavedSearch(
|
||||
id = null,
|
||||
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null,
|
||||
content["name"]!!.jsonPrimitive.content,
|
||||
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||
Json.encodeToString(content["filters"]!!.jsonArray)
|
||||
)
|
||||
}.getOrNull()
|
||||
}.toMutableSet()
|
||||
}.filter { backupSavedSearch ->
|
||||
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
||||
}.ifEmpty { null }
|
||||
|
||||
val currentSources = newSavedSearches.map(Pair<Long, *>::first).toSet()
|
||||
|
||||
newSavedSearches += preferences.savedSearches().get().mapNotNull {
|
||||
kotlin.runCatching {
|
||||
val id = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
val content = parser.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
id to content
|
||||
}.getOrNull()
|
||||
if (newSavedSearches != null) {
|
||||
databaseHelper.insertSavedSearches(newSavedSearches)
|
||||
}
|
||||
|
||||
val otherSerialized = preferences.savedSearches().get().mapNotNull {
|
||||
val sourceId = it.substringBefore(":").toLongOrNull() ?: return@mapNotNull null
|
||||
if (sourceId in currentSources) return@mapNotNull null
|
||||
it
|
||||
}.toSet()
|
||||
|
||||
val newSerialized = newSavedSearches.map { (source, savedSearch) ->
|
||||
"$source:" + Json.encodeToString(savedSearch)
|
||||
}.toSet()
|
||||
preferences.savedSearches().set(otherSerialized + newSerialized)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,13 +36,16 @@ import exh.metadata.sql.models.SearchTitle
|
||||
import exh.metadata.sql.queries.SearchMetadataQueries
|
||||
import exh.metadata.sql.queries.SearchTagQueries
|
||||
import exh.metadata.sql.queries.SearchTitleQueries
|
||||
import exh.savedsearches.mappers.SavedSearchTypeMapping
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.savedsearches.queries.SavedSearchQueries
|
||||
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||
|
||||
/**
|
||||
* This class provides operations to manage the database through its interfaces.
|
||||
*/
|
||||
open class DatabaseHelper(context: Context) :
|
||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries, FavoriteEntryQueries /* SY <-- */ {
|
||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries, FavoriteEntryQueries, SavedSearchQueries /* SY <-- */ {
|
||||
|
||||
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
||||
.name(DbOpenCallback.DATABASE_NAME)
|
||||
@ -63,6 +66,7 @@ open class DatabaseHelper(context: Context) :
|
||||
.addTypeMapping(SearchTitle::class.java, SearchTitleTypeMapping())
|
||||
.addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping())
|
||||
.addTypeMapping(FavoriteEntry::class.java, FavoriteEntryTypeMapping())
|
||||
.addTypeMapping(SavedSearch::class.java, SavedSearchTypeMapping())
|
||||
// SY <--
|
||||
.build()
|
||||
|
||||
|
@ -13,6 +13,7 @@ import exh.merged.sql.tables.MergedTable
|
||||
import exh.metadata.sql.tables.SearchMetadataTable
|
||||
import exh.metadata.sql.tables.SearchTagTable
|
||||
import exh.metadata.sql.tables.SearchTitleTable
|
||||
import exh.savedsearches.tables.SavedSearchTable
|
||||
|
||||
class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
|
||||
@ -25,7 +26,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = /* SY --> */ 12 /* SY <-- */
|
||||
const val DATABASE_VERSION = /* SY --> */ 13 /* SY <-- */
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
@ -41,6 +42,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
execSQL(SearchTitleTable.createTableQuery)
|
||||
execSQL(MergedTable.createTableQuery)
|
||||
execSQL(FavoriteEntryTable.createTableQuery)
|
||||
execSQL(SavedSearchTable.createTableQuery)
|
||||
// SY <--
|
||||
|
||||
// DB indexes
|
||||
@ -101,6 +103,9 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
if (oldVersion < 12) {
|
||||
db.execSQL(FavoriteEntryTable.fixTableQuery)
|
||||
}
|
||||
if (oldVersion < 13) {
|
||||
db.execSQL(SavedSearchTable.createTableQuery)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
@ -411,8 +411,6 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun ehLastVersionCode() = flowPrefs.getInt("eh_last_version_code", 0)
|
||||
|
||||
fun savedSearches() = flowPrefs.getStringSet("eh_saved_searches", emptySet())
|
||||
|
||||
fun logLevel() = flowPrefs.getInt(Keys.eh_logLevel, 0)
|
||||
|
||||
fun enableSourceBlacklist() = flowPrefs.getBoolean("eh_enable_source_blacklist", true)
|
||||
|
@ -37,7 +37,6 @@ import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourceController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet.FilterNavigationView.Companion.MAX_SAVED_SEARCHES
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
|
||||
@ -84,6 +83,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
searchQuery: String? = null,
|
||||
// SY -->
|
||||
smartSearchConfig: SourceController.SmartSearchConfig? = null,
|
||||
savedSearch: Long? = null,
|
||||
filterList: String? = null
|
||||
// SY <--
|
||||
) : this(
|
||||
@ -99,6 +99,10 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
putParcelable(SMART_SEARCH_CONFIG_KEY, smartSearchConfig)
|
||||
}
|
||||
|
||||
if (savedSearch != null) {
|
||||
putLong(SAVED_SEARCH_CONFIG_KEY, savedSearch)
|
||||
}
|
||||
|
||||
if (filterList != null) {
|
||||
putString(FILTERS_CONFIG_KEY, filterList)
|
||||
}
|
||||
@ -154,7 +158,8 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
return BrowseSourcePresenter(
|
||||
args.getLong(SOURCE_ID_KEY),
|
||||
args.getString(SEARCH_QUERY_KEY),
|
||||
filters = args.getString(FILTERS_CONFIG_KEY)
|
||||
filters = args.getString(FILTERS_CONFIG_KEY),
|
||||
savedSearch = args.getLong(SAVED_SEARCH_CONFIG_KEY, 0).takeUnless { it == 0L }
|
||||
)
|
||||
// SY <--
|
||||
}
|
||||
@ -179,6 +184,10 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
// SY <--
|
||||
}
|
||||
|
||||
fun setSavedSearches(savedSearches: List<EXHSavedSearch>) {
|
||||
filterSheet?.setSavedSearches(savedSearches)
|
||||
}
|
||||
|
||||
open fun initFilterSheet() {
|
||||
if (presenter.sourceFilters.isEmpty()) {
|
||||
// SY -->
|
||||
@ -207,6 +216,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
// EXH -->
|
||||
onSaveClicked = {
|
||||
filterSheet?.context?.let {
|
||||
val names = presenter.loadSearches().map { it.name }
|
||||
var searchName = ""
|
||||
MaterialAlertDialogBuilder(it)
|
||||
.setTitle(R.string.save_search)
|
||||
@ -214,27 +224,18 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
searchName = input
|
||||
}
|
||||
.setPositiveButton(R.string.action_save) { _, _ ->
|
||||
val oldSavedSearches = presenter.loadSearches()
|
||||
if (searchName.isNotBlank() &&
|
||||
oldSavedSearches.size < MAX_SAVED_SEARCHES
|
||||
) {
|
||||
val newSearches = oldSavedSearches + EXHSavedSearch(
|
||||
searchName.trim(),
|
||||
presenter.query,
|
||||
presenter.sourceFilters
|
||||
)
|
||||
presenter.saveSearches(newSearches)
|
||||
filterSheet?.setSavedSearches(newSearches)
|
||||
if (searchName.isNotBlank() && searchName !in names) {
|
||||
presenter.saveSearch(searchName.trim(), presenter.query, presenter.sourceFilters)
|
||||
} else {
|
||||
it.toast(R.string.save_search_invalid_name)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.action_cancel, null)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
onSavedSearchClicked = cb@{ indexToSearch ->
|
||||
val savedSearches = presenter.loadSearches()
|
||||
|
||||
val search = savedSearches.getOrNull(indexToSearch)
|
||||
onSavedSearchClicked = cb@{ idOfSearch ->
|
||||
val search = presenter.loadSearch(idOfSearch)
|
||||
|
||||
if (search == null) {
|
||||
filterSheet?.context?.let {
|
||||
@ -261,32 +262,14 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters)
|
||||
activity?.invalidateOptionsMenu()
|
||||
},
|
||||
onSavedSearchDeleteClicked = cb@{ indexToDelete, name ->
|
||||
val savedSearches = presenter.loadSearches()
|
||||
|
||||
val search = savedSearches.getOrNull(indexToDelete)
|
||||
|
||||
if (search == null || search.name != name) {
|
||||
filterSheet?.context?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
.setTitle(R.string.save_search_failed_to_delete)
|
||||
.setMessage(R.string.save_search_failed_to_delete_message)
|
||||
.show()
|
||||
}
|
||||
return@cb
|
||||
}
|
||||
|
||||
onSavedSearchDeleteClicked = cb@{ idToDelete, name ->
|
||||
filterSheet?.context?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
.setTitle(R.string.save_search_delete)
|
||||
.setMessage(it.getString(R.string.save_search_delete_message, search.name))
|
||||
.setMessage(it.getString(R.string.save_search_delete_message, name))
|
||||
.setPositiveButton(R.string.action_cancel, null)
|
||||
.setNegativeButton(android.R.string.ok) { _, _ ->
|
||||
val newSearches = savedSearches.filterIndexed { index, _ ->
|
||||
index != indexToDelete
|
||||
}
|
||||
presenter.saveSearches(newSearches)
|
||||
filterSheet?.setSavedSearches(newSearches)
|
||||
presenter.deleteSearch(idToDelete)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
@ -836,6 +819,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
|
||||
// SY -->
|
||||
const val SMART_SEARCH_CONFIG_KEY = "smartSearchConfig"
|
||||
const val SAVED_SEARCH_CONFIG_KEY = "savedSearch"
|
||||
const val FILTERS_CONFIG_KEY = "filters"
|
||||
// SY <--
|
||||
}
|
||||
|
@ -42,8 +42,9 @@ import eu.kanade.tachiyomi.util.removeCovers
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import exh.log.xLogE
|
||||
import exh.savedsearches.EXHSavedSearch
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.source.isEhBasedSource
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
@ -73,6 +74,7 @@ open class BrowseSourcePresenter(
|
||||
searchQuery: String? = null,
|
||||
// SY -->
|
||||
private val filters: String? = null,
|
||||
private val savedSearch: Long? = null,
|
||||
// SY <--
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
@ -134,21 +136,45 @@ open class BrowseSourcePresenter(
|
||||
sourceFilters = source.getFilterList()
|
||||
|
||||
// SY -->
|
||||
val savedSearchFilters = savedSearch
|
||||
val jsonFilters = filters
|
||||
if (jsonFilters != null) {
|
||||
if (savedSearchFilters != null) {
|
||||
runCatching {
|
||||
val filters = Json.decodeFromString<JsonSavedSearch>(jsonFilters)
|
||||
filterSerializer.deserialize(sourceFilters, filters.filters)
|
||||
val savedSearch = db.getSavedSearch(savedSearchFilters).executeAsBlocking() ?: return@runCatching
|
||||
query = savedSearch.query.orEmpty()
|
||||
val filtersJson = savedSearch.filtersJson
|
||||
?: return@runCatching
|
||||
val filters = Json.decodeFromString<JsonArray>(filtersJson)
|
||||
filterSerializer.deserialize(sourceFilters, filters)
|
||||
appliedFilters = sourceFilters
|
||||
}
|
||||
} else if (jsonFilters != null) {
|
||||
runCatching {
|
||||
val filters = Json.decodeFromString<JsonArray>(jsonFilters)
|
||||
filterSerializer.deserialize(sourceFilters, filters)
|
||||
appliedFilters = sourceFilters
|
||||
}
|
||||
}
|
||||
val allDefault = sourceFilters == source.getFilterList()
|
||||
|
||||
db.getSavedSearches(source.id)
|
||||
.asRxObservable()
|
||||
.map {
|
||||
loadSearches(it)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeLatestCache(
|
||||
{ controller, savedSearches ->
|
||||
controller.setSavedSearches(savedSearches)
|
||||
}
|
||||
)
|
||||
// SY <--
|
||||
|
||||
if (savedState != null) {
|
||||
query = savedState.getString(::query.name, "")
|
||||
}
|
||||
|
||||
restartPager(/* SY -->*/ filters = if (allDefault) this.appliedFilters else sourceFilters /* SY <--*/)
|
||||
restartPager()
|
||||
}
|
||||
|
||||
override fun onSave(state: Bundle) {
|
||||
@ -319,7 +345,7 @@ open class BrowseSourcePresenter(
|
||||
.forEach { service ->
|
||||
launchIO {
|
||||
try {
|
||||
service.match(manga)?.let { track ->
|
||||
service.match(source, manga)?.let { track ->
|
||||
track.manga_id = manga.id!!
|
||||
(service as TrackService).bind(track)
|
||||
db.insertTrack(track).executeAsBlocking()
|
||||
@ -456,48 +482,84 @@ open class BrowseSourcePresenter(
|
||||
}
|
||||
|
||||
// EXH -->
|
||||
fun saveSearches(searches: List<EXHSavedSearch>) {
|
||||
val otherSerialized = prefs.savedSearches().get().filterNot {
|
||||
it.startsWith("${source.id}:")
|
||||
}.toSet()
|
||||
val newSerialized = searches.map {
|
||||
"${source.id}:" + Json.encodeToString(
|
||||
JsonSavedSearch(
|
||||
it.name,
|
||||
it.query,
|
||||
if (it.filterList != null) {
|
||||
filterSerializer.serialize(it.filterList)
|
||||
} else JsonArray(emptyList())
|
||||
fun saveSearch(name: String, query: String, filterList: FilterList) {
|
||||
launchIO {
|
||||
kotlin.runCatching {
|
||||
val savedSearch = SavedSearch(
|
||||
id = null,
|
||||
source = source.id,
|
||||
name = name.trim(),
|
||||
query = query.nullIfBlank(),
|
||||
filtersJson = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) }
|
||||
)
|
||||
)
|
||||
|
||||
db.insertSavedSearch(savedSearch).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
prefs.savedSearches().set(otherSerialized + newSerialized)
|
||||
}
|
||||
|
||||
fun loadSearches(): List<EXHSavedSearch> {
|
||||
return prefs.savedSearches().get().mapNotNull {
|
||||
val id = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
if (id != source.id) return@mapNotNull null
|
||||
val content = try {
|
||||
Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
fun deleteSearch(searchId: Long) {
|
||||
launchIO {
|
||||
db.deleteSavedSearch(searchId).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSearch(searchId: Long): EXHSavedSearch? {
|
||||
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null
|
||||
return EXHSavedSearch(
|
||||
id = search.id!!,
|
||||
name = search.name,
|
||||
query = search.query.orEmpty(),
|
||||
filterList = runCatching {
|
||||
val originalFilters = source.getFilterList()
|
||||
filterSerializer.deserialize(
|
||||
filters = originalFilters,
|
||||
json = search.filtersJson
|
||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
||||
?: return@runCatching null
|
||||
)
|
||||
originalFilters
|
||||
}.getOrNull()
|
||||
)
|
||||
}
|
||||
|
||||
fun loadSearches(searches: List<SavedSearch> = db.getSavedSearches(source.id).executeAsBlocking()): List<EXHSavedSearch> {
|
||||
return searches.map {
|
||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
val filters = try {
|
||||
Json.decodeFromString<JsonArray>(filtersJson)
|
||||
} catch (e: Exception) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
xLogE("Failed to load saved search!", e)
|
||||
null
|
||||
} ?: return@map EXHSavedSearch(
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
|
||||
try {
|
||||
val originalFilters = source.getFilterList()
|
||||
filterSerializer.deserialize(originalFilters, content.filters)
|
||||
filterSerializer.deserialize(originalFilters, filters)
|
||||
EXHSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
originalFilters
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = originalFilters
|
||||
)
|
||||
} catch (t: RuntimeException) {
|
||||
// Load failed
|
||||
xLogE("Failed to load saved search!", t)
|
||||
EXHSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
null
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import eu.kanade.tachiyomi.widget.SimpleNavigationView
|
||||
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
||||
import exh.savedsearches.EXHSavedSearch
|
||||
import exh.source.getMainSource
|
||||
import exh.util.under
|
||||
|
||||
class SourceFilterSheet(
|
||||
activity: Activity,
|
||||
@ -32,8 +31,8 @@ class SourceFilterSheet(
|
||||
private val onResetClicked: () -> Unit,
|
||||
// EXH -->
|
||||
private val onSaveClicked: () -> Unit,
|
||||
var onSavedSearchClicked: (Int) -> Unit = {},
|
||||
var onSavedSearchDeleteClicked: (Int, String) -> Unit = { _, _ -> }
|
||||
var onSavedSearchClicked: (Long) -> Unit = {},
|
||||
var onSavedSearchDeleteClicked: (Long, String) -> Unit = { _, _ -> }
|
||||
// EXH <--
|
||||
) : BaseBottomSheetDialog(activity) {
|
||||
|
||||
@ -97,9 +96,9 @@ class SourceFilterSheet(
|
||||
// SY -->
|
||||
var onSaveClicked = {}
|
||||
|
||||
var onSavedSearchClicked: (Int) -> Unit = {}
|
||||
var onSavedSearchClicked: (Long) -> Unit = {}
|
||||
|
||||
var onSavedSearchDeleteClicked: (Int, String) -> Unit = { _, _ -> }
|
||||
var onSavedSearchDeleteClicked: (Long, String) -> Unit = { _, _ -> }
|
||||
|
||||
private val savedSearchesAdapter = SavedSearchesAdapter(getSavedSearchesChips(searches))
|
||||
// SY <--
|
||||
@ -143,17 +142,13 @@ class SourceFilterSheet(
|
||||
}
|
||||
|
||||
private fun getSavedSearchesChips(searches: List<EXHSavedSearch>): List<Chip> {
|
||||
recycler.post {
|
||||
binding.saveSearchBtn.isVisible = searches.size under MAX_SAVED_SEARCHES
|
||||
}
|
||||
return searches.withIndex()
|
||||
.sortedBy { it.value.name }
|
||||
.map { (index, search) ->
|
||||
return searches
|
||||
.map { search ->
|
||||
Chip(context).apply {
|
||||
text = search.name
|
||||
setOnClickListener { onSavedSearchClicked(index) }
|
||||
setOnClickListener { onSavedSearchClicked(search.id) }
|
||||
setOnLongClickListener {
|
||||
onSavedSearchDeleteClicked(index, search.name); true
|
||||
onSavedSearchDeleteClicked(search.id, search.name); true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,10 +158,6 @@ class SourceFilterSheet(
|
||||
fun hideFilterButton() {
|
||||
binding.filterBtn.isVisible = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_SAVED_SEARCHES = 500 // if you want more than this, fuck you, i guess
|
||||
}
|
||||
// EXH <--
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet
|
||||
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -196,25 +195,21 @@ open class IndexController :
|
||||
onFilterClicked = {
|
||||
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
||||
filterSheet?.dismiss()
|
||||
val json = if (allDefault) {
|
||||
null
|
||||
if (allDefault) {
|
||||
onBrowseClick(
|
||||
presenter.query.nullIfBlank()
|
||||
)
|
||||
} else {
|
||||
Json.encodeToString(
|
||||
JsonSavedSearch(
|
||||
"",
|
||||
"",
|
||||
filterSerializer.serialize(presenter.sourceFilters)
|
||||
)
|
||||
onBrowseClick(
|
||||
presenter.query.nullIfBlank(),
|
||||
filters = Json.encodeToString(filterSerializer.serialize(presenter.sourceFilters))
|
||||
)
|
||||
}
|
||||
onBrowseClick(presenter.query.nullIfBlank(), json)
|
||||
},
|
||||
onResetClicked = {},
|
||||
onSaveClicked = {},
|
||||
onSavedSearchClicked = cb@{ indexToSearch ->
|
||||
val savedSearches = presenter.loadSearches()
|
||||
|
||||
val search = savedSearches.getOrNull(indexToSearch)
|
||||
onSavedSearchClicked = cb@{ idOfSearch ->
|
||||
val search = presenter.loadSearch(idOfSearch)
|
||||
|
||||
if (search == null) {
|
||||
filterSheet?.context?.let {
|
||||
@ -237,14 +232,10 @@ open class IndexController :
|
||||
filterSheet?.dismiss()
|
||||
|
||||
if (!allDefault) {
|
||||
val json = Json.encodeToString(
|
||||
JsonSavedSearch(
|
||||
"",
|
||||
"",
|
||||
filterSerializer.serialize(presenter.sourceFilters)
|
||||
)
|
||||
onBrowseClick(
|
||||
search = presenter.query.nullIfBlank(),
|
||||
savedSearch = search.id
|
||||
)
|
||||
onBrowseClick(presenter.query.nullIfBlank(), json)
|
||||
}
|
||||
},
|
||||
onSavedSearchDeleteClicked = { _, _ -> }
|
||||
@ -325,8 +316,8 @@ open class IndexController :
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
fun onBrowseClick(search: String? = null, filters: String? = null) {
|
||||
router.replaceTopController(BrowseSourceController(presenter.source, search, filterList = filters).withFadeTransaction())
|
||||
fun onBrowseClick(search: String? = null, savedSearch: Long? = null, filters: String? = null) {
|
||||
router.replaceTopController(BrowseSourceController(presenter.source, search, savedSearch = savedSearch, filterList = filters).withFadeTransaction())
|
||||
}
|
||||
|
||||
private fun onLatestClick() {
|
||||
|
@ -19,7 +19,6 @@ import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import exh.log.xLogE
|
||||
import exh.savedsearches.EXHSavedSearch
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@ -37,6 +36,7 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import logcat.LogPriority
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@ -212,30 +212,61 @@ open class IndexPresenter(
|
||||
|
||||
private val filterSerializer = FilterSerializer()
|
||||
|
||||
fun loadSearch(searchId: Long): EXHSavedSearch? {
|
||||
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null
|
||||
return EXHSavedSearch(
|
||||
id = search.id!!,
|
||||
name = search.name,
|
||||
query = search.query.orEmpty(),
|
||||
filterList = runCatching {
|
||||
val originalFilters = source.getFilterList()
|
||||
filterSerializer.deserialize(
|
||||
filters = originalFilters,
|
||||
json = search.filtersJson
|
||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
||||
?: return@runCatching null
|
||||
)
|
||||
originalFilters
|
||||
}.getOrNull()
|
||||
)
|
||||
}
|
||||
|
||||
fun loadSearches(): List<EXHSavedSearch> {
|
||||
return preferences.savedSearches().get().mapNotNull {
|
||||
val id = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null
|
||||
if (id != source.id) return@mapNotNull null
|
||||
val content = try {
|
||||
Json.decodeFromString<JsonSavedSearch>(it.substringAfter(':'))
|
||||
return db.getSavedSearches(source.id).executeAsBlocking().map {
|
||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
val filters = try {
|
||||
Json.decodeFromString<JsonArray>(filtersJson)
|
||||
} catch (e: Exception) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
null
|
||||
} ?: return@map EXHSavedSearch(
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
|
||||
try {
|
||||
val originalFilters = source.getFilterList()
|
||||
filterSerializer.deserialize(originalFilters, content.filters)
|
||||
filterSerializer.deserialize(originalFilters, filters)
|
||||
EXHSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
originalFilters
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = originalFilters
|
||||
)
|
||||
} catch (t: RuntimeException) {
|
||||
// Load failed
|
||||
xLogE("Failed to load saved search!", t)
|
||||
EXHSavedSearch(
|
||||
content.name,
|
||||
content.query,
|
||||
null
|
||||
id = it.id!!,
|
||||
name = it.name,
|
||||
query = it.query.orEmpty(),
|
||||
filterList = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.xLogE
|
||||
import exh.log.xLogW
|
||||
import exh.merged.sql.models.MergedMangaReference
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.source.BlacklistedSources
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.HBROWSE_SOURCE_ID
|
||||
@ -47,11 +48,17 @@ import exh.source.MERGED_SOURCE_ID
|
||||
import exh.source.PERV_EDEN_EN_SOURCE_ID
|
||||
import exh.source.PERV_EDEN_IT_SOURCE_ID
|
||||
import exh.source.TSUMINO_SOURCE_ID
|
||||
import exh.util.nullIfBlank
|
||||
import exh.util.under
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -395,6 +402,26 @@ object EXHMigrations {
|
||||
if (oldVersion under 30) {
|
||||
BackupCreatorJob.setupTask(context)
|
||||
}
|
||||
if (oldVersion under 31) {
|
||||
val savedSearches = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
||||
kotlin.runCatching {
|
||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||
SavedSearch(
|
||||
id = null,
|
||||
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null,
|
||||
content["name"]!!.jsonPrimitive.content,
|
||||
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||
Json.encodeToString(content["filters"]!!.jsonArray)
|
||||
)
|
||||
}.getOrNull()
|
||||
}?.ifEmpty { null }
|
||||
if (savedSearches != null) {
|
||||
db.insertSavedSearches(savedSearches)
|
||||
}
|
||||
prefs.edit(commit = true) {
|
||||
remove("eh_saved_searches")
|
||||
}
|
||||
}
|
||||
|
||||
// if (oldVersion under 1) { } (1 is current release version)
|
||||
// do stuff here when releasing changed crap
|
||||
|
@ -7,18 +7,15 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.toSManga
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
import exh.EXHMigrations
|
||||
import exh.eh.EHentaiThrottleManager
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.xLogE
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
||||
import exh.metadata.metadata.base.insertFlatMetadataAsync
|
||||
import exh.savedsearches.JsonSavedSearch
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.EXH_SOURCE_ID
|
||||
import exh.source.isEhBasedManga
|
||||
@ -30,11 +27,7 @@ import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.lang.RuntimeException
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("unused")
|
||||
@ -171,7 +164,7 @@ object DebugFunctions {
|
||||
it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null
|
||||
}
|
||||
|
||||
fun clearSavedSearches() = prefs.savedSearches().set(emptySet())
|
||||
fun clearSavedSearches() = db.deleteAllSavedSearches().executeAsBlocking()
|
||||
|
||||
fun listAllSources() = sourceManager.getCatalogueSources().joinToString("\n") {
|
||||
"${it.id}: ${it.name} (${it.lang.uppercase()})"
|
||||
@ -249,7 +242,7 @@ object DebugFunctions {
|
||||
)
|
||||
}
|
||||
|
||||
fun copyEHentaiSavedSearchesToExhentai() {
|
||||
/*fun copyEHentaiSavedSearchesToExhentai() {
|
||||
runBlocking {
|
||||
val source = sourceManager.get(EH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
|
||||
val newSource = sourceManager.get(EXH_SOURCE_ID) as? CatalogueSource ?: return@runBlocking
|
||||
@ -325,7 +318,7 @@ object DebugFunctions {
|
||||
}
|
||||
prefs.savedSearches().set((otherSerialized + newSerialized).toSet())
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
fun fixReaderViewerBackupBug() {
|
||||
db.inTransaction {
|
||||
|
@ -3,6 +3,7 @@ package exh.savedsearches
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
||||
data class EXHSavedSearch(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val query: String,
|
||||
val filterList: FilterList?
|
||||
|
@ -1,11 +0,0 @@
|
||||
package exh.savedsearches
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
|
||||
@Serializable
|
||||
data class JsonSavedSearch(
|
||||
val name: String,
|
||||
val query: String,
|
||||
val filters: JsonArray
|
||||
)
|
@ -0,0 +1,66 @@
|
||||
package exh.savedsearches.mappers
|
||||
|
||||
import android.database.Cursor
|
||||
import androidx.core.content.contentValuesOf
|
||||
import androidx.core.database.getStringOrNull
|
||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.savedsearches.tables.SavedSearchTable.COL_FILTERS_JSON
|
||||
import exh.savedsearches.tables.SavedSearchTable.COL_ID
|
||||
import exh.savedsearches.tables.SavedSearchTable.COL_NAME
|
||||
import exh.savedsearches.tables.SavedSearchTable.COL_QUERY
|
||||
import exh.savedsearches.tables.SavedSearchTable.COL_SOURCE
|
||||
import exh.savedsearches.tables.SavedSearchTable.TABLE
|
||||
|
||||
class SavedSearchTypeMapping : SQLiteTypeMapping<SavedSearch>(
|
||||
SavedSearchPutResolver(),
|
||||
SavedSearchGetResolver(),
|
||||
SavedSearchDeleteResolver()
|
||||
)
|
||||
|
||||
class SavedSearchPutResolver : DefaultPutResolver<SavedSearch>() {
|
||||
|
||||
override fun mapToInsertQuery(obj: SavedSearch) = InsertQuery.builder()
|
||||
.table(TABLE)
|
||||
.build()
|
||||
|
||||
override fun mapToUpdateQuery(obj: SavedSearch) = UpdateQuery.builder()
|
||||
.table(TABLE)
|
||||
.where("$COL_ID = ?")
|
||||
.whereArgs(obj.id)
|
||||
.build()
|
||||
|
||||
override fun mapToContentValues(obj: SavedSearch) = contentValuesOf(
|
||||
COL_ID to obj.id,
|
||||
COL_SOURCE to obj.source,
|
||||
COL_NAME to obj.name,
|
||||
COL_QUERY to obj.query,
|
||||
COL_FILTERS_JSON to obj.filtersJson
|
||||
)
|
||||
}
|
||||
|
||||
class SavedSearchGetResolver : DefaultGetResolver<SavedSearch>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): SavedSearch = SavedSearch(
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID)),
|
||||
source = cursor.getLong(cursor.getColumnIndexOrThrow(COL_SOURCE)),
|
||||
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME)),
|
||||
query = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(COL_QUERY)),
|
||||
filtersJson = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(COL_FILTERS_JSON))
|
||||
)
|
||||
}
|
||||
|
||||
class SavedSearchDeleteResolver : DefaultDeleteResolver<SavedSearch>() {
|
||||
|
||||
override fun mapToDeleteQuery(obj: SavedSearch) = DeleteQuery.builder()
|
||||
.table(TABLE)
|
||||
.where("$COL_ID = ?")
|
||||
.whereArgs(obj.id)
|
||||
.build()
|
||||
}
|
18
app/src/main/java/exh/savedsearches/models/SavedSearch.kt
Normal file
18
app/src/main/java/exh/savedsearches/models/SavedSearch.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package exh.savedsearches.models
|
||||
|
||||
data class SavedSearch(
|
||||
// Tag identifier, unique
|
||||
var id: Long?,
|
||||
|
||||
// The source the saved search is for
|
||||
var source: Long,
|
||||
|
||||
// If false the manga will not grab chapter updates
|
||||
var name: String,
|
||||
|
||||
// The query if there is any
|
||||
var query: String?,
|
||||
|
||||
// The filter list
|
||||
var filtersJson: String?,
|
||||
)
|
@ -0,0 +1,82 @@
|
||||
package exh.savedsearches.queries
|
||||
|
||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import exh.savedsearches.tables.SavedSearchTable
|
||||
|
||||
interface SavedSearchQueries : DbProvider {
|
||||
fun getSavedSearches(source: Long) = db.get()
|
||||
.listOfObjects(SavedSearch::class.java)
|
||||
.withQuery(
|
||||
Query.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.where("${SavedSearchTable.COL_SOURCE} = ?")
|
||||
.whereArgs(source)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun deleteSavedSearches(source: Long) = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.where("${SavedSearchTable.COL_SOURCE} = ?")
|
||||
.whereArgs(source)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun getSavedSearches() = db.get()
|
||||
.listOfObjects(SavedSearch::class.java)
|
||||
.withQuery(
|
||||
Query.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.orderBy(SavedSearchTable.COL_ID)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun getSavedSearch(id: Long) = db.get()
|
||||
.`object`(SavedSearch::class.java)
|
||||
.withQuery(
|
||||
Query.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.where("${SavedSearchTable.COL_ID} = ?")
|
||||
.whereArgs(id)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun insertSavedSearch(savedSearch: SavedSearch) = db.put().`object`(savedSearch).prepare()
|
||||
|
||||
fun insertSavedSearches(savedSearches: List<SavedSearch>) = db.put().objects(savedSearches).prepare()
|
||||
|
||||
fun deleteSavedSearch(savedSearch: SavedSearch) = db.delete().`object`(savedSearch).prepare()
|
||||
|
||||
fun deleteSavedSearch(id: Long) = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.where("${SavedSearchTable.COL_ID} = ?")
|
||||
.whereArgs(id)
|
||||
.build()
|
||||
).prepare()
|
||||
|
||||
fun deleteAllSavedSearches() = db.delete().byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(SavedSearchTable.TABLE)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
/*fun setMangasForMergedManga(mergedMangaId: Long, mergedMangases: List<SavedSearch>) {
|
||||
db.inTransaction {
|
||||
deleteSavedSearches(mergedMangaId).executeAsBlocking()
|
||||
mergedMangases.chunked(100) { chunk ->
|
||||
insertSavedSearches(chunk).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package exh.savedsearches.tables
|
||||
|
||||
object SavedSearchTable {
|
||||
|
||||
const val TABLE = "saved_search"
|
||||
|
||||
const val COL_ID = "_id"
|
||||
|
||||
const val COL_SOURCE = "source"
|
||||
|
||||
const val COL_NAME = "name"
|
||||
|
||||
const val COL_QUERY = "query"
|
||||
|
||||
const val COL_FILTERS_JSON = "filters_json"
|
||||
|
||||
val createTableQuery: String
|
||||
get() =
|
||||
"""CREATE TABLE $TABLE(
|
||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||
$COL_SOURCE INTEGER NOT NULL,
|
||||
$COL_NAME TEXT NOT NULL,
|
||||
$COL_QUERY TEXT,
|
||||
$COL_FILTERS_JSON TEXT
|
||||
)"""
|
||||
}
|
@ -355,6 +355,7 @@
|
||||
<string name="save_search_delete">Delete saved search query?</string>
|
||||
<string name="save_search_delete_message">Are you sure you wish to delete your saved search query: \'%1$s\'?</string>
|
||||
<string name="save_search_invalid">Saved search invalid, filters have changed</string>
|
||||
<string name="save_search_invalid_name">Invalid saved search name</string>
|
||||
|
||||
<!-- Source Categories -->
|
||||
<string name="no_source_categories">No source categories available</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user