import AppIntents import Foundation import SwiftUI import WidgetKit struct MomentProvider: TimelineProvider { func placeholder(in context: Context) -> MomentEntry { MomentEntry(date: .now, lastRating: nil, lastRecordedAt: nil) } func getSnapshot(in context: Context, completion: @escaping (MomentEntry) -> Void) { completion(currentEntry()) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { completion(Timeline(entries: [currentEntry()], policy: .never)) } private func currentEntry() -> MomentEntry { let defaults = MomentWidgetStorage.defaults let lastRating = defaults.string(forKey: MomentWidgetStorage.lastRatingKey) .flatMap(MomentRatingValue.init(rawValue:)) let lastRecordedAt = defaults.object(forKey: MomentWidgetStorage.lastRecordedAtKey) as? Date return MomentEntry(date: .now, lastRating: lastRating, lastRecordedAt: lastRecordedAt) } } struct MomentEntry: TimelineEntry { let date: Date let lastRating: MomentRatingValue? let lastRecordedAt: Date? } struct MomentEntryView: View { let entry: MomentEntry private let ratings: [MomentRatingValue] = [ .veryGood, .good, .middle, .bad, .veryBad ] var body: some View { VStack(alignment: .leading, spacing: 14) { HStack(alignment: .firstTextBaseline) { Text("Wie geht es dir?") .font(.system(size: 18, weight: .bold)) .foregroundStyle(.white) .lineLimit(1) .minimumScaleFactor(0.82) .shadow(color: .black.opacity(0.12), radius: 2, y: 1) Spacer(minLength: 6) if showsSavedState { Text("Gespeichert") .font(.system(size: 10, weight: .bold)) .foregroundStyle(.white) .lineLimit(1) .minimumScaleFactor(0.75) } } HStack(spacing: 7) { ForEach(ratings, id: \.self) { rating in Button(intent: TrackMomentIntent(rating: rating)) { ZStack(alignment: .topTrailing) { Text(rating.emoji) .font(.system(size: entry.lastRating == rating ? 28 : 25, weight: .bold)) .frame(maxWidth: .infinity) .frame(height: 42) .background(buttonBackground(for: rating), in: RoundedRectangle(cornerRadius: 8)) if showsSavedState, entry.lastRating == rating { Image(systemName: "checkmark.circle.fill") .font(.system(size: 12, weight: .bold)) .foregroundStyle(.white, Color.green) .offset(x: 4, y: -4) } } } .buttonStyle(.plain) .accessibilityLabel(Text(rating.rawValue)) } } } .padding(14) .containerBackground(for: .widget) { LinearGradient( colors: [ Color(red: 0.82, green: 0.90, blue: 0.76), Color(red: 0.66, green: 0.78, blue: 0.60) ], startPoint: .topLeading, endPoint: .bottomTrailing ) } } private var showsSavedState: Bool { guard let lastRecordedAt = entry.lastRecordedAt else { return false } return Date.now.timeIntervalSince(lastRecordedAt) < 90 } private func buttonBackground(for rating: MomentRatingValue) -> Color { guard showsSavedState, entry.lastRating == rating else { return .white.opacity(0.68) } return .white.opacity(0.92) } } struct Moment: Widget { nonisolated static let kind = "Moment" var body: some WidgetConfiguration { StaticConfiguration(kind: Self.kind, provider: MomentProvider()) { entry in MomentEntryView(entry: entry) } .configurationDisplayName("Momentaufnahme") .description("Tracke deine Stimmung direkt vom Homescreen.") .supportedFamilies([.systemSmall, .systemMedium]) } } #Preview(as: .systemSmall) { Moment() } timeline: { MomentEntry(date: .now, lastRating: .good, lastRecordedAt: .now) }