diff --git a/SportsTime/Features/Home/Views/HomeView.swift b/SportsTime/Features/Home/Views/HomeView.swift index a6ab38f..373cb8a 100644 --- a/SportsTime/Features/Home/Views/HomeView.swift +++ b/SportsTime/Features/Home/Views/HomeView.swift @@ -106,7 +106,7 @@ struct HomeView: View { } .sheet(item: $selectedSuggestedTrip) { suggestedTrip in NavigationStack { - TripDetailView(trip: suggestedTrip.trip, games: suggestedTrip.richGames) + TripDetailView(trip: suggestedTrip.trip) } } .sheet(isPresented: $showProPaywall) { @@ -315,7 +315,7 @@ struct SavedTripCard: View { var body: some View { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack(spacing: Theme.Spacing.md) { // Route preview icon @@ -555,7 +555,7 @@ struct SavedTripsListView: View { ForEach(Array(sortedTrips.enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { SavedTripListRow(trip: trip) } diff --git a/SportsTime/Features/Home/Views/Variants/Airbnb/HomeContent_Airbnb.swift b/SportsTime/Features/Home/Views/Variants/Airbnb/HomeContent_Airbnb.swift index 31371e8..dfbd0a5 100644 --- a/SportsTime/Features/Home/Views/Variants/Airbnb/HomeContent_Airbnb.swift +++ b/SportsTime/Features/Home/Views/Variants/Airbnb/HomeContent_Airbnb.swift @@ -166,7 +166,7 @@ struct HomeContent_Airbnb: View { ForEach(savedTrips.prefix(4)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { tripCard(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/AppleMaps/HomeContent_AppleMaps.swift b/SportsTime/Features/Home/Views/Variants/AppleMaps/HomeContent_AppleMaps.swift index 25280a5..adfd480 100644 --- a/SportsTime/Features/Home/Views/Variants/AppleMaps/HomeContent_AppleMaps.swift +++ b/SportsTime/Features/Home/Views/Variants/AppleMaps/HomeContent_AppleMaps.swift @@ -123,7 +123,7 @@ struct HomeContent_AppleMaps: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { recentRow(trip, isLast: index == min(2, savedTrips.count - 1)) } diff --git a/SportsTime/Features/Home/Views/Variants/ArtDeco/HomeContent_ArtDeco.swift b/SportsTime/Features/Home/Views/Variants/ArtDeco/HomeContent_ArtDeco.swift index e4718b4..c91ba48 100644 --- a/SportsTime/Features/Home/Views/Variants/ArtDeco/HomeContent_ArtDeco.swift +++ b/SportsTime/Features/Home/Views/Variants/ArtDeco/HomeContent_ArtDeco.swift @@ -424,7 +424,7 @@ struct HomeContent_ArtDeco: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack { decoDiamond diff --git a/SportsTime/Features/Home/Views/Variants/Brutalist/HomeContent_Brutalist.swift b/SportsTime/Features/Home/Views/Variants/Brutalist/HomeContent_Brutalist.swift index 13c0901..143c7dd 100644 --- a/SportsTime/Features/Home/Views/Variants/Brutalist/HomeContent_Brutalist.swift +++ b/SportsTime/Features/Home/Views/Variants/Brutalist/HomeContent_Brutalist.swift @@ -224,7 +224,7 @@ struct HomeContent_Brutalist: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack { Text(trip.name.uppercased()) diff --git a/SportsTime/Features/Home/Views/Variants/CarrotWeather/HomeContent_CarrotWeather.swift b/SportsTime/Features/Home/Views/Variants/CarrotWeather/HomeContent_CarrotWeather.swift index edabe84..8f65cf9 100644 --- a/SportsTime/Features/Home/Views/Variants/CarrotWeather/HomeContent_CarrotWeather.swift +++ b/SportsTime/Features/Home/Views/Variants/CarrotWeather/HomeContent_CarrotWeather.swift @@ -215,7 +215,7 @@ struct HomeContent_CarrotWeather: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { tripRow(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/Classic/HomeContent_Classic.swift b/SportsTime/Features/Home/Views/Variants/Classic/HomeContent_Classic.swift index 27a1c76..10a83d0 100644 --- a/SportsTime/Features/Home/Views/Variants/Classic/HomeContent_Classic.swift +++ b/SportsTime/Features/Home/Views/Variants/Classic/HomeContent_Classic.swift @@ -207,7 +207,7 @@ struct HomeContent_Classic: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { classicTripCard(savedTrip: savedTrip, trip: trip) } diff --git a/SportsTime/Features/Home/Views/Variants/DarkIndustrial/HomeContent_DarkIndustrial.swift b/SportsTime/Features/Home/Views/Variants/DarkIndustrial/HomeContent_DarkIndustrial.swift index 00e8694..9ac2ba3 100644 --- a/SportsTime/Features/Home/Views/Variants/DarkIndustrial/HomeContent_DarkIndustrial.swift +++ b/SportsTime/Features/Home/Views/Variants/DarkIndustrial/HomeContent_DarkIndustrial.swift @@ -376,7 +376,7 @@ struct HomeContent_DarkIndustrial: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack { Rectangle() diff --git a/SportsTime/Features/Home/Views/Variants/Fantastical/HomeContent_Fantastical.swift b/SportsTime/Features/Home/Views/Variants/Fantastical/HomeContent_Fantastical.swift index 7d0d089..90f4264 100644 --- a/SportsTime/Features/Home/Views/Variants/Fantastical/HomeContent_Fantastical.swift +++ b/SportsTime/Features/Home/Views/Variants/Fantastical/HomeContent_Fantastical.swift @@ -183,7 +183,7 @@ struct HomeContent_Fantastical: View { ForEach(savedTrips.prefix(4)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { eventRow(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/Flighty/HomeContent_Flighty.swift b/SportsTime/Features/Home/Views/Variants/Flighty/HomeContent_Flighty.swift index ad9d050..9d17c4a 100644 --- a/SportsTime/Features/Home/Views/Variants/Flighty/HomeContent_Flighty.swift +++ b/SportsTime/Features/Home/Views/Variants/Flighty/HomeContent_Flighty.swift @@ -193,7 +193,7 @@ struct HomeContent_Flighty: View { ForEach(savedTrips.prefix(2)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { tripCard(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/Glassmorphism/HomeContent_Glassmorphism.swift b/SportsTime/Features/Home/Views/Variants/Glassmorphism/HomeContent_Glassmorphism.swift index 5941a8e..e4438e2 100644 --- a/SportsTime/Features/Home/Views/Variants/Glassmorphism/HomeContent_Glassmorphism.swift +++ b/SportsTime/Features/Home/Views/Variants/Glassmorphism/HomeContent_Glassmorphism.swift @@ -284,7 +284,7 @@ struct HomeContent_Glassmorphism: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack(spacing: 16) { // Glow orb diff --git a/SportsTime/Features/Home/Views/Variants/LuxuryEditorial/HomeContent_LuxuryEditorial.swift b/SportsTime/Features/Home/Views/Variants/LuxuryEditorial/HomeContent_LuxuryEditorial.swift index 994ced5..a3d3b0d 100644 --- a/SportsTime/Features/Home/Views/Variants/LuxuryEditorial/HomeContent_LuxuryEditorial.swift +++ b/SportsTime/Features/Home/Views/Variants/LuxuryEditorial/HomeContent_LuxuryEditorial.swift @@ -283,7 +283,7 @@ struct HomeContent_LuxuryEditorial: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack(alignment: .top, spacing: 16) { // Number diff --git a/SportsTime/Features/Home/Views/Variants/MaximalistChaos/HomeContent_MaximalistChaos.swift b/SportsTime/Features/Home/Views/Variants/MaximalistChaos/HomeContent_MaximalistChaos.swift index 35ea5bc..4b5940c 100644 --- a/SportsTime/Features/Home/Views/Variants/MaximalistChaos/HomeContent_MaximalistChaos.swift +++ b/SportsTime/Features/Home/Views/Variants/MaximalistChaos/HomeContent_MaximalistChaos.swift @@ -392,7 +392,7 @@ struct HomeContent_MaximalistChaos: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { let colors = [skyBlue, hotOrange, electricPurple] let accentColor = colors[index % colors.count] diff --git a/SportsTime/Features/Home/Views/Variants/NeoBrutalist/HomeContent_NeoBrutalist.swift b/SportsTime/Features/Home/Views/Variants/NeoBrutalist/HomeContent_NeoBrutalist.swift index 2a1f83b..f93f05b 100644 --- a/SportsTime/Features/Home/Views/Variants/NeoBrutalist/HomeContent_NeoBrutalist.swift +++ b/SportsTime/Features/Home/Views/Variants/NeoBrutalist/HomeContent_NeoBrutalist.swift @@ -290,7 +290,7 @@ struct HomeContent_NeoBrutalist: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack { Rectangle() diff --git a/SportsTime/Features/Home/Views/Variants/NikeRunClub/HomeContent_NikeRunClub.swift b/SportsTime/Features/Home/Views/Variants/NikeRunClub/HomeContent_NikeRunClub.swift index 1c5ade2..783b209 100644 --- a/SportsTime/Features/Home/Views/Variants/NikeRunClub/HomeContent_NikeRunClub.swift +++ b/SportsTime/Features/Home/Views/Variants/NikeRunClub/HomeContent_NikeRunClub.swift @@ -169,7 +169,7 @@ struct HomeContent_NikeRunClub: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { activityRow(trip, index: index) } diff --git a/SportsTime/Features/Home/Views/Variants/Organic/HomeContent_Organic.swift b/SportsTime/Features/Home/Views/Variants/Organic/HomeContent_Organic.swift index 1d21ed3..fff3fd5 100644 --- a/SportsTime/Features/Home/Views/Variants/Organic/HomeContent_Organic.swift +++ b/SportsTime/Features/Home/Views/Variants/Organic/HomeContent_Organic.swift @@ -248,7 +248,7 @@ struct HomeContent_Organic: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack(spacing: 14) { // Organic dot cluster diff --git a/SportsTime/Features/Home/Views/Variants/Playful/HomeContent_Playful.swift b/SportsTime/Features/Home/Views/Variants/Playful/HomeContent_Playful.swift index 594d08c..c3c8dbe 100644 --- a/SportsTime/Features/Home/Views/Variants/Playful/HomeContent_Playful.swift +++ b/SportsTime/Features/Home/Views/Variants/Playful/HomeContent_Playful.swift @@ -304,7 +304,7 @@ struct HomeContent_Playful: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { let colors = [candyYellow, candyGreen, candyBlue] let accentColor = colors[index % colors.count] diff --git a/SportsTime/Features/Home/Views/Variants/RetroFuturism/HomeContent_RetroFuturism.swift b/SportsTime/Features/Home/Views/Variants/RetroFuturism/HomeContent_RetroFuturism.swift index dceb4cb..21f1201 100644 --- a/SportsTime/Features/Home/Views/Variants/RetroFuturism/HomeContent_RetroFuturism.swift +++ b/SportsTime/Features/Home/Views/Variants/RetroFuturism/HomeContent_RetroFuturism.swift @@ -303,7 +303,7 @@ struct HomeContent_RetroFuturism: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { HStack { Text(trip.name) diff --git a/SportsTime/Features/Home/Views/Variants/SeatGeek/HomeContent_SeatGeek.swift b/SportsTime/Features/Home/Views/Variants/SeatGeek/HomeContent_SeatGeek.swift index fe5140d..cb3a69b 100644 --- a/SportsTime/Features/Home/Views/Variants/SeatGeek/HomeContent_SeatGeek.swift +++ b/SportsTime/Features/Home/Views/Variants/SeatGeek/HomeContent_SeatGeek.swift @@ -162,7 +162,7 @@ struct HomeContent_SeatGeek: View { ForEach(savedTrips.prefix(4)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { yourTripCard(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/SoftPastel/HomeContent_SoftPastel.swift b/SportsTime/Features/Home/Views/Variants/SoftPastel/HomeContent_SoftPastel.swift index 3696789..ae49397 100644 --- a/SportsTime/Features/Home/Views/Variants/SoftPastel/HomeContent_SoftPastel.swift +++ b/SportsTime/Features/Home/Views/Variants/SoftPastel/HomeContent_SoftPastel.swift @@ -321,7 +321,7 @@ struct HomeContent_SoftPastel: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { let colors = [pastelPeach, pastelMint, pastelLavender] let accentColor = colors[index % colors.count] diff --git a/SportsTime/Features/Home/Views/Variants/Spotify/HomeContent_Spotify.swift b/SportsTime/Features/Home/Views/Variants/Spotify/HomeContent_Spotify.swift index a56e821..2894e2d 100644 --- a/SportsTime/Features/Home/Views/Variants/Spotify/HomeContent_Spotify.swift +++ b/SportsTime/Features/Home/Views/Variants/Spotify/HomeContent_Spotify.swift @@ -101,7 +101,7 @@ struct HomeContent_Spotify: View { ForEach(savedTrips.prefix(5)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { tripCoverCard(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/Strava/HomeContent_Strava.swift b/SportsTime/Features/Home/Views/Variants/Strava/HomeContent_Strava.swift index ba36252..3baaed6 100644 --- a/SportsTime/Features/Home/Views/Variants/Strava/HomeContent_Strava.swift +++ b/SportsTime/Features/Home/Views/Variants/Strava/HomeContent_Strava.swift @@ -210,7 +210,7 @@ struct HomeContent_Strava: View { ForEach(savedTrips.prefix(3)) { savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { activityCard(trip) } diff --git a/SportsTime/Features/Home/Views/Variants/SwissModernist/HomeContent_SwissModernist.swift b/SportsTime/Features/Home/Views/Variants/SwissModernist/HomeContent_SwissModernist.swift index d47ed1b..bc7fd74 100644 --- a/SportsTime/Features/Home/Views/Variants/SwissModernist/HomeContent_SwissModernist.swift +++ b/SportsTime/Features/Home/Views/Variants/SwissModernist/HomeContent_SwissModernist.swift @@ -304,7 +304,7 @@ struct HomeContent_SwissModernist: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { VStack(spacing: 0) { HStack(spacing: 24) { diff --git a/SportsTime/Features/Home/Views/Variants/Things3/HomeContent_Things3.swift b/SportsTime/Features/Home/Views/Variants/Things3/HomeContent_Things3.swift index c479cea..c60a3a6 100644 --- a/SportsTime/Features/Home/Views/Variants/Things3/HomeContent_Things3.swift +++ b/SportsTime/Features/Home/Views/Variants/Things3/HomeContent_Things3.swift @@ -162,7 +162,7 @@ struct HomeContent_Things3: View { ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { - TripDetailView(trip: trip, games: savedTrip.games) + TripDetailView(trip: trip) } label: { tripRow(trip) } diff --git a/SportsTime/Features/Polls/Views/PollDetailView.swift b/SportsTime/Features/Polls/Views/PollDetailView.swift index ffc3ddb..1be51ee 100644 --- a/SportsTime/Features/Polls/Views/PollDetailView.swift +++ b/SportsTime/Features/Polls/Views/PollDetailView.swift @@ -14,6 +14,7 @@ struct PollDetailView: View { @State private var showShareSheet = false @State private var showDeleteConfirmation = false @State private var showVotingSheet = false + @State private var selectedTrip: Trip? @State private var isOwner = false let pollId: UUID? @@ -123,12 +124,25 @@ struct PollDetailView: View { } message: { Text("This will permanently delete the poll and all votes. This action cannot be undone.") } + .sheet(item: $selectedTrip) { trip in + NavigationStack { + TripDetailView(trip: trip) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button("Done") { + selectedTrip = nil + } + } + } + } + } } @ViewBuilder private func pollContent(_ poll: TripPoll) -> some View { ScrollView { - VStack(spacing: 20) { + VStack(spacing: Theme.Spacing.lg) { // Share Code Card shareCodeCard(poll) @@ -143,85 +157,161 @@ struct PollDetailView: View { // Trip Previews tripPreviewsSection(poll) } - .padding() + .padding(.horizontal, Theme.Spacing.md) + .padding(.vertical, Theme.Spacing.lg) } + .background(Theme.backgroundGradient(colorScheme)) } @ViewBuilder private func shareCodeCard(_ poll: TripPoll) -> some View { - VStack(spacing: 8) { - Text("Share Code") - .font(.caption) - .foregroundStyle(.secondary) + VStack(spacing: Theme.Spacing.md) { + // Icon + ZStack { + Circle() + .fill(Theme.warmOrange.opacity(0.15)) + .frame(width: 56, height: 56) + Image(systemName: "link.circle.fill") + .font(.system(size: 28)) + .foregroundStyle(Theme.warmOrange) + } - Text(poll.shareCode) - .font(.system(size: 32, weight: .bold, design: .monospaced)) - .foregroundStyle(Theme.warmOrange) + VStack(spacing: Theme.Spacing.xs) { + Text("Share Code") + .font(.subheadline) + .foregroundStyle(Theme.textSecondary(colorScheme)) - Text("sportstime://poll/\(poll.shareCode)") - .font(.caption2) - .foregroundStyle(.secondary) + Text(poll.shareCode) + .font(.system(size: 36, weight: .bold, design: .monospaced)) + .foregroundStyle(Theme.warmOrange) + .tracking(4) + } + + // Copy button + Button { + UIPasteboard.general.string = poll.shareCode + } label: { + Label("Copy Code", systemImage: "doc.on.doc") + .font(.subheadline.weight(.medium)) + .foregroundStyle(Theme.warmOrange) + .padding(.horizontal, Theme.Spacing.md) + .padding(.vertical, Theme.Spacing.sm) + .background(Theme.warmOrange.opacity(0.1)) + .clipShape(Capsule()) + } } .frame(maxWidth: .infinity) - .padding() + .padding(Theme.Spacing.lg) .background(Theme.cardBackground(colorScheme)) - .clipShape(RoundedRectangle(cornerRadius: 12)) + .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large)) + .overlay( + RoundedRectangle(cornerRadius: Theme.CornerRadius.large) + .strokeBorder(Theme.warmOrange.opacity(0.2), lineWidth: 1) + ) } @ViewBuilder private var votingStatusCard: some View { - HStack { - VStack(alignment: .leading, spacing: 4) { - Text(viewModel.hasVoted ? "You voted" : "You haven't voted yet") - .font(.headline) + HStack(spacing: Theme.Spacing.md) { + // Status icon + ZStack { + Circle() + .fill(viewModel.hasVoted ? Theme.mlsGreen.opacity(0.15) : Theme.warmOrange.opacity(0.15)) + .frame(width: 44, height: 44) + Image(systemName: viewModel.hasVoted ? "checkmark.circle.fill" : "hand.raised.fill") + .font(.title3) + .foregroundStyle(viewModel.hasVoted ? Theme.mlsGreen : Theme.warmOrange) + } - Text("\(viewModel.votes.count) total votes") - .font(.subheadline) - .foregroundStyle(.secondary) + VStack(alignment: .leading, spacing: 2) { + Text(viewModel.hasVoted ? "You voted" : "Cast your vote") + .font(.headline) + .foregroundStyle(Theme.textPrimary(colorScheme)) + + HStack(spacing: Theme.Spacing.xs) { + Image(systemName: "person.2.fill") + .font(.caption2) + Text("\(viewModel.votes.count) vote\(viewModel.votes.count == 1 ? "" : "s")") + .font(.subheadline) + } + .foregroundStyle(Theme.textSecondary(colorScheme)) } Spacer() - Button(viewModel.hasVoted ? "Change Vote" : "Vote Now") { + Button { showVotingSheet = true + } label: { + Text(viewModel.hasVoted ? "Change" : "Vote") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.white) + .padding(.horizontal, Theme.Spacing.md) + .padding(.vertical, Theme.Spacing.sm) + .background(Theme.warmOrange) + .clipShape(Capsule()) } - .buttonStyle(.borderedProminent) - .tint(Theme.warmOrange) } - .padding() + .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) - .clipShape(RoundedRectangle(cornerRadius: 12)) + .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) } @ViewBuilder private func resultsSection(_ results: PollResults) -> some View { - VStack(alignment: .leading, spacing: 12) { - Text("Results") - .font(.headline) + VStack(alignment: .leading, spacing: Theme.Spacing.md) { + // Section header + HStack(spacing: Theme.Spacing.sm) { + Image(systemName: "chart.bar.fill") + .foregroundStyle(Theme.warmOrange) + Text("Results") + .font(.headline) + .foregroundStyle(Theme.textPrimary(colorScheme)) + } - ForEach(results.tripScores, id: \.tripIndex) { item in - let trip = results.poll.tripSnapshots[item.tripIndex] - ResultRow( - rank: results.tripScores.firstIndex { $0.tripIndex == item.tripIndex }! + 1, - tripName: trip.name, - score: item.score, - percentage: results.scorePercentage(for: item.tripIndex) - ) + VStack(spacing: Theme.Spacing.sm) { + ForEach(results.tripScores, id: \.tripIndex) { item in + let trip = results.poll.tripSnapshots[item.tripIndex] + let rank = results.tripScores.firstIndex { $0.tripIndex == item.tripIndex }! + 1 + ResultRow( + rank: rank, + tripName: trip.name, + score: item.score, + percentage: results.scorePercentage(for: item.tripIndex), + isLeader: rank == 1 && item.score > 0 + ) + } } } - .padding() + .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) - .clipShape(RoundedRectangle(cornerRadius: 12)) + .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) } @ViewBuilder private func tripPreviewsSection(_ poll: TripPoll) -> some View { - VStack(alignment: .leading, spacing: 12) { - Text("Trip Options") - .font(.headline) + VStack(alignment: .leading, spacing: Theme.Spacing.md) { + // Section header + HStack(spacing: Theme.Spacing.sm) { + Image(systemName: "map.fill") + .foregroundStyle(Theme.warmOrange) + Text("Trip Options") + .font(.headline) + .foregroundStyle(Theme.textPrimary(colorScheme)) + + Spacer() + + Text("\(poll.tripSnapshots.count) trips") + .font(.caption) + .foregroundStyle(Theme.textMuted(colorScheme)) + } ForEach(Array(poll.tripSnapshots.enumerated()), id: \.element.id) { index, trip in - TripPreviewCard(trip: trip, index: index + 1) + Button { + selectedTrip = trip + } label: { + TripPreviewCard(trip: trip, index: index + 1) + } + .buttonStyle(.plain) } } } @@ -241,43 +331,72 @@ struct PollDetailView: View { // MARK: - Result Row private struct ResultRow: View { + @Environment(\.colorScheme) private var colorScheme let rank: Int let tripName: String let score: Int let percentage: Double + var isLeader: Bool = false + + private var rankIcon: String { + switch rank { + case 1: return "trophy.fill" + case 2: return "medal.fill" + case 3: return "medal.fill" + default: return "\(rank).circle.fill" + } + } + + private var rankColor: Color { + switch rank { + case 1: return Theme.warmOrange + case 2: return .gray + case 3: return .brown + default: return .secondary + } + } var body: some View { - HStack(spacing: 12) { - Text("#\(rank)") - .font(.headline) - .foregroundStyle(rank == 1 ? Theme.warmOrange : .secondary) - .frame(width: 30) + HStack(spacing: Theme.Spacing.sm) { + // Rank badge + ZStack { + Circle() + .fill(rankColor.opacity(0.15)) + .frame(width: 36, height: 36) + Image(systemName: rankIcon) + .font(.subheadline) + .foregroundStyle(rankColor) + } VStack(alignment: .leading, spacing: 4) { Text(tripName) - .font(.subheadline) + .font(.subheadline.weight(isLeader ? .semibold : .regular)) + .foregroundStyle(Theme.textPrimary(colorScheme)) GeometryReader { geometry in ZStack(alignment: .leading) { - Rectangle() - .fill(Color.secondary.opacity(0.2)) - .frame(height: 8) - .clipShape(RoundedRectangle(cornerRadius: 4)) + RoundedRectangle(cornerRadius: 4) + .fill(Theme.cardBackgroundElevated(colorScheme)) + .frame(height: 6) - Rectangle() - .fill(rank == 1 ? Theme.warmOrange : Color.secondary) - .frame(width: geometry.size.width * percentage, height: 8) - .clipShape(RoundedRectangle(cornerRadius: 4)) + RoundedRectangle(cornerRadius: 4) + .fill(rankColor) + .frame(width: max(geometry.size.width * percentage, percentage > 0 ? 6 : 0), height: 6) } } - .frame(height: 8) + .frame(height: 6) } + // Score badge Text("\(score)") - .font(.caption) - .foregroundStyle(.secondary) - .frame(width: 40, alignment: .trailing) + .font(.subheadline.weight(.medium).monospacedDigit()) + .foregroundStyle(rankColor) + .padding(.horizontal, 10) + .padding(.vertical, 4) + .background(rankColor.opacity(0.1)) + .clipShape(Capsule()) } + .padding(.vertical, Theme.Spacing.xs) } } @@ -289,47 +408,59 @@ private struct TripPreviewCard: View { let index: Int var body: some View { - VStack(alignment: .leading, spacing: 8) { - HStack { - Text("Option \(index)") - .font(.caption) - .fontWeight(.semibold) - .foregroundStyle(.white) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background(Theme.warmOrange) - .clipShape(Capsule()) + HStack(spacing: 12) { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("Option \(index)") + .font(.caption) + .fontWeight(.semibold) + .foregroundStyle(.white) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(Theme.warmOrange) + .clipShape(Capsule()) - Text(trip.name) - .font(.headline) - } + Text(trip.name) + .font(.headline) + .foregroundStyle(.primary) + } - // Date range and duration - HStack { - Label(trip.formattedDateRange, systemImage: "calendar") - Spacer() - Label("\(trip.tripDuration) days", systemImage: "clock") - } - .font(.caption) - .foregroundStyle(.secondary) - - HStack { - Label("\(trip.stops.count) stops", systemImage: "mappin.and.ellipse") - Spacer() - Label("\(trip.stops.flatMap { $0.games }.count) games", systemImage: "sportscourt") - } - .font(.caption) - .foregroundStyle(.secondary) - - // Show cities - Text(trip.stops.map { $0.city }.joined(separator: " → ")) + // Date range and duration + HStack { + Label(trip.formattedDateRange, systemImage: "calendar") + Spacer() + Label("\(trip.tripDuration) days", systemImage: "clock") + } .font(.caption) .foregroundStyle(.secondary) - .lineLimit(2) + + HStack { + Label("\(trip.stops.count) stops", systemImage: "mappin.and.ellipse") + Spacer() + Label("\(trip.stops.flatMap { $0.games }.count) games", systemImage: "sportscourt") + } + .font(.caption) + .foregroundStyle(.secondary) + + // Show cities + Text(trip.stops.map { $0.city }.joined(separator: " → ")) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(2) + } + + Image(systemName: "chevron.right") + .font(.caption) + .fontWeight(.semibold) + .foregroundStyle(.tertiary) } .padding() .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: 12)) + .overlay( + RoundedRectangle(cornerRadius: 12) + .strokeBorder(Color.secondary.opacity(0.1), lineWidth: 1) + ) } } diff --git a/SportsTime/Features/Trip/Views/TripDetailView.swift b/SportsTime/Features/Trip/Views/TripDetailView.swift index a3fabdc..a6bae3c 100644 --- a/SportsTime/Features/Trip/Views/TripDetailView.swift +++ b/SportsTime/Features/Trip/Views/TripDetailView.swift @@ -12,7 +12,7 @@ struct TripDetailView: View { @Environment(\.colorScheme) private var colorScheme let trip: Trip - let games: [String: RichGame] + private let providedGames: [String: RichGame]? @Query private var savedTrips: [SavedTrip] @State private var showProPaywall = false @@ -25,10 +25,29 @@ struct TripDetailView: View { @State private var isSaved = false @State private var routePolylines: [MKPolyline] = [] @State private var isLoadingRoutes = false + @State private var loadedGames: [String: RichGame] = [:] + @State private var isLoadingGames = false private let exportService = ExportService() private let dataProvider = AppDataProvider.shared + /// Games dictionary - uses provided games if available, otherwise uses loaded games + private var games: [String: RichGame] { + providedGames ?? loadedGames + } + + /// Initialize with trip and games dictionary (existing callers) + init(trip: Trip, games: [String: RichGame]) { + self.trip = trip + self.providedGames = games + } + + /// Initialize with just trip - games will be loaded from AppDataProvider + init(trip: Trip) { + self.trip = trip + self.providedGames = nil + } + var body: some View { ScrollView { VStack(spacing: 0) { @@ -94,6 +113,9 @@ struct TripDetailView: View { .onAppear { checkIfSaved() } + .task { + await loadGamesIfNeeded() + } .overlay { if isExporting { exportProgressOverlay @@ -300,7 +322,15 @@ struct TripDetailView: View { .font(.title2) .foregroundStyle(Theme.textPrimary(colorScheme)) - ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in + if isLoadingGames { + HStack { + Spacer() + ProgressView("Loading games...") + .padding(.vertical, Theme.Spacing.xl) + Spacer() + } + } else { + ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in switch section { case .day(let dayNumber, let date, let gamesOnDay): DaySection( @@ -313,6 +343,7 @@ struct TripDetailView: View { TravelSection(segment: segment) .staggeredAnimation(index: index) } + } } } } @@ -493,6 +524,34 @@ struct TripDetailView: View { // MARK: - Actions + /// Load games from AppDataProvider if not provided + private func loadGamesIfNeeded() async { + // Skip if games were provided + guard providedGames == nil else { return } + + // Collect all game IDs from the trip + let gameIds = trip.stops.flatMap { $0.games } + guard !gameIds.isEmpty else { return } + + isLoadingGames = true + + // Load RichGame data from AppDataProvider + var loaded: [String: RichGame] = [:] + for gameId in gameIds { + do { + if let game = try await dataProvider.fetchGame(by: gameId), + let richGame = dataProvider.richGame(from: game) { + loaded[gameId] = richGame + } + } catch { + // Skip games that fail to load + } + } + + loadedGames = loaded + isLoadingGames = false + } + private func exportPDF() async { isExporting = true exportProgress = nil @@ -911,8 +970,7 @@ struct ShareSheet: UIViewControllerRepresentable { startLocation: LocationInput(name: "New York"), endLocation: LocationInput(name: "Chicago") ) - ), - games: [:] + ) ) } } diff --git a/SportsTime/Features/Trip/Views/TripOptionsView.swift b/SportsTime/Features/Trip/Views/TripOptionsView.swift index ff2ce30..6cf12a7 100644 --- a/SportsTime/Features/Trip/Views/TripOptionsView.swift +++ b/SportsTime/Features/Trip/Views/TripOptionsView.swift @@ -292,7 +292,7 @@ struct TripOptionsView: View { .themedBackground() .navigationDestination(isPresented: $showTripDetail) { if let trip = selectedTrip { - TripDetailView(trip: trip, games: games) + TripDetailView(trip: trip) } } .onChange(of: showTripDetail) { _, isShowing in diff --git a/TO-DOS.md b/TO-DOS.md index b24b246..170477e 100644 --- a/TO-DOS.md +++ b/TO-DOS.md @@ -40,14 +40,4 @@ sharing needs to completely overhauled. should be able to share a trip summary, // bugs - fucking game show at 7 am ... the fuck? all all trips view when choosing "packed" "moderate" "relaxed" the capsule the option is in does a weird animation that looks off. -- Text on achievements is not wrapping and is being cutoff -- Remove username on share -- more on share doesn’t do anything -- Created a poll and when I tap on it I get poll not found? - group poll refreshed every time I go to screen, should update in bg and pull to refresh? -- User who made it should be able to delete it -- Trip details aren’t showing -- Home Screen quick start should be removed -- Today games aren’t highlighted in the schedule tab -- features trip showing both nba and nhl but only has a nhl game -