Use domain layer for saved searches

This commit is contained in:
Jobobby04 2022-07-04 18:10:43 -04:00
parent 485e6719c3
commit 141b0477e7
30 changed files with 654 additions and 278 deletions

View File

@ -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,
)
}
}
}
}

View File

@ -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,
)
}
}
}
}

View File

@ -3,6 +3,8 @@ package eu.kanade.domain
import eu.kanade.data.manga.FavoritesEntryRepositoryImpl
import eu.kanade.data.manga.MangaMergeRepositoryImpl
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.GetMergedChapterByMangaId
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.GetMergedMangaForDownloading
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.GetSearchTitles
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.MangaMergeRepository
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.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.ToggleExcludeFromDataSaver
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.InjektRegistrar
import uy.kohesive.injekt.api.addFactory
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
import xyz.nulldev.ts.api.http.serializer.FilterSerializer
class SYDomainModule : InjektModule {
@ -53,11 +72,13 @@ class SYDomainModule : InjektModule {
addFactory { GetMangaBySource(get()) }
addFactory { DeleteChapters(get()) }
addFactory { DeleteMangaById(get()) }
addFactory { FilterSerializer() }
addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) }
addFactory { GetFlatMetadataById(get()) }
addFactory { InsertFlatMetadata(get()) }
addFactory { GetExhFavoriteMangaWithMetadata(get()) }
addFactory { GetSearchMetadata(get()) }
addFactory { GetSearchTags(get()) }
addFactory { GetSearchTitles(get()) }
addFactory { GetIdsOfFavoriteMangaWithMetadata(get()) }
@ -77,5 +98,22 @@ class SYDomainModule : InjektModule {
addFactory { GetFavoriteEntries(get()) }
addFactory { InsertFavoriteEntries(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()) }
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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() }
}
}
}

View File

@ -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() }
}
}
}

View File

@ -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>)
}

View File

@ -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>)
}

View File

