USB-Meter / USB Meter / Views / Sidebar / SidebarList / Sections / SidebarUSBMetersSectionView.swift
Newer Older
117 lines | 4.004kb
Bogdan Timofte authored 2 months 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
Bogdan Timofte authored a month ago
16
    let onAddMeter: () -> Void
Bogdan Timofte authored 2 months ago
17

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

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

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

            
70
    private var usbSectionHeader: some View {
Bogdan Timofte authored 2 months ago
71
        HStack(alignment: .firstTextBaseline) {
72
            VStack(alignment: .leading, spacing: 2) {
73
                Text("USB & Known Meters")
74
                    .font(.headline)
75
                if meters.isEmpty == false {
76
                    Text(sectionSubtitleText)
77
                        .font(.caption)
78
                        .foregroundColor(.secondary)
79
                        .lineLimit(1)
80
                }
81
            }
Bogdan Timofte authored 2 months ago
82
            Spacer()
Bogdan Timofte authored a month ago
83
            Button(action: onAddMeter) {
84
                Image(systemName: "plus.circle.fill")
85
                    .font(.body.weight(.semibold))
86
                    .foregroundColor(.blue)
87
            }
88
            .buttonStyle(.plain)
Bogdan Timofte authored 2 months ago
89
            Text("\(meters.count)")
90
                .font(.caption.weight(.bold))
91
                .padding(.horizontal, 10)
92
                .padding(.vertical, 6)
93
                .meterCard(tint: .blue, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999)
94
        }
95
    }
Bogdan Timofte authored 2 months ago
96

            
97
    private var sectionSubtitleText: String {
98
        switch (liveMeterCount, offlineMeterCount) {
99
        case let (live, offline) where live > 0 && offline > 0:
100
            return "\(live) live • \(offline) stored"
101
        case let (live, _) where live > 0:
102
            return "\(live) live meter\(live == 1 ? "" : "s")"
103
        case let (_, offline) where offline > 0:
104
            return "\(offline) known meter\(offline == 1 ? "" : "s")"
105
        default:
106
            return ""
107
        }
108
    }
109

            
110
    private var liveMeterCount: Int {
111
        meters.filter { $0.meter != nil }.count
112
    }
113

            
114
    private var offlineMeterCount: Int {
115
        max(0, meters.count - liveMeterCount)
116
    }
Bogdan Timofte authored 2 months ago
117
}