diff --git a/app/src/main/graphql/GameById.graphql b/app/src/main/graphql/GameById.graphql index aced4b7..64c414b 100644 --- a/app/src/main/graphql/GameById.graphql +++ b/app/src/main/graphql/GameById.graphql @@ -28,5 +28,10 @@ query GameById($id: String!) { corScore oppScore } + + recapArticleTitle + recapLink + recapPublishedAt + recapArticleImage } } \ No newline at end of file diff --git a/app/src/main/graphql/Games.graphql b/app/src/main/graphql/Games.graphql index 66fe3c6..712c472 100644 --- a/app/src/main/graphql/Games.graphql +++ b/app/src/main/graphql/Games.graphql @@ -11,10 +11,6 @@ query Games{ color } gender - boxScore { - corScore - oppScore - } result } } \ No newline at end of file diff --git a/app/src/main/graphql/schema.graphqls b/app/src/main/graphql/schema.graphqls index e6c5c91..0583fea 100644 --- a/app/src/main/graphql/schema.graphqls +++ b/app/src/main/graphql/schema.graphqls @@ -136,6 +136,14 @@ type GameType { utcDate: String ticketLink: String + + recapLink: String + + recapArticleTitle: String + + recapArticleImage: String + + recapPublishedAt: String } """ 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 bacee06..3a9b69f 100644 --- a/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt +++ b/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt @@ -1,25 +1,19 @@ package com.cornellappdev.score.components -import android.app.GameState -import android.graphics.Paint.Align 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.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.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter @@ -31,7 +25,9 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import com.cornellappdev.score.R import com.cornellappdev.score.theme.Style.scoreHeaderText -import com.cornellappdev.score.theme.Style.vsText +import com.cornellappdev.score.theme.rankHeaderNeutral +import com.cornellappdev.score.theme.rankHeaderRed +import com.cornellappdev.score.util.toPlacement @Composable fun GameScoreHeader( @@ -95,6 +91,90 @@ fun GameScoreHeader( } } +@Composable +fun NthPlaceScoreHeader( + rank: Int, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxSize() + .background( + brush = Brush.linearGradient( + colors = listOf(rankHeaderRed, rankHeaderNeutral) + ) + ) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxSize() + ) { + + Image( + painter = painterResource(R.drawable.cornell_logo), + contentDescription = "Left Team Logo", + contentScale = ContentScale.FillBounds, + modifier = Modifier.size(70.dp) + ) + Spacer(modifier = Modifier.width(11.dp)) + + //nth place + Text( + text = "${rank.toPlacement()} Place", + style = scoreHeaderText, + ) + } + } +} + +@Composable +fun AlternativeScoreHeader( + resultString: String, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxSize() + .background( + brush = Brush.linearGradient( + colors = listOf(rankHeaderRed, rankHeaderNeutral) + ) + ) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxSize() + ) { + + Image( + painter = painterResource(R.drawable.cornell_logo), + contentDescription = "Left Team Logo", + contentScale = ContentScale.FillBounds, + modifier = Modifier.size(70.dp) + ) + Spacer(modifier = Modifier.width(11.dp)) + + //nth place + Text( + text = resultString.split("(")[0], + style = scoreHeaderText, + ) + } + } +} + +@Composable +fun WinLoseHeaderContent( + teamOneLogo: Painter, + teamTwoLogo: Painter, +) { + //parse result (remove score in parentheses) +} + @Preview @Composable private fun GameScoreHeaderPreview() = ScorePreview { @@ -107,4 +187,17 @@ private fun GameScoreHeaderPreview() = ScorePreview { rightScore = 0, modifier = Modifier.height(185.dp) ) +} + +@Preview +@Composable +private fun NthPlaceScoreHeaderPreview() = ScorePreview { + NthPlaceScoreHeader(6, Modifier.height(185.dp)) +} + +@Preview +@Composable +private fun alternativeScoreHeaderPreview() = ScorePreview { + AlternativeScoreHeader("11th of 18 (881)", Modifier.height(185.dp)) + AlternativeScoreHeader("High Point", Modifier.height(185.dp)) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/components/Leaderboard.kt b/app/src/main/java/com/cornellappdev/score/components/Leaderboard.kt new file mode 100644 index 0000000..e638f2c --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/Leaderboard.kt @@ -0,0 +1,138 @@ +package com.cornellappdev.score.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +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.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +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 androidx.compose.ui.unit.dp +import com.cornellappdev.score.R +import com.cornellappdev.score.theme.CrimsonPrimary +import com.cornellappdev.score.theme.Style.bodyNormal +import com.cornellappdev.score.theme.Style.bodySemibold +import com.cornellappdev.score.theme.White +import com.cornellappdev.score.util.sampleIvyLeaderboardData + +data class LeaderboardData( + val rank: Int, + val icon: Int, + val school: String, + val points: Double +) + +@Composable +fun Leaderboard( + leaderboardData: List +) { + Column( + modifier = Modifier.fillMaxWidth() + .padding(vertical = 8.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(topEnd = 8.dp, topStart = 8.dp)) + .background(color = CrimsonPrimary) + .height(33.dp) + ) { + Text("Leaderboard", color = White, style = bodyNormal) + } + for (team in leaderboardData) { + LeaderboardRow( + rank = team.rank, + icon = painterResource(team.icon), + school = team.school, + points = team.points + ) + } + } +} + +@Composable +fun LeaderboardRow( + rank: Int, + icon: Painter, + school: String, + points: Double +) { + val pointsFormatted = String.format("%.1f", points) + val isCornell = (school == "Cornell") + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .background(color = Color.White) + .border(width = Dp.Hairline, color = CrimsonPrimary) + .padding(vertical = 8.dp) + ) { + Text( + text = "$rank", style = if (isCornell) {bodySemibold} else {bodyNormal}, modifier = Modifier + .padding(10.dp) + .width(34.dp) + ) + Spacer(modifier = Modifier.width(11.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.width(120.dp) + ) { + Image( + painter = icon, + contentDescription = "School icon", + modifier = Modifier.size(30.dp) + ) + Text(text = school, style = if (isCornell) {bodySemibold} else {bodyNormal}, modifier = Modifier.padding(10.dp)) + } + Spacer(modifier = Modifier.width(11.dp)) + Text( + text = "$pointsFormatted pts", + style = if (isCornell) {bodySemibold} else {bodyNormal}, + modifier = Modifier + .padding(10.dp) + .width(88.dp) + + ) + } +} + +@Preview +@Composable +private fun LeaderboardRowPreview() { + LeaderboardRow( + rank = 1, + icon = painterResource(R.drawable.cornell_logo), + school = "Cornell", + points = 614.5 + ) +} + +@Preview +@Composable +private fun LeaderboardPreview() { + Box( + modifier = Modifier.background(White).padding(horizontal = 24.dp).fillMaxWidth() + ){ + Leaderboard(sampleIvyLeaderboardData) + } +} 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 68b339c..5384e26 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt @@ -1,11 +1,18 @@ +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.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.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -15,22 +22,55 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.cornellappdev.score.R -import com.cornellappdev.score.components.ScorePreview +import com.cornellappdev.score.model.ArticleHighlightData +import com.cornellappdev.score.model.DetailsCardData import com.cornellappdev.score.model.ScoreEvent +import com.cornellappdev.score.model.Sport +import com.cornellappdev.score.theme.GrayMedium 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.heading2 import com.cornellappdev.score.theme.Style.metricNormal import com.cornellappdev.score.theme.Style.metricSemibold +import com.cornellappdev.score.util.sampleDetailsCardData import com.cornellappdev.score.util.scoreEvents1 @Composable -fun ScoringSummary(scoreEvents: List, modifier: Modifier = Modifier) { - Column(modifier = modifier) { - scoreEvents.take(3).map { +fun ScoringSummary( + scoreEvents: List, + gameCard: DetailsCardData, + modifier: Modifier = Modifier, + navigateToGameScoreSummary: (List) -> Unit +) { + Column(modifier = modifier.fillMaxWidth()) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Text( + "Scoring Summary", fontSize = 18.sp, + style = heading2, + ) + if (gameCard.boxScore.isNotEmpty()) { + Spacer(modifier = Modifier.weight(1f)) + IconButton(onClick = { navigateToGameScoreSummary(gameCard.scoreEvent) }) { + Icon( + painter = painterResource(id = R.drawable.ic_right_chevron), + contentDescription = "Back button", + modifier = Modifier + .width(24.dp) + .height(24.dp), + tint = GrayMedium + ) + } + } + } + scoreEvents.take(3).forEach { ScoreEventItem(it) HorizontalDivider(color = Color.LightGray, thickness = 0.5.dp) } @@ -38,14 +78,17 @@ fun ScoringSummary(scoreEvents: List, modifier: Modifier = Modifier) } @Composable -fun ScoreEventItem(event: ScoreEvent) { +fun ScoreEventItem( + event: ScoreEvent, + modifier: Modifier = Modifier +) { Row( - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(vertical = 16.dp), verticalAlignment = Alignment.CenterVertically ) { - if (event.team.name == "COR") { // TODO: Check if its "COR" for all queries. It is for baseball + if (event.team.name == "COR" || event.team.name == "Cornell") { // TODO: Check if its "COR" for all queries. It is for baseball Image( painter = painterResource(R.drawable.cornell_logo), contentDescription = event.team.name, @@ -63,7 +106,6 @@ fun ScoreEventItem(event: ScoreEvent) { ) } - Row( modifier = Modifier.weight(2f), horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -84,7 +126,6 @@ fun ScoreEventItem(event: ScoreEvent) { ) } - Row( horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically @@ -95,7 +136,7 @@ fun ScoreEventItem(event: ScoreEvent) { color = GrayPrimary, textAlign = TextAlign.Center ) - val (homeScore, awayScore) = event.score.split(" - ").map { it.toInt() } + val (homeScore, awayScore) = event.score.split("-").map { it.trim().toInt() } Row(verticalAlignment = Alignment.CenterVertically) { Text( @@ -124,5 +165,17 @@ fun ScoreEventItem(event: ScoreEvent) { @Preview @Composable private fun PreviewScoringSummary() = ScorePreview { - ScoringSummary(scoreEvents = scoreEvents1) + ScoringSummary( + scoreEvents = scoreEvents1, + gameCard = sampleDetailsCardData.copy( + articleData = ArticleHighlightData( + "Late Goal Lifts No. 6 Men’s Hockey Over Brown", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/09", + Sport.ICE_HOCKEY + ) + ), + navigateToGameScoreSummary = {} + ) } diff --git a/app/src/main/java/com/cornellappdev/score/model/Game.kt b/app/src/main/java/com/cornellappdev/score/model/Game.kt index 6074d76..ee3cb19 100644 --- a/app/src/main/java/com/cornellappdev/score/model/Game.kt +++ b/app/src/main/java/com/cornellappdev/score/model/Game.kt @@ -127,7 +127,9 @@ data class DetailsCardData( val daysUntilGame: Int?, val hoursUntilGame: Int?, val homeScore: Int, - val oppScore: Int + val oppScore: Int, + val articleData: ArticleHighlightData? = null, + val result: String? = null ) // Scoring information by round of a game, used in the box score 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 001f500..0dc6f24 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -1,7 +1,7 @@ package com.cornellappdev.score.screen -import ScoringSummary import androidx.compose.foundation.Image +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column @@ -16,36 +16,36 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton 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.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import com.apollographql.apollo.api.BooleanExpression import com.cornellappdev.score.R +import com.cornellappdev.score.components.AlternativeScoreHeader import com.cornellappdev.score.components.BoxScore import com.cornellappdev.score.components.EmptyStateBox import com.cornellappdev.score.components.ErrorState import com.cornellappdev.score.components.GameDetailsLoadingScreen import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.NavigationHeader +import com.cornellappdev.score.components.NthPlaceScoreHeader +import com.cornellappdev.score.components.ScorePreview import com.cornellappdev.score.components.ScorePullToRefreshBox +import com.cornellappdev.score.components.ScoringSummary import com.cornellappdev.score.components.TimeUntilStartCard +import com.cornellappdev.score.components.highlights.ArticleHighlightCard import com.cornellappdev.score.model.ApiResponse +import com.cornellappdev.score.model.ArticleHighlightData import com.cornellappdev.score.model.DetailsCardData -import com.cornellappdev.score.model.GameData -import com.cornellappdev.score.model.GameDetailsBoxScore import com.cornellappdev.score.model.ScoreEvent -import com.cornellappdev.score.model.TeamBoxScore -import com.cornellappdev.score.model.TeamGameSummary -import com.cornellappdev.score.model.TeamScore +import com.cornellappdev.score.model.Sport import com.cornellappdev.score.theme.GrayMedium import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyNormal @@ -53,11 +53,12 @@ import com.cornellappdev.score.theme.Style.heading1 import com.cornellappdev.score.theme.Style.heading2 import com.cornellappdev.score.theme.Style.heading3 import com.cornellappdev.score.theme.White +import com.cornellappdev.score.util.sampleDetailsCardData import com.cornellappdev.score.viewmodel.GameDetailsViewModel -import java.time.LocalDate @Composable fun GameDetailsScreen( + modifier: Modifier = Modifier, gameDetailsViewModel: GameDetailsViewModel = hiltViewModel(), onBackArrow: () -> Unit = {}, navigateToGameScoreSummary: (List) -> Unit @@ -67,7 +68,8 @@ fun GameDetailsScreen( // We have a separate loading state for this screen so we don't want the refresh indicator // to persist as the screen loads. false, - gameDetailsViewModel::onRefresh + gameDetailsViewModel::onRefresh, + modifier = modifier ) { Column( modifier = Modifier @@ -105,107 +107,83 @@ fun GameDetailsScreen( @Composable fun GameDetailsContent( gameCard: DetailsCardData, + modifier: Modifier = Modifier, navigateToGameScoreSummary: (List) -> Unit ) { + // TODO: add navigation + val scrollState = rememberScrollState() Column( - modifier = Modifier + modifier = modifier .background(White) .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { - // TODO: add navigation - GameScoreHeader( - leftTeamLogo = painterResource(R.drawable.cornell_logo), - rightTeamLogo = gameCard.opponentLogo, - gradientColor1 = Color(0xFFE1A69F), - gradientColor2 = gameCard.opponentColor, - leftScore = gameCard.homeScore, - rightScore = gameCard.oppScore, - modifier = Modifier.height(185.dp) - ) + + if (gameCard.result != null) { + if (gameCard.result.contains("place")) { + val place = gameCard.result.split(",")[0] + AlternativeScoreHeader(place) + } else if (gameCard.result.contains("of")) { + //todo take the first three words of result + } else if (gameCard.title.contains("Tournament")) { + //todo: how do i extract this? + AlternativeScoreHeader("Temp") + } else if (gameCard.result.contains("-")) { + //todo jk we can prob just feed the string into the standard score header: GameScoreHeader + AlternativeScoreHeader("Temp") +// val temp = gameCard.result.split(",")[1].trim(), +// val rawScores = temp.split("-") +// val leftScore = rawScores[0] +// val rightScore = rawScores[1] + } + }else { + //standard score header + GameScoreHeader( + leftTeamLogo = painterResource(R.drawable.cornell_logo), + rightTeamLogo = gameCard.opponentLogo, + gradientColor1 = Color(0xFFE1A69F), + gradientColor2 = gameCard.opponentColor, + leftScore = gameCard.homeScore, + rightScore = gameCard.oppScore, + modifier = Modifier.height(185.dp) + ) + } Spacer(modifier = Modifier.height(24.dp)) Column(Modifier.padding(horizontal = 24.dp)) { - Text( - text = gameCard.sport, - style = heading3.copy(color = GrayPrimary) + GameDetailsInformation( + gameCard, + scrollState ) - Text( - text = gameCard.title, - style = heading1.copy(color = GrayPrimary), - maxLines = 1, - modifier = Modifier - .horizontalScroll(scrollState) - ) - Spacer(modifier = Modifier.height(13.5.dp)) - - Row(verticalAlignment = Alignment.CenterVertically) { - Image( - painter = painterResource(id = R.drawable.ic_location), - contentDescription = "Location Icon", - modifier = Modifier - .width(24.dp) - .height(24.dp), - colorFilter = ColorFilter.tint(GrayMedium) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text(text = gameCard.locationString, style = bodyNormal.copy(color = GrayPrimary)) - Spacer(modifier = Modifier.width(12.dp)) - Icon( - painter = painterResource(id = R.drawable.ic_time), - contentDescription = "Time Icon", - modifier = Modifier - .size(24.dp), - tint = GrayMedium - ) - Spacer(modifier = Modifier.width(4.dp)) - Text(text = gameCard.dateString, style = bodyNormal.copy(color = GrayPrimary)) - } - // render the below if the game is in the future - // TODO: MESSY, is it every the case when there is a boxscore but no scoring summary if (gameCard.isPastStartTime) { - //if (gameCard.scoreBreakdown?.isNotEmpty() == true) { Spacer(modifier = Modifier.height(24.dp)) - BoxScore(gameCard.gameData) - Spacer(modifier = Modifier.height(24.dp)) - // } - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Text( - "Scoring Summary", fontSize = 18.sp, - style = heading2, + when (gameCard.sport) { + in listOf( + "Swim Dive", + "Track and Field", + "Wrestling", + "Gymnastics", + "Golf", + "Polo", + "Fencing", + "Equestrian" ) - if (gameCard.boxScore.isNotEmpty()) { - Spacer(modifier = Modifier.weight(1f)) - IconButton(onClick = { navigateToGameScoreSummary(gameCard.scoreEvent) }) { - Icon( - painter = painterResource(id = R.drawable.ic_right_chevron), - contentDescription = "Back button", - modifier = Modifier - .width(24.dp) - .height(24.dp), - ) - } + -> if (gameCard.articleData != null) { + GameDetailsContentRecap(gameCard.articleData) } + + in listOf( + "Baseball", "Basketball", "Field Hockey", + "Football", "Ice Hockey", "Lacrosse", "Soccer", + "Softball" + ) -> GameDetailsContentBoxScore(gameCard, navigateToGameScoreSummary) + + //todo if this game is an ivy tournament, use the ivy leaderboard } - if (gameCard.boxScore.isNotEmpty()) { - Spacer(modifier = Modifier.height(16.dp)) - ScoringSummary(gameCard.scoreEvent) - } else { - EmptyStateBox( - icon = R.drawable.ic_speaker_gray, - title = "No scores yet.", - height = 200.dp - ) - } - } else { - val context = LocalContext.current + } else { //display time til start Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -220,169 +198,166 @@ fun GameDetailsContent( } Spacer(modifier = Modifier.weight(1f)) - -// ButtonPrimary( -// "Add to Calendar", -// painterResource(R.drawable.ic_calendar), -// onClick = { -// gameCard.toCalendarEvent()?.let { event -> -// addToCalendar(context = context, event) -// } -// } -// ) } - } } } } +@Composable +fun GameDetailsContentBoxScore( + gameCard: DetailsCardData, + navigateToGameScoreSummary: (List) -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.fillMaxWidth() + ) { + BoxScore(gameCard.gameData) + Spacer(modifier = Modifier.height(24.dp)) + + + if (gameCard.boxScore.isNotEmpty()) { + Spacer(modifier = Modifier.height(16.dp)) + ScoringSummary( + gameCard.scoreEvent, + gameCard = gameCard, + navigateToGameScoreSummary = navigateToGameScoreSummary + ) + } else { + EmptyStateBox( + icon = R.drawable.ic_speaker_gray, + title = "No scores yet.", + height = 200.dp + ) + } + } +} + +@Composable +fun GameDetailsContentRecap( + article: ArticleHighlightData +) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text("Highlights", style = heading2) + Spacer(Modifier.height(13.dp)) + ArticleHighlightCard(article, isWideFormat = true) + } +} + +@Composable +private fun GameDetailsInformation( + gameCard: DetailsCardData, + scrollState: ScrollState +) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = gameCard.sport, + style = heading3.copy(color = GrayPrimary) + ) + Text( + text = gameCard.title, + style = heading1.copy(color = GrayPrimary), + maxLines = 1, + modifier = Modifier + .horizontalScroll(scrollState) + ) + Spacer(modifier = Modifier.height(13.5.dp)) + + Row(verticalAlignment = Alignment.CenterVertically) { + Image( + painter = painterResource(id = R.drawable.ic_location), + contentDescription = "Location Icon", + modifier = Modifier + .width(24.dp) + .height(24.dp), + colorFilter = ColorFilter.tint(GrayMedium) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text(text = gameCard.locationString, style = bodyNormal.copy(color = GrayPrimary)) + Spacer(modifier = Modifier.width(12.dp)) + Icon( + painter = painterResource(id = R.drawable.ic_time), + contentDescription = "Time Icon", + modifier = Modifier + .size(24.dp), + tint = GrayMedium + ) + Spacer(modifier = Modifier.width(4.dp)) + Text(text = gameCard.dateString, style = bodyNormal.copy(color = GrayPrimary)) + } + } +} + +//todo update previews to test different headers + +@Preview +@Composable +private fun GameDetailsInfoPreview() = ScorePreview { + val scrollState = rememberScrollState() + GameDetailsInformation( + sampleDetailsCardData, scrollState = scrollState + ) +} + + @Preview @Composable -private fun GameDetailsPreview() { +private fun GameDetailsPreStartPreview() { GameDetailsContent( - DetailsCardData( - title = "Championship Game", - opponentLogo = "https://example.com/logo.png", - opponent = "Wildcats", - opponentColor = Color(0xFF123456), - date = LocalDate.of(2025, 4, 20), - time = "7:30 PM", - dateString = "April 20, 2025", - isPastStartTime = false, - location = "Main Stadium", - locationString = "Main Stadium, Cityville", - gender = "Men's", - genderIcon = 123, // Dummy resource ID - sport = "Basketball", - sportIcon = 456, // Dummy resource ID - boxScore = listOf( - GameDetailsBoxScore( - team = "Tigers", - period = "1st", - time = "12:34", - description = "3-point shot", - scorer = "John Doe", - assist = "Mike Smith", - scoreBy = "Tigers", - corScore = 21, - oppScore = 18 - ), - GameDetailsBoxScore( - team = "Wildcats", - period = "1st", - time = "10:01", - description = "Layup", - scorer = "Jane Roe", - assist = "Tom Lee", - scoreBy = "Wildcats", - corScore = 21, - oppScore = 20 - ) - ), + sampleDetailsCardData.copy(isPastStartTime = false), navigateToGameScoreSummary = {} + ) +} + +@Preview +@Composable +private fun GameDetailsAfterStartPreview() { + GameDetailsContent( + sampleDetailsCardData, navigateToGameScoreSummary = {} + ) +} + +@Preview +@Composable +private fun EmptyGameDetailsLeaderboardPreview() { + GameDetailsContent( + sampleDetailsCardData.copy( + sport = "Swim and Dive", + boxScore = emptyList(), scoreBreakdown = listOf( - listOf("10", "15", "20", "18"), // Tigers per quarter - listOf("12", "10", "18", "22") // Wildcats per quarter - ), - gameData = GameData( - Pair( - TeamScore( - team = TeamBoxScore( - name = "Tigers", - ), - scoresByPeriod = listOf(20, 18, 22, 18), - totalScore = 78 - ), - TeamScore( - team = TeamBoxScore( - name = "Wildcats", - ), - scoresByPeriod = listOf(18, 20, 16, 21), - totalScore = 75 - ) - ) - ), - scoreEvent = listOf( - ScoreEvent( - id = 1, - time = "11:11", - quarter = "2nd", - team = TeamGameSummary( - name = "Tigers", - logo = "https://example.com/tigers.png" - ), - eventType = "3PT", - score = "36-34", - description = "Three-pointer by John Doe" - ), - ScoreEvent( - id = 2, - time = "08:45", - quarter = "3rd", - team = TeamGameSummary( - name = "Wildcats", - logo = "https://example.com/wildcats.png" - ), - eventType = "FT", - score = "36-35", - description = "Free throw by Jane Roe" - ) + emptyList(), + emptyList() ), - daysUntilGame = 6, - hoursUntilGame = 144, - homeScore = 78, - oppScore = 75 + scoreEvent = emptyList() ), navigateToGameScoreSummary = {} ) } @Preview @Composable -private fun EmptyGameDetailsPreview() { +private fun EmptyGameDetailsHighlightPreview() { GameDetailsContent( - DetailsCardData( - title = "Championship Game", - opponentLogo = "https://example.com/logo.png", - opponent = "Wildcats", - opponentColor = Color(0xFF123456), - date = LocalDate.of(2025, 4, 20), - time = "7:30 PM", - dateString = "April 20, 2025", - isPastStartTime = true, - location = "Main Stadium", - locationString = "Main Stadium, Cityville", - gender = "Men's", - genderIcon = 123, // Dummy resource ID - sport = "Basketball", - sportIcon = 456, // Dummy resource ID + sampleDetailsCardData.copy( + sport = "Equestrian", boxScore = emptyList(), scoreBreakdown = listOf( emptyList(), emptyList() ), - gameData = GameData( - Pair( - TeamScore( - team = TeamBoxScore( - name = "Tigers", - ), - scoresByPeriod = emptyList(), - totalScore = 0 - ), - TeamScore( - team = TeamBoxScore( - name = "Wildcats", - ), - scoresByPeriod = emptyList(), - totalScore = 0 - ) - ) - ), scoreEvent = emptyList(), - daysUntilGame = 0, - hoursUntilGame = 0, - homeScore = 0, - oppScore = 0 + articleData = + ArticleHighlightData( + "Late Goal Lifts No. 6 Men’s Hockey Over Brown", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/09", + Sport.ICE_HOCKEY + ) + ), navigateToGameScoreSummary = {} ) } \ No newline at end of file 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 a4f8fa5..2a93edc 100644 --- a/app/src/main/java/com/cornellappdev/score/theme/Color.kt +++ b/app/src/main/java/com/cornellappdev/score/theme/Color.kt @@ -25,4 +25,7 @@ val PennBlue = Color(0x66021E59) val PrincetonOrange = Color(0x66FF6000) val Wash = Color(0xFFF4F4F4) -val Stroke = Color(0xFFD6D6D6) \ No newline at end of file +val Stroke = Color(0xFFD6D6D6) + +val rankHeaderRed = Color(0x66B31B1B) +val rankHeaderNeutral = Color(0x6689572E) \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/util/GameDataUtil.kt b/app/src/main/java/com/cornellappdev/score/util/GameDataUtil.kt index 2da4dbb..47eafff 100644 --- a/app/src/main/java/com/cornellappdev/score/util/GameDataUtil.kt +++ b/app/src/main/java/com/cornellappdev/score/util/GameDataUtil.kt @@ -112,7 +112,10 @@ fun parseResultScore(result: String?): Pair? { val validSports = setOf( "Baseball", "Basketball", "Field Hockey", - "Football", "Ice Hockey", "Lacrosse", "Soccer" + "Football", "Ice Hockey", "Lacrosse", "Soccer", + "Softball", "Swim Dive", "Track and Field", "Wrestling", + "Gymnastics", "Golf", "Fencing", "Equestrian" + //"Polo" - waiting on icon designs ) fun isValidSport(sportName: String): Boolean { diff --git a/app/src/main/java/com/cornellappdev/score/util/GeneralUtil.kt b/app/src/main/java/com/cornellappdev/score/util/GeneralUtil.kt new file mode 100644 index 0000000..f9cf967 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/util/GeneralUtil.kt @@ -0,0 +1,13 @@ +package com.cornellappdev.score.util + +fun Int.toPlacement(): String { + val suffix = when { + this % 100 in 11..13 -> "th" + this % 10 == 1 -> "st" + this % 10 == 2 -> "nd" + this % 10 == 3 -> "rd" + else -> "th" + } + + return "$this$suffix" +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt index 476077b..b2456f2 100644 --- a/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt +++ b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt @@ -2,18 +2,21 @@ package com.cornellappdev.score.util import androidx.compose.ui.graphics.Color import com.cornellappdev.score.R +import com.cornellappdev.score.components.LeaderboardData import com.cornellappdev.score.model.ArticleHighlightData +import com.cornellappdev.score.model.DetailsCardData import com.cornellappdev.score.model.GameCardData import com.cornellappdev.score.model.GameData +import com.cornellappdev.score.model.GameDetailsBoxScore import com.cornellappdev.score.model.GenderDivision import com.cornellappdev.score.model.HighlightData -import com.cornellappdev.score.model.VideoHighlightData import com.cornellappdev.score.model.ScoreEvent import com.cornellappdev.score.model.Sport import com.cornellappdev.score.model.SportSelection import com.cornellappdev.score.model.TeamBoxScore import com.cornellappdev.score.model.TeamGameSummary import com.cornellappdev.score.model.TeamScore +import com.cornellappdev.score.model.VideoHighlightData import java.time.LocalDate val PENN_GAME = GameCardData( @@ -230,46 +233,195 @@ val recentSearchList = listOf("Columbia", "Men's ice hockey", "Late goal lifts N //Mixed type val highlightsList = listOf( HighlightData.Video - (VideoHighlightData( - "vs Columbia", - "maxresdefault.jpg", - "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", - "11/09", - Sport.BASEBALL, - GenderDivision.MALE - )), + ( + VideoHighlightData( + "vs Columbia", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/09", + Sport.BASEBALL, + GenderDivision.MALE + ) + ), HighlightData.Article - (ArticleHighlightData( - "Late Goal Lifts No. 6 Men’s Hockey Over Brown", - "maxresdefault.jpg", - "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", - "11/09", - Sport.ICE_HOCKEY - )), + ( + ArticleHighlightData( + "Late Goal Lifts No. 6 Men’s Hockey Over Brown", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/09", + Sport.ICE_HOCKEY + ) + ), HighlightData.Video - (VideoHighlightData( - "vs Columbia", - "maxresdefault.jpg", - "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", - "11/9", - Sport.BASEBALL, - GenderDivision.MALE - )), + ( + VideoHighlightData( + "vs Columbia", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/9", + Sport.BASEBALL, + GenderDivision.MALE + ) + ), HighlightData.Article - (ArticleHighlightData( - "Late Goal Lifts No. 6 Men’s Hockey Over Brown", - "maxresdefault.jpg", - "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", - "11/09", - Sport.ICE_HOCKEY - )), + ( + ArticleHighlightData( + "Late Goal Lifts No. 6 Men’s Hockey Over Brown", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/09", + Sport.ICE_HOCKEY + ) + ), HighlightData.Video - (VideoHighlightData( - "vs Columbia", - "maxresdefault.jpg", - "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", - "11/9", - Sport.BASEBALL, - GenderDivision.MALE - )) + ( + VideoHighlightData( + "vs Columbia", + "maxresdefault.jpg", + "https://cornellsun.com/article/london-mcdavid-is-making-a-name-for-herself-at-cornell", + "11/9", + Sport.BASEBALL, + GenderDivision.MALE + ) + ) +) + +/*GAME DETAILS SCREEN*/ +val sampleDetailsCardData = DetailsCardData( + title = "Championship Game", + opponentLogo = "https://example.com/logo.png", + opponent = "Wildcats", + opponentColor = Color(0xFF123456), + date = LocalDate.of(2025, 4, 20), + time = "7:30 PM", + dateString = "April 20, 2025", + isPastStartTime = true, + location = "Main Stadium", + locationString = "Main Stadium, Cityville", + gender = "Men's", + genderIcon = 123, // Dummy resource ID + sport = "Basketball", + sportIcon = 456, // Dummy resource ID + boxScore = listOf( + GameDetailsBoxScore( + team = "Tigers", + period = "1st", + time = "12:34", + description = "3-point shot", + scorer = "John Doe", + assist = "Mike Smith", + scoreBy = "Tigers", + corScore = 21, + oppScore = 18 + ), + GameDetailsBoxScore( + team = "Wildcats", + period = "1st", + time = "10:01", + description = "Layup", + scorer = "Jane Roe", + assist = "Tom Lee", + scoreBy = "Wildcats", + corScore = 21, + oppScore = 20 + ) + ), + scoreBreakdown = listOf( + listOf("10", "15", "20", "18"), // Tigers per quarter + listOf("12", "10", "18", "22") // Wildcats per quarter + ), + gameData = GameData( + Pair( + TeamScore( + team = TeamBoxScore( + name = "Tigers", + ), + scoresByPeriod = listOf(20, 18, 22, 18), + totalScore = 78 + ), + TeamScore( + team = TeamBoxScore( + name = "Wildcats", + ), + scoresByPeriod = listOf(18, 20, 16, 21), + totalScore = 75 + ) + ) + ), + scoreEvent = listOf( + ScoreEvent( + id = 1, + time = "11:11", + quarter = "2nd", + team = TeamGameSummary( + name = "Cornell", + logo = "https://example.com/cornel_logo.png" + ), + eventType = "3PT", + score = "36-34", + description = "Three-pointer by John Doe" + ), + ScoreEvent( + id = 2, + time = "08:45", + quarter = "3rd", + team = TeamGameSummary( + name = "Yale", + logo = "https://cornellbigred.com/images/logos/YALE_LOGO_2020.png?width=80&height=80&mode=max" + ), + eventType = "FT", + score = "36-35", + description = "Free throw by Jane Roe" + ) + ), + daysUntilGame = 6, + hoursUntilGame = 144, + homeScore = 78, + oppScore = 75 +) + +val sampleIvyLeaderboardData = listOf( + LeaderboardData( + rank = 1, + icon = R.drawable.cornell_logo, + school = "Princeton", + points = 614.5 + ), + LeaderboardData( + rank = 2, + icon = R.drawable.cornell_logo, + school = "Penn", + points = 614.5 + ), + LeaderboardData( + rank = 3, + icon = R.drawable.cornell_logo, + school = "Harvard", + points = 614.5 + ), + LeaderboardData( + rank = 4, + icon = R.drawable.cornell_logo, + school = "Brown", + points = 614.5 + ), + LeaderboardData( + rank = 5, + icon = R.drawable.cornell_logo, + school = "Yale", + points = 614.5 + ), + LeaderboardData( + rank = 6, + icon = R.drawable.cornell_logo, + school = "Cornell", + points = 614.5 + ), + LeaderboardData( + rank = 7, + icon = R.drawable.cornell_logo, + school = "Columbia", + points = 614.5 + ) ) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 05c415e..e8be535 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.jetbrainsKotlinAndroid) version "1.9.10" apply false id("com.google.dagger.hilt.android") version "2.51.1" apply false - kotlin("jvm") version "2.0.20" - kotlin("plugin.serialization") version "2.0.20" + kotlin("jvm") version "2.1.10" + kotlin("plugin.serialization") version "2.1.10" id("com.google.gms.google-services") version "4.4.3" apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index baadf24..ba8a5f1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.8.2" +agp = "8.13.2" composeLintChecks = "1.4.2" kotlin = "1.9.0" coreKtx = "1.10.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cfc8e57..a48d9a8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Sep 17 00:24:01 EDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists