feat: home/lock widgets, Health-aware streaks, workout editor, and entitlement fixes
Widgets (new KisaniCalWidgets extension) - Event Countdown widget: configurable via AppIntent, pick from your events or set a custom name/date; dot-grid progress toward the event. - Day Progress and Tasks-Done-Today widgets; lock screen widgets. - Shared dot grid sizes circles to fill the widget evenly at any count. - Tasks/calendar prefs now persist to the App Group so widgets read live data. Health & streaks - Persist HealthKit authorization and re-establish on launch (fixes the Today dashboard and streak sync disappearing after relaunch). - Add a Health permission toggle to onboarding; unify Settings/Profile on the manager's state. - Day streak counts from a logged Health workout OR marking all exercises complete; nudge to mark exercises complete even when Health logged the workout. Workout editor - Drag to reorder exercises and move them across sections (drag-and-drop); swipe to delete. - Add-exercise sheet: global search and keep-adding-multiple with a running count. Fixes - Restore app entitlements link (Sign in with Apple, HealthKit, App Group, iCloud). - Add HealthKit usage strings; widget Info.plist NSExtension + nested bundle id. - Calendar "Connect" routes to Settings when access was denied. - Bake DEVELOPMENT_TEAM and shared versions into project.yml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1039,11 +1039,19 @@ struct ProgramDetailSheet: View {
|
||||
ForEach(prog.sections) { section in
|
||||
Section {
|
||||
if section.exercises.isEmpty {
|
||||
Text("No exercises — tap + to add")
|
||||
Text("No exercises — tap + or drag one here")
|
||||
.font(AppFonts.sans(11)).foregroundColor(AppColors.text3(cs))
|
||||
.frame(maxWidth: .infinity).padding(.vertical, 10)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.dropDestination(for: String.self) { items, _ in
|
||||
guard let s = items.first, let id = UUID(uuidString: s) else { return false }
|
||||
withAnimation(KisaniSpring.snappy) {
|
||||
vm.moveExercise(programId: programId, exerciseId: id,
|
||||
toSection: section.id, before: nil)
|
||||
}
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
ForEach(section.exercises) { exercise in
|
||||
HStack(spacing: 10) {
|
||||
@@ -1065,6 +1073,15 @@ struct ProgramDetailSheet: View {
|
||||
.padding(.vertical, 4)
|
||||
.listRowBackground(AppColors.surface(cs))
|
||||
.listRowSeparator(.hidden)
|
||||
.draggable(exercise.id.uuidString)
|
||||
.dropDestination(for: String.self) { items, _ in
|
||||
guard let s = items.first, let id = UUID(uuidString: s) else { return false }
|
||||
withAnimation(KisaniSpring.snappy) {
|
||||
vm.moveExercise(programId: programId, exerciseId: id,
|
||||
toSection: section.id, before: exercise.id)
|
||||
}
|
||||
return true
|
||||
}
|
||||
.swipeActions(edge: .trailing) {
|
||||
Button(role: .destructive) {
|
||||
vm.deleteExercise(programId: programId, sectionId: section.id, exerciseId: exercise.id)
|
||||
@@ -1093,6 +1110,14 @@ struct ProgramDetailSheet: View {
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.padding(.vertical, 2)
|
||||
.dropDestination(for: String.self) { items, _ in
|
||||
guard let s = items.first, let id = UUID(uuidString: s) else { return false }
|
||||
withAnimation(KisaniSpring.snappy) {
|
||||
vm.moveExercise(programId: programId, exerciseId: id,
|
||||
toSection: section.id, before: nil)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
} header: {
|
||||
HStack {
|
||||
|
||||
Reference in New Issue
Block a user