Merge develop -> main: full session (KC-51 through KC-71) #28

Merged
kutesir merged 64 commits from develop into main 2026-07-12 22:57:14 +00:00
4 changed files with 27 additions and 1 deletions
Showing only changes of commit c56a6a9d21 - Show all commits

View File

@@ -7,7 +7,7 @@ import Foundation
/// Format: wenza://analytics overview /// Format: wenza://analytics overview
/// wenza://analytics/weekly/<yyyy-MM-dd> a weekly report /// wenza://analytics/weekly/<yyyy-MM-dd> a weekly report
/// wenza://analytics/monthly/<yyyy-MM> a monthly report /// wenza://analytics/monthly/<yyyy-MM> a monthly report
enum AnalyticsDeepLink: Equatable { enum AnalyticsDeepLink: Equatable, Identifiable {
case overview case overview
case weekly(weekStartKey: String) case weekly(weekStartKey: String)
case monthly(monthKey: String) case monthly(monthKey: String)
@@ -15,6 +15,8 @@ enum AnalyticsDeepLink: Equatable {
static let scheme = "wenza" static let scheme = "wenza"
static let host = "analytics" static let host = "analytics"
var id: String { stringValue }
/// Stable userInfo key carried on notifications. /// Stable userInfo key carried on notifications.
static let userInfoKey = "kisani.analytics.deeplink" static let userInfoKey = "kisani.analytics.deeplink"

View File

@@ -18,6 +18,7 @@ struct ContentView: View {
return 0 return 0
}() }()
@State private var showQuickAddTask = false @State private var showQuickAddTask = false
@State private var analyticsLink: AnalyticsDeepLink?
@StateObject private var tabState = FloatingTabState() @StateObject private var tabState = FloatingTabState()
@ObservedObject private var shortcuts = ShortcutHandler.shared @ObservedObject private var shortcuts = ShortcutHandler.shared
@ObservedObject private var tutorial = TutorialManager.shared @ObservedObject private var tutorial = TutorialManager.shared
@@ -108,6 +109,7 @@ struct ContentView: View {
// Backfill any missing analytics reports (idempotent), then notify. // Backfill any missing analytics reports (idempotent), then notify.
let generated = AnalyticsService.shared.reconcile(using: workoutVM) let generated = AnalyticsService.shared.reconcile(using: workoutVM)
NotificationManager.shared.scheduleAnalyticsReports(generated) NotificationManager.shared.scheduleAnalyticsReports(generated)
if let link = NotificationManager.shared.consumePendingAnalyticsDeepLink() { analyticsLink = link }
// Pull workouts from Health, then reschedule so a detected workout updates reminders. // Pull workouts from Health, then reschedule so a detected workout updates reminders.
Task { Task {
await workoutVM.syncFromHealthKit() await workoutVM.syncFromHealthKit()
@@ -166,6 +168,11 @@ struct ContentView: View {
.presentationDetents([.height(150)]) .presentationDetents([.height(150)])
.presentationDragIndicator(.visible) .presentationDragIndicator(.visible)
} }
.sheet(item: $analyticsLink) { link in
AnalyticsView(initialLink: link)
.presentationDetents([.large])
.presentationDragIndicator(.visible)
}
#if DEBUG #if DEBUG
.onAppear { .onAppear {
// Test hook: auto-open Quick Add (used to verify the voicefield binding // Test hook: auto-open Quick Add (used to verify the voicefield binding

View File

@@ -1627,3 +1627,11 @@ last. Device/build QA tracked here.
has issues only a compiler will surface. Remaining UI polish: deep-link routing has issues only a compiler will surface. Remaining UI polish: deep-link routing
into the presented view, Dynamic-Type audit, denied-permission state for future into the presented view, Dynamic-Type audit, denied-permission state for future
HealthKit trends. HealthKit trends.
### KC-51 progress — Phase 4c: deep-link routing end-to-end (core-verified)
- `AnalyticsDeepLink` now `Identifiable` (id = url string) → usable with
`.sheet(item:)`. Round-trip re-verified standalone.
- Notification tap → stores pending link (delegate) → `ContentView` consumes it on
launch + `.active` → presents `AnalyticsView(initialLink:)` opened to the
weekly/monthly area. Completes goal condition #7 (report notifications
deep-link to their report). Wiring not build-verified (SwiftUI/app deps).

View File

@@ -13,6 +13,15 @@ struct AnalyticsView: View {
@State private var area: Area = .overview @State private var area: Area = .overview
@State private var selectedExercise: String? @State private var selectedExercise: String?
/// Optional deep link (from a report notification) picks the initial area.
init(initialLink: AnalyticsDeepLink? = nil) {
switch initialLink {
case .weekly: _area = State(initialValue: .weekly)
case .monthly: _area = State(initialValue: .monthly)
default: break // keep default .overview
}
}
enum Area: String, CaseIterable, Identifiable { enum Area: String, CaseIterable, Identifiable {
case overview = "Overview", daily = "Daily", weekly = "Weekly" case overview = "Overview", daily = "Daily", weekly = "Weekly"
case monthly = "Monthly", trends = "12-Month", exercises = "Exercises" case monthly = "Monthly", trends = "12-Month", exercises = "Exercises"