USB-Meter / USB Meter / Views / ChargedDevices / BatteryCheckpointEditorSheetView.swift
1 contributor
105 lines | 3.581kb
//
//  BatteryCheckpointEditorSheetView.swift
//  USB Meter
//
//  Created by Codex on 10/04/2026.
//

import SwiftUI

struct BatteryCheckpointEditorSheetView: View {
    @EnvironmentObject private var appData: AppData
    @EnvironmentObject private var meter: Meter
    @Environment(\.dismiss) private var dismiss

    @State private var batteryPercent = ""
    @State private var label = ""
    @State private var confirmationWarning: BatteryCheckpointPlausibilityWarning?

    private var activeSession: ChargeSessionSummary? {
        appData.activeChargeSessionSummary(for: meter.btSerial.macAddress.description)
    }

    private var plausibilityWarning: BatteryCheckpointPlausibilityWarning? {
        guard let percent = Double(batteryPercent),
              let activeSession else {
            return nil
        }
        return appData.batteryCheckpointPlausibilityWarning(percent: percent, for: activeSession.id)
    }

    var body: some View {
        NavigationView {
            Form {
                Section(
                    header: ContextInfoHeader(
                        title: "Checkpoint",
                        message: "The checkpoint is stored on the active charge session and later used for capacity estimation and the typical charge curve."
                    )
                ) {
                    TextField("Battery %", text: $batteryPercent)
                        .keyboardType(.decimalPad)
                    TextField("Label (optional)", text: $label)
                }

                if let plausibilityWarning {
                    Section(header: Text(plausibilityWarning.title)) {
                        Text(plausibilityWarning.message)
                            .font(.footnote)
                            .foregroundColor(.orange)
                    }
                }
            }
            .navigationTitle("Battery Checkpoint")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Save") {
                        saveCheckpoint()
                    }
                    .disabled(
                        (Double(batteryPercent) ?? -1) < 0
                            || (Double(batteryPercent) ?? 101) > 100
                            || appData.activeChargeSessionSummary(for: meter.btSerial.macAddress.description) == nil
                    )
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .alert(item: $confirmationWarning) { warning in
            Alert(
                title: Text(warning.title),
                message: Text(warning.message),
                primaryButton: .destructive(Text("Save Anyway")) {
                    saveCheckpoint(forceOverride: true)
                },
                secondaryButton: .cancel()
            )
        }
    }

    private func saveCheckpoint(forceOverride: Bool = false) {
        guard let percent = Double(batteryPercent) else {
            return
        }

        if !forceOverride, let plausibilityWarning {
            confirmationWarning = plausibilityWarning
            return
        }

        let didSave = appData.addBatteryCheckpoint(
            percent: percent,
            label: label,
            for: meter
        )
        if didSave {
            dismiss()
        }
    }
}