Newer Older
180 lines | 5.761kb
Bogdan Timofte authored 2 months ago
1
//
2
//  SidebarView.swift
3
//  USB Meter
4
//
5

            
6
import SwiftUI
7
import Combine
8

            
Bogdan Timofte authored a month ago
9
private enum SidebarCreationSheet: Identifiable {
10
    case meter
11
    case device
12
    case charger
13

            
14
    var id: String {
15
        switch self {
16
        case .meter:
17
            return "meter"
18
        case .device:
19
            return "device"
20
        case .charger:
21
            return "charger"
22
        }
23
    }
24
}
25

            
Bogdan Timofte authored 2 months ago
26
struct SidebarView: View {
27
    @EnvironmentObject private var appData: AppData
28
    @State private var isHelpExpanded = false
29
    @State private var dismissedAutoHelpReason: SidebarHelpReason?
30
    @State private var now = Date()
Bogdan Timofte authored a month ago
31
    @State private var creationSheet: SidebarCreationSheet?
Bogdan Timofte authored 2 months ago
32
    private let helpRefreshTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
33
    private let noDevicesHelpDelay: TimeInterval = 12
34

            
35
    var body: some View {
36
        SidebarListView(backgroundTint: appData.bluetoothManager.managerState.color) {
37
            usbMetersSection
38
        } helpSection: {
39
            helpSection
40
        } debugSection: {
41
            debugSection
42
        }
43
        .onAppear {
44
            appData.bluetoothManager.start()
45
            now = Date()
46
        }
47
        .onReceive(helpRefreshTimer) { currentDate in
48
            now = currentDate
49
        }
50
        .onChange(of: activeHelpAutoReason) { newReason in
51
            if newReason == nil {
52
                dismissedAutoHelpReason = nil
53
            }
54
        }
Bogdan Timofte authored a month ago
55
        .sheet(item: $creationSheet) { sheet in
56
            switch sheet {
57
            case .meter:
58
                MeterEditorSheetView()
59
                    .environmentObject(appData)
60
            case .device:
61
                ChargedDeviceEditorSheetView(
Bogdan Timofte authored a month ago
62
                    meterMACAddress: nil
Bogdan Timofte authored a month ago
63
                )
64
                .environmentObject(appData)
65
            case .charger:
Bogdan Timofte authored a month ago
66
                ChargerEditorSheetView(appData: appData)
Bogdan Timofte authored a month ago
67
            }
68
        }
Bogdan Timofte authored 2 months ago
69
    }
70

            
71
    private var usbMetersSection: some View {
Bogdan Timofte authored a month ago
72
        Group {
73
            SidebarUSBMetersSectionView(
74
                meters: appData.meterSummaries,
75
                managerState: appData.bluetoothManager.managerState,
76
                hasLiveMeters: appData.meters.isEmpty == false,
77
                scanStartedAt: appData.bluetoothManager.scanStartedAt,
78
                now: now,
79
                noDevicesHelpDelay: noDevicesHelpDelay,
80
                onAddMeter: { creationSheet = .meter }
81
            )
82

            
83
            SidebarChargedDevicesSectionView(
84
                title: "Devices",
85
                chargedDevices: appData.deviceSummaries,
86
                emptyStateText: "No devices yet. Open Charge Record on a live meter or use the add button here to create one and start learning capacity.",
87
                tint: .orange,
88
                onAdd: { creationSheet = .device }
89
            )
90

            
91
            SidebarChargedDevicesSectionView(
92
                title: "Chargers",
93
                chargedDevices: appData.chargerSummaries,
94
                emptyStateText: "No chargers yet. Add one here so wireless sessions can track both the charged device and the charger being used.",
95
                tint: .pink,
96
                onAdd: { creationSheet = .charger }
97
            )
98
        }
Bogdan Timofte authored 2 months ago
99
    }
100

            
101
    private var helpSection: some View {
102
        SidebarHelpSectionView(
103
            activeReason: activeHelpAutoReason,
104
            isExpanded: helpIsExpanded,
105
            bluetoothStatusTint: appData.bluetoothManager.managerState.color,
106
            bluetoothStatusText: bluetoothStatusText,
107
            cloudSyncHelpTitle: appData.cloudAvailability.helpTitle,
108
            cloudSyncHelpMessage: appData.cloudAvailability.helpMessage,
109
            onToggle: toggleHelpSection,
110
            onOpenSettings: openSettings
111
        ) {
112
            appData.bluetoothManager.managerState.helpView
113
        } deviceHelpDestination: {
114
            DeviceHelpView()
115
        }
116
    }
117

            
118
    private var debugSection: some View {
119
        SidebarDebugSectionView()
120
    }
121

            
122
    private var bluetoothStatusText: String {
123
        switch appData.bluetoothManager.managerState {
124
        case .poweredOff:
125
            return "Off"
126
        case .poweredOn:
127
            return "On"
128
        case .resetting:
129
            return "Resetting"
130
        case .unauthorized:
131
            return "Unauthorized"
132
        case .unknown:
133
            return "Unknown"
134
        case .unsupported:
135
            return "Unsupported"
136
        @unknown default:
137
            return "Other"
138
        }
139
    }
140

            
141
    private var helpIsExpanded: Bool {
142
        isHelpExpanded || shouldAutoExpandHelp
143
    }
144

            
145
    private var shouldAutoExpandHelp: Bool {
146
        guard let activeHelpAutoReason else {
147
            return false
148
        }
149
        return dismissedAutoHelpReason != activeHelpAutoReason
150
    }
151

            
152
    private var activeHelpAutoReason: SidebarHelpReason? {
153
        SidebarAutoHelpResolver.activeReason(
154
            managerState: appData.bluetoothManager.managerState,
155
            cloudAvailability: appData.cloudAvailability,
156
            hasLiveMeters: appData.meters.isEmpty == false,
157
            scanStartedAt: appData.bluetoothManager.scanStartedAt,
158
            now: now,
159
            noDevicesHelpDelay: noDevicesHelpDelay
160
        )
161
    }
162

            
163
    private func toggleHelpSection() {
164
        withAnimation(.easeInOut(duration: 0.22)) {
165
            if shouldAutoExpandHelp {
166
                dismissedAutoHelpReason = activeHelpAutoReason
167
                isHelpExpanded = false
168
            } else {
169
                isHelpExpanded.toggle()
170
            }
171
        }
172
    }
173

            
174
    private func openSettings() {
175
        guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else {
176
            return
177
        }
178
        UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
179
    }
180
}