Skip to content
This repository has been archived by the owner on Mar 30, 2024. It is now read-only.

Commit

Permalink
Implement test version of listening history, fix playlist images, fix…
Browse files Browse the repository at this point in the history
… widths

Signed-off-by: iTaysonLab <[email protected]>
  • Loading branch information
iTaysonLab committed May 4, 2022
1 parent 8c9321c commit 3fb4736
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class SpInternalApi @Inject constructor(
HubImage(
uri = playlist.attributes.formatAttributesList.find { it.key == "image" }?.value
?: playlist.attributes.formatAttributesList.find { it.key == "image_url" }?.value
?: playlist.attributes.unknownFields.asMap()[13]
?.lengthDelimitedList
?.get(0)?.toStringUtf8()
// I HAVE NO IDEA ABOUT THIS DON'T TOUCH
?.split(Regex(".default.."))?.get(1)
?: "https://i.scdn.co/image/${
ImageId.fromHex(Utils.bytesToHex(playlist.attributes.picture)).hexId()
}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import dev.zacsweers.moshix.sealed.annotations.TypeLabel
sealed class HubComponent {
// HOME

@TypeLabel("home:shortSectionHeader", alternateLabels = ["home:sectionHeader"])
@TypeLabel("home:shortSectionHeader", alternateLabels = ["home:sectionHeader", "home:encoreSectionHeader"])
object HomeShortSectionHeader: HubComponent()

@TypeLabel("home:tappableSectionHeader")
Expand Down Expand Up @@ -76,9 +76,12 @@ sealed class HubComponent {
@TypeLabel("synth:playlistHeaderLarge")
object LargePlaylistHeader: HubComponent()

@TypeLabel("synth:playlistTrackRow")
@TypeLabel("synth:playlistTrackRow", alternateLabels = ["listeninghistory:trackRow"])
object PlaylistTrackRow: HubComponent()

@TypeLabel("listeninghistory:dividerAfterEntityRow")
object EmptySpace: HubComponent()

@DefaultObject
object Unknown: HubComponent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fun HubBinder (
item: HubItem
) {
when (item.component) {
HubComponent.HomeShortSectionHeader -> HomeSectionHeader(item.text!!)
HubComponent.HomeShortSectionHeader -> HomeSectionHeader(item.text!!, delegate)
HubComponent.HomeLargeSectionHeader -> HomeSectionLargeHeader(navController, delegate, item)
HubComponent.GlueSectionHeader -> SectionHeader(item.text!!, delegate)
HubComponent.ShortcutsContainer -> ShortcutsContainer(navController, delegate, item.children!!)
Expand Down Expand Up @@ -46,6 +46,7 @@ fun HubBinder (
HubComponent.ImageRow -> ImageRow(navController, delegate, item)

HubComponent.OutlinedButton -> OutlineButton(navController, delegate, item)
HubComponent.EmptySpace -> {}

else -> {
Text("Unsupported, id = ${item.id}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubText
import bruhcollective.itaysonlab.jetispot.ui.hub.HubScreenDelegate

@Composable
fun HomeSectionHeader (
text: HubText
text: HubText,
delegate: HubScreenDelegate,
) {
Box(Modifier.padding(vertical = 8.dp)) {
Box(Modifier.padding(vertical = 8.dp).padding(horizontal = if (delegate.isSurroundedWithPadding()) 0.dp else 16.dp)) {
Text(text = text.title!!, color = MaterialTheme.colorScheme.onSurface, fontWeight = FontWeight.Bold, fontSize = 21.sp, modifier = Modifier.align(Alignment.CenterStart))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fun PlaylistHeader(
Column(
Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(
brush = Brush.verticalGradient(
colors = listOf(dominantColorAsBg.value, Color.Transparent)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package bruhcollective.itaysonlab.jetispot.ui.hub.components

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -27,6 +24,7 @@ fun PlaylistTrackRow(
Row(
Modifier
.clickableHub(navController, delegate, item)
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
AsyncImage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import bruhcollective.itaysonlab.jetispot.core.SpApiManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubResponse
import bruhcollective.itaysonlab.jetispot.ui.screens.config.ConfigScreen
import bruhcollective.itaysonlab.jetispot.ui.screens.history.ListeningHistoryScreen
import bruhcollective.itaysonlab.jetispot.ui.screens.hub.HubScreen
import bruhcollective.itaysonlab.jetispot.ui.screens.hub.PlaylistScreen

Expand All @@ -21,7 +22,8 @@ fun DynamicSpIdScreen(
uri: String,
fullUri: String,
) {
val uriSeparated = uri.split(":")
var uriSeparated = uri.split(":")
if (uriSeparated[0] == "user") uriSeparated = uriSeparated.drop(2)
val id = uriSeparated.getOrElse(1) { "" }

when (uriSeparated[0]) {
Expand All @@ -41,6 +43,12 @@ fun DynamicSpIdScreen(
"playlist" -> PlaylistScreen(navController, id)
"config" -> ConfigScreen(navController)

"internal" -> {
when (id) {
"listeninghistory" -> ListeningHistoryScreen(navController)
}
}

else -> {
Box(Modifier.fillMaxSize()) {
Column(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.history

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.navigation.NavController
import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpApiManager
import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubResponse
import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
import bruhcollective.itaysonlab.jetispot.ui.hub.HubBinder
import bruhcollective.itaysonlab.jetispot.ui.hub.HubScreenDelegate
import bruhcollective.itaysonlab.jetispot.ui.shared.ControllableScaffold
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingErrorPage
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
import bruhcollective.itaysonlab.jetispot.ui.shared.evo.LargeTopAppBar
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListeningHistoryScreen(
navController: NavController,
viewModel: HistoryViewModel = hiltViewModel()
) {
val scrollBehavior = remember { TopAppBarDefaults.enterAlwaysScrollBehavior() }
val scope = rememberCoroutineScope()
val loadFunc: suspend CoroutineScope.() -> Unit = remember {{
viewModel.load {
getListeningHistory()
}
}}

LaunchedEffect(Unit) {
loadFunc()
}

when (viewModel.state) {
is HistoryViewModel.State.Loaded -> {
ControllableScaffold(topBar = {
LargeTopAppBar(title = {
Text(stringResource(id = R.string.listening_history))
}, navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(Icons.Default.ArrowBack, null)
}
}, contentPadding = PaddingValues(top = with(LocalDensity.current) { WindowInsets.statusBars.getTop(LocalDensity.current).toDp() }), scrollBehavior = scrollBehavior)
}, drawContentUnderTopBar = false, modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { padding ->
LazyColumn(
modifier = Modifier
.fillMaxHeight()
.padding(padding)
) {
(viewModel.state as HistoryViewModel.State.Loaded).data.apply {
if (header != null) {
item(
key = header.id,
contentType = header.component.javaClass.simpleName,
) {
HubBinder(navController, viewModel, header)
}
}

items(body, key = { it.id }, contentType = { it.component.javaClass.simpleName }) {
HubBinder(navController, viewModel, it)
}
}
}
}
}
is HistoryViewModel.State.Error -> PagingErrorPage(onReload = { scope.launch(block = loadFunc) }, modifier = Modifier.fillMaxSize())
HistoryViewModel.State.Loading -> PagingLoadingPage(Modifier.fillMaxSize())
}
}

@HiltViewModel
class HistoryViewModel @Inject constructor(
private val spInternalApi: SpInternalApi,
private val spApiManager: SpApiManager,
private val spPlayerServiceManager: SpPlayerServiceManager
) : ViewModel(), HubScreenDelegate {
private val _state = mutableStateOf<State>(State.Loading)
val state: State get() = _state.value

suspend fun load(loader: suspend SpInternalApi.(SpApiManager) -> HubResponse) {
_state.value = try {
State.Loaded(spInternalApi.loader(spApiManager))
} catch (e: Exception) {
e.printStackTrace()
State.Error(e)
}
}

suspend fun reload(loader: suspend SpInternalApi.(SpApiManager) -> HubResponse) {
_state.value = State.Loading
load(loader)
}

override fun play(data: PlayFromContextData) {
spPlayerServiceManager.play(data.uri, data.player)
}

override fun isSurroundedWithPadding() = false

override suspend fun calculateDominantColor(url: String, dark: Boolean): Color {
return try {
val apiResult = spApiManager.partners.getDominantColors(url).data.extractedColors[0].let {
if (dark) it.colorRaw else it.colorDark
}.hex

Color(android.graphics.Color.parseColor(apiResult))
} catch (e: Exception) {
// e.printStackTrace()
Color.Transparent
}
}

sealed class State {
class Loaded(val data: HubResponse) : State()
class Error(val error: Exception) : State()
object Loading : State()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,10 @@ fun PlaylistScreen(
) {
val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior() }
val scope = rememberCoroutineScope()
val loadFunc: suspend CoroutineScope.() -> Unit = remember {{
viewModel.loadPlaylist(id)
// TODO: Load likes
}}

LaunchedEffect(Unit) {
loadFunc()
viewModel.loadPlaylist(id)
// TODO: Load likes
}

when (viewModel.state) {
Expand Down Expand Up @@ -88,7 +85,7 @@ fun PlaylistScreen(
}
}
}
is PlaylistViewModel.State.Error -> PagingErrorPage(onReload = { scope.launch(block = loadFunc) }, modifier = Modifier.fillMaxSize())
is PlaylistViewModel.State.Error -> PagingErrorPage(onReload = { scope.launch { viewModel.reloadPlaylist(id) } }, modifier = Modifier.fillMaxSize())
PlaylistViewModel.State.Loading -> PagingLoadingPage(Modifier.fillMaxSize())
}
}
Expand All @@ -111,6 +108,11 @@ class PlaylistViewModel @Inject constructor(
}
}

suspend fun reloadPlaylist(id: String) {
_state.value = State.Loading
loadPlaylist(id)
}

suspend fun load(loader: suspend SpInternalApi.(SpApiManager) -> HubResponse) {
_state.value = try {
State.Loaded(spInternalApi.loader(spApiManager))
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,6 @@
<string name="logout_message">This will remove all data linked to your account from this device. Settings won\'t be reset to default.</string>
<string name="logout_confirm">Confirm</string>
<string name="logout_cancel">Cancel</string>

<string name="listening_history">Listening history</string>
</resources>

0 comments on commit 3fb4736

Please sign in to comment.