TakePulseView.swift 6.73 KB
//
//  TakePulseView.swift
//  hippo-watch Watch App
//
//  Created by shihao on 2025/10/21.
//

import Foundation
import SwiftUI

struct TakePulseView: View {
    @StateObject private var pulseMonitor = PulseMonitor()
    @State private var showPulseDetailView = false
    @State private var showPulseFailedView = false
    @State private var displayType: TakePulseViewDisplayType = .waitForTakePulse

    enum TakePulseViewDisplayType {
        case waitForTakePulse
        case takingPulse
        case pulseFailed
    }
    
    var body: some View {
        ZStack(alignment: .top) {
            Image(.takePulseBg)
                .resizable()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .aspectRatio(contentMode: .fill)
                .ignoresSafeArea()
            
            switch displayType {
            case .waitForTakePulse:
                waitForTakePulseView
            case .takingPulse:
                takingPulseView
            case .pulseFailed:
                pulseFailedView
            }
            
            HStack {
                Image(.takePulseHelp)
                    .padding(.top, 22)
                    .padding(.leading, 8)
                    .onTapGesture {
                        showPulseFailedView = true
                    }
                
                Spacer()
            }
            
        }
        .fullScreenCover(isPresented: $showPulseDetailView, content: {
            if let pulseType = pulseMonitor.pulseType {
                PulseDetailView(pulseType: pulseType)
            }
        })
        .fullScreenCover(isPresented: $showPulseFailedView, content: {
            PulseFailedReasonView(isPresent: $showPulseFailedView)
        })
        .onAppear {
            ThinkSDKUtil.track(eventName: "enter_doublefeel_watch_page",
                               properties: ["page_type": "诊脉"])
        }
    }
    
    @ViewBuilder
    private var waitForTakePulseView: some View {
        VStack(spacing: 0) {
            
            Image(.takePulseIcon)
            .padding(.top, 35.x)
            
            Spacer()
            
            Text("请保持静坐,并将手腕放平")
                .font(.custom("Songti SC Regular", size: 11))
                .foregroundColor(.init(hex: "#896CDC"))
                .padding(.top, 10.x)
            
            Button {
                withAnimation {
                    displayType = .takingPulse
                    pulseMonitor.startDiagnosis { pulseType in
                        if let pulseType {
                            showPulseDetailView = true
                            reset()
                            Task {
                                let _ = try? await HealthService().uploadPulse(pulseType: pulseType)
                                WatchSessionManager.share.notifyPhoneToRefreshPulse()
                            }
                        }else {
                            displayType = .pulseFailed
                        }
                    }
                }
                ThinkSDKUtil.track(eventName: "click_doublefeel_watch_page",
                                   properties: ["page_type": "诊断",
                                                "click_content": "开始诊脉"])
            } label: {
                Text("立即诊脉")
                    .font(.system(size: 15, weight: .semibold))
                    .foregroundColor(.white)
                    .frame(height: 36, alignment: .center)
                    .frame(maxWidth: .infinity)
                    .background(Color(hex: "#896CDC"))
                    .cornerRadius(18)
            }
            .padding(.top, 4.x)
            .padding(.horizontal, 8)
            .padding(.bottom, 10.x)
        }
    }
    
    @ViewBuilder
    private var takingPulseView: some View {
        VStack(spacing: 0) {
            
            ZStack(alignment: .center) {
                RingProgressView(progress: pulseMonitor.progress, color: .init(hex: "#896CDC"), size: 110)
                
                Image(.takePulseIcon)
                    
            }
            .padding(.top, 38.x)
            
            Spacer()
            
            Text("正在分析数据\n诊脉中…")
                .font(.custom("Songti SC Regular", size: 11))
                .foregroundColor(.init(hex: "#896CDC"))
                .padding(.top, 19.x)
                .padding(.bottom, 24.x)
                .multilineTextAlignment(.center)
        }
        .background (
            AnimatedGIFView(gifName: "takePulseBg")
                .frame(width: ScreenSize.width, height: ScreenSize.height)
        )
    }
    
    @ViewBuilder
    private var pulseFailedView: some View {
        VStack(alignment: .center, spacing: 0) {
            Spacer()
            Text("诊断失败\n未读取到数据")
                .font(.custom("Songti SC Regular", size: 11))
                .foregroundColor(.init(hex: "#896CDC"))
                .multilineTextAlignment(.center)
            Spacer()
            Button {
                withAnimation {
                    showPulseFailedView = true
                    reset()
                }
                ThinkSDKUtil.track(eventName: "click_doublefeel_watch_page",
                                   properties: ["page_type": "诊断",
                                                "click_content": "为何会诊脉失败"])
            } label: {
                Text("为何会诊脉失败")
                    .font(.system(size: 15, weight: .semibold))
                    .foregroundColor(.white)
                    .frame(height: 36, alignment: .center)
                    .frame(maxWidth: .infinity)
                    .background(Color(hex: "#896CDC"))
                    .cornerRadius(18)
            }
            .padding(.horizontal, 8)
            .padding(.bottom, 18.x)
        }
    }
    
    private func reset() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            displayType = .waitForTakePulse
            pulseMonitor.progress = 0
        }
    }
}


fileprivate struct RingProgressView: View {
    var progress: Double         // 0.0 ~ 1.0
    var lineWidth: CGFloat = 4
    var color: Color = .blue
    var size: CGFloat = 100

    var body: some View {
        ZStack {
            // 背景环
            Circle()
                .stroke(Color.gray.opacity(0.2), lineWidth: lineWidth)

            // 前景进度环
            Circle()
                .trim(from: 0, to: CGFloat(progress))
                .stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
                .rotationEffect(.degrees(-90)) // 从顶部开始
                .animation(.easeInOut(duration: 0.3), value: progress)
        }
        .frame(width: size, height: size)
    }
}