analytics(deeplink): route report notification tap to its report (KC-51 ph4c)

Completes deep-link routing (goal #7): AnalyticsDeepLink is Identifiable; a
tapped weekly/monthly report notification is consumed in ContentView on
launch/active and presents AnalyticsView opened to that area. Core round-trip
re-verified standalone; SwiftUI wiring needs an Xcode build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-07-07 23:36:45 +03:00
committed by kutesir
parent f8a1f90f8c
commit bcbcda5ba5
4 changed files with 27 additions and 1 deletions

View File

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

View File

@@ -18,6 +18,7 @@ struct ContentView: View {
return 0
}()
@State private var showQuickAddTask = false
@State private var analyticsLink: AnalyticsDeepLink?
@StateObject private var tabState = FloatingTabState()
@ObservedObject private var shortcuts = ShortcutHandler.shared
@ObservedObject private var tutorial = TutorialManager.shared
@@ -108,6 +109,7 @@ struct ContentView: View {
// Backfill any missing analytics reports (idempotent), then notify.
let generated = AnalyticsService.shared.reconcile(using: workoutVM)
NotificationManager.shared.scheduleAnalyticsReports(generated)
if let link = NotificationManager.shared.consumePendingAnalyticsDeepLink() { analyticsLink = link }
// Pull workouts from Health, then reschedule so a detected workout updates reminders.
Task {
await workoutVM.syncFromHealthKit()
@@ -166,6 +168,11 @@ struct ContentView: View {
.presentationDetents([.height(150)])
.presentationDragIndicator(.visible)
}
.sheet(item: $analyticsLink) { link in
AnalyticsView(initialLink: link)
.presentationDetents([.large])
.presentationDragIndicator(.visible)
}
#if DEBUG
.onAppear {
// 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
into the presented view, Dynamic-Type audit, denied-permission state for future
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 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 {
case overview = "Overview", daily = "Daily", weekly = "Weekly"
case monthly = "Monthly", trends = "12-Month", exercises = "Exercises"