Use domain layer for saved searches
This commit is contained in:
parent
485e6719c3
commit
141b0477e7
@ -0,0 +1,73 @@
|
|||||||
|
package eu.kanade.data.source
|
||||||
|
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.data.exh.feedSavedSearchMapper
|
||||||
|
import eu.kanade.data.exh.savedSearchMapper
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import exh.savedsearches.models.FeedSavedSearch
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class FeedSavedSearchRepositoryImpl(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) : FeedSavedSearchRepository {
|
||||||
|
|
||||||
|
override suspend fun getGlobal(): List<FeedSavedSearch> {
|
||||||
|
return handler.awaitList { feed_saved_searchQueries.selectAllGlobal(feedSavedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGlobalAsFlow(): Flow<List<FeedSavedSearch>> {
|
||||||
|
return handler.subscribeToList { feed_saved_searchQueries.selectAllGlobal(feedSavedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getGlobalFeedSavedSearch(): List<SavedSearch> {
|
||||||
|
return handler.awaitList { feed_saved_searchQueries.selectGlobalFeedSavedSearch(savedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun countGlobal(): Long {
|
||||||
|
return handler.awaitOne { feed_saved_searchQueries.countGlobal() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getBySourceId(sourceId: Long): List<FeedSavedSearch> {
|
||||||
|
return handler.awaitList { feed_saved_searchQueries.selectBySource(sourceId, feedSavedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBySourceIdAsFlow(sourceId: Long): Flow<List<FeedSavedSearch>> {
|
||||||
|
return handler.subscribeToList { feed_saved_searchQueries.selectBySource(sourceId, feedSavedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getBySourceIdFeedSavedSearch(sourceId: Long): List<SavedSearch> {
|
||||||
|
return handler.awaitList { feed_saved_searchQueries.selectSourceFeedSavedSearch(sourceId, savedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun countBySourceId(sourceId: Long): Long {
|
||||||
|
return handler.awaitOne { feed_saved_searchQueries.countSourceFeedSavedSearch(sourceId) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(feedSavedSearchId: Long) {
|
||||||
|
handler.await { feed_saved_searchQueries.deleteById(feedSavedSearchId) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(feedSavedSearch: FeedSavedSearch): Long {
|
||||||
|
return handler.awaitOne(true) {
|
||||||
|
feed_saved_searchQueries.insert(
|
||||||
|
feedSavedSearch.source,
|
||||||
|
feedSavedSearch.savedSearch,
|
||||||
|
feedSavedSearch.global,
|
||||||
|
)
|
||||||
|
feed_saved_searchQueries.selectLastInsertedRowId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insertAll(feedSavedSearch: List<FeedSavedSearch>) {
|
||||||
|
return handler.await(true) {
|
||||||
|
feedSavedSearch.forEach {
|
||||||
|
feed_saved_searchQueries.insert(
|
||||||
|
it.source,
|
||||||
|
it.savedSearch,
|
||||||
|
it.global,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package eu.kanade.data.source
|
||||||
|
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.data.exh.savedSearchMapper
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class SavedSearchRepositoryImpl(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) : SavedSearchRepository {
|
||||||
|
|
||||||
|
override suspend fun getById(savedSearchId: Long): SavedSearch? {
|
||||||
|
return handler.awaitOneOrNull { saved_searchQueries.selectById(savedSearchId, savedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getBySourceId(sourceId: Long): List<SavedSearch> {
|
||||||
|
return handler.awaitList { saved_searchQueries.selectBySource(sourceId, savedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBySourceIdAsFlow(sourceId: Long): Flow<List<SavedSearch>> {
|
||||||
|
return handler.subscribeToList { saved_searchQueries.selectBySource(sourceId, savedSearchMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(savedSearchId: Long) {
|
||||||
|
handler.await { saved_searchQueries.deleteById(savedSearchId) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(savedSearch: SavedSearch): Long {
|
||||||
|
return handler.awaitOne(true) {
|
||||||
|
saved_searchQueries.insert(
|
||||||
|
savedSearch.source,
|
||||||
|
savedSearch.name,
|
||||||
|
savedSearch.query,
|
||||||
|
savedSearch.filtersJson,
|
||||||
|
)
|
||||||
|
saved_searchQueries.selectLastInsertedRowId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insertAll(savedSearch: List<SavedSearch>) {
|
||||||
|
handler.await(true) {
|
||||||
|
savedSearch.forEach {
|
||||||
|
saved_searchQueries.insert(
|
||||||
|
it.source,
|
||||||
|
it.name,
|
||||||
|
it.query,
|
||||||
|
it.filtersJson,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package eu.kanade.domain
|
|||||||
import eu.kanade.data.manga.FavoritesEntryRepositoryImpl
|
import eu.kanade.data.manga.FavoritesEntryRepositoryImpl
|
||||||
import eu.kanade.data.manga.MangaMergeRepositoryImpl
|
import eu.kanade.data.manga.MangaMergeRepositoryImpl
|
||||||
import eu.kanade.data.manga.MangaMetadataRepositoryImpl
|
import eu.kanade.data.manga.MangaMetadataRepositoryImpl
|
||||||
|
import eu.kanade.data.source.FeedSavedSearchRepositoryImpl
|
||||||
|
import eu.kanade.data.source.SavedSearchRepositoryImpl
|
||||||
import eu.kanade.domain.chapter.interactor.DeleteChapters
|
import eu.kanade.domain.chapter.interactor.DeleteChapters
|
||||||
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId
|
||||||
import eu.kanade.domain.manga.interactor.DeleteByMergeId
|
import eu.kanade.domain.manga.interactor.DeleteByMergeId
|
||||||
@ -19,6 +21,7 @@ import eu.kanade.domain.manga.interactor.GetMergedManga
|
|||||||
import eu.kanade.domain.manga.interactor.GetMergedMangaById
|
import eu.kanade.domain.manga.interactor.GetMergedMangaById
|
||||||
import eu.kanade.domain.manga.interactor.GetMergedMangaForDownloading
|
import eu.kanade.domain.manga.interactor.GetMergedMangaForDownloading
|
||||||
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
|
import eu.kanade.domain.manga.interactor.GetMergedReferencesById
|
||||||
|
import eu.kanade.domain.manga.interactor.GetSearchMetadata
|
||||||
import eu.kanade.domain.manga.interactor.GetSearchTags
|
import eu.kanade.domain.manga.interactor.GetSearchTags
|
||||||
import eu.kanade.domain.manga.interactor.GetSearchTitles
|
import eu.kanade.domain.manga.interactor.GetSearchTitles
|
||||||
import eu.kanade.domain.manga.interactor.InsertFavoriteEntries
|
import eu.kanade.domain.manga.interactor.InsertFavoriteEntries
|
||||||
@ -29,16 +32,32 @@ import eu.kanade.domain.manga.interactor.UpdateMergedSettings
|
|||||||
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
|
import eu.kanade.domain.manga.repository.FavoritesEntryRepository
|
||||||
import eu.kanade.domain.manga.repository.MangaMergeRepository
|
import eu.kanade.domain.manga.repository.MangaMergeRepository
|
||||||
import eu.kanade.domain.manga.repository.MangaMetadataRepository
|
import eu.kanade.domain.manga.repository.MangaMetadataRepository
|
||||||
|
import eu.kanade.domain.source.interactor.CountFeedSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.CountFeedSavedSearchGlobal
|
||||||
|
import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.DeleteSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||||
|
import eu.kanade.domain.source.interactor.GetFeedSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.GetFeedSavedSearchGlobal
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceIdFeed
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchGlobalFeed
|
||||||
import eu.kanade.domain.source.interactor.GetShowLatest
|
import eu.kanade.domain.source.interactor.GetShowLatest
|
||||||
import eu.kanade.domain.source.interactor.GetSourceCategories
|
import eu.kanade.domain.source.interactor.GetSourceCategories
|
||||||
|
import eu.kanade.domain.source.interactor.InsertFeedSavedSearch
|
||||||
|
import eu.kanade.domain.source.interactor.InsertSavedSearch
|
||||||
import eu.kanade.domain.source.interactor.SetSourceCategories
|
import eu.kanade.domain.source.interactor.SetSourceCategories
|
||||||
import eu.kanade.domain.source.interactor.ToggleExcludeFromDataSaver
|
import eu.kanade.domain.source.interactor.ToggleExcludeFromDataSaver
|
||||||
import eu.kanade.domain.source.interactor.ToggleSources
|
import eu.kanade.domain.source.interactor.ToggleSources
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
import uy.kohesive.injekt.api.InjektModule
|
import uy.kohesive.injekt.api.InjektModule
|
||||||
import uy.kohesive.injekt.api.InjektRegistrar
|
import uy.kohesive.injekt.api.InjektRegistrar
|
||||||
import uy.kohesive.injekt.api.addFactory
|
import uy.kohesive.injekt.api.addFactory
|
||||||
import uy.kohesive.injekt.api.addSingletonFactory
|
import uy.kohesive.injekt.api.addSingletonFactory
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
||||||
|
|
||||||
class SYDomainModule : InjektModule {
|
class SYDomainModule : InjektModule {
|
||||||
|
|
||||||
@ -53,11 +72,13 @@ class SYDomainModule : InjektModule {
|
|||||||
addFactory { GetMangaBySource(get()) }
|
addFactory { GetMangaBySource(get()) }
|
||||||
addFactory { DeleteChapters(get()) }
|
addFactory { DeleteChapters(get()) }
|
||||||
addFactory { DeleteMangaById(get()) }
|
addFactory { DeleteMangaById(get()) }
|
||||||
|
addFactory { FilterSerializer() }
|
||||||
|
|
||||||
addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) }
|
addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) }
|
||||||
addFactory { GetFlatMetadataById(get()) }
|
addFactory { GetFlatMetadataById(get()) }
|
||||||
addFactory { InsertFlatMetadata(get()) }
|
addFactory { InsertFlatMetadata(get()) }
|
||||||
addFactory { GetExhFavoriteMangaWithMetadata(get()) }
|
addFactory { GetExhFavoriteMangaWithMetadata(get()) }
|
||||||
|
addFactory { GetSearchMetadata(get()) }
|
||||||
addFactory { GetSearchTags(get()) }
|
addFactory { GetSearchTags(get()) }
|
||||||
addFactory { GetSearchTitles(get()) }
|
addFactory { GetSearchTitles(get()) }
|
||||||
addFactory { GetIdsOfFavoriteMangaWithMetadata(get()) }
|
addFactory { GetIdsOfFavoriteMangaWithMetadata(get()) }
|
||||||
@ -77,5 +98,22 @@ class SYDomainModule : InjektModule {
|
|||||||
addFactory { GetFavoriteEntries(get()) }
|
addFactory { GetFavoriteEntries(get()) }
|
||||||
addFactory { InsertFavoriteEntries(get()) }
|
addFactory { InsertFavoriteEntries(get()) }
|
||||||
addFactory { DeleteFavoriteEntries(get()) }
|
addFactory { DeleteFavoriteEntries(get()) }
|
||||||
|
|
||||||
|
addSingletonFactory<SavedSearchRepository> { SavedSearchRepositoryImpl(get()) }
|
||||||
|
addFactory { GetSavedSearchById(get()) }
|
||||||
|
addFactory { GetSavedSearchBySourceId(get()) }
|
||||||
|
addFactory { DeleteSavedSearchById(get()) }
|
||||||
|
addFactory { InsertSavedSearch(get()) }
|
||||||
|
addFactory { GetExhSavedSearch(get(), get(), get()) }
|
||||||
|
|
||||||
|
addSingletonFactory<FeedSavedSearchRepository> { FeedSavedSearchRepositoryImpl(get()) }
|
||||||
|
addFactory { InsertFeedSavedSearch(get()) }
|
||||||
|
addFactory { DeleteFeedSavedSearchById(get()) }
|
||||||
|
addFactory { GetFeedSavedSearchGlobal(get()) }
|
||||||
|
addFactory { GetFeedSavedSearchBySourceId(get()) }
|
||||||
|
addFactory { CountFeedSavedSearchGlobal(get()) }
|
||||||
|
addFactory { CountFeedSavedSearchBySourceId(get()) }
|
||||||
|
addFactory { GetSavedSearchGlobalFeed(get()) }
|
||||||
|
addFactory { GetSavedSearchBySourceIdFeed(get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
|
||||||
|
class CountFeedSavedSearchBySourceId(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(sourceId: Long): Long {
|
||||||
|
return feedSavedSearchRepository.countBySourceId(sourceId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
|
||||||
|
class CountFeedSavedSearchGlobal(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(): Long {
|
||||||
|
return feedSavedSearchRepository.countGlobal()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
|
||||||
|
class DeleteFeedSavedSearchById(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(feedSavedSearchId: Long) {
|
||||||
|
return feedSavedSearchRepository.delete(feedSavedSearchId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
|
|
||||||
|
class DeleteSavedSearchById(
|
||||||
|
private val savedSearchRepository: SavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(savedSearchId: Long) {
|
||||||
|
return savedSearchRepository.delete(savedSearchId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
|
import exh.log.xLogE
|
||||||
|
import exh.savedsearches.EXHSavedSearch
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
|
||||||
|
|
||||||
|
class GetExhSavedSearch(
|
||||||
|
private val getSavedSearchById: GetSavedSearchById,
|
||||||
|
private val getSavedSearchBySourceId: GetSavedSearchBySourceId,
|
||||||
|
private val filterSerializer: FilterSerializer,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun awaitOne(savedSearchId: Long, getFilterList: () -> FilterList): EXHSavedSearch? {
|
||||||
|
val search = getSavedSearchById.awaitOrNull(savedSearchId) ?: return null
|
||||||
|
return withIOContext { loadSearch(search, getFilterList) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun await(sourceId: Long, getFilterList: () -> FilterList): List<EXHSavedSearch> {
|
||||||
|
return withIOContext { loadSearches(getSavedSearchBySourceId.await(sourceId), getFilterList) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(sourceId: Long, getFilterList: () -> FilterList): Flow<List<EXHSavedSearch>> {
|
||||||
|
return getSavedSearchBySourceId.subscribe(sourceId)
|
||||||
|
.map { loadSearches(it, getFilterList) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSearches(searches: List<SavedSearch>, getFilterList: () -> FilterList): List<EXHSavedSearch> {
|
||||||
|
return searches.map { loadSearch(it, getFilterList) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSearch(search: SavedSearch, getFilterList: () -> FilterList): EXHSavedSearch {
|
||||||
|
val filters = getFilters(search.filtersJson)
|
||||||
|
|
||||||
|
return EXHSavedSearch(
|
||||||
|
id = search.id,
|
||||||
|
name = search.name,
|
||||||
|
query = search.query.orEmpty(),
|
||||||
|
filterList = filters?.let { deserializeFilters(it, getFilterList) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFilters(filtersJson: String?): JsonArray? {
|
||||||
|
return runCatching {
|
||||||
|
filtersJson?.let { Json.decodeFromString<JsonArray>(it) }
|
||||||
|
}.onFailure {
|
||||||
|
xLogE("Failed to load saved search!", it)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserializeFilters(filters: JsonArray, getFilterList: () -> FilterList): FilterList? {
|
||||||
|
return runCatching {
|
||||||
|
val originalFilters = getFilterList()
|
||||||
|
filterSerializer.deserialize(originalFilters, filters)
|
||||||
|
originalFilters
|
||||||
|
}.onFailure {
|
||||||
|
xLogE("Failed to load saved search!", it)
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import exh.savedsearches.models.FeedSavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class GetFeedSavedSearchBySourceId(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(sourceId: Long): List<FeedSavedSearch> {
|
||||||
|
return feedSavedSearchRepository.getBySourceId(sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(sourceId: Long): Flow<List<FeedSavedSearch>> {
|
||||||
|
return feedSavedSearchRepository.getBySourceIdAsFlow(sourceId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import exh.savedsearches.models.FeedSavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class GetFeedSavedSearchGlobal(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(): List<FeedSavedSearch> {
|
||||||
|
return feedSavedSearchRepository.getGlobal()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(): Flow<List<FeedSavedSearch>> {
|
||||||
|
return feedSavedSearchRepository.getGlobalAsFlow()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
|
||||||
|
class GetSavedSearchById(
|
||||||
|
private val savedSearchRepository: SavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(savedSearchId: Long): SavedSearch {
|
||||||
|
return savedSearchRepository.getById(savedSearchId)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitOrNull(savedSearchId: Long): SavedSearch? {
|
||||||
|
return savedSearchRepository.getById(savedSearchId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class GetSavedSearchBySourceId(
|
||||||
|
private val savedSearchRepository: SavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(sourceId: Long): List<SavedSearch> {
|
||||||
|
return savedSearchRepository.getBySourceId(sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(sourceId: Long): Flow<List<SavedSearch>> {
|
||||||
|
return savedSearchRepository.getBySourceIdAsFlow(sourceId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
|
||||||
|
class GetSavedSearchBySourceIdFeed(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(sourceId: Long): List<SavedSearch> {
|
||||||
|
return feedSavedSearchRepository.getBySourceIdFeedSavedSearch(sourceId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
|
||||||
|
class GetSavedSearchGlobalFeed(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(): List<SavedSearch> {
|
||||||
|
return feedSavedSearchRepository.getGlobalFeedSavedSearch()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.FeedSavedSearchRepository
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import exh.savedsearches.models.FeedSavedSearch
|
||||||
|
import logcat.LogPriority
|
||||||
|
import logcat.asLog
|
||||||
|
|
||||||
|
class InsertFeedSavedSearch(
|
||||||
|
private val feedSavedSearchRepository: FeedSavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(feedSavedSearch: FeedSavedSearch): Long? {
|
||||||
|
return try {
|
||||||
|
feedSavedSearchRepository.insert(feedSavedSearch)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR) { e.asLog() }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitAll(feedSavedSearch: List<FeedSavedSearch>) {
|
||||||
|
try {
|
||||||
|
feedSavedSearchRepository.insertAll(feedSavedSearch)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR) { e.asLog() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.repository.SavedSearchRepository
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import logcat.LogPriority
|
||||||
|
import logcat.asLog
|
||||||
|
|
||||||
|
class InsertSavedSearch(
|
||||||
|
private val savedSearchRepository: SavedSearchRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(savedSearch: SavedSearch): Long? {
|
||||||
|
return try {
|
||||||
|
savedSearchRepository.insert(savedSearch)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR) { e.asLog() }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun awaitAll(savedSearch: List<SavedSearch>) {
|
||||||
|
try {
|
||||||
|
savedSearchRepository.insertAll(savedSearch)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR) { e.asLog() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package eu.kanade.domain.source.repository
|
||||||
|
|
||||||
|
import exh.savedsearches.models.FeedSavedSearch
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface FeedSavedSearchRepository {
|
||||||
|
|
||||||
|
suspend fun getGlobal(): List<FeedSavedSearch>
|
||||||
|
|
||||||
|
fun getGlobalAsFlow(): Flow<List<FeedSavedSearch>>
|
||||||
|
|
||||||
|
suspend fun getGlobalFeedSavedSearch(): List<SavedSearch>
|
||||||
|
|
||||||
|
suspend fun countGlobal(): Long
|
||||||
|
|
||||||
|
suspend fun getBySourceId(sourceId: Long): List<FeedSavedSearch>
|
||||||
|
|
||||||
|
fun getBySourceIdAsFlow(sourceId: Long): Flow<List<FeedSavedSearch>>
|
||||||
|
|
||||||
|
suspend fun getBySourceIdFeedSavedSearch(sourceId: Long): List<SavedSearch>
|
||||||
|
|
||||||
|
suspend fun countBySourceId(sourceId: Long): Long
|
||||||
|
|
||||||
|
suspend fun delete(feedSavedSearchId: Long)
|
||||||
|
|
||||||
|
suspend fun insert(feedSavedSearch: FeedSavedSearch): Long?
|
||||||
|
|
||||||
|
suspend fun insertAll(feedSavedSearch: List<FeedSavedSearch>)
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package eu.kanade.domain.source.repository
|
||||||
|
|
||||||
|
import exh.savedsearches.models.SavedSearch
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface SavedSearchRepository {
|
||||||
|
|
||||||
|
suspend fun getById(savedSearchId: Long): SavedSearch?
|
||||||
|
|
||||||
|
suspend fun getBySourceId(sourceId: Long): List<SavedSearch>
|
||||||
|
|
||||||
|
fun getBySourceIdAsFlow(sourceId: Long): Flow<List<SavedSearch>>
|
||||||
|
|
||||||
|
suspend fun delete(savedSearchId: Long)
|
||||||
|
|
||||||
|
suspend fun insert(savedSearch: SavedSearch): Long?
|
||||||
|
|
||||||
|
suspend fun insertAll(savedSearch: List<SavedSearch>)
|
||||||
|
}
|
@ -500,12 +500,11 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
|
|||||||
backupSavedSearches.filter { backupSavedSearch ->
|
backupSavedSearches.filter { backupSavedSearch ->
|
||||||
currentSavedSearches.none { it.source == backupSavedSearch.source && it.name == backupSavedSearch.name }
|
currentSavedSearches.none { it.source == backupSavedSearch.source && it.name == backupSavedSearch.name }
|
||||||
}.forEach {
|
}.forEach {
|
||||||
saved_searchQueries.insertSavedSearch(
|
saved_searchQueries.insert(
|
||||||
_id = null,
|
|
||||||
source = it.source,
|
source = it.source,
|
||||||
name = it.name,
|
name = it.name,
|
||||||
query = it.query.nullIfBlank(),
|
query = it.query.nullIfBlank(),
|
||||||
filters_json = it.filterList.nullIfBlank()
|
filtersJson = it.filterList.nullIfBlank()
|
||||||
?.takeUnless { it == "[]" },
|
?.takeUnless { it == "[]" },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ open class FeedController :
|
|||||||
|
|
||||||
private fun addFeedSearch(source: CatalogueSource) {
|
private fun addFeedSearch(source: CatalogueSource) {
|
||||||
viewScope.launchUI {
|
viewScope.launchUI {
|
||||||
val items = presenter.getSourceSavedSearches(source)
|
val items = presenter.getSourceSavedSearches(source.id)
|
||||||
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
|
||||||
|
|
||||||
|
@ -73,6 +73,6 @@ class FeedItem(
|
|||||||
* @return hashcode
|
* @return hashcode
|
||||||
*/
|
*/
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return feed.id!!.toInt()
|
return feed.id.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
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.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
|
import eu.kanade.domain.source.interactor.CountFeedSavedSearchGlobal
|
||||||
|
import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.GetFeedSavedSearchGlobal
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchGlobalFeed
|
||||||
|
import eu.kanade.domain.source.interactor.InsertFeedSavedSearch
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
@ -46,16 +49,20 @@ 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 handler 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 handler: DatabaseHandler = Injekt.get(),
|
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val insertManga: InsertManga = Injekt.get(),
|
private val insertManga: InsertManga = Injekt.get(),
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
|
private val getFeedSavedSearchGlobal: GetFeedSavedSearchGlobal = Injekt.get(),
|
||||||
|
private val getSavedSearchGlobalFeed: GetSavedSearchGlobalFeed = Injekt.get(),
|
||||||
|
private val countFeedSavedSearchGlobal: CountFeedSavedSearchGlobal = Injekt.get(),
|
||||||
|
private val getSavedSearchBySourceId: GetSavedSearchBySourceId = Injekt.get(),
|
||||||
|
private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(),
|
||||||
|
private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(),
|
||||||
) : BasePresenter<FeedController>() {
|
) : BasePresenter<FeedController>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,9 +83,9 @@ open class FeedPresenter(
|
|||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
handler.subscribeToList { feed_saved_searchQueries.selectAllGlobal() }
|
getFeedSavedSearchGlobal.subscribe()
|
||||||
.onEach {
|
.onEach {
|
||||||
getFeed()
|
getFeed(it)
|
||||||
}
|
}
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
}
|
}
|
||||||
@ -90,7 +97,7 @@ open class FeedPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun hasTooManyFeeds(): Boolean {
|
suspend fun hasTooManyFeeds(): Boolean {
|
||||||
return handler.awaitOne { feed_saved_searchQueries.countGlobal() } > 10
|
return countFeedSavedSearchGlobal.await() > 10
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEnabledSources(): List<CatalogueSource> {
|
fun getEnabledSources(): List<CatalogueSource> {
|
||||||
@ -104,36 +111,33 @@ open class FeedPresenter(
|
|||||||
return list.sortedBy { it.id.toString() !in pinnedSources }
|
return list.sortedBy { it.id.toString() !in pinnedSources }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSourceSavedSearches(source: CatalogueSource): List<SavedSearch> {
|
suspend fun getSourceSavedSearches(sourceId: Long): List<SavedSearch> {
|
||||||
return handler.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
|
return getSavedSearchBySourceId.await(sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFeed(source: CatalogueSource, savedSearch: SavedSearch?) {
|
fun createFeed(source: CatalogueSource, savedSearch: SavedSearch?) {
|
||||||
launchIO {
|
launchIO {
|
||||||
handler.await {
|
insertFeedSavedSearch.await(
|
||||||
feed_saved_searchQueries.insertFeedSavedSearch(
|
FeedSavedSearch(
|
||||||
_id = null,
|
id = -1,
|
||||||
source = source.id,
|
source = source.id,
|
||||||
saved_search = savedSearch?.id,
|
savedSearch = savedSearch?.id,
|
||||||
global = true,
|
global = true,
|
||||||
)
|
),
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteFeed(feed: FeedSavedSearch) {
|
fun deleteFeed(feed: FeedSavedSearch) {
|
||||||
launchIO {
|
launchIO {
|
||||||
handler.await {
|
deleteFeedSavedSearchById.await(feed.id)
|
||||||
feed_saved_searchQueries.deleteById(feed.id ?: return@await)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSourcesToGetFeed(): List<Pair<FeedSavedSearch, SavedSearch?>> {
|
private suspend fun getSourcesToGetFeed(feedSavedSearch: List<FeedSavedSearch>): List<Pair<FeedSavedSearch, SavedSearch?>> {
|
||||||
val savedSearches = handler.awaitList {
|
val savedSearches = getSavedSearchGlobalFeed.await()
|
||||||
feed_saved_searchQueries.selectGlobalFeedSavedSearch(savedSearchMapper)
|
.associateBy { it.id }
|
||||||
}.associateBy { it.id }
|
return feedSavedSearch
|
||||||
return handler.awaitList { feed_saved_searchQueries.selectAllGlobal(feedSavedSearchMapper) }
|
|
||||||
.map { it to savedSearches[it.savedSearch] }
|
.map { it to savedSearches[it.savedSearch] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,12 +156,12 @@ open class FeedPresenter(
|
|||||||
/**
|
/**
|
||||||
* Initiates get manga per feed.
|
* Initiates get manga per feed.
|
||||||
*/
|
*/
|
||||||
suspend fun getFeed() {
|
private suspend fun getFeed(feedSavedSearch: List<FeedSavedSearch>) {
|
||||||
// Create image fetch subscription
|
// Create image fetch subscription
|
||||||
initializeFetchImageSubscription()
|
initializeFetchImageSubscription()
|
||||||
|
|
||||||
// Create items with the initial state
|
// Create items with the initial state
|
||||||
val initialItems = getSourcesToGetFeed().map { (feed, savedSearch) ->
|
val initialItems = getSourcesToGetFeed(feedSavedSearch).map { (feed, savedSearch) ->
|
||||||
createCatalogueSearchItem(
|
createCatalogueSearchItem(
|
||||||
feed,
|
feed,
|
||||||
savedSearch,
|
savedSearch,
|
||||||
@ -168,7 +172,7 @@ open class FeedPresenter(
|
|||||||
var items = initialItems
|
var items = initialItems
|
||||||
|
|
||||||
fetchSourcesSubscription?.unsubscribe()
|
fetchSourcesSubscription?.unsubscribe()
|
||||||
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed())
|
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed(feedSavedSearch))
|
||||||
.flatMap(
|
.flatMap(
|
||||||
{ (feed, savedSearch) ->
|
{ (feed, savedSearch) ->
|
||||||
val source = sourceManager.get(feed.source) as? CatalogueSource
|
val source = sourceManager.get(feed.source) as? CatalogueSource
|
||||||
|
@ -2,8 +2,6 @@ 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.domain.category.interactor.GetCategories
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
import eu.kanade.domain.category.interactor.SetMangaCategories
|
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
@ -14,6 +12,9 @@ import eu.kanade.domain.manga.interactor.InsertManga
|
|||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
|
import eu.kanade.domain.source.interactor.DeleteSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||||
|
import eu.kanade.domain.source.interactor.InsertSavedSearch
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
import eu.kanade.domain.track.interactor.InsertTrack
|
||||||
import eu.kanade.domain.track.model.toDomainTrack
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
@ -48,12 +49,9 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem
|
|||||||
import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
|
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.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
|
||||||
import exh.log.xLogE
|
|
||||||
import exh.savedsearches.EXHSavedSearch
|
|
||||||
import exh.savedsearches.models.SavedSearch
|
import exh.savedsearches.models.SavedSearch
|
||||||
import exh.source.isEhBasedSource
|
import exh.source.isEhBasedSource
|
||||||
import exh.util.nullIfBlank
|
import exh.util.nullIfBlank
|
||||||
@ -90,7 +88,6 @@ open class BrowseSourcePresenter(
|
|||||||
private val savedSearch: Long? = null,
|
private val savedSearch: Long? = null,
|
||||||
// SY <--
|
// SY <--
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = 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(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
@ -102,6 +99,11 @@ open class BrowseSourcePresenter(
|
|||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
private val insertTrack: InsertTrack = Injekt.get(),
|
||||||
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(),
|
||||||
|
// SY -->
|
||||||
|
private val deleteSavedSearchById: DeleteSavedSearchById = Injekt.get(),
|
||||||
|
private val insertSavedSearch: InsertSavedSearch = Injekt.get(),
|
||||||
|
private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(),
|
||||||
|
// SY <--
|
||||||
) : BasePresenter<BrowseSourceController>() {
|
) : BasePresenter<BrowseSourceController>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,18 +163,12 @@ open class BrowseSourcePresenter(
|
|||||||
val savedSearchFilters = savedSearch
|
val savedSearchFilters = savedSearch
|
||||||
val jsonFilters = filters
|
val jsonFilters = filters
|
||||||
if (savedSearchFilters != null) {
|
if (savedSearchFilters != null) {
|
||||||
runCatching {
|
val savedSearch = runBlocking { getExhSavedSearch.awaitOne(savedSearchFilters) { sourceFilters } }
|
||||||
val savedSearch = runBlocking {
|
if (savedSearch != null) {
|
||||||
database.awaitOneOrNull {
|
query = savedSearch.query
|
||||||
saved_searchQueries.selectById(savedSearchFilters, savedSearchMapper)
|
if (savedSearch.filterList != null) {
|
||||||
}
|
appliedFilters = savedSearch.filterList
|
||||||
} ?: return@runCatching
|
}
|
||||||
query = savedSearch.query.orEmpty()
|
|
||||||
val filtersJson = savedSearch.filtersJson
|
|
||||||
?: return@runCatching
|
|
||||||
val filters = Json.decodeFromString<JsonArray>(filtersJson)
|
|
||||||
filterSerializer.deserialize(sourceFilters, filters)
|
|
||||||
appliedFilters = sourceFilters
|
|
||||||
}
|
}
|
||||||
} else if (jsonFilters != null) {
|
} else if (jsonFilters != null) {
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -182,8 +178,7 @@ open class BrowseSourcePresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
database.subscribeToList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
|
getExhSavedSearch.subscribe(source.id, source::getFilterList)
|
||||||
.map { loadSearches(it) }
|
|
||||||
.onEach {
|
.onEach {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
view?.setSavedSearches(it)
|
view?.setSavedSearches(it)
|
||||||
@ -529,92 +524,28 @@ open class BrowseSourcePresenter(
|
|||||||
// EXH -->
|
// EXH -->
|
||||||
fun saveSearch(name: String, query: String, filterList: FilterList) {
|
fun saveSearch(name: String, query: String, filterList: FilterList) {
|
||||||
launchIO {
|
launchIO {
|
||||||
kotlin.runCatching {
|
insertSavedSearch.await(
|
||||||
database.await {
|
SavedSearch(
|
||||||
saved_searchQueries.insertSavedSearch(
|
id = -1,
|
||||||
_id = null,
|
source = source.id,
|
||||||
source = source.id,
|
name = name.trim(),
|
||||||
name = name.trim(),
|
query = query.nullIfBlank(),
|
||||||
query = query.nullIfBlank(),
|
filtersJson = runCatching { filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) } }.getOrNull(),
|
||||||
filters_json = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) },
|
),
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteSearch(searchId: Long) {
|
|
||||||
launchIO {
|
|
||||||
database.await { saved_searchQueries.deleteById(searchId) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
|
|
||||||
return withIOContext {
|
|
||||||
val search = database.awaitOneOrNull {
|
|
||||||
saved_searchQueries.selectById(searchId, savedSearchMapper)
|
|
||||||
} ?: return@withIOContext null
|
|
||||||
EXHSavedSearch(
|
|
||||||
id = search.id!!,
|
|
||||||
name = search.name,
|
|
||||||
query = search.query.orEmpty(),
|
|
||||||
filterList = runCatching {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(
|
|
||||||
filters = originalFilters,
|
|
||||||
json = search.filtersJson
|
|
||||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
|
||||||
?: return@runCatching null,
|
|
||||||
)
|
|
||||||
originalFilters
|
|
||||||
}.getOrNull(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadSearches(searches: List<SavedSearch>? = null): List<EXHSavedSearch> {
|
fun deleteSearch(savedSearchId: Long) {
|
||||||
return withIOContext {
|
launchIO {
|
||||||
(searches ?: (database.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }))
|
deleteSavedSearchById.await(savedSearchId)
|
||||||
.map {
|
|
||||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null,
|
|
||||||
)
|
|
||||||
val filters = try {
|
|
||||||
Json.decodeFromString<JsonArray>(filtersJson)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
xLogE("Failed to load saved search!", e)
|
|
||||||
null
|
|
||||||
} ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(originalFilters, 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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun loadSearch(searchId: Long) =
|
||||||
|
getExhSavedSearch.awaitOne(searchId, source::getFilterList)
|
||||||
|
|
||||||
|
suspend fun loadSearches() =
|
||||||
|
getExhSavedSearch.await(source.id, source::getFilterList)
|
||||||
// EXH <--
|
// EXH <--
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,18 @@ 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.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.InsertManga
|
import eu.kanade.domain.manga.interactor.InsertManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toDbManga
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.domain.manga.model.toMangaUpdate
|
import eu.kanade.domain.manga.model.toMangaUpdate
|
||||||
|
import eu.kanade.domain.source.interactor.CountFeedSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.DeleteFeedSavedSearchById
|
||||||
|
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||||
|
import eu.kanade.domain.source.interactor.GetFeedSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceId
|
||||||
|
import eu.kanade.domain.source.interactor.GetSavedSearchBySourceIdFeed
|
||||||
|
import eu.kanade.domain.source.interactor.InsertFeedSavedSearch
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
@ -24,19 +28,14 @@ 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.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.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
@ -63,11 +62,17 @@ sealed class SourceFeed {
|
|||||||
*/
|
*/
|
||||||
open class SourceFeedPresenter(
|
open class SourceFeedPresenter(
|
||||||
val source: CatalogueSource,
|
val source: CatalogueSource,
|
||||||
val handler: DatabaseHandler = Injekt.get(),
|
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val insertManga: InsertManga = Injekt.get(),
|
private val insertManga: InsertManga = Injekt.get(),
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
|
private val getFeedSavedSearchBySourceId: GetFeedSavedSearchBySourceId = Injekt.get(),
|
||||||
|
private val getSavedSearchBySourceIdFeed: GetSavedSearchBySourceIdFeed = Injekt.get(),
|
||||||
|
private val countFeedSavedSearchBySourceId: CountFeedSavedSearchBySourceId = Injekt.get(),
|
||||||
|
private val getSavedSearchBySourceId: GetSavedSearchBySourceId = Injekt.get(),
|
||||||
|
private val insertFeedSavedSearch: InsertFeedSavedSearch = Injekt.get(),
|
||||||
|
private val deleteFeedSavedSearchById: DeleteFeedSavedSearchById = Injekt.get(),
|
||||||
|
private val getExhSavedSearch: GetExhSavedSearch = Injekt.get(),
|
||||||
) : BasePresenter<SourceFeedController>() {
|
) : BasePresenter<SourceFeedController>() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,9 +110,9 @@ open class SourceFeedPresenter(
|
|||||||
|
|
||||||
sourceFilters = source.getFilterList()
|
sourceFilters = source.getFilterList()
|
||||||
|
|
||||||
handler.subscribeToList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
|
getFeedSavedSearchBySourceId.subscribe(source.id)
|
||||||
.onEach {
|
.onEach {
|
||||||
getFeed()
|
getFeed(it)
|
||||||
}
|
}
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
}
|
}
|
||||||
@ -119,46 +124,42 @@ open class SourceFeedPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun hasTooManyFeeds(): Boolean {
|
suspend fun hasTooManyFeeds(): Boolean {
|
||||||
return withIOContext {
|
return countFeedSavedSearchBySourceId.await(source.id) > 10
|
||||||
handler.awaitList {
|
|
||||||
feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id)
|
|
||||||
}.size > 10
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSourceSavedSearches(): List<SavedSearch> {
|
suspend fun getSourceSavedSearches(): List<SavedSearch> {
|
||||||
return handler.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
|
return getSavedSearchBySourceId.await(source.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFeed(savedSearchId: Long) {
|
fun createFeed(savedSearchId: Long) {
|
||||||
launchIO {
|
launchIO {
|
||||||
handler.await {
|
insertFeedSavedSearch.await(
|
||||||
feed_saved_searchQueries.insertFeedSavedSearch(
|
FeedSavedSearch(
|
||||||
_id = null,
|
id = -1,
|
||||||
source = source.id,
|
source = source.id,
|
||||||
saved_search = savedSearchId,
|
savedSearch = savedSearchId,
|
||||||
global = false,
|
global = false,
|
||||||
)
|
),
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteFeed(feed: FeedSavedSearch) {
|
fun deleteFeed(feed: FeedSavedSearch) {
|
||||||
launchIO {
|
launchIO {
|
||||||
handler.await { feed_saved_searchQueries.deleteById(feed.id ?: return@await) }
|
deleteFeedSavedSearchById.await(feed.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSourcesToGetFeed(): List<SourceFeed> {
|
private suspend fun getSourcesToGetFeed(feedSavedSearch: List<FeedSavedSearch>): List<SourceFeed> {
|
||||||
val savedSearches = handler.awaitList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
|
val savedSearches = getSavedSearchBySourceIdFeed.await(source.id)
|
||||||
.associateBy { it.id!! }
|
.associateBy { it.id }
|
||||||
|
|
||||||
return listOfNotNull(
|
return listOfNotNull(
|
||||||
if (source.supportsLatest) {
|
if (source.supportsLatest) {
|
||||||
SourceFeed.Latest
|
SourceFeed.Latest
|
||||||
} else null,
|
} else null,
|
||||||
SourceFeed.Browse,
|
SourceFeed.Browse,
|
||||||
) + handler.awaitList { feed_saved_searchQueries.selectBySource(source.id, feedSavedSearchMapper) }
|
) + feedSavedSearch
|
||||||
.map { SourceFeed.SourceSavedSearch(it, savedSearches[it.savedSearch]!!) }
|
.map { SourceFeed.SourceSavedSearch(it, savedSearches[it.savedSearch]!!) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,12 +176,12 @@ open class SourceFeedPresenter(
|
|||||||
/**
|
/**
|
||||||
* Initiates get manga per feed.
|
* Initiates get manga per feed.
|
||||||
*/
|
*/
|
||||||
suspend fun getFeed() {
|
private suspend fun getFeed(feedSavedSearch: List<FeedSavedSearch>) {
|
||||||
// Create image fetch subscription
|
// Create image fetch subscription
|
||||||
initializeFetchImageSubscription()
|
initializeFetchImageSubscription()
|
||||||
|
|
||||||
// Create items with the initial state
|
// Create items with the initial state
|
||||||
val initialItems = getSourcesToGetFeed().map {
|
val initialItems = getSourcesToGetFeed(feedSavedSearch).map {
|
||||||
createCatalogueSearchItem(
|
createCatalogueSearchItem(
|
||||||
it,
|
it,
|
||||||
null,
|
null,
|
||||||
@ -189,7 +190,7 @@ open class SourceFeedPresenter(
|
|||||||
var items = initialItems
|
var items = initialItems
|
||||||
|
|
||||||
fetchSourcesSubscription?.unsubscribe()
|
fetchSourcesSubscription?.unsubscribe()
|
||||||
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed())
|
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed(feedSavedSearch))
|
||||||
.flatMap(
|
.flatMap(
|
||||||
{ sourceFeed ->
|
{ sourceFeed ->
|
||||||
Observable.defer {
|
Observable.defer {
|
||||||
@ -323,70 +324,9 @@ open class SourceFeedPresenter(
|
|||||||
return localManga?.toDbManga()!!
|
return localManga?.toDbManga()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
|
suspend fun loadSearch(searchId: Long) =
|
||||||
return withIOContext {
|
getExhSavedSearch.awaitOne(searchId, source::getFilterList)
|
||||||
val search = handler.awaitOneOrNull {
|
|
||||||
saved_searchQueries.selectById(searchId, savedSearchMapper)
|
|
||||||
} ?: return@withIOContext null
|
|
||||||
EXHSavedSearch(
|
|
||||||
id = search.id!!,
|
|
||||||
name = search.name,
|
|
||||||
query = search.query.orEmpty(),
|
|
||||||
filterList = runCatching {
|
|
||||||
val originalFilters = source.getFilterList()
|
|
||||||
filterSerializer.deserialize(
|
|
||||||
filters = originalFilters,
|
|
||||||
json = search.filtersJson
|
|
||||||
?.let { Json.decodeFromString<JsonArray>(it) }
|
|
||||||
?: return@runCatching null,
|
|
||||||
)
|
|
||||||
originalFilters
|
|
||||||
}.getOrNull(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun loadSearches(): List<EXHSavedSearch> {
|
suspend fun loadSearches() =
|
||||||
return withIOContext {
|
getExhSavedSearch.await(source.id, source::getFilterList)
|
||||||
handler.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }.map {
|
|
||||||
val filtersJson = it.filtersJson ?: return@map EXHSavedSearch(
|
|
||||||
id = it.id!!,
|
|
||||||
name = it.name,
|
|
||||||
query = it.query.orEmpty(),
|
|
||||||
filterList = null,
|
|
||||||
)
|
|
||||||
val filters = try {
|
|
||||||
Json.decodeFromString<JsonArray>(filtersJson)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ import eu.kanade.domain.manga.interactor.GetMangaBySource
|
|||||||
import eu.kanade.domain.manga.interactor.InsertMergedReference
|
import eu.kanade.domain.manga.interactor.InsertMergedReference
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.MangaUpdate
|
import eu.kanade.domain.manga.model.MangaUpdate
|
||||||
|
import eu.kanade.domain.source.interactor.InsertFeedSavedSearch
|
||||||
|
import eu.kanade.domain.source.interactor.InsertSavedSearch
|
||||||
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.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
@ -40,6 +42,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|||||||
import exh.eh.EHentaiUpdateWorker
|
import exh.eh.EHentaiUpdateWorker
|
||||||
import exh.log.xLogE
|
import exh.log.xLogE
|
||||||
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
|
||||||
@ -76,6 +80,8 @@ object EXHMigrations {
|
|||||||
private val updateChapter: UpdateChapter by injectLazy()
|
private val updateChapter: UpdateChapter by injectLazy()
|
||||||
private val deleteChapters: DeleteChapters by injectLazy()
|
private val deleteChapters: DeleteChapters by injectLazy()
|
||||||
private val insertMergedReference: InsertMergedReference by injectLazy()
|
private val insertMergedReference: InsertMergedReference by injectLazy()
|
||||||
|
private val insertSavedSearch: InsertSavedSearch by injectLazy()
|
||||||
|
private val insertFeedSavedSearch: InsertFeedSavedSearch by injectLazy()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a migration when the application is updated.
|
* Performs a migration when the application is updated.
|
||||||
@ -360,29 +366,32 @@ object EXHMigrations {
|
|||||||
}
|
}
|
||||||
if (oldVersion under 31) {
|
if (oldVersion under 31) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
handler.await(true) {
|
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
|
||||||
prefs.getStringSet("eh_saved_searches", emptySet())?.forEach {
|
runCatching {
|
||||||
kotlin.runCatching {
|
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
||||||
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
|
SavedSearch(
|
||||||
saved_searchQueries.insertSavedSearch(
|
id = -1,
|
||||||
_id = null,
|
source = it.substringBefore(':').toLongOrNull()
|
||||||
source = it.substringBefore(':').toLongOrNull() ?: return@forEach,
|
?: return@runCatching null,
|
||||||
name = content["name"]!!.jsonPrimitive.content,
|
name = content["name"]!!.jsonPrimitive.content,
|
||||||
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
|
||||||
filters_json = Json.encodeToString(content["filters"]!!.jsonArray),
|
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handler.await(true) {
|
|
||||||
prefs.getStringSet("latest_tab_sources", emptySet())?.forEach {
|
|
||||||
feed_saved_searchQueries.insertFeedSavedSearch(
|
|
||||||
_id = null,
|
|
||||||
source = it.toLong(),
|
|
||||||
saved_search = null,
|
|
||||||
global = true,
|
|
||||||
)
|
)
|
||||||
}
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
if (!savedSearch.isNullOrEmpty()) {
|
||||||
|
insertSavedSearch.awaitAll(savedSearch)
|
||||||
|
}
|
||||||
|
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
|
||||||
|
FeedSavedSearch(
|
||||||
|
id = -1,
|
||||||
|
source = it.toLong(),
|
||||||
|
savedSearch = null,
|
||||||
|
global = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!feedSavedSearch.isNullOrEmpty()) {
|
||||||
|
insertFeedSavedSearch.awaitAll(feedSavedSearch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefs.edit(commit = true) {
|
prefs.edit(commit = true) {
|
||||||
|
@ -3,11 +3,11 @@ package exh.favorites
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import eu.kanade.data.AndroidDatabaseHandler
|
|
||||||
import eu.kanade.data.DatabaseHandler
|
import eu.kanade.data.DatabaseHandler
|
||||||
import eu.kanade.domain.category.interactor.GetCategories
|
import eu.kanade.domain.category.interactor.GetCategories
|
||||||
import eu.kanade.domain.category.interactor.SetMangaCategories
|
import eu.kanade.domain.category.interactor.SetMangaCategories
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
|
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.GetManga
|
import eu.kanade.domain.manga.interactor.GetManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.Manga
|
import eu.kanade.domain.manga.model.Manga
|
||||||
@ -49,6 +49,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
// TODO only apply database changes after sync
|
// TODO only apply database changes after sync
|
||||||
class FavoritesSyncHelper(val context: Context) {
|
class FavoritesSyncHelper(val context: Context) {
|
||||||
private val handler: DatabaseHandler by injectLazy()
|
private val handler: DatabaseHandler by injectLazy()
|
||||||
|
private val getLibraryManga: GetLibraryManga by injectLazy()
|
||||||
private val getCategories: GetCategories by injectLazy()
|
private val getCategories: GetCategories by injectLazy()
|
||||||
private val getManga: GetManga by injectLazy()
|
private val getManga: GetManga by injectLazy()
|
||||||
private val updateManga: UpdateManga by injectLazy()
|
private val updateManga: UpdateManga by injectLazy()
|
||||||
@ -96,7 +97,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Validate library state
|
// Validate library state
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_verifying_library), context = context)
|
||||||
val libraryManga = handler.awaitList { (handler as AndroidDatabaseHandler).getLibraryQuery() }
|
val libraryManga = getLibraryManga.await()
|
||||||
val seenManga = HashSet<Long>(libraryManga.size)
|
val seenManga = HashSet<Long>(libraryManga.size)
|
||||||
libraryManga.forEach {
|
libraryManga.forEach {
|
||||||
if (!it.isEhBasedManga()) return@forEach
|
if (!it.isEhBasedManga()) return@forEach
|
||||||
|
@ -2,14 +2,14 @@ package exh.savedsearches.models
|
|||||||
|
|
||||||
data class FeedSavedSearch(
|
data class FeedSavedSearch(
|
||||||
// Tag identifier, unique
|
// Tag identifier, unique
|
||||||
var id: Long?,
|
val id: Long,
|
||||||
|
|
||||||
// Source for the saved search
|
// Source for the saved search
|
||||||
var source: Long,
|
val source: Long,
|
||||||
|
|
||||||
// If -1 then get latest, if set get the saved search
|
// If -1 then get latest, if set get the saved search
|
||||||
var savedSearch: Long?,
|
val savedSearch: Long?,
|
||||||
|
|
||||||
// If the feed is a global or source specific feed
|
// If the feed is a global or source specific feed
|
||||||
var global: Boolean,
|
val global: Boolean,
|
||||||
)
|
)
|
||||||
|
@ -2,17 +2,17 @@ package exh.savedsearches.models
|
|||||||
|
|
||||||
data class SavedSearch(
|
data class SavedSearch(
|
||||||
// Tag identifier, unique
|
// Tag identifier, unique
|
||||||
var id: Long?,
|
val id: Long,
|
||||||
|
|
||||||
// The source the saved search is for
|
// The source the saved search is for
|
||||||
var source: Long,
|
val source: Long,
|
||||||
|
|
||||||
// If false the manga will not grab chapter updates
|
// If false the manga will not grab chapter updates
|
||||||
var name: String,
|
val name: String,
|
||||||
|
|
||||||
// The query if there is any
|
// The query if there is any
|
||||||
var query: String?,
|
val query: String?,
|
||||||
|
|
||||||
// The filter list
|
// The filter list
|
||||||
var filtersJson: String?,
|
val filtersJson: String?,
|
||||||
)
|
)
|
||||||
|
@ -16,13 +16,13 @@ countGlobal:
|
|||||||
SELECT count(*) FROM feed_saved_search WHERE global = 1;
|
SELECT count(*) FROM feed_saved_search WHERE global = 1;
|
||||||
|
|
||||||
selectBySource:
|
selectBySource:
|
||||||
SELECT * FROM feed_saved_search WHERE source = ? AND global = 0;
|
SELECT * FROM feed_saved_search WHERE source = :sourceId AND global = 0;
|
||||||
|
|
||||||
insertFeedSavedSearch:
|
insert:
|
||||||
INSERT INTO feed_saved_search (_id, source, saved_search, global) VALUES (?, ?, ?, ?);
|
INSERT INTO feed_saved_search (source, saved_search, global) VALUES (:sourceId, :savedSearch, :global);
|
||||||
|
|
||||||
deleteById:
|
deleteById:
|
||||||
DELETE FROM feed_saved_search WHERE _id = ?;
|
DELETE FROM feed_saved_search WHERE _id = :id;
|
||||||
|
|
||||||
deleteAll:
|
deleteAll:
|
||||||
DELETE FROM feed_saved_search;
|
DELETE FROM feed_saved_search;
|
||||||
@ -38,7 +38,7 @@ ON saved_search._id = M.saved_search;
|
|||||||
selectSourceFeedSavedSearch:
|
selectSourceFeedSavedSearch:
|
||||||
SELECT saved_search.*
|
SELECT saved_search.*
|
||||||
FROM (
|
FROM (
|
||||||
SELECT saved_search FROM feed_saved_search WHERE global = 0 AND source = ?
|
SELECT saved_search FROM feed_saved_search WHERE global = 0 AND source = :sourceId
|
||||||
) AS M
|
) AS M
|
||||||
JOIN saved_search
|
JOIN saved_search
|
||||||
ON saved_search._id = M.saved_search;
|
ON saved_search._id = M.saved_search;
|
||||||
@ -46,7 +46,10 @@ ON saved_search._id = M.saved_search;
|
|||||||
countSourceFeedSavedSearch:
|
countSourceFeedSavedSearch:
|
||||||
SELECT count(*)
|
SELECT count(*)
|
||||||
FROM (
|
FROM (
|
||||||
SELECT saved_search FROM feed_saved_search WHERE global = 0 AND source = ?
|
SELECT saved_search FROM feed_saved_search WHERE global = 0 AND source = :sourceId
|
||||||
) AS M
|
) AS M
|
||||||
JOIN saved_search
|
JOIN saved_search
|
||||||
ON saved_search._id = M.saved_search;
|
ON saved_search._id = M.saved_search;
|
||||||
|
|
||||||
|
selectLastInsertedRowId:
|
||||||
|
SELECT last_insert_rowid();
|
@ -7,30 +7,33 @@ CREATE TABLE saved_search(
|
|||||||
);
|
);
|
||||||
|
|
||||||
selectBySource:
|
selectBySource:
|
||||||
SELECT * FROM saved_search WHERE source = ?;
|
SELECT * FROM saved_search WHERE source = :sourceId;
|
||||||
|
|
||||||
deleteBySource:
|
deleteBySource:
|
||||||
DELETE FROM saved_search WHERE source = ?;
|
DELETE FROM saved_search WHERE source = :sourceId;
|
||||||
|
|
||||||
selectAll:
|
selectAll:
|
||||||
SELECT * FROM saved_search;
|
SELECT * FROM saved_search;
|
||||||
|
|
||||||
selectById:
|
selectById:
|
||||||
SELECT * FROM saved_search WHERE _id = ?;
|
SELECT * FROM saved_search WHERE _id = :id;
|
||||||
|
|
||||||
selectByIds:
|
selectByIds:
|
||||||
SELECT * FROM saved_search WHERE _id IN ?;
|
SELECT * FROM saved_search WHERE _id IN :ids;
|
||||||
|
|
||||||
selectNamesAndSources:
|
selectNamesAndSources:
|
||||||
SELECT source, name
|
SELECT source, name
|
||||||
FROM saved_search;
|
FROM saved_search;
|
||||||
|
|
||||||
insertSavedSearch:
|
insert:
|
||||||
INSERT INTO saved_search (_id, source, name, query, filters_json)
|
INSERT INTO saved_search (source, name, query, filters_json)
|
||||||
VALUES (?, ?, ?, ?, ?);
|
VALUES (:source, :name, :query, :filtersJson);
|
||||||
|
|
||||||
deleteById:
|
deleteById:
|
||||||
DELETE FROM saved_search WHERE _id = ?;
|
DELETE FROM saved_search WHERE _id = :id;
|
||||||
|
|
||||||
deleteAll:
|
deleteAll:
|
||||||
DELETE FROM saved_search;
|
DELETE FROM saved_search;
|
||||||
|
|
||||||
|
selectLastInsertedRowId:
|
||||||
|
SELECT last_insert_rowid();
|
Loading…
x
Reference in New Issue
Block a user