// // SidebarUSBMetersSectionView.swift // USB Meter // import SwiftUI import CoreBluetooth struct SidebarUSBMetersSectionView: View { let meters: [AppData.MeterSummary] let managerState: CBManagerState let hasLiveMeters: Bool let scanStartedAt: Date? let now: Date let noDevicesHelpDelay: TimeInterval var body: some View { Section(header: usbSectionHeader) { if meters.isEmpty { Text(devicesEmptyStateText) .font(.footnote) .foregroundColor(.secondary) .frame(maxWidth: .infinity, alignment: .leading) .padding(18) .meterCard( tint: isWaitingForFirstDiscovery ? .blue : .secondary, fillOpacity: 0.14, strokeOpacity: 0.20 ) } else { ForEach(meters) { meterSummary in if let meter = meterSummary.meter { NavigationLink(destination: MeterView().environmentObject(meter)) { MeterRowView() .environmentObject(meter) } .buttonStyle(.plain) } else { NavigationLink(destination: MeterDetailView(meterSummary: meterSummary)) { MeterCardView(meterSummary: meterSummary) } .buttonStyle(.plain) } } } } } private var isWaitingForFirstDiscovery: Bool { guard managerState == .poweredOn else { return false } guard hasLiveMeters == false else { return false } guard let scanStartedAt else { return false } return now.timeIntervalSince(scanStartedAt) < noDevicesHelpDelay } private var devicesEmptyStateText: String { if isWaitingForFirstDiscovery { return "Scanning for nearby supported meters..." } return "No meters yet. Nearby supported meters will appear here and remain available after they disappear." } private var usbSectionHeader: some View { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 2) { Text("USB & Known Meters") .font(.headline) if meters.isEmpty == false { Text(sectionSubtitleText) .font(.caption) .foregroundColor(.secondary) .lineLimit(1) } } Spacer() Text("\(meters.count)") .font(.caption.weight(.bold)) .padding(.horizontal, 10) .padding(.vertical, 6) .meterCard(tint: .blue, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999) } } private var sectionSubtitleText: String { switch (liveMeterCount, offlineMeterCount) { case let (live, offline) where live > 0 && offline > 0: return "\(live) live • \(offline) stored" case let (live, _) where live > 0: return "\(live) live meter\(live == 1 ? "" : "s")" case let (_, offline) where offline > 0: return "\(offline) known meter\(offline == 1 ? "" : "s")" default: return "" } } private var liveMeterCount: Int { meters.filter { $0.meter != nil }.count } private var offlineMeterCount: Int { max(0, meters.count - liveMeterCount) } }