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>
116 lines
3.3 KiB
Swift
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
|
|
)
|
|
}
|
|
}
|