Initial commit for first version of project
This commit is contained in:
118
GasTankAR/Views/Docuframe/DocuframeViewModel.swift
Normal file
118
GasTankAR/Views/Docuframe/DocuframeViewModel.swift
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// DocuframeViewModel.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 28.08.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
/// Präsentationslogik für den DOCUframe-Tab: verwaltet die Fotoauswahl und stößt Upload bzw.
|
||||
/// Mailversand über den injizierten ``DocuframeService`` an. Kennt keine UI-Details.
|
||||
@MainActor
|
||||
@Observable
|
||||
final class DocuframeViewModel {
|
||||
/// Auswahl anhand der stabilen SwiftData-Identität der Fotos.
|
||||
var selectedIDs: Set<PersistentIdentifier> = []
|
||||
/// Läuft gerade ein Upload/Mailversand?
|
||||
private(set) var isProcessing = false
|
||||
/// Ergebnis des letzten Vorgangs für die Anzeige (nil = kein Hinweis).
|
||||
var result: DocuframeActionResult?
|
||||
|
||||
@ObservationIgnored private let service: DocuframeService
|
||||
|
||||
/// Der Service wird injiziert (Default: Stub) – austauschbar für echte Anbindung oder Tests.
|
||||
init(service: DocuframeService = StubDocuframeService()) {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
var hasSelection: Bool { !selectedIDs.isEmpty }
|
||||
var selectionCount: Int { selectedIDs.count }
|
||||
|
||||
func isSelected(_ photo: ProjectPhoto) -> Bool {
|
||||
selectedIDs.contains(photo.persistentModelID)
|
||||
}
|
||||
|
||||
func toggleSelection(_ photo: ProjectPhoto) {
|
||||
let id = photo.persistentModelID
|
||||
if selectedIDs.contains(id) {
|
||||
selectedIDs.remove(id)
|
||||
} else {
|
||||
selectedIDs.insert(id)
|
||||
}
|
||||
}
|
||||
|
||||
func upload(from photos: [ProjectPhoto], leadID: String) async {
|
||||
await perform(from: photos, leadID: leadID, successText: "in DOCUframe hochgeladen") {
|
||||
try await service.uploadPhotos($0, projectLeadID: $1)
|
||||
}
|
||||
}
|
||||
|
||||
func sendEmail(from photos: [ProjectPhoto], leadID: String) async {
|
||||
await perform(from: photos, leadID: leadID, successText: "per E-Mail versendet") {
|
||||
try await service.sendEmail($0, projectLeadID: $1)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Gemeinsame Ausführung
|
||||
|
||||
private func perform(
|
||||
from photos: [ProjectPhoto],
|
||||
leadID: String,
|
||||
successText: String,
|
||||
action: ([DocuframePhoto], String) async throws -> Void
|
||||
) async {
|
||||
let selected = photos.filter { selectedIDs.contains($0.persistentModelID) }
|
||||
guard !selected.isEmpty else {
|
||||
result = .failure(message: DocuframeError.noPhotosSelected.localizedDescription)
|
||||
return
|
||||
}
|
||||
|
||||
isProcessing = true
|
||||
defer { isProcessing = false }
|
||||
|
||||
let payload = selected.map(Self.makeDTO)
|
||||
do {
|
||||
try await action(payload, leadID)
|
||||
result = .success(message: "\(selected.count) Foto(s) \(successText).")
|
||||
} catch {
|
||||
// Bei HTTP 4xx/5xx (und anderen Fehlern) den Nutzer informieren.
|
||||
result = .failure(message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bildet das Persistenz-Model auf ein persistenzunabhängiges Domänen-DTO ab.
|
||||
private static func makeDTO(_ photo: ProjectPhoto) -> DocuframePhoto {
|
||||
DocuframePhoto(
|
||||
imageData: photo.imageData,
|
||||
creationDate: photo.creationDate,
|
||||
latitude: photo.latitude,
|
||||
longitude: photo.longitude
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ergebnis eines DOCUframe-Vorgangs für die Anzeige (Erfolg oder Fehler).
|
||||
enum DocuframeActionResult {
|
||||
case success(message: String)
|
||||
case failure(message: String)
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .success: "Erledigt"
|
||||
case .failure: "Fehler"
|
||||
}
|
||||
}
|
||||
|
||||
var message: String {
|
||||
switch self {
|
||||
case .success(let message), .failure(let message): message
|
||||
}
|
||||
}
|
||||
|
||||
var isError: Bool {
|
||||
if case .failure = self { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user