Files
Sportstime/SportsTime/Export/Sharing/ProgressCardGenerator.swift
Trey t fe36f99bca 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>
2026-01-14 08:54:37 -06:00

116 lines
3.3 KiB
Swift

//
// ProgressCardGenerator.swift
// SportsTime
//
// Generates shareable stadium progress cards.
//
import SwiftUI
import UIKit
// MARK: - Progress Share Content
struct ProgressShareContent: ShareableContent {
let progress: LeagueProgress
let tripCount: Int
let username: String?
var cardType: ShareCardType { .stadiumProgress }
@MainActor
func render(theme: ShareTheme) async throws -> UIImage {
let mapGenerator = ShareMapSnapshotGenerator()
let mapSnapshot = await mapGenerator.generateProgressMap(
visited: progress.stadiumsVisited,
remaining: progress.stadiumsRemaining,
theme: theme
)
let cardView = ProgressCardView(
progress: progress,
tripCount: tripCount,
username: username,
theme: theme,
mapSnapshot: mapSnapshot
)
let renderer = ImageRenderer(content: cardView)
renderer.scale = 3.0
guard let image = renderer.uiImage else {
throw ShareError.renderingFailed
}
return image
}
}
// MARK: - Progress Card View
private struct ProgressCardView: View {
let progress: LeagueProgress
let tripCount: Int
let username: String?
let theme: ShareTheme
let mapSnapshot: UIImage?
var body: some View {
ZStack {
ShareCardBackground(theme: theme)
VStack(spacing: 40) {
ShareCardHeader(
title: "\(progress.sport.displayName) Stadium Quest",
sport: progress.sport,
theme: theme
)
Spacer()
// Progress ring
ShareProgressRing(
current: progress.visitedStadiums,
total: progress.totalStadiums,
theme: theme
)
Text("\(Int(progress.completionPercentage))% Complete")
.font(.system(size: 28, weight: .medium))
.foregroundStyle(theme.secondaryTextColor)
// Stats row
ShareStatsRow(
stats: [
(value: "\(progress.visitedStadiums)", label: "visited"),
(value: "\(progress.totalStadiums - progress.visitedStadiums)", label: "remain"),
(value: "\(tripCount)", label: "trips")
],
theme: theme
)
// Map
if let snapshot = mapSnapshot {
Image(uiImage: snapshot)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 960)
.clipShape(RoundedRectangle(cornerRadius: 20))
.overlay {
RoundedRectangle(cornerRadius: 20)
.stroke(theme.accentColor.opacity(0.3), lineWidth: 2)
}
}
Spacer()
ShareCardFooter(theme: theme, username: username)
}
.padding(ShareCardDimensions.padding)
}
.frame(
width: ShareCardDimensions.cardSize.width,
height: ShareCardDimensions.cardSize.height
)
}
}