Initial FeelAloud project
This commit is contained in:
134
Moment/Moment.swift
Normal file
134
Moment/Moment.swift
Normal file
@@ -0,0 +1,134 @@
|
||||
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<MomentEntry>) -> 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)
|
||||
}
|
||||
Reference in New Issue
Block a user