Allow refreshing the feed tab

This commit is contained in:
Jobobby04 2023-02-04 19:58:09 -05:00
parent 59d307c6a1
commit 475fb82c66
3 changed files with 68 additions and 15 deletions

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -30,6 +31,7 @@ import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -49,6 +51,7 @@ import eu.kanade.presentation.components.BadgeGroup
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.MangaCover import eu.kanade.presentation.components.MangaCover
import eu.kanade.presentation.components.PullRefresh
import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topSmallPaddingValues import eu.kanade.presentation.util.topSmallPaddingValues
@ -57,6 +60,8 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState
import exh.savedsearches.models.FeedSavedSearch import exh.savedsearches.models.FeedSavedSearch
import exh.savedsearches.models.SavedSearch import exh.savedsearches.models.SavedSearch
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
import eu.kanade.domain.manga.model.MangaCover as MangaCoverData import eu.kanade.domain.manga.model.MangaCover as MangaCoverData
data class FeedItemUI( data class FeedItemUI(
@ -76,6 +81,7 @@ fun FeedScreen(
onClickSource: (CatalogueSource) -> Unit, onClickSource: (CatalogueSource) -> Unit,
onClickDelete: (FeedSavedSearch) -> Unit, onClickDelete: (FeedSavedSearch) -> Unit,
onClickManga: (Manga) -> Unit, onClickManga: (Manga) -> Unit,
onRefresh: () -> Unit,
getMangaState: @Composable (Manga, CatalogueSource?) -> State<Manga>, getMangaState: @Composable (Manga, CatalogueSource?) -> State<Manga>,
) { ) {
when { when {
@ -85,8 +91,24 @@ fun FeedScreen(
modifier = Modifier.padding(contentPadding), modifier = Modifier.padding(contentPadding),
) )
else -> { else -> {
var refreshing by remember { mutableStateOf(false) }
LaunchedEffect(refreshing) {
if (refreshing) {
delay(1.seconds)
refreshing = false
}
}
PullRefresh(
refreshing = refreshing && state.isLoadingItems,
onRefresh = {
refreshing = true
onRefresh()
},
enabled = !state.isLoadingItems,
) {
ScrollbarLazyColumn( ScrollbarLazyColumn(
contentPadding = contentPadding + topSmallPaddingValues, contentPadding = contentPadding + topSmallPaddingValues,
modifier = Modifier.fillMaxSize(),
) { ) {
items( items(
state.items.orEmpty(), state.items.orEmpty(),
@ -106,6 +128,7 @@ fun FeedScreen(
} }
} }
} }
}
@Composable @Composable
fun FeedItem( fun FeedItem(

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.feed
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.ui.util.fastAny
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetManga
@ -48,6 +49,8 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import xyz.nulldev.ts.api.http.serializer.FilterSerializer import xyz.nulldev.ts.api.http.serializer.FilterSerializer
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import eu.kanade.domain.manga.model.Manga as DomainManga import eu.kanade.domain.manga.model.Manga as DomainManga
/** /**
@ -71,6 +74,7 @@ open class FeedScreenModel(
val events = _events.receiveAsFlow() val events = _events.receiveAsFlow()
private val coroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher() private val coroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
var lastRefresh = System.currentTimeMillis().milliseconds
init { init {
getFeedSavedSearchGlobal.subscribe() getFeedSavedSearchGlobal.subscribe()
@ -95,6 +99,24 @@ open class FeedScreenModel(
.launchIn(coroutineScope) .launchIn(coroutineScope)
} }
fun init() {
if (lastRefresh - System.currentTimeMillis().milliseconds > 30.seconds) return
refresh()
}
fun refresh() {
lastRefresh = System.currentTimeMillis().milliseconds
coroutineScope.launchIO {
val newItems = state.value.items?.map { it.copy(results = null) } ?: return@launchIO
mutableState.update { state ->
state.copy(
items = newItems,
)
}
getFeed(newItems)
}
}
fun openAddDialog() { fun openAddDialog() {
coroutineScope.launchIO { coroutineScope.launchIO {
if (hasTooManyFeeds()) { if (hasTooManyFeeds()) {
@ -321,4 +343,7 @@ data class FeedScreenState(
val isEmpty val isEmpty
get() = items.isNullOrEmpty() get() = items.isNullOrEmpty()
val isLoadingItems
get() = items?.fastAny { it.results == null } != false
} }

View File

@ -30,6 +30,10 @@ fun Screen.feedTab(): TabContent {
val screenModel = rememberScreenModel { FeedScreenModel() } val screenModel = rememberScreenModel { FeedScreenModel() }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
LaunchedEffect(Unit) {
screenModel.init()
}
return TabContent( return TabContent(
titleRes = R.string.feed, titleRes = R.string.feed,
actions = listOf( actions = listOf(
@ -68,6 +72,7 @@ fun Screen.feedTab(): TabContent {
onClickManga = { manga -> onClickManga = { manga ->
navigator.push(MangaScreen(manga.id, true)) navigator.push(MangaScreen(manga.id, true))
}, },
onRefresh = screenModel::refresh,
getMangaState = { manga, source -> screenModel.getManga(initialManga = manga, source = source) }, getMangaState = { manga, source -> screenModel.getManga(initialManga = manga, source = source) },
) )