Files
Reflect/Shared/Models/AppTheme.swift
Trey t 0442eab1f8 Rebrand entire project from Feels to Reflect
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>
2026-02-26 11:47:16 -06:00

298 lines
11 KiB
Swift

//
// AppTheme.swift
// Reflect (iOS)
//
// Created by Claude Code on 12/26/24.
//
import SwiftUI
/// Cohesive themes that bundle colors, icons, entry styles, and voting layouts
/// into unified aesthetic experiences. Each theme is designed around a specific
/// emotional resonance and target user persona.
enum AppTheme: Int, CaseIterable, Identifiable {
case zenGarden = 0
case synthwave = 1
case celestial = 2
case editorial = 3
case mixtape = 4
case bloom = 5
case heartfelt = 6
case minimal = 7
case luxe = 8
case forecast = 9
case playful = 10
case journal = 11
var id: Int { rawValue }
// MARK: - Display Properties
var name: String {
switch self {
case .zenGarden: return "Zen Garden"
case .synthwave: return "Synthwave"
case .celestial: return "Celestial"
case .editorial: return "Editorial"
case .mixtape: return "Mixtape"
case .bloom: return "Bloom"
case .heartfelt: return "Heartfelt"
case .minimal: return "Minimal"
case .luxe: return "Luxe"
case .forecast: return "Forecast"
case .playful: return "Playful"
case .journal: return "Journal"
}
}
var tagline: String {
switch self {
case .zenGarden: return "Meditative calm"
case .synthwave: return "Retro-futuristic energy"
case .celestial: return "Cosmic wisdom"
case .editorial: return "Literary elegance"
case .mixtape: return "Analog nostalgia"
case .bloom: return "Growth & healing"
case .heartfelt: return "Emotional depth"
case .minimal: return "Pure simplicity"
case .luxe: return "Premium refinement"
case .forecast: return "Mood as weather"
case .playful: return "Fun & vibrant"
case .journal: return "Personal diary"
}
}
var description: String {
switch self {
case .zenGarden:
return "Japanese minimalism meets mindful awareness. Soft pastels, organic growth icons, clean entries, and contemplative vertical voting."
case .synthwave:
return "80s arcade aesthetic with neon glow. Electric colors, cosmic icons, grid backgrounds, and equalizer-bar voting."
case .celestial:
return "Journey from void to starlight. Moon phases, orbital layouts, and planetary arrangements for cosmic mood tracking."
case .editorial:
return "Magazine-quality typography and layout. Clean icons, pull-quote entries, and sophisticated presentation."
case .mixtape:
return "Cassette culture and analog warmth. Tape reels, track numbers, and the tactile feel of pressing play."
case .bloom:
return "From wilted flower to full bloom. Atmospheric glowing entries, organic icons, and the gentle metaphor of growth."
case .heartfelt:
return "Unashamed emotional expression. Heart icons from broken to sparkling, bold colors, intuitive selection."
case .minimal:
return "Only the essentials. Clean typography, flat design, and zero distractions."
case .luxe:
return "Liquid glass and premium materials. Cutting-edge iOS design language for the discerning user."
case .forecast:
return "Your mood is the weather. Storm to sunshine icons, colorful bubble entries, and natural intuition."
case .playful:
return "Life's too short to be serious. Vibrant neons, familiar emoji, and game-like interaction."
case .journal:
return "Like writing in a physical diary. Stacked paper notes, handwritten feel, and intimate reflection."
}
}
var emoji: String {
switch self {
case .zenGarden: return "🧘"
case .synthwave: return "🌆"
case .celestial: return ""
case .editorial: return "📰"
case .mixtape: return "📼"
case .bloom: return "🌸"
case .heartfelt: return "💖"
case .minimal: return ""
case .luxe: return "💎"
case .forecast: return "🌦️"
case .playful: return "🎮"
case .journal: return "📒"
}
}
// MARK: - Theme Components
var colorTint: MoodTints {
switch self {
case .zenGarden: return .Pastel
case .synthwave: return .Neon
case .celestial: return .Default
case .editorial: return .Pastel
case .mixtape: return .Default
case .bloom: return .Pastel
case .heartfelt: return .Pastel
case .minimal: return .Pastel
case .luxe: return .Default
case .forecast: return .Default
case .playful: return .Neon
case .journal: return .Default
}
}
var iconPack: MoodImages {
switch self {
case .zenGarden: return .Garden
case .synthwave: return .Cosmic
case .celestial: return .Cosmic
case .editorial: return .FontAwesome
case .mixtape: return .Emoji
case .bloom: return .Garden
case .heartfelt: return .Hearts
case .minimal: return .FontAwesome
case .luxe: return .Cosmic
case .forecast: return .Weather
case .playful: return .Emoji
case .journal: return .FontAwesome
}
}
var entryStyle: DayViewStyle {
switch self {
case .zenGarden: return .minimal
case .synthwave: return .neon
case .celestial: return .orbit
case .editorial: return .chronicle
case .mixtape: return .tape
case .bloom: return .aura
case .heartfelt: return .bubble
case .minimal: return .minimal
case .luxe: return .glass
case .forecast: return .bubble
case .playful: return .pattern
case .journal: return .stack
}
}
var votingLayout: VotingLayoutStyle {
switch self {
case .zenGarden: return .stacked
case .synthwave: return .neon
case .celestial: return .orbit
case .editorial: return .horizontal
case .mixtape: return .cards
case .bloom: return .aura
case .heartfelt: return .horizontal
case .minimal: return .horizontal
case .luxe: return .aura
case .forecast: return .horizontal
case .playful: return .cards
case .journal: return .stacked
}
}
var paywallStyle: PaywallStyle {
switch self {
case .zenGarden: return .zen
case .synthwave: return .neon
case .celestial: return .celestial
case .editorial: return .editorial
case .mixtape: return .mixtape
case .bloom: return .garden
case .heartfelt: return .heartfelt
case .minimal: return .minimal
case .luxe: return .luxe
case .forecast: return .forecast
case .playful: return .playful
case .journal: return .journal
}
}
var lockScreenStyle: LockScreenStyle {
switch self {
case .zenGarden: return .zen
case .synthwave: return .neon
case .celestial: return .celestial
case .editorial: return .editorial
case .mixtape: return .mixtape
case .bloom: return .bloom
case .heartfelt: return .heartfelt
case .minimal: return .minimal
case .luxe: return .luxe
case .forecast: return .forecast
case .playful: return .playful
case .journal: return .journal
}
}
// MARK: - Preview Colors (for theme picker UI)
var previewColors: [Color] {
switch self {
case .zenGarden:
return [Color(hex: "#C1E1C1"), Color(hex: "#A7C7E7"), Color(hex: "#fdfd96")]
case .synthwave:
return [Color(red: 0, green: 1, blue: 0.82), Color(red: 1, green: 0, blue: 0.8), Color(hex: "#050510")]
case .celestial:
return [Color(hex: "#0b84ff"), Color(hex: "#31d158"), Color(hex: "#1a1a2e")]
case .editorial:
return [Color(hex: "#A7C7E7"), Color(hex: "#2c2c2c"), Color(hex: "#f5f5f5")]
case .mixtape:
return [Color(hex: "#8B4513"), Color(hex: "#D2691E"), Color(hex: "#2c2c2c")]
case .bloom:
return [Color(hex: "#C1E1C1"), Color(hex: "#ffb347"), Color(hex: "#FF6961")]
case .heartfelt:
return [Color(hex: "#FF6961"), Color(hex: "#ffb347"), Color(hex: "#C1E1C1")]
case .minimal:
return [Color(hex: "#A7C7E7"), Color(hex: "#e0e0e0"), Color(hex: "#f8f8f8")]
case .luxe:
return [Color(hex: "#0b84ff"), Color(hex: "#31d158"), Color.white.opacity(0.8)]
case .forecast:
return [Color(hex: "#0b84ff"), Color(hex: "#ffd709"), Color(hex: "#ff453a")]
case .playful:
return [Color(hex: "#39FF14"), Color(hex: "#FFF01F"), Color(hex: "#FF5F1F")]
case .journal:
return [Color(hex: "#D2B48C"), Color(hex: "#8B4513"), Color(hex: "#FFFEF0")]
}
}
// MARK: - Apply Theme
/// Applies all theme settings to UserDefaults
func apply() {
// Set color tint
GroupUserDefaults.groupDefaults.set(colorTint.rawValue, forKey: UserDefaultsStore.Keys.moodTint.rawValue)
// Set icon pack
GroupUserDefaults.groupDefaults.set(iconPack.rawValue, forKey: UserDefaultsStore.Keys.moodImages.rawValue)
// Set entry style
GroupUserDefaults.groupDefaults.set(entryStyle.rawValue, forKey: UserDefaultsStore.Keys.dayViewStyle.rawValue)
// Set voting layout
GroupUserDefaults.groupDefaults.set(votingLayout.rawValue, forKey: UserDefaultsStore.Keys.votingLayoutStyle.rawValue)
// Set paywall style
GroupUserDefaults.groupDefaults.set(paywallStyle.rawValue, forKey: UserDefaultsStore.Keys.paywallStyle.rawValue)
// Set lock screen style
GroupUserDefaults.groupDefaults.set(lockScreenStyle.rawValue, forKey: UserDefaultsStore.Keys.lockScreenStyle.rawValue)
// Log the theme change
Task { @MainActor in
AnalyticsManager.shared.track(.themeApplied(themeName: name))
}
}
}
// MARK: - Theme Categories for Browsing
extension AppTheme {
enum Category: String, CaseIterable {
case calm = "Calm & Mindful"
case energetic = "Energetic & Bold"
case sophisticated = "Sophisticated"
case expressive = "Expressive"
var themes: [AppTheme] {
switch self {
case .calm:
return [.zenGarden, .minimal, .bloom]
case .energetic:
return [.synthwave, .playful, .mixtape]
case .sophisticated:
return [.celestial, .editorial, .luxe]
case .expressive:
return [.heartfelt, .forecast, .journal]
}
}
}
}