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>
This commit is contained in:
Trey t
2026-02-10 09:26:21 -06:00
parent 15bc1c94c1
commit bfef0a4472
8 changed files with 1286 additions and 82 deletions

View File

@@ -0,0 +1,213 @@
//
// AllMoodsVariations.swift
// Feels
//
// 3 design variations for the All Moods Total sharing template.
//
import SwiftUI
// MARK: - V2 "Gradient" Warm gradient, rounded cards, bold
struct AllMoodsV2: View {
let metrics: [MoodMetrics]
let totalCount: Int
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
private var moodTint: MoodTints = .Default
var image: UIImage {
shareView.asImage(size: CGSize(width: 666, height: 1190))
}
var shareView: some View {
ZStack {
// Gradient bg
LinearGradient(
colors: [Color(hex: "FFF5EE"), Color(hex: "FFE4D6"), Color(hex: "FFD4C2")],
startPoint: .top,
endPoint: .bottom
)
VStack(spacing: 0) {
Spacer().frame(height: 80)
// Title card
VStack(spacing: 12) {
Text("All Time Moods")
.font(.system(size: 22, weight: .semibold, design: .rounded))
.foregroundColor(Color(hex: "8B5E3C"))
Text("\(totalCount)")
.font(.system(size: 96, weight: .heavy, design: .rounded))
.foregroundColor(Color(hex: "4A2810"))
Text("entries tracked")
.font(.system(size: 16, weight: .medium, design: .rounded))
.foregroundColor(Color(hex: "8B5E3C").opacity(0.7))
}
Spacer().frame(height: 50)
// Mood circles row
HStack(spacing: 20) {
ForEach(metrics.sorted(by: { $0.mood.rawValue > $1.mood.rawValue }), id: \.mood) { metric in
VStack(spacing: 12) {
ZStack {
Circle()
.fill(moodTint.color(forMood: metric.mood))
.frame(width: 90, height: 90)
.shadow(color: moodTint.color(forMood: metric.mood).opacity(0.4), radius: 12, y: 6)
metric.mood.icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 36, height: 36)
.foregroundColor(.white)
}
Text("\(metric.percent, specifier: "%.0f")%")
.font(.system(size: 20, weight: .bold, design: .rounded))
.foregroundColor(Color(hex: "4A2810"))
Text("\(metric.total)")
.font(.system(size: 14, weight: .medium, design: .rounded))
.foregroundColor(Color(hex: "8B5E3C").opacity(0.6))
}
}
}
.padding(.horizontal, 24)
Spacer()
// Bottom card
HStack(alignment: .bottom) {
ForEach(metrics.sorted(by: { $0.percent > $1.percent }), id: \.mood) { metric in
VStack(spacing: 4) {
RoundedRectangle(cornerRadius: 8)
.fill(moodTint.color(forMood: metric.mood))
.frame(height: CGFloat(metric.percent) * 2.5)
Text(metric.mood.strValue)
.font(.system(size: 11, weight: .medium, design: .rounded))
.foregroundColor(Color(hex: "8B5E3C"))
}
.frame(maxWidth: .infinity)
}
}
.frame(height: 300, alignment: .bottom)
.padding(24)
.background(
RoundedRectangle(cornerRadius: 24)
.fill(.white.opacity(0.6))
)
.padding(.horizontal, 32)
Spacer().frame(height: 50)
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: .semibold, design: .rounded))
.foregroundColor(Color(hex: "8B5E3C").opacity(0.4))
}
Spacer().frame(height: 50)
}
}
.frame(width: 666, height: 1190)
}
var body: some View { shareView }
}
// MARK: - V5 "Color Block" Mood colors fill proportional strips, white text
struct AllMoodsV5: View {
let metrics: [MoodMetrics]
let totalCount: Int
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
private var moodTint: MoodTints = .Default
var image: UIImage {
shareView.asImage(size: CGSize(width: 666, height: 1190))
}
var shareView: some View {
let sorted = metrics.sorted(by: { $0.mood.rawValue > $1.mood.rawValue })
let headerHeight: CGFloat = 80
let footerHeight: CGFloat = 180
let blocksHeight: CGFloat = 1190 - headerHeight - footerHeight
return VStack(spacing: 0) {
// Header dark band
ZStack {
Color(hex: "1C1C1E")
Text("\(totalCount) moods")
.font(.system(size: 20, weight: .bold, design: .rounded))
.foregroundColor(.white)
.tracking(2)
.textCase(.uppercase)
}
.frame(height: headerHeight)
// Color blocks fill proportionally, no gaps
ForEach(sorted, id: \.mood) { metric in
let blockHeight = max(blocksHeight * CGFloat(metric.percent / 100), 60)
ZStack {
moodTint.color(forMood: metric.mood)
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(metric.mood.strValue.uppercased())
.font(.system(size: 14, weight: .bold, design: .rounded))
.tracking(2)
Text("\(metric.total) days")
.font(.system(size: 12, weight: .medium))
.opacity(0.7)
}
Spacer()
Text("\(metric.percent, specifier: "%.0f")%")
.font(.system(size: 42, weight: .heavy, design: .rounded))
}
.foregroundColor(.white)
.padding(.horizontal, 40)
}
.frame(height: blockHeight)
}
// Footer dark band
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 }
}