From f1f6b729aa4a9e4dcccf3c966c44967b35008475 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Mon, 27 Oct 2025 20:44:04 -0400 Subject: [PATCH] Fix several UI focus issues (#81) Remembers the page position & focused item on both Movie & Series detail pages Only loads similar movies & series once so that if you click on one and go back, it doesn't refresh the entire list Remember the tab on the Favorite page (if remember tabs is enabled in settings) --- .../damontecres/wholphin/hilt/AppModule.kt | 7 +- .../damontecres/wholphin/ui/Extensions.kt | 9 + .../wholphin/ui/detail/FavoritesPage.kt | 45 +++-- .../wholphin/ui/detail/movie/MovieDetails.kt | 60 ++++-- .../ui/detail/movie/MovieViewModel.kt | 50 +++-- .../ui/detail/series/SeriesDetails.kt | 173 ++++++++++-------- .../ui/detail/series/SeriesViewModel.kt | 32 ++-- .../wholphin/util/RememberTabManager.kt | 18 ++ 8 files changed, 241 insertions(+), 153 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt b/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt index c6c86de6..c1f9da80 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt @@ -27,7 +27,6 @@ import org.jellyfin.sdk.api.okhttp.OkHttpFactory import org.jellyfin.sdk.createJellyfin import org.jellyfin.sdk.model.ClientInfo import org.jellyfin.sdk.model.DeviceInfo -import java.util.UUID import javax.inject.Qualifier import javax.inject.Singleton @@ -144,11 +143,11 @@ object AppModule { appPreference: DataStore, @IoCoroutineScope scope: CoroutineScope, ) = object : RememberTabManager { - fun key(itemId: UUID) = "${serverRepository.currentServer?.id}_${serverRepository.currentUser?.id}_$itemId" + fun key(itemId: String) = "${serverRepository.currentServer?.id}_${serverRepository.currentUser?.id}_$itemId" override fun getRememberedTab( preferences: UserPreferences, - itemId: UUID, + itemId: String, defaultTab: Int, ): Int { if (preferences.appPreferences.interfacePreferences.rememberSelectedTab) { @@ -161,7 +160,7 @@ object AppModule { override fun saveRememberedTab( preferences: UserPreferences, - itemId: UUID, + itemId: String, tabIndex: Int, ) { if (preferences.appPreferences.interfacePreferences.rememberSelectedTab) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt index 49ab7d89..c21d9231 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt @@ -321,6 +321,15 @@ fun rememberPosition(initialPosition: RowColumn = RowColumn(-1, -1)) = ) } +/** + * Convenient way to [rememberSaveable] a [RowColumn] + */ +@Composable +fun rememberPosition( + row: Int, + column: Int, +) = rememberPosition(RowColumn(row, column)) + /** * Convenient way to [rememberSaveable] a Int */ diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt index c7d7b140..92fa57c9 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt @@ -34,7 +34,7 @@ import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid import com.github.damontecres.wholphin.ui.components.ErrorMessage -import com.github.damontecres.wholphin.ui.ifElse +import com.github.damontecres.wholphin.ui.nav.NavDrawerItem import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel import com.github.damontecres.wholphin.ui.tryRequestFocus import org.jellyfin.sdk.model.api.BaseItemKind @@ -46,29 +46,31 @@ fun FavoritesPage( preferencesViewModel: PreferencesViewModel = hiltViewModel(), ) { val uiPrefs = preferences.appPreferences.interfacePreferences - val rememberedTabIndex = 0 - // TODO remember tab -// if (uiPrefs.rememberSelectedTab) { -// uiPrefs.getRememberedTabsOrDefault(destination.itemId.toString(), 0) -// } else { -// 0 -// } + val rememberedTabIndex = + remember { + preferencesViewModel.getRememberedTab( + preferences, + NavDrawerItem.Favorites.id, + 0, + ) + } val tabs = listOf("Movies", "TV Shows", "Episodes") + val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } } var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } - val firstTabFocusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() } - -// if (uiPrefs.rememberSelectedTab) { -// LaunchedEffect(selectedTabIndex) { -// preferencesViewModel.preferenceDataStore.updateData { -// preferences.appPreferences.rememberTab(destination.itemId, selectedTabIndex) -// } -// } -// } + LaunchedEffect(Unit) { + tabFocusRequesters.getOrNull(selectedTabIndex)?.tryRequestFocus() + } + LaunchedEffect(selectedTabIndex) { + preferencesViewModel.saveRememberedTab( + preferences, + NavDrawerItem.Favorites.id, + selectedTabIndex, + ) + } var showHeader by rememberSaveable { mutableStateOf(true) } val onClickItem = { item: BaseItem -> @@ -89,7 +91,7 @@ fun FavoritesPage( modifier = Modifier .padding(start = 32.dp, top = 16.dp, bottom = 16.dp) - .focusRestorer(firstTabFocusRequester) + .focusRestorer(tabFocusRequesters[0]) .onFocusChanged { if (!it.isFocused) { focusTabIndex = selectedTabIndex @@ -126,10 +128,7 @@ fun FavoritesPage( modifier = Modifier .padding(8.dp) - .ifElse( - index == selectedTabIndex, - Modifier.focusRequester(firstTabFocusRequester), - ), + .focusRequester(tabFocusRequesters[index]), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt index 3015d903..134b2b1e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt @@ -69,6 +69,7 @@ import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.nav.Destination +import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState @@ -243,6 +244,12 @@ fun MovieDetails( } } +private const val HEADER_ROW = 0 +private const val PEOPLE_ROW = HEADER_ROW + 1 +private const val TRAILER_ROW = PEOPLE_ROW + 1 +private const val CHAPTER_ROW = TRAILER_ROW + 1 +private const val SIMILAR_ROW = CHAPTER_ROW + 1 + @Composable fun MovieDetailsContent( preferences: UserPreferences, @@ -262,15 +269,15 @@ fun MovieDetailsContent( modifier: Modifier = Modifier, ) { val scope = rememberCoroutineScope() - val focusRequester = remember { FocusRequester() } + var position by rememberInt(0) + val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } } val dto = movie.data val backdropImageUrl = movie.backdropImageUrl val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO val bringIntoViewRequester = remember { BringIntoViewRequester() } LaunchedEffect(Unit) { -// bringIntoViewRequester.bringIntoView() - focusRequester.tryRequestFocus() + focusRequesters.getOrNull(position)?.tryRequestFocus() } Box(modifier = modifier) { if (backdropImageUrl.isNotNullOrBlank()) { @@ -329,18 +336,22 @@ fun MovieDetailsContent( resumePosition = resumePosition, watched = dto.userData?.played ?: false, favorite = dto.userData?.isFavorite ?: false, - playOnClick = playOnClick, + playOnClick = { + position = HEADER_ROW + playOnClick.invoke(it) + }, moreOnClick = moreOnClick, watchOnClick = watchOnClick, favoriteOnClick = favoriteOnClick, buttonOnFocusChanged = { if (it.isFocused) { + position = HEADER_ROW scope.launch(ExceptionHandler()) { bringIntoViewRequester.bringIntoView() } } }, - modifier = Modifier.focusRequester(focusRequester), + modifier = Modifier.focusRequester(focusRequesters[HEADER_ROW]), ) } } @@ -348,9 +359,14 @@ fun MovieDetailsContent( item { PersonRow( people = people, - onClick = {}, + onClick = { + position = PEOPLE_ROW + }, onLongClick = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[PEOPLE_ROW]), ) } } @@ -358,8 +374,14 @@ fun MovieDetailsContent( item { TrailerRow( trailers = trailers, - onClickTrailer = trailerOnClick, - modifier = Modifier.fillMaxWidth(), + onClickTrailer = { + position = TRAILER_ROW + trailerOnClick.invoke(it) + }, + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[TRAILER_ROW]), ) } } @@ -367,9 +389,15 @@ fun MovieDetailsContent( item { ChapterRow( chapters = chapters, - onClick = { playOnClick.invoke(it.position) }, + onClick = { + position = CHAPTER_ROW + playOnClick.invoke(it.position) + }, onLongClick = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[CHAPTER_ROW]), ) } } @@ -378,7 +406,10 @@ fun MovieDetailsContent( ItemRow( title = stringResource(R.string.more_like_this), items = similar, - onClickItem = onClickItem, + onClickItem = { + position = SIMILAR_ROW + onClickItem.invoke(it) + }, onLongClickItem = {}, cardContent = { index, item, mod, onClick, onLongClick -> SeasonCard( @@ -391,7 +422,10 @@ fun MovieDetailsContent( imageWidth = Dp.Unspecified, ) }, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[SIMILAR_ROW]), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index 2e30d5b3..bc2ce98d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -19,6 +19,8 @@ import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler +import com.github.damontecres.wholphin.util.LoadingExceptionHandler +import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -47,7 +49,7 @@ class MovieViewModel val trailers = MutableLiveData>(listOf()) val people = MutableLiveData>(listOf()) val chapters = MutableLiveData>(listOf()) - val similar = MutableLiveData>(listOf()) + val similar = MutableLiveData>() val chosenStreams = MutableLiveData(null) override fun init( @@ -55,14 +57,21 @@ class MovieViewModel potential: BaseItem?, ): Job? { this.itemId = itemId - return viewModelScope.launch(ExceptionHandler()) { - super.init(itemId, potential)?.join() + return viewModelScope.launch( + Dispatchers.IO + + LoadingExceptionHandler( + loading, + "Error fetching movie", + ), + ) { + fetchAndSetItem(itemId) item.value?.let { item -> + val result = itemPlaybackRepository.getSelectedTracks(item.id, item) + withContext(Dispatchers.Main) { + chosenStreams.value = result + loading.value = LoadingState.Success + } viewModelScope.launchIO { - val result = itemPlaybackRepository.getSelectedTracks(item.id, item) - withContext(Dispatchers.Main) { - chosenStreams.value = result - } val remoteTrailers = item.data.remoteTrailers ?.mapNotNull { t -> @@ -98,19 +107,20 @@ class MovieViewModel }.orEmpty() chapters.value = Chapter.Companion.fromDto(item.data, api) } - - val similar = - api.libraryApi - .getSimilarItems( - GetSimilarItemsRequest( - userId = serverRepository.currentUser?.id, - itemId = itemId, - fields = SlimItemFields, - limit = 25, - ), - ).content.items - .map { BaseItem.Companion.from(it, api) } - this@MovieViewModel.similar.setValueOnMain(similar) + if (!similar.isInitialized) { + val similar = + api.libraryApi + .getSimilarItems( + GetSimilarItemsRequest( + userId = serverRepository.currentUser?.id, + itemId = itemId, + fields = SlimItemFields, + limit = 25, + ), + ).content.items + .map { BaseItem.Companion.from(it, api) } + this@MovieViewModel.similar.setValueOnMain(similar) + } } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt index 746871e0..f14c7f53 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt @@ -63,12 +63,10 @@ import com.github.damontecres.wholphin.ui.components.StarRating import com.github.damontecres.wholphin.ui.components.StarRatingPrecision import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo -import com.github.damontecres.wholphin.ui.data.RowColumn -import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.rememberPosition +import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler @@ -183,6 +181,11 @@ fun SeriesDetails( } } +private const val HEADER_ROW = 0 +private const val SEASONS_ROW = HEADER_ROW + 1 +private const val PEOPLE_ROW = SEASONS_ROW + 1 +private const val SIMILAR_ROW = PEOPLE_ROW + 1 + @Composable fun SeriesDetailsContent( preferences: UserPreferences, @@ -200,11 +203,14 @@ fun SeriesDetailsContent( favoriteOnClick: () -> Unit, modifier: Modifier = Modifier, ) { + val scope = rememberCoroutineScope() val bringIntoViewRequester = remember { BringIntoViewRequester() } - var position by rememberPosition() - val focusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { focusRequester.tryRequestFocus() } + var position by rememberInt() + val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } } + LaunchedEffect(Unit) { + focusRequesters.getOrNull(position)?.tryRequestFocus() + } Box( modifier = modifier, @@ -256,51 +262,95 @@ fun SeriesDetailsContent( played = played, favorite = favorite, overviewOnClick = overviewOnClick, - playOnClick = playOnClick, + playOnClick = { + position = HEADER_ROW + playOnClick.invoke() + }, watchOnClick = watchOnClick, favoriteOnClick = favoriteOnClick, bringIntoViewRequester = bringIntoViewRequester, modifier = Modifier - .fillMaxWidth(.6f) + .fillMaxWidth(.7f) .bringIntoViewRequester(bringIntoViewRequester) - .padding(bottom = 80.dp) - .ifElse(position.row < 0, Modifier.focusRequester(focusRequester)), + .padding(bottom = 8.dp), ) + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = + Modifier + .padding(start = 16.dp) + .focusRequester(focusRequesters[HEADER_ROW]) + .padding(bottom = 80.dp), + ) { + ExpandablePlayButton( + title = R.string.play, + resume = Duration.ZERO, + icon = Icons.Default.PlayArrow, + onClick = { + position = HEADER_ROW + playOnClick.invoke() + }, + modifier = + Modifier.onFocusChanged { + if (it.isFocused) { + scope.launch(ExceptionHandler()) { + bringIntoViewRequester.bringIntoView() + } + } + }, + ) + ExpandableFaButton( + title = if (played) R.string.mark_unwatched else R.string.mark_watched, + iconStringRes = if (played) R.string.fa_eye else R.string.fa_eye_slash, + onClick = watchOnClick, + modifier = + Modifier.onFocusChanged { + if (it.isFocused) { + scope.launch(ExceptionHandler()) { + bringIntoViewRequester.bringIntoView() + } + } + }, + ) + ExpandableFaButton( + title = if (favorite) R.string.remove_favorite else R.string.add_favorite, + iconStringRes = R.string.fa_heart, + onClick = favoriteOnClick, + iconColor = if (favorite) Color.Red else Color.Unspecified, + modifier = + Modifier.onFocusChanged { + if (it.isFocused) { + scope.launch(ExceptionHandler()) { + bringIntoViewRequester.bringIntoView() + } + } + }, + ) + } } item { ItemRow( title = "Seasons", items = seasons.items, - onClickItem = onClickItem, - onLongClickItem = onLongClickItem, - cardOnFocus = { isFocused, index -> -// if (isFocused) { -// scope.launch(ExceptionHandler()) { -// bringIntoViewRequester.bringIntoView() -// } -// } + onClickItem = { + position = SEASONS_ROW + onClickItem.invoke(it) }, + onLongClickItem = onLongClickItem, modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .focusRequester(focusRequesters[SEASONS_ROW]), cardContent = @Composable { index, item, mod, onClick, onLongClick -> SeasonCard( item = item, - onClick = { - position = RowColumn(0, index) - onClick.invoke() - }, + onClick = onClick, onLongClick = onLongClick, imageHeight = Cards.height2x3, imageWidth = Dp.Unspecified, showImageOverlay = true, - modifier = - mod - .ifElse( - position.row == 0 && position.column == index, - Modifier.focusRequester(focusRequester), - ), + modifier = mod, ) }, ) @@ -309,9 +359,14 @@ fun SeriesDetailsContent( item { PersonRow( people = people, - onClick = {}, + onClick = { + position = PEOPLE_ROW + }, onLongClick = {}, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[PEOPLE_ROW]), ) } } @@ -320,7 +375,10 @@ fun SeriesDetailsContent( ItemRow( title = stringResource(R.string.more_like_this), items = similar, - onClickItem = onClickItem, + onClickItem = { + position = SIMILAR_ROW + onClickItem.invoke(it) + }, onLongClickItem = {}, cardContent = { index, item, mod, onClick, onLongClick -> SeasonCard( @@ -333,7 +391,10 @@ fun SeriesDetailsContent( imageWidth = Dp.Unspecified, ) }, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[SIMILAR_ROW]), ) } } @@ -409,52 +470,6 @@ fun SeriesDetailsHeader( textBoxHeight = Dp.Unspecified, ) } - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.padding(start = 16.dp), - ) { - ExpandablePlayButton( - title = R.string.play, - resume = Duration.ZERO, - icon = Icons.Default.PlayArrow, - onClick = { playOnClick.invoke() }, - modifier = - Modifier.onFocusChanged { - if (it.isFocused) { - scope.launch(ExceptionHandler()) { - bringIntoViewRequester.bringIntoView() - } - } - }, - ) - ExpandableFaButton( - title = if (played) R.string.mark_unwatched else R.string.mark_watched, - iconStringRes = if (played) R.string.fa_eye else R.string.fa_eye_slash, - onClick = watchOnClick, - modifier = - Modifier.onFocusChanged { - if (it.isFocused) { - scope.launch(ExceptionHandler()) { - bringIntoViewRequester.bringIntoView() - } - } - }, - ) - ExpandableFaButton( - title = if (favorite) R.string.remove_favorite else R.string.add_favorite, - iconStringRes = R.string.fa_heart, - onClick = favoriteOnClick, - iconColor = if (favorite) Color.Red else Color.Unspecified, - modifier = - Modifier.onFocusChanged { - if (it.isFocused) { - scope.launch(ExceptionHandler()) { - bringIntoViewRequester.bringIntoView() - } - } - }, - ) - } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 8ad3232d..7cfbfa32 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -71,7 +71,7 @@ class SeriesViewModel val seasons = MutableLiveData(ItemListAndMapping.empty()) val episodes = MutableLiveData(EpisodeList.Loading) val people = MutableLiveData>(listOf()) - val similar = MutableLiveData>(listOf()) + val similar = MutableLiveData>() fun init( prefs: UserPreferences, @@ -107,18 +107,20 @@ class SeriesViewModel people.map { Person.fromDto(it, api) } }.orEmpty() } - val similar = - api.libraryApi - .getSimilarItems( - GetSimilarItemsRequest( - userId = serverRepository.currentUser?.id, - itemId = itemId, - fields = SlimItemFields, - limit = 25, - ), - ).content.items - .map { BaseItem.from(it, api, true) } - this@SeriesViewModel.similar.setValueOnMain(similar) + if (!similar.isInitialized) { + val similar = + api.libraryApi + .getSimilarItems( + GetSimilarItemsRequest( + userId = serverRepository.currentUser?.id, + itemId = itemId, + fields = SlimItemFields, + limit = 25, + ), + ).content.items + .map { BaseItem.from(it, api, true) } + this@SeriesViewModel.similar.setValueOnMain(similar) + } } } @@ -320,7 +322,9 @@ class SeriesViewModel ).content.items .firstOrNull() if (nextUp != null) { - navigateTo(Destination.Playback(nextUp.id, 0L)) + withContext(Dispatchers.Main) { + navigateTo(Destination.Playback(nextUp.id, 0L)) + } } else { showToast( context, diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/RememberTabManager.kt b/app/src/main/java/com/github/damontecres/wholphin/util/RememberTabManager.kt index ba196369..1c69d947 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/RememberTabManager.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/RememberTabManager.kt @@ -11,6 +11,15 @@ interface RememberTabManager { preferences: UserPreferences, itemId: UUID, defaultTab: Int, + ): Int = getRememberedTab(preferences, itemId.toString(), defaultTab) + + /** + * If enabled, get the remembered tab index for the given item + */ + fun getRememberedTab( + preferences: UserPreferences, + itemId: String, + defaultTab: Int, ): Int /** @@ -20,5 +29,14 @@ interface RememberTabManager { preferences: UserPreferences, itemId: UUID, tabIndex: Int, + ) = saveRememberedTab(preferences, itemId.toString(), tabIndex) + + /** + * If enabled, save the remembered tab index for the given item + */ + fun saveRememberedTab( + preferences: UserPreferences, + itemId: String, + tabIndex: Int, ) }