feat: enforce custom Theme colors app-wide, add debug sample trips and poll
Replace all system colors (.secondary, Color(.secondarySystemBackground), etc.) with Theme.textPrimary/textSecondary/textMuted/cardBackground/ surfaceGlow across 13 views. Remove PostHog debug logging. Add debug settings for sample trips and hardcoded group poll preview. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -93,9 +93,7 @@ private struct CategoryPill: View {
|
||||
if isSelected {
|
||||
return Theme.warmOrange.opacity(0.2)
|
||||
} else {
|
||||
return colorScheme == .dark
|
||||
? Color.white.opacity(0.08)
|
||||
: Color.black.opacity(0.04)
|
||||
return Theme.cardBackgroundElevated(colorScheme)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +101,7 @@ private struct CategoryPill: View {
|
||||
if isSelected {
|
||||
return Theme.warmOrange.opacity(0.08)
|
||||
} else {
|
||||
return colorScheme == .dark
|
||||
? Color.white.opacity(0.03)
|
||||
: Color.black.opacity(0.02)
|
||||
return Theme.cardBackground(colorScheme)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ struct AddItemSheet: View {
|
||||
// Search field
|
||||
HStack {
|
||||
Image(systemName: "magnifyingglass")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.accessibilityHidden(true)
|
||||
TextField("Search for a place...", text: $searchQuery)
|
||||
.textFieldStyle(.plain)
|
||||
@@ -155,14 +155,14 @@ struct AddItemSheet: View {
|
||||
selectedPlace = nil
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
}
|
||||
.minimumHitTarget()
|
||||
.accessibilityLabel("Clear search")
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background(Color(.systemGray6))
|
||||
.background(Theme.cardBackgroundElevated(colorScheme))
|
||||
.cornerRadius(10)
|
||||
|
||||
// Search results
|
||||
@@ -181,7 +181,7 @@ struct AddItemSheet: View {
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: 300)
|
||||
.background(Color(.systemBackground))
|
||||
.background(Theme.cardBackground(colorScheme))
|
||||
.cornerRadius(10)
|
||||
} else if !searchQuery.isEmpty && !isSearching {
|
||||
Text("No results found")
|
||||
|
||||
@@ -12,6 +12,7 @@ import SwiftUI
|
||||
|
||||
/// Renders a single timeline item (stop, travel, or rest).
|
||||
struct TimelineItemView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let item: TimelineItem
|
||||
let games: [String: RichGame]
|
||||
let isFirst: Bool
|
||||
@@ -69,7 +70,7 @@ struct TimelineItemView: View {
|
||||
}
|
||||
|
||||
private var connectorColor: Color {
|
||||
Color.secondary.opacity(0.3)
|
||||
Theme.surfaceGlow(colorScheme)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@@ -124,6 +125,7 @@ struct TimelineItemView: View {
|
||||
// MARK: - Stop Item Content
|
||||
|
||||
struct StopItemContent: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let stop: ItineraryStop
|
||||
let games: [String: RichGame]
|
||||
|
||||
@@ -141,14 +143,14 @@ struct StopItemContent: View {
|
||||
if !stop.state.isEmpty {
|
||||
Text(stop.state)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(stop.arrivalDate.formatted(date: .abbreviated, time: .omitted))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
// Games
|
||||
@@ -159,12 +161,12 @@ struct StopItemContent: View {
|
||||
} else {
|
||||
Text(stop.hasGames ? "Game details loading..." : "Waypoint")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.italic()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.background(Theme.cardBackground(colorScheme))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
}
|
||||
@@ -172,6 +174,7 @@ struct StopItemContent: View {
|
||||
// MARK: - Travel Item Content
|
||||
|
||||
struct TravelItemContent: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let segment: TravelSegment
|
||||
|
||||
var body: some View {
|
||||
@@ -182,25 +185,25 @@ struct TravelItemContent: View {
|
||||
.fontWeight(.medium)
|
||||
|
||||
Text("\u{2022}")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.accessibilityHidden(true)
|
||||
|
||||
Text(segment.formattedDistance)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
Text("\u{2022}")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.accessibilityHidden(true)
|
||||
|
||||
Text(segment.formattedDuration)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
Text("\(segment.fromLocation.name) \u{2192} \(segment.toLocation.name)")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
.accessibilityLabel("\(segment.fromLocation.name) to \(segment.toLocation.name)")
|
||||
|
||||
// EV Charging stops if applicable
|
||||
@@ -211,13 +214,13 @@ struct TravelItemContent: View {
|
||||
.accessibilityHidden(true)
|
||||
Text("\(segment.evChargingStops.count) charging stop(s)")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 12)
|
||||
.background(Color(.tertiarySystemBackground))
|
||||
.background(Theme.cardBackgroundElevated(colorScheme))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
}
|
||||
@@ -225,6 +228,7 @@ struct TravelItemContent: View {
|
||||
// MARK: - Rest Item Content
|
||||
|
||||
struct RestItemContent: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let rest: RestDay
|
||||
|
||||
var body: some View {
|
||||
@@ -238,17 +242,17 @@ struct RestItemContent: View {
|
||||
|
||||
Text(rest.date.formatted(date: .abbreviated, time: .omitted))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
Text(rest.location.name)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
if let notes = rest.notes {
|
||||
Text(notes)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.italic()
|
||||
}
|
||||
}
|
||||
@@ -262,6 +266,7 @@ struct RestItemContent: View {
|
||||
// MARK: - Timeline Game Row
|
||||
|
||||
struct TimelineGameRow: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let richGame: RichGame
|
||||
|
||||
var body: some View {
|
||||
@@ -286,7 +291,7 @@ struct TimelineGameRow: View {
|
||||
Text(richGame.stadium.name)
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -325,6 +330,7 @@ struct TimelineView: View {
|
||||
|
||||
/// Horizontal scrolling timeline for compact display.
|
||||
struct HorizontalTimelineView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let option: ItineraryOption
|
||||
let games: [String: RichGame]
|
||||
|
||||
@@ -355,19 +361,19 @@ struct HorizontalTimelineView: View {
|
||||
if item.isTravel {
|
||||
// Travel already shows direction, minimal connector
|
||||
Rectangle()
|
||||
.fill(Color.secondary.opacity(0.3))
|
||||
.fill(Theme.surfaceGlow(colorScheme))
|
||||
.frame(width: 20, height: 2)
|
||||
} else {
|
||||
// Standard connector with arrow
|
||||
HStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.fill(Color.secondary.opacity(0.3))
|
||||
.fill(Theme.surfaceGlow(colorScheme))
|
||||
.frame(width: 16, height: 2)
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
Rectangle()
|
||||
.fill(Color.secondary.opacity(0.3))
|
||||
.fill(Theme.surfaceGlow(colorScheme))
|
||||
.frame(width: 16, height: 2)
|
||||
}
|
||||
}
|
||||
@@ -377,6 +383,7 @@ struct HorizontalTimelineView: View {
|
||||
// MARK: - Horizontal Timeline Item View
|
||||
|
||||
struct HorizontalTimelineItemView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let item: TimelineItem
|
||||
let games: [String: RichGame]
|
||||
|
||||
@@ -386,7 +393,7 @@ struct HorizontalTimelineItemView: View {
|
||||
|
||||
Text(shortLabel)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
.lineLimit(1)
|
||||
.frame(width: 60)
|
||||
}
|
||||
@@ -405,7 +412,7 @@ struct HorizontalTimelineItemView: View {
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
.frame(width: 44, height: 44)
|
||||
.background(Circle().fill(Color(.secondarySystemBackground)))
|
||||
.background(Circle().fill(Theme.cardBackgroundElevated(colorScheme)))
|
||||
|
||||
case .travel(let segment):
|
||||
Image(systemName: segment.travelMode == .drive ? "car.fill" : "airplane")
|
||||
|
||||
@@ -24,6 +24,7 @@ struct LocationSearchSheet: View {
|
||||
let onAdd: (LocationInput) -> Void
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@State private var searchText = ""
|
||||
@State private var searchResults: [LocationSearchResult] = []
|
||||
@State private var isSearching = false
|
||||
@@ -47,7 +48,7 @@ struct LocationSearchSheet: View {
|
||||
// Search field
|
||||
HStack {
|
||||
Image(systemName: "magnifyingglass")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
.accessibilityHidden(true)
|
||||
TextField("Search cities, addresses, places...", text: $searchText)
|
||||
.textFieldStyle(.plain)
|
||||
@@ -60,14 +61,14 @@ struct LocationSearchSheet: View {
|
||||
searchResults = []
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
}
|
||||
.minimumHitTarget()
|
||||
.accessibilityLabel("Clear search")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.background(Theme.cardBackgroundElevated(colorScheme))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.padding()
|
||||
|
||||
@@ -91,26 +92,28 @@ struct LocationSearchSheet: View {
|
||||
.accessibilityHidden(true)
|
||||
VStack(alignment: .leading) {
|
||||
Text(result.name)
|
||||
.foregroundStyle(.primary)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
if !result.address.isEmpty {
|
||||
Text(result.address)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Image(systemName: "plus.circle")
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.themedBackground()
|
||||
.navigationTitle(navigationTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
|
||||
Reference in New Issue
Block a user