Revert "Update sources catalog ui"

This reverts commit 597ad01e8f.
This commit is contained in:
Koitharu
2024-05-27 17:29:31 +03:00
parent bf740ddc93
commit 8d5bde6e60
14 changed files with 55 additions and 162 deletions

View File

@@ -69,7 +69,6 @@ class MigrateUseCase @Inject constructor(
lastCheckTime = System.currentTimeMillis(),
lastChapterDate = lastChapter?.uploadDate ?: 0L,
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
lastError = null,
)
tracksDao.delete(oldDetails.id)
tracksDao.upsert(newTrack)

View File

@@ -84,7 +84,6 @@ class JsonDeserializer(private val json: JSONObject) {
source = json.getString("source"),
isEnabled = json.getBoolean("enabled"),
sortKey = json.getInt("sort_key"),
addedIn = json.getIntOrDefault("added_in", 0),
)
fun toMap(): Map<String, Any?> {

View File

@@ -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.Migration19To20
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.Migration3To4
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.TracksDao
const val DATABASE_VERSION = 21
const val DATABASE_VERSION = 20
@Database(
entities = [
@@ -119,7 +118,6 @@ fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
Migration17To18(),
Migration18To19(),
Migration19To20(),
Migration20To21(),
)
fun MangaDatabase(context: Context): MangaDatabase = Room

View File

@@ -11,7 +11,6 @@ import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import kotlinx.coroutines.flow.Flow
import org.intellij.lang.annotations.Language
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
@@ -69,7 +68,6 @@ abstract class MangaSourcesDao {
source = source,
isEnabled = isEnabled,
sortKey = getMaxSortKey() + 1,
addedIn = BuildConfig.VERSION_CODE,
)
upsert(entity)
}

View File

@@ -14,5 +14,4 @@ data class MangaSourceEntity(
val source: String,
@ColumnInfo(name = "enabled") val isEnabled: Boolean,
@ColumnInfo(name = "sort_key", index = true) val sortKey: Int,
@ColumnInfo(name = "added_in") val addedIn: Int,
)

View File

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

View File

@@ -156,7 +156,6 @@ class MangaSourcesRepository @Inject constructor(
}
}
@Deprecated("")
suspend fun assimilateNewSources(): Set<MangaSource> {
val new = getNewSources()
if (new.isEmpty()) {
@@ -168,7 +167,6 @@ class MangaSourcesRepository @Inject constructor(
source = x.name,
isEnabled = false,
sortKey = ++maxSortKey,
addedIn = BuildConfig.VERSION_CODE,
)
}
dao.insertIfAbsent(entities)

View File

@@ -6,32 +6,30 @@ import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import coil.ImageLoader
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.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
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.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.toLocale
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.parsers.model.ContentType
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
import javax.inject.Inject
@AndroidEntryPoint
class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
OnListItemClickListener<SourceCatalogItem.Source>,
AppBarOwner, MenuItem.OnActionExpandListener, ChipsView.OnChipClickListener {
AppBarOwner, MenuItem.OnActionExpandListener {
@Inject
lateinit var coil: ImageLoader
@@ -47,24 +45,18 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
super.onCreate(savedInstanceState)
setContentView(ActivitySourcesCatalogBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val sourcesAdapter = SourcesCatalogAdapter(this, coil, this)
with(viewBinding.recyclerView) {
setHasFixedSize(true)
addItemDecoration(TypedListSpacingDecoration(context, false))
adapter = sourcesAdapter
}
viewBinding.chipsFilter.onChipClickListener = this
viewModel.content.observe(this, sourcesAdapter)
val pagerAdapter = SourcesCatalogPagerAdapter(this, coil, this)
viewBinding.pager.adapter = pagerAdapter
val tabMediator = TabLayoutMediator(viewBinding.tabs, viewBinding.pager, pagerAdapter)
tabMediator.attach()
viewModel.content.observe(this, pagerAdapter)
viewModel.hasNewSources.observe(this, ::onHasNewSourcesChanged)
viewModel.onActionDone.observeEvent(
this,
ReversibleActionObserver(viewBinding.recyclerView),
ReversibleActionObserver(viewBinding.pager),
)
viewModel.appliedFilter.observe(this) {
supportActionBar?.subtitle = it.locale?.toLocale().getDisplayName(this)
}
viewModel.filter.observe(this) {
viewBinding.chipsFilter.setChips(it)
viewModel.locale.observe(this) {
supportActionBar?.subtitle = it?.toLocale().getDisplayName(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) {
viewModel.addSource(item.source)
}
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
viewBinding.tabs.isVisible = false
viewBinding.pager.isUserInputEnabled = false
val sq = (item.actionView as? SearchView)?.query?.trim()?.toString().orEmpty()
viewModel.performSearch(sq)
return true
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
viewBinding.tabs.isVisible = true
viewBinding.pager.isUserInputEnabled = true
viewModel.performSearch(null)
return true
}
@@ -102,7 +92,7 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
if (newSourcesSnackbar?.isShownOrQueued == true) {
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) {
NewSourcesDialogFragment.show(supportFragmentManager)
}

View File

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

View File

@@ -19,7 +19,6 @@ import org.koitharu.kotatsu.core.util.ext.lifecycleScope
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.parsers.model.ContentType
@Deprecated("")
class SourcesCatalogListProducer @AssistedInject constructor(
@Assisted private val locale: String?,
@Assisted private val contentType: ContentType,

View File

@@ -1,25 +1,29 @@
package org.koitharu.kotatsu.settings.sources.catalog
import androidx.annotation.MainThread
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.internal.lifecycle.RetainedLifecycleImpl
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
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.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.call
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapToSet
import java.util.EnumMap
import java.util.EnumSet
import java.util.Locale
import javax.inject.Inject
@@ -27,40 +31,41 @@ import javax.inject.Inject
@HiltViewModel
class SourcesCatalogViewModel @Inject constructor(
private val repository: MangaSourcesRepository,
private val listProducerFactory: SourcesCatalogListProducer.Factory,
private val settings: AppSettings,
) : BaseViewModel() {
private val lifecycle = RetainedLifecycleImpl()
private var searchQuery: String? = null
val onActionDone = MutableEventFlow<ReversibleAction>()
val locales = repository.allMangaSources.mapToSet { it.locale }
private val searchQuery = MutableStateFlow<String?>(null)
val appliedFilter = MutableStateFlow(
SourcesCatalogFilter(
types = emptySet(),
locale = Locale.getDefault().language.takeIf { it in locales },
),
)
val locale = MutableStateFlow(Locale.getDefault().language.takeIf { it in locales })
val hasNewSources = repository.observeNewSources()
.map { it.isNotEmpty() }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
val filter: StateFlow<List<ChipModel>> = appliedFilter.map {
buildFilter(it)
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, buildFilter(appliedFilter.value))
private val listProducers = locale.map { lc ->
createListProducers(lc)
}.stateIn(viewModelScope, SharingStarted.Eagerly, createListProducers(locale.value))
val content: StateFlow<List<SourceCatalogItem>> = combine(
searchQuery,
appliedFilter,
) { q, f ->
buildSourcesList(f, q)
val content: StateFlow<List<SourceCatalogPage>> = listProducers.flatMapLatest {
val flows = it.entries.map { (type, producer) -> producer.list.map { x -> SourceCatalogPage(type, x) } }
combine<SourceCatalogPage, List<SourceCatalogPage>>(flows, Array<SourceCatalogPage>::toList)
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
override fun onCleared() {
super.onCleared()
lifecycle.dispatchOnCleared()
}
fun performSearch(query: String?) {
searchQuery.value = query?.trim()
searchQuery = query
listProducers.value.forEach { (_, v) -> v.setQuery(query) }
}
fun setLocale(value: String?) {
appliedFilter.value = appliedFilter.value.copy(locale = value)
locale.value = value
}
fun addSource(source: MangaSource) {
@@ -76,64 +81,15 @@ class SourcesCatalogViewModel @Inject constructor(
}
}
fun setContentType(value: ContentType, isAdd: Boolean) {
val filter = appliedFilter.value
val types = EnumSet.noneOf(ContentType::class.java)
types.addAll(filter.types)
if (isAdd) {
types.add(value)
} else {
types.remove(value)
@MainThread
private fun createListProducers(lc: String?): Map<ContentType, SourcesCatalogListProducer> {
val types = EnumSet.allOf(ContentType::class.java)
if (settings.isNsfwContentDisabled) {
types.remove(ContentType.HENTAI)
}
appliedFilter.value = filter.copy(types = types)
}
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,
)
return types.associateWithTo(EnumMap(ContentType::class.java)) { type ->
listProducerFactory.create(lc, type, lifecycle).also {
it.setQuery(searchQuery)
}
}
}

View File

@@ -25,7 +25,6 @@ class TrackEntity(
@ColumnInfo(name = "last_check_time") val lastCheckTime: Long,
@ColumnInfo(name = "last_chapter_date") val lastChapterDate: Long,
@ColumnInfo(name = "last_result") val lastResult: Int,
@ColumnInfo(name = "last_error") val lastError: String?,
) {
companion object {
@@ -43,7 +42,6 @@ class TrackEntity(
lastCheckTime = 0L,
lastChapterDate = 0,
lastResult = RESULT_NONE,
lastError = null,
)
}
}

View File

@@ -174,7 +174,6 @@ class TrackingRepository @Inject constructor(
lastCheckTime = tracking.lastCheck?.toEpochMilli() ?: 0L,
lastChapterDate = tracking.lastChapterDate?.toEpochMilli() ?: 0L,
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
lastError = null,
)
db.getTracksDao().upsert(entity)
}
@@ -231,7 +230,6 @@ class TrackingRepository @Inject constructor(
lastCheckTime = System.currentTimeMillis(),
lastChapterDate = lastChapterDate,
lastResult = TrackEntity.RESULT_FAILED,
lastError = updates.error?.toString(),
)
is MangaUpdates.Success -> TrackEntity(
@@ -241,7 +239,6 @@ class TrackingRepository @Inject constructor(
lastCheckTime = System.currentTimeMillis(),
lastChapterDate = updates.lastChapterDate().ifZero { lastChapterDate },
lastResult = if (updates.isNotEmpty()) TrackEntity.RESULT_HAS_UPDATE else TrackEntity.RESULT_NO_UPDATE,
lastError = null,
)
}
}

View File

@@ -19,36 +19,19 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
<HorizontalScrollView
android:id="@+id/scrollView_chips"
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingHorizontal="@dimen/list_spacing_large"
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>
app:tabGravity="start"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="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" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>