Migrate saved search and feed saved search to SQLDelight

This commit is contained in:
Jobobby04 2022-04-22 19:19:50 -04:00
parent 4a115785eb
commit 26b30adf4a
18 changed files with 469 additions and 558 deletions

View File

@ -0,0 +1,13 @@
package eu.kanade.data.exh
import exh.savedsearches.models.FeedSavedSearch
val feedSavedSearchMapper: (Long, Long, Long?, Boolean) -> FeedSavedSearch =
{ id, source, savedSearch, global ->
FeedSavedSearch(
id = id,
source = source,
savedSearch = savedSearch,
global = global
)
}

View File

@ -0,0 +1,14 @@
package eu.kanade.data.exh
import exh.savedsearches.models.SavedSearch
val savedSearchMapper: (Long, Long, String, String?, String?) -> SavedSearch =
{ id, source, name, query, filtersJson ->
SavedSearch(
id = id,
source = source,
name = name,
query = query,
filtersJson = filtersJson
)
}

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.data.DatabaseHandler
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -20,6 +21,7 @@ import uy.kohesive.injekt.injectLazy
abstract class AbstractBackupManager(protected val context: Context) { abstract class AbstractBackupManager(protected val context: Context) {
internal val databaseHelper: DatabaseHelper by injectLazy() internal val databaseHelper: DatabaseHelper by injectLazy()
internal val databaseHandler: DatabaseHandler by injectLazy()
internal val sourceManager: SourceManager by injectLazy() internal val sourceManager: SourceManager by injectLazy()
internal val trackManager: TrackManager by injectLazy() internal val trackManager: TrackManager by injectLazy()
protected val preferences: PreferencesHelper by injectLazy() protected val preferences: PreferencesHelper by injectLazy()

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup.full
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.AbstractBackupManager import eu.kanade.tachiyomi.data.backup.AbstractBackupManager
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
@ -39,11 +40,11 @@ import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadataAsync import exh.metadata.metadata.base.insertFlatMetadataAsync
import exh.savedsearches.models.SavedSearch
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
import exh.util.executeOnIO import exh.util.executeOnIO
import exh.util.nullIfBlank import exh.util.nullIfBlank
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import logcat.LogPriority import logcat.LogPriority
import okio.buffer import okio.buffer
@ -167,7 +168,8 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
* @return list of [BackupSavedSearch] to be backed up * @return list of [BackupSavedSearch] to be backed up
*/ */
private fun backupSavedSearches(): List<BackupSavedSearch> { private fun backupSavedSearches(): List<BackupSavedSearch> {
return databaseHelper.getSavedSearches().executeAsBlocking().map { return runBlocking {
databaseHandler.awaitList { saved_searchQueries.selectAll(savedSearchMapper) }.map {
BackupSavedSearch( BackupSavedSearch(
it.name, it.name,
it.query.orEmpty(), it.query.orEmpty(),
@ -176,6 +178,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
) )
} }
} }
}
// SY <-- // SY <--
/** /**
@ -431,25 +434,24 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
} }
// SY --> // SY -->
internal fun restoreSavedSearches(backupSavedSearches: List<BackupSavedSearch>) { internal suspend fun restoreSavedSearches(backupSavedSearches: List<BackupSavedSearch>) {
val currentSavedSearches = databaseHelper.getSavedSearches() val currentSavedSearches = databaseHandler.awaitList {
.executeAsBlocking() saved_searchQueries.selectAll(savedSearchMapper)
}
val newSavedSearches = backupSavedSearches.filter { backupSavedSearch -> databaseHandler.await(true) {
backupSavedSearches.filter { backupSavedSearch ->
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source } currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
}.map { }.forEach {
SavedSearch( saved_searchQueries.insertSavedSearch(
id = null, _id = null,
it.source, source = it.source,
it.name, name = it.name,
it.query.nullIfBlank(), query = it.query.nullIfBlank(),
filtersJson = it.filterList.nullIfBlank() filters_json = it.filterList.nullIfBlank()
?.takeUnless { it == "[]" }, ?.takeUnless { it == "[]" },
) )
}.ifEmpty { null } }
if (newSavedSearches != null) {
databaseHelper.insertSavedSearches(newSavedSearches)
} }
} }

View File

@ -76,7 +76,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBa
} }
// SY --> // SY -->
private fun restoreSavedSearches(backupSavedSearches: List<BackupSavedSearch>) { private suspend fun restoreSavedSearches(backupSavedSearches: List<BackupSavedSearch>) {
backupManager.restoreSavedSearches(backupSavedSearches) backupManager.restoreSavedSearches(backupSavedSearches)
restoreProgress += 1 restoreProgress += 1

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.legacy
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.AbstractBackupManager import eu.kanade.tachiyomi.data.backup.AbstractBackupManager
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.Companion.CURRENT_VERSION import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.Companion.CURRENT_VERSION
@ -289,12 +290,15 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
} }
// SY --> // SY -->
internal fun restoreSavedSearches(jsonSavedSearches: String) { internal suspend fun restoreSavedSearches(jsonSavedSearches: String) {
val backupSavedSearches = jsonSavedSearches.split("***").toSet() val backupSavedSearches = jsonSavedSearches.split("***").toSet()
val currentSavedSearches = databaseHelper.getSavedSearches().executeAsBlocking() val currentSavedSearches = databaseHandler.awaitList {
saved_searchQueries.selectAll(savedSearchMapper)
}
val newSavedSearches = backupSavedSearches.mapNotNull { databaseHandler.await(true) {
backupSavedSearches.mapNotNull {
runCatching { runCatching {
val content = parser.decodeFromString<JsonObject>(it.substringAfter(':')) val content = parser.decodeFromString<JsonObject>(it.substringAfter(':'))
SavedSearch( SavedSearch(
@ -307,10 +311,16 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
}.getOrNull() }.getOrNull()
}.filter { backupSavedSearch -> }.filter { backupSavedSearch ->
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source } currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
}.ifEmpty { null } }.forEach {
saved_searchQueries.insertSavedSearch(
if (newSavedSearches != null) { _id = null,
databaseHelper.insertSavedSearches(newSavedSearches) source = it.source,
name = it.name,
query = it.query.nullIfBlank(),
filters_json = it.filtersJson.nullIfBlank()
?.takeUnless { it == "[]" },
)
}
} }
} }

View File

@ -73,7 +73,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
} }
// SY --> // SY -->
private fun restoreSavedSearches(savedSearches: String) { private suspend fun restoreSavedSearches(savedSearches: String) {
backupManager.restoreSavedSearches(savedSearches) backupManager.restoreSavedSearches(savedSearches)
restoreProgress += 1 restoreProgress += 1

View File

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.data.database.queries package eu.kanade.tachiyomi.data.database.queries
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
import exh.savedsearches.tables.FeedSavedSearchTable
import exh.savedsearches.tables.SavedSearchTable
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
@ -76,32 +74,6 @@ fun getReadMangaNotInLibraryQuery() =
) )
""" """
/**
* Query to get the global feed saved searches
*/
fun getGlobalFeedSavedSearchQuery() =
"""
SELECT ${SavedSearchTable.TABLE}.*
FROM (
SELECT ${FeedSavedSearchTable.COL_SAVED_SEARCH_ID} FROM ${FeedSavedSearchTable.TABLE} WHERE ${FeedSavedSearchTable.COL_GLOBAL} = 1
) AS M
JOIN ${SavedSearchTable.TABLE}
ON ${SavedSearchTable.TABLE}.${SavedSearchTable.COL_ID} = M.${FeedSavedSearchTable.COL_SAVED_SEARCH_ID}
"""
/**
* Query to get the source feed saved searches
*/
fun getSourceFeedSavedSearchQuery() =
"""
SELECT ${SavedSearchTable.TABLE}.*
FROM (
SELECT ${FeedSavedSearchTable.COL_SAVED_SEARCH_ID} FROM ${FeedSavedSearchTable.TABLE} WHERE ${FeedSavedSearchTable.COL_GLOBAL} = 0 AND ${FeedSavedSearchTable.COL_SOURCE} = ?
) AS M
JOIN ${SavedSearchTable.TABLE}
ON ${SavedSearchTable.TABLE}.${SavedSearchTable.COL_ID} = M.${FeedSavedSearchTable.COL_SAVED_SEARCH_ID}
"""
/** /**
* Query to get the manga from the library, with their categories, read and unread count. * Query to get the manga from the library, with their categories, read and unread count.
*/ */

View File

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch import exh.savedsearches.models.SavedSearch
@ -66,9 +67,10 @@ open class FeedController :
} }
private fun addFeed() { private fun addFeed() {
viewScope.launchUI {
if (presenter.hasTooManyFeeds()) { if (presenter.hasTooManyFeeds()) {
activity?.toast(R.string.too_many_in_feed) activity?.toast(R.string.too_many_in_feed)
return return@launchUI
} }
val items = presenter.getEnabledSources() val items = presenter.getEnabledSources()
val itemsStrings = items.map { it.toString() } val itemsStrings = items.map { it.toString() }
@ -85,8 +87,10 @@ open class FeedController :
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
} }
}
private fun addFeedSearch(source: CatalogueSource) { private fun addFeedSearch(source: CatalogueSource) {
viewScope.launchUI {
val items = presenter.getSourceSavedSearches(source) val items = presenter.getSourceSavedSearches(source)
val itemsStrings = listOf(activity!!.getString(R.string.latest)) + items.map { it.name } val itemsStrings = listOf(activity!!.getString(R.string.latest)) + items.map { it.name }
var selectedIndex = 0 var selectedIndex = 0
@ -102,6 +106,7 @@ open class FeedController :
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
} }
}
/** /**
* Called when manga in global search is clicked, opens manga. * Called when manga in global search is clicked, opens manga.

View File

@ -1,6 +1,9 @@
package eu.kanade.tachiyomi.ui.browse.feed package eu.kanade.tachiyomi.ui.browse.feed
import android.os.Bundle import android.os.Bundle
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.feedSavedSearchMapper
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
@ -15,9 +18,12 @@ import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import logcat.LogPriority import logcat.LogPriority
@ -35,11 +41,12 @@ import xyz.nulldev.ts.api.http.serializer.FilterSerializer
* Function calls should be done from here. UI calls should be done from the controller. * Function calls should be done from here. UI calls should be done from the controller.
* *
* @param sourceManager manages the different sources. * @param sourceManager manages the different sources.
* @param db manages the database calls. * @param database manages the database calls.
* @param preferences manages the preference calls. * @param preferences manages the preference calls.
*/ */
open class FeedPresenter( open class FeedPresenter(
val sourceManager: SourceManager = Injekt.get(), val sourceManager: SourceManager = Injekt.get(),
val database: DatabaseHandler = Injekt.get(),
val db: DatabaseHelper = Injekt.get(), val db: DatabaseHelper = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
) : BasePresenter<FeedController>() { ) : BasePresenter<FeedController>() {
@ -62,14 +69,11 @@ open class FeedPresenter(
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
db.getGlobalFeedSavedSearches() database.subscribeToList { feed_saved_searchQueries.selectAllGlobal() }
.asRxObservable() .onEach {
.observeOn(AndroidSchedulers.mainThread())
.doOnEach {
getFeed() getFeed()
} }
.subscribe() .launchIn(presenterScope)
.let(::add)
} }
override fun onDestroy() { override fun onDestroy() {
@ -78,8 +82,10 @@ open class FeedPresenter(
super.onDestroy() super.onDestroy()
} }
fun hasTooManyFeeds(): Boolean { suspend fun hasTooManyFeeds(): Boolean {
return db.getGlobalFeedSavedSearches().executeAsBlocking().size > 10 return withIOContext {
database.awaitList { feed_saved_searchQueries.selectAllGlobal() }.size > 10
}
} }
fun getEnabledSources(): List<CatalogueSource> { fun getEnabledSources(): List<CatalogueSource> {
@ -93,33 +99,38 @@ open class FeedPresenter(
return list.sortedBy { it.id.toString() !in pinnedSources } return list.sortedBy { it.id.toString() !in pinnedSources }
} }
fun getSourceSavedSearches(source: CatalogueSource): List<SavedSearch> { suspend fun getSourceSavedSearches(source: CatalogueSource): List<SavedSearch> {
return db.getSavedSearches(source.id).executeAsBlocking() return withIOContext {
database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
}
} }
fun createFeed(source: CatalogueSource, savedSearch: SavedSearch?) { fun createFeed(source: CatalogueSource, savedSearch: SavedSearch?) {
launchIO { launchIO {
db.insertFeedSavedSearch( database.await {
FeedSavedSearch( feed_saved_searchQueries.insertFeedSavedSearch(
id = null, _id = null,
source = source.id, source = source.id,
savedSearch = savedSearch?.id, saved_search = savedSearch?.id,
global = true, global = true,
), )
).executeAsBlocking() }
} }
} }
fun deleteFeed(feed: FeedSavedSearch) { fun deleteFeed(feed: FeedSavedSearch) {
launchIO { launchIO {
db.deleteFeedSavedSearch(feed).executeAsBlocking() database.await {
feed_saved_searchQueries.deleteById(feed.id ?: return@await)
}
} }
} }
private fun getSourcesToGetFeed(): List<Pair<FeedSavedSearch, SavedSearch?>> { private suspend fun getSourcesToGetFeed(): List<Pair<FeedSavedSearch, SavedSearch?>> {
val savedSearches = db.getGlobalSavedSearchesFeed().executeAsBlocking() val savedSearches = database.awaitList {
.associateBy { it.id!! } feed_saved_searchQueries.selectGlobalFeedSavedSearch(savedSearchMapper)
return db.getGlobalFeedSavedSearches().executeAsBlocking() }.associateBy { it.id }
return database.awaitList { feed_saved_searchQueries.selectAllGlobal(feedSavedSearchMapper) }
.map { it to savedSearches[it.savedSearch] } .map { it to savedSearches[it.savedSearch] }
} }
@ -138,7 +149,7 @@ open class FeedPresenter(
/** /**
* Initiates get manga per feed. * Initiates get manga per feed.
*/ */
fun getFeed() { suspend fun getFeed() {
// Create image fetch subscription // Create image fetch subscription
initializeFetchImageSubscription() initializeFetchImageSubscription()

View File

@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.more.MoreController import eu.kanade.tachiyomi.ui.more.MoreController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.preference.asImmediateFlow import eu.kanade.tachiyomi.util.preference.asImmediateFlow
import eu.kanade.tachiyomi.util.system.connectivityManager import eu.kanade.tachiyomi.util.system.connectivityManager
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
@ -201,7 +202,7 @@ open class BrowseSourceController(bundle: Bundle) :
// SY --> // SY -->
this, this,
presenter.source, presenter.source,
presenter.loadSearches(), emptyList(),
// SY <-- // SY <--
onFilterClicked = { onFilterClicked = {
showProgressBar() showProgressBar()
@ -216,6 +217,7 @@ open class BrowseSourceController(bundle: Bundle) :
}, },
// EXH --> // EXH -->
onSaveClicked = { onSaveClicked = {
viewScope.launchUI {
filterSheet?.context?.let { filterSheet?.context?.let {
val names = presenter.loadSearches().map { it.name } val names = presenter.loadSearches().map { it.name }
var searchName = "" var searchName = ""
@ -234,8 +236,10 @@ open class BrowseSourceController(bundle: Bundle) :
.setNegativeButton(R.string.action_cancel, null) .setNegativeButton(R.string.action_cancel, null)
.show() .show()
} }
}
}, },
onSavedSearchClicked = cb@{ idOfSearch -> onSavedSearchClicked = { idOfSearch ->
viewScope.launchUI {
val search = presenter.loadSearch(idOfSearch) val search = presenter.loadSearch(idOfSearch)
if (search == null) { if (search == null) {
@ -245,12 +249,12 @@ open class BrowseSourceController(bundle: Bundle) :
.setMessage(R.string.save_search_failed_to_load_message) .setMessage(R.string.save_search_failed_to_load_message)
.show() .show()
} }
return@cb return@launchUI
} }
if (search.filterList == null) { if (search.filterList == null) {
activity?.toast(R.string.save_search_invalid) activity?.toast(R.string.save_search_invalid)
return@cb return@launchUI
} }
presenter.sourceFilters = FilterList(search.filterList) presenter.sourceFilters = FilterList(search.filterList)
@ -262,8 +266,9 @@ open class BrowseSourceController(bundle: Bundle) :
filterSheet?.dismiss() filterSheet?.dismiss()
presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters) presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters)
activity?.invalidateOptionsMenu() activity?.invalidateOptionsMenu()
}
}, },
onSavedSearchDeleteClicked = cb@{ idToDelete, name -> onSavedSearchDeleteClicked = { idToDelete, name ->
filterSheet?.context?.let { filterSheet?.context?.let {
MaterialAlertDialogBuilder(it) MaterialAlertDialogBuilder(it)
.setTitle(R.string.save_search_delete) .setTitle(R.string.save_search_delete)
@ -277,6 +282,9 @@ open class BrowseSourceController(bundle: Bundle) :
}, },
// EXH <-- // EXH <--
) )
launchUI {
filterSheet?.setSavedSearches(presenter.loadSearches())
}
filterSheet?.setFilters(presenter.filterItems) filterSheet?.setFilters(presenter.filterItems)
filterSheet?.setOnShowListener { actionFab?.hide() } filterSheet?.setOnShowListener { actionFab?.hide() }

View File

@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
import android.os.Bundle import android.os.Bundle
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
@ -37,6 +39,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
@ -50,8 +53,10 @@ import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -78,6 +83,7 @@ open class BrowseSourcePresenter(
// SY <-- // SY <--
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(), private val db: DatabaseHelper = Injekt.get(),
private val database: DatabaseHandler = Injekt.get(),
private val prefs: PreferencesHelper = Injekt.get(), private val prefs: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(), private val coverCache: CoverCache = Injekt.get(),
) : BasePresenter<BrowseSourceController>() { ) : BasePresenter<BrowseSourceController>() {
@ -140,7 +146,11 @@ open class BrowseSourcePresenter(
val jsonFilters = filters val jsonFilters = filters
if (savedSearchFilters != null) { if (savedSearchFilters != null) {
runCatching { runCatching {
val savedSearch = db.getSavedSearch(savedSearchFilters).executeAsBlocking() ?: return@runCatching val savedSearch = runBlocking {
database.awaitOneOrNull {
saved_searchQueries.selectById(savedSearchFilters, savedSearchMapper)
}
} ?: return@runCatching
query = savedSearch.query.orEmpty() query = savedSearch.query.orEmpty()
val filtersJson = savedSearch.filtersJson val filtersJson = savedSearch.filtersJson
?: return@runCatching ?: return@runCatching
@ -156,18 +166,14 @@ open class BrowseSourcePresenter(
} }
} }
db.getSavedSearches(source.id) database.subscribeToList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
.asRxObservable() .map { loadSearches(it) }
.map { .onEach {
loadSearches(it) withUIContext {
view?.setSavedSearches(it)
} }
.subscribeOn(Schedulers.io()) }
.observeOn(AndroidSchedulers.mainThread()) .launchIn(presenterScope)
.subscribeLatestCache(
{ controller, savedSearches ->
controller.setSavedSearches(savedSearches)
},
)
// SY <-- // SY <--
if (savedState != null) { if (savedState != null) {
@ -485,28 +491,31 @@ open class BrowseSourcePresenter(
fun saveSearch(name: String, query: String, filterList: FilterList) { fun saveSearch(name: String, query: String, filterList: FilterList) {
launchIO { launchIO {
kotlin.runCatching { kotlin.runCatching {
val savedSearch = SavedSearch( database.await {
id = null, saved_searchQueries.insertSavedSearch(
_id = null,
source = source.id, source = source.id,
name = name.trim(), name = name.trim(),
query = query.nullIfBlank(), query = query.nullIfBlank(),
filtersJson = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) }, filters_json = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) },
) )
}
db.insertSavedSearch(savedSearch).executeAsBlocking()
} }
} }
} }
fun deleteSearch(searchId: Long) { fun deleteSearch(searchId: Long) {
launchIO { launchIO {
db.deleteSavedSearch(searchId).executeAsBlocking() database.await { saved_searchQueries.deleteById(searchId) }
} }
} }
fun loadSearch(searchId: Long): EXHSavedSearch? { suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null return withIOContext {
return EXHSavedSearch( val search = database.awaitOneOrNull {
saved_searchQueries.selectById(searchId, savedSearchMapper)
} ?: return@withIOContext null
EXHSavedSearch(
id = search.id!!, id = search.id!!,
name = search.name, name = search.name,
query = search.query.orEmpty(), query = search.query.orEmpty(),
@ -522,9 +531,12 @@ open class BrowseSourcePresenter(
}.getOrNull(), }.getOrNull(),
) )
} }
}
fun loadSearches(searches: List<SavedSearch> = db.getSavedSearches(source.id).executeAsBlocking()): List<EXHSavedSearch> { suspend fun loadSearches(searches: List<SavedSearch>? = null): List<EXHSavedSearch> {
return searches.map { return withIOContext {
(searches ?: (database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }))
.map {
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch( val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
id = it.id!!, id = it.id!!,
name = it.name, name = it.name,
@ -564,5 +576,6 @@ open class BrowseSourcePresenter(
} }
} }
} }
}
// EXH <-- // EXH <--
} }

