From 81bfd2b06f38ad29d894d82e9e6120e38f7709d9 Mon Sep 17 00:00:00 2001 From: Romman Sabbir Date: Wed, 24 Apr 2024 10:23:41 +0600 Subject: [PATCH] Updates (#12) * feat : char detail ui updated, return playlist and track id on track item click * feat : added support for direct play track from the play list in chart detail view * feat : player background marked as clickable to stop taking action from user when the view is overlaying --- .../decompose/ChartDetailsComponent.kt | 28 +++++++++++ .../musicapp/chartdetails/ChartDetails.kt | 46 ++++++++++++------- .../chartdetails/ChartDetailsLarge.kt | 35 +++++++------- .../decompose/ChartDetailsComponent.kt | 2 +- .../musicapp/decompose/MusicRootImpl.kt | 3 +- .../musicapp/decompose/PlayerComponent.kt | 2 +- .../kotlin/musicapp/playerview/PlayerView.kt | 4 +- .../musicapp/playerview/PlayerViewModel.kt | 2 +- 8 files changed, 82 insertions(+), 40 deletions(-) create mode 100644 shared/src/commonMain/kotlin/com/example/musicapp_kmp/decompose/ChartDetailsComponent.kt diff --git a/shared/src/commonMain/kotlin/com/example/musicapp_kmp/decompose/ChartDetailsComponent.kt b/shared/src/commonMain/kotlin/com/example/musicapp_kmp/decompose/ChartDetailsComponent.kt new file mode 100644 index 0000000..1db40ab --- /dev/null +++ b/shared/src/commonMain/kotlin/com/example/musicapp_kmp/decompose/ChartDetailsComponent.kt @@ -0,0 +1,28 @@ +package com.example.musicapp_kmp.decompose + +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import musicapp.chartdetails.ChartDetailsViewModel +import musicapp.network.models.topfiftycharts.Item + + +/** + * Created by abdulbasit on 19/03/2023. + */ + +interface ChartDetailsComponent { + val viewModel: ChartDetailsViewModel + fun onOutPut(output: Output) + sealed class Output { + data object GoBack : Output() + data class OnPlayAllSelected(val playlist: List) : Output() + data class OnTrackSelected(val trackId: String, val playlist: List) : Output() + } + + @Parcelize + sealed interface Input : Parcelable { + + @Parcelize + data class TrackUpdated(val trackId: String) : Input, Parcelable + } +} diff --git a/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetails.kt b/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetails.kt index fdc4895..5445184 100644 --- a/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetails.kt +++ b/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetails.kt @@ -3,6 +3,7 @@ package musicapp.chartdetails import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,7 +17,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon @@ -52,7 +53,6 @@ import com.seiko.imageloader.rememberAsyncImagePainter internal fun ChartDetailsScreen( chartDetailsComponent: ChartDetailsComponent, ) { - val state = chartDetailsComponent.viewModel.chartDetailsViewState.collectAsState() when (val resultedState = state.value) { is ChartDetailsViewState.Failure -> Failure(resultedState.error) @@ -68,12 +68,9 @@ internal fun ChartDetailsScreen( ) ) }, - onPlayTrack = { + onPlayTrack = { id, list -> chartDetailsComponent.onOutPut( - ChartDetailsComponent.Output.OnTrackSelected( - it - ) - ) + ChartDetailsComponent.Output.OnTrackSelected(id, list)) } ) } @@ -111,7 +108,7 @@ internal fun Failure(message: String) { internal fun ChartDetailsView( chartDetails: TopFiftyCharts, onPlayAllClicked: (List) -> Unit, - onPlayTrack: (String) -> Unit, + onPlayTrack: (String, List) -> Unit, playingTrackId: Any ) { @@ -147,8 +144,7 @@ internal fun ChartDetailsView( Image( painter = painter, contentDescription = chartDetails.images?.first()?.url.orEmpty(), - modifier = Modifier.padding(top = 24.dp, bottom = 24.dp).fillMaxWidth() - .aspectRatio(1f) + modifier = Modifier.padding(top = 100.dp, bottom = 24.dp).fillMaxWidth().aspectRatio(1f) .clip(RoundedCornerShape(25.dp)), contentScale = ContentScale.Crop, ) @@ -169,12 +165,24 @@ internal fun ChartDetailsView( Spacer(Modifier.height(32.dp).fillMaxWidth()) OptionChips(onPlayAllClicked, chartDetails.tracks?.items ?: emptyList()) } - items(chartDetails.tracks?.items ?: emptyList()) { track -> + itemsIndexed(chartDetails.tracks?.items ?: emptyList()) { index, track -> Box( - modifier = Modifier.clip(RoundedCornerShape(20.dp)).fillMaxWidth().background( - if (track.track?.id.orEmpty() == selectedTrack.value) Color(0xCCFACD66) - else Color(0xFF33373B) - ).padding(16.dp) + modifier = Modifier + .clip(RoundedCornerShape(20.dp)) + .fillMaxWidth().background( + if (track.track?.id.orEmpty() == selectedTrack.value) Color(0xCCFACD66) + else Color(0xFF33373B) + ) + .padding(16.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }) + { + onPlayTrack( + track.track?.id.orEmpty(), + chartDetails.tracks?.items ?: mutableListOf() + ) + } ) { val active by remember { mutableStateOf(false) } Row(modifier = Modifier.fillMaxWidth()) { @@ -182,7 +190,10 @@ internal fun ChartDetailsView( rememberAsyncImagePainter(track.track?.album?.images?.first()?.url.orEmpty()) Box(modifier = Modifier .clickable { - onPlayTrack(track.track?.id.orEmpty()) + onPlayTrack( + track.track?.id.orEmpty(), + chartDetails.tracks?.items ?: mutableListOf() + ) }) { Image( painter, @@ -231,6 +242,9 @@ internal fun ChartDetailsView( ) } } + if (index == chartDetails.tracks?.items?.lastIndex) { + Column(modifier = Modifier.fillMaxWidth().height(100.dp)) { } + } } } } diff --git a/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetailsLarge.kt b/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetailsLarge.kt index acf081b..d171c33 100644 --- a/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetailsLarge.kt +++ b/shared/src/commonMain/kotlin/musicapp/chartdetails/ChartDetailsLarge.kt @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -60,19 +62,9 @@ internal fun ChartDetailsScreenLarge( is ChartDetailsViewState.Success -> ChartDetailsViewLarge( chartDetails = resultedState.chartDetails, playingTrackId = resultedState.playingTrackId, - onPlayAllClicked = { - chartDetailsComponent.onOutPut( - ChartDetailsComponent.Output.OnPlayAllSelected( - it - ) - ) - }, - onPlayTrack = { - chartDetailsComponent.onOutPut( - ChartDetailsComponent.Output.OnTrackSelected( - it - ) - ) + onPlayAllClicked = { chartDetailsComponent.onOutPut(ChartDetailsComponent.Output.OnPlayAllSelected(it)) }, + onPlayTrack = { id, list -> + chartDetailsComponent.onOutPut(ChartDetailsComponent.Output.OnTrackSelected(id, list)) } ) } @@ -99,7 +91,7 @@ internal fun ChartDetailsScreenLarge( internal fun ChartDetailsViewLarge( chartDetails: TopFiftyCharts, onPlayAllClicked: (List) -> Unit, - onPlayTrack: (String) -> Unit, + onPlayTrack: (String, List) -> Unit, playingTrackId: String ) { val painter = rememberAsyncImagePainter(chartDetails.images?.first()?.url.orEmpty()) @@ -171,10 +163,15 @@ internal fun ChartDetailsViewLarge( } items(chartDetails.tracks?.items ?: emptyList()) { track -> Box( - modifier = Modifier.clip(RoundedCornerShape(20.dp)).fillMaxWidth().background( - if (track.track?.id.orEmpty() == selectedTrack.value) Color(0xCCFACD66) - else Color(0xFF33373B) - ).padding(16.dp) + modifier = Modifier + .clip(RoundedCornerShape(20.dp)) + .fillMaxWidth() + .background(if (track.track?.id.orEmpty() == selectedTrack.value) Color(0xCCFACD66) else Color(0xFF33373B)) + .padding(16.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }) + { onPlayTrack(track.track?.id.orEmpty(), chartDetails.tracks?.items ?: mutableListOf()) } ) { Row(modifier = Modifier.fillMaxWidth()) { val active by remember { mutableStateOf(false) } @@ -182,7 +179,7 @@ internal fun ChartDetailsViewLarge( rememberAsyncImagePainter(track.track?.album?.images?.first()?.url.orEmpty()) Box(modifier = Modifier .clickable { - onPlayTrack(track.track?.id.orEmpty()) + onPlayTrack(track.track?.id.orEmpty(), chartDetails.tracks?.items ?: mutableListOf()) }) { Image( painter, diff --git a/shared/src/commonMain/kotlin/musicapp/decompose/ChartDetailsComponent.kt b/shared/src/commonMain/kotlin/musicapp/decompose/ChartDetailsComponent.kt index 410c988..624999c 100644 --- a/shared/src/commonMain/kotlin/musicapp/decompose/ChartDetailsComponent.kt +++ b/shared/src/commonMain/kotlin/musicapp/decompose/ChartDetailsComponent.kt @@ -15,7 +15,7 @@ interface ChartDetailsComponent { sealed class Output { data object GoBack : Output() data class OnPlayAllSelected(val playlist: List) : Output() - data class OnTrackSelected(val trackId: String) : Output() + data class OnTrackSelected(val trackId: String, val playlist: List) : Output() } @Serializable diff --git a/shared/src/commonMain/kotlin/musicapp/decompose/MusicRootImpl.kt b/shared/src/commonMain/kotlin/musicapp/decompose/MusicRootImpl.kt index 0ea733e..151009e 100644 --- a/shared/src/commonMain/kotlin/musicapp/decompose/MusicRootImpl.kt +++ b/shared/src/commonMain/kotlin/musicapp/decompose/MusicRootImpl.kt @@ -111,8 +111,9 @@ class MusicRootImpl( } is ChartDetailsComponent.Output.OnTrackSelected -> { + dialogNavigation.activate(DialogConfig(output.playlist)) CoroutineScope(Dispatchers.Default).launch { - musicPlayerInput.emit(PlayerComponent.Input.PlayTrack(output.trackId)) + musicPlayerInput.emit(PlayerComponent.Input.PlayTrack(output.trackId, output.playlist)) } } } diff --git a/shared/src/commonMain/kotlin/musicapp/decompose/PlayerComponent.kt b/shared/src/commonMain/kotlin/musicapp/decompose/PlayerComponent.kt index 98b8628..a021998 100644 --- a/shared/src/commonMain/kotlin/musicapp/decompose/PlayerComponent.kt +++ b/shared/src/commonMain/kotlin/musicapp/decompose/PlayerComponent.kt @@ -20,7 +20,7 @@ interface PlayerComponent { } sealed interface Input { - data class PlayTrack(val trackId: String) : Input + data class PlayTrack(val trackId: String, val tracksList: List) : Input data class UpdateTracks(val tracksList: List) : Input } diff --git a/shared/src/commonMain/kotlin/musicapp/playerview/PlayerView.kt b/shared/src/commonMain/kotlin/musicapp/playerview/PlayerView.kt index 08ffbe4..a5517cf 100644 --- a/shared/src/commonMain/kotlin/musicapp/playerview/PlayerView.kt +++ b/shared/src/commonMain/kotlin/musicapp/playerview/PlayerView.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon @@ -71,7 +72,8 @@ internal fun PlayerView(playerComponent: PlayerComponent) { Box( modifier = Modifier.fillMaxWidth().background(Color(0xCC101010)) - .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 56.dp) + .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 16.dp) + .clickable(indication = null, interactionSource = remember { MutableInteractionSource() }) { } ) { Row(modifier = Modifier.fillMaxWidth()) { val painter = rememberAsyncImagePainter( diff --git a/shared/src/commonMain/kotlin/musicapp/playerview/PlayerViewModel.kt b/shared/src/commonMain/kotlin/musicapp/playerview/PlayerViewModel.kt index d32480a..cf89dc8 100644 --- a/shared/src/commonMain/kotlin/musicapp/playerview/PlayerViewModel.kt +++ b/shared/src/commonMain/kotlin/musicapp/playerview/PlayerViewModel.kt @@ -44,7 +44,7 @@ class PlayerViewModel( when (it) { is PlayerComponent.Input.PlayTrack -> chartDetailsViewState.value = - chartDetailsViewState.value.copy(playingTrackId = it.trackId) + chartDetailsViewState.value.copy(playingTrackId = it.trackId, trackList = it.tracksList) is PlayerComponent.Input.UpdateTracks -> chartDetailsViewState.value =