// // WaterView.swift // 摸鱼侠 plugin v0.1 // // 喝水 — 8 cup target with progress dots, tap big cup to log a sip. // import SwiftUI struct WaterView: View { @ObservedObject var store: WorkerStore var body: some View { VStack(spacing: 18) { statusBadge cupHero controls Divider() .background(WorkerTheme.overlay08) .padding(.horizontal, 24) goalRow tipText Spacer(minLength: 0) } .padding(.top, 8) } // MARK: - Status private var statusBadge: some View { HStack(spacing: 8) { Circle() .fill(WorkerTheme.water) .frame(width: 8, height: 8) .shadow(color: WorkerTheme.water.opacity(0.7), radius: 4) Text(badgeText) .font(.system(size: 12.5, weight: .semibold)) .foregroundColor(WorkerTheme.fg85) } .padding(.horizontal, 12) .frame(height: 26) .background(Capsule().fill(WorkerTheme.overlay06)) } private var badgeText: String { if store.waterCupsToday >= store.waterGoal { return "今日目标已达成 ✨" } return "今日 \(store.waterCupsToday) / \(store.waterGoal) 杯" } // MARK: - Cup hero (tappable) private var cupHero: some View { VStack(spacing: 12) { Button(action: { store.waterAddCup() }) { ZStack { // Water-fill cup illustration. cupShape .frame(width: 130, height: 160) } } .buttonStyle(.plain) .help("点击杯子记录一杯水") // Progress dots HStack(spacing: 6) { ForEach(0..= store.waterGoal ? "今天的水喝够了,给自己鼓个掌 👏" : "保持每小时一杯,工作更高效。") .font(.system(size: 11)) .foregroundColor(WorkerTheme.fg45) .padding(.horizontal, 20) .multilineTextAlignment(.center) .frame(maxWidth: .infinity) } } // MARK: - Cup outline shape private struct CupOutline: Shape { func path(in rect: CGRect) -> Path { // Slight trapezoid: narrower at the bottom. Straight rim with // a 4pt rounded base. var p = Path() let topInset: CGFloat = 0 let bottomInset: CGFloat = rect.width * 0.08 let radius: CGFloat = 8 // Start top-left. p.move(to: CGPoint(x: rect.minX + topInset, y: rect.minY)) // Top edge. p.addLine(to: CGPoint(x: rect.maxX - topInset, y: rect.minY)) // Right side (sloping inward). p.addLine(to: CGPoint(x: rect.maxX - bottomInset, y: rect.maxY - radius)) // Bottom-right curve. p.addQuadCurve( to: CGPoint(x: rect.maxX - bottomInset - radius, y: rect.maxY), control: CGPoint(x: rect.maxX - bottomInset, y: rect.maxY) ) // Bottom edge. p.addLine(to: CGPoint(x: rect.minX + bottomInset + radius, y: rect.maxY)) // Bottom-left curve. p.addQuadCurve( to: CGPoint(x: rect.minX + bottomInset, y: rect.maxY - radius), control: CGPoint(x: rect.minX + bottomInset, y: rect.maxY) ) // Left side back to top. p.addLine(to: CGPoint(x: rect.minX + topInset, y: rect.minY)) p.closeSubpath() return p } }