Przeglądaj źródła

Properly show history state (#7052)

* Make `HistoryState` similar to `MigrateState`

* Review Changes

* Also cache the transformation

Co-authored-by: Andreas <[email protected]>

* Fix States

Co-authored-by: Andreas <[email protected]>
FourTOne5 2 lat temu
rodzic
commit
5bd5b21543

+ 24 - 18
app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt

@@ -17,7 +17,6 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Delete
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.Checkbox
-import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 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.style.TextOverflow
 import androidx.compose.ui.unit.dp
+import androidx.paging.LoadState
 import androidx.paging.compose.LazyPagingItems
 import androidx.paging.compose.collectAsLazyPagingItems
 import androidx.paging.compose.items
 import eu.kanade.domain.history.model.HistoryWithRelations
 import eu.kanade.presentation.components.EmptyScreen
+import eu.kanade.presentation.components.LoadingScreen
 import eu.kanade.presentation.components.MangaCover
 import eu.kanade.presentation.util.horizontalPadding
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 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.toTimestampString
 import uy.kohesive.injekt.Injekt
@@ -66,37 +67,37 @@ fun HistoryScreen(
     onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
 ) {
     val state by presenter.state.collectAsState()
-    val history = state.list?.collectAsLazyPagingItems()
-    when {
-        history == null -> {
-            CircularProgressIndicator()
-        }
-        history.itemCount == 0 -> {
-            EmptyScreen(
-                textResource = R.string.information_no_recent_manga
-            )
-        }
-        else -> {
+    when (state) {
+        is HistoryState.Loading -> LoadingScreen()
+        is HistoryState.Error -> Text(text = (state as HistoryState.Error).error.message!!)
+        is HistoryState.Success ->
             HistoryContent(
                 nestedScroll = nestedScrollInterop,
-                history = history,
+                history = (state as HistoryState.Success).uiModels.collectAsLazyPagingItems(),
                 onClickCover = onClickCover,
                 onClickResume = onClickResume,
                 onClickDelete = onClickDelete,
             )
-        }
     }
 }
 
 @Composable
 fun HistoryContent(
-    history: LazyPagingItems<UiModel>,
+    history: LazyPagingItems<HistoryUiModel>,
     onClickCover: (HistoryWithRelations) -> Unit,
     onClickResume: (HistoryWithRelations) -> Unit,
     onClickDelete: (HistoryWithRelations, Boolean) -> Unit,
     preferences: PreferencesHelper = Injekt.get(),
     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 dateFormat: DateFormat = remember { preferences.dateFormat() }
 
@@ -111,7 +112,7 @@ fun HistoryContent(
     ) {
         items(history) { item ->
             when (item) {
-                is UiModel.Header -> {
+                is HistoryUiModel.Header -> {
                     HistoryHeader(
                         modifier = Modifier
                             .animateItemPlacement(),
@@ -120,7 +121,7 @@ fun HistoryContent(
                         dateFormat = dateFormat
                     )
                 }
-                is UiModel.Item -> {
+                is HistoryUiModel.Item -> {
                     val value = item.item
                     HistoryItem(
                         modifier = Modifier.animateItemPlacement(),
@@ -282,3 +283,8 @@ private val chapterFormatter = DecimalFormat(
     "#.###",
     DecimalFormatSymbols().apply { decimalSeparator = '.' },
 )
+
+sealed class HistoryUiModel {
+    data class Header(val date: Date) : HistoryUiModel()
+    data class Item(val item: HistoryWithRelations) : HistoryUiModel()
+}

+ 37 - 38
app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt

@@ -11,6 +11,7 @@ import eu.kanade.domain.history.interactor.GetNextChapterForManga
 import eu.kanade.domain.history.interactor.RemoveHistoryById
 import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
 import eu.kanade.domain.history.model.HistoryWithRelations
+import eu.kanade.presentation.history.HistoryUiModel
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 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.MutableStateFlow
 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.update
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import java.util.Date
@@ -40,40 +42,45 @@ class HistoryPresenter(
     private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(),
 ) : BasePresenter<HistoryController>() {
 
-    private var _query: MutableStateFlow<String> = MutableStateFlow("")
-    private var _state: MutableStateFlow<HistoryState> = MutableStateFlow(HistoryState.EMPTY)
-    val state: StateFlow<HistoryState> = _state
+    private val _query: MutableStateFlow<String> = MutableStateFlow("")
+    private val _state: MutableStateFlow<HistoryState> = MutableStateFlow(HistoryState.Loading)
+    val state: StateFlow<HistoryState> = _state.asStateFlow()
 
     override fun onCreate(savedState: Bundle?) {
         super.onCreate(savedState)
 
         presenterScope.launchIO {
-            _state.update { state ->
-                state.copy(
-                    list = _query.flatMapLatest { query ->
-                        getHistory.subscribe(query)
-                            .map { pagingData ->
-                                pagingData
-                                    .map {
-                                        UiModel.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 -> UiModel.Header(afterDate)
-                                            // Return null to avoid adding a separator between two items.
-                                            else -> null
-                                        }
-                                    }
-                            }
+            _query.collectLatest { query ->
+                getHistory.subscribe(query)
+                    .catch { exception ->
+                        _state.emit(HistoryState.Error(exception))
+                    }
+                    .map { pagingData ->
+                        pagingData.toHistoryUiModels()
+                    }
+                    .cachedIn(presenterScope)
+                    .let { uiModelsPagingDataFlow ->
+                        _state.emit(HistoryState.Success(uiModelsPagingDataFlow))
                     }
-                        .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) {
         presenterScope.launchIO {
             _query.emit(query)
@@ -112,16 +119,8 @@ class HistoryPresenter(
     }
 }
 
-sealed class UiModel {
-    data class Item(val item: HistoryWithRelations) : UiModel()
-    data class Header(val date: Date) : UiModel()
-}
-
-data class HistoryState(
-    val list: Flow<PagingData<UiModel>>? = null,
-) {
-
-    companion object {
-        val EMPTY = HistoryState(null)
-    }
+sealed class HistoryState {
+    object Loading : HistoryState()
+    data class Error(val error: Throwable) : HistoryState()
+    data class Success(val uiModels: Flow<PagingData<HistoryUiModel>>) : HistoryState()
 }