Fix some UI preference changes not being reflected immediately (#1146)

This commit is contained in:
Ray 2026-03-25 21:55:00 -04:00 committed by GitHub
parent 2d863e9c32
commit c92de3c33e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 14 deletions

View file

@ -12,6 +12,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -45,6 +46,7 @@ fun MainContent(
screensaverService: ScreensaverService, screensaverService: ScreensaverService,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val preferences by rememberUpdatedState(UserPreferences(appPreferences))
Surface( Surface(
modifier = modifier =
modifier modifier
@ -93,10 +95,6 @@ fun MainContent(
backdropService.clearBackdrop() backdropService.clearBackdrop()
} }
val current = key.current val current = key.current
val preferences =
remember(appPreferences) {
UserPreferences(appPreferences)
}
var showContent by remember { var showContent by remember {
mutableStateOf(true) mutableStateOf(true)
} }

View file

@ -38,6 +38,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.acra.ACRA import org.acra.ACRA
@ -434,3 +436,8 @@ fun Response<BaseItemDtoQueryResult>.toBaseItems(
fun Int?.gt(that: Int) = (this ?: 0) > that fun Int?.gt(that: Int) = (this ?: 0) > that
fun Int?.lt(that: Int) = (this ?: 0) < that fun Int?.lt(that: Int) = (this ?: 0) < that
/**
* Easy way to combine two flows into a [Pair]
*/
fun <T1, T2> Flow<T1>.combinePair(flow: Flow<T2>): Flow<Pair<T1, T2>> = combine(flow) { t1, t2 -> Pair(t1, t2) }

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -85,6 +86,7 @@ fun EpisodeDetails(
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) } var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) } var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending) val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
val canDelete by viewModel.canDelete.collectAsState()
val preferredSubtitleLanguage = val preferredSubtitleLanguage =
viewModel.serverRepository.currentUserDto viewModel.serverRepository.currentUserDto
@ -226,7 +228,7 @@ fun EpisodeDetails(
onClearChosenStreams = { onClearChosenStreams = {
viewModel.clearChosenStreams(chosenStreams) viewModel.clearChosenStreams(chosenStreams)
}, },
canDelete = viewModel.canDelete, canDelete = canDelete,
), ),
) )
}, },
@ -236,7 +238,7 @@ fun EpisodeDetails(
favoriteOnClick = { favoriteOnClick = {
viewModel.setFavorite(ep.id, !ep.favorite) viewModel.setFavorite(ep.id, !ep.favorite)
}, },
canDelete = viewModel.canDelete, canDelete = canDelete,
deleteOnClick = { showDeleteDialog = ep }, deleteOnClick = { showDeleteDialog = ep },
modifier = modifier, modifier = modifier,
) )

View file

