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>
240 lines
9.0 KiB
Swift
240 lines
9.0 KiB
Swift
//
|
|
// LongestStreakVariations.swift
|
|
// Reflect
|
|
//
|
|
// 2 design variations for the Longest Streak sharing template.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
// MARK: - V2 "Gradient Bar" — Warm gradient bg, horizontal progress
|
|
|
|
struct LongestStreakV2: View {
|
|
let streakEntries: [MoodEntryModel]
|
|
let selectedMood: Mood
|
|
|
|
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
|
|
private var moodTint: MoodTints = .Default
|
|
|
|
private let dateFormatter: DateFormatter = {
|
|
let f = DateFormatter()
|
|
f.dateFormat = "MMM d, yyyy"
|
|
return f
|
|
}()
|
|
|
|
var image: UIImage {
|
|
shareView.asImage(size: CGSize(width: 650, height: 400))
|
|
}
|
|
|
|
var shareView: some View {
|
|
ZStack {
|
|
LinearGradient(
|
|
colors: [Color(hex: "FFF5EE"), Color(hex: "FFE4D6")],
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
|
|
VStack(spacing: 20) {
|
|
// Top row
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("Longest Streak")
|
|
.font(.system(size: 14, weight: .semibold, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C"))
|
|
.textCase(.uppercase)
|
|
.tracking(2)
|
|
|
|
HStack(alignment: .firstTextBaseline, spacing: 8) {
|
|
Text("\(streakEntries.count)")
|
|
.font(.system(size: 56, weight: .heavy, design: .rounded))
|
|
.foregroundColor(Color(hex: "4A2810"))
|
|
|
|
Text("days")
|
|
.font(.system(size: 20, weight: .medium, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C"))
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
ZStack {
|
|
Circle()
|
|
.fill(moodTint.color(forMood: selectedMood))
|
|
.frame(width: 72, height: 72)
|
|
.shadow(color: moodTint.color(forMood: selectedMood).opacity(0.4), radius: 12)
|
|
|
|
selectedMood.icon
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 32, height: 32)
|
|
.foregroundColor(.white)
|
|
}
|
|
}
|
|
.padding(.horizontal, 36)
|
|
|
|
// Progress bar
|
|
VStack(spacing: 8) {
|
|
GeometryReader { geo in
|
|
ZStack(alignment: .leading) {
|
|
Capsule()
|
|
.fill(Color(hex: "4A2810").opacity(0.1))
|
|
.frame(height: 24)
|
|
|
|
Capsule()
|
|
.fill(moodTint.color(forMood: selectedMood))
|
|
.frame(width: geo.size.width * 0.85, height: 24)
|
|
.shadow(color: moodTint.color(forMood: selectedMood).opacity(0.3), radius: 6)
|
|
}
|
|
}
|
|
.frame(height: 24)
|
|
|
|
HStack {
|
|
Text(dateFormatter.string(from: streakEntries.first?.forDate ?? Date()))
|
|
.font(.system(size: 12, weight: .medium, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C").opacity(0.6))
|
|
|
|
Spacer()
|
|
|
|
Text(dateFormatter.string(from: streakEntries.last?.forDate ?? Date()))
|
|
.font(.system(size: 12, weight: .medium, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C").opacity(0.6))
|
|
}
|
|
}
|
|
.padding(.horizontal, 36)
|
|
|
|
// Footer
|
|
HStack {
|
|
Text(selectedMood.strValue)
|
|
.font(.system(size: 14, weight: .semibold, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C"))
|
|
|
|
Spacer()
|
|
|
|
VStack(spacing: 6) {
|
|
Image("ReflectAppIcon")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 72, height: 72)
|
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
|
|
Text("Reflect")
|
|
.font(.system(size: 22, weight: .medium, design: .rounded))
|
|
.foregroundColor(Color(hex: "8B5E3C").opacity(0.4))
|
|
}
|
|
}
|
|
.padding(.horizontal, 36)
|
|
}
|
|
.padding(.vertical, 32)
|
|
}
|
|
.frame(width: 650, height: 400)
|
|
}
|
|
|
|
var body: some View { shareView }
|
|
}
|
|
|
|
// MARK: - V3 "Dark Badge" — Dark bg, glowing badge, neon accent
|
|
|
|
struct LongestStreakV3: View {
|
|
let streakEntries: [MoodEntryModel]
|
|
let selectedMood: Mood
|
|
|
|
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults)
|
|
private var moodTint: MoodTints = .Default
|
|
|
|
private let dateFormatter: DateFormatter = {
|
|
let f = DateFormatter()
|
|
f.dateStyle = .medium
|
|
return f
|
|
}()
|
|
|
|
var image: UIImage {
|
|
shareView.asImage(size: CGSize(width: 650, height: 400))
|
|
}
|
|
|
|
var shareView: some View {
|
|
ZStack {
|
|
Color(hex: "0A0A0F")
|
|
|
|
HStack(spacing: 0) {
|
|
// Left half — badge centered
|
|
ZStack {
|
|
Circle()
|
|
.fill(moodTint.color(forMood: selectedMood).opacity(0.15))
|
|
.frame(width: 220, height: 220)
|
|
|
|
Circle()
|
|
.stroke(moodTint.color(forMood: selectedMood).opacity(0.3), lineWidth: 2)
|
|
.frame(width: 180, height: 180)
|
|
|
|
Circle()
|
|
.stroke(moodTint.color(forMood: selectedMood), lineWidth: 3)
|
|
.frame(width: 150, height: 150)
|
|
|
|
VStack(spacing: 4) {
|
|
Text("\(streakEntries.count)")
|
|
.font(.system(size: 60, weight: .heavy, design: .rounded))
|
|
.foregroundColor(.white)
|
|
|
|
Text("DAYS")
|
|
.font(.system(size: 13, weight: .bold))
|
|
.foregroundColor(moodTint.color(forMood: selectedMood))
|
|
.tracking(4)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
// Right half — text left-aligned, vertically centered
|
|
VStack(alignment: .leading, spacing: 20) {
|
|
Text("LONGEST STREAK")
|
|
.font(.system(size: 14, weight: .bold))
|
|
.foregroundColor(Color(hex: "6E6E80"))
|
|
.tracking(4)
|
|
|
|
HStack(spacing: 10) {
|
|
selectedMood.icon
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 28, height: 28)
|
|
.foregroundColor(moodTint.color(forMood: selectedMood))
|
|
|
|
Text(selectedMood.strValue)
|
|
.font(.system(size: 30, weight: .semibold))
|
|
.foregroundColor(.white)
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Text(dateFormatter.string(from: streakEntries.first?.forDate ?? Date()))
|
|
.font(.system(size: 18, weight: .medium))
|
|
.foregroundColor(.white.opacity(0.7))
|
|
|
|
Text("→")
|
|
.font(.system(size: 16))
|
|
.foregroundColor(Color(hex: "6E6E80"))
|
|
|
|
Text(dateFormatter.string(from: streakEntries.last?.forDate ?? Date()))
|
|
.font(.system(size: 18, weight: .medium))
|
|
.foregroundColor(.white.opacity(0.7))
|
|
}
|
|
|
|
VStack(spacing: 6) {
|
|
Image("ReflectAppIcon")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 72, height: 72)
|
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
|
|
Text("Reflect")
|
|
.font(.system(size: 22, weight: .medium))
|
|
.foregroundColor(Color(hex: "3A3A4A"))
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.padding(.leading, 20)
|
|
}
|
|
}
|
|
.frame(width: 650, height: 400)
|
|
}
|
|
|
|
var body: some View { shareView }
|
|
}
|