Migrate saved search and feed saved search to SQLDelight
This commit is contained in:
parent
4a115785eb
commit
26b30adf4a
13
app/src/main/java/eu/kanade/data/exh/FeedSavedSearch.kt
Normal file
13
app/src/main/java/eu/kanade/data/exh/FeedSavedSearch.kt
Normal 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
|
||||||
|
)
|
||||||
|
}
|
14
app/src/main/java/eu/kanade/data/exh/SavedSearch.kt
Normal file
14
app/src/main/java/eu/kanade/data/exh/SavedSearch.kt
Normal 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
|
||||||
|
)
|
||||||
|
}
|
@ -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()
|
||||||
|
@ -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,13 +168,15 @@ 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 {
|
||||||
BackupSavedSearch(
|
databaseHandler.awaitList { saved_searchQueries.selectAll(savedSearchMapper) }.map {
|
||||||
it.name,
|
BackupSavedSearch(
|
||||||
it.query.orEmpty(),
|
it.name,
|
||||||
it.filtersJson ?: "[]",
|
it.query.orEmpty(),
|
||||||
it.source,
|
it.filtersJson ?: "[]",
|
||||||
)
|
it.source,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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) {
|
||||||
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
backupSavedSearches.filter { backupSavedSearch ->
|
||||||
}.map {
|
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
||||||
SavedSearch(
|
}.forEach {
|
||||||
id = null,
|
saved_searchQueries.insertSavedSearch(
|
||||||
it.source,
|
_id = null,
|
||||||
it.name,
|
source = it.source,
|
||||||
it.query.nullIfBlank(),
|
name = it.name,
|
||||||
filtersJson = it.filterList.nullIfBlank()
|
query = it.query.nullIfBlank(),
|
||||||
?.takeUnless { it == "[]" },
|
filters_json = it.filterList.nullIfBlank()
|
||||||
)
|
?.takeUnless { it == "[]" },
|
||||||
}.ifEmpty { null }
|
)
|
||||||
|
}
|
||||||
if (newSavedSearches != null) {
|
|
||||||
databaseHelper.insertSavedSearches(newSavedSearches)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,28 +290,37 @@ 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) {
|
||||||
runCatching {
|
backupSavedSearches.mapNotNull {
|
||||||
val content = parser.decodeFromString<JsonObject>(it.substringAfter(':'))
|
runCatching {
|
||||||
SavedSearch(
|
val content = parser.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||||
id = null,
|
SavedSearch(
|
||||||
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null,
|
id = null,
|
||||||
content["name"]!!.jsonPrimitive.content,
|
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null,
|
||||||
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
content["name"]!!.jsonPrimitive.content,
|
||||||
Json.encodeToString(content["filters"]!!.jsonArray),
|
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||||
|
Json.encodeToString(content["filters"]!!.jsonArray),
|
||||||
|
)
|
||||||
|
}.getOrNull()
|
||||||
|
}.filter { backupSavedSearch ->
|
||||||
|
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
||||||
|
}.forEach {
|
||||||
|
saved_searchQueries.insertSavedSearch(
|
||||||
|
_id = null,
|
||||||
|
source = it.source,
|
||||||
|
name = it.name,
|
||||||
|
query = it.query.nullIfBlank(),
|
||||||
|
filters_json = it.filtersJson.nullIfBlank()
|
||||||
|
?.takeUnless { it == "[]" },
|
||||||
)
|
)
|
||||||
}.getOrNull()
|
}
|
||||||
}.filter { backupSavedSearch ->
|
|
||||||
currentSavedSearches.none { it.name == backupSavedSearch.name && it.source == backupSavedSearch.source }
|
|
||||||
}.ifEmpty { null }
|
|
||||||
|
|
||||||
if (newSavedSearches != null) {
|
|
||||||
databaseHelper.insertSavedSearches(newSavedSearches)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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,41 +67,45 @@ open class FeedController :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addFeed() {
|
private fun addFeed() {
|
||||||
if (presenter.hasTooManyFeeds()) {
|
viewScope.launchUI {
|
||||||
activity?.toast(R.string.too_many_in_feed)
|
if (presenter.hasTooManyFeeds()) {
|
||||||
return
|
activity?.toast(R.string.too_many_in_feed)
|
||||||
}
|
return@launchUI
|
||||||
val items = presenter.getEnabledSources()
|
}
|
||||||
val itemsStrings = items.map { it.toString() }
|
val items = presenter.getEnabledSources()
|
||||||
var selectedIndex = 0
|
val itemsStrings = items.map { it.toString() }
|
||||||
|
var selectedIndex = 0
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(activity!!)
|
MaterialAlertDialogBuilder(activity!!)
|
||||||
.setTitle(R.string.feed)
|
.setTitle(R.string.feed)
|
||||||
.setSingleChoiceItems(itemsStrings.toTypedArray(), selectedIndex) { _, which ->
|
.setSingleChoiceItems(itemsStrings.toTypedArray(), selectedIndex) { _, which ->
|
||||||
selectedIndex = which
|
selectedIndex = which
|
||||||
}
|
}
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
addFeedSearch(items[selectedIndex])
|
addFeedSearch(items[selectedIndex])
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFeedSearch(source: CatalogueSource) {
|
private fun addFeedSearch(source: CatalogueSource) {
|
||||||
val items = presenter.getSourceSavedSearches(source)
|
viewScope.launchUI {
|
||||||
val itemsStrings = listOf(activity!!.getString(R.string.latest)) + items.map { it.name }
|
val items = presenter.getSourceSavedSearches(source)
|
||||||
var selectedIndex = 0
|
val itemsStrings = listOf(activity!!.getString(R.string.latest)) + items.map { it.name }
|
||||||
|
var selectedIndex = 0
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(activity!!)
|
MaterialAlertDialogBuilder(activity!!)
|
||||||
.setTitle(R.string.feed)
|
.setTitle(R.string.feed)
|
||||||
.setSingleChoiceItems(itemsStrings.toTypedArray(), selectedIndex) { _, which ->
|
.setSingleChoiceItems(itemsStrings.toTypedArray(), selectedIndex) { _, which ->
|
||||||
selectedIndex = which
|
selectedIndex = which
|
||||||
}
|
}
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
presenter.createFeed(source, items.getOrNull(selectedIndex - 1))
|
presenter.createFeed(source, items.getOrNull(selectedIndex - 1))
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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,54 +217,58 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
},
|
},
|
||||||
// EXH -->
|
// EXH -->
|
||||||
onSaveClicked = {
|
onSaveClicked = {
|
||||||
filterSheet?.context?.let {
|
viewScope.launchUI {
|
||||||
val names = presenter.loadSearches().map { it.name }
|
|
||||||
var searchName = ""
|
|
||||||
MaterialAlertDialogBuilder(it)
|
|
||||||
.setTitle(R.string.save_search)
|
|
||||||
.setTextInput(hint = it.getString(R.string.save_search_hint)) { input ->
|
|
||||||
searchName = input
|
|
||||||
}
|
|
||||||
.setPositiveButton(R.string.action_save) { _, _ ->
|
|
||||||
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@{ idOfSearch ->
|
|
||||||
val search = presenter.loadSearch(idOfSearch)
|
|
||||||
|
|
||||||
if (search == null) {
|
|
||||||
filterSheet?.context?.let {
|
filterSheet?.context?.let {
|
||||||
|
val names = presenter.loadSearches().map { it.name }
|
||||||
|
var searchName = ""
|
||||||
MaterialAlertDialogBuilder(it)
|
MaterialAlertDialogBuilder(it)
|
||||||
.setTitle(R.string.save_search_failed_to_load)
|
.setTitle(R.string.save_search)
|
||||||
.setMessage(R.string.save_search_failed_to_load_message)
|
.setTextInput(hint = it.getString(R.string.save_search_hint)) { input ->
|
||||||
|
searchName = input
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.action_save) { _, _ ->
|
||||||
|
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()
|
.show()
|
||||||
}
|
}
|
||||||
return@cb
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search.filterList == null) {
|
|
||||||
activity?.toast(R.string.save_search_invalid)
|
|
||||||
return@cb
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.sourceFilters = FilterList(search.filterList)
|
|
||||||
filterSheet?.setFilters(presenter.filterItems)
|
|
||||||
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
|
||||||
|
|
||||||
showProgressBar()
|
|
||||||
adapter?.clear()
|
|
||||||
filterSheet?.dismiss()
|
|
||||||
presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters)
|
|
||||||
activity?.invalidateOptionsMenu()
|
|
||||||
},
|
},
|
||||||
onSavedSearchDeleteClicked = cb@{ idToDelete, name ->
|
onSavedSearchClicked = { idOfSearch ->
|
||||||
|
viewScope.launchUI {
|
||||||
|
val search = presenter.loadSearch(idOfSearch)
|
||||||
|
|
||||||
|
if (search == null) {
|
||||||
|
filterSheet?.context?.let {
|
||||||
|
MaterialAlertDialogBuilder(it)
|
||||||
|
.setTitle(R.string.save_search_failed_to_load)
|
||||||
|
.setMessage(R.string.save_search_failed_to_load_message)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
return@launchUI
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search.filterList == null) {
|
||||||
|
activity?.toast(R.string.save_search_invalid)
|
||||||
|
return@launchUI
|
||||||
|
}
|
||||||
|
|
||||||
|
presenter.sourceFilters = FilterList(search.filterList)
|
||||||
|
filterSheet?.setFilters(presenter.filterItems)
|
||||||
|
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
||||||
|
|
||||||
|
showProgressBar()
|
||||||
|
adapter?.clear()
|
||||||
|
filterSheet?.dismiss()
|
||||||
|
presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters)
|
||||||
|
activity?.invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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() }
|
||||||
|
@ -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())
|
.launchIn(presenterScope)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribeLatestCache(
|
|
||||||
{ controller, savedSearches ->
|
|
||||||
controller.setSavedSearches(savedSearches)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
if (savedState != null) {
|
if (savedState != null) {
|
||||||
@ -485,83 +491,90 @@ 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(
|
||||||
source = source.id,
|
_id = null,
|
||||||
name = name.trim(),
|
source = source.id,
|
||||||
query = query.nullIfBlank(),
|
name = name.trim(),
|
||||||
filtersJson = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) },
|
query = query.nullIfBlank(),
|
||||||
)
|
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 {
|
||||||
id = search.id!!,
|
saved_searchQueries.selectById(searchId, savedSearchMapper)
|
||||||
name = search.name,
|
} ?: return@withIOContext null
|
||||||
query = search.query.orEmpty(),
|
EXHSavedSearch(
|
||||||
filterList = runCatching {
|
id = search.id!!,
|
||||||
val originalFilters = source.getFilterList()
|
name = search.name,
|
||||||
filterSerializer.deserialize(
|
query = search.query.orEmpty(),
|
||||||
filters = originalFilters,
|
filterList = runCatching {
|
||||||
json = search.filtersJson
|
val originalFilters = source.getFilterList()
|
||||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
filterSerializer.deserialize(
|
||||||
?: return@runCatching null,
|
filters = originalFilters,
|
||||||
)
|
json = search.filtersJson
|
||||||
originalFilters
|
?.let { Json.decodeFromString<JsonArray>(it) }
|
||||||
}.getOrNull(),
|
?: return@runCatching null,
|
||||||
)
|
)
|
||||||
|
originalFilters
|
||||||
|
}.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 {
|
||||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
(searches ?: (database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }))
|
||||||
id = it.id!!,
|
.map {
|
||||||
name = it.name,
|
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
||||||
query = it.query.orEmpty(),
|
id = it.id!!,
|
||||||
filterList = null,
|
name = it.name,
|
||||||
)
|
query = it.query.orEmpty(),
|
||||||
val filters = try {
|
filterList = null,
|
||||||
Json.decodeFromString<JsonArray>(filtersJson)
|
)
|
||||||
} catch (e: Exception) {
|
val filters = try {
|
||||||
xLogE("Failed to load saved search!", e)
|
Json.decodeFromString<JsonArray>(filtersJson)
|
||||||
null
|
} catch (e: Exception) {
|
||||||
} ?: return@map EXHSavedSearch(
|
xLogE("Failed to load saved search!", e)
|
||||||
id = it.id!!,
|
null
|
||||||
name = it.name,
|
} ?: return@map EXHSavedSearch(
|
||||||
query = it.query.orEmpty(),
|
id = it.id!!,
|
||||||
filterList = null,
|
name = it.name,
|
||||||
)
|
query = it.query.orEmpty(),
|
||||||
|
filterList = null,
|
||||||
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val originalFilters = source.getFilterList()
|
val originalFilters = source.getFilterList()
|
||||||
filterSerializer.deserialize(originalFilters, filters)
|
filterSerializer.deserialize(originalFilters, filters)
|
||||||
EXHSavedSearch(
|
EXHSavedSearch(
|
||||||
id = it.id!!,
|
id = it.id!!,
|
||||||
name = it.name,
|
name = it.name,
|
||||||
query = it.query.orEmpty(),
|
query = it.query.orEmpty(),
|
||||||
filterList = originalFilters,
|
filterList = originalFilters,
|
||||||
)
|
)
|
||||||
} catch (t: RuntimeException) {
|
} catch (t: RuntimeException) {
|
||||||
// Load failed
|
// Load failed
|
||||||
xLogE("Failed to load saved search!", t)
|
xLogE("Failed to load saved search!", t)
|
||||||
EXHSavedSearch(
|
EXHSavedSearch(
|
||||||
id = it.id!!,
|
id = it.id!!,
|
||||||
name = it.name,
|
name = it.name,
|
||||||
query = it.query.orEmpty(),
|
query = it.query.orEmpty(),
|
||||||
filterList = null,
|
filterList = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// EXH <--
|
// EXH <--
|
||||||
|
@ -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,51 +204,60 @@ open class SourceFeedController :
|
|||||||
},
|
},
|
||||||
onResetClicked = {},
|
onResetClicked = {},
|
||||||
onSaveClicked = {},
|
onSaveClicked = {},
|
||||||
onSavedSearchClicked = cb@{ idOfSearch ->
|
onSavedSearchClicked = { idOfSearch ->
|
||||||
val search = presenter.loadSearch(idOfSearch)
|
viewScope.launchUI {
|
||||||
|
val search = presenter.loadSearch(idOfSearch)
|
||||||
|
|
||||||
if (search == null) {
|
if (search == null) {
|
||||||
filterSheet?.context?.let {
|
filterSheet?.context?.let {
|
||||||
MaterialAlertDialogBuilder(it)
|
MaterialAlertDialogBuilder(it)
|
||||||
.setTitle(R.string.save_search_failed_to_load)
|
.setTitle(R.string.save_search_failed_to_load)
|
||||||
.setMessage(R.string.save_search_failed_to_load_message)
|
.setMessage(R.string.save_search_failed_to_load_message)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
return@launchUI
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search.filterList == null) {
|
||||||
|
activity?.toast(R.string.save_search_invalid)
|
||||||
|
return@launchUI
|
||||||
|
}
|
||||||
|
|
||||||
|
presenter.sourceFilters = FilterList(search.filterList)
|
||||||
|
filterSheet?.setFilters(presenter.filterItems)
|
||||||
|
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
||||||
|
filterSheet?.dismiss()
|
||||||
|
|
||||||
|
if (!allDefault) {
|
||||||
|
onBrowseClick(
|
||||||
|
search = presenter.query.nullIfBlank(),
|
||||||
|
savedSearch = search.id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSavedSearchDeleteClicked = { idOfSearch, name ->
|
||||||
|
viewScope.launchUI {
|
||||||
|
if (presenter.hasTooManyFeeds()) {
|
||||||
|
activity?.toast(R.string.too_many_in_feed)
|
||||||
|
return@launchUI
|
||||||
|
}
|
||||||
|
withUIContext {
|
||||||
|
MaterialAlertDialogBuilder(activity!!)
|
||||||
|
.setTitle(R.string.feed)
|
||||||
|
.setMessage(activity!!.getString(R.string.feed_add, name))
|
||||||
|
.setPositiveButton(R.string.action_add) { _, _ ->
|
||||||
|
presenter.createFeed(idOfSearch)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
return@cb
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search.filterList == null) {
|
|
||||||
activity?.toast(R.string.save_search_invalid)
|
|
||||||
return@cb
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.sourceFilters = FilterList(search.filterList)
|
|
||||||
filterSheet?.setFilters(presenter.filterItems)
|
|
||||||
val allDefault = presenter.sourceFilters == presenter.source.getFilterList()
|
|
||||||
filterSheet?.dismiss()
|
|
||||||
|
|
||||||
if (!allDefault) {
|
|
||||||
onBrowseClick(
|
|
||||||
search = presenter.query.nullIfBlank(),
|
|
||||||
savedSearch = search.id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSavedSearchDeleteClicked = cb@{ idOfSearch, name ->
|
|
||||||
if (presenter.hasTooManyFeeds()) {
|
|
||||||
activity?.toast(R.string.too_many_in_feed)
|
|
||||||
return@cb
|
|
||||||
}
|
|
||||||
MaterialAlertDialogBuilder(activity!!)
|
|
||||||
.setTitle(R.string.feed)
|
|
||||||
.setMessage(activity!!.getString(R.string.feed_add, name))
|
|
||||||
.setPositiveButton(R.string.action_add) { _, _ ->
|
|
||||||
presenter.createFeed(idOfSearch)
|
|
||||||
}
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.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
|
||||||
|
@ -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,62 +309,69 @@ 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 {
|
||||||
id = search.id!!,
|
saved_searchQueries.selectById(searchId, savedSearchMapper)
|
||||||
name = search.name,
|
} ?: return@withIOContext null
|
||||||
query = search.query.orEmpty(),
|
EXHSavedSearch(
|
||||||
filterList = runCatching {
|
id = search.id!!,
|
||||||
val originalFilters = source.getFilterList()
|
name = search.name,
|
||||||
filterSerializer.deserialize(
|
query = search.query.orEmpty(),
|
||||||
filters = originalFilters,
|
filterList = runCatching {
|
||||||
json = search.filtersJson
|
val originalFilters = source.getFilterList()
|
||||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
filterSerializer.deserialize(
|
||||||
?: return@runCatching null,
|
filters = originalFilters,
|
||||||
)
|
json = search.filtersJson
|
||||||
originalFilters
|
?.let { Json.decodeFromString<JsonArray>(it) }
|
||||||
}.getOrNull(),
|
?: return@runCatching null,
|
||||||
)
|
)
|
||||||
|
originalFilters
|
||||||
|
}.getOrNull(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadSearches(): List<EXHSavedSearch> {
|
suspend fun loadSearches(): List<EXHSavedSearch> {
|
||||||
return db.getSavedSearches(source.id).executeAsBlocking().map {
|
return withIOContext {
|
||||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }.map {
|
||||||
id = it.id!!,
|
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null,
|
|
||||||
)
|
|
||||||
val filters = try {
|
|
||||||
Json.decodeFromString<JsonArray>(filtersJson)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
} ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(originalFilters, filters)
|
|
||||||
EXHSavedSearch(
|
|
||||||
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(
|
|
||||||
id = it.id!!,
|
id = it.id!!,
|
||||||
name = it.name,
|
name = it.name,
|
||||||
query = it.query.orEmpty(),
|
query = it.query.orEmpty(),
|
||||||
filterList = null,
|
filterList = null,
|
||||||
)
|
)
|
||||||
|
val filters = try {
|
||||||
|
Json.decodeFromString<JsonArray>(filtersJson)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e is CancellationException) throw 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, filters)
|
||||||
|
EXHSavedSearch(
|
||||||
|
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(
|
||||||
|
id = it.id!!,
|
||||||
|
name = it.name,
|
||||||
|
query = it.query.orEmpty(),
|
||||||
|
filterList = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
kotlin.runCatching {
|
database.await(true) {
|
||||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
prefs.getStringSet("eh_saved_searches", emptySet())?.forEach {
|
||||||
SavedSearch(
|
kotlin.runCatching {
|
||||||
id = null,
|
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||||
source = it.substringBefore(':').toLongOrNull() ?: return@mapNotNull null,
|
saved_searchQueries.insertSavedSearch(
|
||||||
content["name"]!!.jsonPrimitive.content,
|
_id = null,
|
||||||
content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
source = it.substringBefore(':').toLongOrNull() ?: return@forEach,
|
||||||
Json.encodeToString(content["filters"]!!.jsonArray),
|
name = content["name"]!!.jsonPrimitive.content,
|
||||||
)
|
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||||
}.getOrNull()
|
filters_json = Json.encodeToString(content["filters"]!!.jsonArray)
|
||||||
}?.ifEmpty { null }
|
)
|
||||||
if (savedSearches != null) {
|
}
|
||||||
db.insertSavedSearches(savedSearches).executeAsBlocking()
|
}
|
||||||
}
|
}
|
||||||
val feed = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
database.await(true) {
|
||||||
FeedSavedSearch(
|
prefs.getStringSet("latest_tab_sources", emptySet())?.forEach {
|
||||||
id = null,
|
feed_saved_searchQueries.insertFeedSavedSearch(
|
||||||
source = it.toLong(),
|
_id = null,
|
||||||
savedSearch = null,
|
source = it.toLong(),
|
||||||
global = true,
|
saved_search = null,
|
||||||
)
|
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")
|
||||||
|
@ -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()})"
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user