USB-Meter / USB Meter / Views / ChargedDevices / Sidebar / SidebarChargedDeviceLibraryView.swift
1 contributor
159 lines | 5.353kb
//
//  SidebarChargedDeviceLibraryView.swift
//  USB Meter
//
//  Created by Codex on 22/04/2026.
//

import SwiftUI

/// Full-management library for the sidebar — navigates into detail instead of select-and-dismiss.
struct SidebarChargedDeviceLibraryView: View {
    @EnvironmentObject private var appData: AppData

    let mode: ChargedDeviceLibraryMode

    @State private var showingNewEditor = false
    @State private var editingChargedDevice: ChargedDeviceSummary?
    @State private var pendingDeletion: ChargedDeviceSummary?

    private var tint: Color {
        mode == .device ? .orange : .pink
    }

    var body: some View {
        List {
            if displayedDevices.isEmpty {
                emptyStateView
            } else {
                deviceRows
            }
        }
        .listStyle(InsetGroupedListStyle())
        .background(backgroundGradient)
        .navigationTitle(mode.title)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .primaryAction) {
                Button("New") { showingNewEditor = true }
            }
        }
        .sidebarToggleToolbarItem()
        .sheet(isPresented: $showingNewEditor) { newEditorSheet }
        .sheet(item: $editingChargedDevice) { device in editEditorSheet(device) }
        .confirmationDialog(
            "Delete \(pendingDeletion?.name ?? mode.singularTitle)?",
            isPresented: Binding(
                get: { pendingDeletion != nil },
                set: { if !$0 { pendingDeletion = nil } }
            ),
            titleVisibility: .visible
        ) {
            Button("Delete", role: .destructive, action: deletePendingDevice)
            Button("Cancel", role: .cancel) { pendingDeletion = nil }
        } message: {
            Text("This will permanently remove the \(mode.singularTitle.lowercased()) and all associated data.")
        }
    }

    private var emptyStateView: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(spacing: 8) {
                Text("No \(mode.title.lowercased()) yet.")
                    .font(.headline)
                ContextInfoButton(
                    title: mode.title,
                    message: emptyStateDescription
                )
            }
        }
        .padding(.vertical, 10)
        .listRowBackground(Color.clear)
    }

    private var deviceRows: some View {
        ForEach(displayedDevices) { device in
            NavigationLink(destination: ChargedDeviceDetailView(chargedDeviceID: device.id)) {
                ChargedDeviceLibraryRowView(chargedDevice: device, isSelected: false)
            }
            .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                rowActions(for: device)
            }
            .contextMenu {
                Button {
                    editingChargedDevice = device
                } label: {
                    Label("Edit \(mode.singularTitle)", systemImage: "pencil")
                }
                Button(role: .destructive) {
                    pendingDeletion = device
                } label: {
                    Label("Delete \(mode.singularTitle)", systemImage: "trash")
                }
            }
        }
    }

    private var backgroundGradient: some View {
        LinearGradient(
            colors: [tint.opacity(0.14), Color.clear],
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        )
        .ignoresSafeArea()
    }

    private var displayedDevices: [ChargedDeviceSummary] {
        mode == .device ? appData.deviceSummaries : appData.chargerSummaries
    }

    @ViewBuilder
    private var newEditorSheet: some View {
        if mode == .charger {
            ChargerEditorSheetView(meterMACAddress: nil)
                .environmentObject(appData)
        } else {
            ChargedDeviceEditorSheetView(meterMACAddress: nil)
                .environmentObject(appData)
        }
    }

    @ViewBuilder
    private func editEditorSheet(_ device: ChargedDeviceSummary) -> some View {
        if device.isCharger {
            ChargerEditorSheetView(chargedDevice: device)
                .environmentObject(appData)
        } else {
            ChargedDeviceEditorSheetView(meterMACAddress: nil, chargedDevice: device)
                .environmentObject(appData)
        }
    }

    @ViewBuilder
    private func rowActions(for device: ChargedDeviceSummary) -> some View {
        Button(role: .destructive) {
            pendingDeletion = device
        } label: {
            Label("Delete", systemImage: "trash")
        }
        Button {
            editingChargedDevice = device
        } label: {
            Label("Edit", systemImage: "pencil")
        }
        .tint(.blue)
    }

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

    private func deletePendingDevice() {
        if let device = pendingDeletion {
            _ = appData.deleteChargedDevice(id: device.id)
            pendingDeletion = nil
        }
    }
}