@ -500,12 +500,11 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
backupSavedSearches.filter { backupSavedSearch ->
currentSavedSearches.none { it.source == backupSavedSearch.source && it.name == backupSavedSearch.name }
}.forEach {
saved_searchQueries.insertSavedSearch(
_id = null,
saved_searchQueries.insert(
source = it.source,
name = it.name,
query = it.query.nullIfBlank(),
filters_json = it.filterList.nullIfBlank()
filtersJson = it.filterList.nullIfBlank()
?.takeUnless { it == "[]" },
)
}

View File

@ -91,7 +91,7 @@ open class FeedController :
private fun addFeedSearch(source: CatalogueSource) {
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 }
var selectedIndex = 0

View File

@ -73,6 +73,6 @@ class FeedItem(
* @return hashcode
*/
override fun hashCode(): Int {
return feed.id!!.toInt()
return feed.id.toInt()
}
}

View File

@ -1,14 +1,17 @@
package eu.kanade.tachiyomi.ui.browse.feed
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.InsertManga
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDbManga
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.toDomainManga
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.
*
* @param sourceManager manages the different sources.
* @param handler manages the database calls.
* @param preferences manages the preference calls.
*/
open class FeedPresenter(
val sourceManager: SourceManager = Injekt.get(),
val handler: DatabaseHandler = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val insertManga: InsertManga = 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>() {
/**
@ -76,9 +83,9 @@ open class FeedPresenter(
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
handler.subscribeToList { feed_saved_searchQueries.selectAllGlobal() }
getFeedSavedSearchGlobal.subscribe()
.onEach {
getFeed()
getFeed(it)
}
.launchIn(presenterScope)
}
@ -90,7 +97,7 @@ open class FeedPresenter(
}
suspend fun hasTooManyFeeds(): Boolean {
return handler.awaitOne { feed_saved_searchQueries.countGlobal() } > 10
return countFeedSavedSearchGlobal.await() > 10
}
fun getEnabledSources(): List<CatalogueSource> {
@ -104,36 +111,33 @@ open class FeedPresenter(
return list.sortedBy { it.id.toString() !in pinnedSources }
}
suspend fun getSourceSavedSearches(source: CatalogueSource): List<SavedSearch> {
return handler.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
suspend fun getSourceSavedSearches(sourceId: Long): List<SavedSearch> {
return getSavedSearchBySourceId.await(sourceId)
}
fun createFeed(source: CatalogueSource, savedSearch: SavedSearch?) {
launchIO {
handler.await {
feed_saved_searchQueries.insertFeedSavedSearch(
_id = null,
insertFeedSavedSearch.await(
FeedSavedSearch(
id = -1,
source = source.id,
saved_search = savedSearch?.id,
savedSearch = savedSearch?.id,
global = true,
),
)
}
}
}
fun deleteFeed(feed: FeedSavedSearch) {
launchIO {
handler.await {
feed_saved_searchQueries.deleteById(feed.id ?: return@await)
}
deleteFeedSavedSearchById.await(feed.id)
}
}
private suspend fun getSourcesToGetFeed(): List<Pair<FeedSavedSearch, SavedSearch?>> {
val savedSearches = handler.awaitList {
feed_saved_searchQueries.selectGlobalFeedSavedSearch(savedSearchMapper)
}.associateBy { it.id }
return handler.awaitList { feed_saved_searchQueries.selectAllGlobal(feedSavedSearchMapper) }
private suspend fun getSourcesToGetFeed(feedSavedSearch: List<FeedSavedSearch>): List<Pair<FeedSavedSearch, SavedSearch?>> {
val savedSearches = getSavedSearchGlobalFeed.await()
.associateBy { it.id }
return feedSavedSearch
.map { it to savedSearches[it.savedSearch] }
}
@ -152,12 +156,12 @@ open class FeedPresenter(
/**
* Initiates get manga per feed.
*/
suspend fun getFeed() {
private suspend fun getFeed(feedSavedSearch: List<FeedSavedSearch>) {
// Create image fetch subscription
initializeFetchImageSubscription()
// Create items with the initial state
val initialItems = getSourcesToGetFeed().map { (feed, savedSearch) ->
val initialItems = getSourcesToGetFeed(feedSavedSearch).map { (feed, savedSearch) ->
createCatalogueSearchItem(
feed,
savedSearch,
@ -168,7 +172,7 @@ open class FeedPresenter(
var items = initialItems
fetchSourcesSubscription?.unsubscribe()
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed())
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed(feedSavedSearch))
.flatMap(
{ (feed, savedSearch) ->
val source = sourceManager.get(feed.source) as? CatalogueSource

View File

@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
import android.os.Bundle
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.SetMangaCategories
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.model.toDbManga
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.model.toDomainTrack
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.util.chapter.ChapterSettingsHelper
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.removeCovers
import eu.kanade.tachiyomi.util.system.logcat
import exh.log.xLogE
import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.models.SavedSearch
import exh.source.isEhBasedSource
import exh.util.nullIfBlank
@ -90,7 +88,6 @@ open class BrowseSourcePresenter(
private val savedSearch: Long? = null,
// SY <--
private val sourceManager: SourceManager = Injekt.get(),
private val database: DatabaseHandler = Injekt.get(),
private val prefs: PreferencesHelper = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
@ -102,6 +99,11 @@ open class BrowseSourcePresenter(
private val updateManga: UpdateManga = Injekt.get(),
private val insertTrack: InsertTrack = 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>() {
/**
@ -161,18 +163,12 @@ open class BrowseSourcePresenter(
val savedSearchFilters = savedSearch
val jsonFilters = filters
if (savedSearchFilters != null) {
runCatching {
val savedSearch = runBlocking {
database.awaitOneOrNull {
saved_searchQueries.selectById(savedSearchFilters, savedSearchMapper)
val savedSearch = runBlocking { getExhSavedSearch.awaitOne(savedSearchFilters) { sourceFilters } }
if (savedSearch != null) {
query = savedSearch.query
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) {
runCatching {
@ -182,8 +178,7 @@ open class BrowseSourcePresenter(
}
}
database.subscribeToList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
.map { loadSearches(it) }
getExhSavedSearch.subscribe(source.id, source::getFilterList)
.onEach {
withUIContext {
view?.setSavedSearches(it)
@ -529,92 +524,28 @@ open class BrowseSourcePresenter(
// EXH -->
fun saveSearch(name: String, query: String, filterList: FilterList) {
launchIO {
kotlin.runCatching {
database.await {
saved_searchQueries.insertSavedSearch(
_id = null,
insertSavedSearch.await(
SavedSearch(
id = -1,
source = source.id,
name = name.trim(),
query = query.nullIfBlank(),
filters_json = filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) },
filtersJson = runCatching { filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) } }.getOrNull(),
),
)
}
}
}
}
fun deleteSearch(searchId: Long) {
fun deleteSearch(savedSearchId: Long) {
launchIO {
database.await { saved_searchQueries.deleteById(searchId) }
deleteSavedSearchById.await(savedSearchId)
}
}
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 loadSearch(searchId: Long) =
getExhSavedSearch.awaitOne(searchId, source::getFilterList)
suspend fun loadSearches(searches: List<SavedSearch>? = null): List<EXHSavedSearch> {
return withIOContext {
(searches ?: (database.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) {
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 loadSearches() =
getExhSavedSearch.await(source.id, source::getFilterList)
// EXH <--
}

View File

@ -2,14 +2,18 @@ package eu.kanade.tachiyomi.ui.browse.source.feed
import android.os.Bundle
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.InsertManga
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDbManga
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.toDomainManga
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.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.logcat
import exh.log.xLogE
import exh.savedsearches.EXHSavedSearch
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import logcat.LogPriority
import rx.Observable
import rx.Subscription
@ -63,11 +62,17 @@ sealed class SourceFeed {
*/
open class SourceFeedPresenter(
val source: CatalogueSource,
val handler: DatabaseHandler = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val insertManga: InsertManga = 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>() {
/**
@ -105,9 +110,9 @@ open class SourceFeedPresenter(
sourceFilters = source.getFilterList()
handler.subscribeToList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
getFeedSavedSearchBySourceId.subscribe(source.id)
.onEach {
getFeed()
getFeed(it)
}
.launchIn(presenterScope)
}
@ -119,46 +124,42 @@ open class SourceFeedPresenter(
}
suspend fun hasTooManyFeeds(): Boolean {
return withIOContext {
handler.awaitList {
feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id)
}.size > 10
}
return countFeedSavedSearchBySourceId.await(source.id) > 10
}
suspend fun getSourceSavedSearches(): List<SavedSearch> {
return handler.awaitList { saved_searchQueries.selectBySource(source.id, savedSearchMapper) }
return getSavedSearchBySourceId.await(source.id)
}
fun createFeed(savedSearchId: Long) {
launchIO {
handler.await {
feed_saved_searchQueries.insertFeedSavedSearch(
_id = null,
insertFeedSavedSearch.await(
FeedSavedSearch(
id = -1,
source = source.id,
saved_search = savedSearchId,
savedSearch = savedSearchId,
global = false,
),
)
}
}
}
fun deleteFeed(feed: FeedSavedSearch) {
launchIO {
handler.await { feed_saved_searchQueries.deleteById(feed.id ?: return@await) }
deleteFeedSavedSearchById.await(feed.id)
}
}
private suspend fun getSourcesToGetFeed(): List<SourceFeed> {
val savedSearches = handler.awaitList { feed_saved_searchQueries.selectSourceFeedSavedSearch(source.id, savedSearchMapper) }
.associateBy { it.id!! }
private suspend fun getSourcesToGetFeed(feedSavedSearch: List<FeedSavedSearch>): List<SourceFeed> {
val savedSearches = getSavedSearchBySourceIdFeed.await(source.id)
.associateBy { it.id }
return listOfNotNull(
if (source.supportsLatest) {
SourceFeed.Latest
} else null,
SourceFeed.Browse,
) + handler.awaitList { feed_saved_searchQueries.selectBySource(source.id, feedSavedSearchMapper) }
) + feedSavedSearch
.map { SourceFeed.SourceSavedSearch(it, savedSearches[it.savedSearch]!!) }
}
@ -175,12 +176,12 @@ open class SourceFeedPresenter(
/**
* Initiates get manga per feed.
*/
suspend fun getFeed() {
private suspend fun getFeed(feedSavedSearch: List<FeedSavedSearch>) {
// Create image fetch subscription
initializeFetchImageSubscription()
// Create items with the initial state
val initialItems = getSourcesToGetFeed().map {
val initialItems = getSourcesToGetFeed(feedSavedSearch).map {
createCatalogueSearchItem(
it,
null,
@ -189,7 +190,7 @@ open class SourceFeedPresenter(
var items = initialItems
fetchSourcesSubscription?.unsubscribe()
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed())
fetchSourcesSubscription = Observable.from(getSourcesToGetFeed(feedSavedSearch))
.flatMap(
{ sourceFeed ->
Observable.defer {
@ -323,70 +324,9 @@ open class SourceFeedPresenter(
return localManga?.toDbManga()!!
}
suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
return withIOContext {
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 loadSearch(searchId: Long) =
getExhSavedSearch.awaitOne(searchId, source::getFilterList)
suspend fun loadSearches(): List<EXHSavedSearch> {
return withIOContext {
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,
)
}
}
}
}
suspend fun loadSearches() =
getExhSavedSearch.await(source.id, source::getFilterList)
}

View File

@ -14,6 +14,8 @@ import eu.kanade.domain.manga.interactor.GetMangaBySource
import eu.kanade.domain.manga.interactor.InsertMergedReference
import eu.kanade.domain.manga.interactor.UpdateManga
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.data.backup.BackupCreatorJob
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.log.xLogE
import exh.merged.sql.models.MergedMangaReference
import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch
import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID
import exh.source.HBROWSE_SOURCE_ID
@ -76,6 +80,8 @@ object EXHMigrations {
private val updateChapter: UpdateChapter by injectLazy()
private val deleteChapters: DeleteChapters 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.
@ -360,29 +366,32 @@ object EXHMigrations {
}
if (oldVersion under 31) {
runBlocking {
handler.await(true) {
prefs.getStringSet("eh_saved_searches", emptySet())?.forEach {
kotlin.runCatching {
val savedSearch = prefs.getStringSet("eh_saved_searches", emptySet())?.mapNotNull {
runCatching {
val content = Json.decodeFromString<JsonObject>(it.substringAfter(':'))
saved_searchQueries.insertSavedSearch(
_id = null,
source = it.substringBefore(':').toLongOrNull() ?: return@forEach,
SavedSearch(
id = -1,
source = it.substringBefore(':').toLongOrNull()
?: return@runCatching null,
name = content["name"]!!.jsonPrimitive.content,
query = content["query"]!!.jsonPrimitive.contentOrNull?.nullIfBlank(),
filters_json = Json.encodeToString(content["filters"]!!.jsonArray),
filtersJson = Json.encodeToString(content["filters"]!!.jsonArray),
)
}.getOrNull()
}
if (!savedSearch.isNullOrEmpty()) {
insertSavedSearch.awaitAll(savedSearch)
}
}
handler.await(true) {
prefs.getStringSet("latest_tab_sources", emptySet())?.forEach {
feed_saved_searchQueries.insertFeedSavedSearch(
_id = null,
val feedSavedSearch = prefs.getStringSet("latest_tab_sources", emptySet())?.map {
FeedSavedSearch(
id = -1,
source = it.toLong(),
saved_search = null,
savedSearch = null,
global = true,
)
}
if (!feedSavedSearch.isNullOrEmpty()) {
insertFeedSavedSearch.awaitAll(feedSavedSearch)
}
}
prefs.edit(commit = true) {

View File

@ -3,11 +3,11 @@ package exh.favorites
import android.content.Context
import android.net.wifi.WifiManager
import android.os.PowerManager
import eu.kanade.data.AndroidDatabaseHandler
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories
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.UpdateManga
import eu.kanade.domain.manga.model.Manga
@ -49,6 +49,7 @@ import kotlin.time.Duration.Companion.seconds
// TODO only apply database changes after sync
class FavoritesSyncHelper(val context: Context) {
private val handler: DatabaseHandler by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val getManga: GetManga by injectLazy()
private val updateManga: UpdateManga by injectLazy()
@ -96,7 +97,7 @@ class FavoritesSyncHelper(val context: Context) {
// Validate library state
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)
libraryManga.forEach {
if (!it.isEhBasedManga()) return@forEach

View File

@ -2,14 +2,14 @@ package exh.savedsearches.models
data class FeedSavedSearch(
// Tag identifier, unique
var id: Long?,
val id: Long,
// Source for the saved search
var source: Long,
val source: Long,
// 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
var global: Boolean,
val global: Boolean,
)

View File

@ -2,17 +2,17 @@ package exh.savedsearches.models
data class SavedSearch(
// Tag identifier, unique
var id: Long?,
val id: Long,
// The source the saved search is for
var source: Long,
val source: Long,
// If false the manga will not grab chapter updates
var name: String,
val name: String,
// The query if there is any
var query: String?,
val query: String?,
// The filter list
var filtersJson: String?,
val filtersJson: String?,
)

View File

@ -16,13 +16,13 @@ countGlobal:
SELECT count(*) FROM feed_saved_search WHERE global = 1;
selectBySource:
SELECT * FROM feed_saved_search WHERE source = ? AND global = 0;
SELECT * FROM feed_saved_search WHERE source = :sourceId AND global = 0;
insertFeedSavedSearch:
INSERT INTO feed_saved_search (_id, source, saved_search, global) VALUES (?, ?, ?, ?);
insert:
INSERT INTO feed_saved_search (source, saved_search, global) VALUES (:sourceId, :savedSearch, :global);
deleteById:
DELETE FROM feed_saved_search WHERE _id = ?;
DELETE FROM feed_saved_search WHERE _id = :id;
deleteAll:
DELETE FROM feed_saved_search;
@ -38,7 +38,7 @@ ON saved_search._id = M.saved_search;
selectSourceFeedSavedSearch:
SELECT saved_search.*
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
JOIN saved_search
ON saved_search._id = M.saved_search;
@ -46,7 +46,10 @@ ON saved_search._id = M.saved_search;
countSourceFeedSavedSearch:
SELECT count(*)
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
JOIN saved_search
ON saved_search._id = M.saved_search;
selectLastInsertedRowId:
SELECT last_insert_rowid();

View File

@ -7,30 +7,33 @@ CREATE TABLE saved_search(
);
selectBySource:
SELECT * FROM saved_search WHERE source = ?;
SELECT * FROM saved_search WHERE source = :sourceId;
deleteBySource:
DELETE FROM saved_search WHERE source = ?;
DELETE FROM saved_search WHERE source = :sourceId;
selectAll:
SELECT * FROM saved_search;
selectById:
SELECT * FROM saved_search WHERE _id = ?;
SELECT * FROM saved_search WHERE _id = :id;
selectByIds:
SELECT * FROM saved_search WHERE _id IN ?;
SELECT * FROM saved_search WHERE _id IN :ids;
selectNamesAndSources:
SELECT source, name
FROM saved_search;
insertSavedSearch:
INSERT INTO saved_search (_id, source, name, query, filters_json)
VALUES (?, ?, ?, ?, ?);
insert:
INSERT INTO saved_search (source, name, query, filters_json)
VALUES (:source, :name, :query, :filtersJson);
deleteById:
DELETE FROM saved_search WHERE _id = ?;
DELETE FROM saved_search WHERE _id = :id;
deleteAll:
DELETE FROM saved_search;
selectLastInsertedRowId:
SELECT last_insert_rowid();