@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.detail.episode
import android.content.Context import android.content.Context
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.asFlow
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.ItemPlaybackRepository import com.github.damontecres.wholphin.data.ItemPlaybackRepository
@ -19,6 +20,8 @@ import com.github.damontecres.wholphin.services.StreamChoiceService
import com.github.damontecres.wholphin.services.ThemeSongPlayer import com.github.damontecres.wholphin.services.ThemeSongPlayer
import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.services.deleteItem import com.github.damontecres.wholphin.services.deleteItem
import com.github.damontecres.wholphin.ui.combinePair
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.setValueOnMain
@ -34,6 +37,11 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.ApiClient
@ -68,11 +76,19 @@ class EpisodeViewModel
val item = MutableLiveData<BaseItem?>(null) val item = MutableLiveData<BaseItem?>(null)
val chosenStreams = MutableLiveData<ChosenStreams?>(null) val chosenStreams = MutableLiveData<ChosenStreams?>(null)
var canDelete: Boolean = false val canDelete = MutableStateFlow(false)
private set
init { init {
init() init()
viewModelScope.launchDefault {
item
.asFlow()
.filterNotNull()
.combinePair(userPreferencesService.flow.map { it.appPreferences })
.collectLatest { (item, preferences) ->
canDelete.update { mediaManagementService.canDelete(item, preferences) }
}
}
} }
private fun fetchAndSetItem(): Deferred<BaseItem> = private fun fetchAndSetItem(): Deferred<BaseItem> =
@ -101,7 +117,6 @@ class EpisodeViewModel
) { ) {
val prefs = userPreferencesService.getCurrent() val prefs = userPreferencesService.getCurrent()
val item = fetchAndSetItem().await() val item = fetchAndSetItem().await()
canDelete = mediaManagementService.canDelete(item)
val result = itemPlaybackRepository.getSelectedTracks(item.id, item, prefs) val result = itemPlaybackRepository.getSelectedTracks(item.id, item, prefs)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
this@EpisodeViewModel.item.value = item this@EpisodeViewModel.item.value = item

View file

@ -106,6 +106,7 @@ fun MovieDetails(
val loading by viewModel.loading.observeAsState(LoadingState.Loading) val loading by viewModel.loading.observeAsState(LoadingState.Loading)
val chosenStreams by viewModel.chosenStreams.observeAsState(null) val chosenStreams by viewModel.chosenStreams.observeAsState(null)
val discovered by viewModel.discovered.collectAsState() val discovered by viewModel.discovered.collectAsState()
val canDelete by viewModel.canDelete.collectAsState()
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) } var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
var moreDialog by remember { mutableStateOf<DialogParams?>(null) } var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
@ -211,7 +212,7 @@ fun MovieDetails(
seriesId = null, seriesId = null,
sourceId = chosenStreams?.source?.id?.toUUIDOrNull(), sourceId = chosenStreams?.source?.id?.toUUIDOrNull(),
canClearChosenStreams = chosenStreams?.itemPlayback != null || chosenStreams?.plc != null, canClearChosenStreams = chosenStreams?.itemPlayback != null || chosenStreams?.plc != null,
canDelete = viewModel.canDelete, canDelete = canDelete,
actions = moreActions, actions = moreActions,
onChooseVersion = { onChooseVersion = {
chooseVersion = chooseVersion =
@ -323,7 +324,7 @@ fun MovieDetails(
onClickDiscover = { index, item -> onClickDiscover = { index, item ->
viewModel.navigateTo(item.destination) viewModel.navigateTo(item.destination)
}, },
canDelete = viewModel.canDelete, canDelete = canDelete,
deleteOnClick = { showDeleteDialog = movie }, deleteOnClick = { showDeleteDialog = movie },
modifier = modifier, modifier = modifier,
) )

View file

@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.detail.movie
import android.content.Context import android.content.Context
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.asFlow
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.ExtrasItem import com.github.damontecres.wholphin.data.ExtrasItem
@ -29,6 +30,8 @@ import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.services.deleteItem import com.github.damontecres.wholphin.services.deleteItem
import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.ui.combinePair
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
@ -46,6 +49,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -93,11 +99,19 @@ class MovieViewModel
val chosenStreams = MutableLiveData<ChosenStreams?>(null) val chosenStreams = MutableLiveData<ChosenStreams?>(null)
val discovered = MutableStateFlow<List<DiscoverItem>>(listOf()) val discovered = MutableStateFlow<List<DiscoverItem>>(listOf())
var canDelete: Boolean = false val canDelete = MutableStateFlow(false)
private set
init { init {
init() init()
viewModelScope.launchDefault {
item
.asFlow()
.filterNotNull()
.combinePair(userPreferencesService.flow.map { it.appPreferences })
.collectLatest { (item, preferences) ->
canDelete.update { mediaManagementService.canDelete(item, preferences) }
}
}
} }
private fun fetchAndSetItem(): Deferred<BaseItem> = private fun fetchAndSetItem(): Deferred<BaseItem> =
@ -112,7 +126,6 @@ class MovieViewModel
api.userLibraryApi.getItem(itemId).content.let { api.userLibraryApi.getItem(itemId).content.let {
BaseItem.from(it, api) BaseItem.from(it, api)
} }
canDelete = mediaManagementService.canDelete(item)
this@MovieViewModel.item.setValueOnMain(item) this@MovieViewModel.item.setValueOnMain(item)
item item
} }