Newer Older
182 lines | 5.824kb
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",
Bogdan Timofte authored a month ago
85
                mode: .device,
Bogdan Timofte authored a month ago
86
                chargedDevices: appData.deviceSummaries,
87
                emptyStateText: "No devices yet. Open Charge Record on a live meter or use the add button here to create one and start learning capacity.",
88
                tint: .orange,
89
                onAdd: { creationSheet = .device }
90
            )
91

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

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

            
120
    private var debugSection: some View {
121
        SidebarDebugSectionView()
122
    }
123

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

            
143
    private var helpIsExpanded: Bool {
144
        isHelpExpanded || shouldAutoExpandHelp
145
    }
146

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

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

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

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