Merge pull request #1 from bbara04/todos

Refactor: replace collectAsState with lifecycle-aware variant & add per-library visibility toggles
This commit is contained in:
Barnabás Balogh
2026-05-22 13:34:38 +02:00
committed by GitHub
26 changed files with 160 additions and 110 deletions

View File

@@ -59,6 +59,7 @@ dependencies {
implementation(project(":core-ui"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.androidx.activity.compose)

View File

@@ -18,8 +18,8 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -147,8 +147,8 @@ class TvActivity : ComponentActivity() {
updateViewModel: AppUpdateViewModel = hiltViewModel()
) {
var sessionLoaded by remember { mutableStateOf(false) }
val isLoggedIn by userSessionRepository.isLoggedIn.collectAsState(initial = false)
val availableUpdate by updateViewModel.availableUpdate.collectAsState()
val isLoggedIn by userSessionRepository.isLoggedIn.collectAsStateWithLifecycle(initialValue = false)
val availableUpdate by updateViewModel.availableUpdate.collectAsStateWithLifecycle()
val context = LocalContext.current
LaunchedEffect(Unit) {

View File

@@ -7,8 +7,8 @@ import androidx.compose.material.icons.outlined.Movie
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Tv
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
@@ -35,10 +35,10 @@ fun TvAppScreen(
viewModel: AppViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val libraries by viewModel.libraries.collectAsState()
val continueWatching by viewModel.continueWatching.collectAsState()
val nextUp by viewModel.nextUp.collectAsState()
val latestLibraryContent by viewModel.latestLibraryContent.collectAsState()
val libraries by viewModel.libraries.collectAsStateWithLifecycle()
val continueWatching by viewModel.continueWatching.collectAsStateWithLifecycle()
val nextUp by viewModel.nextUp.collectAsStateWithLifecycle()
val latestLibraryContent by viewModel.latestLibraryContent.collectAsStateWithLifecycle()
val navigationManager = LocalNavigationManager.current
@Suppress("UNCHECKED_CAST")

View File

@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
@@ -40,8 +40,8 @@ fun TvEpisodeScreen(
viewModel.selectEpisode(episode)
}
val episode = viewModel.episode.collectAsState()
val seriesTitle = viewModel.seriesTitle.collectAsState()
val episode = viewModel.episode.collectAsStateWithLifecycle()
val seriesTitle = viewModel.seriesTitle.collectAsStateWithLifecycle()
val selectedEpisode = episode.value
if (selectedEpisode == null) {

View File

@@ -15,7 +15,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@@ -35,7 +35,7 @@ fun TvLibraryScreen(
viewModel.selectLibrary(libraryId = library.id)
}
val libraryItems = viewModel.contents.collectAsState()
val libraryItems = viewModel.contents.collectAsStateWithLifecycle()
Scaffold(
modifier = modifier,

View File

@@ -1,8 +1,8 @@
package hu.bbara.purefin.ui.screen.login
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -16,18 +16,18 @@ fun TvLoginScreen(
viewModel: LoginViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val serverUrl by viewModel.url.collectAsState()
val phase by viewModel.phase.collectAsState()
val selectedServer by viewModel.selectedServer.collectAsState()
val discoveredServers by viewModel.discoveredServers.collectAsState()
val username by viewModel.username.collectAsState()
val password by viewModel.password.collectAsState()
val isSearching by viewModel.isSearching.collectAsState()
val isLoggingIn by viewModel.isLoggingIn.collectAsState()
val quickConnectAvailable by viewModel.quickConnectAvailable.collectAsState()
val quickConnectCode by viewModel.quickConnectCode.collectAsState()
val isQuickConnecting by viewModel.isQuickConnecting.collectAsState()
val errorMessage by viewModel.errorMessage.collectAsState()
val serverUrl by viewModel.url.collectAsStateWithLifecycle()
val phase by viewModel.phase.collectAsStateWithLifecycle()
val selectedServer by viewModel.selectedServer.collectAsStateWithLifecycle()
val discoveredServers by viewModel.discoveredServers.collectAsStateWithLifecycle()
val username by viewModel.username.collectAsStateWithLifecycle()
val password by viewModel.password.collectAsStateWithLifecycle()
val isSearching by viewModel.isSearching.collectAsStateWithLifecycle()
val isLoggingIn by viewModel.isLoggingIn.collectAsStateWithLifecycle()
val quickConnectAvailable by viewModel.quickConnectAvailable.collectAsStateWithLifecycle()
val quickConnectCode by viewModel.quickConnectCode.collectAsStateWithLifecycle()
val isQuickConnecting by viewModel.isQuickConnecting.collectAsStateWithLifecycle()
val errorMessage by viewModel.errorMessage.collectAsStateWithLifecycle()
val coroutineScope = rememberCoroutineScope()
val state = remember(

View File

@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
@@ -32,7 +32,7 @@ fun TvMovieScreen(
viewModel.selectMovie(movie)
}
val movieItem = viewModel.movie.collectAsState()
val movieItem = viewModel.movie.collectAsStateWithLifecycle()
if (movieItem.value != null) {
TvMovieScreenContent(

View File

@@ -29,8 +29,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -85,8 +85,8 @@ fun TvPlayerScreen(
viewModel.loadMedia(mediaId)
}
val uiState by viewModel.uiState.collectAsState()
val controlsVisible by viewModel.controlsVisible.collectAsState()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val controlsVisible by viewModel.controlsVisible.collectAsStateWithLifecycle()
var isPlaylistExpanded by remember { mutableStateOf(false) }
var trackPanelType by remember { mutableStateOf<TvTrackPanelType?>(null) }
var pendingTrackButtonFocus by remember { mutableStateOf<TvTrackPanelType?>(null) }

View File

@@ -8,8 +8,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -46,7 +46,7 @@ fun TvSeriesScreen(
viewModel.selectSeries(series)
}
val series = viewModel.series.collectAsState()
val series = viewModel.series.collectAsStateWithLifecycle()
val seriesData = series.value
if (seriesData != null && seriesData.seasons.isNotEmpty()) {

View File

@@ -26,8 +26,8 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -53,7 +53,7 @@ fun TvSettingsScreen(
onBack: () -> Unit = viewModel::onBack,
modifier: Modifier = Modifier
) {
val settingGroups by viewModel.settingGroups.collectAsState()
val settingGroups by viewModel.settingGroups.collectAsStateWithLifecycle()
val context = LocalContext.current
LaunchedEffect(viewModel, context) {
@@ -117,7 +117,7 @@ private fun TvSettingOptionItem(
) {
when (option) {
is RangeSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
TvNumberSettingItem(
title = option.title,
@@ -129,7 +129,7 @@ private fun TvSettingOptionItem(
}
is BooleanSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
TvBooleanSettingItem(
title = option.title,
@@ -140,7 +140,7 @@ private fun TvSettingOptionItem(
}
is StringSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
TvStringSettingItem(
title = option.title,
@@ -178,7 +178,7 @@ private fun <T> TvDropdownSettingOptionItem(
option: DropdownSetting<T>,
viewModel: SettingsViewModel
) {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
TvDropdownSettingItem(
title = option.title,

View File

@@ -58,6 +58,7 @@ dependencies {
implementation(project(":core-ui"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.androidx.activity.compose)

View File

@@ -16,8 +16,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -146,7 +146,7 @@ class PurefinActivity : ComponentActivity() {
navigationManager: NavigationManager
) {
var sessionLoaded by remember { mutableStateOf(false) }
val isLoggedIn by userSessionRepository.isLoggedIn.collectAsState(initial = false)
val isLoggedIn by userSessionRepository.isLoggedIn.collectAsStateWithLifecycle(initialValue = false)
LaunchedEffect(Unit) {
userSessionRepository.isLoggedIn.collect {

View File

@@ -11,8 +11,8 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
@@ -38,15 +38,15 @@ fun AppScreen(
updateViewModel: AppUpdateViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val libraries by viewModel.libraries.collectAsState()
val libraryContent by viewModel.latestLibraryContent.collectAsState()
val suggestions by viewModel.suggestions.collectAsState()
val continueWatching by viewModel.continueWatching.collectAsState()
val nextUp by viewModel.nextUp.collectAsState()
val isRefreshing by viewModel.isRefreshing.collectAsState()
val isOnline by viewModel.isOnline.collectAsState()
val isCheckingForUpdates by updateViewModel.isCheckingForUpdates.collectAsState()
val availableUpdate by updateViewModel.availableUpdate.collectAsState()
val libraries by viewModel.libraries.collectAsStateWithLifecycle()
val libraryContent by viewModel.latestLibraryContent.collectAsStateWithLifecycle()
val suggestions by viewModel.suggestions.collectAsStateWithLifecycle()
val continueWatching by viewModel.continueWatching.collectAsStateWithLifecycle()
val nextUp by viewModel.nextUp.collectAsStateWithLifecycle()
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
val isOnline by viewModel.isOnline.collectAsStateWithLifecycle()
val isCheckingForUpdates by updateViewModel.isCheckingForUpdates.collectAsStateWithLifecycle()
val availableUpdate by updateViewModel.availableUpdate.collectAsStateWithLifecycle()
val navigationManager = LocalNavigationManager.current
val snackbarHostState = remember { SnackbarHostState() }

View File

@@ -17,7 +17,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -32,8 +32,8 @@ fun DownloadsContent(
modifier: Modifier = Modifier,
viewModel: DownloadsViewModel = hiltViewModel(),
) {
val downloads = viewModel.downloads.collectAsState(emptyList())
val activeDownloads = viewModel.activeDownloads.collectAsState()
val downloads = viewModel.downloads.collectAsStateWithLifecycle(initialValue = emptyList())
val activeDownloads = viewModel.activeDownloads.collectAsStateWithLifecycle()
val isEmpty = downloads.value.isEmpty() && activeDownloads.value.isEmpty()

View File

@@ -8,7 +8,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
@@ -46,8 +46,8 @@ fun EpisodeScreen(
viewModel.selectEpisode(episode)
}
val episode = viewModel.episode.collectAsState()
val downloadState = viewModel.downloadState.collectAsState()
val episode = viewModel.episode.collectAsStateWithLifecycle()
val downloadState = viewModel.downloadState.collectAsStateWithLifecycle()
val notificationPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()

View File

@@ -24,8 +24,8 @@ import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
@@ -55,7 +55,7 @@ fun SearchOverlay(
BackHandler(onBack = onDismiss)
var query by rememberSaveable { mutableStateOf("") }
val searchResults by searchViewModel.searchResult.collectAsState()
val searchResults by searchViewModel.searchResult.collectAsStateWithLifecycle()
val dismissInteractionSource = remember { MutableInteractionSource() }
Box(

View File

@@ -14,7 +14,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -40,7 +40,7 @@ fun LibraryScreen(
viewModel.selectLibrary(libraryId = library.id)
}
val libraryItems = viewModel.contents.collectAsState()
val libraryItems = viewModel.contents.collectAsStateWithLifecycle()
Scaffold(
modifier = modifier.testTag(LibraryScreenTag),

View File

@@ -1,8 +1,8 @@
package hu.bbara.purefin.ui.screen.login
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -16,18 +16,18 @@ fun LoginScreen(
viewModel: LoginViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val serverUrl by viewModel.url.collectAsState()
val phase by viewModel.phase.collectAsState()
val selectedServer by viewModel.selectedServer.collectAsState()
val discoveredServers by viewModel.discoveredServers.collectAsState()
val username by viewModel.username.collectAsState()
val password by viewModel.password.collectAsState()
val isSearching by viewModel.isSearching.collectAsState()
val isLoggingIn by viewModel.isLoggingIn.collectAsState()
val quickConnectAvailable by viewModel.quickConnectAvailable.collectAsState()
val quickConnectCode by viewModel.quickConnectCode.collectAsState()
val isQuickConnecting by viewModel.isQuickConnecting.collectAsState()
val errorMessage by viewModel.errorMessage.collectAsState()
val serverUrl by viewModel.url.collectAsStateWithLifecycle()
val phase by viewModel.phase.collectAsStateWithLifecycle()
val selectedServer by viewModel.selectedServer.collectAsStateWithLifecycle()
val discoveredServers by viewModel.discoveredServers.collectAsStateWithLifecycle()
val username by viewModel.username.collectAsStateWithLifecycle()
val password by viewModel.password.collectAsStateWithLifecycle()
val isSearching by viewModel.isSearching.collectAsStateWithLifecycle()
val isLoggingIn by viewModel.isLoggingIn.collectAsStateWithLifecycle()
val quickConnectAvailable by viewModel.quickConnectAvailable.collectAsStateWithLifecycle()
val quickConnectCode by viewModel.quickConnectCode.collectAsStateWithLifecycle()
val isQuickConnecting by viewModel.isQuickConnecting.collectAsStateWithLifecycle()
val errorMessage by viewModel.errorMessage.collectAsStateWithLifecycle()
val coroutineScope = rememberCoroutineScope()
val state = remember(

View File

@@ -8,7 +8,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
@@ -39,8 +39,8 @@ fun MovieScreen(
viewModel.selectMovie(movie)
}
val movieItem = viewModel.movie.collectAsState()
val downloadState = viewModel.downloadState.collectAsState()
val movieItem = viewModel.movie.collectAsStateWithLifecycle()
val downloadState = viewModel.downloadState.collectAsStateWithLifecycle()
val notificationPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()

View File

@@ -25,8 +25,8 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -71,8 +71,8 @@ fun PlayerScreen(
viewModel: PlayerViewModel,
onBack: () -> Unit
) {
val uiState by viewModel.uiState.collectAsState()
val controlsVisible by viewModel.controlsVisible.collectAsState()
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val controlsVisible by viewModel.controlsVisible.collectAsStateWithLifecycle()
val context = LocalContext.current
val activity = context as? Activity

View File

@@ -6,8 +6,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -49,7 +49,7 @@ fun SeriesScreen(
viewModel.selectSeries(series)
}
val seriesState = viewModel.series.collectAsState()
val seriesState = viewModel.series.collectAsStateWithLifecycle()
val seriesData = seriesState.value
if (seriesData != null && seriesData.seasons.isNotEmpty()) {
@@ -58,9 +58,9 @@ fun SeriesScreen(
}
SeriesScreenInternal(
series = seriesData,
seriesDownloadState = viewModel.seriesDownloadState.collectAsState().value,
seasonDownloadState = viewModel.seasonDownloadState.collectAsState().value,
isSmartDownloadEnabled = viewModel.isSmartDownloadEnabled.collectAsState().value,
seriesDownloadState = viewModel.seriesDownloadState.collectAsStateWithLifecycle().value,
seasonDownloadState = viewModel.seasonDownloadState.collectAsStateWithLifecycle().value,
isSmartDownloadEnabled = viewModel.isSmartDownloadEnabled.collectAsStateWithLifecycle().value,
onDownloadOptionSelected = { option, selectedSeason ->
when (option) {
SeriesDownloadOption.SEASON ->

View File

@@ -14,8 +14,8 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -42,7 +42,7 @@ fun SettingsScreen(
viewModel: SettingsViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val settingGroups by viewModel.settingGroups.collectAsState()
val settingGroups by viewModel.settingGroups.collectAsStateWithLifecycle()
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(viewModel) {
@@ -107,7 +107,7 @@ private fun SettingOptionItem(
) {
when (option) {
is RangeSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
RangeSettingItem(
title = option.title,
@@ -119,7 +119,7 @@ private fun SettingOptionItem(
}
is BooleanSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
BooleanSettingItem(
title = option.title,
@@ -130,7 +130,7 @@ private fun SettingOptionItem(
}
is StringSetting -> {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
StringSettingItem(
title = option.title,
@@ -168,7 +168,7 @@ private fun <T> DropdownSettingOptionItem(
option: DropdownSetting<T>,
viewModel: SettingsViewModel
) {
val value by viewModel.value(option).collectAsState(initial = null)
val value by viewModel.value(option).collectAsStateWithLifecycle(initialValue = null)
value?.let {
DropdownSettingItem(
title = option.title,

View File

@@ -9,6 +9,7 @@ import hu.bbara.purefin.core.data.NetworkMonitor
import hu.bbara.purefin.core.data.UserSessionRepository
import hu.bbara.purefin.core.download.MediaDownloadController
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
import hu.bbara.purefin.core.settings.SettingsRepository
import hu.bbara.purefin.core.model.EpisodeUiModel
import hu.bbara.purefin.core.model.LibraryUiModel
import hu.bbara.purefin.core.model.MediaUiModel
@@ -41,6 +42,7 @@ class AppViewModel @Inject constructor(
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
private val navigationManager: NavigationManager,
private val mediaDownloadManager: MediaDownloadController,
private val settingsRepository: SettingsRepository,
networkMonitor: NetworkMonitor,
) : ViewModel() {
@@ -147,9 +149,14 @@ class AppViewModel @Inject constructor(
homeRepository.latestLibraryContent,
localMediaRepository.movies,
localMediaRepository.series,
localMediaRepository.episodes
) { libraryMap, moviesMap, seriesMap, episodesMap ->
libraryMap.mapValues { (_, items) ->
localMediaRepository.episodes,
settingsRepository.settings,
) { libraryMap, moviesMap, seriesMap, episodesMap, settings ->
libraryMap
.filter { (libraryId, _) ->
settings.booleanSettings["home_library_visible_$libraryId"] ?: true
}
.mapValues { (_, items) ->
items.mapNotNull { media ->
when (media) {
is Media.MovieMedia -> moviesMap[media.movieId]?.let {

View File

@@ -0,0 +1,35 @@
package hu.bbara.purefin.core.feature.settings
import hu.bbara.purefin.core.data.HomeRepository
import hu.bbara.purefin.core.settings.BooleanSetting
import hu.bbara.purefin.core.settings.SettingGroup
import hu.bbara.purefin.core.settings.SettingsGroupProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class HomeLibrarySettingsProvider @Inject constructor(
private val homeRepository: HomeRepository,
) : SettingsGroupProvider {
override val settingGroups: Flow<List<SettingGroup>> = homeRepository.libraries.map { libraries ->
if (libraries.isEmpty()) return@map emptyList()
val options = libraries.map { library ->
BooleanSetting(
key = "home_library_visible_${library.id}",
title = library.name,
defaultValue = true
)
}
listOf(
SettingGroup(
title = "Home Screen Libraries",
options = options
)
)
}
}

View File

@@ -5,6 +5,7 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet
import hu.bbara.purefin.core.feature.settings.HomeLibrarySettingsProvider
import hu.bbara.purefin.core.feature.settings.LogoutSettingsProvider
import hu.bbara.purefin.core.feature.update.AppUpdateController
@@ -16,6 +17,10 @@ abstract class SettingsGroupProviderModule {
@IntoSet
abstract fun bindAppUpdateSettingsProvider(impl: AppUpdateController): SettingsGroupProvider
@Binds
@IntoSet
abstract fun bindHomeLibrarySettingsProvider(impl: HomeLibrarySettingsProvider): SettingsGroupProvider
@Binds
@IntoSet
abstract fun bindLogoutSettingsProvider(impl: LogoutSettingsProvider): SettingsGroupProvider

View File

@@ -37,6 +37,7 @@ androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-co
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-navigation3 = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-navigation3", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }