ScanCarteScreen.swift
119 lignes · 4244 octets
import SwiftUI // Port of ui/ScanCarteScreen.kt. CameraX preview + capture is replaced by the // VisionKit document camera (perspective-corrected pages); the photo picker // covers the simulator and imports. struct ScanCarteScreen: View { @ObservedObject var viewModel: ScanCarteViewModel let onBack: () -> Void let onContactSaved: (Int64) -> Void @State private var showDocumentCamera = false @State private var showPhotoPicker = false var body: some View { Group { switch viewModel.state { case .idle: captureLauncher case .processing: ScanProgressView(message: "Reconnaissance du texte…") case .draftReady(let draft): ContactDraftScreen( draft: draft, onCardChange: { viewModel.updateDraft($0) }, onSaveToCarnet: { viewModel.saveToCarnet() }, onRetry: { viewModel.reset() }, isEditing: viewModel.isEditing ) case .error(let message): errorView(message) } } .toolbar(.hidden, for: .navigationBar) .onChange(of: viewModel.saveDoneId) { id in guard let id else { return } viewModel.consumeSaveDone() onContactSaved(id) } .fullScreenCover(isPresented: $showDocumentCamera) { DocumentScannerView { outcome in showDocumentCamera = false switch outcome { case .page(let image): viewModel.processCapture(image, source: .documentCamera) case .cancelled: break case .failed: viewModel.onCaptureDecodeFailed() } } .ignoresSafeArea() } .sheet(isPresented: $showPhotoPicker) { PhotoPickerView { image in showPhotoPicker = false if let image { viewModel.processCapture(image, source: .photoLibrary) } } } } private var captureLauncher: some View { VStack(alignment: .leading, spacing: 0) { ScreenTopBar(title: "Scanner une carte", onBack: onBack) Text("Cadrez la carte de visite, puis capturez.") .font(C2VFont.bodyMedium) .foregroundColor(C2VColor.texteFaible) .padding(.horizontal, 18) .padding(.vertical, 8) Spacer() VStack(spacing: 10) { if DocumentScannerView.isSupported { Button("Capturer") { showDocumentCamera = true } .buttonStyle(C2VPrimaryButtonStyle()) } Button("Choisir une photo") { showPhotoPicker = true } .buttonStyle( DocumentScannerView.isSupported ? AnyButtonStyle(C2VOutlineButtonStyle()) : AnyButtonStyle(C2VPrimaryButtonStyle()) ) } .padding(18) } .background(C2VColor.fond) } private func errorView(_ message: String) -> some View { VStack(spacing: 12) { Spacer() Text(message) .font(C2VFont.bodyLarge) .foregroundColor(C2VColor.encre) .multilineTextAlignment(.center) Button("Reprendre la capture") { viewModel.reset() } .buttonStyle(C2VPrimaryButtonStyle()) Button("Retour", action: onBack) .buttonStyle(C2VOutlineButtonStyle()) Spacer() } .padding(18) .background(C2VColor.fond) } } /// Type-erased button style (conditional primary/outline in the launcher). struct AnyButtonStyle: ButtonStyle { private let makeBodyClosure: (Configuration) -> AnyView init<S: ButtonStyle>(_ style: S) { makeBodyClosure = { AnyView(style.makeBody(configuration: $0)) } } func makeBody(configuration: Configuration) -> some View { makeBodyClosure(configuration) } }
GitRust