mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 17:11:51 +01:00
Properly show history state (#7052)
* Make `HistoryState` similar to `MigrateState` * Review Changes * Also cache the transformation Co-authored-by: Andreas <andreas.everos@gmail.com> * Fix States Co-authored-by: Andreas <andreas.everos@gmail.com>
This commit is contained in:
parent
aec980662f
commit
5bd5b21543
@ -17,7 +17,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@ -37,17 +36,19 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import androidx.paging.compose.items
|
import androidx.paging.compose.items
|
||||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.MangaCover
|
import eu.kanade.presentation.components.MangaCover
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
|
import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter
|
||||||
import eu.kanade.tachiyomi.ui.recent.history.UiModel
|
import eu.kanade.tachiyomi.ui.recent.history.HistoryState
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -66,37 +67,37 @@ fun HistoryScreen(
|
|||||||
onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
|
onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val state by presenter.state.collectAsState()
|
val state by presenter.state.collectAsState()
|
||||||
val history = state.list?.collectAsLazyPagingItems()
|
when (state) {
|
||||||
when {
|
is HistoryState.Loading -> LoadingScreen()
|
||||||
history == null -> {
|
is HistoryState.Error -> Text(text = (state as HistoryState.Error).error.message!!)
|
||||||
CircularProgressIndicator()
|
is HistoryState.Success ->
|
||||||
}
|
|
||||||
history.itemCount == 0 -> {
|
|
||||||
EmptyScreen(
|
|
||||||
textResource = R.string.information_no_recent_manga
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
HistoryContent(
|
HistoryContent(
|
||||||
nestedScroll = nestedScrollInterop,
|
nestedScroll = nestedScrollInterop,
|
||||||
history = history,
|
history = (state as HistoryState.Success).uiModels.collectAsLazyPagingItems(),
|
||||||
onClickCover = onClickCover,
|
onClickCover = onClickCover,
|
||||||
onClickResume = onClickResume,
|
onClickResume = onClickResume,
|
||||||
onClickDelete = onClickDelete,
|
onClickDelete = onClickDelete,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryContent(
|
fun HistoryContent(
|
||||||
history: LazyPagingItems<UiModel>,
|
history: LazyPagingItems<HistoryUiModel>,
|
||||||
onClickCover: (HistoryWithRelations) -> Unit,
|
onClickCover: (HistoryWithRelations) -> Unit,
|
||||||
onClickResume: (HistoryWithRelations) -> Unit,
|
onClickResume: (HistoryWithRelations) -> Unit,
|
||||||
onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
|
onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
|
||||||
preferences: PreferencesHelper = Injekt.get(),
|
preferences: PreferencesHelper = Injekt.get(),
|
||||||
nestedScroll: NestedScrollConnection
|
nestedScroll: NestedScrollConnection
|
||||||
) {
|
) {
|
||||||
|
if (history.loadState.refresh is LoadState.Loading) {
|
||||||
|
LoadingScreen()
|
||||||
|
return
|
||||||
|
} else if (history.loadState.refresh is LoadState.NotLoading && history.itemCount == 0) {
|
||||||
|
EmptyScreen(textResource = R.string.information_no_recent_manga)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val relativeTime: Int = remember { preferences.relativeTime().get() }
|
val relativeTime: Int = remember { preferences.relativeTime().get() }
|
||||||
val dateFormat: DateFormat = remember { preferences.dateFormat() }
|
val dateFormat: DateFormat = remember { preferences.dateFormat() }
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ fun HistoryContent(
|
|||||||
) {
|
) {
|
||||||
items(history) { item ->
|
items(history) { item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
is UiModel.Header -> {
|
is HistoryUiModel.Header -> {
|
||||||
HistoryHeader(
|
HistoryHeader(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.animateItemPlacement(),
|
.animateItemPlacement(),
|
||||||
@ -120,7 +121,7 @@ fun HistoryContent(
|
|||||||
dateFormat = dateFormat
|
dateFormat = dateFormat
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is UiModel.Item -> {
|
is HistoryUiModel.Item -> {
|
||||||
val value = item.item
|
val value = item.item
|
||||||
HistoryItem(
|
HistoryItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
@ -282,3 +283,8 @@ private val chapterFormatter = DecimalFormat(
|
|||||||
"#.###",
|
"#.###",
|
||||||
DecimalFormatSymbols().apply { decimalSeparator = '.' },
|
DecimalFormatSymbols().apply { decimalSeparator = '.' },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed class HistoryUiModel {
|
||||||
|
data class Header(val date: Date) : HistoryUiModel()
|
||||||
|
data class Item(val item: HistoryWithRelations) : HistoryUiModel()
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ import eu.kanade.domain.history.interactor.GetNextChapterForManga
|
|||||||
import eu.kanade.domain.history.interactor.RemoveHistoryById
|
import eu.kanade.domain.history.interactor.RemoveHistoryById
|
||||||
import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
|
import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
|
||||||
import eu.kanade.domain.history.model.HistoryWithRelations
|
import eu.kanade.domain.history.model.HistoryWithRelations
|
||||||
|
import eu.kanade.presentation.history.HistoryUiModel
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
@ -20,9 +21,10 @@ import eu.kanade.tachiyomi.util.system.toast
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
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.Date
|
import java.util.Date
|
||||||
@ -40,40 +42,45 @@ class HistoryPresenter(
|
|||||||
private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
|
private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
|
||||||
) : BasePresenter<HistoryController>() {
|
) : BasePresenter<HistoryController>() {
|
||||||
|
|
||||||
private var _query: MutableStateFlow<String> = MutableStateFlow("")
|
private val _query: MutableStateFlow<String> = MutableStateFlow("")
|
||||||
private var _state: MutableStateFlow<HistoryState> = MutableStateFlow(HistoryState.EMPTY)
|
private val _state: MutableStateFlow<HistoryState> = MutableStateFlow(HistoryState.Loading)
|
||||||
val state: StateFlow<HistoryState> = _state
|
val state: StateFlow<HistoryState> = _state.asStateFlow()
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
_state.update { state ->
|
_query.collectLatest { query ->
|
||||||
state.copy(
|
getHistory.subscribe(query)
|
||||||
list = _query.flatMapLatest { query ->
|
.catch { exception ->
|
||||||
getHistory.subscribe(query)
|
_state.emit(HistoryState.Error(exception))
|
||||||
.map { pagingData ->
|
}
|
||||||
pagingData
|
.map { pagingData ->
|
||||||
.map {
|
pagingData.toHistoryUiModels()
|
||||||
UiModel.Item(it)
|
}
|
||||||
}
|
.cachedIn(presenterScope)
|
||||||
.insertSeparators { before, after ->
|
.let { uiModelsPagingDataFlow ->
|
||||||
val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
_state.emit(HistoryState.Success(uiModelsPagingDataFlow))
|
||||||
val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
|
||||||
when {
|
|
||||||
beforeDate.time != afterDate.time && afterDate.time != 0L -> UiModel.Header(afterDate)
|
|
||||||
// Return null to avoid adding a separator between two items.
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.cachedIn(presenterScope),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PagingData<HistoryWithRelations>.toHistoryUiModels(): PagingData<HistoryUiModel> {
|
||||||
|
return this.map {
|
||||||
|
HistoryUiModel.Item(it)
|
||||||
|
}
|
||||||
|
.insertSeparators { before, after ->
|
||||||
|
val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
||||||
|
val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
||||||
|
when {
|
||||||
|
beforeDate.time != afterDate.time && afterDate.time != 0L -> HistoryUiModel.Header(afterDate)
|
||||||
|
// Return null to avoid adding a separator between two items.
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun search(query: String) {
|
fun search(query: String) {
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
_query.emit(query)
|
_query.emit(query)
|
||||||
@ -112,16 +119,8 @@ class HistoryPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class UiModel {
|
sealed class HistoryState {
|
||||||
data class Item(val item: HistoryWithRelations) : UiModel()
|
object Loading : HistoryState()
|
||||||
data class Header(val date: Date) : UiModel()
|
data class Error(val error: Throwable) : HistoryState()
|
||||||
}
|
data class Success(val uiModels: Flow<PagingData<HistoryUiModel>>) : HistoryState()
|
||||||
|
|
||||||
data class HistoryState(
|
|
||||||
val list: Flow<PagingData<UiModel>>? = null,
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val EMPTY = HistoryState(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user