Share schedule-editing and starter-seed-state helpers across platforms

This commit is contained in:
2026-07-16 19:54:57 -04:00
parent 9968cb2056
commit f6c5bc911f
4 changed files with 79 additions and 57 deletions
+5 -32
View File
@@ -85,11 +85,6 @@ struct ScheduleAddEditView: View {
/// the routine to start. The Today board starts it and navigates in.
private let onStartNow: (Routine) -> Void
/// Calendar weekday numbers in Monday-first display order (MonSat = 27, Sun = 1).
private let weekdayOrder = [2, 3, 4, 5, 6, 7, 1]
/// Gregorian short names, 0-indexed from Sunday weekday `n` is `symbols[n - 1]`.
private let weekdaySymbols = Calendar(identifier: .gregorian).shortWeekdaySymbols
/// `defaultDate` seeds the `.once` day when adding the Today board passes the day
/// currently on screen, so "navigate to a day, tap +" plans a one-off for that day.
init(schedule: Schedule? = nil, defaultDate: Date = Date(),
@@ -103,7 +98,7 @@ struct ScheduleAddEditView: View {
_weekdays = State(initialValue: Set(schedule?.weekdays ?? []))
_date = State(initialValue: schedule?.date ?? defaultDate)
_reminderEnabled = State(initialValue: schedule?.reminderMinutes != nil)
_reminderTime = State(initialValue: Self.time(fromMinutes: schedule?.reminderMinutes))
_reminderTime = State(initialValue: ScheduleEditing.time(fromMinutes: schedule?.reminderMinutes))
self.onStartNow = onStartNow
let offersNow = schedule == nil && defaultDate.isSameDay(as: Date())
@@ -270,12 +265,12 @@ struct ScheduleAddEditView: View {
/// A row of toggleable weekday chips, Monday-first.
private var weekdayPicker: some View {
HStack(spacing: 6) {
ForEach(weekdayOrder, id: \.self) { n in
ForEach(ScheduleEditing.weekdayOrder, id: \.self) { n in
let on = weekdays.contains(n)
Button {
if on { weekdays.remove(n) } else { weekdays.insert(n) }
} label: {
Text(weekdaySymbols[n - 1])
Text(ScheduleEditing.weekdaySymbols[n - 1])
.font(.caption.weight(.semibold))
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
@@ -310,36 +305,14 @@ struct ScheduleAddEditView: View {
recurrence: recurrence.rawValue,
weekdays: recurrence == .fixedDays ? weekdays.sorted() : nil,
date: recurrence == .once ? date : nil,
reminderMinutes: reminderEnabled ? Self.minutesFromMidnight(of: reminderTime) : nil,
order: isEditing ? order : nextOrder(),
reminderMinutes: reminderEnabled ? ScheduleEditing.minutesFromMidnight(of: reminderTime) : nil,
order: isEditing ? order : ScheduleEditing.nextOrder(in: modelContext),
createdAt: createdAt,
updatedAt: Date()
)
Task { await sync.save(schedule: doc) }
dismiss()
}
/// One past the highest existing schedule order appends a new schedule to the end.
private func nextOrder() -> Int {
let existing = (try? modelContext.fetch(FetchDescriptor<Schedule>())) ?? []
return (existing.map(\.order).max() ?? -1) + 1
}
// MARK: - Reminder time conversion
/// A Date (today, local calendar) at the stored minutes-from-midnight, for the
/// hour-and-minute picker. Nil (no reminder yet) seeds a sensible 8:00 AM default.
private static func time(fromMinutes minutes: Int?) -> Date {
let startOfDay = Calendar.current.startOfDay(for: Date())
return Calendar.current.date(byAdding: .minute, value: minutes ?? 8 * 60, to: startOfDay)
?? startOfDay
}
/// The picked time back to minutes from midnight (local wall-clock).
private static func minutesFromMidnight(of time: Date) -> Int {
let comps = Calendar.current.dateComponents([.hour, .minute], from: time)
return (comps.hour ?? 0) * 60 + (comps.minute ?? 0)
}
}
// MARK: - Routine picker