mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	HistoryScreen: Remove paging (#8125)
* HistoryScreen: Remove paging Per my testing performance-wise there's virtually no difference in loading time. * cleanups * add key and contentType
This commit is contained in:
		@@ -1,6 +1,5 @@
 | 
			
		||||
package eu.kanade.data.history
 | 
			
		||||
 | 
			
		||||
import androidx.paging.PagingSource
 | 
			
		||||
import eu.kanade.data.DatabaseHandler
 | 
			
		||||
import eu.kanade.data.chapter.chapterMapper
 | 
			
		||||
import eu.kanade.data.manga.mangaMapper
 | 
			
		||||
@@ -10,19 +9,17 @@ import eu.kanade.domain.history.model.HistoryWithRelations
 | 
			
		||||
import eu.kanade.domain.history.repository.HistoryRepository
 | 
			
		||||
import eu.kanade.domain.manga.model.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.logcat
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
 | 
			
		||||
class HistoryRepositoryImpl(
 | 
			
		||||
    private val handler: DatabaseHandler,
 | 
			
		||||
) : HistoryRepository {
 | 
			
		||||
 | 
			
		||||
    override fun getHistory(query: String): PagingSource<Long, HistoryWithRelations> {
 | 
			
		||||
        return handler.subscribeToPagingSource(
 | 
			
		||||
            countQuery = { historyViewQueries.countHistory(query) },
 | 
			
		||||
            queryProvider = { limit, offset ->
 | 
			
		||||
                historyViewQueries.history(query, limit, offset, historyWithRelationsMapper)
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    override fun getHistory(query: String): Flow<List<HistoryWithRelations>> {
 | 
			
		||||
        return handler.subscribeToList {
 | 
			
		||||
            historyViewQueries.history(query, historyWithRelationsMapper)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun getLastHistory(): HistoryWithRelations? {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
package eu.kanade.domain.history.interactor
 | 
			
		||||
 | 
			
		||||
import androidx.paging.Pager
 | 
			
		||||
import androidx.paging.PagingConfig
 | 
			
		||||
import androidx.paging.PagingData
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryWithRelations
 | 
			
		||||
import eu.kanade.domain.history.repository.HistoryRepository
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
@@ -10,12 +7,7 @@ import kotlinx.coroutines.flow.Flow
 | 
			
		||||
class GetHistory(
 | 
			
		||||
    private val repository: HistoryRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    fun subscribe(query: String): Flow<PagingData<HistoryWithRelations>> {
 | 
			
		||||
        return Pager(
 | 
			
		||||
            PagingConfig(pageSize = 25),
 | 
			
		||||
        ) {
 | 
			
		||||
            repository.getHistory(query)
 | 
			
		||||
        }.flow
 | 
			
		||||
    fun subscribe(query: String): Flow<List<HistoryWithRelations>> {
 | 
			
		||||
        return repository.getHistory(query)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
package eu.kanade.domain.history.repository
 | 
			
		||||
 | 
			
		||||
import androidx.paging.PagingSource
 | 
			
		||||
import eu.kanade.domain.chapter.model.Chapter
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryUpdate
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryWithRelations
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
interface HistoryRepository {
 | 
			
		||||
 | 
			
		||||
    fun getHistory(query: String): PagingSource<Long, HistoryWithRelations>
 | 
			
		||||
    fun getHistory(query: String): Flow<List<HistoryWithRelations>>
 | 
			
		||||
 | 
			
		||||
    suspend fun getLastHistory(): HistoryWithRelations?
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,9 @@ package eu.kanade.presentation.history
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.collectAsState
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.paging.LoadState
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryWithRelations
 | 
			
		||||
import eu.kanade.presentation.components.EmptyScreen
 | 
			
		||||
import eu.kanade.presentation.components.LoadingScreen
 | 
			
		||||
@@ -39,20 +40,25 @@ fun HistoryScreen(
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
    ) { contentPadding ->
 | 
			
		||||
        val items = presenter.getLazyHistory()
 | 
			
		||||
        when {
 | 
			
		||||
            items.loadState.refresh is LoadState.Loading && items.itemCount < 1 -> LoadingScreen()
 | 
			
		||||
            items.loadState.refresh is LoadState.NotLoading && items.itemCount < 1 -> EmptyScreen(textResource = R.string.information_no_recent_manga)
 | 
			
		||||
            else -> HistoryContent(
 | 
			
		||||
                history = items,
 | 
			
		||||
                contentPadding = contentPadding,
 | 
			
		||||
                onClickCover = onClickCover,
 | 
			
		||||
                onClickResume = onClickResume,
 | 
			
		||||
                onClickDelete = { presenter.dialog = Dialog.Delete(it) },
 | 
			
		||||
            )
 | 
			
		||||
        val items by presenter.getHistory().collectAsState(initial = null)
 | 
			
		||||
        items.let {
 | 
			
		||||
            if (it == null) {
 | 
			
		||||
                LoadingScreen()
 | 
			
		||||
            } else if (it.isEmpty()) {
 | 
			
		||||
                EmptyScreen(textResource = R.string.information_no_recent_manga)
 | 
			
		||||
            } else {
 | 
			
		||||
                HistoryContent(
 | 
			
		||||
                    history = it,
 | 
			
		||||
                    contentPadding = contentPadding,
 | 
			
		||||
                    onClickCover = onClickCover,
 | 
			
		||||
                    onClickResume = onClickResume,
 | 
			
		||||
                    onClickDelete = { item -> presenter.dialog = Dialog.Delete(item) },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        LaunchedEffect(items.loadState.refresh) {
 | 
			
		||||
            if (items.loadState.refresh is LoadState.NotLoading) {
 | 
			
		||||
 | 
			
		||||
        LaunchedEffect(items) {
 | 
			
		||||
            if (items != null) {
 | 
			
		||||
                (presenter.view?.activity as? MainActivity)?.ready = true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,10 @@
 | 
			
		||||
package eu.kanade.presentation.history.components
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.core.LinearEasing
 | 
			
		||||
import androidx.compose.animation.core.animateFloat
 | 
			
		||||
import androidx.compose.animation.core.infiniteRepeatable
 | 
			
		||||
import androidx.compose.animation.core.rememberInfiniteTransition
 | 
			
		||||
import androidx.compose.animation.core.tween
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.lazy.rememberLazyListState
 | 
			
		||||
import androidx.compose.foundation.lazy.items
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.geometry.Offset
 | 
			
		||||
import androidx.compose.ui.graphics.Brush
 | 
			
		||||
import androidx.paging.compose.LazyPagingItems
 | 
			
		||||
import androidx.paging.compose.items
 | 
			
		||||
import eu.kanade.domain.history.model.HistoryWithRelations
 | 
			
		||||
import eu.kanade.domain.ui.UiPreferences
 | 
			
		||||
import eu.kanade.presentation.components.RelativeDateHeader
 | 
			
		||||
@@ -21,7 +12,6 @@ import eu.kanade.presentation.components.ScrollbarLazyColumn
 | 
			
		||||
import eu.kanade.presentation.history.HistoryUiModel
 | 
			
		||||
import eu.kanade.presentation.util.bottomNavPaddingValues
 | 
			
		||||
import eu.kanade.presentation.util.plus
 | 
			
		||||
import eu.kanade.presentation.util.shimmerGradient
 | 
			
		||||
import eu.kanade.presentation.util.topPaddingValues
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
@@ -29,7 +19,7 @@ import java.text.DateFormat
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun HistoryContent(
 | 
			
		||||
    history: LazyPagingItems<HistoryUiModel>,
 | 
			
		||||
    history: List<HistoryUiModel>,
 | 
			
		||||
    contentPadding: PaddingValues,
 | 
			
		||||
    onClickCover: (HistoryWithRelations) -> Unit,
 | 
			
		||||
    onClickResume: (HistoryWithRelations) -> Unit,
 | 
			
		||||
@@ -41,14 +31,21 @@ fun HistoryContent(
 | 
			
		||||
 | 
			
		||||
    ScrollbarLazyColumn(
 | 
			
		||||
        contentPadding = contentPadding + bottomNavPaddingValues + topPaddingValues,
 | 
			
		||||
        state = rememberLazyListState(),
 | 
			
		||||
    ) {
 | 
			
		||||
        items(history) { item ->
 | 
			
		||||
        items(
 | 
			
		||||
            items = history,
 | 
			
		||||
            key = { "history-${it.hashCode()}" },
 | 
			
		||||
            contentType = {
 | 
			
		||||
                when (it) {
 | 
			
		||||
                    is HistoryUiModel.Header -> "header"
 | 
			
		||||
                    is HistoryUiModel.Item -> "item"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        ) { item ->
 | 
			
		||||
            when (item) {
 | 
			
		||||
                is HistoryUiModel.Header -> {
 | 
			
		||||
                    RelativeDateHeader(
 | 
			
		||||
                        modifier = Modifier
 | 
			
		||||
                            .animateItemPlacement(),
 | 
			
		||||
                        modifier = Modifier.animateItemPlacement(),
 | 
			
		||||
                        date = item.date,
 | 
			
		||||
                        relativeTime = relativeTime,
 | 
			
		||||
                        dateFormat = dateFormat,
 | 
			
		||||
@@ -64,31 +61,6 @@ fun HistoryContent(
 | 
			
		||||
                        onClickDelete = { onClickDelete(value) },
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                null -> {
 | 
			
		||||
                    val transition = rememberInfiniteTransition()
 | 
			
		||||
                    val translateAnimation = transition.animateFloat(
 | 
			
		||||
                        initialValue = 0f,
 | 
			
		||||
                        targetValue = 1000f,
 | 
			
		||||
                        animationSpec = infiniteRepeatable(
 | 
			
		||||
                            animation = tween(
 | 
			
		||||
                                durationMillis = 1000,
 | 
			
		||||
                                easing = LinearEasing,
 | 
			
		||||
                            ),
 | 
			
		||||
                        ),
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                    val brush = remember {
 | 
			
		||||
                        Brush.linearGradient(
 | 
			
		||||
                            colors = shimmerGradient,
 | 
			
		||||
                            start = Offset(0f, 0f),
 | 
			
		||||
                            end = Offset(
 | 
			
		||||
                                x = translateAnimation.value,
 | 
			
		||||
                                y = 00f,
 | 
			
		||||
                            ),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    HistoryItemShimmer(brush = brush)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
package eu.kanade.presentation.history.components
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.aspectRatio
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxHeight
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.outlined.Delete
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
@@ -20,9 +16,6 @@ import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.draw.drawBehind
 | 
			
		||||
import androidx.compose.ui.graphics.Brush
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.font.FontWeight
 | 
			
		||||
import androidx.compose.ui.text.style.TextOverflow
 | 
			
		||||
@@ -65,9 +58,10 @@ fun HistoryItem(
 | 
			
		||||
            val textStyle = MaterialTheme.typography.bodyMedium
 | 
			
		||||
            Text(
 | 
			
		||||
                text = history.title,
 | 
			
		||||
                fontWeight = FontWeight.SemiBold,
 | 
			
		||||
                maxLines = 2,
 | 
			
		||||
                overflow = TextOverflow.Ellipsis,
 | 
			
		||||
                style = textStyle.copy(fontWeight = FontWeight.SemiBold),
 | 
			
		||||
                style = textStyle,
 | 
			
		||||
            )
 | 
			
		||||
            val readAt = remember { history.readAt?.toTimestampString() ?: "" }
 | 
			
		||||
            Text(
 | 
			
		||||
@@ -95,49 +89,6 @@ fun HistoryItem(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun HistoryItemShimmer(brush: Brush) {
 | 
			
		||||
    Row(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .height(HISTORY_ITEM_HEIGHT)
 | 
			
		||||
            .padding(horizontal = horizontalPadding, vertical = 8.dp),
 | 
			
		||||
        verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
    ) {
 | 
			
		||||
        Box(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .fillMaxHeight()
 | 
			
		||||
                .aspectRatio(MangaCover.Book.ratio)
 | 
			
		||||
                .clip(RoundedCornerShape(4.dp))
 | 
			
		||||
                .drawBehind {
 | 
			
		||||
                    drawRect(brush = brush)
 | 
			
		||||
                },
 | 
			
		||||
        )
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .weight(1f)
 | 
			
		||||
                .padding(start = horizontalPadding, end = 8.dp),
 | 
			
		||||
        ) {
 | 
			
		||||
            Box(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .drawBehind {
 | 
			
		||||
                        drawRect(brush = brush)
 | 
			
		||||
                    }
 | 
			
		||||
                    .height(14.dp)
 | 
			
		||||
                    .fillMaxWidth(0.70f),
 | 
			
		||||
            )
 | 
			
		||||
            Box(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .padding(top = 4.dp)
 | 
			
		||||
                    .height(14.dp)
 | 
			
		||||
                    .fillMaxWidth(0.45f)
 | 
			
		||||
                    .drawBehind {
 | 
			
		||||
                        drawRect(brush = brush)
 | 
			
		||||
                    },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private val chapterFormatter = DecimalFormat(
 | 
			
		||||
    "#.###",
 | 
			
		||||
    DecimalFormatSymbols().apply { decimalSeparator = '.' },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
package eu.kanade.presentation.util
 | 
			
		||||
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
 | 
			
		||||
val shimmerGradient = listOf(
 | 
			
		||||
    Color.LightGray.copy(alpha = 0.8f),
 | 
			
		||||
    Color.LightGray.copy(alpha = 0.2f),
 | 
			
		||||
    Color.LightGray.copy(alpha = 0.8f),
 | 
			
		||||
)
 | 
			
		||||
@@ -5,14 +5,8 @@ import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.rememberCoroutineScope
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.paging.PagingData
 | 
			
		||||
import androidx.paging.cachedIn
 | 
			
		||||
import androidx.paging.compose.LazyPagingItems
 | 
			
		||||
import androidx.paging.compose.collectAsLazyPagingItems
 | 
			
		||||
import androidx.paging.insertSeparators
 | 
			
		||||
import androidx.paging.map
 | 
			
		||||
import eu.kanade.core.util.insertSeparators
 | 
			
		||||
import eu.kanade.domain.base.BasePreferences
 | 
			
		||||
import eu.kanade.domain.chapter.model.Chapter
 | 
			
		||||
import eu.kanade.domain.history.interactor.DeleteHistoryTable
 | 
			
		||||
@@ -32,6 +26,7 @@ import eu.kanade.tachiyomi.util.system.toast
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.catch
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
import kotlinx.coroutines.flow.receiveAsFlow
 | 
			
		||||
import logcat.LogPriority
 | 
			
		||||
@@ -57,11 +52,11 @@ class HistoryPresenter(
 | 
			
		||||
    val isIncognitoMode: Boolean by preferences.incognitoMode().asState()
 | 
			
		||||
 | 
			
		||||
    @Composable
 | 
			
		||||
    fun getLazyHistory(): LazyPagingItems<HistoryUiModel> {
 | 
			
		||||
        val scope = rememberCoroutineScope()
 | 
			
		||||
    fun getHistory(): Flow<List<HistoryUiModel>> {
 | 
			
		||||
        val query = searchQuery ?: ""
 | 
			
		||||
        val flow = remember(query) {
 | 
			
		||||
        return remember(query) {
 | 
			
		||||
            getHistory.subscribe(query)
 | 
			
		||||
                .distinctUntilChanged()
 | 
			
		||||
                .catch { error ->
 | 
			
		||||
                    logcat(LogPriority.ERROR, error)
 | 
			
		||||
                    _events.send(Event.InternalError)
 | 
			
		||||
@@ -69,15 +64,11 @@ class HistoryPresenter(
 | 
			
		||||
                .map { pagingData ->
 | 
			
		||||
                    pagingData.toHistoryUiModels()
 | 
			
		||||
                }
 | 
			
		||||
                .cachedIn(scope)
 | 
			
		||||
        }
 | 
			
		||||
        return flow.collectAsLazyPagingItems()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun PagingData<HistoryWithRelations>.toHistoryUiModels(): PagingData<HistoryUiModel> {
 | 
			
		||||
        return this.map {
 | 
			
		||||
            HistoryUiModel.Item(it)
 | 
			
		||||
        }
 | 
			
		||||
    private fun List<HistoryWithRelations>.toHistoryUiModels(): List<HistoryUiModel> {
 | 
			
		||||
        return 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,13 +26,6 @@ JOIN (
 | 
			
		||||
) AS max_last_read
 | 
			
		||||
ON chapters.manga_id = max_last_read.manga_id;
 | 
			
		||||
 | 
			
		||||
countHistory:
 | 
			
		||||
SELECT count(*)
 | 
			
		||||
FROM historyView
 | 
			
		||||
WHERE historyView.readAt > 0
 | 
			
		||||
AND maxReadAtChapterId = historyView.chapterId
 | 
			
		||||
AND lower(historyView.title) LIKE ('%' || :query || '%');
 | 
			
		||||
 | 
			
		||||
history:
 | 
			
		||||
SELECT
 | 
			
		||||
id,
 | 
			
		||||
@@ -50,8 +43,7 @@ FROM historyView
 | 
			
		||||
WHERE historyView.readAt > 0
 | 
			
		||||
AND maxReadAtChapterId = historyView.chapterId
 | 
			
		||||
AND lower(historyView.title) LIKE ('%' || :query || '%')
 | 
			
		||||
ORDER BY readAt DESC
 | 
			
		||||
LIMIT :limit OFFSET :offset;
 | 
			
		||||
ORDER BY readAt DESC;
 | 
			
		||||
 | 
			
		||||
getLatestHistory:
 | 
			
		||||
SELECT
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user