USB-Meter / USB Meter / Views / ContentView.swift
Newer Older
180 lines | 6.092kb
Bogdan Timofte authored 2 weeks ago
1
//
2
//  ContentView.swift
3
//  USB Meter
4
//
5
//  Created by Bogdan Timofte on 01/03/2020.
6
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
7
//
8

            
9
//MARK: Bluetooth Icon: https://upload.wikimedia.org/wikipedia/commons/d/da/Bluetooth.svg
10

            
11
import SwiftUI
12

            
13
struct ContentView: View {
14

            
15
    @EnvironmentObject private var appData: AppData
16

            
17
    var body: some View {
18
        NavigationView {
Bogdan Timofte authored 2 weeks ago
19
            ScrollView {
20
                VStack(alignment: .leading, spacing: 18) {
21
                    headerCard
22
                    helpSection
23
                    devicesSection
Bogdan Timofte authored 2 weeks ago
24
                }
Bogdan Timofte authored 2 weeks ago
25
                .padding()
Bogdan Timofte authored 2 weeks ago
26
            }
Bogdan Timofte authored 2 weeks ago
27
            .background(
28
                LinearGradient(
29
                    colors: [
30
                        appData.bluetoothManager.managerState.color.opacity(0.18),
31
                        Color.clear
32
                    ],
33
                    startPoint: .topLeading,
34
                    endPoint: .bottomTrailing
35
                )
36
                .ignoresSafeArea()
37
            )
Bogdan Timofte authored 2 weeks ago
38
            .navigationBarTitle(Text("USB Meters"), displayMode: .inline)
39
        }
Bogdan Timofte authored 2 weeks ago
40
        .onAppear {
41
            appData.bluetoothManager.start()
42
        }
Bogdan Timofte authored 2 weeks ago
43
    }
Bogdan Timofte authored 2 weeks ago
44

            
45
    private var headerCard: some View {
46
        VStack(alignment: .leading, spacing: 10) {
47
            Text("USB Meters")
48
                .font(.system(.title2, design: .rounded).weight(.bold))
49
            Text("Browse nearby supported meters and jump into live diagnostics, charge records, and device controls.")
50
                .font(.footnote)
51
                .foregroundColor(.secondary)
52
            HStack {
53
                Label("Bluetooth", systemImage: "bolt.horizontal.circle.fill")
54
                    .font(.footnote.weight(.semibold))
55
                    .foregroundColor(appData.bluetoothManager.managerState.color)
56
                Spacer()
57
                Text(bluetoothStatusText)
58
                    .font(.caption.weight(.semibold))
59
                    .foregroundColor(.secondary)
60
            }
61
        }
62
        .padding(18)
63
        .meterCard(tint: appData.bluetoothManager.managerState.color, fillOpacity: 0.22, strokeOpacity: 0.26)
64
    }
65

            
66
    private var helpSection: some View {
67
        VStack(alignment: .leading, spacing: 12) {
68
            Text("Help")
69
                .font(.headline)
70

            
71
            NavigationLink(destination: appData.bluetoothManager.managerState.helpView) {
72
                sidebarLinkCard(
73
                    title: "Bluetooth",
74
                    subtitle: "Permissions, adapter state, and connection tips.",
75
                    symbol: "bolt.horizontal.circle.fill",
76
                    tint: appData.bluetoothManager.managerState.color
77
                )
78
            }
79
            .buttonStyle(.plain)
80

            
81
            NavigationLink(destination: DeviceHelpView()) {
82
                sidebarLinkCard(
83
                    title: "Device",
84
                    subtitle: "Quick checks when a meter is not responding as expected.",
85
                    symbol: "questionmark.circle.fill",
86
                    tint: .orange
87
                )
88
            }
89
            .buttonStyle(.plain)
90
        }
91
    }
92

            
93
    private var devicesSection: some View {
94
        VStack(alignment: .leading, spacing: 12) {
95
            HStack {
96
                Text("Discovered Devices")
97
                    .font(.headline)
98
                Spacer()
99
                Text("\(appData.meters.count)")
100
                    .font(.caption.weight(.bold))
101
                    .padding(.horizontal, 10)
102
                    .padding(.vertical, 6)
103
                    .meterCard(tint: .blue, fillOpacity: 0.18, strokeOpacity: 0.24, cornerRadius: 999)
104
            }
105

            
106
            if appData.meters.isEmpty {
107
                Text("No supported meters are visible right now.")
108
                    .font(.footnote)
109
                    .foregroundColor(.secondary)
110
                    .frame(maxWidth: .infinity, alignment: .leading)
111
                    .padding(18)
112
                    .meterCard(tint: .secondary, fillOpacity: 0.14, strokeOpacity: 0.20)
113
            } else {
114
                ForEach(discoveredMeters, id: \.self) { meter in
115
                    NavigationLink(destination: MeterView().environmentObject(meter)) {
116
                        MeterRowView()
117
                            .environmentObject(meter)
118
                    }
119
                    .buttonStyle(.plain)
120
                }
121
            }
122
        }
123
    }
124

            
125
    private var discoveredMeters: [Meter] {
126
        Array(appData.meters.values).sorted { lhs, rhs in
127
            lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
128
        }
129
    }
130

            
131
    private var bluetoothStatusText: String {
132
        switch appData.bluetoothManager.managerState {
133
        case .poweredOff:
134
            return "Off"
135
        case .poweredOn:
136
            return "On"
137
        case .resetting:
138
            return "Resetting"
139
        case .unauthorized:
140
            return "Unauthorized"
141
        case .unknown:
142
            return "Unknown"
143
        case .unsupported:
144
            return "Unsupported"
145
        @unknown default:
146
            return "Other"
147
        }
148
    }
149

            
150
    private func sidebarLinkCard(
151
        title: String,
152
        subtitle: String,
153
        symbol: String,
154
        tint: Color
155
    ) -> some View {
156
        HStack(spacing: 14) {
157
            Image(systemName: symbol)
158
                .font(.system(size: 18, weight: .semibold))
159
                .foregroundColor(tint)
160
                .frame(width: 42, height: 42)
161
                .background(Circle().fill(tint.opacity(0.18)))
162

            
163
            VStack(alignment: .leading, spacing: 4) {
164
                Text(title)
165
                    .font(.headline)
166
                Text(subtitle)
167
                    .font(.caption)
168
                    .foregroundColor(.secondary)
169
            }
170

            
171
            Spacer()
172

            
173
            Image(systemName: "chevron.right")
174
                .font(.footnote.weight(.bold))
175
                .foregroundColor(.secondary)
176
        }
177
        .padding(14)
178
        .meterCard(tint: tint, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 18)
179
    }
Bogdan Timofte authored 2 weeks ago
180
}