feat: fix travel placement bug, add theme-based alternate icons, fix animated background crash
- Fix repeat-city travel placement: use stop indices instead of global city name matching so Follow Team trips with repeat cities show travel correctly - Add TravelPlacement helper and regression tests (7 tests) - Add alternate app icons for each theme, auto-switch on theme change - Fix index-out-of-range crash in AnimatedSportsBackground (19 configs, was iterating 20) - Add marketing video configs, engine, and new video components - Add docs and data exports Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ struct AnimatedSportsBackground: View {
|
||||
RouteMapLayer(animate: animate)
|
||||
|
||||
// Floating sports icons with gentle glow
|
||||
ForEach(0..<20, id: \.self) { index in
|
||||
ForEach(0..<AnimatedSportsIcon.configs.count, id: \.self) { index in
|
||||
AnimatedSportsIcon(index: index, animate: animate)
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ struct AnimatedSportsIcon: View {
|
||||
let animate: Bool
|
||||
@State private var glowOpacity: Double = 0
|
||||
|
||||
private let configs: [(x: CGFloat, y: CGFloat, icon: String, rotation: Double, scale: CGFloat)] = [
|
||||
static let configs: [(x: CGFloat, y: CGFloat, icon: String, rotation: Double, scale: CGFloat)] = [
|
||||
// Edge icons
|
||||
(0.06, 0.08, "football.fill", -15, 0.85),
|
||||
(0.94, 0.1, "basketball.fill", 12, 0.8),
|
||||
@@ -133,7 +133,7 @@ struct AnimatedSportsIcon: View {
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
let config = configs[index]
|
||||
let config = Self.configs[index]
|
||||
|
||||
GeometryReader { geo in
|
||||
ZStack {
|
||||
|
||||
@@ -34,6 +34,19 @@ enum AppTheme: String, CaseIterable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
/// The alternate icon name in the asset catalog, or nil for the default (teal).
|
||||
var alternateIconName: String? {
|
||||
switch self {
|
||||
case .teal: return nil
|
||||
case .orbit: return "AppIcon-orbit"
|
||||
case .retro: return "AppIcon-retro"
|
||||
case .clutch: return "AppIcon-clutch"
|
||||
case .monochrome: return "AppIcon-monochrome"
|
||||
case .sunset: return "AppIcon-sunset"
|
||||
case .midnight: return "AppIcon-midnight"
|
||||
}
|
||||
}
|
||||
|
||||
var previewColors: [Color] {
|
||||
switch self {
|
||||
case .teal: return [Color(hex: "4ECDC4"), Color(hex: "1A535C"), Color(hex: "FFE66D")]
|
||||
@@ -56,9 +69,16 @@ final class ThemeManager {
|
||||
var currentTheme: AppTheme {
|
||||
didSet {
|
||||
UserDefaults.standard.set(currentTheme.rawValue, forKey: "selectedTheme")
|
||||
updateAppIcon(for: currentTheme)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAppIcon(for theme: AppTheme) {
|
||||
let iconName = theme.alternateIconName
|
||||
guard UIApplication.shared.alternateIconName != iconName else { return }
|
||||
UIApplication.shared.setAlternateIconName(iconName)
|
||||
}
|
||||
|
||||
private init() {
|
||||
if let saved = UserDefaults.standard.string(forKey: "selectedTheme"),
|
||||
let theme = AppTheme(rawValue: saved) {
|
||||
|
||||
Reference in New Issue
Block a user