Minor refactoring

This commit is contained in:
arkon 2023-07-16 18:43:37 -04:00
parent dd3ca0c131
commit ef7b285151
23 changed files with 78 additions and 114 deletions

View File

@ -23,7 +23,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.SourcesState import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Pin import tachiyomi.domain.source.model.Pin
@ -40,7 +40,7 @@ import tachiyomi.source.local.isLocal
@Composable @Composable
fun SourcesScreen( fun SourcesScreen(
state: SourcesState, state: SourcesScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickItem: (Source, Listing) -> Unit, onClickItem: (Source, Listing) -> Unit,
onClickPin: (Source) -> Unit, onClickPin: (Source) -> Unit,

View File

@ -1,20 +1,14 @@
package eu.kanade.tachiyomi.data.backup package eu.kanade.tachiyomi.data.backup
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID // Filter options
internal object BackupConst {
object BackupConst { const val BACKUP_CATEGORY = 0x1
const val BACKUP_CATEGORY_MASK = 0x1
private const val NAME = "BackupRestoreServices" const val BACKUP_CHAPTER = 0x2
const val EXTRA_URI = "$ID.$NAME.EXTRA_URI" const val BACKUP_CHAPTER_MASK = 0x2
const val BACKUP_HISTORY = 0x4
// Filter options const val BACKUP_HISTORY_MASK = 0x4
internal const val BACKUP_CATEGORY = 0x1 const val BACKUP_TRACK = 0x8
internal const val BACKUP_CATEGORY_MASK = 0x1 const val BACKUP_TRACK_MASK = 0x8
internal const val BACKUP_CHAPTER = 0x2 const val BACKUP_ALL = 0xF
internal const val BACKUP_CHAPTER_MASK = 0x2
internal const val BACKUP_HISTORY = 0x4
internal const val BACKUP_HISTORY_MASK = 0x4
internal const val BACKUP_TRACK = 0x8
internal const val BACKUP_TRACK_MASK = 0x8
internal const val BACKUP_ALL = 0xF
} }

View File

@ -5,7 +5,6 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.domain.chapter.model.copyFrom import eu.kanade.domain.chapter.model.copyFrom
import eu.kanade.domain.manga.model.copyFrom
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK

View File

@ -6,7 +6,6 @@ import com.jakewharton.disklrucache.DiskLruCache
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.storage.saveTo
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Response import okhttp3.Response

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.GetChapter

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.DeletableTrackService import eu.kanade.tachiyomi.data.track.DeletableTrackService
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy

View File

@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy

View File

@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Interceptor import okhttp3.Interceptor

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.DeletableTrackService import eu.kanade.tachiyomi.data.track.DeletableTrackService
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.track.kitsu package eu.kanade.tachiyomi.data.track.kitsu
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.DeletableTrackService import eu.kanade.tachiyomi.data.track.DeletableTrackService
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.DeletableTrackService import eu.kanade.tachiyomi.data.track.DeletableTrackService
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response

View File

@ -29,17 +29,14 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
onChangeSearchFilter = screenModel::setSourceFilter, onChangeSearchFilter = screenModel::setSourceFilter,
onToggleResults = screenModel::toggleFilterResults, onToggleResults = screenModel::toggleFilterResults,
onClickSource = { onClickSource = {
if (!screenModel.incognitoMode.get()) {
screenModel.lastUsedSourceId.set(it.id)
}
navigator.push(SourceSearchScreen(state.manga!!, it.id, state.searchQuery)) navigator.push(SourceSearchScreen(state.manga!!, it.id, state.searchQuery))
}, },
onClickItem = { screenModel.setDialog(MigrateSearchDialog.Migrate(it)) }, onClickItem = { screenModel.setDialog(MigrateSearchScreenModel.Dialog.Migrate(it)) },
onLongClickItem = { navigator.push(MangaScreen(it.id, true)) }, onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
) )
when (val dialog = state.dialog) { when (val dialog = state.dialog) {
is MigrateSearchDialog.Migrate -> { is MigrateSearchScreenModel.Dialog.Migrate -> {
MigrateDialog( MigrateDialog(
oldManga = state.manga!!, oldManga = state.manga!!,
newManga = dialog.manga, newManga = dialog.manga,

View File

@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.ui.browse.migration.search
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
@ -12,21 +10,14 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class MigrateSearchScreenModel( class MigrateSearchScreenModel(
val mangaId: Long, val mangaId: Long,
preferences: BasePreferences = Injekt.get(), getManga: GetManga = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) { ) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) {
val incognitoMode = preferences.incognitoMode()
val lastUsedSourceId = sourcePreferences.lastUsedSource()
init { init {
coroutineScope.launch { coroutineScope.launch {
val manga = getManga.await(mangaId)!! val manga = getManga.await(mangaId)!!
@ -40,16 +31,15 @@ class MigrateSearchScreenModel(
} }
override fun getEnabledSources(): List<CatalogueSource> { override fun getEnabledSources(): List<CatalogueSource> {
val enabledLanguages = sourcePreferences.enabledLanguages().get() return super.getEnabledSources()
val disabledSources = sourcePreferences.disabledSources().get()
val pinnedSources = sourcePreferences.pinnedSources().get()
return sourceManager.getCatalogueSources()
.filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources } .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
.filter { it.lang in enabledLanguages } .sortedWith(
.filterNot { "${it.id}" in disabledSources } compareBy(
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" })) { it.id != state.value.manga!!.source },
.sortedByDescending { it.id == state.value.manga!!.source } { "${it.id}" !in pinnedSources },
{ "${it.name.lowercase()} (${it.lang})" },
),
)
} }
override fun updateSearchQuery(query: String?) { override fun updateSearchQuery(query: String?) {
@ -68,17 +58,17 @@ class MigrateSearchScreenModel(
return mutableState.value.items return mutableState.value.items
} }
fun setSourceFilter(filter: SourceFilter) { override fun setSourceFilter(filter: SourceFilter) {
mutableState.update { it.copy(sourceFilter = filter) } mutableState.update { it.copy(sourceFilter = filter) }
} }
fun toggleFilterResults() { override fun toggleFilterResults() {
mutableState.update { mutableState.update {
it.copy(onlyShowHasResults = !it.onlyShowHasResults) it.copy(onlyShowHasResults = !it.onlyShowHasResults)
} }
} }
fun setDialog(dialog: MigrateSearchDialog?) { fun setDialog(dialog: Dialog?) {
mutableState.update { mutableState.update {
it.copy(dialog = dialog) it.copy(dialog = dialog)
} }
@ -87,7 +77,7 @@ class MigrateSearchScreenModel(
@Immutable @Immutable
data class State( data class State(
val manga: Manga? = null, val manga: Manga? = null,
val dialog: MigrateSearchDialog? = null, val dialog: Dialog? = null,
val searchQuery: String? = null, val searchQuery: String? = null,
val sourceFilter: SourceFilter = SourceFilter.PinnedOnly, val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
@ -98,8 +88,8 @@ class MigrateSearchScreenModel(
val total: Int = items.size val total: Int = items.size
val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
} }
}
sealed class MigrateSearchDialog { sealed class Dialog {
data class Migrate(val manga: Manga) : MigrateSearchDialog() data class Migrate(val manga: Manga) : Dialog()
}
} }

View File

@ -3,11 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.source
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.interactor.GetEnabledSources import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.browse.SourceUiModel import eu.kanade.presentation.browse.SourceUiModel
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
@ -24,12 +22,10 @@ import uy.kohesive.injekt.api.get
import java.util.TreeMap import java.util.TreeMap
class SourcesScreenModel( class SourcesScreenModel(
private val preferences: BasePreferences = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val getEnabledSources: GetEnabledSources = Injekt.get(), private val getEnabledSources: GetEnabledSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(), private val toggleSource: ToggleSource = Injekt.get(),
private val toggleSourcePin: ToggleSourcePin = Injekt.get(), private val toggleSourcePin: ToggleSourcePin = Injekt.get(),
) : StateScreenModel<SourcesState>(SourcesState()) { ) : StateScreenModel<SourcesScreenModel.State>(State()) {
private val _events = Channel<Event>(Int.MAX_VALUE) private val _events = Channel<Event>(Int.MAX_VALUE)
val events = _events.receiveAsFlow() val events = _events.receiveAsFlow()
@ -81,12 +77,6 @@ class SourcesScreenModel(
} }
} }
fun onOpenSource(source: Source) {
if (!preferences.incognitoMode().get()) {
sourcePreferences.lastUsedSource().set(source.id)
}
}
fun toggleSource(source: Source) { fun toggleSource(source: Source) {
toggleSource.await(source) toggleSource.await(source)
} }
@ -109,17 +99,17 @@ class SourcesScreenModel(
data class Dialog(val source: Source) data class Dialog(val source: Source)
@Immutable
data class State(
val dialog: Dialog? = null,
val isLoading: Boolean = true,
val items: List<SourceUiModel> = emptyList(),
) {
val isEmpty = items.isEmpty()
}
companion object { companion object {
const val PINNED_KEY = "pinned" const val PINNED_KEY = "pinned"
const val LAST_USED_KEY = "last_used" const val LAST_USED_KEY = "last_used"
} }
} }
@Immutable
data class SourcesState(
val dialog: SourcesScreenModel.Dialog? = null,
val isLoading: Boolean = true,
val items: List<SourceUiModel> = emptyList(),
) {
val isEmpty = items.isEmpty()
}

View File

@ -47,7 +47,6 @@ fun Screen.sourcesTab(): TabContent {
state = state, state = state,
contentPadding = contentPadding, contentPadding = contentPadding,
onClickItem = { source, listing -> onClickItem = { source, listing ->
screenModel.onOpenSource(source)
navigator.push(BrowseSourceScreen(source.id, listing.query)) navigator.push(BrowseSourceScreen(source.id, listing.query))
}, },
onClickPin = screenModel::togglePin, onClickPin = screenModel::togglePin,

View File

@ -13,6 +13,7 @@ import androidx.paging.map
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.preference.asState import eu.kanade.core.preference.asState
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toDomainManga
@ -65,6 +66,7 @@ class BrowseSourceScreenModel(
listingQuery: String?, listingQuery: String?,
sourceManager: SourceManager = Injekt.get(), sourceManager: SourceManager = Injekt.get(),
sourcePreferences: SourcePreferences = Injekt.get(), sourcePreferences: SourcePreferences = Injekt.get(),
basePreferences: BasePreferences = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(), private val coverCache: CoverCache = Injekt.get(),
private val getRemoteManga: GetRemoteManga = Injekt.get(), private val getRemoteManga: GetRemoteManga = Injekt.get(),
@ -104,6 +106,10 @@ class BrowseSourceScreenModel(
) )
} }
} }
if (!basePreferences.incognitoMode().get()) {
sourcePreferences.lastUsedSource().set(source.id)
}
} }
/** /**

View File

@ -64,9 +64,6 @@ class GlobalSearchScreen(
onChangeSearchFilter = screenModel::setSourceFilter, onChangeSearchFilter = screenModel::setSourceFilter,
onToggleResults = screenModel::toggleFilterResults, onToggleResults = screenModel::toggleFilterResults,
onClickSource = { onClickSource = {
if (!screenModel.incognitoMode.get()) {
screenModel.lastUsedSourceId.set(it.id)
}
navigator.push(BrowseSourceScreen(it.id, state.searchQuery)) navigator.push(BrowseSourceScreen(it.id, state.searchQuery))
}, },
onClickItem = { navigator.push(MangaScreen(it.id, true)) }, onClickItem = { navigator.push(MangaScreen(it.id, true)) },

View File

@ -1,25 +1,14 @@
package eu.kanade.tachiyomi.ui.browse.source.globalsearch package eu.kanade.tachiyomi.ui.browse.source.globalsearch
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class GlobalSearchScreenModel( class GlobalSearchScreenModel(
initialQuery: String = "", initialQuery: String = "",
initialExtensionFilter: String? = null, initialExtensionFilter: String? = null,
preferences: BasePreferences = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
) : SearchScreenModel<GlobalSearchScreenModel.State>(State(searchQuery = initialQuery)) { ) : SearchScreenModel<GlobalSearchScreenModel.State>(State(searchQuery = initialQuery)) {
val incognitoMode = preferences.incognitoMode()
val lastUsedSourceId = sourcePreferences.lastUsedSource()
init { init {
extensionFilter = initialExtensionFilter extensionFilter = initialExtensionFilter
if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) { if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) {
@ -28,15 +17,8 @@ class GlobalSearchScreenModel(
} }
override fun getEnabledSources(): List<CatalogueSource> { override fun getEnabledSources(): List<CatalogueSource> {
val enabledLanguages = sourcePreferences.enabledLanguages().get() return super.getEnabledSources()
val disabledSources = sourcePreferences.disabledSources().get()
val pinnedSources = sourcePreferences.pinnedSources().get()
return sourceManager.getCatalogueSources()
.filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources } .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
.filter { it.lang in enabledLanguages }
.filterNot { "${it.id}" in disabledSources }
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
} }
override fun updateSearchQuery(query: String?) { override fun updateSearchQuery(query: String?) {
@ -55,11 +37,11 @@ class GlobalSearchScreenModel(
return mutableState.value.items return mutableState.value.items
} }
fun setSourceFilter(filter: SourceFilter) { override fun setSourceFilter(filter: SourceFilter) {
mutableState.update { it.copy(sourceFilter = filter) } mutableState.update { it.copy(sourceFilter = filter) }
} }
fun toggleFilterResults() { override fun toggleFilterResults() {
mutableState.update { mutableState.update {
it.copy(onlyShowHasResults = !it.onlyShowHasResults) it.copy(onlyShowHasResults = !it.onlyShowHasResults)
} }

View File

@ -10,16 +10,19 @@ import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.util.ioCoroutineScope import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import tachiyomi.core.util.lang.awaitSingle import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -27,6 +30,7 @@ import java.util.concurrent.Executors
abstract class SearchScreenModel<T>( abstract class SearchScreenModel<T>(
initialState: T, initialState: T,
private val sourcePreferences: SourcePreferences = Injekt.get(), private val sourcePreferences: SourcePreferences = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val getManga: GetManga = Injekt.get(), private val getManga: GetManga = Injekt.get(),
@ -34,12 +38,13 @@ abstract class SearchScreenModel<T>(
) : StateScreenModel<T>(initialState) { ) : StateScreenModel<T>(initialState) {
private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher() private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()
private var searchJob: Job? = null
protected var query: String? = null protected var query: String? = null
protected var extensionFilter: String? = null protected var extensionFilter: String? = null
private val sources by lazy { getSelectedSources() } private val sources by lazy { getSelectedSources() }
private val pinnedSources by lazy { sourcePreferences.pinnedSources().get() } protected val pinnedSources = sourcePreferences.pinnedSources().get()
private val sortComparator = { map: Map<CatalogueSource, SearchItemResult> -> private val sortComparator = { map: Map<CatalogueSource, SearchItemResult> ->
compareBy<CatalogueSource>( compareBy<CatalogueSource>(
@ -53,14 +58,27 @@ abstract class SearchScreenModel<T>(
fun getManga(initialManga: Manga): State<Manga> { fun getManga(initialManga: Manga): State<Manga> {
return produceState(initialValue = initialManga) { return produceState(initialValue = initialManga) {
getManga.subscribe(initialManga.url, initialManga.source) getManga.subscribe(initialManga.url, initialManga.source)
.filterNotNull()
.collectLatest { manga -> .collectLatest { manga ->
if (manga == null) return@collectLatest
value = manga value = manga
} }
} }
} }
abstract fun getEnabledSources(): List<CatalogueSource> open fun getEnabledSources(): List<CatalogueSource> {
val enabledLanguages = sourcePreferences.enabledLanguages().get()
val disabledSources = sourcePreferences.disabledSources().get()
val pinnedSources = sourcePreferences.pinnedSources().get()
return sourceManager.getCatalogueSources()
.filter { it.lang in enabledLanguages && "${it.id}" !in disabledSources }
.sortedWith(
compareBy(
{ "${it.id}" !in pinnedSources },
{ "${it.name.lowercase()} (${it.lang})" },
),
)
}
private fun getSelectedSources(): List<CatalogueSource> { private fun getSelectedSources(): List<CatalogueSource> {
val enabledSources = getEnabledSources() val enabledSources = getEnabledSources()
@ -73,8 +91,8 @@ abstract class SearchScreenModel<T>(
return extensionManager.installedExtensionsFlow.value return extensionManager.installedExtensionsFlow.value
.filter { it.pkgName == filter } .filter { it.pkgName == filter }
.flatMap { it.sources } .flatMap { it.sources }
.filter { it in enabledSources }
.filterIsInstance<CatalogueSource>() .filterIsInstance<CatalogueSource>()
.filter { it in enabledSources }
} }
abstract fun updateSearchQuery(query: String?) abstract fun updateSearchQuery(query: String?)
@ -87,15 +105,19 @@ abstract class SearchScreenModel<T>(
updateItems(function(getItems())) updateItems(function(getItems()))
} }
abstract fun setSourceFilter(filter: SourceFilter)
abstract fun toggleFilterResults()
fun search(query: String) { fun search(query: String) {
if (this.query == query) return if (this.query == query) return
this.query = query this.query = query
searchJob?.cancel()
val initialItems = getSelectedSources().associateWith { SearchItemResult.Loading } val initialItems = getSelectedSources().associateWith { SearchItemResult.Loading }
updateItems(initialItems) updateItems(initialItems)
searchJob = ioCoroutineScope.launch {
ioCoroutineScope.launch {
sources sources
.map { source -> .map { source ->
async { async {