watch: texto de la complicacion proporcional y complicacion de 3 dias
- La rectangular de hoy escala el texto segun comidas: 1 comida -> 17pt (hasta 3 lineas), 2 -> 15pt, 3 -> 12pt; antes 12pt fijo y con dos comidas quedaba enano - Nueva complicacion "MealMood - 3 dias": hoy + los dos siguientes en una linea por dia (abreviatura en coral si es hoy + platos) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>81</string>
|
||||
<string>82</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -8,6 +8,7 @@ import SwiftUI
|
||||
struct MealMoodWatchWidgetBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
MealMoodWatchWidget()
|
||||
MealMoodWatchDaysWidget()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,16 +84,21 @@ struct TodayComplicationView: View {
|
||||
default:
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
if let day = entry.day {
|
||||
ForEach(Array(day.meals.prefix(3).enumerated()), id: \.offset) { _, meal in
|
||||
// The rectangular slot fits ~3 lines: with fewer meals the
|
||||
// text scales up so the space isn't wasted.
|
||||
let meals = Array(day.meals.prefix(3))
|
||||
let nameSize: CGFloat = meals.count <= 1 ? 17 : (meals.count == 2 ? 15 : 12)
|
||||
ForEach(Array(meals.enumerated()), id: \.offset) { _, meal in
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: WatchWeekPayload.icon(for: meal.type))
|
||||
.font(.system(size: 9, weight: .bold))
|
||||
.font(.system(size: nameSize - 3, weight: .bold))
|
||||
.foregroundStyle(accent(for: meal.type))
|
||||
.widgetAccentable()
|
||||
.frame(width: 11)
|
||||
.frame(width: nameSize)
|
||||
Text(meal.name ?? "—")
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
.lineLimit(1)
|
||||
.font(.system(size: nameSize, weight: .semibold, design: .rounded))
|
||||
.lineLimit(meals.count <= 1 ? 3 : (meals.count == 2 ? 2 : 1))
|
||||
.minimumScaleFactor(0.75)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -108,6 +114,10 @@ struct TodayComplicationView: View {
|
||||
}
|
||||
|
||||
private func accent(for mealType: String) -> Color {
|
||||
Self.accentColor(for: mealType)
|
||||
}
|
||||
|
||||
static func accentColor(for mealType: String) -> Color {
|
||||
switch mealType {
|
||||
case "breakfast": return Color(red: 0.95, green: 0.68, blue: 0.25)
|
||||
case "lunch": return Color(red: 0.98, green: 0.55, blue: 0.30)
|
||||
@@ -125,3 +135,102 @@ struct TodayComplicationView: View {
|
||||
return meals.first(where: { $0.name != nil }) ?? meals.first
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Next days complication
|
||||
|
||||
struct DaysEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let days: [WatchWeekPayload.Day]
|
||||
}
|
||||
|
||||
struct DaysProvider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> DaysEntry {
|
||||
DaysEntry(date: Date(), days: sampleDays)
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (DaysEntry) -> Void) {
|
||||
let days = upcomingDays
|
||||
completion(DaysEntry(date: Date(), days: days.isEmpty ? sampleDays : days))
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<DaysEntry>) -> Void) {
|
||||
let entry = DaysEntry(date: Date(), days: upcomingDays)
|
||||
let nextMidnight = Calendar.current.startOfDay(for: Date()).addingTimeInterval(86_400 + 300)
|
||||
completion(Timeline(entries: [entry], policy: .after(nextMidnight)))
|
||||
}
|
||||
|
||||
/// Today and the next two planned days (falls back to the week's first
|
||||
/// days when today is outside the planned range, e.g. weekends off).
|
||||
private var upcomingDays: [WatchWeekPayload.Day] {
|
||||
guard let payload = WatchWeekPayload.stored() else { return [] }
|
||||
let todayOffset = (Calendar.current.component(.weekday, from: Date()) + 5) % 7
|
||||
let fromToday = payload.days.filter { $0.dayOfWeek >= todayOffset }
|
||||
let source = fromToday.isEmpty ? payload.days : fromToday
|
||||
return Array(source.prefix(3))
|
||||
}
|
||||
|
||||
private var sampleDays: [WatchWeekPayload.Day] {
|
||||
[
|
||||
.init(dayOfWeek: 0, title: "Lun 8", isToday: true, meals: [
|
||||
.init(type: "lunch", label: "Comida", name: "Ensalada"),
|
||||
.init(type: "dinner", label: "Cena", name: "Salmón")]),
|
||||
.init(dayOfWeek: 1, title: "Mar 9", isToday: false, meals: [
|
||||
.init(type: "dinner", label: "Cena", name: "Lentejas")]),
|
||||
.init(dayOfWeek: 2, title: "Mié 10", isToday: false, meals: [
|
||||
.init(type: "dinner", label: "Cena", name: "Pizza casera")])
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct MealMoodWatchDaysWidget: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: "MealMoodWatchDays", provider: DaysProvider()) { entry in
|
||||
DaysComplicationView(entry: entry)
|
||||
.containerBackground(.clear, for: .widget)
|
||||
}
|
||||
.configurationDisplayName("MealMood — 3 días")
|
||||
.supportedFamilies([.accessoryRectangular])
|
||||
}
|
||||
}
|
||||
|
||||
struct DaysComplicationView: View {
|
||||
let entry: DaysEntry
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
if entry.days.isEmpty {
|
||||
Text(verbatim: "MealMood")
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
Image(systemName: "iphone.and.arrow.forward")
|
||||
.font(.system(size: 10))
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(entry.days, id: \.dayOfWeek) { day in
|
||||
HStack(alignment: .firstTextBaseline, spacing: 4) {
|
||||
Text(dayAbbrev(day))
|
||||
.font(.system(size: 11, weight: .heavy, design: .rounded))
|
||||
.foregroundStyle(day.isToday
|
||||
? Color(red: 1.0, green: 0.45, blue: 0.35)
|
||||
: .secondary)
|
||||
.widgetAccentable()
|
||||
.frame(width: 28, alignment: .leading)
|
||||
Text(mealsLine(day))
|
||||
.font(.system(size: 12, weight: .medium, design: .rounded))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private func dayAbbrev(_ day: WatchWeekPayload.Day) -> String {
|
||||
String(day.title.split(separator: " ").first ?? "").uppercased()
|
||||
}
|
||||
|
||||
private func mealsLine(_ day: WatchWeekPayload.Day) -> String {
|
||||
let names = day.meals.compactMap(\.name)
|
||||
return names.isEmpty ? "—" : names.joined(separator: " · ")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>81</string>
|
||||
<string>82</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
Reference in New Issue
Block a user