diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt index d1893dbb..312b7446 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.buildAnnotatedString import com.github.damontecres.wholphin.ui.DateFormatter +import com.github.damontecres.wholphin.ui.abbreviateNumber import com.github.damontecres.wholphin.ui.detail.CardGridItem import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds import com.github.damontecres.wholphin.ui.dot @@ -88,6 +89,15 @@ data class BaseItem( @Transient val ui = BaseItemUi( + episodeCornerText = + data.indexNumber?.let { "E$it" } + ?: data.premiereDate?.let(::formatDateTime), + episdodeUnplayedCornerText = + data.indexNumber?.let { "E$it" } + ?: data.userData + ?.unplayedItemCount + ?.takeIf { it > 0 } + ?.let { abbreviateNumber(it) }, quickDetails = buildAnnotatedString { val details = @@ -191,5 +201,7 @@ val BaseItemDto.aspectRatioFloat: Float? get() = width?.let { w -> height?.let { @Immutable data class BaseItemUi( + val episodeCornerText: String?, + val episdodeUnplayedCornerText: String?, val quickDetails: AnnotatedString, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt index ac0bbece..70de419b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/BannerCard.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.isSpecified import androidx.compose.ui.unit.sp import androidx.tv.material3.Card import androidx.tv.material3.CardDefaults @@ -40,7 +41,6 @@ import com.github.damontecres.wholphin.ui.AspectRatios import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.LocalImageUrlService -import com.github.damontecres.wholphin.ui.isNotNullOrBlank import org.jellyfin.sdk.model.api.ImageType /** @@ -63,17 +63,19 @@ fun BannerCard( ) { val imageUrlService = LocalImageUrlService.current val density = LocalDensity.current + val fillHeight = + remember(cardHeight) { + if (cardHeight.isSpecified) { + with(density) { + cardHeight.roundToPx() + } + } else { + null + } + } val imageUrl = - remember(item, cardHeight) { + remember(item, fillHeight) { if (item != null) { - val fillHeight = - if (cardHeight != Dp.Unspecified) { - with(density) { - cardHeight.roundToPx() - } - } else { - null - } imageUrlService.getItemImageUrl( item, ImageType.PRIMARY, @@ -101,7 +103,7 @@ fun BannerCard( .fillMaxSize(), // .background(MaterialTheme.colorScheme.surfaceVariant), ) { - if (!imageError && imageUrl.isNotNullOrBlank()) { + if (!imageError && imageUrl != null) { AsyncImage( model = imageUrl, contentDescription = null, @@ -121,7 +123,7 @@ fun BannerCard( .align(Alignment.Center), ) } - if (played || cornerText.isNotNullOrBlank()) { + if (played || cornerText != null) { Row( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, @@ -133,7 +135,7 @@ fun BannerCard( if (played && (playPercent <= 0 || playPercent >= 100)) { WatchedIcon(Modifier.size(24.dp)) } - if (cornerText.isNotNullOrBlank()) { + if (cornerText != null) { Box( modifier = Modifier diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt index dc5aaa95..b19e7900 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt @@ -55,7 +55,6 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.SeriesName import com.github.damontecres.wholphin.ui.components.TabRow -import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.logTab import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp @@ -215,9 +214,6 @@ fun SeriesOverviewContent( if (interactionSource.collectIsFocusedAsState().value) { onFocusEpisode.invoke(episodeIndex) } - val cornerText = - episode?.data?.indexNumber?.let { "E$it" } - ?: episode?.data?.premiereDate?.let(::formatDateTime) BannerCard( name = episode?.name, item = episode, @@ -226,7 +222,7 @@ fun SeriesOverviewContent( ?.aspectRatio ?.coerceAtLeast(AspectRatios.FOUR_THREE) ?: (AspectRatios.WIDE), - cornerText = cornerText, + cornerText = episode?.ui?.episodeCornerText, played = episode?.data?.userData?.played ?: false, playPercent = episode?.data?.userData?.playedPercentage diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index 20859f9f..33d40a4a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -1,6 +1,5 @@ package com.github.damontecres.wholphin.ui.main -import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.focusGroup import androidx.compose.foundation.focusable @@ -25,7 +24,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -49,7 +47,6 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.AspectRatios import com.github.damontecres.wholphin.ui.Cards -import com.github.damontecres.wholphin.ui.abbreviateNumber import com.github.damontecres.wholphin.ui.cards.BannerCard import com.github.damontecres.wholphin.ui.cards.ItemRow import com.github.damontecres.wholphin.ui.components.CircularProgress @@ -88,28 +85,15 @@ fun HomePage( playlistViewModel: AddPlaylistViewModel = hiltViewModel(), ) { val context = LocalContext.current - var firstLoad by rememberSaveable { mutableStateOf(true) } LaunchedEffect(Unit) { - viewModel.init(preferences).join() - firstLoad = false + viewModel.init() } val loading by viewModel.loadingState.observeAsState(LoadingState.Loading) val refreshing by viewModel.refreshState.observeAsState(LoadingState.Loading) val watchingRows by viewModel.watchingRows.observeAsState(listOf()) val latestRows by viewModel.latestRows.observeAsState(listOf()) - LaunchedEffect(loading) { - val state = loading - if (!firstLoad && state is LoadingState.Error) { - // After the first load, refreshes occur in the background and an ErrorMessage won't show - // So send a Toast on errors instead - Toast - .makeText( - context, - "Home refresh error: ${state.localizedMessage}", - Toast.LENGTH_LONG, - ).show() - } - } + + val homeRows = remember(watchingRows, latestRows) { watchingRows + latestRows } when (val state = loading) { is LoadingState.Error -> { @@ -127,7 +111,7 @@ fun HomePage( var showPlaylistDialog by remember { mutableStateOf(null) } val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending) HomePageContent( - watchingRows + latestRows, + homeRows = homeRows, onClickItem = { position, item -> viewModel.navigationManager.navigateTo(item.destination()) }, @@ -339,21 +323,11 @@ fun HomePageContent( .focusRequester(rowFocusRequesters[rowIndex]) .animateItem(), cardContent = { index, item, cardModifier, onClick, onLongClick -> - val cornerText = - remember(item) { - item?.data?.indexNumber?.let { "E$it" } - ?: item - ?.data - ?.userData - ?.unplayedItemCount - ?.takeIf { it > 0 } - ?.let { abbreviateNumber(it) } - } BannerCard( name = item?.data?.seriesName ?: item?.name, item = item, aspectRatio = AspectRatios.TALL, - cornerText = cornerText, + cornerText = item?.ui?.episdodeUnplayedCornerText, played = item?.data?.userData?.played ?: false, favorite = item?.favorite ?: false, playPercent = diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt index e2704ad8..1911e34d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt @@ -14,9 +14,11 @@ import com.github.damontecres.wholphin.services.DatePlayedService import com.github.damontecres.wholphin.services.FavoriteWatchManager import com.github.damontecres.wholphin.services.LatestNextUpService import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem import com.github.damontecres.wholphin.ui.setValueOnMain +import com.github.damontecres.wholphin.ui.showToast import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.HomeRowLoadingState import com.github.damontecres.wholphin.util.LoadingExceptionHandler @@ -24,7 +26,6 @@ import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.api.client.ApiClient @@ -47,6 +48,7 @@ class HomeViewModel private val datePlayedService: DatePlayedService, private val latestNextUpService: LatestNextUpService, private val backdropService: BackdropService, + private val userPreferencesService: UserPreferencesService, ) : ViewModel() { val loadingState = MutableLiveData(LoadingState.Pending) val refreshState = MutableLiveData(LoadingState.Pending) @@ -57,18 +59,11 @@ class HomeViewModel init { datePlayedService.invalidateAll() + init() } - fun init(preferences: UserPreferences): Job { - val reload = loadingState.value != LoadingState.Success - if (reload) { - loadingState.value = LoadingState.Loading - } - refreshState.value = LoadingState.Loading - this.preferences = preferences - val prefs = preferences.appPreferences.homePagePreferences - val limit = prefs.maxItemsPerRow - return viewModelScope.launch( + fun init() { + viewModelScope.launch( Dispatchers.IO + LoadingExceptionHandler( loadingState, @@ -76,67 +71,83 @@ class HomeViewModel ), ) { Timber.d("init HomeViewModel") + val reload = loadingState.value != LoadingState.Success + if (reload) { + loadingState.setValueOnMain(LoadingState.Loading) + } + refreshState.setValueOnMain(LoadingState.Loading) + this@HomeViewModel.preferences = userPreferencesService.getCurrent() + val prefs = preferences.appPreferences.homePagePreferences + val limit = prefs.maxItemsPerRow if (reload) { backdropService.clearBackdrop() } - - serverRepository.currentUserDto.value?.let { userDto -> - val includedIds = - navDrawerItemRepository - .getFilteredNavDrawerItems(navDrawerItemRepository.getNavDrawerItems()) - .filter { it is ServerNavDrawerItem } - .map { (it as ServerNavDrawerItem).itemId } - val resume = latestNextUpService.getResume(userDto.id, limit, true) - val nextUp = - latestNextUpService.getNextUp( - userDto.id, - limit, - prefs.enableRewatchingNextUp, - false, - ) - val watching = - buildList { - if (prefs.combineContinueNext) { - val items = latestNextUpService.buildCombined(resume, nextUp) - add( - HomeRowLoadingState.Success( - title = context.getString(R.string.continue_watching), - items = items, - ), - ) - } else { - if (resume.isNotEmpty()) { + try { + serverRepository.currentUserDto.value?.let { userDto -> + val includedIds = + navDrawerItemRepository + .getFilteredNavDrawerItems(navDrawerItemRepository.getNavDrawerItems()) + .filter { it is ServerNavDrawerItem } + .map { (it as ServerNavDrawerItem).itemId } + val resume = latestNextUpService.getResume(userDto.id, limit, true) + val nextUp = + latestNextUpService.getNextUp( + userDto.id, + limit, + prefs.enableRewatchingNextUp, + false, + ) + val watching = + buildList { + if (prefs.combineContinueNext) { + val items = latestNextUpService.buildCombined(resume, nextUp) add( HomeRowLoadingState.Success( title = context.getString(R.string.continue_watching), - items = resume, - ), - ) - } - if (nextUp.isNotEmpty()) { - add( - HomeRowLoadingState.Success( - title = context.getString(R.string.next_up), - items = nextUp, + items = items, ), ) + } else { + if (resume.isNotEmpty()) { + add( + HomeRowLoadingState.Success( + title = context.getString(R.string.continue_watching), + items = resume, + ), + ) + } + if (nextUp.isNotEmpty()) { + add( + HomeRowLoadingState.Success( + title = context.getString(R.string.next_up), + items = nextUp, + ), + ) + } } } - } - val latest = latestNextUpService.getLatest(userDto, limit, includedIds) - val pendingLatest = latest.map { HomeRowLoadingState.Loading(it.title) } + val latest = latestNextUpService.getLatest(userDto, limit, includedIds) + val pendingLatest = latest.map { HomeRowLoadingState.Loading(it.title) } - withContext(Dispatchers.Main) { - this@HomeViewModel.watchingRows.value = watching - if (reload) { - this@HomeViewModel.latestRows.value = pendingLatest + withContext(Dispatchers.Main) { + this@HomeViewModel.watchingRows.value = watching + if (reload) { + this@HomeViewModel.latestRows.value = pendingLatest + } + loadingState.value = LoadingState.Success } - loadingState.value = LoadingState.Success + refreshState.setValueOnMain(LoadingState.Success) + val loadedLatest = latestNextUpService.loadLatest(latest) + this@HomeViewModel.latestRows.setValueOnMain(loadedLatest) + } + } catch (ex: Exception) { + Timber.e(ex) + if (!reload) { + loadingState.setValueOnMain(LoadingState.Error(ex)) + } else { + showToast(context, "Error refreshing home: ${ex.localizedMessage}") } - refreshState.setValueOnMain(LoadingState.Success) - val loadedLatest = latestNextUpService.loadLatest(latest) - this@HomeViewModel.latestRows.setValueOnMain(loadedLatest) } } } @@ -147,7 +158,7 @@ class HomeViewModel ) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { favoriteWatchManager.setWatched(itemId, played) withContext(Dispatchers.Main) { - init(preferences) + init() } } @@ -157,7 +168,7 @@ class HomeViewModel ) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { favoriteWatchManager.setFavorite(itemId, favorite) withContext(Dispatchers.Main) { - init(preferences) + init() } } 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 559445b4..f38d50bd 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 @@ -3,7 +3,6 @@ package com.github.damontecres.wholphin.ui.nav import android.content.Context import androidx.activity.compose.BackHandler import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.focusGroup import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState @@ -14,7 +13,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState @@ -26,13 +24,10 @@ import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Settings import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind @@ -94,7 +89,6 @@ 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.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -117,9 +111,9 @@ class NavDrawerViewModel val backdropService: BackdropService, private val seerrServerRepository: SeerrServerRepository, ) : ViewModel() { - // private var all: List? = null val moreLibraries = MutableLiveData>(null) val libraries = MutableLiveData>(listOf()) + val selectedIndex = MutableLiveData(-1) val showMore = MutableLiveData(false) @@ -133,7 +127,6 @@ class NavDrawerViewModel fun init() { viewModelScope.launchIO { val all = navDrawerItemRepository.getNavDrawerItems() -// this@NavDrawerViewModel.all = all val libraries = navDrawerItemRepository.getFilteredNavDrawerItems(all) val moreLibraries = all.toMutableList().apply { removeAll(libraries) } @@ -187,6 +180,37 @@ class NavDrawerViewModel } } + fun onClickDrawerItem( + index: Int, + item: NavDrawerItem, + ) { + if (item !is NavDrawerItem.More) setShowMore(false) + when (item) { + NavDrawerItem.Favorites -> { + setIndex(index) + navigationManager.navigateToFromDrawer( + Destination.Favorites, + ) + } + + NavDrawerItem.More -> { + setShowMore(!showMore.value!!) + } + + NavDrawerItem.Discover -> { + setIndex(index) + navigationManager.navigateToFromDrawer( + Destination.Discover, + ) + } + + is ServerNavDrawerItem -> { + setIndex(index) + navigationManager.navigateToFromDrawer(item.destination) + } + } + } + fun setIndex(index: Int) { selectedIndex.value = index } @@ -250,7 +274,7 @@ fun NavDrawer( viewModel: NavDrawerViewModel = hiltViewModel( LocalView.current.findViewTreeViewModelStoreOwner()!!, - key = "${server?.id}_${user?.id}", // Keyed to the server & user to ensure its reset when switching either + key = "${server.id}_${user.id}", // Keyed to the server & user to ensure its reset when switching either ), ) { val drawerState = rememberDrawerState(DrawerValue.Closed) @@ -271,76 +295,14 @@ fun NavDrawer( LaunchedEffect(Unit) { viewModel.init() } val showMore by viewModel.showMore.observeAsState(false) -// val libraries = if (showPinnedOnly) pinnedLibraries else allLibraries // A negative index is a built in page, >=0 is a library val selectedIndex by viewModel.selectedIndex.observeAsState(-1) - var focusedIndex by remember { mutableIntStateOf(Int.MIN_VALUE) } - val derivedFocusedIndex by remember { derivedStateOf { focusedIndex } } - - fun setShowMore(value: Boolean) { - viewModel.setShowMore(value) - } BackHandler(enabled = showMore && drawerState.currentValue == DrawerValue.Open) { - setShowMore(false) + viewModel.setShowMore(false) } - val onClick = { index: Int, item: NavDrawerItem -> - when (item) { - NavDrawerItem.Favorites -> { - viewModel.setIndex(index) - viewModel.navigationManager.navigateToFromDrawer( - Destination.Favorites, - ) - } - - NavDrawerItem.More -> { - setShowMore(!showMore) - } - - NavDrawerItem.Discover -> { - viewModel.setIndex(index) - viewModel.navigationManager.navigateToFromDrawer( - Destination.Discover, - ) - } - - is ServerNavDrawerItem -> { - viewModel.setIndex(index) - viewModel.navigationManager.navigateToFromDrawer(item.destination) - } - } - } - // Temporarily disabled, see https://github.com/damontecres/Wholphin/pull/127#issuecomment-3478058418 - if (false && preferences.appPreferences.interfacePreferences.navDrawerSwitchOnFocus) { - LaunchedEffect(derivedFocusedIndex) { - val index = derivedFocusedIndex - delay(600) - if (index != selectedIndex) { - if (index == -1) { - viewModel.setIndex(-1) - viewModel.navigationManager.goToHome() - } else if (index in libraries.indices) { - if (moreLibraries.isEmpty() || index != libraries.lastIndex) { - libraries.getOrNull(index)?.let { - onClick.invoke(index, it) - } - } - } else { - val newIndex = libraries.size - index + 1 - if (newIndex in moreLibraries.indices) { - moreLibraries.getOrNull(newIndex)?.let { - onClick.invoke(index, it) - } - } - } - } - } - } - - val closedDrawerWidth = 40.dp - val drawerWidth by animateDpAsState(if (drawerState.isOpen) 260.dp else closedDrawerWidth) - val drawerPadding by animateDpAsState(if (drawerState.isOpen) 0.dp else 8.dp) + val closedDrawerWidth = NavigationDrawerItemDefaults.CollapsedDrawerItemWidth val drawerBackground by animateColorAsState( if (drawerState.isOpen) { MaterialTheme.colorScheme.surface @@ -382,15 +344,12 @@ fun NavDrawer( modifier = Modifier .fillMaxHeight() - .width(drawerWidth) .drawBehind { drawRect(drawerBackground) }, ) { // Even though some must be clicked, focusing on it should clear other focused items val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE } val userImageUrl = remember(user) { viewModel.getUserImage(user) } ProfileIcon( user = user, @@ -403,7 +362,7 @@ fun NavDrawer( SetupDestination.UserList(server), ) }, - modifier = Modifier.padding(start = drawerPadding), + modifier = Modifier, ) LazyColumn( state = listState, @@ -426,13 +385,10 @@ fun NavDrawer( scrollToSelected() } } - }.fillMaxHeight() - .padding(start = drawerPadding), + }.fillMaxHeight(), ) { item { val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { if (focused) focusedIndex = -2 } IconNavItem( text = stringResource(R.string.search), icon = Icons.Default.Search, @@ -449,13 +405,11 @@ fun NavDrawer( .ifElse( selectedIndex == -2, Modifier.focusRequester(focusRequester), - ).animateItem(), + ), ) } item { val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { if (focused) focusedIndex = -1 } IconNavItem( text = stringResource(R.string.home), icon = Icons.Default.Home, @@ -475,13 +429,11 @@ fun NavDrawer( .ifElse( selectedIndex == -1, Modifier.focusRequester(focusRequester), - ).animateItem(), + ), ) } itemsIndexed(libraries) { index, it -> val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { if (focused) focusedIndex = index } NavItem( library = it, selected = selectedIndex == index, @@ -489,31 +441,26 @@ fun NavDrawer( drawerOpen = drawerState.isOpen, interactionSource = interactionSource, onClick = { - onClick.invoke(index, it) - if (it !is NavDrawerItem.More) setShowMore(false) + viewModel.onClickDrawerItem(index, it) }, modifier = Modifier .ifElse( selectedIndex == index, Modifier.focusRequester(focusRequester), - ).animateItem(), + ), ) } if (showMore) { itemsIndexed(moreLibraries) { index, it -> val adjustedIndex = (index + libraries.size + 1) val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { - if (focused) focusedIndex = adjustedIndex - } NavItem( library = it, selected = selectedIndex == adjustedIndex, moreExpanded = showMore, drawerOpen = drawerState.isOpen, - onClick = { onClick.invoke(adjustedIndex, it) }, + onClick = { viewModel.onClickDrawerItem(adjustedIndex, it) }, containerColor = if (drawerState.isOpen) { MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp) @@ -526,14 +473,12 @@ fun NavDrawer( .ifElse( selectedIndex == adjustedIndex, Modifier.focusRequester(focusRequester), - ).animateItem(), + ), ) } } item { val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE } IconNavItem( text = stringResource(R.string.settings), icon = Icons.Default.Settings, @@ -547,7 +492,7 @@ fun NavDrawer( ), ) }, - modifier = Modifier.animateItem(), + modifier = Modifier, ) } } @@ -587,7 +532,6 @@ fun NavigationDrawerScope.ProfileIcon( modifier: Modifier = Modifier, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { - val focused by interactionSource.collectIsFocusedAsState() NavigationDrawerItem( modifier = modifier, selected = false, @@ -639,7 +583,7 @@ fun NavigationDrawerScope.IconNavItem( icon, contentDescription = null, tint = color, - modifier = Modifier.padding(0.dp), + modifier = Modifier, ) }, supportingContent = @@ -675,29 +619,31 @@ fun NavigationDrawerScope.NavItem( val context = LocalContext.current val useFont = library !is ServerNavDrawerItem || library.type != CollectionType.LIVETV val icon = - when (library) { - NavDrawerItem.Favorites -> { - R.string.fa_heart - } + remember(library) { + when (library) { + NavDrawerItem.Favorites -> { + R.string.fa_heart + } - NavDrawerItem.More -> { - R.string.fa_ellipsis - } + NavDrawerItem.More -> { + R.string.fa_ellipsis + } - NavDrawerItem.Discover -> { - R.string.fa_magnifying_glass_plus - } + NavDrawerItem.Discover -> { + R.string.fa_magnifying_glass_plus + } - is ServerNavDrawerItem -> { - when (library.type) { - CollectionType.MOVIES -> R.string.fa_film - CollectionType.TVSHOWS -> R.string.fa_tv - CollectionType.HOMEVIDEOS -> R.string.fa_video - CollectionType.LIVETV -> R.drawable.gf_dvr - CollectionType.MUSIC -> R.string.fa_music - CollectionType.BOXSETS -> R.string.fa_open_folder - CollectionType.PLAYLISTS -> R.string.fa_list_ul - else -> R.string.fa_film + is ServerNavDrawerItem -> { + when (library.type) { + CollectionType.MOVIES -> R.string.fa_film + CollectionType.TVSHOWS -> R.string.fa_tv + CollectionType.HOMEVIDEOS -> R.string.fa_video + CollectionType.LIVETV -> R.drawable.gf_dvr + CollectionType.MUSIC -> R.string.fa_music + CollectionType.BOXSETS -> R.string.fa_open_folder + CollectionType.PLAYLISTS -> R.string.fa_list_ul + else -> R.string.fa_film + } } } } @@ -732,14 +678,17 @@ fun NavigationDrawerScope.NavItem( } } }, - trailingContent = { + trailingContent = if (library is NavDrawerItem.More) { - Icon( - imageVector = if (moreExpanded) Icons.Default.ArrowDropDown else Icons.Default.KeyboardArrowLeft, - contentDescription = null, - ) - } - }, + { + Icon( + imageVector = if (moreExpanded) Icons.Default.ArrowDropDown else Icons.Default.KeyboardArrowLeft, + contentDescription = null, + ) + } + } else { + null + }, interactionSource = interactionSource, ) { Text( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/util/LocalClock.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/util/LocalClock.kt index ec634ab3..d07f86ff 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/util/LocalClock.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/util/LocalClock.kt @@ -8,8 +8,10 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import com.github.damontecres.wholphin.ui.TimeFormatter +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.isActive +import kotlinx.coroutines.withContext import java.time.LocalDateTime val LocalClock = compositionLocalOf { Clock() } @@ -32,12 +34,14 @@ data class Clock( fun ProvideLocalClock(content: @Composable () -> Unit) { val clock = remember { Clock() } LaunchedEffect(Unit) { - while (isActive) { - val now = LocalDateTime.now() - val time = TimeFormatter.format(now) - clock.now.value = now - clock.timeString.value = time - delay(2_000) + withContext(Dispatchers.IO) { + while (isActive) { + val now = LocalDateTime.now() + val time = TimeFormatter.format(now) + clock.now.value = now + clock.timeString.value = time + delay(2_000) + } } } CompositionLocalProvider(LocalClock provides clock, content)