From db7abb8cf7bd582296e1c5c34e645703f5f150f7 Mon Sep 17 00:00:00 2001 From: Emil Jiang Date: Wed, 4 Mar 2026 17:33:00 -0500 Subject: [PATCH 1/2] calendar ticket link --- app/src/main/graphql/GameById.graphql | 1 + .../com/cornellappdev/score/model/Game.kt | 11 ++- .../score/model/GameByIdQueryMappers.kt | 6 +- .../score/screen/GameDetailsScreen.kt | 75 +++++++++++++++---- app/src/main/res/drawable/ticket.xml | 18 +++++ 5 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 app/src/main/res/drawable/ticket.xml diff --git a/app/src/main/graphql/GameById.graphql b/app/src/main/graphql/GameById.graphql index aced4b7..eb0e9c9 100644 --- a/app/src/main/graphql/GameById.graphql +++ b/app/src/main/graphql/GameById.graphql @@ -28,5 +28,6 @@ query GameById($id: String!) { corScore oppScore } + ticketLink } } \ No newline at end of file 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 d6c93d7..6bf6c4c 100644 --- a/app/src/main/java/com/cornellappdev/score/model/Game.kt +++ b/app/src/main/java/com/cornellappdev/score/model/Game.kt @@ -67,7 +67,8 @@ data class GameDetailsGame( val time: String?, val scoreBreakdown: List?>?, val team: GameDetailsTeam?, - val boxScore: List? + val boxScore: List?, + val ticketUrl: String? ) @@ -126,7 +127,8 @@ data class DetailsCardData( val daysUntilGame: Int?, val hoursUntilGame: Int?, val homeScore: Int, - val oppScore: Int + val oppScore: Int, + val ticketUrl: String? ) // Scoring information by round of a game, used in the box score @@ -146,7 +148,7 @@ data class TeamScore( // Aggregated game data showing scores for both teams data class GameData( val teamScores: Pair -){ +) { val maxPeriods: Int get() = maxOf( @@ -298,7 +300,8 @@ fun GameDetailsGame.toGameCardData(): DetailsCardData { homeScore = convertScores(scoreBreakdown?.getOrNull(0), sport).second ?: parsedScores?.first ?: 0, oppScore = convertScores(scoreBreakdown?.getOrNull(1), sport).second - ?: parsedScores?.second ?: 0 + ?: parsedScores?.second ?: 0, + ticketUrl = ticketUrl ?: "" ) } diff --git a/app/src/main/java/com/cornellappdev/score/model/GameByIdQueryMappers.kt b/app/src/main/java/com/cornellappdev/score/model/GameByIdQueryMappers.kt index 81ad01a..e44323d 100644 --- a/app/src/main/java/com/cornellappdev/score/model/GameByIdQueryMappers.kt +++ b/app/src/main/java/com/cornellappdev/score/model/GameByIdQueryMappers.kt @@ -17,13 +17,15 @@ fun GameByIdQuery.Game.toGameDetails(): GameDetailsGame { time = this.time, scoreBreakdown = this.scoreBreakdown, team = this.team?.toGameDetailsTeam(), - boxScore = this.boxScore?.mapNotNull { it?.toGameDetailsBoxScore() } + boxScore = this.boxScore?.mapNotNull { it?.toGameDetailsBoxScore() }, + ticketUrl = ticketLink ) } + fun GameByIdQuery.Team.toGameDetailsTeam(): GameDetailsTeam { return GameDetailsTeam( id = this.id, - color = parseColor(this.color).copy(alpha = 0.4f*255), + color = parseColor(this.color).copy(alpha = 0.4f * 255), image = this.image, name = this.name ) 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..ea272b3 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -1,6 +1,10 @@ package com.cornellappdev.score.screen import ScoringSummary +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll @@ -31,6 +35,7 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.score.R import com.cornellappdev.score.components.BoxScore +import com.cornellappdev.score.components.ButtonPrimary import com.cornellappdev.score.components.EmptyStateBox import com.cornellappdev.score.components.ErrorState import com.cornellappdev.score.components.GameDetailsLoadingScreen @@ -55,6 +60,10 @@ import com.cornellappdev.score.theme.Style.heading3 import com.cornellappdev.score.theme.White import com.cornellappdev.score.viewmodel.GameDetailsViewModel import java.time.LocalDate +import java.time.LocalTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.Locale @Composable fun GameDetailsScreen( @@ -220,16 +229,26 @@ 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) -// } -// } -// ) + Row() { + ButtonPrimary( + "Buy Tickets", + painterResource(R.drawable.ticket), + onClick = { + gameCard.ticketUrl?.let { url -> + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(intent) + } + } + ) + Spacer(Modifier.size(16.dp)) + ButtonPrimary( + "Add to Calendar", + painterResource(R.drawable.ic_calendar), + onClick = { + addGameToCalendar(context, gameCard) + } + ) + } } } @@ -331,7 +350,8 @@ private fun GameDetailsPreview() { daysUntilGame = 6, hoursUntilGame = 144, homeScore = 78, - oppScore = 75 + oppScore = 75, + ticketUrl = "" ), navigateToGameScoreSummary = {} ) } @@ -382,7 +402,36 @@ private fun EmptyGameDetailsPreview() { daysUntilGame = 0, hoursUntilGame = 0, homeScore = 0, - oppScore = 0 - ), navigateToGameScoreSummary = {} + oppScore = 0, + ticketUrl = "" + ), + navigateToGameScoreSummary = {}, ) +} + +// helper +fun addGameToCalendar(context: Context, gameCard: DetailsCardData) { + val date = gameCard.date ?: return + val time = gameCard.time + + val startDateTime = try { + val formatter = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH) + val localTime = LocalTime.parse(time.trim().uppercase(), formatter) + date.atTime(localTime) + } catch (e: Exception) { + date.atStartOfDay() + } + + val zoneId = ZoneId.systemDefault() + val startMillis = startDateTime.atZone(zoneId).toInstant().toEpochMilli() + val endMillis = startDateTime.plusHours(2).atZone(zoneId).toInstant().toEpochMilli() + + val intent = Intent(Intent.ACTION_INSERT, CalendarContract.Events.CONTENT_URI).apply { + putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis) + putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis) + putExtra(CalendarContract.Events.TITLE, gameCard.title) + putExtra(CalendarContract.Events.EVENT_LOCATION, gameCard.locationString) + putExtra(CalendarContract.Events.DESCRIPTION, "${gameCard.sport} - ${gameCard.gender}") + } + context.startActivity(intent) } \ No newline at end of file diff --git a/app/src/main/res/drawable/ticket.xml b/app/src/main/res/drawable/ticket.xml new file mode 100644 index 0000000..11bab77 --- /dev/null +++ b/app/src/main/res/drawable/ticket.xml @@ -0,0 +1,18 @@ + + + + From 45d28c355ad318849ffbc468bbc6497cb5f713fa Mon Sep 17 00:00:00 2001 From: Emil Jiang Date: Wed, 4 Mar 2026 17:42:48 -0500 Subject: [PATCH 2/2] fix --- .../java/com/cornellappdev/score/screen/GameDetailsScreen.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ea272b3..99d2361 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.CalendarContract +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll @@ -416,9 +417,10 @@ fun addGameToCalendar(context: Context, gameCard: DetailsCardData) { val startDateTime = try { val formatter = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH) - val localTime = LocalTime.parse(time.trim().uppercase(), formatter) + val localTime = LocalTime.parse(time.trim().uppercase().replace(".", ""), formatter) date.atTime(localTime) } catch (e: Exception) { + Log.e("Calendar", "Failed to parse time: '$time'", e) date.atStartOfDay() }