Files
KisaniCal/KisaniCal/Managers/TutorialManager.swift
kutesir 81813530c8 Rebrand app to "Wenza" (display name + user-facing copy)
Renames the user-facing app to Wenza without changing identity:
- CFBundleDisplayName → "Wenza" (app) and "Wenza Widgets" (widget extension).
- Calendar/Health permission prompts now reference Wenza.
- In-app copy updated: sidebar title, Auth + Onboarding screens, Settings
  about/help rows, feedback email subject, tutorial text, widget description.

Bundle identifier, App Group, UserDefaults storage keys, team ID, and the
internal Xcode target/project names are intentionally unchanged, so this stays
the same App Store app with no user data loss. App Store listing name must be
changed separately in App Store Connect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:31:51 +03:00

108 lines
4.4 KiB
Swift

import SwiftUI
struct TutorialStep {
let title: String
let body: String
let icon: String
let anchorIndex: Int
}
struct PageTip {
let title: String
let body: String
let icon: String
}
@MainActor
final class TutorialManager: ObservableObject {
static let shared = TutorialManager()
// MARK: - Tab onboarding (existing)
@AppStorage("kisani.tutorial.done.v1") private(set) var completed: Bool = false
@Published var step: Int = 0
let steps: [TutorialStep] = [
.init(title: "Add Tasks with NLP",
body: "Type naturally — \"dentist tomorrow 3pm\" or \"mum's birthday on 8th June every year\" — Wenza parses the date, time, and recurrence for you.",
icon: "sparkles",
anchorIndex: 0),
.init(title: "Calendar",
body: "See tasks and iPhone calendar events side-by-side, day by day. Nothing falls through the cracks.",
icon: "calendar",
anchorIndex: 1),
.init(title: "Eisenhower Matrix",
body: "Sort tasks into Urgent, Schedule, Delegate, or Eliminate. Focus on what actually moves the needle.",
icon: "square.grid.2x2",
anchorIndex: 2),
.init(title: "Workout Tracker",
body: "Build programs, log sets, and keep your streak alive. Your gym routine lives right here.",
icon: "dumbbell",
anchorIndex: 3),
]
var isActive: Bool { !completed }
var current: TutorialStep { steps[min(step, steps.count - 1)] }
var isLast: Bool { step == steps.count - 1 }
func advance() {
if isLast { withAnimation(KisaniSpring.exit) { completed = true } }
else { withAnimation(KisaniSpring.entrance) { step += 1 } }
}
func skip() {
withAnimation(KisaniSpring.exit) { completed = true }
}
// MARK: - Today view tutorial
@AppStorage("kisani.tutorial.today.v1") var todayDone: Bool = false
@Published var todayStep: Int = 0
let todayTips: [PageTip] = [
.init(title: "Today's Focus",
body: "Everything due today lives here — overdue items appear at the top so nothing slips through. Tap any task to open it.",
icon: "checkmark.circle"),
.init(title: "Add Tasks Instantly",
body: "Tap + and type naturally. \"Doctor Wednesday 2pm\" or \"Neo's birthday 15 June yearly\" — Wenza handles dates, times, and recurrence.",
icon: "sparkles"),
.init(title: "Quick Actions",
body: "Long-press a task to pin, postpone, or move it to a matrix quadrant. Swipe left to delete.",
icon: "hand.tap"),
]
var todayIsActive: Bool { !todayDone }
var todayCurrent: PageTip { todayTips[min(todayStep, todayTips.count - 1)] }
var todayIsLast: Bool { todayStep == todayTips.count - 1 }
func advanceToday() {
if todayIsLast { withAnimation(KisaniSpring.exit) { todayDone = true } }
else { withAnimation(KisaniSpring.entrance) { todayStep += 1 } }
}
func skipToday() { withAnimation(KisaniSpring.exit) { todayDone = true } }
// MARK: - Workout view tutorial
@AppStorage("kisani.tutorial.workout.v1") var workoutDone: Bool = false
@Published var workoutStep: Int = 0
let workoutTips: [PageTip] = [
.init(title: "Set Up Your Programs",
body: "Tap ··· to create workout programs and assign them to days of the week. Each program holds your exercises and target sets.",
icon: "calendar.badge.plus"),
.init(title: "Log Your Sets",
body: "Tap each set row to mark it complete as you train. The dot grid at the top shows your session progress in real-time.",
icon: "checkmark.square"),
.init(title: "Build Your Streak",
body: "Complete every scheduled workout day to grow your streak. Consistency compounds — your streak is shown at the top.",
icon: "flame"),
]
var workoutIsActive: Bool { !workoutDone }
var workoutCurrent: PageTip { workoutTips[min(workoutStep, workoutTips.count - 1)] }
var workoutIsLast: Bool { workoutStep == workoutTips.count - 1 }
func advanceWorkout() {
if workoutIsLast { withAnimation(KisaniSpring.exit) { workoutDone = true } }
else { withAnimation(KisaniSpring.entrance) { workoutStep += 1 } }
}
func skipWorkout() { withAnimation(KisaniSpring.exit) { workoutDone = true } }
}