Complete rename across all bundle IDs, App Groups, CloudKit containers, StoreKit product IDs, data store filenames, URL schemes, logger subsystems, Swift identifiers, user-facing strings (7 languages), file names, directory names, Xcode project, schemes, assets, and documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
222 lines
7.9 KiB
Swift
222 lines
7.9 KiB
Swift
//
|
|
// MonthTotalVariations.swift
|
|
// Reflect
|
|
//
|
|
// 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("ReflectAppIcon")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 96, height: 96)
|
|
.clipShape(RoundedRectangle(cornerRadius: 22))
|
|
|
|
Text("Reflect")
|
|
.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("ReflectAppIcon")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 96, height: 96)
|
|
.clipShape(RoundedRectangle(cornerRadius: 22))
|
|
|
|
Text("Reflect")
|
|
.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 }
|
|
}
|