@@ -69,7 +69,6 @@ class MigrateUseCase @Inject constructor(
|
|||||||
lastCheckTime = System.currentTimeMillis(),
|
lastCheckTime = System.currentTimeMillis(),
|
||||||
lastChapterDate = lastChapter?.uploadDate ?: 0L,
|
lastChapterDate = lastChapter?.uploadDate ?: 0L,
|
||||||
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
||||||
lastError = null,
|
|
||||||
)
|
)
|
||||||
tracksDao.delete(oldDetails.id)
|
tracksDao.delete(oldDetails.id)
|
||||||
tracksDao.upsert(newTrack)
|
tracksDao.upsert(newTrack)
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ class JsonDeserializer(private val json: JSONObject) {
|
|||||||
source = json.getString("source"),
|
source = json.getString("source"),
|
||||||
isEnabled = json.getBoolean("enabled"),
|
isEnabled = json.getBoolean("enabled"),
|
||||||
sortKey = json.getInt("sort_key"),
|
sortKey = json.getInt("sort_key"),
|
||||||
addedIn = json.getIntOrDefault("added_in", 0),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toMap(): Map<String, Any?> {
|
fun toMap(): Map<String, Any?> {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.koitharu.kotatsu.core.db.migrations.Migration17To18
|
|||||||
import org.koitharu.kotatsu.core.db.migrations.Migration18To19
|
import org.koitharu.kotatsu.core.db.migrations.Migration18To19
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration19To20
|
import org.koitharu.kotatsu.core.db.migrations.Migration19To20
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration1To2
|
import org.koitharu.kotatsu.core.db.migrations.Migration1To2
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration20To21
|
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration2To3
|
import org.koitharu.kotatsu.core.db.migrations.Migration2To3
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration3To4
|
import org.koitharu.kotatsu.core.db.migrations.Migration3To4
|
||||||
import org.koitharu.kotatsu.core.db.migrations.Migration4To5
|
import org.koitharu.kotatsu.core.db.migrations.Migration4To5
|
||||||
@@ -59,7 +58,7 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity
|
|||||||
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
||||||
import org.koitharu.kotatsu.tracker.data.TracksDao
|
import org.koitharu.kotatsu.tracker.data.TracksDao
|
||||||
|
|
||||||
const val DATABASE_VERSION = 21
|
const val DATABASE_VERSION = 20
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
@@ -119,7 +118,6 @@ fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
|
|||||||
Migration17To18(),
|
Migration17To18(),
|
||||||
Migration18To19(),
|
Migration18To19(),
|
||||||
Migration19To20(),
|
Migration19To20(),
|
||||||
Migration20To21(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun MangaDatabase(context: Context): MangaDatabase = Room
|
fun MangaDatabase(context: Context): MangaDatabase = Room
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import androidx.sqlite.db.SimpleSQLiteQuery
|
|||||||
import androidx.sqlite.db.SupportSQLiteQuery
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
|
||||||
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
|
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
|
||||||
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
||||||
|
|
||||||
@@ -69,7 +68,6 @@ abstract class MangaSourcesDao {
|
|||||||
source = source,
|
source = source,
|
||||||
isEnabled = isEnabled,
|
isEnabled = isEnabled,
|
||||||
sortKey = getMaxSortKey() + 1,
|
sortKey = getMaxSortKey() + 1,
|
||||||
addedIn = BuildConfig.VERSION_CODE,
|
|
||||||
)
|
)
|
||||||
upsert(entity)
|
upsert(entity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,4 @@ data class MangaSourceEntity(
|
|||||||
val source: String,
|
val source: String,
|
||||||
@ColumnInfo(name = "enabled") val isEnabled: Boolean,
|
@ColumnInfo(name = "enabled") val isEnabled: Boolean,
|
||||||
@ColumnInfo(name = "sort_key", index = true) val sortKey: Int,
|
@ColumnInfo(name = "sort_key", index = true) val sortKey: Int,
|
||||||
@ColumnInfo(name = "added_in") val addedIn: Int,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.core.db.migrations
|
|
||||||
|
|
||||||
import androidx.room.migration.Migration
|
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
||||||
|
|
||||||
class Migration20To21 : Migration(20, 21) {
|
|
||||||
|
|
||||||
override fun migrate(db: SupportSQLiteDatabase) {
|
|
||||||
db.execSQL("ALTER TABLE tracks ADD COLUMN `last_error` TEXT DEFAULT NULL")
|
|
||||||
db.execSQL("ALTER TABLE sources ADD COLUMN `added_in` INTEGER NOT NULL DEFAULT 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -156,7 +156,6 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
suspend fun assimilateNewSources(): Set<MangaSource> {
|
suspend fun assimilateNewSources(): Set<MangaSource> {
|
||||||
val new = getNewSources()
|
val new = getNewSources()
|
||||||
if (new.isEmpty()) {
|
if (new.isEmpty()) {
|
||||||
@@ -168,7 +167,6 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
source = x.name,
|
source = x.name,
|
||||||
isEnabled = false,
|
isEnabled = false,
|
||||||
sortKey = ++maxSortKey,
|
sortKey = ++maxSortKey,
|
||||||
addedIn = BuildConfig.VERSION_CODE,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
dao.insertIfAbsent(entities)
|
dao.insertIfAbsent(entities)
|
||||||
|
|||||||
@@ -6,32 +6,30 @@ import android.view.View
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.chip.Chip
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
||||||
import org.koitharu.kotatsu.core.util.ext.observe
|
import org.koitharu.kotatsu.core.util.ext.observe
|
||||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||||
import org.koitharu.kotatsu.core.util.ext.toLocale
|
import org.koitharu.kotatsu.core.util.ext.toLocale
|
||||||
import org.koitharu.kotatsu.databinding.ActivitySourcesCatalogBinding
|
import org.koitharu.kotatsu.databinding.ActivitySourcesCatalogBinding
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
|
||||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
|
||||||
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
|
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
||||||
OnListItemClickListener<SourceCatalogItem.Source>,
|
OnListItemClickListener<SourceCatalogItem.Source>,
|
||||||
AppBarOwner, MenuItem.OnActionExpandListener, ChipsView.OnChipClickListener {
|
AppBarOwner, MenuItem.OnActionExpandListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var coil: ImageLoader
|
lateinit var coil: ImageLoader
|
||||||
@@ -47,24 +45,18 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(ActivitySourcesCatalogBinding.inflate(layoutInflater))
|
setContentView(ActivitySourcesCatalogBinding.inflate(layoutInflater))
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
val sourcesAdapter = SourcesCatalogAdapter(this, coil, this)
|
val pagerAdapter = SourcesCatalogPagerAdapter(this, coil, this)
|
||||||
with(viewBinding.recyclerView) {
|
viewBinding.pager.adapter = pagerAdapter
|
||||||
setHasFixedSize(true)
|
val tabMediator = TabLayoutMediator(viewBinding.tabs, viewBinding.pager, pagerAdapter)
|
||||||
addItemDecoration(TypedListSpacingDecoration(context, false))
|
tabMediator.attach()
|
||||||
adapter = sourcesAdapter
|
viewModel.content.observe(this, pagerAdapter)
|
||||||
}
|
|
||||||
viewBinding.chipsFilter.onChipClickListener = this
|
|
||||||
viewModel.content.observe(this, sourcesAdapter)
|
|
||||||
viewModel.hasNewSources.observe(this, ::onHasNewSourcesChanged)
|
viewModel.hasNewSources.observe(this, ::onHasNewSourcesChanged)
|
||||||
viewModel.onActionDone.observeEvent(
|
viewModel.onActionDone.observeEvent(
|
||||||
this,
|
this,
|
||||||
ReversibleActionObserver(viewBinding.recyclerView),
|
ReversibleActionObserver(viewBinding.pager),
|
||||||
)
|
)
|
||||||
viewModel.appliedFilter.observe(this) {
|
viewModel.locale.observe(this) {
|
||||||
supportActionBar?.subtitle = it.locale?.toLocale().getDisplayName(this)
|
supportActionBar?.subtitle = it?.toLocale().getDisplayName(this)
|
||||||
}
|
|
||||||
viewModel.filter.observe(this) {
|
|
||||||
viewBinding.chipsFilter.setChips(it)
|
|
||||||
}
|
}
|
||||||
addMenuProvider(SourcesCatalogMenuProvider(this, viewModel, this))
|
addMenuProvider(SourcesCatalogMenuProvider(this, viewModel, this))
|
||||||
}
|
}
|
||||||
@@ -76,23 +68,21 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChipClick(chip: Chip, data: Any?) {
|
|
||||||
when (data) {
|
|
||||||
is ContentType -> viewModel.setContentType(data, chip.isChecked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(item: SourceCatalogItem.Source, view: View) {
|
override fun onItemClick(item: SourceCatalogItem.Source, view: View) {
|
||||||
viewModel.addSource(item.source)
|
viewModel.addSource(item.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||||
|
viewBinding.tabs.isVisible = false
|
||||||
|
viewBinding.pager.isUserInputEnabled = false
|
||||||
val sq = (item.actionView as? SearchView)?.query?.trim()?.toString().orEmpty()
|
val sq = (item.actionView as? SearchView)?.query?.trim()?.toString().orEmpty()
|
||||||
viewModel.performSearch(sq)
|
viewModel.performSearch(sq)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||||
|
viewBinding.tabs.isVisible = true
|
||||||
|
viewBinding.pager.isUserInputEnabled = true
|
||||||
viewModel.performSearch(null)
|
viewModel.performSearch(null)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -102,7 +92,7 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
|||||||
if (newSourcesSnackbar?.isShownOrQueued == true) {
|
if (newSourcesSnackbar?.isShownOrQueued == true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val snackbar = Snackbar.make(viewBinding.recyclerView, R.string.new_sources_text, Snackbar.LENGTH_INDEFINITE)
|
val snackbar = Snackbar.make(viewBinding.pager, R.string.new_sources_text, Snackbar.LENGTH_INDEFINITE)
|
||||||
snackbar.setAction(R.string.explore) {
|
snackbar.setAction(R.string.explore) {
|
||||||
NewSourcesDialogFragment.show(supportFragmentManager)
|
NewSourcesDialogFragment.show(supportFragmentManager)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.settings.sources.catalog
|
|
||||||
|
|
||||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
data class SourcesCatalogFilter(
|
|
||||||
val types: Set<ContentType>,
|
|
||||||
val locale: String?,
|
|
||||||
)
|
|
||||||
@@ -19,7 +19,6 @@ import org.koitharu.kotatsu.core.util.ext.lifecycleScope
|
|||||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
class SourcesCatalogListProducer @AssistedInject constructor(
|
class SourcesCatalogListProducer @AssistedInject constructor(
|
||||||
@Assisted private val locale: String?,
|
@Assisted private val locale: String?,
|
||||||
@Assisted private val contentType: ContentType,
|
@Assisted private val contentType: ContentType,
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
package org.koitharu.kotatsu.settings.sources.catalog
|
package org.koitharu.kotatsu.settings.sources.catalog
|
||||||
|
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.internal.lifecycle.RetainedLifecycleImpl
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView.ChipModel
|
|
||||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||||
import org.koitharu.kotatsu.core.util.ext.call
|
import org.koitharu.kotatsu.core.util.ext.call
|
||||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||||
|
import java.util.EnumMap
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -27,40 +31,41 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SourcesCatalogViewModel @Inject constructor(
|
class SourcesCatalogViewModel @Inject constructor(
|
||||||
private val repository: MangaSourcesRepository,
|
private val repository: MangaSourcesRepository,
|
||||||
|
private val listProducerFactory: SourcesCatalogListProducer.Factory,
|
||||||
|
private val settings: AppSettings,
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
private val lifecycle = RetainedLifecycleImpl()
|
||||||
|
private var searchQuery: String? = null
|
||||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||||
val locales = repository.allMangaSources.mapToSet { it.locale }
|
val locales = repository.allMangaSources.mapToSet { it.locale }
|
||||||
|
val locale = MutableStateFlow(Locale.getDefault().language.takeIf { it in locales })
|
||||||
private val searchQuery = MutableStateFlow<String?>(null)
|
|
||||||
val appliedFilter = MutableStateFlow(
|
|
||||||
SourcesCatalogFilter(
|
|
||||||
types = emptySet(),
|
|
||||||
locale = Locale.getDefault().language.takeIf { it in locales },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val hasNewSources = repository.observeNewSources()
|
val hasNewSources = repository.observeNewSources()
|
||||||
.map { it.isNotEmpty() }
|
.map { it.isNotEmpty() }
|
||||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
|
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
|
||||||
|
|
||||||
val filter: StateFlow<List<ChipModel>> = appliedFilter.map {
|
private val listProducers = locale.map { lc ->
|
||||||
buildFilter(it)
|
createListProducers(lc)
|
||||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, buildFilter(appliedFilter.value))
|
}.stateIn(viewModelScope, SharingStarted.Eagerly, createListProducers(locale.value))
|
||||||
|
|
||||||
val content: StateFlow<List<SourceCatalogItem>> = combine(
|
val content: StateFlow<List<SourceCatalogPage>> = listProducers.flatMapLatest {
|
||||||
searchQuery,
|
val flows = it.entries.map { (type, producer) -> producer.list.map { x -> SourceCatalogPage(type, x) } }
|
||||||
appliedFilter,
|
combine<SourceCatalogPage, List<SourceCatalogPage>>(flows, Array<SourceCatalogPage>::toList)
|
||||||
) { q, f ->
|
|
||||||
buildSourcesList(f, q)
|
|
||||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
lifecycle.dispatchOnCleared()
|
||||||
|
}
|
||||||
|
|
||||||
fun performSearch(query: String?) {
|
fun performSearch(query: String?) {
|
||||||
searchQuery.value = query?.trim()
|
searchQuery = query
|
||||||
|
listProducers.value.forEach { (_, v) -> v.setQuery(query) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLocale(value: String?) {
|
fun setLocale(value: String?) {
|
||||||
appliedFilter.value = appliedFilter.value.copy(locale = value)
|
locale.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSource(source: MangaSource) {
|
fun addSource(source: MangaSource) {
|
||||||
@@ -76,64 +81,15 @@ class SourcesCatalogViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setContentType(value: ContentType, isAdd: Boolean) {
|
@MainThread
|
||||||
val filter = appliedFilter.value
|
private fun createListProducers(lc: String?): Map<ContentType, SourcesCatalogListProducer> {
|
||||||
val types = EnumSet.noneOf(ContentType::class.java)
|
val types = EnumSet.allOf(ContentType::class.java)
|
||||||
types.addAll(filter.types)
|
if (settings.isNsfwContentDisabled) {
|
||||||
if (isAdd) {
|
types.remove(ContentType.HENTAI)
|
||||||
types.add(value)
|
|
||||||
} else {
|
|
||||||
types.remove(value)
|
|
||||||
}
|
}
|
||||||
appliedFilter.value = filter.copy(types = types)
|
return types.associateWithTo(EnumMap(ContentType::class.java)) { type ->
|
||||||
}
|
listProducerFactory.create(lc, type, lifecycle).also {
|
||||||
|
it.setQuery(searchQuery)
|
||||||
private fun buildFilter(applied: SourcesCatalogFilter): List<ChipModel> = buildList(ContentType.entries.size) {
|
|
||||||
for (ct in ContentType.entries) {
|
|
||||||
add(
|
|
||||||
ChipModel(
|
|
||||||
tint = 0,
|
|
||||||
title = ct.name,
|
|
||||||
icon = 0,
|
|
||||||
isCheckable = true,
|
|
||||||
isChecked = ct in applied.types,
|
|
||||||
data = ct,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun buildSourcesList(filter: SourcesCatalogFilter, query: String?): List<SourceCatalogItem> {
|
|
||||||
val sources = repository.getDisabledSources().toMutableList()
|
|
||||||
sources.retainAll {
|
|
||||||
(filter.types.isEmpty() || it.contentType in filter.types) && it.locale == filter.locale
|
|
||||||
}
|
|
||||||
if (!query.isNullOrEmpty()) {
|
|
||||||
sources.retainAll { it.title.contains(query, ignoreCase = true) }
|
|
||||||
}
|
|
||||||
return if (sources.isEmpty()) {
|
|
||||||
listOf(
|
|
||||||
if (query == null) {
|
|
||||||
SourceCatalogItem.Hint(
|
|
||||||
icon = R.drawable.ic_empty_feed,
|
|
||||||
title = R.string.no_manga_sources,
|
|
||||||
text = R.string.no_manga_sources_catalog_text,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
SourceCatalogItem.Hint(
|
|
||||||
icon = R.drawable.ic_empty_feed,
|
|
||||||
title = R.string.nothing_found,
|
|
||||||
text = R.string.no_manga_sources_found,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
sources.sortBy { it.title }
|
|
||||||
sources.map {
|
|
||||||
SourceCatalogItem.Source(
|
|
||||||
source = it,
|
|
||||||
showSummary = query != null,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ class TrackEntity(
|
|||||||
@ColumnInfo(name = "last_check_time") val lastCheckTime: Long,
|
@ColumnInfo(name = "last_check_time") val lastCheckTime: Long,
|
||||||
@ColumnInfo(name = "last_chapter_date") val lastChapterDate: Long,
|
@ColumnInfo(name = "last_chapter_date") val lastChapterDate: Long,
|
||||||
@ColumnInfo(name = "last_result") val lastResult: Int,
|
@ColumnInfo(name = "last_result") val lastResult: Int,
|
||||||
@ColumnInfo(name = "last_error") val lastError: String?,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -43,7 +42,6 @@ class TrackEntity(
|
|||||||
lastCheckTime = 0L,
|
lastCheckTime = 0L,
|
||||||
lastChapterDate = 0,
|
lastChapterDate = 0,
|
||||||
lastResult = RESULT_NONE,
|
lastResult = RESULT_NONE,
|
||||||
lastError = null,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,6 @@ class TrackingRepository @Inject constructor(
|
|||||||
lastCheckTime = tracking.lastCheck?.toEpochMilli() ?: 0L,
|
lastCheckTime = tracking.lastCheck?.toEpochMilli() ?: 0L,
|
||||||
lastChapterDate = tracking.lastChapterDate?.toEpochMilli() ?: 0L,
|
lastChapterDate = tracking.lastChapterDate?.toEpochMilli() ?: 0L,
|
||||||
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
||||||
lastError = null,
|
|
||||||
)
|
)
|
||||||
db.getTracksDao().upsert(entity)
|
db.getTracksDao().upsert(entity)
|
||||||
}
|
}
|
||||||
@@ -231,7 +230,6 @@ class TrackingRepository @Inject constructor(
|
|||||||
lastCheckTime = System.currentTimeMillis(),
|
lastCheckTime = System.currentTimeMillis(),
|
||||||
lastChapterDate = lastChapterDate,
|
lastChapterDate = lastChapterDate,
|
||||||
lastResult = TrackEntity.RESULT_FAILED,
|
lastResult = TrackEntity.RESULT_FAILED,
|
||||||
lastError = updates.error?.toString(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
is MangaUpdates.Success -> TrackEntity(
|
is MangaUpdates.Success -> TrackEntity(
|
||||||
@@ -241,7 +239,6 @@ class TrackingRepository @Inject constructor(
|
|||||||
lastCheckTime = System.currentTimeMillis(),
|
lastCheckTime = System.currentTimeMillis(),
|
||||||
lastChapterDate = updates.lastChapterDate().ifZero { lastChapterDate },
|
lastChapterDate = updates.lastChapterDate().ifZero { lastChapterDate },
|
||||||
lastResult = if (updates.isNotEmpty()) TrackEntity.RESULT_HAS_UPDATE else TrackEntity.RESULT_NO_UPDATE,
|
lastResult = if (updates.isNotEmpty()) TrackEntity.RESULT_HAS_UPDATE else TrackEntity.RESULT_NO_UPDATE,
|
||||||
lastError = null,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,36 +19,19 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize" />
|
android:layout_height="?attr/actionBarSize" />
|
||||||
|
|
||||||
<HorizontalScrollView
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:id="@+id/scrollView_chips"
|
android:id="@+id/tabs"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
app:tabGravity="start"
|
||||||
android:paddingHorizontal="@dimen/list_spacing_large"
|
app:tabMode="scrollable" />
|
||||||
android:scrollbars="none">
|
|
||||||
|
|
||||||
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
|
||||||
android:id="@+id/chips_filter"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingVertical="@dimen/margin_small"
|
|
||||||
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
|
|
||||||
app:selectionRequired="false"
|
|
||||||
app:singleLine="true"
|
|
||||||
app:singleSelection="false" />
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/pager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user