Tasks: single-occurrence recurrence + Today/Tomorrow/Next 7/Later + icons
Switch lists from calendar-expansion (every occurrence in every section) to the TickTick model: one task contributes ONE next-active occurrence. - VM: nextActiveOccurrence + activeRows; replace todayTasks/next3/upcoming with todayTasks/tomorrowTasks/next7DaysTasks/laterTasks buckets. A recurring task shows a single row; completing it advances to the next. - TodayView: sections are now Today / Tomorrow / Next 7 Days / Later (UpcomingSection gained a title param). - TaskRowView: add ⏰ (reminder) and 🔁 (recurring) indicators on the right; fix the hardcoded "Annual" label to show the real frequency. Calendar grid keeps per-date occurrences (it's a date grid, not a list). Matrix dynamic promotion (Problem 4) handled separately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -124,16 +124,33 @@ struct TodayView: View {
|
||||
.padding(.top, 10)
|
||||
}
|
||||
|
||||
// ── Next 3 Days ──
|
||||
let next3 = taskVM.next3DaysTasks
|
||||
if !next3.isEmpty {
|
||||
Next3DaysSection(tasksByDay: next3, onToggle: { taskVM.toggle($0) })
|
||||
// ── Tomorrow ──
|
||||
if !taskVM.tomorrowTasks.isEmpty {
|
||||
UpcomingSection(
|
||||
title: "Tomorrow",
|
||||
tasks: taskVM.tomorrowTasks,
|
||||
onToggle: { taskVM.toggle($0) },
|
||||
onPostpone: { taskVM.postpone($0) },
|
||||
onDelete: { taskVM.delete($0) }
|
||||
)
|
||||
}
|
||||
|
||||
// ── Upcoming (4+ days) ──
|
||||
if !taskVM.upcomingTasks.isEmpty {
|
||||
// ── Next 7 Days (2–7 days out) ──
|
||||
if !taskVM.next7DaysTasks.isEmpty {
|
||||
UpcomingSection(
|
||||
tasks: taskVM.upcomingTasks,
|
||||
title: "Next 7 Days",
|
||||
tasks: taskVM.next7DaysTasks,
|
||||
onToggle: { taskVM.toggle($0) },
|
||||
onPostpone: { taskVM.postpone($0) },
|
||||
onDelete: { taskVM.delete($0) }
|
||||
)
|
||||
}
|
||||
|
||||
// ── Later (beyond 7 days) ──
|
||||
if !taskVM.laterTasks.isEmpty {
|
||||
UpcomingSection(
|
||||
title: "Later",
|
||||
tasks: taskVM.laterTasks,
|
||||
onToggle: { taskVM.toggle($0) },
|
||||
onPostpone: { taskVM.postpone($0) },
|
||||
onDelete: { taskVM.delete($0) }
|
||||
@@ -151,8 +168,9 @@ struct TodayView: View {
|
||||
}
|
||||
|
||||
let hasAnything = !taskVM.overdueTasks.isEmpty || !taskVM.todayTasks.isEmpty
|
||||
|| !taskVM.upcomingTasks.isEmpty || !taskVM.completedTodayTasks.isEmpty
|
||||
|| !taskVM.next3DaysTasks.isEmpty || !todayCalEvents.isEmpty
|
||||
|| !taskVM.tomorrowTasks.isEmpty || !taskVM.next7DaysTasks.isEmpty
|
||||
|| !taskVM.laterTasks.isEmpty || !taskVM.completedTodayTasks.isEmpty
|
||||
|| !todayCalEvents.isEmpty
|
||||
|| workoutVM.program(for: Date()) != nil
|
||||
if !hasAnything {
|
||||
VStack(spacing: 6) {
|
||||
@@ -889,12 +907,13 @@ private struct SessionSectionHeader: View {
|
||||
|
||||
struct UpcomingSection: View {
|
||||
@Environment(\.colorScheme) private var cs
|
||||
var title: String = "Upcoming"
|
||||
let tasks: [TaskItem]
|
||||
let onToggle: (TaskItem) -> Void
|
||||
let onPostpone: (TaskItem) -> Void
|
||||
let onDelete: (TaskItem) -> Void
|
||||
|
||||
@AppStorage("upcomingCollapsed") private var collapsed = false
|
||||
@State private var collapsed = false
|
||||
@State private var showAll = false
|
||||
|
||||
private let pageSize = 5
|
||||
@@ -908,7 +927,7 @@ struct UpcomingSection: View {
|
||||
|
||||
// ── Header (tappable to collapse) ──
|
||||
HStack(spacing: 6) {
|
||||
Text("Upcoming")
|
||||
Text(title)
|
||||
.font(AppFonts.sans(10.5, weight: .semibold))
|
||||
.foregroundColor(AppColors.text3(cs))
|
||||
|
||||
@@ -1173,7 +1192,7 @@ struct TaskRowView: View {
|
||||
.foregroundColor(AppColors.text(cs))
|
||||
if let d = task.dueDate, showDetails {
|
||||
HStack(spacing: 4) {
|
||||
Text("\(dueFmt.string(from: d))\(task.isRecurring ? " · Annual" : "")")
|
||||
Text("\(dueFmt.string(from: d))\(task.isRecurring ? " · \(task.recurrenceLabel ?? "Repeat")" : "")")
|
||||
.font(AppFonts.mono(9.5))
|
||||
.foregroundColor(AppColors.text3(cs))
|
||||
Text("·")
|
||||
@@ -1188,6 +1207,20 @@ struct TaskRowView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
// ⏰ reminder · 🔁 recurring indicators
|
||||
HStack(spacing: 5) {
|
||||
if task.reminderDate != nil || task.constantReminder {
|
||||
Image(systemName: "alarm")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(AppColors.text3(cs))
|
||||
}
|
||||
if task.isRecurring {
|
||||
Image(systemName: "repeat")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(AppColors.text3(cs))
|
||||
}
|
||||
}
|
||||
|
||||
TagChip(
|
||||
text: task.category.rawValue,
|
||||
color: task.quadrant.color,
|
||||
|
||||
Reference in New Issue
Block a user