Make UpcomingViewModel collect manga only while subscribed (#3725)
Assisted-by: Claude:claude-opus-5
This commit is contained in:
@@ -17,6 +17,7 @@ import androidx.compose.runtime.toMutableStateList
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastForEachIndexed
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
@@ -41,7 +42,7 @@ class UpcomingScreen : Screen() {
|
|||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
val viewModel = viewModel<UpcomingViewModel>()
|
val viewModel = viewModel<UpcomingViewModel>()
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
when (state.dialog) {
|
when (state.dialog) {
|
||||||
is UpcomingViewModel.Dialog.FilterSheet -> {
|
is UpcomingViewModel.Dialog.FilterSheet -> {
|
||||||
|
|||||||
@@ -3,23 +3,29 @@ package mihon.feature.upcoming
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.util.fastMap
|
import androidx.compose.ui.util.fastMap
|
||||||
import androidx.compose.ui.util.fastMapIndexedNotNull
|
import androidx.compose.ui.util.fastMapIndexedNotNull
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import eu.kanade.core.util.insertSeparatorsReversed
|
import eu.kanade.core.util.insertSeparatorsReversed
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.WhileSubscribed
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
import kotlinx.datetime.YearMonth
|
import kotlinx.datetime.YearMonth
|
||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import kotlinx.datetime.yearMonth
|
import kotlinx.datetime.yearMonth
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.upcoming.interactor.GetUpcomingManga
|
import mihon.domain.upcoming.interactor.GetUpcomingManga
|
||||||
import tachiyomi.core.common.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@@ -27,39 +33,57 @@ import tachiyomi.domain.upcoming.service.UpcomingPreferences
|
|||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class UpcomingViewModel(
|
class UpcomingViewModel(
|
||||||
private val getUpcomingManga: GetUpcomingManga = Injekt.get(),
|
private val getUpcomingManga: GetUpcomingManga = Injekt.get(),
|
||||||
val getCategories: GetCategories = Injekt.get(),
|
val getCategories: GetCategories = Injekt.get(),
|
||||||
val upcomingPreferences: UpcomingPreferences = Injekt.get(),
|
val upcomingPreferences: UpcomingPreferences = Injekt.get(),
|
||||||
) : StateViewModel<UpcomingViewModel.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
val excludedCategories = upcomingPreferences.filterExcludedCategories
|
val excludedCategories = upcomingPreferences.filterExcludedCategories
|
||||||
val includedCategories = upcomingPreferences.filterIncludedCategories
|
val includedCategories = upcomingPreferences.filterIncludedCategories
|
||||||
|
|
||||||
init {
|
private val selectedYearMonth = MutableStateFlow(
|
||||||
viewModelScope.launchIO {
|
value = Clock.System.now()
|
||||||
getUpcomingItemPreferenceFlow()
|
.toLocalDateTime(TimeZone.currentSystemDefault())
|
||||||
|
.date
|
||||||
|
.yearMonth,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val dialog = MutableStateFlow<Dialog?>(null)
|
||||||
|
|
||||||
|
private val upcoming = getUpcomingItemPreferenceFlow()
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.flatMapLatest {
|
.flatMapLatest {
|
||||||
getUpcomingManga.subscribe(
|
getUpcomingManga.subscribe(
|
||||||
excludedCategories = it.filterExcludedCategories,
|
excludedCategories = it.filterExcludedCategories,
|
||||||
includedCategories = it.filterIncludedCategories,
|
includedCategories = it.filterIncludedCategories,
|
||||||
)
|
)
|
||||||
.distinctUntilChanged()
|
|
||||||
}
|
}
|
||||||
.collectLatest {
|
.distinctUntilChanged()
|
||||||
mutableState.update { state ->
|
.map { it.toUpcomingUIModels() }
|
||||||
val upcomingItems = it.toUpcomingUIModels()
|
.flowOn(Dispatchers.IO)
|
||||||
state.copy(
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), emptyList())
|
||||||
items = upcomingItems,
|
|
||||||
events = upcomingItems.toEvents(),
|
val state: StateFlow<State> = combine(
|
||||||
headerIndexes = upcomingItems.getHeaderIndexes(),
|
upcoming,
|
||||||
|
selectedYearMonth,
|
||||||
|
dialog,
|
||||||
|
) { upcoming, selectedYearMonth, dialog ->
|
||||||
|
State(
|
||||||
|
selectedYearMonth = selectedYearMonth,
|
||||||
|
items = upcoming,
|
||||||
|
events = upcoming.toEvents(),
|
||||||
|
headerIndexes = upcoming.getHeaderIndexes(),
|
||||||
|
dialog = dialog,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
.stateIn(
|
||||||
}
|
viewModelScope,
|
||||||
}
|
SharingStarted.WhileSubscribed(5.seconds),
|
||||||
|
State(selectedYearMonth = selectedYearMonth.value),
|
||||||
|
)
|
||||||
|
|
||||||
private fun List<Manga>.toUpcomingUIModels(): List<UpcomingUIModel> {
|
private fun List<Manga>.toUpcomingUIModels(): List<UpcomingUIModel> {
|
||||||
var mangaCount = 0
|
var mangaCount = 0
|
||||||
@@ -101,7 +125,7 @@ class UpcomingViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSelectedYearMonth(yearMonth: YearMonth) {
|
fun setSelectedYearMonth(yearMonth: YearMonth) {
|
||||||
mutableState.update { it.copy(selectedYearMonth = yearMonth) }
|
selectedYearMonth.update { yearMonth }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUpcomingItemPreferenceFlow(): Flow<ItemPreferences> {
|
private fun getUpcomingItemPreferenceFlow(): Flow<ItemPreferences> {
|
||||||
@@ -117,11 +141,11 @@ class UpcomingViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resetDialog() {
|
fun resetDialog() {
|
||||||
mutableState.update { it.copy(dialog = null) }
|
dialog.update { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showFilterDialog() {
|
fun showFilterDialog() {
|
||||||
mutableState.update { it.copy(dialog = Dialog.FilterSheet) }
|
dialog.update { Dialog.FilterSheet }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cycleCategory(category: Category) {
|
fun cycleCategory(category: Category) {
|
||||||
@@ -143,10 +167,7 @@ class UpcomingViewModel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val selectedYearMonth: YearMonth = Clock.System.now()
|
val selectedYearMonth: YearMonth,
|
||||||
.toLocalDateTime(TimeZone.currentSystemDefault())
|
|
||||||
.date
|
|
||||||
.yearMonth,
|
|
||||||
val items: List<UpcomingUIModel> = listOf(),
|
val items: List<UpcomingUIModel> = listOf(),
|
||||||
val events: Map<LocalDate, Int> = mapOf(),
|
val events: Map<LocalDate, Int> = mapOf(),
|
||||||
val headerIndexes: Map<LocalDate, Int> = mapOf(),
|
val headerIndexes: Map<LocalDate, Int> = mapOf(),
|
||||||
|
|||||||
Reference in New Issue
Block a user