Merge develop -> main: full session (KC-51 through KC-71) #28
@@ -2029,3 +2029,36 @@ offset from an unrelated comment, verified via stash — not introduced by this
|
||||
change). `OverdueCard`'s wiring is technically unreachable today since
|
||||
`overdueTasks` already excludes recurring tasks by design — wired anyway for
|
||||
consistency in case that changes.
|
||||
|
||||
---
|
||||
|
||||
## KC-60 — Habit reminders also introduced in the in-app tutorial, not just onboarding
|
||||
|
||||
**Status:** Implemented (not build-verified)
|
||||
**Reported by:** User: "do we have the behavior reminders as part of onboarding
|
||||
and also in the tutorial"
|
||||
|
||||
### Description
|
||||
Habit reminders (KC-55) were added to Onboarding, but **not** to the app's
|
||||
separate tutorial system (`TutorialManager` — the spotlight/coachmark walkthrough
|
||||
shown per-tab: intro tour, Today tips, Workout tips). Someone who skips or
|
||||
doesn't linger on onboarding's toggles had no second chance to discover the
|
||||
feature exists.
|
||||
|
||||
### Change
|
||||
- New fourth tutorial track in `TutorialManager`: `settingsTips` /
|
||||
`settingsStep` / `settingsDone` (persisted key `kisani.tutorial.settings.v1`),
|
||||
following the exact same pattern as the existing Today/Workout tip tracks
|
||||
(`advanceSettings()`/`skipSettings()`).
|
||||
- One tip: "Habit Reminders" — introduces Prayer/Bed/Coffee and the private
|
||||
streak via the notification's "Done" action.
|
||||
- Wired into `SettingsView` via the same `InViewTutorialCard` component
|
||||
Today/Workout already use — shown once, the first time Settings is opened.
|
||||
|
||||
Files: `TutorialManager.swift`, `SettingsView.swift`.
|
||||
|
||||
### Note
|
||||
Not build-verified (no iOS runtime here). Self-reviewed: balanced braces/
|
||||
parens confirmed; wiring mirrors `TodayView`/`WorkoutView`'s existing
|
||||
`tm.xIsActive` + `InViewTutorialCard` pattern exactly, reusing the same shared
|
||||
component rather than introducing a new one.
|
||||
|
||||
@@ -104,4 +104,24 @@ final class TutorialManager: ObservableObject {
|
||||
else { withAnimation(KisaniSpring.entrance) { workoutStep += 1 } }
|
||||
}
|
||||
func skipWorkout() { withAnimation(KisaniSpring.exit) { workoutDone = true } }
|
||||
|
||||
// MARK: - Settings view tutorial
|
||||
@AppStorage("kisani.tutorial.settings.v1") var settingsDone: Bool = false
|
||||
@Published var settingsStep: Int = 0
|
||||
|
||||
let settingsTips: [PageTip] = [
|
||||
.init(title: "Habit Reminders",
|
||||
body: "Prayer, making your bed, coffee — under Habits, set daily nudges for the small routines you don't want to forget. Tap Done on the notification to keep a private streak.",
|
||||
icon: "hands.sparkles"),
|
||||
]
|
||||
|
||||
var settingsIsActive: Bool { !settingsDone }
|
||||
var settingsCurrent: PageTip { settingsTips[min(settingsStep, settingsTips.count - 1)] }
|
||||
var settingsIsLast: Bool { settingsStep == settingsTips.count - 1 }
|
||||
|
||||
func advanceSettings() {
|
||||
if settingsIsLast { withAnimation(KisaniSpring.exit) { settingsDone = true } }
|
||||
else { withAnimation(KisaniSpring.entrance) { settingsStep += 1 } }
|
||||
}
|
||||
func skipSettings() { withAnimation(KisaniSpring.exit) { settingsDone = true } }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ struct SettingsView: View {
|
||||
@EnvironmentObject var taskVM: TaskViewModel
|
||||
@ObservedObject private var auth = AuthManager.shared
|
||||
@ObservedObject private var calStore = CalendarStore.shared
|
||||
@ObservedObject private var tm = TutorialManager.shared
|
||||
|
||||
// Persisted settings
|
||||
@AppStorage("appearanceMode") private var appearanceMode = 0
|
||||
@@ -189,6 +190,21 @@ struct SettingsView: View {
|
||||
Spacer().frame(height: 40)
|
||||
}
|
||||
}
|
||||
|
||||
if tm.settingsIsActive {
|
||||
VStack {
|
||||
Spacer()
|
||||
InViewTutorialCard(
|
||||
tips: tm.settingsTips,
|
||||
step: $tm.settingsStep,
|
||||
onAdvance: { tm.advanceSettings() },
|
||||
onSkip: { tm.skipSettings() }
|
||||
)
|
||||
.padding(.bottom, 84)
|
||||
}
|
||||
.transition(.opacity)
|
||||
.zIndex(10)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showProfile) { ProfileStatsSheet().environmentObject(taskVM).environmentObject(workoutVM).presentationDetents([.large]).presentationDragIndicator(.visible) }
|
||||
.sheet(isPresented: $showTabBar) { TabBarSheet(showMatrix: $showMatrixTab, showWorkout: $showWorkoutTab).presentationDetents([.height(320)]).presentationDragIndicator(.visible) }
|
||||
|
||||
Reference in New Issue
Block a user