feat: implement Haptika PPG and Watch V1
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import SwiftUI
|
||||
|
||||
struct WatchRootView: View {
|
||||
@EnvironmentObject private var model: WatchAppModel
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if model.hasCompletedOnboarding {
|
||||
WatchHomeView()
|
||||
} else {
|
||||
WatchOnboardingView()
|
||||
}
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchOnboardingView: View {
|
||||
@EnvironmentObject private var model: WatchAppModel
|
||||
@State private var page = 0
|
||||
@State private var authorizationError: String?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
TabView(selection: $page) {
|
||||
onboardingPage(icon: "hand.raised.fill", title: "Vos données", text: "Les mesures restent sur vos appareils et se synchronisent avec votre iPhone appairé.").tag(0)
|
||||
onboardingPage(icon: "heart.text.square.fill", title: "Bien-être, pas diagnostic", text: "Haptika n’est pas un dispositif médical. Consultez un professionnel en cas de symptôme.").tag(1)
|
||||
onboardingPage(icon: "waveform.path.ecg", title: "Mesurer sans vibrer", text: "La Watch mesure le pouls optique. Les vibrations sont toujours arrêtées pendant cette phase.").tag(2)
|
||||
}
|
||||
.tabViewStyle(.verticalPage)
|
||||
|
||||
if let authorizationError {
|
||||
Text(authorizationError).font(.caption2).foregroundStyle(.orange)
|
||||
}
|
||||
|
||||
Button(page < 2 ? "Continuer" : "Autoriser Santé") {
|
||||
if page < 2 {
|
||||
withAnimation { page += 1 }
|
||||
} else {
|
||||
Task {
|
||||
do {
|
||||
try await model.measurement.requestAuthorization()
|
||||
model.hasCompletedOnboarding = true
|
||||
} catch {
|
||||
authorizationError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding(.horizontal, 6)
|
||||
.background(WatchTheme.background.ignoresSafeArea())
|
||||
}
|
||||
|
||||
private func onboardingPage(icon: String, title: String, text: String) -> some View {
|
||||
VStack(spacing: 10) {
|
||||
Image(systemName: icon).font(.system(size: 34)).foregroundStyle(WatchTheme.accent)
|
||||
Text(title).font(.headline).multilineTextAlignment(.center)
|
||||
Text(text).font(.caption).foregroundStyle(WatchTheme.secondary).multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchHomeView: View {
|
||||
@EnvironmentObject private var model: WatchAppModel
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
Section {
|
||||
NavigationLink(value: WatchHomeDestination.measure) { homeRow("Mesurer", "waveform.path.ecg", WatchTheme.mint) }
|
||||
NavigationLink(value: WatchHomeDestination.coherence) { homeRow("Ma Cohérence", "circle.dotted", WatchTheme.accent) }
|
||||
NavigationLink(value: WatchHomeDestination.exercises) { homeRow("Exercices", "wind", .cyan) }
|
||||
NavigationLink(value: WatchHomeDestination.cardiacMirror) { homeRow("Miroir cardiaque", "heart.circle", .pink) }
|
||||
NavigationLink(value: WatchHomeDestination.history) { homeRow("Historique", "chart.xyaxis.line", .indigo) }
|
||||
} header: {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("HAPTIKA").font(.caption2).foregroundStyle(WatchTheme.accent)
|
||||
Text("Prendre un temps").font(.headline).foregroundStyle(.white)
|
||||
}
|
||||
.textCase(nil)
|
||||
}
|
||||
|
||||
Section {
|
||||
HStack {
|
||||
Circle().fill(model.connectivity.isReachable ? WatchTheme.mint : .gray).frame(width: 7, height: 7)
|
||||
Text(model.connectivity.isReachable ? "iPhone joignable" : "Mode autonome")
|
||||
}
|
||||
.font(.caption2)
|
||||
.foregroundStyle(WatchTheme.secondary)
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: WatchHomeDestination.self) { destination in
|
||||
switch destination {
|
||||
case .measure: WatchMeasurementView()
|
||||
case .coherence: WatchCoherenceView()
|
||||
case .exercises: WatchExerciseListView()
|
||||
case .cardiacMirror: WatchCardiacMirrorView()
|
||||
case .history: WatchHistoryView()
|
||||
}
|
||||
}
|
||||
.haptikaScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private func homeRow(_ title: String, _ icon: String, _ color: Color) -> some View {
|
||||
Label {
|
||||
Text(title).font(.subheadline.weight(.semibold))
|
||||
} icon: {
|
||||
Image(systemName: icon).foregroundStyle(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user