@@ -42,6 +42,28 @@ struct MeterChargeRecordContentView: View {
|
||
| 42 | 42 |
case standbyPower |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
+ private enum FinalCheckpoint: Hashable {
|
|
| 46 |
+ case full |
|
| 47 |
+ case skip |
|
| 48 |
+ case custom |
|
| 49 |
+ |
|
| 50 |
+ var label: String {
|
|
| 51 |
+ switch self {
|
|
| 52 |
+ case .full: return "Full" |
|
| 53 |
+ case .skip: return "Skip" |
|
| 54 |
+ case .custom: return "Other %" |
|
| 55 |
+ } |
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ var icon: String {
|
|
| 59 |
+ switch self {
|
|
| 60 |
+ case .full: return "battery.100percent" |
|
| 61 |
+ case .skip: return "minus.circle" |
|
| 62 |
+ case .custom: return "pencil" |
|
| 63 |
+ } |
|
| 64 |
+ } |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 45 | 67 |
private enum SessionStartRequirement: Identifiable {
|
| 46 | 68 |
case existingSession |
| 47 | 69 |
case device |
@@ -76,35 +98,13 @@ struct MeterChargeRecordContentView: View {
|
||
| 76 | 98 |
} |
| 77 | 99 |
} |
| 78 | 100 |
|
| 79 |
- private enum FinalCheckpoint: Hashable {
|
|
| 80 |
- case full |
|
| 81 |
- case skip |
|
| 82 |
- case custom |
|
| 83 |
- |
|
| 84 |
- var label: String {
|
|
| 85 |
- switch self {
|
|
| 86 |
- case .full: return "Full" |
|
| 87 |
- case .skip: return "Skip" |
|
| 88 |
- case .custom: return "Other %" |
|
| 89 |
- } |
|
| 90 |
- } |
|
| 91 |
- |
|
| 92 |
- var icon: String {
|
|
| 93 |
- switch self {
|
|
| 94 |
- case .full: return "battery.100percent" |
|
| 95 |
- case .skip: return "minus.circle" |
|
| 96 |
- case .custom: return "pencil" |
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- @EnvironmentObject private var appData: AppData |
|
| 101 |
+@EnvironmentObject private var appData: AppData |
|
| 102 | 102 |
@EnvironmentObject private var usbMeter: Meter |
| 103 | 103 |
|
| 104 | 104 |
@State private var showingInlineTargetEditor = false |
| 105 | 105 |
@State private var draftTargetText = "" |
| 106 | 106 |
@State private var showingStopConfirm = false |
| 107 |
- @State private var finalCheckpointMode: FinalCheckpoint = .full |
|
| 107 |
+ @State private var finalCheckpointMode: FinalCheckpoint = .skip |
|
| 108 | 108 |
@State private var finalCheckpointText = "" |
| 109 | 109 |
@State private var pendingCheckpointDeletion: ChargeCheckpointSummary? |
| 110 | 110 |
@State private var draftChargingTransportMode: ChargingTransportMode? |
@@ -705,9 +705,7 @@ struct MeterChargeRecordContentView: View {
|
||
| 705 | 705 |
Text("The device associated with this session no longer exists. Stop the session to close it.")
|
| 706 | 706 |
.font(.caption) |
| 707 | 707 |
.foregroundColor(.secondary) |
| 708 |
- Button("Stop Session") {
|
|
| 709 |
- finalCheckpointMode = .skip |
|
| 710 |
- finalCheckpointText = "" |
|
| 708 |
+ Button("Terminate Session") {
|
|
| 711 | 709 |
_ = appData.stopChargeSession( |
| 712 | 710 |
sessionID: openChargeSession.id, |
| 713 | 711 |
finalBatteryPercent: nil |
@@ -803,8 +801,8 @@ struct MeterChargeRecordContentView: View {
|
||
| 803 | 801 |
.buttonStyle(.plain) |
| 804 | 802 |
} |
| 805 | 803 |
|
| 806 |
- Button("Stop") {
|
|
| 807 |
- finalCheckpointMode = .full |
|
| 804 |
+ Button("Terminate Session") {
|
|
| 805 |
+ finalCheckpointMode = .skip |
|
| 808 | 806 |
finalCheckpointText = "" |
| 809 | 807 |
showingStopConfirm = true |
| 810 | 808 |
} |
@@ -1039,7 +1037,7 @@ struct MeterChargeRecordContentView: View {
|
||
| 1039 | 1037 |
|
| 1040 | 1038 |
HStack(spacing: 10) {
|
| 1041 | 1039 |
Button("Finish") {
|
| 1042 |
- finalCheckpointMode = .full |
|
| 1040 |
+ finalCheckpointMode = .skip |
|
| 1043 | 1041 |
finalCheckpointText = "" |
| 1044 | 1042 |
showingStopConfirm = true |
| 1045 | 1043 |
} |
@@ -1206,14 +1204,11 @@ struct MeterChargeRecordContentView: View {
|
||
| 1206 | 1204 |
Text("Final Checkpoint (optional)")
|
| 1207 | 1205 |
.font(.subheadline.weight(.semibold)) |
| 1208 | 1206 |
|
| 1209 |
- // Three compact option tiles |
|
| 1210 | 1207 |
HStack(spacing: 8) {
|
| 1211 | 1208 |
ForEach([FinalCheckpoint.full, .skip, .custom], id: \.self) { mode in
|
| 1212 | 1209 |
Button {
|
| 1213 | 1210 |
finalCheckpointMode = mode |
| 1214 |
- if mode != .custom {
|
|
| 1215 |
- finalCheckpointText = "" |
|
| 1216 |
- } |
|
| 1211 |
+ if mode != .custom { finalCheckpointText = "" }
|
|
| 1217 | 1212 |
} label: {
|
| 1218 | 1213 |
VStack(spacing: 5) {
|
| 1219 | 1214 |
Image(systemName: mode.icon) |
@@ -1225,11 +1220,7 @@ struct MeterChargeRecordContentView: View {
|
||
| 1225 | 1220 |
} |
| 1226 | 1221 |
.frame(maxWidth: .infinity) |
| 1227 | 1222 |
.padding(.vertical, 10) |
| 1228 |
- .background( |
|
| 1229 |
- finalCheckpointMode == mode |
|
| 1230 |
- ? Color.primary.opacity(0.10) |
|
| 1231 |
- : Color.clear |
|
| 1232 |
- ) |
|
| 1223 |
+ .background(finalCheckpointMode == mode ? Color.primary.opacity(0.10) : Color.clear) |
|
| 1233 | 1224 |
.meterCard( |
| 1234 | 1225 |
tint: finalCheckpointMode == mode ? .primary : .secondary, |
| 1235 | 1226 |
fillOpacity: finalCheckpointMode == mode ? 0.08 : 0.04, |
@@ -1241,7 +1232,6 @@ struct MeterChargeRecordContentView: View {
|
||
| 1241 | 1232 |
} |
| 1242 | 1233 |
} |
| 1243 | 1234 |
|
| 1244 |
- // Custom % input |
|
| 1245 | 1235 |
if finalCheckpointMode == .custom {
|
| 1246 | 1236 |
HStack(spacing: 8) {
|
| 1247 | 1237 |
Button { adjustFinalCheckpoint(by: -1) } label: {
|
@@ -1255,8 +1245,7 @@ struct MeterChargeRecordContentView: View {
|
||
| 1255 | 1245 |
.frame(width: 56) |
| 1256 | 1246 |
.multilineTextAlignment(.center) |
| 1257 | 1247 |
|
| 1258 |
- Text("%")
|
|
| 1259 |
- .foregroundColor(.secondary) |
|
| 1248 |
+ Text("%").foregroundColor(.secondary)
|
|
| 1260 | 1249 |
|
| 1261 | 1250 |
Button { adjustFinalCheckpoint(by: 1) } label: {
|
| 1262 | 1251 |
Image(systemName: "plus.circle").font(.title3) |
@@ -1267,22 +1256,23 @@ struct MeterChargeRecordContentView: View {
|
||
| 1267 | 1256 |
} |
| 1268 | 1257 |
} |
| 1269 | 1258 |
|
| 1270 |
- // Action row |
|
| 1271 |
- HStack(spacing: 10) {
|
|
| 1272 |
- Button("Cancel") {
|
|
| 1259 |
+ HStack(spacing: 8) {
|
|
| 1260 |
+ Button("Discard") {
|
|
| 1261 |
+ _ = appData.deleteChargeSession(sessionID: session.id) |
|
| 1273 | 1262 |
showingStopConfirm = false |
| 1274 | 1263 |
finalCheckpointText = "" |
| 1264 |
+ finalCheckpointMode = .full |
|
| 1275 | 1265 |
} |
| 1276 | 1266 |
.frame(maxWidth: .infinity) |
| 1277 | 1267 |
.padding(.vertical, 9) |
| 1278 | 1268 |
.meterCard(tint: .secondary, fillOpacity: 0.10, strokeOpacity: 0.14, cornerRadius: 14) |
| 1279 | 1269 |
.buttonStyle(.plain) |
| 1280 | 1270 |
|
| 1281 |
- let stopDisabled = finalCheckpointMode == .custom |
|
| 1271 |
+ let saveDisabled = finalCheckpointMode == .custom |
|
| 1282 | 1272 |
&& finalCheckpointText.isEmpty == false |
| 1283 | 1273 |
&& parsedFinalCheckpoint == nil |
| 1284 | 1274 |
|
| 1285 |
- Button("Stop Session") {
|
|
| 1275 |
+ Button("Save") {
|
|
| 1286 | 1276 |
_ = appData.stopChargeSession( |
| 1287 | 1277 |
sessionID: session.id, |
| 1288 | 1278 |
finalBatteryPercent: resolvedFinalCheckpoint |
@@ -1293,9 +1283,19 @@ struct MeterChargeRecordContentView: View {
|
||
| 1293 | 1283 |
} |
| 1294 | 1284 |
.frame(maxWidth: .infinity) |
| 1295 | 1285 |
.padding(.vertical, 9) |
| 1296 |
- .meterCard(tint: .red, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 1286 |
+ .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 1287 |
+ .buttonStyle(.plain) |
|
| 1288 |
+ .disabled(saveDisabled) |
|
| 1289 |
+ |
|
| 1290 |
+ Button("Cancel") {
|
|
| 1291 |
+ showingStopConfirm = false |
|
| 1292 |
+ finalCheckpointText = "" |
|
| 1293 |
+ finalCheckpointMode = .full |
|
| 1294 |
+ } |
|
| 1295 |
+ .frame(maxWidth: .infinity) |
|
| 1296 |
+ .padding(.vertical, 9) |
|
| 1297 |
+ .meterCard(tint: .secondary, fillOpacity: 0.10, strokeOpacity: 0.14, cornerRadius: 14) |
|
| 1297 | 1298 |
.buttonStyle(.plain) |
| 1298 |
- .disabled(stopDisabled) |
|
| 1299 | 1299 |
} |
| 1300 | 1300 |
} |
| 1301 | 1301 |
.padding(14) |