From ff91fa87e26c947b517e466387af4d73e040e56d Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:31:02 -0500 Subject: [PATCH] Few minor UI fixes (#343) - Keep the currently selected nav drawer item in view and focus on when entering - Adjust star rating height so that the row is consistent preventing resizing when moving between episodes with & without a rating - Simplify checks when switching to a new server and use more specific keys to prevent needing to switch servers twice --- .../damontecres/wholphin/MainActivity.kt | 26 ++++---- .../wholphin/ui/components/DotSeparatedRow.kt | 20 +++++-- .../wholphin/ui/components/Rating.kt | 6 +- .../damontecres/wholphin/ui/nav/NavDrawer.kt | 39 +++++++++++- .../wholphin/ui/setup/ServerList.kt | 9 ++- .../wholphin/ui/setup/SwitchServerContent.kt | 5 +- .../ui/setup/SwitchServerViewModel.kt | 59 ++++++++++++++----- 7 files changed, 125 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index 175db929..e544c0fb 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -110,15 +110,15 @@ class MainActivity : AppCompatActivity() { } isRestoringSession = false } - val server by serverRepository.currentServer.observeAsState() - val user by serverRepository.currentUser.observeAsState() - val userDto by serverRepository.currentUserDto.observeAsState() + val current by serverRepository.current.observeAsState() val preferences = - UserPreferences( - appPreferences, - userDto?.configuration ?: DefaultUserConfiguration, - ) + remember(current) { + UserPreferences( + appPreferences, + current?.userDto?.configuration ?: DefaultUserConfiguration, + ) + } if (isRestoringSession) { Box( @@ -131,9 +131,9 @@ class MainActivity : AppCompatActivity() { ) } } else { - key(server, user) { + key(current) { val initialDestination = - if (server != null && user != null) { + if (current != null) { Destination.Home() } else { Destination.ServerList @@ -150,16 +150,16 @@ class MainActivity : AppCompatActivity() { } } val deviceProfile = - remember(server, appPreferences) { + remember(current, appPreferences) { createDeviceProfile( this@MainActivity, preferences, - server?.serverVersion, + current?.server?.serverVersion, ) } ApplicationContent( - user = user, - server = server, + user = current?.user, + server = current?.server, navigationManager = navigationManager, preferences = preferences, deviceProfile = deviceProfile, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt index eb206f03..8fa88aa6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/DotSeparatedRow.kt @@ -18,6 +18,7 @@ package com.github.damontecres.wholphin.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -78,10 +79,19 @@ fun DotSeparatedRow( @Composable private fun DotSeparatedRowPreview() { WholphinTheme { - DotSeparatedRow( - texts = listOf("2025", "1h 48m", "PG-13", "1h 30m left"), - rating = 7.5f, - modifier = Modifier, - ) + Column { + DotSeparatedRow( + texts = listOf("2025", "1h 48m", "PG-13", "1h 30m left"), + rating = 7.5f, + modifier = Modifier, + textStyle = MaterialTheme.typography.titleMedium, + ) + DotSeparatedRow( + texts = listOf("2025", "1h 48m", "PG-13", "1h 30m left 7.5"), + rating = 7.5f, + modifier = Modifier, + textStyle = MaterialTheme.typography.titleLarge, + ) + } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt index a1e8598b..c849462c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Rating.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.components +import android.R.attr.textStyle import androidx.compose.foundation.background import androidx.compose.foundation.focusGroup import androidx.compose.foundation.layout.Arrangement @@ -35,10 +36,12 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.tv.material3.Icon import androidx.tv.material3.LocalContentColor +import androidx.tv.material3.LocalTextStyle import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.wholphin.ui.AppColors @@ -100,11 +103,12 @@ fun SimpleStarRating( text = text, modifier = Modifier, ) + val height = with(LocalDensity.current) { LocalTextStyle.current.fontSize.toDp() } Icon( imageVector = Icons.Filled.Star, tint = starColor, contentDescription = null, - modifier = Modifier, + modifier = Modifier.height(height), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt index 4a162436..4b135a15 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt @@ -41,9 +41,12 @@ import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -85,12 +88,15 @@ import com.github.damontecres.wholphin.ui.spacedByWithFooter import com.github.damontecres.wholphin.ui.theme.LocalTheme import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.tryRequestFocus +import com.github.damontecres.wholphin.util.ExceptionHandler import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.model.api.CollectionType import org.jellyfin.sdk.model.api.DeviceProfile +import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -303,6 +309,28 @@ fun NavDrawer( }, ) val spacedBy = 4.dp + val config = LocalConfiguration.current + val density = LocalDensity.current + val heightInPx = remember { with(density) { config.screenHeightDp.dp.roundToPx() } } + + suspend fun scrollToSelected() { + val target = selectedIndex + 2 + try { + if (target !in + listState.firstVisibleItemIndex.. {} + is ServerConnectionStatus.Success -> {} ServerConnectionStatus.Pending -> { CircularProgress( Modifier.size(IconButtonDefaults.MediumIconSize), @@ -85,7 +88,7 @@ fun ServerList( }, onClick = { when (status) { - ServerConnectionStatus.Success -> onSwitchServer.invoke(server) + is ServerConnectionStatus.Success -> onSwitchServer.invoke(server) ServerConnectionStatus.Pending -> {} is ServerConnectionStatus.Error -> onTestServer.invoke(server) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt index 4cb05d43..40066d95 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt @@ -39,6 +39,7 @@ import com.github.damontecres.wholphin.ui.components.EditTextBox import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.LoadingState +import org.jellyfin.sdk.model.api.PublicSystemInfo @Composable fun SwitchServerContent( @@ -88,7 +89,7 @@ fun SwitchServerContent( servers = servers, connectionStatus = serverStatus, onSwitchServer = { - viewModel.addServer(it.url) + viewModel.switchServer(it) }, onTestServer = { viewModel.testServer(it) @@ -141,7 +142,7 @@ fun SwitchServerContent( connectionStatus = discoveredServers .map { it.id } - .associateWith { ServerConnectionStatus.Success }, + .associateWith { ServerConnectionStatus.Success(PublicSystemInfo()) }, onSwitchServer = { viewModel.addServer(it.url) }, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt index be93e772..75af5155 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt @@ -87,7 +87,7 @@ class SwitchServerViewModel viewModelScope.launchIO { delay(1000) val result = internalTestServer(server) - if (result == ServerConnectionStatus.Success) { + if (result is ServerConnectionStatus.Success) { showToast(context, context.getString(R.string.success), Toast.LENGTH_SHORT) } else if (result is ServerConnectionStatus.Error) { showToast(context, result.message ?: "Error", Toast.LENGTH_SHORT) @@ -97,27 +97,30 @@ class SwitchServerViewModel private suspend fun internalTestServer(server: JellyfinServer): ServerConnectionStatus = try { - jellyfin - .createApi( - server.url, - httpClientOptions = - HttpClientOptions( - requestTimeout = 6.seconds, - connectTimeout = 6.seconds, - socketTimeout = 6.seconds, - ), - ).systemApi - .getPublicSystemInfo() + val systemInfo = + jellyfin + .createApi( + server.url, + httpClientOptions = + HttpClientOptions( + requestTimeout = 6.seconds, + connectTimeout = 6.seconds, + socketTimeout = 6.seconds, + ), + ).systemApi + .getPublicSystemInfo() + .content + val result = ServerConnectionStatus.Success(systemInfo) withContext(Dispatchers.Main) { serverStatus.value = serverStatus.value!!.toMutableMap().apply { put( server.id, - ServerConnectionStatus.Success, + result, ) } } - ServerConnectionStatus.Success + result } catch (ex: Exception) { val status = ServerConnectionStatus.Error(ex.localizedMessage) Timber.w(ex, "Error checking server ${server.url}") @@ -133,6 +136,34 @@ class SwitchServerViewModel status } + fun switchServer(server: JellyfinServer) { + viewModelScope.launchIO { + withContext(Dispatchers.Main) { + serverStatus.value = + serverStatus.value!!.toMutableMap().apply { + put( + server.id, + ServerConnectionStatus.Pending, + ) + } + } + val result = internalTestServer(server) + if (result is ServerConnectionStatus.Success) { + val updatedServer = + server.copy( + name = result.systemInfo.serverName, + version = result.systemInfo.version, + ) + serverRepository.addAndChangeServer(updatedServer) + withContext(Dispatchers.Main) { + navigationManager.navigateTo(Destination.UserList(server)) + } + } else if (result is ServerConnectionStatus.Error) { + showToast(context, "Error connecting: $${result.message}") + } + } + } + fun addServer(inputUrl: String) { addServerState.value = LoadingState.Loading viewModelScope.launchIO {