From ba46f6f52bc2af0ff36d75b9d5fdb13e10edcbd5 Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 15:42:31 -0400 Subject: [PATCH 1/8] Leaderboard UI --- .../score/components/Leaderboard.kt | 138 ++++++++++++++++++ .../score/util/TestingConstants.kt | 47 ++++++ 2 files changed, 185 insertions(+) create mode 100644 app/src/main/java/com/cornellappdev/score/components/Leaderboard.kt 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/util/TestingConstants.kt b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt index 476077b..2142356 100644 --- a/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt +++ b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt @@ -1,7 +1,9 @@ package com.cornellappdev.score.util import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import com.cornellappdev.score.R +import com.cornellappdev.score.components.LeaderboardData import com.cornellappdev.score.model.ArticleHighlightData import com.cornellappdev.score.model.GameCardData import com.cornellappdev.score.model.GameData @@ -272,4 +274,49 @@ val highlightsList = listOf( Sport.BASEBALL, GenderDivision.MALE )) +) + +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 From 9e877d16aab91173d461aa8f325ab9827455d450 Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 15:43:47 -0400 Subject: [PATCH 2/8] Alternative game details headers --- .../score/components/GameScoreHeader.kt | 110 ++++++++++++++++-- .../com/cornellappdev/score/theme/Color.kt | 5 +- .../cornellappdev/score/util/GeneralUtil.kt | 13 +++ 3 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/cornellappdev/score/util/GeneralUtil.kt 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..c4147fa 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 @@ -30,8 +24,11 @@ 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.Sport 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 +92,90 @@ fun GameScoreHeader( } } +@Composable +fun AlternativeScoreHeader( + 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 NthPlaceScoreHeader( + 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 +188,17 @@ private fun GameScoreHeaderPreview() = ScorePreview { rightScore = 0, modifier = Modifier.height(185.dp) ) +} + +@Preview +@Composable +private fun AlternativeScoreHeaderPreview() = ScorePreview { + AlternativeScoreHeader(6, Modifier.height(185.dp)) +} + +@Preview +@Composable +private fun NthPlaceScoreHeaderPreview() = ScorePreview { + NthPlaceScoreHeader("11th of 18 (881)", Modifier.height(185.dp)) + NthPlaceScoreHeader("High Point", Modifier.height(185.dp)) } \ 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/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 From 370bdb6dfdb8bd7555ce56903b903619c6235b34 Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 15:44:26 -0400 Subject: [PATCH 3/8] Start adding alternative game details screens --- .../score/screen/GameDetailsScreen.kt | 277 +++++++++++++----- 1 file changed, 211 insertions(+), 66 deletions(-) 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..d876c13 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,9 @@ 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.gestures.ScrollableState import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -35,14 +37,18 @@ 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.Leaderboard import com.cornellappdev.score.components.NavigationHeader import com.cornellappdev.score.components.ScorePullToRefreshBox 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.Sport import com.cornellappdev.score.model.TeamBoxScore import com.cornellappdev.score.model.TeamGameSummary import com.cornellappdev.score.model.TeamScore @@ -53,6 +59,8 @@ 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.highlightsList +import com.cornellappdev.score.util.sampleIvyLeaderboardData import com.cornellappdev.score.viewmodel.GameDetailsViewModel import java.time.LocalDate @@ -128,81 +136,70 @@ fun GameDetailsContent( 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, + //TODO switch to matching for cleaner code + if (gameCard.sport == "Swim and Dive") { + Leaderboard(sampleIvyLeaderboardData) + } else if (gameCard.sport == "Equestrian") { + //todo extract this into another component + Text("Highlights", style = heading2) + Spacer(modifier = Modifier.height(13.dp)) + ArticleHighlightCard( + 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 + ), + isWideFormat = false ) - 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), - ) + } else { + //if (gameCard.scoreBreakdown?.isNotEmpty() == true) { + BoxScore(gameCard.gameData) + Spacer(modifier = Modifier.height(24.dp)) + // } + + 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), + ) + } } } - } - 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 - ) + 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 @@ -230,13 +227,59 @@ fun GameDetailsContent( // } // } // ) - } + } } } } } +@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)) + } + } +} + @Preview @Composable private fun GameDetailsPreview() { @@ -385,4 +428,106 @@ private fun EmptyGameDetailsPreview() { oppScore = 0 ), navigateToGameScoreSummary = {} ) +} + +@Preview +@Composable +private fun EmptyGameDetailsLeaderboardPreview() { + 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 = "Swim and Dive", + sportIcon = 456, // Dummy resource ID + 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 + ), navigateToGameScoreSummary = {} + ) +} + +@Preview +@Composable +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 = "Equestrian", + sportIcon = 456, // Dummy resource ID + 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 + ), navigateToGameScoreSummary = {} + ) } \ No newline at end of file From e73c4e8c0675e075f62712151f51f279cec09d3c Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 15:45:10 -0400 Subject: [PATCH 4/8] Upgrade versions for Android Studio Quail --- build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 From 4f9c1daac3bf018fb5d1006d63ecb4766da0da12 Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 17:19:36 -0400 Subject: [PATCH 5/8] Fix score summary bug todo - ui doesn't match design --- .../cornellappdev/score/components/ScoreSummary.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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..2565e13 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt @@ -1,3 +1,5 @@ +package com.cornellappdev.score.components + import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -17,7 +19,6 @@ 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.components.ScorePreview import com.cornellappdev.score.model.ScoreEvent import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyMedium @@ -30,7 +31,7 @@ import com.cornellappdev.score.util.scoreEvents1 @Composable fun ScoringSummary(scoreEvents: List, modifier: Modifier = Modifier) { Column(modifier = modifier) { - scoreEvents.take(3).map { + scoreEvents.take(3).forEach { ScoreEventItem(it) HorizontalDivider(color = Color.LightGray, thickness = 0.5.dp) } @@ -38,9 +39,12 @@ 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 @@ -95,7 +99,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( From 8f0ec20301bb4c90df44052a352d94d69cfcd375 Mon Sep 17 00:00:00 2001 From: amjiao Date: Tue, 15 Sep 2026 17:21:02 -0400 Subject: [PATCH 6/8] Clean up previews, add testing constants --- .../score/screen/GameDetailsScreen.kt | 258 ++---------------- .../score/util/TestingConstants.kt | 185 ++++++++++--- 2 files changed, 175 insertions(+), 268 deletions(-) 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 d876c13..e8a24cf 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -1,10 +1,8 @@ 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.gestures.ScrollableState import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -25,7 +23,6 @@ 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 @@ -39,19 +36,16 @@ import com.cornellappdev.score.components.GameDetailsLoadingScreen import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.Leaderboard import com.cornellappdev.score.components.NavigationHeader +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.Sport -import com.cornellappdev.score.model.TeamBoxScore -import com.cornellappdev.score.model.TeamGameSummary -import com.cornellappdev.score.model.TeamScore import com.cornellappdev.score.theme.GrayMedium import com.cornellappdev.score.theme.GrayPrimary import com.cornellappdev.score.theme.Style.bodyNormal @@ -59,13 +53,13 @@ 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.highlightsList +import com.cornellappdev.score.util.sampleDetailsCardData import com.cornellappdev.score.util.sampleIvyLeaderboardData import com.cornellappdev.score.viewmodel.GameDetailsViewModel -import java.time.LocalDate @Composable fun GameDetailsScreen( + modifier: Modifier = Modifier, gameDetailsViewModel: GameDetailsViewModel = hiltViewModel(), onBackArrow: () -> Unit = {}, navigateToGameScoreSummary: (List) -> Unit @@ -75,7 +69,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 @@ -113,11 +108,12 @@ fun GameDetailsScreen( @Composable fun GameDetailsContent( gameCard: DetailsCardData, + modifier: Modifier = Modifier, navigateToGameScoreSummary: (List) -> Unit ) { val scrollState = rememberScrollState() Column( - modifier = Modifier + modifier = modifier .background(White) .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -143,7 +139,7 @@ fun GameDetailsContent( // 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.isPastStartTime == true) { Spacer(modifier = Modifier.height(24.dp)) //TODO switch to matching for cleaner code @@ -202,7 +198,7 @@ fun GameDetailsContent( } } } else { - val context = LocalContext.current + //val context = LocalContext.current Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -238,10 +234,10 @@ fun GameDetailsContent( private fun GameDetailsInformation( gameCard: DetailsCardData, scrollState: ScrollState -){ +) { Column( modifier = Modifier.fillMaxWidth() - ){ + ) { Text( text = gameCard.sport, style = heading3.copy(color = GrayPrimary) @@ -282,151 +278,27 @@ private fun GameDetailsInformation( @Preview @Composable -private fun GameDetailsPreview() { +private fun GameDetailsInfoPreview() = ScorePreview { + val scrollState = rememberScrollState() + GameDetailsInformation( + sampleDetailsCardData, scrollState = scrollState + ) +} + + +@Preview +@Composable +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 - ) - ), - 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" - ) - ), - daysUntilGame = 6, - hoursUntilGame = 144, - homeScore = 78, - oppScore = 75 - ), navigateToGameScoreSummary = {} + sampleDetailsCardData.copy(isPastStartTime = false), navigateToGameScoreSummary = {} ) } @Preview @Composable -private fun EmptyGameDetailsPreview() { +private fun GameDetailsAfterStartPreview() { 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 - 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 - ), navigateToGameScoreSummary = {} + sampleDetailsCardData, navigateToGameScoreSummary = {} ) } @@ -434,49 +306,14 @@ private fun EmptyGameDetailsPreview() { @Composable private fun EmptyGameDetailsLeaderboardPreview() { 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 + sampleDetailsCardData.copy( sport = "Swim and Dive", - sportIcon = 456, // Dummy resource ID 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 + scoreEvent = emptyList() ), navigateToGameScoreSummary = {} ) } @@ -485,49 +322,14 @@ private fun EmptyGameDetailsLeaderboardPreview() { @Composable 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 + sampleDetailsCardData.copy( sport = "Equestrian", - sportIcon = 456, // Dummy resource ID 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 + scoreEvent = emptyList() ), navigateToGameScoreSummary = {} ) } \ 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 2142356..2e79576 100644 --- a/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt +++ b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt @@ -1,21 +1,22 @@ package com.cornellappdev.score.util import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource 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( @@ -232,48 +233,152 @@ 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 = "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" + ) + ), + daysUntilGame = 6, + hoursUntilGame = 144, + homeScore = 78, + oppScore = 75 ) val sampleIvyLeaderboardData = listOf( From 0497bef5de002a52430b379309fa6dc1b89ab75f Mon Sep 17 00:00:00 2001 From: amjiao Date: Thu, 24 Sep 2026 16:20:05 -0400 Subject: [PATCH 7/8] Add game details screen recap format + some refactoring --- .../score/components/GameScoreHeader.kt | 15 +- .../score/components/ScoreSummary.kt | 61 +++++- .../com/cornellappdev/score/model/Game.kt | 4 +- .../score/screen/GameDetailsScreen.kt | 196 ++++++++++-------- .../cornellappdev/score/util/GameDataUtil.kt | 5 +- .../score/util/TestingConstants.kt | 8 +- 6 files changed, 185 insertions(+), 104 deletions(-) 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 c4147fa..3a9b69f 100644 --- a/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt +++ b/app/src/main/java/com/cornellappdev/score/components/GameScoreHeader.kt @@ -24,7 +24,6 @@ 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.Sport import com.cornellappdev.score.theme.Style.scoreHeaderText import com.cornellappdev.score.theme.rankHeaderNeutral import com.cornellappdev.score.theme.rankHeaderRed @@ -93,7 +92,7 @@ fun GameScoreHeader( } @Composable -fun AlternativeScoreHeader( +fun NthPlaceScoreHeader( rank: Int, modifier: Modifier = Modifier, ) { @@ -131,7 +130,7 @@ fun AlternativeScoreHeader( } @Composable -fun NthPlaceScoreHeader( +fun AlternativeScoreHeader( resultString: String, modifier: Modifier = Modifier, ) { @@ -192,13 +191,13 @@ private fun GameScoreHeaderPreview() = ScorePreview { @Preview @Composable -private fun AlternativeScoreHeaderPreview() = ScorePreview { - AlternativeScoreHeader(6, Modifier.height(185.dp)) +private fun NthPlaceScoreHeaderPreview() = ScorePreview { + NthPlaceScoreHeader(6, Modifier.height(185.dp)) } @Preview @Composable -private fun NthPlaceScoreHeaderPreview() = ScorePreview { - NthPlaceScoreHeader("11th of 18 (881)", Modifier.height(185.dp)) - NthPlaceScoreHeader("High Point", Modifier.height(185.dp)) +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/ScoreSummary.kt b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt index 2565e13..5384e26 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt @@ -4,10 +4,15 @@ 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 @@ -17,20 +22,54 @@ 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.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) { +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) @@ -49,7 +88,7 @@ fun ScoreEventItem( .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, @@ -67,7 +106,6 @@ fun ScoreEventItem( ) } - Row( modifier = Modifier.weight(2f), horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -88,7 +126,6 @@ fun ScoreEventItem( ) } - Row( horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically @@ -128,5 +165,17 @@ fun ScoreEventItem( @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 e8a24cf..0dc6f24 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -16,7 +16,6 @@ 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 @@ -26,16 +25,17 @@ import androidx.compose.ui.graphics.ColorFilter 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.Leaderboard 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 @@ -54,7 +54,6 @@ 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.util.sampleIvyLeaderboardData import com.cornellappdev.score.viewmodel.GameDetailsViewModel @Composable @@ -111,6 +110,8 @@ fun GameDetailsContent( modifier: Modifier = Modifier, navigateToGameScoreSummary: (List) -> Unit ) { + // TODO: add navigation + val scrollState = rememberScrollState() Column( modifier = modifier @@ -118,16 +119,36 @@ fun GameDetailsContent( .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)) @@ -137,68 +158,32 @@ fun GameDetailsContent( scrollState ) - // 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 == true) { + if (gameCard.isPastStartTime) { Spacer(modifier = Modifier.height(24.dp)) - - //TODO switch to matching for cleaner code - if (gameCard.sport == "Swim and Dive") { - Leaderboard(sampleIvyLeaderboardData) - } else if (gameCard.sport == "Equestrian") { - //todo extract this into another component - Text("Highlights", style = heading2) - Spacer(modifier = Modifier.height(13.dp)) - ArticleHighlightCard( - 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 - ), - isWideFormat = false + when (gameCard.sport) { + in listOf( + "Swim Dive", + "Track and Field", + "Wrestling", + "Gymnastics", + "Golf", + "Polo", + "Fencing", + "Equestrian" ) - } else { - //if (gameCard.scoreBreakdown?.isNotEmpty() == true) { - BoxScore(gameCard.gameData) - Spacer(modifier = Modifier.height(24.dp)) - // } - - 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), - ) - } - } - } - 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 - ) + -> 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 } - } else { - //val context = LocalContext.current + } else { //display time til start Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -213,23 +198,55 @@ 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, @@ -276,6 +293,8 @@ private fun GameDetailsInformation( } } +//todo update previews to test different headers + @Preview @Composable private fun GameDetailsInfoPreview() = ScorePreview { @@ -329,7 +348,16 @@ private fun EmptyGameDetailsHighlightPreview() { emptyList(), emptyList() ), - scoreEvent = emptyList() + scoreEvent = emptyList(), + 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/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/TestingConstants.kt b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt index 2e79576..b2456f2 100644 --- a/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt +++ b/app/src/main/java/com/cornellappdev/score/util/TestingConstants.kt @@ -355,8 +355,8 @@ val sampleDetailsCardData = DetailsCardData( time = "11:11", quarter = "2nd", team = TeamGameSummary( - name = "Tigers", - logo = "https://example.com/tigers.png" + name = "Cornell", + logo = "https://example.com/cornel_logo.png" ), eventType = "3PT", score = "36-34", @@ -367,8 +367,8 @@ val sampleDetailsCardData = DetailsCardData( time = "08:45", quarter = "3rd", team = TeamGameSummary( - name = "Wildcats", - logo = "https://example.com/wildcats.png" + name = "Yale", + logo = "https://cornellbigred.com/images/logos/YALE_LOGO_2020.png?width=80&height=80&mode=max" ), eventType = "FT", score = "36-35", From e650d9cf30c8a8099b469735b9f45a682a6cad2f Mon Sep 17 00:00:00 2001 From: amjiao Date: Thu, 24 Sep 2026 16:35:22 -0400 Subject: [PATCH 8/8] Update graphql files with backend changes --- app/src/main/graphql/GameById.graphql | 5 +++++ app/src/main/graphql/Games.graphql | 4 ---- app/src/main/graphql/schema.graphqls | 8 ++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) 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 } """