View File

@ -24,6 +24,8 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch import exh.savedsearches.models.SavedSearch
@ -184,7 +186,7 @@ open class SourceFeedController :
// SY --> // SY -->
this, this,
presenter.source, presenter.source,
presenter.loadSearches(), emptyList(),
// SY <-- // SY <--
onFilterClicked = { onFilterClicked = {
val allDefault = presenter.sourceFilters == presenter.source.getFilterList() val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
@ -202,7 +204,8 @@ open class SourceFeedController :
}, },
onResetClicked = {}, onResetClicked = {},
onSaveClicked = {}, onSaveClicked = {},
onSavedSearchClicked = cb@{ idOfSearch -> onSavedSearchClicked = { idOfSearch ->
viewScope.launchUI {
val search = presenter.loadSearch(idOfSearch) val search = presenter.loadSearch(idOfSearch)
if (search == null) { if (search == null) {
@ -212,12 +215,12 @@ open class SourceFeedController :
.setMessage(R.string.save_search_failed_to_load_message) .setMessage(R.string.save_search_failed_to_load_message)
.show() .show()
} }
return@cb return@launchUI
} }
if (search.filterList == null) { if (search.filterList == null) {
activity?.toast(R.string.save_search_invalid) activity?.toast(R.string.save_search_invalid)
return@cb return@launchUI
} }
presenter.sourceFilters = FilterList(search.filterList) presenter.sourceFilters = FilterList(search.filterList)
@ -231,12 +234,15 @@ open class SourceFeedController :
savedSearch = search.id, savedSearch = search.id,
) )
} }
}
}, },
onSavedSearchDeleteClicked = cb@{ idOfSearch, name -> onSavedSearchDeleteClicked = { idOfSearch, name ->
viewScope.launchUI {
if (presenter.hasTooManyFeeds()) { if (presenter.hasTooManyFeeds()) {
activity?.toast(R.string.too_many_in_feed) activity?.toast(R.string.too_many_in_feed)
return@cb return@launchUI
} }
withUIContext {
MaterialAlertDialogBuilder(activity!!) MaterialAlertDialogBuilder(activity!!)
.setTitle(R.string.feed) .setTitle(R.string.feed)
.setMessage(activity!!.getString(R.string.feed_add, name)) .setMessage(activity!!.getString(R.string.feed_add, name))
@ -245,8 +251,13 @@ open class SourceFeedController :
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
}
}
}, },
) )
launchUI {
filterSheet?.setSavedSearches(presenter.loadSearches())
}
filterSheet?.setFilters(presenter.filterItems) filterSheet?.setFilters(presenter.filterItems)
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly // TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly

View File

@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.source.feed
import android.os.Bundle import android.os.Bundle
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.data.DatabaseHandler
import eu.kanade.data.exh.feedSavedSearchMapper
import eu.kanade.data.exh.savedSearchMapper
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
@ -16,11 +19,15 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter.Companion.toItems
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.log.xLogE import exh.log.xLogE
import exh.savedsearches.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
@ -46,11 +53,12 @@ sealed class SourceFeed {
* Function calls should be done from here. UI calls should be done from the controller. * Function calls should be done from here. UI calls should be done from the controller.
* *
* @param source the source. * @param source the source.
* @param db manages the database calls. * @param database manages the database calls.
* @param preferences manages the preference calls. * @param preferences manages the preference calls.
*/ */
open class SourceFeedPresenter( open class SourceFeedPresenter(
val source: CatalogueSource, val source: CatalogueSource,
val database: DatabaseHandler = Injekt.get(),
val db: DatabaseHelper = Injekt.get(), val db: DatabaseHelper = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
) : BasePresenter<SourceFeedController>() { ) : BasePresenter<SourceFeedController>() {
@ -90,14 +98,11 @@ open class SourceFeedPresenter(
sourceFilters = source.getFilterList() sourceFilters = source.getFilterList()
db.getSourceFeedSavedSearches(source.id) database.subscribeToList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
.asRxObservable() .onEach {
.observeOn(AndroidSchedulers.mainThread())
.doOnEach {
getFeed() getFeed()
} }
.subscribe() .launchIn(presenterScope)
.let(::add)
} }
override fun onDestroy() { override fun onDestroy() {
@ -106,35 +111,39 @@ open class SourceFeedPresenter(
super.onDestroy() super.onDestroy()
} }
fun hasTooManyFeeds(): Boolean { suspend fun hasTooManyFeeds(): Boolean {
return db.getSourceFeedSavedSearches(source.id).executeAsBlocking().size > 10 return withIOContext {
database.awaitList {
feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id)
}.size > 10
}
} }
fun getSourceSavedSearches(): List<SavedSearch> { suspend fun getSourceSavedSearches(): List<SavedSearch> {
return db.getSavedSearches(source.id).executeAsBlocking() return database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
} }
fun createFeed(savedSearchId: Long) { fun createFeed(savedSearchId: Long) {
launchIO { launchIO {
db.insertFeedSavedSearch( database.await {
FeedSavedSearch( feed_saved_searchQueries.insertFeedSavedSearch(
id = null, _id = null,
source = source.id, source = source.id,
savedSearch = savedSearchId, saved_search = savedSearchId,
global = false, global = false
), )
).executeAsBlocking() }
} }
} }
fun deleteFeed(feed: FeedSavedSearch) { fun deleteFeed(feed: FeedSavedSearch) {
launchIO { launchIO {
db.deleteFeedSavedSearch(feed).executeAsBlocking() database.await { feed_saved_searchQueries.deleteById(feed.id ?: return@await) }
} }
} }
private fun getSourcesToGetFeed(): List<SourceFeed> { private suspend fun getSourcesToGetFeed(): List<SourceFeed> {
val savedSearches = db.getSourceSavedSearchesFeed(source.id).executeAsBlocking() val savedSearches = database.awaitList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
.associateBy { it.id!! } .associateBy { it.id!! }
return listOfNotNull( return listOfNotNull(
@ -142,7 +151,7 @@ open class SourceFeedPresenter(
SourceFeed.Latest SourceFeed.Latest
} else null, } else null,
SourceFeed.Browse, SourceFeed.Browse,
) + db.getSourceFeedSavedSearches(source.id).executeAsBlocking() ) + database.awaitList { feed_saved_searchQueries.selectBySource(source.id, feedSavedSearchMapper) }
.map { SourceFeed.SourceSavedSearch(it, savedSearches[it.savedSearch]!!) } .map { SourceFeed.SourceSavedSearch(it, savedSearches[it.savedSearch]!!) }
} }
@ -159,7 +168,7 @@ open class SourceFeedPresenter(
/** /**
* Initiates get manga per feed. * Initiates get manga per feed.
*/ */
fun getFeed() { suspend fun getFeed() {
// Create image fetch subscription // Create image fetch subscription
initializeFetchImageSubscription() initializeFetchImageSubscription()
@ -300,9 +309,12 @@ open class SourceFeedPresenter(
return localManga return localManga
} }
fun loadSearch(searchId: Long): EXHSavedSearch? { suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
val search = db.getSavedSearch(searchId).executeAsBlocking() ?: return null return withIOContext {
return EXHSavedSearch( val search = database.awaitOneOrNull {
saved_searchQueries.selectById(searchId, savedSearchMapper)
} ?: return@withIOContext null
EXHSavedSearch(
id = search.id!!, id = search.id!!,
name = search.name, name = search.name,
query = search.query.orEmpty(), query = search.query.orEmpty(),
@ -318,9 +330,11 @@ open class SourceFeedPresenter(
}.getOrNull(), }.getOrNull(),
) )
} }
}
fun loadSearches(): List<EXHSavedSearch> { suspend fun loadSearches(): List<EXHSavedSearch> {
return db.getSavedSearches(source.id).executeAsBlocking().map { return withIOContext {
database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }.map {
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch( val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
id = it.id!!, id = it.id!!,
name = it.name, name = it.name,
@ -330,6 +344,7 @@ open class SourceFeedPresenter(
val filters = try { val filters = try {
Json.decodeFromString<JsonArray>(filtersJson) Json.decodeFromString<JsonArray>(filtersJson)
} catch (e: Exception) { } catch (e: Exception) {
if (e is CancellationException) throw e
null null
} ?: return@map EXHSavedSearch( } ?: return@map EXHSavedSearch(
id = it.id!!, id = it.id!!,
@ -360,3 +375,4 @@ open class SourceFeedPresenter(
} }
} }
} }
}

View File

@ -6,6 +6,7 @@ import androidx.preference.PreferenceManager
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.data.DatabaseHandler
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
@ -40,8 +41,6 @@ import exh.eh.EHentaiUpdateWorker
import exh.log.xLogE import exh.log.xLogE
import exh.log.xLogW import exh.log.xLogW
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID import exh.source.EH_SOURCE_ID
import exh.source.HBROWSE_SOURCE_ID import exh.source.HBROWSE_SOURCE_ID
@ -51,6 +50,7 @@ import exh.source.PERV_EDEN_IT_SOURCE_ID
import exh.source.TSUMINO_SOURCE_ID import exh.source.TSUMINO_SOURCE_ID
import exh.util.nullIfBlank import exh.util.nullIfBlank
import exh.util.under import exh.util.under
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
@ -69,6 +69,7 @@ import java.net.URISyntaxException
object EXHMigrations { object EXHMigrations {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
private val database: DatabaseHandler by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
/** /**
@ -404,31 +405,31 @@ object EXHMigrations {
BackupCreatorJob.setupTask(context) BackupCreatorJob.setupTask(context)
} }
if (oldVersion under 31) { if (oldVersion under 31) {
val savedSearches = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull { runBlocking {
database.await(true) {
prefs.getStringSet("eh_saved_searches", emptySet())?.forEach {
kotlin.runCatching { kotlin.runCatching {
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':')) val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
SavedSearch( saved_searchQueries.insertSavedSearch(
id = null, _id = null,
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null, source = it.substringBefore(':').toLongOrNull() ?: return@forEach,
content["name"]!!.jsonPrimitive.content, name = content["name"]!!.jsonPrimitive.content,
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(), query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
Json.encodeToString(content["filters"]!!.jsonArray), filters_json = Json.encodeToString(content["filters"]!!.jsonArray)
) )
}.getOrNull()
}?.ifEmpty { null }
if (savedSearches != null) {
db.insertSavedSearches(savedSearches).executeAsBlocking()
} }
val feed = prefs.getStringSet("latest_tab_sources", emptySet())?.map { }
FeedSavedSearch( }
id = null, database.await(true) {
prefs.getStringSet("latest_tab_sources", emptySet())?.forEach {
feed_saved_searchQueries.insertFeedSavedSearch(
_id = null,
source = it.toLong(), source = it.toLong(),
savedSearch = null, saved_search = null,
global = true, global = true,
) )
}?.ifEmpty { null } }
if (feed != null) { }
db.insertFeedSavedSearches(feed).executeAsBlocking()
} }
prefs.edit(commit = true) { prefs.edit(commit = true) {
remove("eh_saved_searches") remove("eh_saved_searches")

View File

@ -3,6 +3,7 @@ package exh.debug
import android.app.Application import android.app.Application
import androidx.work.WorkManager import androidx.work.WorkManager
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.data.DatabaseHandler
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.data.database.tables.MangaTable import eu.kanade.tachiyomi.data.database.tables.MangaTable
@ -34,6 +35,7 @@ import java.util.UUID
object DebugFunctions { object DebugFunctions {
val app: Application by injectLazy() val app: Application by injectLazy()
val db: DatabaseHelper by injectLazy() val db: DatabaseHelper by injectLazy()
val database: DatabaseHandler by injectLazy()
val prefs: PreferencesHelper by injectLazy() val prefs: PreferencesHelper by injectLazy()
val sourceManager: SourceManager by injectLazy() val sourceManager: SourceManager by injectLazy()
@ -164,7 +166,7 @@ object DebugFunctions {
it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null it.favorite && db.getSearchMetadataForManga(it.id!!).executeAsBlocking() == null
} }
fun clearSavedSearches() = db.deleteAllSavedSearches().executeAsBlocking() fun clearSavedSearches() = runBlocking { database.await { saved_searchQueries.deleteAll() } }
fun listAllSources() = sourceManager.getCatalogueSources().joinToString("\n") { fun listAllSources() = sourceManager.getCatalogueSources().joinToString("\n") {
"${it.id}: ${it.name} (${it.lang.uppercase()})" "${it.id}: ${it.name} (${it.lang.uppercase()})"

View File

@ -1,86 +1,5 @@
package exh.savedsearches.queries package exh.savedsearches.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.queries.getGlobalFeedSavedSearchQuery
import eu.kanade.tachiyomi.data.database.queries.getSourceFeedSavedSearchQuery
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
import exh.savedsearches.tables.FeedSavedSearchTable
interface FeedSavedSearchQueries : DbProvider { interface FeedSavedSearchQueries : DbProvider
fun getGlobalFeedSavedSearches() = db.get()
.listOfObjects(FeedSavedSearch::class.java)
.withQuery(
Query.builder()
.table(FeedSavedSearchTable.TABLE)
.where("${FeedSavedSearchTable.COL_GLOBAL} = 1")
.orderBy(FeedSavedSearchTable.COL_ID)
.build(),
)
.prepare()
fun getSourceFeedSavedSearches(sourceId: Long) = db.get()
.listOfObjects(FeedSavedSearch::class.java)
.withQuery(
Query.builder()
.table(FeedSavedSearchTable.TABLE)
.where("${FeedSavedSearchTable.COL_SOURCE} = ? AND ${FeedSavedSearchTable.COL_GLOBAL} = 0")
.whereArgs(sourceId)
.orderBy(FeedSavedSearchTable.COL_ID)
.build(),
)
.prepare()
fun insertFeedSavedSearch(savedSearch: FeedSavedSearch) = db.put().`object`(savedSearch).prepare()
fun insertFeedSavedSearches(savedSearches: List<FeedSavedSearch>) = db.put().objects(savedSearches).prepare()
fun deleteFeedSavedSearch(savedSearch: FeedSavedSearch) = db.delete().`object`(savedSearch).prepare()
fun deleteFeedSavedSearch(id: Long) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(FeedSavedSearchTable.TABLE)
.where("${FeedSavedSearchTable.COL_ID} = ?")
.whereArgs(id)
.build(),
).prepare()
fun deleteAllFeedSavedSearches() = db.delete().byQuery(
DeleteQuery.builder()
.table(FeedSavedSearchTable.TABLE)
.build(),
)
.prepare()
fun getGlobalSavedSearchesFeed() = db.get()
.listOfObjects(SavedSearch::class.java)
.withQuery(
RawQuery.builder()
.query(getGlobalFeedSavedSearchQuery())
.build(),
)
.prepare()
fun getSourceSavedSearchesFeed(sourceId: Long) = db.get()
.listOfObjects(SavedSearch::class.java)
.withQuery(
RawQuery.builder()
.query(getSourceFeedSavedSearchQuery())
.args(sourceId)
.build(),
)
.prepare()
/*fun setMangasForMergedManga(mergedMangaId: Long, mergedMangases: List<SavedSearch>) {
db.inTransaction {
deleteSavedSearches(mergedMangaId).executeAsBlocking()
mergedMangases.chunked(100) { chunk ->
insertSavedSearches(chunk).executeAsBlocking()
}
}
}*/
}

View File

@ -1,93 +1,5 @@
package exh.savedsearches.queries 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 eu.kanade.tachiyomi.data.database.DbProvider
import exh.savedsearches.models.SavedSearch
import exh.savedsearches.tables.SavedSearchTable
interface SavedSearchQueries : DbProvider { 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 getSavedSearches(ids: List<Long>) = db.get()
.listOfObjects(SavedSearch::class.java)
.withQuery(
Query.builder()
.table(SavedSearchTable.TABLE)
.where("${SavedSearchTable.COL_ID} IN (?)")
.whereArgs(ids.joinToString())
.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()
}
}
}*/
}