USB-Meter / USB Meter / Views / Sidebar / SidebarList / Sections / SidebarUSBMetersSectionView.swift
Newer Older
110 lines | 3.736kb
Bogdan Timofte authored a week ago
1
//
2
//  SidebarUSBMetersSectionView.swift
3
//  USB Meter
4
//
5

            
6
import SwiftUI
7
import CoreBluetooth
8

            
9
struct SidebarUSBMetersSectionView: View {
10
    let meters: [AppData.MeterSummary]
11
    let managerState: CBManagerState
12
    let hasLiveMeters: Bool
13
    let scanStartedAt: Date?
14
    let now: Date
15
    let noDevicesHelpDelay: TimeInterval
16

            
17
    var body: some View {
18
        Section(header: usbSectionHeader) {
19
            if meters.isEmpty {
20
                Text(devicesEmptyStateText)
21
                    .font(.footnote)
22
                    .foregroundColor(.secondary)
23
                    .frame(maxWidth: .infinity, alignment: .leading)
24
                    .padding(18)
25
                    .meterCard(
26
                        tint: isWaitingForFirstDiscovery ? .blue : .secondary,
27
                        fillOpacity: 0.14,
28
                        strokeOpacity: 0.20
29
                    )
30
            } else {
31
                ForEach(meters) { meterSummary in
32
                    if let meter = meterSummary.meter {
33
                        NavigationLink(destination: MeterView().environmentObject(meter)) {
34
                            MeterRowView()
35
                                .environmentObject(meter)
36
                        }
37
                        .buttonStyle(.plain)
38
                    } else {
39
                        NavigationLink(destination: MeterDetailView(meterSummary: meterSummary)) {
40
                            MeterCardView(meterSummary: meterSummary)
41
                        }
42
                        .buttonStyle(.plain)
43
                    }
44
                }
45
            }
46
        }
47
    }
48

            
49
    private var isWaitingForFirstDiscovery: Bool {
50
        guard managerState == .poweredOn else {
51
            return false
52
        }
53
        guard hasLiveMeters == false else {
54
            return false
55
        }
56
        guard let scanStartedAt else {
57
            return false
58
        }
59
        return now.timeIntervalSince(scanStartedAt) < noDevicesHelpDelay
60
    }
61

            
62
    private var devicesEmptyStateText: String {
63
        if isWaitingForFirstDiscovery {
64
            return "Scanning for nearby supported meters..."
65
        }
66
        return "No meters yet. Nearby supported meters will appear here and remain available after they disappear."
67
    }
68

            
69
    private var usbSectionHeader: some View {
Bogdan Timofte authored a week ago
70
        HStack(alignment: .firstTextBaseline) {
71
            VStack(alignment: .leading, spacing: 2) {
72
                Text("USB & Known Meters")
73
                    .font(.headline)
74
                if meters.isEmpty == false {
75
                    Text(sectionSubtitleText)
76
                        .font(.caption)
77
                        .foregroundColor(.secondary)
78
                        .lineLimit(1)
79
                }
80
            }
Bogdan Timofte authored a week ago
81
            Spacer()
82
            Text("\(meters.count)")
83
                .font(.caption.weight(.bold))
84
                .padding(.horizontal, 10)
85
                .padding(.vertical, 6)
86
                .meterCard(tint: .blue, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999)
87
        }
88
    }
Bogdan Timofte authored a week ago
89

            
90
    private var sectionSubtitleText: String {
91
        switch (liveMeterCount, offlineMeterCount) {
92
        case let (live, offline) where live > 0 && offline > 0:
93
            return "\(live) live • \(offline) stored"
94
        case let (live, _) where live > 0:
95
            return "\(live) live meter\(live == 1 ? "" : "s")"
96
        case let (_, offline) where offline > 0:
97
            return "\(offline) known meter\(offline == 1 ? "" : "s")"
98
        default:
99
            return ""
100
        }
101
    }
102

            
103
    private var liveMeterCount: Int {
104
        meters.filter { $0.meter != nil }.count
105
    }
106

            
107
    private var offlineMeterCount: Int {
108
        max(0, meters.count - liveMeterCount)
109
    }
Bogdan Timofte authored a week ago
110
}