// // ContentView.swift // Reflect Watch App // // Main voting interface for logging moods on Apple Watch. // import SwiftUI import WatchKit struct ContentView: View { @State private var showConfirmation = false @State private var selectedMood: Mood? @State private var todaysMood: Mood? var body: some View { ZStack { if let mood = todaysMood ?? selectedMood, showConfirmation || todaysMood != nil { // Show "already rated" view AlreadyRatedView(mood: mood) } else { // Show voting UI VStack(spacing: 8) { Text("How do you feel?") .font(.system(size: 16, weight: .medium)) .foregroundColor(.secondary) // Top row: Great, Good, Average HStack(spacing: 8) { MoodButton(mood: .great, action: { logMood(.great) }) MoodButton(mood: .good, action: { logMood(.good) }) MoodButton(mood: .average, action: { logMood(.average) }) } // Bottom row: Bad, Horrible HStack(spacing: 8) { MoodButton(mood: .bad, action: { logMood(.bad) }) MoodButton(mood: .horrible, action: { logMood(.horrible) }) } } } } .onAppear { checkTodaysEntry() } } private func checkTodaysEntry() { let entry = ExtensionDataProvider.shared.getTodayEntry() if let entry = entry, entry.mood != .missing && entry.mood != .placeholder { todaysMood = entry.mood } } private func logMood(_ mood: Mood) { selectedMood = mood // Haptic feedback WKInterfaceDevice.current().play(.success) let date = Date() // Send to iPhone for centralized logging (iOS handles all side effects) // Also save locally as fallback and for immediate complication updates Task { @MainActor in // Always save locally for immediate complication display ExtensionDataProvider.shared.add(mood: mood, forDate: date, entryType: .watch) // Send to iPhone - it will handle HealthKit, Live Activity, etc. _ = WatchConnectivityManager.shared.sendMoodToPhone(mood: mood.rawValue, date: date) } // Show confirmation and keep it (user already rated) withAnimation(.easeInOut(duration: 0.2)) { showConfirmation = true todaysMood = mood } } } // MARK: - Already Rated View struct AlreadyRatedView: View { let mood: Mood var body: some View { VStack(spacing: 12) { Text(mood.watchEmoji) .font(.system(size: 50)) Text("Logged!") .font(.system(size: 18, weight: .semibold)) .foregroundColor(.secondary) } } } // MARK: - Mood Button struct MoodButton: View { let mood: Mood let action: () -> Void var body: some View { Button(action: action) { Text(mood.watchEmoji) .font(.system(size: 28)) .frame(maxWidth: .infinity) .frame(height: 50) .background(mood.watchColor.opacity(0.3)) .cornerRadius(12) } .buttonStyle(.plain) } } // MARK: - Watch Mood Image Provider /// Provides the appropriate emoji based on user's selected mood image style enum WatchMoodImageStyle: Int { case fontAwesome = 0 case emoji = 1 case handEmoji = 2 case weather = 3 case garden = 4 case hearts = 5 case cosmic = 6 static var current: WatchMoodImageStyle { // Use optional chaining for preview safety - App Group may not exist in canvas guard let defaults = UserDefaults(suiteName: Constants.currentGroupShareId) else { return .emoji } let rawValue = defaults.integer(forKey: "moodImages") return WatchMoodImageStyle(rawValue: rawValue) ?? .emoji } func emoji(for mood: Mood) -> String { switch self { case .fontAwesome: // FontAwesome uses face icons - map to similar emoji switch mood { case .great: return "😁" case .good: return "🙂" case .average: return "😐" case .bad: return "🙁" case .horrible: return "😫" case .missing, .placeholder: return "❓" } case .emoji: switch mood { case .great: return "😀" case .good: return "🙂" case .average: return "😑" case .bad: return "😕" case .horrible: return "💩" case .missing, .placeholder: return "❓" } case .handEmoji: switch mood { case .great: return "🙏" case .good: return "👍" case .average: return "🖖" case .bad: return "👎" case .horrible: return "🖕" case .missing, .placeholder: return "❓" } case .weather: switch mood { case .great: return "☀️" case .good: return "⛅" case .average: return "☁️" case .bad: return "🌧️" case .horrible: return "⛈️" case .missing: return "🌫️" case .placeholder: return "❓" } case .garden: switch mood { case .great: return "🌸" case .good: return "🌿" case .average: return "🌱" case .bad: return "🍂" case .horrible: return "🥀" case .missing: return "🕳️" case .placeholder: return "❓" } case .hearts: switch mood { case .great: return "💖" case .good: return "🩷" case .average: return "🤍" case .bad: return "🩶" case .horrible: return "💔" case .missing: return "🖤" case .placeholder: return "❓" } case .cosmic: switch mood { case .great: return "⭐" case .good: return "🌕" case .average: return "🌓" case .bad: return "🌑" case .horrible: return "🕳️" case .missing: return "✧" case .placeholder: return "❓" } } } } // MARK: - Watch-Specific Mood Extensions extension Mood { /// Emoji representation for watch display based on user's selected style var watchEmoji: String { WatchMoodImageStyle.current.emoji(for: self) } /// Color for watch UI (simplified palette) var watchColor: Color { switch self { case .great: return .green case .good: return .mint case .average: return .yellow case .bad: return .orange case .horrible: return .red case .missing, .placeholder: return .gray } } } #Preview { ContentView() }