// // 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 } } }