feat(sharing): implement unified sharing system for social media
Replace old ProgressCardGenerator with protocol-based sharing architecture supporting trips, achievements, and stadium progress. Features 8 color themes, Instagram Stories optimization (1080x1920), and reusable card components with map snapshots. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,12 @@ final class ProgressViewModel {
|
||||
leagueProgress.stadiumsRemaining
|
||||
}
|
||||
|
||||
/// Count of trips for the selected sport (stub - can be enhanced)
|
||||
var tripCount: Int {
|
||||
// TODO: Fetch saved trips count from SwiftData
|
||||
0
|
||||
}
|
||||
|
||||
/// Recent visits sorted by date
|
||||
var recentVisits: [VisitSummary] {
|
||||
visits
|
||||
|
||||
@@ -36,6 +36,20 @@ struct AchievementsListView: View {
|
||||
}
|
||||
.themedBackground()
|
||||
.navigationTitle("Achievements")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
if !earnedAchievements.isEmpty {
|
||||
ShareButton(
|
||||
content: AchievementCollectionContent(
|
||||
achievements: earnedAchievements,
|
||||
year: Calendar.current.component(.year, from: Date())
|
||||
),
|
||||
style: .icon
|
||||
)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await loadAchievements()
|
||||
}
|
||||
@@ -184,6 +198,10 @@ struct AchievementsListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var earnedAchievements: [AchievementProgress] {
|
||||
achievements.filter { $0.isEarned }
|
||||
}
|
||||
|
||||
private var filteredAchievements: [AchievementProgress] {
|
||||
let filtered: [AchievementProgress]
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ struct ProgressTabView: View {
|
||||
@State private var viewModel = ProgressViewModel()
|
||||
@State private var showVisitSheet = false
|
||||
@State private var showPhotoImport = false
|
||||
@State private var showShareSheet = false
|
||||
@State private var selectedStadium: Stadium?
|
||||
@State private var selectedVisitId: UUID?
|
||||
|
||||
@@ -65,12 +64,12 @@ struct ProgressTabView: View {
|
||||
.themedBackground()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
showShareSheet = true
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
ShareButton(
|
||||
progress: viewModel.leagueProgress,
|
||||
tripCount: viewModel.tripCount,
|
||||
style: .icon
|
||||
)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
@@ -125,9 +124,6 @@ struct ProgressTabView: View {
|
||||
)
|
||||
.presentationDetents([.medium])
|
||||
}
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
ProgressShareView(progress: viewModel.leagueProgress)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - League Selector
|
||||
|
||||
@@ -18,9 +18,7 @@ struct TripDetailView: View {
|
||||
@State private var showProPaywall = false
|
||||
@State private var selectedDay: ItineraryDay?
|
||||
@State private var showExportSheet = false
|
||||
@State private var showShareSheet = false
|
||||
@State private var exportURL: URL?
|
||||
@State private var shareURL: URL?
|
||||
@State private var isExporting = false
|
||||
@State private var exportProgress: PDFAssetPrefetcher.PrefetchProgress?
|
||||
@State private var mapCameraPosition: MapCameraPosition = .automatic
|
||||
@@ -63,12 +61,8 @@ struct TripDetailView: View {
|
||||
.toolbarBackground(Theme.cardBackground(colorScheme), for: .navigationBar)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .primaryAction) {
|
||||
Button {
|
||||
shareTrip()
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
ShareButton(trip: trip, style: .icon)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
|
||||
Button {
|
||||
if StoreManager.shared.isPro {
|
||||
@@ -94,13 +88,6 @@ struct TripDetailView: View {
|
||||
ShareSheet(items: [url])
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
if let url = shareURL {
|
||||
ShareSheet(items: [url])
|
||||
} else {
|
||||
ShareSheet(items: [trip.name, trip.formattedDateRange])
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showProPaywall) {
|
||||
PaywallView()
|
||||
}
|
||||
@@ -523,11 +510,6 @@ struct TripDetailView: View {
|
||||
isExporting = false
|
||||
}
|
||||
|
||||
private func shareTrip() {
|
||||
shareURL = exportService.shareTrip(trip)
|
||||
showShareSheet = true
|
||||
}
|
||||
|
||||
private func toggleSaved() {
|
||||
if isSaved {
|
||||
unsaveTrip()
|
||||
|
||||
Reference in New Issue
Block a user