diff --git a/app/src/main/java/hu/bbara/purefin/app/home/HomePage.kt b/app/src/main/java/hu/bbara/purefin/app/home/HomePage.kt index bd2950e..4fed9b3 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/HomePage.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/HomePage.kt @@ -47,6 +47,7 @@ fun HomePage( val continueWatching = viewModel.continueWatching.collectAsState() val nextUp = viewModel.nextUp.collectAsState() val latestLibraryContent = viewModel.latestLibraryContent.collectAsState() + val isRefreshing = viewModel.isRefreshing.collectAsState() LifecycleResumeEffect(Unit) { viewModel.onResumed() @@ -89,6 +90,8 @@ fun HomePage( libraryContent = latestLibraryContent.value, continueWatching = continueWatching.value, nextUp = nextUp.value, + isRefreshing = isRefreshing.value, + onRefresh = viewModel::onRefresh, onMovieSelected = viewModel::onMovieSelected, onSeriesSelected = viewModel::onSeriesSelected, onEpisodeSelected = viewModel::onEpisodeSelected, diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt index 81413cf..f66b952 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt @@ -6,7 +6,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -16,21 +18,29 @@ import hu.bbara.purefin.feature.shared.home.NextUpItem import hu.bbara.purefin.feature.shared.home.PosterItem import org.jellyfin.sdk.model.UUID +@OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeContent( libraries: List, libraryContent: Map>, continueWatching: List, nextUp: List, + isRefreshing: Boolean, + onRefresh: () -> Unit, onMovieSelected: (UUID) -> Unit, onSeriesSelected: (UUID) -> Unit, onEpisodeSelected: (UUID, UUID, UUID) -> Unit, modifier: Modifier = Modifier ) { - LazyColumn( + PullToRefreshBox( + isRefreshing = isRefreshing, + onRefresh = onRefresh, modifier = modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background) + ) { + LazyColumn( + modifier = Modifier.fillMaxSize() ) { item { Spacer(modifier = Modifier.height(8.dp)) @@ -69,4 +79,5 @@ fun HomeContent( Spacer(modifier = Modifier.height(8.dp)) } } + } } diff --git a/core/data/src/main/java/hu/bbara/purefin/core/data/InMemoryAppContentRepository.kt b/core/data/src/main/java/hu/bbara/purefin/core/data/InMemoryAppContentRepository.kt index 07f6b76..3a07634 100644 --- a/core/data/src/main/java/hu/bbara/purefin/core/data/InMemoryAppContentRepository.kt +++ b/core/data/src/main/java/hu/bbara/purefin/core/data/InMemoryAppContentRepository.kt @@ -297,15 +297,18 @@ class InMemoryAppContentRepository @Inject constructor( if (!isOnline) return if(loadJob?.isActive == true) { + loadJob?.join() return } - loadJob = scope.launch { + val job = scope.launch { loadLibraries() loadContinueWatching() loadNextUp() loadLatestLibraryContent() persistHomeCache() } + loadJob = job + job.join() } private suspend fun serverUrl(): String { diff --git a/feature/shared/src/main/java/hu/bbara/purefin/feature/shared/home/HomePageViewModel.kt b/feature/shared/src/main/java/hu/bbara/purefin/feature/shared/home/HomePageViewModel.kt index 948645f..51b8043 100644 --- a/feature/shared/src/main/java/hu/bbara/purefin/feature/shared/home/HomePageViewModel.kt +++ b/feature/shared/src/main/java/hu/bbara/purefin/feature/shared/home/HomePageViewModel.kt @@ -13,7 +13,10 @@ import hu.bbara.purefin.core.data.navigation.Route import hu.bbara.purefin.core.data.navigation.SeriesDto import hu.bbara.purefin.core.data.session.UserSessionRepository import hu.bbara.purefin.core.model.Media +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -31,6 +34,9 @@ class HomePageViewModel @Inject constructor( private val refreshHomeDataUseCase: RefreshHomeDataUseCase ) : ViewModel() { + private val _isRefreshing = MutableStateFlow(false) + val isRefreshing: StateFlow = _isRefreshing.asStateFlow() + private val _url = userSessionRepository.serverUrl.stateIn( scope = viewModelScope, started = SharingStarted.Eagerly, @@ -195,6 +201,19 @@ class HomePageViewModel @Inject constructor( } } + fun onRefresh() { + viewModelScope.launch { + _isRefreshing.value = true + try { + refreshHomeDataUseCase() + } catch (e: Exception) { + // Refresh is best-effort; don't crash on failure + } finally { + _isRefreshing.value = false + } + } + } + fun logout() { viewModelScope.launch { userSessionRepository.setLoggedIn(false)