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 10c48451..b9acb2d5 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 @@ -19,7 +19,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.KeyboardArrowLeft @@ -87,6 +86,7 @@ import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption import com.github.damontecres.wholphin.ui.setValueOnMain +import com.github.damontecres.wholphin.ui.setup.UserIconCardImage import com.github.damontecres.wholphin.ui.spacedByWithFooter import com.github.damontecres.wholphin.ui.theme.LocalTheme import com.github.damontecres.wholphin.ui.toServerString @@ -99,6 +99,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.extensions.imageApi import org.jellyfin.sdk.model.api.CollectionType import timber.log.Timber import java.util.UUID @@ -108,6 +110,7 @@ import javax.inject.Inject class NavDrawerViewModel @Inject constructor( + private val api: ApiClient, private val navDrawerItemRepository: NavDrawerItemRepository, val navigationManager: NavigationManager, val setupNavigationManager: SetupNavigationManager, @@ -191,6 +194,8 @@ class NavDrawerViewModel fun setShowMore(value: Boolean) { showMore.value = value } + + fun getUserImage(user: JellyfinUser): String = api.imageApi.getUserImageUrl(user.id) } sealed interface NavDrawerItem { @@ -386,12 +391,11 @@ fun NavDrawer( val interactionSource = remember { MutableInteractionSource() } val focused by interactionSource.collectIsFocusedAsState() LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE } - IconNavItem( - text = user?.name ?: "", - subtext = server?.name ?: server?.url, - icon = Icons.Default.AccountCircle, - selected = false, - drawerOpen = drawerState.isOpen, + val userImageUrl = remember(user) { viewModel.getUserImage(user) } + ProfileIcon( + user = user, + imageUrl = userImageUrl, + serverName = server.name ?: server.url, interactionSource = interactionSource, onClick = { viewModel.setupNavigationManager.navigateTo( @@ -572,6 +576,44 @@ fun NavDrawer( } } +@Composable +fun NavigationDrawerScope.ProfileIcon( + user: JellyfinUser, + imageUrl: String?, + serverName: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, +) { + val focused by interactionSource.collectIsFocusedAsState() + NavigationDrawerItem( + modifier = modifier, + selected = false, + onClick = onClick, + leadingContent = { + UserIconCardImage( + id = user.id, + name = user.name, + imageUrl = imageUrl, + modifier = Modifier.fillMaxSize(), + ) + }, + supportingContent = { + Text( + text = serverName, + maxLines = 1, + ) + }, + interactionSource = interactionSource, + ) { + Text( + modifier = Modifier, + text = user.name ?: user.id.toString(), + maxLines = 1, + ) + } +} + @Composable fun NavigationDrawerScope.IconNavItem( text: String, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt index d8b1b096..2b6a2e29 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt @@ -1,6 +1,7 @@ package com.github.damontecres.wholphin.ui.setup import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -38,6 +39,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.window.DialogProperties import androidx.tv.material3.Border import androidx.tv.material3.Button @@ -51,10 +53,13 @@ import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.JellyfinUser import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.FontAwesome +import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.components.DialogItem import com.github.damontecres.wholphin.ui.components.DialogPopup import com.github.damontecres.wholphin.ui.isNotNullOrBlank +import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.tryRequestFocus +import java.util.UUID /** * Display a list of users plus option to add a new one or switch servers @@ -285,6 +290,64 @@ private fun UserIconCard( } } +@Composable +fun UserIconCardImage( + id: UUID, + name: String?, + imageUrl: String?, + modifier: Modifier = Modifier, +) { + var imageError by remember { mutableStateOf(false) } + val userColor = rememberIdColor(id) + Box( + modifier = + modifier.background( + color = userColor, + shape = CircleShape, + ), + contentAlignment = Alignment.Center, + ) { + if (imageUrl.isNotNullOrBlank() && !imageError) { + AsyncImage( + model = imageUrl, + contentDescription = name, + contentScale = ContentScale.Crop, + onError = { imageError = true }, + modifier = + Modifier + .fillMaxSize() + .clip(CircleShape), + ) + } else { + val firstLetter = + remember(id, name) { + (name ?: id.toString()).firstOrNull()?.uppercase() ?: "?" + } + Text( + text = firstLetter, + style = MaterialTheme.typography.bodyLarge, + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center, + ) + } + } +} + +@PreviewTvSpec +@Composable +fun UserIconCardImagePreview() { + WholphinTheme { + UserIconCardImage( + id = UUID.randomUUID(), + name = "A smith", + imageUrl = null, + modifier = Modifier.size(24.dp), + ) + } +} + /** * Add User card component - displays a + icon in a circle */