Add unified task menu, Live Activity, and calendar event actions

Task context menu (new shared TaskMenuItems) consistent across Today,
Matrix, and Calendar: Pin · Date · Move · Priority · Tags · Add to Live
Activity · Delete. Adds TaskViewModel.setPriority.

Live Activity (ActivityKit):
- TaskActivityAttributes shared between app and widget targets
- LiveActivityManager (start/update/end/toggle, iOS 16.1+)
- TaskLiveActivity widget: lock-screen banner + Dynamic Island
- NSSupportsLiveActivities via project.yml; project regenerated

Calendar events (non-subscription, writable only):
- Edit via system EKEventEditViewController (EventEditView wrapper)
- Delete via new CalendarStore.deleteEvent

Also sweeps in prior in-progress edits already present in the working
tree (AuthManager, NotificationManager, TaskItem, task views) and the
updated ISSUES.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-06-06 19:29:25 +03:00
parent a92e9d4c09
commit d0d982f4f5
15 changed files with 525 additions and 111 deletions

View File

@@ -111,5 +111,6 @@ struct KisaniWidgetBundle: WidgetBundle {
TasksCompleteWidget()
LockScreenWidget()
LockScreenCircularWidget()
TaskLiveActivity()
}
}

View File

@@ -0,0 +1,74 @@
import ActivityKit
import WidgetKit
import SwiftUI
@available(iOS 16.1, *)
struct TaskLiveActivity: Widget {
private let accent = Color(red: 0.93, green: 0.42, blue: 0.20) // brand orange
var body: some WidgetConfiguration {
ActivityConfiguration(for: TaskActivityAttributes.self) { context in
// Lock screen / banner
HStack(spacing: 12) {
Image(systemName: context.state.isComplete ? "checkmark.circle.fill" : "circle")
.font(.system(size: 22))
.foregroundColor(context.state.isComplete ? .green : accent)
VStack(alignment: .leading, spacing: 2) {
Text(context.attributes.title)
.font(.system(size: 15, weight: .semibold))
.foregroundColor(.white)
.lineLimit(1)
.strikethrough(context.state.isComplete)
if let due = context.state.dueDate {
Text(due, style: context.state.isComplete ? .date : .relative)
.font(.system(size: 12, design: .monospaced))
.foregroundColor(.white.opacity(0.6))
}
}
Spacer()
if let due = context.state.dueDate, !context.state.isComplete {
Text(due, style: .timer)
.font(.system(size: 16, weight: .bold, design: .monospaced))
.foregroundColor(accent)
.frame(maxWidth: 70)
}
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.activityBackgroundTint(Color.black.opacity(0.55))
.activitySystemActionForegroundColor(.white)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "checklist")
.foregroundColor(accent)
}
DynamicIslandExpandedRegion(.trailing) {
if let due = context.state.dueDate, !context.state.isComplete {
Text(due, style: .timer)
.font(.system(.body, design: .monospaced))
.foregroundColor(accent)
.frame(maxWidth: 64)
}
}
DynamicIslandExpandedRegion(.center) {
Text(context.attributes.title)
.font(.system(size: 14, weight: .semibold))
.lineLimit(1)
}
} compactLeading: {
Image(systemName: "checklist").foregroundColor(accent)
} compactTrailing: {
if let due = context.state.dueDate, !context.state.isComplete {
Text(due, style: .timer)
.font(.system(.caption2, design: .monospaced))
.foregroundColor(accent)
.frame(maxWidth: 44)
}
} minimal: {
Image(systemName: "checklist").foregroundColor(accent)
}
}
}
}