Few UI tweaks (#325)

- Move loading indicator to center of grid when changing filters so it's
more obvious
- Move 'remove' filter to top for the first filter dropdown for
consistency
- Use theme color for download progress indicator
- Show playback controls at the end of playing a video if there's
nothing up next
This commit is contained in:
damontecres 2025-11-25 16:36:33 -05:00 committed by GitHub
parent 0bfe3d2653
commit c0237a6123
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 58 additions and 44 deletions

View file

@ -21,7 +21,10 @@ fun CircularProgress(modifier: Modifier = Modifier) {
Box(modifier = modifier) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.border,
modifier = Modifier.align(Alignment.Center),
modifier =
Modifier
.fillMaxSize()
.align(Alignment.Center),
)
}
}

View file

@ -571,7 +571,7 @@ fun CollectionFolderGrid(
backgroundLoading == LoadingState.Loading,
modifier =
Modifier
.align(Alignment.BottomEnd)
.align(Alignment.Center)
.padding(16.dp),
) {
CircularProgress(

View file

@ -78,31 +78,7 @@ fun FilterByButton(
nestedDropDown = null
},
) {
filterOptions
.forEach { filterOption ->
val currentValue = remember(current) { filterOption.get(current) }
TvDropdownMenuItem(
leadingIcon = {
if (currentValue != null) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Filter active",
)
}
},
text = {
Text(
text = stringResource(filterOption.stringRes),
)
},
onClick = {
nestedDropDown = filterOption
},
modifier = Modifier,
)
}
if (filterCount > 0) {
HorizontalDivider()
val interactionSource = remember { MutableInteractionSource() }
val focused by interactionSource.collectIsFocusedAsState()
TvDropdownMenuItem(
@ -126,6 +102,34 @@ fun FilterByButton(
interactionSource = interactionSource,
modifier = Modifier,
)
HorizontalDivider()
}
filterOptions
.forEachIndexed { index, filterOption ->
val focusRequester = remember { FocusRequester() }
if (index == 0) {
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
}
val currentValue = remember(current) { filterOption.get(current) }
TvDropdownMenuItem(
leadingIcon = {
if (currentValue != null) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Filter active",
)
}
},
text = {
Text(
text = stringResource(filterOption.stringRes),
)
},
onClick = {
nestedDropDown = filterOption
},
modifier = Modifier.focusRequester(focusRequester),
)
}
}

View file

@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.debounce
*/
class ControllerViewState internal constructor(
@param:IntRange(from = 0)
private val hideMilliseconds: Long,
var hideMilliseconds: Long,
val controlsEnabled: Boolean,
) {
private val channel = Channel<Long>(CONFLATED)

View file

@ -167,17 +167,8 @@ fun PlaybackPage(
LaunchedEffect(Unit) {
focusRequester.tryRequestFocus()
}
val controllerViewState =
remember {
ControllerViewState(
preferences.appPreferences.playbackPreferences.controllerTimeoutMs,
true,
)
}.also {
LaunchedEffect(it) {
it.observe()
}
}
val controllerViewState = remember { viewModel.controllerViewState }
var skipIndicatorDuration by remember { mutableLongStateOf(0L) }
LaunchedEffect(controllerViewState.controlsVisible) {
// If controller shows/hides, immediately cancel the skip indicator

View file

@ -128,6 +128,12 @@ class PlaybackViewModel
}
internal val mutex = Mutex()
val controllerViewState =
ControllerViewState(
AppPreference.ControllerTimeout.defaultValue,
true,
)
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
val currentMediaInfo = MutableLiveData<CurrentMediaInfo>(CurrentMediaInfo.EMPTY)
@ -153,6 +159,7 @@ class PlaybackViewModel
val subtitleSearchLanguage = MutableLiveData<String>(Locale.current.language)
init {
viewModelScope.launch(ExceptionHandler()) { controllerViewState.observe() }
player.addListener(this)
(player as? ExoPlayer)?.addAnalyticsListener(this)
addCloseable { player.removeListener(this@PlaybackViewModel) }
@ -178,6 +185,8 @@ class PlaybackViewModel
) {
nextUp.value = null
this.preferences = preferences
controllerViewState.hideMilliseconds =
preferences.appPreferences.playbackPreferences.controllerTimeoutMs
this.deviceProfile = deviceProfile
this.forceTranscoding =
(destination as? Destination.Playback)?.forceTranscoding ?: false
@ -696,6 +705,9 @@ class PlaybackViewModel
Timber.v("Setting next up to ${nextItem?.id}")
withContext(Dispatchers.Main) {
nextUp.value = nextItem
if (nextItem == null) {
controllerViewState.showControls()
}
}
}
}

View file

@ -360,12 +360,16 @@ fun DownloadDialog(
if (progress != null) {
CircularProgressIndicator(
progress = { progress },
color = MaterialTheme.colorScheme.border,
modifier =
Modifier
.size(48.dp)
.padding(8.dp),
)
} else {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.border,
modifier =
Modifier
.size(48.dp)
.padding(8.dp),