Tweak Lyrics code
This commit is contained in:
@@ -13,7 +13,6 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -30,10 +29,10 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.autoSaver
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
@@ -46,7 +45,6 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaMetadata
|
import androidx.media3.common.MediaMetadata
|
||||||
import com.valentinilk.shimmer.shimmer
|
|
||||||
import it.vfsfitvnm.kugou.KuGou
|
import it.vfsfitvnm.kugou.KuGou
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
@@ -55,6 +53,7 @@ import it.vfsfitvnm.vimusic.query
|
|||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
|
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
|
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.DefaultDarkColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.DefaultDarkColorPalette
|
||||||
@@ -66,7 +65,7 @@ import it.vfsfitvnm.vimusic.utils.center
|
|||||||
import it.vfsfitvnm.vimusic.utils.color
|
import it.vfsfitvnm.vimusic.utils.color
|
||||||
import it.vfsfitvnm.vimusic.utils.isShowingSynchronizedLyricsKey
|
import it.vfsfitvnm.vimusic.utils.isShowingSynchronizedLyricsKey
|
||||||
import it.vfsfitvnm.vimusic.utils.medium
|
import it.vfsfitvnm.vimusic.utils.medium
|
||||||
import it.vfsfitvnm.vimusic.utils.relaunchableEffect
|
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
|
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
|
||||||
import it.vfsfitvnm.youtubemusic.Innertube
|
import it.vfsfitvnm.youtubemusic.Innertube
|
||||||
@@ -76,7 +75,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -103,19 +101,31 @@ fun Lyrics(
|
|||||||
|
|
||||||
var isShowingSynchronizedLyrics by rememberPreference(isShowingSynchronizedLyricsKey, false)
|
var isShowingSynchronizedLyrics by rememberPreference(isShowingSynchronizedLyricsKey, false)
|
||||||
|
|
||||||
var state by remember(mediaId, isShowingSynchronizedLyrics) {
|
var isEditing by remember(mediaId, isShowingSynchronizedLyrics) {
|
||||||
mutableStateOf(LyricsState())
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fetchLyrics = relaunchableEffect(mediaId, isShowingSynchronizedLyrics) {
|
val lyrics by produceSaveableState(
|
||||||
|
initialValue = ".",
|
||||||
|
stateSaver = autoSaver<String?>(),
|
||||||
|
mediaId, isShowingSynchronizedLyrics
|
||||||
|
) {
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
Database.synchronizedLyrics(mediaId)
|
Database.synchronizedLyrics(mediaId)
|
||||||
} else {
|
} else {
|
||||||
Database.lyrics(mediaId)
|
Database.lyrics(mediaId)
|
||||||
}.distinctUntilChanged().map flowMap@{ lyrics ->
|
}
|
||||||
if (lyrics != null) return@flowMap lyrics
|
.flowOn(Dispatchers.IO)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collect { value = it }
|
||||||
|
}
|
||||||
|
|
||||||
state = state.copy(isLoading = true)
|
var isError by remember(lyrics) {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(lyrics == null) {
|
||||||
|
if (lyrics != null) return@LaunchedEffect
|
||||||
|
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
val mediaMetadata = mediaMetadataProvider()
|
val mediaMetadata = mediaMetadataProvider()
|
||||||
@@ -137,25 +147,21 @@ fun Lyrics(
|
|||||||
)?.map { it?.value }
|
)?.map { it?.value }
|
||||||
} else {
|
} else {
|
||||||
Innertube.lyrics(NextBody(videoId = mediaId))
|
Innertube.lyrics(NextBody(videoId = mediaId))
|
||||||
}?.map { newLyrics ->
|
}?.onSuccess { newLyrics ->
|
||||||
onLyricsUpdate(isShowingSynchronizedLyrics, mediaId, newLyrics ?: "")
|
onLyricsUpdate(isShowingSynchronizedLyrics, mediaId, newLyrics ?: "")
|
||||||
state = state.copy(isLoading = false)
|
}?.onFailure {
|
||||||
return@flowMap newLyrics ?: ""
|
isError = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = state.copy(isLoading = false)
|
if (isEditing) {
|
||||||
null
|
|
||||||
}.flowOn(Dispatchers.IO).collect { state = state.copy(lyrics = it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.isEditing) {
|
|
||||||
TextFieldDialog(
|
TextFieldDialog(
|
||||||
hintText = "Enter the lyrics",
|
hintText = "Enter the lyrics",
|
||||||
initialTextInput = state.lyrics ?: "",
|
initialTextInput = lyrics ?: "",
|
||||||
singleLine = false,
|
singleLine = false,
|
||||||
maxLines = 10,
|
maxLines = 10,
|
||||||
isTextInputValid = { true },
|
isTextInputValid = { true },
|
||||||
onDismiss = { state = state.copy(isEditing = false) },
|
onDismiss = { isEditing = false },
|
||||||
onDone = {
|
onDone = {
|
||||||
query {
|
query {
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
@@ -163,7 +169,6 @@ fun Lyrics(
|
|||||||
} else {
|
} else {
|
||||||
Database.updateLyrics(mediaId, it)
|
Database.updateLyrics(mediaId, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -181,7 +186,7 @@ fun Lyrics(
|
|||||||
.background(Color.Black.copy(0.8f))
|
.background(Color.Black.copy(0.8f))
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = !state.isLoading && state.lyrics == null,
|
visible = isError && lyrics == null,
|
||||||
enter = slideInVertically { -it },
|
enter = slideInVertically { -it },
|
||||||
exit = slideOutVertically { -it },
|
exit = slideOutVertically { -it },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -198,7 +203,7 @@ fun Lyrics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = state.lyrics?.let(String::isEmpty) ?: false,
|
visible = lyrics?.let(String::isEmpty) ?: false,
|
||||||
enter = slideInVertically { -it },
|
enter = slideInVertically { -it },
|
||||||
exit = slideOutVertically { -it },
|
exit = slideOutVertically { -it },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -214,22 +219,7 @@ fun Lyrics(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.isLoading) {
|
lyrics?.let { lyrics ->
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier = Modifier
|
|
||||||
.shimmer()
|
|
||||||
) {
|
|
||||||
repeat(4) { index ->
|
|
||||||
TextPlaceholder(
|
|
||||||
color = colorPalette.onOverlayShimmer,
|
|
||||||
modifier = Modifier
|
|
||||||
.alpha(1f - index * 0.05f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.lyrics?.let { lyrics ->
|
|
||||||
if (lyrics.isNotEmpty() && lyrics != ".") {
|
if (lyrics.isNotEmpty() && lyrics != ".") {
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
@@ -292,6 +282,14 @@ fun Lyrics(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lyrics == null && !isError) {
|
||||||
|
ShimmerHost(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
repeat(4) {
|
||||||
|
TextPlaceholder(color = colorPalette.onOverlayShimmer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.drawable.ellipsis_horizontal),
|
painter = painterResource(R.drawable.ellipsis_horizontal),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -317,7 +315,7 @@ fun Lyrics(
|
|||||||
text = "Edit lyrics",
|
text = "Edit lyrics",
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
state = state.copy(isEditing = true)
|
isEditing = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -353,23 +351,17 @@ fun Lyrics(
|
|||||||
MenuEntry(
|
MenuEntry(
|
||||||
icon = R.drawable.download,
|
icon = R.drawable.download,
|
||||||
text = "Fetch lyrics again",
|
text = "Fetch lyrics again",
|
||||||
|
isEnabled = lyrics != null,
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
if (state.lyrics == null) {
|
|
||||||
fetchLyrics()
|
|
||||||
} else {
|
|
||||||
query {
|
query {
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
Database.updateSynchronizedLyrics(
|
Database.updateSynchronizedLyrics(mediaId, null)
|
||||||
mediaId,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Database.updateLyrics(mediaId, null)
|
Database.updateLyrics(mediaId, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,10 +373,3 @@ fun Lyrics(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private data class LyricsState(
|
|
||||||
val isLoading: Boolean = false,
|
|
||||||
val isEditing: Boolean = false,
|
|
||||||
val lyrics: String? = ".",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.service.LoginRequiredException
|
import it.vfsfitvnm.vimusic.service.LoginRequiredException
|
||||||
import it.vfsfitvnm.vimusic.service.PlayableFormatNotFoundException
|
import it.vfsfitvnm.vimusic.service.PlayableFormatNotFoundException
|
||||||
import it.vfsfitvnm.vimusic.service.UnplayableException
|
import it.vfsfitvnm.vimusic.service.UnplayableException
|
||||||
@@ -121,6 +122,7 @@ fun Thumbnail(
|
|||||||
isDisplayed = isShowingLyrics && error == null,
|
isDisplayed = isShowingLyrics && error == null,
|
||||||
onDismiss = { onShowLyrics(false) },
|
onDismiss = { onShowLyrics(false) },
|
||||||
onLyricsUpdate = { areSynchronized, mediaId, lyrics ->
|
onLyricsUpdate = { areSynchronized, mediaId, lyrics ->
|
||||||
|
query {
|
||||||
if (areSynchronized) {
|
if (areSynchronized) {
|
||||||
if (Database.updateSynchronizedLyrics(mediaId, lyrics) == 0) {
|
if (Database.updateSynchronizedLyrics(mediaId, lyrics) == 0) {
|
||||||
if (mediaId == mediaItem.mediaId) {
|
if (mediaId == mediaItem.mediaId) {
|
||||||
@@ -138,6 +140,7 @@ fun Thumbnail(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
size = thumbnailSizeDp,
|
size = thumbnailSizeDp,
|
||||||
mediaMetadataProvider = mediaItem::mediaMetadata,
|
mediaMetadataProvider = mediaItem::mediaMetadata,
|
||||||
|
|||||||
Reference in New Issue
Block a user