- Add VoiceOver labels, hints, and element grouping across all 60+ views - Add Reduce Motion support (Theme.Animation.prefersReducedMotion) to all animations - Replace fixed font sizes with semantic Dynamic Type styles - Hide decorative elements from VoiceOver with .accessibilityHidden(true) - Add .minimumHitTarget() modifier ensuring 44pt touch targets - Add AccessibilityAnnouncer utility for VoiceOver announcements - Improve color contrast values in Theme.swift for WCAG AA compliance - Extract CloudKitContainerConfig for explicit container identity - Remove PostHog debug console log from AnalyticsManager Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
78 lines
2.3 KiB
Swift
78 lines
2.3 KiB
Swift
//
|
|
// ShareButton.swift
|
|
// SportsTime
|
|
//
|
|
// Contextual share button component.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct ShareButton<Content: ShareableContent>: View {
|
|
let content: Content
|
|
var style: ShareButtonStyle = .icon
|
|
|
|
@State private var showPreview = false
|
|
|
|
var body: some View {
|
|
Button {
|
|
showPreview = true
|
|
if let tripContent = content as? TripShareContent {
|
|
AnalyticsManager.shared.track(.tripShared(tripId: tripContent.trip.id.uuidString))
|
|
} else if content is ProgressShareContent {
|
|
AnalyticsManager.shared.track(.progressCardShared(sport: "unknown"))
|
|
}
|
|
} label: {
|
|
switch style {
|
|
case .icon:
|
|
Image(systemName: "square.and.arrow.up")
|
|
.accessibilityLabel("Share")
|
|
case .labeled:
|
|
Label("Share", systemImage: "square.and.arrow.up")
|
|
case .pill:
|
|
HStack(spacing: 4) {
|
|
Image(systemName: "square.and.arrow.up")
|
|
Text("Share")
|
|
}
|
|
.font(.subheadline.weight(.medium))
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 6)
|
|
.background(Theme.warmOrange)
|
|
.foregroundStyle(.white)
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
.sheet(isPresented: $showPreview) {
|
|
SharePreviewView(content: content)
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ShareButtonStyle {
|
|
case icon
|
|
case labeled
|
|
case pill
|
|
}
|
|
|
|
// MARK: - Convenience Initializers
|
|
|
|
extension ShareButton where Content == TripShareContent {
|
|
init(trip: Trip, style: ShareButtonStyle = .icon) {
|
|
self.content = TripShareContent(trip: trip)
|
|
self.style = style
|
|
}
|
|
}
|
|
|
|
extension ShareButton where Content == ProgressShareContent {
|
|
init(progress: LeagueProgress, tripCount: Int = 0, style: ShareButtonStyle = .icon) {
|
|
self.content = ProgressShareContent(progress: progress, tripCount: tripCount)
|
|
self.style = style
|
|
}
|
|
}
|
|
|
|
extension ShareButton where Content == AchievementSpotlightContent {
|
|
init(achievement: AchievementProgress, style: ShareButtonStyle = .icon) {
|
|
self.content = AchievementSpotlightContent(achievement: achievement)
|
|
self.style = style
|
|
}
|
|
}
|