diff --git a/app/src/main/java/com/cornellappdev/score/components/EmptyStateMessage.kt b/app/src/main/java/com/cornellappdev/score/components/EmptyStateMessage.kt index c7ab7c8..5ba58cc 100644 --- a/app/src/main/java/com/cornellappdev/score/components/EmptyStateMessage.kt +++ b/app/src/main/java/com/cornellappdev/score/components/EmptyStateMessage.kt @@ -1,8 +1,6 @@ package com.cornellappdev.score.components import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -12,7 +10,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview @@ -23,11 +20,14 @@ import com.cornellappdev.score.theme.GrayMedium import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyNormal import com.cornellappdev.score.theme.Style.heading2 +import com.cornellappdev.score.theme.White @Composable fun EmptyStateMessage(modifier: Modifier = Modifier) { - Column(modifier = modifier.width(158.dp), - horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = modifier.width(158.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { Image( painter = painterResource(id = R.drawable.ic_speaker), contentDescription = "Speaker Icon", @@ -52,8 +52,6 @@ fun EmptyStateMessage(modifier: Modifier = Modifier) { @Preview @Composable -private fun PreviewEmptyStateMessage() { - Box(modifier = Modifier.background(Color.White)) { - EmptyStateMessage() - } +private fun PreviewEmptyStateMessage() = ScorePreview { + EmptyStateMessage() } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/ErrorState.kt b/app/src/main/java/com/cornellappdev/score/components/ErrorState.kt new file mode 100644 index 0000000..cb45ca9 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/ErrorState.kt @@ -0,0 +1,80 @@ +package com.cornellappdev.score.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.R +import com.cornellappdev.score.theme.CrimsonPrimary +import com.cornellappdev.score.theme.GrayMedium +import com.cornellappdev.score.theme.GrayPrimary +import com.cornellappdev.score.theme.Style.bodyNormal +import com.cornellappdev.score.theme.Style.heading2 + +@Composable +fun ErrorState( + onRefresh: () -> Unit, + message: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(100.dp) + ) { + Spacer(modifier = Modifier.height(200.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(R.drawable.ic_feedback), + contentDescription = "feedback bubble" + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = message, + style = heading2.copy(color = GrayPrimary) + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Please try again later.", + style = bodyNormal.copy(color = GrayMedium) + ) + } + + Button( + colors = ButtonDefaults.buttonColors(containerColor = CrimsonPrimary), + onClick = onRefresh + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.ic_cached), + contentDescription = "refresh icon" + ) + Text("Try again") + } + } + Spacer(modifier = Modifier.height(70.dp)) + } +} + +@Preview +@Composable +private fun ErrorStatePreview() = ScorePreview { + ErrorState({}, "Oops! Failed to load.") +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/FeaturedGameCard.kt b/app/src/main/java/com/cornellappdev/score/components/FeaturedGameCard.kt index 3d0d0a1..ed55c55 100644 --- a/app/src/main/java/com/cornellappdev/score/components/FeaturedGameCard.kt +++ b/app/src/main/java/com/cornellappdev/score/components/FeaturedGameCard.kt @@ -110,7 +110,7 @@ fun FeaturedGameHeader( @Preview @Composable -private fun FeaturedGameCardPreview() { +private fun FeaturedGameCardPreview() = ScorePreview { FeaturedGameHeader( leftTeamLogo = painterResource(R.drawable.cornell_logo), rightTeamLogo = "https://cornellbigred.com/images/logos/YALE_LOGO_2020.png?width=80&height=80&mode=max", @@ -177,9 +177,9 @@ fun FeaturedGameCard( } } -@Preview(showBackground = true) +@Preview @Composable -private fun GameScheduleScreen() { +private fun GameScheduleScreen() = ScorePreview { FeaturedGameCard( leftTeamLogo = painterResource(R.drawable.cornell_logo), rightTeamLogo = "https://cornellbigred.com/images/logos/penn_200x200.png?width=80&height=80&mode=max",//painterResource(R.drawable.penn_logo), diff --git a/app/src/main/java/com/cornellappdev/score/components/GameCard.kt b/app/src/main/java/com/cornellappdev/score/components/GameCard.kt index 831a32f..7d28d31 100644 --- a/app/src/main/java/com/cornellappdev/score/components/GameCard.kt +++ b/app/src/main/java/com/cornellappdev/score/components/GameCard.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -18,6 +17,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card @@ -36,21 +36,15 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import com.cornellappdev.score.R -import com.cornellappdev.score.model.GameCardData import com.cornellappdev.score.theme.AmbientColor import com.cornellappdev.score.theme.GrayMedium import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.GrayStroke import com.cornellappdev.score.theme.SpotColor import com.cornellappdev.score.theme.Style.bodyNormal -import com.cornellappdev.score.theme.Style.dateText import com.cornellappdev.score.theme.Style.heading2 import com.cornellappdev.score.theme.Style.labelsNormal -import com.cornellappdev.score.theme.Style.teamName -import com.cornellappdev.score.theme.Style.universityText import com.cornellappdev.score.theme.saturatedGreen -import java.util.Date -import java.util.Locale @Composable fun GameCard( @@ -94,7 +88,8 @@ fun GameCard( ) ) } - ).clickable { onClick(false) } + ) + .clickable { onClick(false) } ) { Column( modifier = Modifier @@ -107,7 +102,8 @@ fun GameCard( modifier = Modifier.fillMaxWidth() ) { Row( - verticalAlignment = Alignment.CenterVertically, modifier = Modifier.widthIn(0.dp, 250.dp) + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.widthIn(0.dp, 250.dp) ) { AsyncImage( model = teamLogo, @@ -209,9 +205,9 @@ fun GameCard( } } -@Preview(showBackground = true) +@Preview @Composable -private fun GameCardPreview() { +private fun GameCardPreview() = ScorePreview { Column { GameCard( teamLogo = "https://cornellbigred.com/images/logos/penn_200x200.png?width=80&height=80&mode=max", //painterResource(id = R.drawable.penn_logo), diff --git a/app/src/main/java/com/cornellappdev/score/components/GameDetailsLoadingScreen.kt b/app/src/main/java/com/cornellappdev/score/components/GameDetailsLoadingScreen.kt new file mode 100644 index 0000000..3371ed4 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/GameDetailsLoadingScreen.kt @@ -0,0 +1,107 @@ +package com.cornellappdev.score.components + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +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.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.R +import com.cornellappdev.score.theme.GrayStroke +import com.cornellappdev.score.theme.LocalInfiniteLoading +import com.cornellappdev.score.theme.Stroke +import com.cornellappdev.score.theme.Wash +import com.cornellappdev.score.util.interpolateColorHSV + +@Composable +fun GameDetailsLoadingScreen( + modifier: Modifier = Modifier +) { + val shimmerColor = interpolateColorHSV(Wash, Stroke, LocalInfiniteLoading.current) + Column( + modifier = modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .background(color = shimmerColor) + .fillMaxWidth() + .height(185.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Canvas( + modifier = Modifier + .size(72.dp) + ) { + drawCircle(color = shimmerColor) + } + Spacer(modifier = Modifier.width(24.dp)) + LoadingStateBox(100, 33.dp, Modifier.width(100.dp)) + Spacer(modifier = Modifier.width(24.dp)) + Canvas( + modifier = Modifier + .size(72.dp) + ) { + drawCircle(color = shimmerColor) + } + } + Column( + modifier = Modifier.padding(24.dp) + ) { + LoadingStateBox(12, 16.dp, Modifier.width(100.dp)) + Spacer(modifier = Modifier.height(12.dp)) + LoadingStateBox(100, 32.dp, Modifier.width(200.dp)) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(12, 16.dp) + + Spacer(modifier = Modifier.height(24.dp)) + LoadingStateBox(8, 125.dp) + Spacer(modifier = Modifier.height(24.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Loading Score Summary", + color = GrayStroke, + style = typography.titleMedium, + modifier = Modifier.weight(1f) + ) + + Image( + painter = painterResource(R.drawable.ic_right_chevron), + contentDescription = "right chevron" + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(8, 48.dp) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(8, 48.dp) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(8, 48.dp) + } + } +} + +@Preview +@Composable +private fun GameDetailsLoadingStatePreview() = ScorePreview { + GameDetailsLoadingScreen() +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt b/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt index 5c7cf90..898c5e7 100644 --- a/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt +++ b/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt @@ -94,7 +94,7 @@ fun GameScoreHeader( @Preview @Composable -private fun GameScoreHeaderPreview() { +private fun GameScoreHeaderPreview() = ScorePreview { GameScoreHeader( leftTeamLogo = painterResource(R.drawable.cornell_logo), rightTeamLogo = painterResource(R.drawable.penn_logo), diff --git a/app/src/main/java/com/cornellappdev/score/components/GamesCarousel.kt b/app/src/main/java/com/cornellappdev/score/components/GamesCarousel.kt index d998174..9490152 100644 --- a/app/src/main/java/com/cornellappdev/score/components/GamesCarousel.kt +++ b/app/src/main/java/com/cornellappdev/score/components/GamesCarousel.kt @@ -27,6 +27,7 @@ import com.cornellappdev.score.theme.CrimsonPrimary import com.cornellappdev.score.theme.GrayLight import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.heading1 +import com.cornellappdev.score.theme.White import com.cornellappdev.score.util.gameList @Composable @@ -106,8 +107,8 @@ fun GamesCarousel( } } -@Preview(showBackground = true, widthDp = 360) @Composable -private fun GamesCarouselPreview() { +@Preview +private fun GamesCarouselPreview() = ScorePreview { GamesCarousel(gameList, GamesCarouselVariant.UPCOMING) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/LoadingScreen.kt b/app/src/main/java/com/cornellappdev/score/components/LoadingScreen.kt new file mode 100644 index 0000000..52225f7 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/LoadingScreen.kt @@ -0,0 +1,133 @@ +package com.cornellappdev.score.components + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +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.material3.Divider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.theme.GrayStroke +import com.cornellappdev.score.theme.LocalInfiniteLoading +import com.cornellappdev.score.theme.Stroke +import com.cornellappdev.score.theme.Style.heading1 +import com.cornellappdev.score.theme.Wash +import com.cornellappdev.score.util.interpolateColorHSV + +@Composable +fun LoadingScreen( + topHeader: String, + bottomHeader: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.fillMaxSize() + ) { + Column( + modifier = Modifier.padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = topHeader, + style = heading1, + color = GrayStroke, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Start) + ) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(12, 200.dp) + Spacer(modifier = Modifier.height(16.dp)) + Row( + modifier = Modifier, + horizontalArrangement = Arrangement.spacedBy(32.dp), + ) { + for (i in 0 until 3) { + Canvas( + modifier = Modifier + .size(10.dp) + .padding(2.dp) + ) { + drawCircle(color = GrayStroke) + } + } + } + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = bottomHeader, + style = heading1, + color = GrayStroke, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Start) + ) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(100, 50.dp) + Spacer(modifier = Modifier.height(24.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(20.dp) + ) { + for (i in 0 until 5) { + LoadingFilter() + } + } + } + Divider(color = GrayStroke) + Column( + modifier = Modifier.padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(24.dp)) + LoadingStateBox(12, 100.dp) + Spacer(modifier = Modifier.height(16.dp)) + LoadingStateBox(12, 100.dp) + } + } +} + +@Composable +private fun LoadingFilter() { + val shimmerColor = interpolateColorHSV(Wash, Stroke, LocalInfiniteLoading.current) + + Column( + modifier = Modifier + .width(54.dp) + .height(56.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceEvenly + ) { + Canvas( + modifier = Modifier + .size(24.dp) + .padding(2.dp) + ) { + drawCircle(color = shimmerColor) + } + Spacer(modifier = Modifier.height(6.dp)) + LoadingStateBox(100, 12.dp) + } +} + +@Preview +@Composable +private fun LoadingFilterPreview() = ScorePreview { + LoadingFilter() +} + +@Preview +@Composable +private fun LoadingScreenPreview() = ScorePreview { + LoadingScreen("Loading Upcoming...", "Loading Schedules...") +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/LoadingStateBox.kt b/app/src/main/java/com/cornellappdev/score/components/LoadingStateBox.kt new file mode 100644 index 0000000..86123a8 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/LoadingStateBox.kt @@ -0,0 +1,44 @@ +package com.cornellappdev.score.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.theme.LocalInfiniteLoading +import com.cornellappdev.score.theme.Stroke +import com.cornellappdev.score.theme.Wash +import com.cornellappdev.score.util.interpolateColorHSV + +@Composable +fun LoadingStateBox( + cornerRoundness: Int, + height: Dp, + modifier: Modifier = Modifier.fillMaxWidth() +) { + Box( + modifier = modifier + .height(height) + .background( + color = interpolateColorHSV( + Wash, + Stroke, + LocalInfiniteLoading.current + ), + shape = RoundedCornerShape(cornerRoundness) + ) + .fillMaxWidth() + ) + +} + +@Preview +@Composable +private fun LoadingStateBoxPreview() = ScorePreview { + LoadingStateBox(12, 16.dp) +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/NavigationHeader.kt b/app/src/main/java/com/cornellappdev/score/components/NavigationHeader.kt index 96bc2f6..1022b17 100644 --- a/app/src/main/java/com/cornellappdev/score/components/NavigationHeader.kt +++ b/app/src/main/java/com/cornellappdev/score/components/NavigationHeader.kt @@ -1,19 +1,11 @@ package com.cornellappdev.score.components -import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text @@ -22,47 +14,47 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.cornellappdev.score.R -import com.cornellappdev.score.theme.AmbientColor -import com.cornellappdev.score.theme.CrimsonPrimary -import com.cornellappdev.score.theme.GrayLight -import com.cornellappdev.score.theme.SpotColor -import com.cornellappdev.score.theme.Style.bodyMedium import com.cornellappdev.score.theme.Style.heading2 -import com.cornellappdev.score.theme.White @Composable fun NavigationHeader(title: String, onBackPressed: () -> Unit) { - Box(modifier = Modifier - .shadow(elevation=8.dp, clip = false, spotColor = Color.Black.copy(0.05f)) - .background(Color.White)) { - Box(modifier = Modifier - .padding(start=24.dp, top=56.dp, bottom=12.dp, end=24.dp) - .background(Color.White) - .fillMaxWidth() - .height(27.dp)) { - IconButton(onClick = onBackPressed) { - Icon( - painter = painterResource(id = R.drawable.ic_left_arrowhead), - contentDescription = "Back button", - modifier = Modifier - .width(24.dp) - .height(24.dp), - ) - } - Text(text = title, modifier = Modifier.align(Alignment.Center), style = heading2, color = Color.Black) - } - } + Box( + modifier = Modifier + .shadow(elevation = 8.dp, clip = false, spotColor = Color.Black.copy(0.05f)) + .background(Color.White) + ) { + Box( + modifier = Modifier + .padding(start = 24.dp, top = 56.dp, bottom = 12.dp, end = 24.dp) + .background(Color.White) + .fillMaxWidth() + .height(27.dp) + ) { + IconButton(onClick = onBackPressed) { + Icon( + painter = painterResource(id = R.drawable.ic_left_arrowhead), + contentDescription = "Back button", + modifier = Modifier + .width(24.dp) + .height(24.dp), + ) + } + Text( + text = title, + modifier = Modifier.align(Alignment.Center), + style = heading2, + color = Color.Black + ) + } + } } @Preview @Composable -private fun NavigationHeaderPreview() { - NavigationHeader("Game Details", {}) +private fun NavigationHeaderPreview() = ScorePreview { + NavigationHeader("Game Details", {}) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/PastGameCard.kt b/app/src/main/java/com/cornellappdev/score/components/PastGameCard.kt index a3c5c66..fcc8506 100644 --- a/app/src/main/java/com/cornellappdev/score/components/PastGameCard.kt +++ b/app/src/main/java/com/cornellappdev/score/components/PastGameCard.kt @@ -3,7 +3,16 @@ package com.cornellappdev.score.components import androidx.compose.foundation.Image import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults @@ -188,9 +197,9 @@ private fun TeamScore( } } -@Preview(showBackground = true) +@Preview @Composable -private fun PastGameCardPreview() { +private fun PastGameCardPreview() = ScorePreview { val gameCard = GameCardData( teamLogo = "https://cornellbigred.com/images/logos/penn_200x200.png?width=80&height=80&mode=max", team = "University of Pennsylvania", diff --git a/app/src/main/java/com/cornellappdev/score/components/ScoreBox.kt b/app/src/main/java/com/cornellappdev/score/components/ScoreBox.kt index 81c073c..3cab34a 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreBox.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreBox.kt @@ -1,9 +1,14 @@ package com.cornellappdev.score.components import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* +import androidx.compose.material3.Divider +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -17,10 +22,8 @@ import com.cornellappdev.score.model.TeamScore import com.cornellappdev.score.theme.CrimsonPrimary import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyNormal -import com.cornellappdev.score.theme.Style.heading6 import com.cornellappdev.score.theme.Style.metricNormal import com.cornellappdev.score.theme.Style.metricSemibold -import com.cornellappdev.score.theme.Style.scoreText import com.cornellappdev.score.theme.saturatedGreen import com.cornellappdev.score.util.emptyGameData import com.cornellappdev.score.util.gameData @@ -82,6 +85,7 @@ fun BoxScore(gameData: GameData) { ) } } + @Composable fun TeamScoreRow(teamScore: TeamScore, totalTextColor: Color) { val showEmpty = teamScore.scoresByPeriod.isEmpty() @@ -132,14 +136,14 @@ fun TeamScoreRow(teamScore: TeamScore, totalTextColor: Color) { } } -@Preview(showBackground = true) +@Preview @Composable -private fun PreviewBoxScore() { +private fun PreviewBoxScore() = ScorePreview { BoxScore(gameData = gameData) } -@Preview(showBackground = true) +@Preview @Composable -private fun PreviewBoxScoreEmpty() { +private fun PreviewBoxScoreEmpty() = ScorePreview { BoxScore(gameData = emptyGameData()) } diff --git a/app/src/main/java/com/cornellappdev/score/components/ScorePreview.kt b/app/src/main/java/com/cornellappdev/score/components/ScorePreview.kt new file mode 100644 index 0000000..51d350f --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/ScorePreview.kt @@ -0,0 +1,52 @@ +package com.cornellappdev.score.components + +import androidx.compose.animation.core.InfiniteRepeatableSpec +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.keyframes +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.theme.LocalInfiniteLoading +import com.cornellappdev.score.theme.White + +@Composable +fun ScorePreview( + padding: Dp = 0.dp, + backgroundColor: Color = White, + content: @Composable () -> Unit +) { + val transition = rememberInfiniteTransition() + + val animatedValue = transition.animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = InfiniteRepeatableSpec( + animation = keyframes { + durationMillis = 2000 + 0f at 0 + 1f at 1000 + 0f at 2000 + } + ), + label = "infinite loading" + ).value + + CompositionLocalProvider( + LocalInfiniteLoading provides animatedValue + ) { + Column( + modifier = Modifier + .background(color = backgroundColor) + .padding(padding) + ) { + content() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt index 73d1f7e..6c4218e 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt @@ -13,15 +13,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.model.ScoreEvent import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyMedium import com.cornellappdev.score.theme.Style.bodyNormal -import com.cornellappdev.score.theme.Style.heading5 import com.cornellappdev.score.theme.Style.metricNormal import com.cornellappdev.score.theme.Style.metricSemibold import com.cornellappdev.score.util.scoreEvents1 @@ -112,8 +111,8 @@ fun ScoreEventItem(event: ScoreEvent) { } } -@Preview(showBackground = true) +@Preview @Composable -private fun PreviewScoringSummary() { +private fun PreviewScoringSummary() = ScorePreview { ScoringSummary(scoreEvents = scoreEvents1) } diff --git a/app/src/main/java/com/cornellappdev/score/components/SportSelectorHeader.kt b/app/src/main/java/com/cornellappdev/score/components/SportSelectorHeader.kt index c0b9afb..313ccd9 100644 --- a/app/src/main/java/com/cornellappdev/score/components/SportSelectorHeader.kt +++ b/app/src/main/java/com/cornellappdev/score/components/SportSelectorHeader.kt @@ -175,7 +175,7 @@ fun SportSelector( @Preview @Composable -private fun PreviewSportSelectorHeader() { +private fun PreviewSportSelectorHeader() = ScorePreview { var selectedOption by remember { mutableStateOf(GenderDivision.ALL) } var selectedSport: SportSelection by remember { mutableStateOf(SportSelection.All) } diff --git a/app/src/main/java/com/cornellappdev/score/components/TimeUntilStartCard.kt b/app/src/main/java/com/cornellappdev/score/components/TimeUntilStartCard.kt index b193e35..7aef44d 100644 --- a/app/src/main/java/com/cornellappdev/score/components/TimeUntilStartCard.kt +++ b/app/src/main/java/com/cornellappdev/score/components/TimeUntilStartCard.kt @@ -70,6 +70,6 @@ fun TimeUntilStartCard(days: Int, hours: Int) { @Preview @Composable -private fun TimeUntilStartCardPreview() { +private fun TimeUntilStartCardPreview() = ScorePreview { TimeUntilStartCard(2, 0) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/nav/root/RootNavigation.kt b/app/src/main/java/com/cornellappdev/score/nav/root/RootNavigation.kt index 46181d0..696eec8 100644 --- a/app/src/main/java/com/cornellappdev/score/nav/root/RootNavigation.kt +++ b/app/src/main/java/com/cornellappdev/score/nav/root/RootNavigation.kt @@ -1,5 +1,9 @@ package com.cornellappdev.score.nav.root +import androidx.compose.animation.core.InfiniteRepeatableSpec +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.keyframes +import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -9,6 +13,7 @@ import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -27,10 +32,10 @@ import com.cornellappdev.score.screen.HomeScreen import com.cornellappdev.score.screen.PastGamesScreen import com.cornellappdev.score.theme.CrimsonPrimary import com.cornellappdev.score.theme.GrayPrimary +import com.cornellappdev.score.theme.LocalInfiniteLoading import com.cornellappdev.score.theme.Style.bodyMedium import com.cornellappdev.score.theme.White import kotlinx.serialization.Serializable -import java.time.LocalDate @Composable fun RootNavigation( @@ -40,6 +45,22 @@ fun RootNavigation( val uiState = rootNavigationViewModel.collectUiStateValue() val navBackStackEntry = navController.currentBackStackEntryAsState().value + val transition = rememberInfiniteTransition() + // Animate a value from 0 to 1 infinitely + val animatedValue = transition.animateFloat( + initialValue = 0f, + targetValue = 1f, + animationSpec = InfiniteRepeatableSpec( + animation = keyframes { + durationMillis = 2000 + 0f at 0 + 1f at 1000 + 0f at 2000 + } + ), + label = "infinite loading" + ).value + LaunchedEffect(uiState.navigationEvent) { uiState.navigationEvent?.consumeSuspend { screen -> navController.navigate(screen) @@ -78,27 +99,29 @@ fun RootNavigation( } ) { innerPadding -> Box(modifier = Modifier.padding(innerPadding)) { - NavHost( - navController = navController, - startDestination = ScoreRootScreens.Home - ) { - composable { - HomeScreen(navigateToGameDetails = { - navController.navigate(ScoreRootScreens.GameDetailsPage("")) - }) - } + CompositionLocalProvider(LocalInfiniteLoading provides animatedValue) { + NavHost( + navController = navController, + startDestination = ScoreRootScreens.Home + ) { + composable { + HomeScreen(navigateToGameDetails = { + navController.navigate(ScoreRootScreens.GameDetailsPage("")) + }) + } - composable { - GameDetailsScreen("", onBackArrow = { - navController.navigateUp() - }) + composable { + GameDetailsScreen("", onBackArrow = { + navController.navigateUp() + }) - } + } - composable { - PastGamesScreen(navigateToGameDetails = { - navController.navigate(ScoreRootScreens.GameDetailsPage("")) - }) + composable { + PastGamesScreen(navigateToGameDetails = { + navController.navigate(ScoreRootScreens.GameDetailsPage("")) + }) + } } } } diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt index e27d4e9..4444fdc 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -2,7 +2,6 @@ package com.cornellappdev.score.screen import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -23,6 +22,7 @@ import com.cornellappdev.score.R import com.cornellappdev.score.components.ButtonPrimary import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.NavigationHeader +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.components.TimeUntilStartCard import com.cornellappdev.score.theme.GrayMedium import com.cornellappdev.score.theme.GrayPrimary @@ -33,7 +33,12 @@ import com.cornellappdev.score.theme.White @Composable fun GameDetailsScreen(gameId: String = "", onBackArrow: () -> Unit = {}) { - Column(modifier = Modifier.background(White).fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { + Column( + modifier = Modifier + .background(White) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { // TODO: add navigation NavigationHeader(title = "Game Details", onBackArrow) GameScoreHeader( @@ -94,7 +99,7 @@ fun GameDetailsScreen(gameId: String = "", onBackArrow: () -> Unit = {}) { @Preview @Composable -private fun GameDetailsScreenPreview() { +private fun GameDetailsScreenPreview() = ScorePreview { GameDetailsScreen() // import androidx.compose.ui.tooling.preview.Preview // import androidx.compose.ui.unit.dp diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameScoreSummaryScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameScoreSummaryScreen.kt index a34d466..09bad26 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameScoreSummaryScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameScoreSummaryScreen.kt @@ -3,6 +3,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -22,19 +23,22 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.cornellappdev.score.components.NavigationHeader +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.model.ScoreEvent import com.cornellappdev.score.theme.Style.bodyNormal import com.cornellappdev.score.theme.Style.spanBodyNormal +import com.cornellappdev.score.theme.White import com.cornellappdev.score.util.scoreEvents2 -import androidx.compose.foundation.layout.fillMaxSize @Composable fun GameScoreSummaryScreenDetail(scoreEvents: List) { - Column(modifier = Modifier.fillMaxSize()){ + Column(modifier = Modifier.fillMaxSize()) { // TODO: add navigation NavigationHeader(title = "Scoring Summary", {}) LazyColumn( - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp, vertical = 8.dp) + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 8.dp) ) { items(scoreEvents.size) { event -> ScoreEventItemDetailed(event = scoreEvents[event]) @@ -74,24 +78,24 @@ fun ScoreEventItemDetailed(event: ScoreEvent) { Text( textAlign = TextAlign.Center, text = - buildAnnotatedString { - withStyle( - style = spanBodyNormal - ) { - append("${event.time} - ${event.quarter.replace(" Quarter", "")}\n") - } - withStyle( - style = homeScoreStyle - ) { - append(event.homeScore) - } - withStyle(style = spanBodyNormal) { - append(" - ") - } - withStyle(style = awayScoreStyle) { - append(event.awayScore) + buildAnnotatedString { + withStyle( + style = spanBodyNormal + ) { + append("${event.time} - ${event.quarter.replace(" Quarter", "")}\n") + } + withStyle( + style = homeScoreStyle + ) { + append(event.homeScore) + } + withStyle(style = spanBodyNormal) { + append(" - ") + } + withStyle(style = awayScoreStyle) { + append(event.awayScore) + } } - } ) } Spacer(modifier = Modifier.width(16.dp)) @@ -105,8 +109,8 @@ fun ScoreEventItemDetailed(event: ScoreEvent) { } -@Preview(showBackground = true) +@Preview @Composable -private fun PreviewScoringDetailsScreen() { +private fun PreviewScoringDetailsScreen() = ScorePreview { GameScoreSummaryScreenDetail(scoreEvents = scoreEvents2) } diff --git a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt index 86081be..599e798 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt @@ -3,7 +3,6 @@ package com.cornellappdev.score.screen import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer @@ -12,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -22,8 +20,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.cornellappdev.score.components.ErrorState import com.cornellappdev.score.components.GameCard import com.cornellappdev.score.components.GamesCarousel +import com.cornellappdev.score.components.LoadingScreen +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.components.SportSelectorHeader import com.cornellappdev.score.model.ApiResponse import com.cornellappdev.score.model.GamesCarouselVariant @@ -50,25 +51,11 @@ fun HomeScreen( ) { when (uiState.loadedState) { is ApiResponse.Loading -> { - //TODO: Add loading screen - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator() - } + LoadingScreen("Loading Upcoming...", "Loading Schedules...") } is ApiResponse.Error -> { - //TODO: Add Error screen - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text( - text = "Failed to load games. Please try again.", - ) - } + ErrorState({ homeViewModel.onRefresh() }, "Oops! Schedules failed to load.") } is ApiResponse.Success -> { @@ -137,7 +124,7 @@ private fun HomeContent( @Preview @Composable -private fun HomeScreenPreview() { +private fun HomeScreenPreview() = ScorePreview { Column( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt index ded0052..8d36258 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt @@ -3,16 +3,13 @@ package com.cornellappdev.score.screen import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -21,8 +18,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.cornellappdev.score.components.ErrorState import com.cornellappdev.score.components.GamesCarousel +import com.cornellappdev.score.components.LoadingScreen import com.cornellappdev.score.components.PastGameCard +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.components.SportSelectorHeader import com.cornellappdev.score.model.ApiResponse import com.cornellappdev.score.model.GamesCarouselVariant @@ -49,25 +49,11 @@ fun PastGamesScreen( ) { when (uiState.loadedState) { is ApiResponse.Loading -> { - //TODO: Add loading screen - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator() - } + LoadingScreen("Loading Latest", "Loading Scores") } is ApiResponse.Error -> { - //TODO: Add Error screen - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text( - text = "Failed to load games. Please try again.", - ) - } + ErrorState({ pastGamesViewModel.onRefresh() }, "Oops! Scores failed to load.") } is ApiResponse.Success -> { @@ -129,7 +115,7 @@ private fun PastGamesContent( @Composable @Preview -private fun PastGamesPreview() { +private fun PastGamesPreview() = ScorePreview { PastGamesContent( uiState = PastGamesUiState( selectedGender = GenderDivision.ALL, diff --git a/app/src/main/java/com/cornellappdev/score/theme/Color.kt b/app/src/main/java/com/cornellappdev/score/theme/Color.kt index 2a45d4d..a4f8fa5 100644 --- a/app/src/main/java/com/cornellappdev/score/theme/Color.kt +++ b/app/src/main/java/com/cornellappdev/score/theme/Color.kt @@ -22,4 +22,7 @@ val AmbientColor = Color(0x12000000) //placeholders, will be replaced once we get backend data val CornellRed = Color(0x66B31B1B) val PennBlue = Color(0x66021E59) -val PrincetonOrange = Color(0x66FF6000) \ No newline at end of file +val PrincetonOrange = Color(0x66FF6000) + +val Wash = Color(0xFFF4F4F4) +val Stroke = Color(0xFFD6D6D6) \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/theme/CompositionLocals.kt b/app/src/main/java/com/cornellappdev/score/theme/CompositionLocals.kt new file mode 100644 index 0000000..346f07d --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/theme/CompositionLocals.kt @@ -0,0 +1,5 @@ +package com.cornellappdev.score.theme + +import androidx.compose.runtime.compositionLocalOf + +val LocalInfiniteLoading = compositionLocalOf { error("No infinite loading provided") } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/util/ColorUtil.kt b/app/src/main/java/com/cornellappdev/score/util/ColorUtil.kt index 89e66bb..888a000 100644 --- a/app/src/main/java/com/cornellappdev/score/util/ColorUtil.kt +++ b/app/src/main/java/com/cornellappdev/score/util/ColorUtil.kt @@ -8,4 +8,49 @@ import androidx.compose.ui.graphics.Color fun parseColor(color: String): Color { val colorInt = Integer.parseInt(color.removePrefix("#"), 16) return Color(colorInt) +} + +/** + * Interpolates between two colors based on a given fraction. + */ +fun interpolateColorHSV(startColor: Color, endColor: Color, fraction: Float): Color { + // Clamp the fraction to be between 0 and 1 + val clampedFraction = fraction.coerceIn(0f, 1f) + + // Convert start and end colors to HSV + val startHSV = FloatArray(3) + val endHSV = FloatArray(3) + android.graphics.Color.colorToHSV( + android.graphics.Color.argb( + (startColor.alpha * 255).toInt(), + (startColor.red * 255).toInt(), + (startColor.green * 255).toInt(), + (startColor.blue * 255).toInt() + ), + startHSV + ) + android.graphics.Color.colorToHSV( + android.graphics.Color.argb( + (endColor.alpha * 255).toInt(), + (endColor.red * 255).toInt(), + (endColor.green * 255).toInt(), + (endColor.blue * 255).toInt() + ), + endHSV + ) + + // Interpolate HSV values + val hue = startHSV[0] + (endHSV[0] - startHSV[0]) * clampedFraction + val saturation = startHSV[1] + (endHSV[1] - startHSV[1]) * clampedFraction + val value = startHSV[2] + (endHSV[2] - startHSV[2]) * clampedFraction + + // Convert back to RGB + val rgb = android.graphics.Color.HSVToColor(floatArrayOf(hue, saturation, value)) + + return Color( + red = ((rgb shr 16) and 0xFF) / 255f, + green = ((rgb shr 8) and 0xFF) / 255f, + blue = (rgb and 0xFF) / 255f, + alpha = ((rgb shr 24) and 0xFF) / 255f + ) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt index 2121b70..f4f6778 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt @@ -60,6 +60,14 @@ class HomeViewModel @Inject constructor( } } + fun onRefresh() { + applyMutation { + copy(loadedState = ApiResponse.Loading) + } + + scoreRepository.fetchGames() + } + fun onGenderSelected(gender: GenderDivision) { applyMutation { copy( diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt index 4df8393..43a2888 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt @@ -59,6 +59,14 @@ class PastGamesViewModel @Inject constructor( } } + fun onRefresh() { + applyMutation { + copy(loadedState = ApiResponse.Loading) + } + + scoreRepository.fetchGames() + } + fun onGenderSelected(gender: GenderDivision) { applyMutation { copy( diff --git a/app/src/main/res/drawable/ic_cached.xml b/app/src/main/res/drawable/ic_cached.xml new file mode 100644 index 0000000..11b40a3 --- /dev/null +++ b/app/src/main/res/drawable/ic_cached.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_feedback.xml b/app/src/main/res/drawable/ic_feedback.xml new file mode 100644 index 0000000..6379b7f --- /dev/null +++ b/app/src/main/res/drawable/ic_feedback.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_right_chevron.xml b/app/src/main/res/drawable/ic_right_chevron.xml new file mode 100644 index 0000000..395c7bc --- /dev/null +++ b/app/src/main/res/drawable/ic_right_chevron.xml @@ -0,0 +1,13 @@ + + + + + +