| 1 |
// |
|
| 2 |
// ChargerEditorSheetView.swift |
|
| 3 |
// USB Meter |
|
| 4 |
// |
|
| 5 | ||
| 6 |
import SwiftUI |
|
| 7 | ||
| 8 |
struct ChargerEditorSheetView: View {
|
|
| 9 |
@Environment(\.dismiss) private var dismiss |
|
| 10 | ||
| 11 |
let appData: AppData |
|
| 12 |
let chargedDevice: ChargedDeviceSummary? |
|
| 13 |
let meterMACAddress: String? |
|
| 14 |
/// When false the view omits its own NavigationView (used as a push destination). |
|
| 15 |
let standalone: Bool |
|
| 16 | ||
| 17 |
@State private var name: String |
|
| 18 |
@State private var chargerType: ChargerType |
|
| 19 |
@State private var notes: String |
|
| 20 | ||
| 21 |
init( |
|
| 22 |
appData: AppData, |
|
| 23 |
chargedDevice: ChargedDeviceSummary? = nil, |
|
| 24 |
meterMACAddress: String? = nil, |
|
| 25 |
standalone: Bool = true |
|
| 26 |
) {
|
|
| 27 |
self.appData = appData |
|
| 28 |
self.chargedDevice = chargedDevice |
|
| 29 |
self.meterMACAddress = meterMACAddress |
|
| 30 |
self.standalone = standalone |
|
| 31 |
_name = State(initialValue: chargedDevice?.name ?? "") |
|
| 32 |
_chargerType = State(initialValue: chargedDevice?.chargerType ?? .genericQi) |
|
| 33 |
_notes = State(initialValue: chargedDevice?.notes ?? "") |
|
| 34 |
} |
|
| 35 | ||
| 36 |
var body: some View {
|
|
| 37 |
if standalone {
|
|
| 38 |
NavigationView { formContent }
|
|
| 39 |
.navigationViewStyle(StackNavigationViewStyle()) |
|
| 40 |
} else {
|
|
| 41 |
formContent |
|
| 42 |
} |
|
| 43 |
} |
|
| 44 | ||
| 45 |
private var formContent: some View {
|
|
| 46 |
Form {
|
|
| 47 |
Section(header: Text("Identity")) {
|
|
| 48 |
TextField("Charger name", text: $name)
|
|
| 49 | ||
| 50 |
if let chargedDevice {
|
|
| 51 |
Text(chargedDevice.qrIdentifier) |
|
| 52 |
.font(.caption.monospaced()) |
|
| 53 |
.foregroundColor(.secondary) |
|
| 54 |
.textSelection(.enabled) |
|
| 55 |
} |
|
| 56 |
} |
|
| 57 | ||
| 58 |
Section( |
|
| 59 |
header: ContextInfoHeader( |
|
| 60 |
title: "Charger Type", |
|
| 61 |
message: "MagSafe and Watch chargers use magnetic alignment, enabling accurate efficiency calibration. Standby current and efficiency are learned automatically from sessions." |
|
| 62 |
) |
|
| 63 |
) {
|
|
| 64 |
Picker("Type", selection: $chargerType) {
|
|
| 65 |
ForEach(ChargerType.allCases) { type in
|
|
| 66 |
Label(type.title, systemImage: type.symbolName) |
|
| 67 |
.tag(type) |
|
| 68 |
} |
|
| 69 |
} |
|
| 70 |
.pickerStyle(.menu) |
|
| 71 |
} |
|
| 72 | ||
| 73 |
Section(header: Text("Notes")) {
|
|
| 74 |
TextField("Optional notes", text: $notes)
|
|
| 75 |
} |
|
| 76 |
} |
|
| 77 |
.navigationTitle(chargedDevice == nil ? "New Charger" : "Edit Charger") |
|
| 78 |
.navigationBarTitleDisplayMode(.inline) |
|
| 79 |
.toolbar {
|
|
| 80 |
ToolbarItem(placement: .cancellationAction) {
|
|
| 81 |
Button("Cancel") { dismiss() }
|
|
| 82 |
} |
|
| 83 |
ToolbarItem(placement: .confirmationAction) {
|
|
| 84 |
Button(chargedDevice == nil ? "Save" : "Update") {
|
|
| 85 |
save() |
|
| 86 |
} |
|
| 87 |
.disabled(name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) |
|
| 88 |
} |
|
| 89 |
} |
|
| 90 |
} |
|
| 91 | ||
| 92 |
private func save() {
|
|
| 93 |
let trimmedNotes = notes.trimmingCharacters(in: .whitespacesAndNewlines) |
|
| 94 |
let notesValue: String? = trimmedNotes.isEmpty ? nil : trimmedNotes |
|
| 95 | ||
| 96 |
let didSave: Bool |
|
| 97 |
if let chargedDevice {
|
|
| 98 |
didSave = appData.updateCharger( |
|
| 99 |
id: chargedDevice.id, |
|
| 100 |
name: name, |
|
| 101 |
chargerType: chargerType, |
|
| 102 |
notes: notesValue |
|
| 103 |
) |
|
| 104 |
} else {
|
|
| 105 |
didSave = appData.createCharger( |
|
| 106 |
name: name, |
|
| 107 |
chargerType: chargerType, |
|
| 108 |
notes: notesValue, |
|
| 109 |
meterMACAddress: meterMACAddress |
|
| 110 |
) |
|
| 111 |
} |
|
| 112 | ||
| 113 |
if didSave {
|
|
| 114 |
dismiss() |
|
| 115 |
} |
|
| 116 |
} |
|
| 117 |
} |