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..99d2361 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,11 @@ package com.cornellappdev.score.screen import ScoringSummary +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 @@ -31,6 +36,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 +61,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 +230,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 +351,8 @@ private fun GameDetailsPreview() { daysUntilGame = 6, hoursUntilGame = 144, homeScore = 78, - oppScore = 75 + oppScore = 75, + ticketUrl = "" ), navigateToGameScoreSummary = {} ) } @@ -382,7 +403,37 @@ 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().replace(".", ""), formatter) + date.atTime(localTime) + } catch (e: Exception) { + Log.e("Calendar", "Failed to parse time: '$time'", e) + 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 @@ + + + +