USB-Meter / USB Meter / Views / ChargedDevices / ChargedDeviceLibrarySheetView.swift
1 contributor
223 lines | 7.799kb
//
//  ChargedDeviceLibrarySheetView.swift
//  USB Meter
//
//  Created by Codex on 10/04/2026.
//

import SwiftUI

enum ChargedDeviceLibraryMode {
    case device
    case charger

    var title: String {
        switch self {
        case .device:
            return "Devices"
        case .charger:
            return "Chargers"
        }
    }

    var singularTitle: String {
        switch self {
        case .device:
            return "Device"
        case .charger:
            return "Charger"
        }
    }

    var suggestedClass: ChargedDeviceClass {
        switch self {
        case .device:
            return .iphone
        case .charger:
            return .charger
        }
    }
}

struct ChargedDeviceLibrarySheetView: View {
    @EnvironmentObject private var appData: AppData

    @Binding var visibility: Bool

    let meterMACAddress: String
    let meterTint: Color
    let mode: ChargedDeviceLibraryMode

    @State private var editorVisibility = false
    @State private var editingChargedDevice: ChargedDeviceSummary?

    var body: some View {
        NavigationView {
            List {
                if displayedChargedDevices.isEmpty {
                    VStack(alignment: .leading, spacing: 10) {
                        Text("No \(mode.title.lowercased()) yet.")
                            .font(.headline)
                        Text(emptyStateDescription)
                            .font(.footnote)
                            .foregroundColor(.secondary)
                    }
                    .padding(.vertical, 10)
                    .listRowBackground(Color.clear)
                } else {
                    ForEach(displayedChargedDevices) { chargedDevice in
                        Button {
                            select(chargedDevice)
                            visibility = false
                        } label: {
                            ChargedDeviceLibraryRowView(
                                chargedDevice: chargedDevice,
                                isSelected: chargedDevice.id == selectedDeviceID
                            )
                        }
                        .buttonStyle(.plain)
                        .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                            Button {
                                editingChargedDevice = chargedDevice
                            } label: {
                                Label("Edit", systemImage: "pencil")
                            }
                            .tint(.blue)
                        }
                        .contextMenu {
                            Button {
                                editingChargedDevice = chargedDevice
                            } label: {
                                Label("Edit Device", systemImage: "pencil")
                            }
                        }
                    }
                }
            }
            .listStyle(InsetGroupedListStyle())
            .background(
                LinearGradient(
                    colors: [meterTint.opacity(0.14), Color.clear],
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
                .ignoresSafeArea()
            )
            .navigationTitle(mode.title)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Done") {
                        visibility = false
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("New") {
                        editorVisibility = true
                    }
                }
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .sheet(isPresented: $editorVisibility) {
            ChargedDeviceEditorSheetView(
                meterMACAddress: meterMACAddress,
                suggestedDeviceClass: mode.suggestedClass
            )
                .environmentObject(appData)
        }
        .sheet(item: $editingChargedDevice) { chargedDevice in
            ChargedDeviceEditorSheetView(
                meterMACAddress: nil,
                chargedDevice: chargedDevice,
                suggestedDeviceClass: mode.suggestedClass
            )
            .environmentObject(appData)
        }
    }

    private var displayedChargedDevices: [ChargedDeviceSummary] {
        switch mode {
        case .device:
            return appData.deviceSummaries
        case .charger:
            return appData.chargerSummaries
        }
    }

    private var selectedDeviceID: UUID? {
        switch mode {
        case .device:
            return appData.currentChargedDeviceSummary(for: meterMACAddress)?.id
        case .charger:
            return appData.currentChargerSummary(for: meterMACAddress)?.id
        }
    }

    private var emptyStateDescription: String {
        switch mode {
        case .device:
            return "Create one here, then select it before or during a charging session. The selected device becomes the default for this meter."
        case .charger:
            return "Create one here, then select it for wireless charging sessions. The selected charger becomes the default wireless source for this meter."
        }
    }

    private func select(_ chargedDevice: ChargedDeviceSummary) {
        switch mode {
        case .device:
            appData.assignChargedDevice(chargedDevice.id, to: meterMACAddress)
        case .charger:
            appData.assignCharger(chargedDevice.id, to: meterMACAddress)
        }
    }
}

private struct ChargedDeviceLibraryRowView: View {
    let chargedDevice: ChargedDeviceSummary
    let isSelected: Bool

    var body: some View {
        HStack(alignment: .top, spacing: 14) {
            ChargedDeviceQRCodeView(qrIdentifier: chargedDevice.qrIdentifier, side: 58)

            VStack(alignment: .leading, spacing: 6) {
                HStack {
                    Label(chargedDevice.name, systemImage: chargedDevice.deviceClass.symbolName)
                        .font(.headline)
                        .foregroundColor(.primary)
                    Spacer()
                    if isSelected {
                        Image(systemName: "checkmark.circle.fill")
                            .foregroundColor(.green)
                    }
                }

                Text(chargedDevice.deviceClass.title)
                    .font(.caption.weight(.semibold))
                    .foregroundColor(.secondary)

                Text(chargedDevice.supportedChargingModes.map(\.title).joined(separator: " + "))
                    .font(.caption2)
                    .foregroundColor(.secondary)

                if let capacity = chargedDevice.estimatedBatteryCapacityWh {
                    Text("Estimated capacity: \(capacity.format(decimalDigits: 2)) Wh")
                        .font(.caption)
                        .foregroundColor(.secondary)
                } else if chargedDevice.isCharger, let chargerMaximumPowerWatts = chargedDevice.chargerMaximumPowerWatts {
                    Text("Max power: \(chargerMaximumPowerWatts.format(decimalDigits: 2)) W")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }

                if let minimumCurrent = chargedDevice.resolvedCompletionCurrentAmps(for: chargedDevice.preferredChargingTransportMode) {
                    Text("Completion current: \(minimumCurrent.format(decimalDigits: 2)) A")
                        .font(.caption2)
                        .foregroundColor(.secondary)
                }
            }
        }
        .padding(.vertical, 4)
    }
}