Allow refreshing the feed tab
This commit is contained in:
parent
59d307c6a1
commit
475fb82c66
@ -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(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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) },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user