mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Save/restore focus
This commit is contained in:
parent
2879cd477c
commit
5d6b5527dc
2 changed files with 102 additions and 28 deletions
|
|
@ -67,11 +67,14 @@ import com.github.damontecres.wholphin.ui.components.Optional
|
|||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.toBaseItems
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||
|
|
@ -159,22 +162,7 @@ class AlbumViewModel
|
|||
loading = LoadingState.Success,
|
||||
)
|
||||
}
|
||||
if (album.data.imageTags?.contains(ImageType.BACKDROP) == true) {
|
||||
backdropService.submit(album)
|
||||
} else {
|
||||
val artistIds =
|
||||
album.data.albumArtists
|
||||
?.shuffled()
|
||||
?.take(50)
|
||||
?.map { it.id }
|
||||
api.itemsApi
|
||||
.getItems(
|
||||
ids = artistIds,
|
||||
imageTypes = listOf(ImageType.BACKDROP),
|
||||
).content.items
|
||||
.firstOrNull()
|
||||
?.let { backdropService.submit(BaseItem(it, false)) }
|
||||
}
|
||||
updateBackDrop()
|
||||
if (state.value.similar.isEmpty()) {
|
||||
viewModelScope.launchIO {
|
||||
val similar =
|
||||
|
|
@ -216,6 +204,29 @@ class AlbumViewModel
|
|||
}
|
||||
}
|
||||
|
||||
fun updateBackDrop() {
|
||||
state.value.album?.let { album ->
|
||||
viewModelScope.launchDefault {
|
||||
if (album.data.imageTags?.contains(ImageType.BACKDROP) == true) {
|
||||
backdropService.submit(album)
|
||||
} else {
|
||||
val artistIds =
|
||||
album.data.albumArtists
|
||||
?.shuffled()
|
||||
?.take(50)
|
||||
?.map { it.id }
|
||||
api.itemsApi
|
||||
.getItems(
|
||||
ids = artistIds,
|
||||
imageTypes = listOf(ImageType.BACKDROP),
|
||||
).content.items
|
||||
.firstOrNull()
|
||||
?.let { backdropService.submit(BaseItem(it, false)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFavorite(
|
||||
itemId: UUID,
|
||||
favorite: Boolean,
|
||||
|
|
@ -252,6 +263,11 @@ data class AlbumState(
|
|||
}
|
||||
}
|
||||
|
||||
private const val HEADER_ROW = 0
|
||||
private const val SONG_ROW = HEADER_ROW + 1
|
||||
private const val MUSIC_VIDEO_ROW = SONG_ROW + 1
|
||||
private const val SIMILAR_ROW = MUSIC_VIDEO_ROW + 1
|
||||
|
||||
@Composable
|
||||
fun AlbumDetailsPage(
|
||||
itemId: UUID,
|
||||
|
|
@ -285,7 +301,6 @@ fun AlbumDetailsPage(
|
|||
onClickRemoveFromQueue = {},
|
||||
)
|
||||
}
|
||||
|
||||
when (val loading = state.loading) {
|
||||
is LoadingState.Error -> {
|
||||
ErrorMessage(loading, modifier)
|
||||
|
|
@ -299,13 +314,24 @@ fun AlbumDetailsPage(
|
|||
|
||||
LoadingState.Success -> {
|
||||
val album = state.album!!
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
var position by rememberPosition(0, 0)
|
||||
|
||||
val firstFocusRequester = remember { FocusRequester() }
|
||||
val firstBringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
val listState = rememberLazyListState()
|
||||
val itemsBefore = 2
|
||||
val focusRequesters =
|
||||
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
||||
val songFocusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) {
|
||||
if (position.row == SONG_ROW) {
|
||||
songFocusRequester.tryRequestFocus()
|
||||
} else {
|
||||
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||
}
|
||||
viewModel.updateBackDrop()
|
||||
}
|
||||
val backHandlerActive by remember {
|
||||
derivedStateOf {
|
||||
listState.firstVisibleItemIndex > itemsBefore
|
||||
|
|
@ -371,13 +397,16 @@ fun AlbumDetailsPage(
|
|||
},
|
||||
favorite = album.favorite,
|
||||
buttonOnFocusChanged = {
|
||||
if (it.isFocused) scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
if (it.isFocused) {
|
||||
position = RowColumn(HEADER_ROW, 0)
|
||||
scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
}
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.onFocusChanged {
|
||||
if (it.hasFocus) scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
}.focusRequester(focusRequester),
|
||||
}.focusRequester(focusRequesters[HEADER_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -396,7 +425,10 @@ fun AlbumDetailsPage(
|
|||
) {
|
||||
SongListItem(
|
||||
song = song,
|
||||
onClick = { viewModel.play(false, index) },
|
||||
onClick = {
|
||||
position = RowColumn(SONG_ROW, index)
|
||||
viewModel.play(false, index)
|
||||
},
|
||||
onLongClick = {
|
||||
if (song != null) {
|
||||
moreDialog =
|
||||
|
|
@ -424,6 +456,9 @@ fun AlbumDetailsPage(
|
|||
Modifier
|
||||
.focusRequester(firstFocusRequester)
|
||||
.bringIntoViewRequester(firstBringIntoViewRequester),
|
||||
).ifElse(
|
||||
position.row == SONG_ROW && position.column == index,
|
||||
Modifier.focusRequester(songFocusRequester),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -434,6 +469,7 @@ fun AlbumDetailsPage(
|
|||
title = stringResource(R.string.music_videos),
|
||||
items = state.musicVideos,
|
||||
onClickItem = { index, item ->
|
||||
position = RowColumn(MUSIC_VIDEO_ROW, index)
|
||||
viewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
onLongClickItem = { index, item ->
|
||||
|
|
@ -450,6 +486,7 @@ fun AlbumDetailsPage(
|
|||
modifier = mod,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[MUSIC_VIDEO_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -459,6 +496,7 @@ fun AlbumDetailsPage(
|
|||
title = stringResource(R.string.more_like_this),
|
||||
items = state.similar,
|
||||
onClickItem = { index, item ->
|
||||
position = RowColumn(SIMILAR_ROW, index)
|
||||
viewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
onLongClickItem = { index, item ->
|
||||
|
|
@ -475,6 +513,7 @@ fun AlbumDetailsPage(
|
|||
modifier = mod,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[SIMILAR_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,12 +64,16 @@ import com.github.damontecres.wholphin.ui.components.Optional
|
|||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.toBaseItems
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||
|
|
@ -270,6 +274,12 @@ data class ArtistState(
|
|||
}
|
||||
}
|
||||
|
||||
private const val HEADER_ROW = 0
|
||||
private const val SONG_ROW = HEADER_ROW + 1
|
||||
private const val ALBUM_ROW = SONG_ROW + 1
|
||||
private const val MUSIC_VIDEO_ROW = ALBUM_ROW + 1
|
||||
private const val SIMILAR_ROW = MUSIC_VIDEO_ROW + 1
|
||||
|
||||
@Composable
|
||||
fun ArtistDetailsPage(
|
||||
itemId: UUID,
|
||||
|
|
@ -316,10 +326,17 @@ fun ArtistDetailsPage(
|
|||
}
|
||||
|
||||
LoadingState.Success -> {
|
||||
var position by rememberPosition(0, 0)
|
||||
val artist = state.artist!!
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusRequesters =
|
||||
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
||||
val songFocusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
if (position.row == SONG_ROW) {
|
||||
songFocusRequester.tryRequestFocus()
|
||||
} else {
|
||||
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||
}
|
||||
viewModel.init()
|
||||
}
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
|
|
@ -378,13 +395,16 @@ fun ArtistDetailsPage(
|
|||
},
|
||||
favorite = artist.favorite,
|
||||
buttonOnFocusChanged = {
|
||||
if (it.isFocused) scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
if (it.isFocused) {
|
||||
position = RowColumn(HEADER_ROW, 0)
|
||||
scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
}
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.onFocusChanged {
|
||||
if (it.hasFocus) scope.launch { bringIntoViewRequester.bringIntoView() }
|
||||
}.focusRequester(focusRequester),
|
||||
}.focusRequester(focusRequesters[HEADER_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -400,7 +420,10 @@ fun ArtistDetailsPage(
|
|||
itemsIndexed(state.topSongs) { index, song ->
|
||||
SongListItem(
|
||||
song = song,
|
||||
onClick = { song?.let { viewModel.play(it) } },
|
||||
onClick = {
|
||||
position = RowColumn(SONG_ROW, index)
|
||||
song?.let { viewModel.play(it) }
|
||||
},
|
||||
onLongClick = {
|
||||
if (song != null) {
|
||||
moreDialog =
|
||||
|
|
@ -418,9 +441,15 @@ fun ArtistDetailsPage(
|
|||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(.75f),
|
||||
showArtist = false,
|
||||
isPlaying = song != null && currentMusic.currentItemId == song.id,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth(.75f)
|
||||
.ifElse(
|
||||
position.row == SONG_ROW && position.column == index,
|
||||
Modifier.focusRequester(songFocusRequester),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -429,6 +458,7 @@ fun ArtistDetailsPage(
|
|||
title = stringResource(R.string.albums),
|
||||
items = state.albums,
|
||||
onClickItem = { index, album ->
|
||||
position = RowColumn(ALBUM_ROW, index)
|
||||
viewModel.navigationManager.navigateTo(album.destination())
|
||||
},
|
||||
onLongClickItem = { index, album ->
|
||||
|
|
@ -444,6 +474,7 @@ fun ArtistDetailsPage(
|
|||
aspectRatio = AspectRatios.SQUARE,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[ALBUM_ROW]),
|
||||
)
|
||||
}
|
||||
if (state.musicVideos.isNotEmpty()) {
|
||||
|
|
@ -452,6 +483,7 @@ fun ArtistDetailsPage(
|
|||
title = stringResource(R.string.music_videos),
|
||||
items = state.musicVideos,
|
||||
onClickItem = { index, item ->
|
||||
position = RowColumn(MUSIC_VIDEO_ROW, index)
|
||||
viewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
onLongClickItem = { index, item ->
|
||||
|
|
@ -468,6 +500,7 @@ fun ArtistDetailsPage(
|
|||
modifier = mod,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[MUSIC_VIDEO_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -477,6 +510,7 @@ fun ArtistDetailsPage(
|
|||
title = stringResource(R.string.more_like_this),
|
||||
items = state.similar,
|
||||
onClickItem = { index, item ->
|
||||
position = RowColumn(SIMILAR_ROW, index)
|
||||
viewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
onLongClickItem = { index, item ->
|
||||
|
|
@ -493,6 +527,7 @@ fun ArtistDetailsPage(
|
|||
modifier = mod,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[SIMILAR_ROW]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue