Files
Reflect/Feels Watch App/ContentView.swift
Trey t 2a703a8969 Add 4 new mood icon styles and improve widget layouts
New mood icon styles:
- Weather (☀️⛈️)
- Garden (🌸🥀)
- Hearts (💖💔)
- Cosmic (🕳️)

Widget improvements:
- Small vote widget: 3-over-2 grid layout
- Medium vote widget: single horizontal row
- Redesigned voted stats view with checkmark badge
- Fixed text truncation on non-subscriber view
- Added comprehensive previews for all widget types

Bug fix:
- Voting header now updates when mood image style changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 19:06:05 -06:00

236 lines
6.9 KiB
Swift

//
// ContentView.swift
// Feels 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?
var body: some View {
ZStack {
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) })
}
}
.opacity(showConfirmation ? 0.3 : 1)
// Confirmation overlay
if showConfirmation {
ConfirmationView(mood: selectedMood)
}
}
}
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
WatchDataProvider.shared.addMood(mood, forDate: date)
// Send to iPhone - it will handle HealthKit, Live Activity, etc.
_ = WatchConnectivityManager.shared.sendMoodToPhone(mood: mood.rawValue, date: date)
}
// Show confirmation
withAnimation(.easeInOut(duration: 0.2)) {
showConfirmation = true
}
// Hide confirmation after delay
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation(.easeInOut(duration: 0.2)) {
showConfirmation = false
}
}
}
}
// 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: - Confirmation View
struct ConfirmationView: View {
let mood: Mood?
var body: some View {
VStack(spacing: 8) {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 40))
.foregroundColor(.green)
Text("Logged!")
.font(.system(size: 18, weight: .semibold))
if let mood = mood {
Text(mood.watchEmoji)
.font(.system(size: 24))
}
}
}
}
// 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()
}