Files
Reflect/Shared/Views/SharingTemplates/Variations/MonthTotalVariations.swift
Trey t bfef0a4472 Add sharing style picker for design variation selection
Users can now swipe between design variations (e.g. Gradient vs Color Block)
when sharing from month/year views and the sharing templates list. Removes
#if DEBUG wrappers from variation files and disables auto-start demo animation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 09:26:21 -06:00

222 lines
7.8 KiB
Swift

//
// MonthTotalVariations.swift
// Feels
//
// 3 design variations for the Month Total sharing template.
//
import SwiftUI
// MARK: - V1 "Clean Calendar" Calendar-style grid, Health-app feel
struct MonthTotalV1: View {
let moodMetrics: [MoodMetrics]
let moodEntries: [MoodEntryModel]
let month: Int
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
private var moodTint: MoodTints = .Default
let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 7)
var image: UIImage {
shareView.asImage(size: CGSize(width: 666, height: 1190))
}
var shareView: some View {
ZStack {
Color.white
VStack(spacing: 0) {
Spacer().frame(height: 70)
// Header
VStack(spacing: 8) {
Text(Random.monthName(fromMonthInt: month))
.font(.system(size: 36, weight: .bold, design: .rounded))
.foregroundColor(Color(hex: "1C1C1E"))
Text("\(moodEntries.count) DAYS TRACKED")
.font(.system(size: 14, weight: .medium, design: .rounded))
.foregroundColor(Color(hex: "8E8E93"))
.tracking(2)
}
Spacer().frame(height: 40)
// Calendar grid
LazyVGrid(columns: columns, spacing: 8) {
ForEach(moodEntries) { entry in
RoundedRectangle(cornerRadius: 10)
.fill(moodTint.color(forMood: entry.mood))
.frame(height: 70)
.overlay(
VStack(spacing: 4) {
entry.mood.icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.foregroundColor(.white)
Text(DateFormattingCache.shared.string(for: entry.forDate ?? Date(), format: .day))
.font(.system(size: 12, weight: .semibold, design: .rounded))
.foregroundColor(.white.opacity(0.8))
}
)
}
}
.padding(.horizontal, 32)
Spacer().frame(height: 40)
// Stats row
HStack(spacing: 0) {
ForEach(moodMetrics.sorted(by: { $0.mood.rawValue > $1.mood.rawValue }), id: \.mood) { metric in
VStack(spacing: 8) {
Capsule()
.fill(moodTint.color(forMood: metric.mood))
.frame(width: 40, height: 6)
Text("\(metric.percent, specifier: "%.0f")%")
.font(.system(size: 18, weight: .bold, design: .rounded))
.foregroundColor(Color(hex: "1C1C1E"))
Text(metric.mood.strValue)
.font(.system(size: 11, weight: .medium))
.foregroundColor(Color(hex: "8E8E93"))
}
.frame(maxWidth: .infinity)
}
}
.padding(.horizontal, 24)
Spacer()
VStack(spacing: 10) {
Image("FeelsAppIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 96, height: 96)
.clipShape(RoundedRectangle(cornerRadius: 22))
Text("Feels")
.font(.system(size: 28, weight: .medium, design: .rounded))
.foregroundColor(Color(hex: "C7C7CC"))
}
Spacer().frame(height: 50)
}
}
.frame(width: 666, height: 1190)
}
var body: some View { shareView }
}
// MARK: - V5 "Stacked Bars" Full-width mood color bars, white overlays
struct MonthTotalV5: View {
let moodMetrics: [MoodMetrics]
let moodEntries: [MoodEntryModel]
let month: Int
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
private var moodTint: MoodTints = .Default
let columns = Array(repeating: GridItem(.flexible(), spacing: 3), count: 7)
var image: UIImage {
shareView.asImage(size: CGSize(width: 666, height: 1190))
}
var shareView: some View {
let sorted = moodMetrics.sorted(by: { $0.mood.rawValue > $1.mood.rawValue })
return VStack(spacing: 0) {
// Header dark band
ZStack {
Color(hex: "1C1C1E")
HStack {
Text(Random.monthName(fromMonthInt: month).uppercased())
.font(.system(size: 18, weight: .heavy, design: .rounded))
.foregroundColor(.white)
.tracking(4)
Spacer()
Text("\(moodEntries.count)")
.font(.system(size: 28, weight: .heavy, design: .rounded))
.foregroundColor(.white)
}
.padding(.horizontal, 32)
}
.frame(height: 80)
// Mini grid of all days
ZStack {
Color(hex: "1C1C1E")
LazyVGrid(columns: columns, spacing: 3) {
ForEach(moodEntries) { entry in
Rectangle()
.fill(moodTint.color(forMood: entry.mood))
.frame(height: 50)
}
}
.padding(24)
}
// Color percentage blocks
ForEach(sorted, id: \.mood) { metric in
let barHeight: CGFloat = max(CGFloat(metric.percent / 100) * 400, 50)
ZStack {
moodTint.color(forMood: metric.mood)
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(metric.mood.strValue.uppercased())
.font(.system(size: 13, weight: .bold, design: .rounded))
.tracking(2)
Text("\(metric.total) days")
.font(.system(size: 11, weight: .medium))
.opacity(0.7)
}
Spacer()
Text("\(metric.percent, specifier: "%.0f")%")
.font(.system(size: 32, weight: .heavy, design: .rounded))
}
.foregroundColor(.white)
.padding(.horizontal, 32)
}
.frame(height: barHeight)
}
// Footer dark band fills remaining space
ZStack {
Color(hex: "1C1C1E")
VStack(spacing: 10) {
Image("FeelsAppIcon")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 96, height: 96)
.clipShape(RoundedRectangle(cornerRadius: 22))
Text("Feels")
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(.white.opacity(0.3))
}
}
.frame(maxHeight: .infinity)
}
.background(Color(hex: "1C1C1E"))
.frame(width: 666, height: 1190)
}
var body: some View { shareView }
}