bc684a099c
Dos cosas que faltaban, las dos visibles en domingo con el fin de semana desactivado: El envio al reloj solo llevaba la semana en curso, asi que no habia forma de ver las semanas ya planificadas por delante. Ahora viajan cuatro (la anterior, la actual y las dos siguientes) y la vista de semana se desliza entre ellas, arrancando en la actual. Y un dia fuera del plan dejaba al reloj sin nada que enseñar: el domingo no existe en el payload si no planificas fines de semana, asi que la complicacion salia vacia y la app decia "abre MealMood en el iPhone", como si fuera un problema de sincronizacion. Ahora se distingue: si hoy no se planifica se dice, y se enseña el proximo dia que si tiene comidas — que es lo util en el reloj. De paso, las semanas se comparan con el calendario alineado a lunes. Con el del sistema, en las regiones donde la semana empieza en domingo, un domingo caia en otra semana que su propio lunes y un envio recien hecho parecia caducado. Refs #36, #37 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013su1ttRiMeMYxkZJ1Y3246
122 lines
4.7 KiB
Swift
122 lines
4.7 KiB
Swift
import SwiftUI
|
|
|
|
/// Week page — Calendar-app style: day number in a circle on the left (today
|
|
/// filled in coral), meals with tinted icons on the right.
|
|
///
|
|
/// Swipes horizontally between every week the phone sent, so the weeks already
|
|
/// planned ahead are reachable from the wrist.
|
|
struct WeekView: View {
|
|
@EnvironmentObject private var store: WatchWeekStore
|
|
@State private var selection: Date?
|
|
|
|
/// Weeks from the snapshot, or the single week of a pre-2.1.2 payload.
|
|
private var weeks: [WatchWeekPayload.Week] {
|
|
if let weeks = store.payload?.weeks, !weeks.isEmpty { return weeks }
|
|
guard let payload = store.payload else { return [] }
|
|
return [WatchWeekPayload.Week(
|
|
weekStartDate: payload.weekStartDate ?? payload.updatedAt.startOfWeekOnWatch(),
|
|
weekTitle: payload.weekTitle,
|
|
days: payload.days
|
|
)]
|
|
}
|
|
|
|
var body: some View {
|
|
Group {
|
|
if weeks.isEmpty {
|
|
EmptySyncView()
|
|
} else {
|
|
TabView(selection: $selection) {
|
|
ForEach(weeks) { week in
|
|
weekPage(week)
|
|
.tag(Optional(week.weekStartDate))
|
|
}
|
|
}
|
|
.tabViewStyle(.verticalPage(transitionStyle: .blur))
|
|
.onAppear {
|
|
// Land on the current week, not on the oldest one.
|
|
if selection == nil {
|
|
selection = weeks.first(where: { $0.isCurrentWeek() })?.weekStartDate
|
|
?? weeks.first?.weekStartDate
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func weekPage(_ week: WatchWeekPayload.Week) -> some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
HStack(spacing: 4) {
|
|
Text(week.weekTitle)
|
|
.font(.system(.footnote, design: .rounded, weight: .bold))
|
|
.foregroundStyle(WatchTheme.coral)
|
|
if !week.isCurrentWeek() {
|
|
Image(systemName: "chevron.up.chevron.down")
|
|
.font(.system(size: 8, weight: .bold))
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
.padding(.horizontal, 2)
|
|
|
|
ForEach(week.days, id: \.dayOfWeek) { day in
|
|
// Only the current week can contain today.
|
|
DayRow(day: day, isToday: week.isCurrentWeek() && day.isCurrentDay())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct DayRow: View {
|
|
let day: WatchWeekPayload.Day
|
|
/// Resolved by the payload when the view renders, not by the snapshot's own
|
|
/// frozen `isToday`.
|
|
let isToday: Bool
|
|
|
|
/// "Lun 8" → ("Lun", "8")
|
|
private var parts: (name: String, number: String) {
|
|
let pieces = day.title.split(separator: " ", maxSplits: 1)
|
|
guard pieces.count == 2 else { return (day.title, "") }
|
|
return (String(pieces[0]), String(pieces[1]))
|
|
}
|
|
|
|
var body: some View {
|
|
HStack(alignment: .top, spacing: 8) {
|
|
VStack(spacing: 0) {
|
|
Text(parts.name.uppercased())
|
|
.font(.system(size: 9, weight: .bold, design: .rounded))
|
|
.foregroundStyle(isToday ? WatchTheme.coral : .secondary)
|
|
Text(parts.number)
|
|
.font(.system(size: 15, weight: .bold, design: .rounded))
|
|
.foregroundStyle(isToday ? .white : .primary)
|
|
.frame(width: 26, height: 26)
|
|
.background(
|
|
Circle().fill(isToday ? WatchTheme.coral : .clear)
|
|
)
|
|
}
|
|
.frame(width: 32)
|
|
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
ForEach(Array(day.meals.enumerated()), id: \.offset) { _, meal in
|
|
HStack(spacing: 4) {
|
|
Image(systemName: WatchWeekPayload.icon(for: meal.type))
|
|
.font(.system(size: 9, weight: .semibold))
|
|
.foregroundStyle(WatchTheme.accent(for: meal.type))
|
|
.frame(width: 12)
|
|
Text(meal.name ?? "—")
|
|
.font(.system(size: 13, design: .rounded))
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(.vertical, 5)
|
|
.padding(.horizontal, 7)
|
|
.background(
|
|
isToday ? WatchTheme.coral.opacity(0.16) : Color.white.opacity(0.07),
|
|
in: RoundedRectangle(cornerRadius: 9)
|
|
)
|
|
}
|
|
}
|
|
}
|