PDF-001 & PDF-002 (ungetestet)

This commit is contained in:
2026-08-25 01:27:29 +02:00
parent 81178cbbc2
commit ebf85df188
10 changed files with 713 additions and 5 deletions
@@ -25,6 +25,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.BrokenImage
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.PictureAsPdf
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
@@ -37,6 +38,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
@@ -64,6 +67,7 @@ import de.medapp.wunddoku.R
import de.medapp.wunddoku.domain.model.CheckItem
import de.medapp.wunddoku.domain.model.DocumentationBlock
import de.medapp.wunddoku.domain.model.Photo
import kotlinx.coroutines.launch
/**
* Bearbeitungsansicht für eine Wunddokumentation. Zeigt einen Block nach dem anderen mit
@@ -109,7 +113,19 @@ fun DocumentationEditScreen(
contract = ActivityResultContracts.PickMultipleVisualMedia(),
) { uris: List<Uri> -> viewModel.onPhotosPicked(uris) }
// Für die kurze Erfolgs-/Fehlermeldung nach einem PDF-Exportversuch (rein zum manuellen
// Testen von PDF-001/002 - Öffnen/Teilen der Datei folgt erst in PDF-005/006).
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(uiState.pdfExportMessage) {
val message = uiState.pdfExportMessage
if (message != null) {
snackbarHostState.showSnackbar(message)
viewModel.dismissPdfExportMessage()
}
}
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
TopAppBar(
title = {
@@ -126,9 +142,18 @@ fun DocumentationEditScreen(
}
},
actions = {
// PDF-Export/Unterschrift folgen in einer späteren Phase. "Fertig" beendet
// die Bearbeitung bereits jetzt (Inhalte sind ohnehin per Autosave
// gesichert, kein expliziter Speichern-Button nötig).
// PDF-005 (Öffnen)/PDF-006 (Teilen) folgen später - dieser Button erzeugt
// ausschließlich die Datei im internen Speicher (PDF-001/002), als
// minimaler Trigger zum manuellen Testen der Export-Funktionalität.
if (uiState.pdfExportInProgress) {
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
} else {
IconButton(onClick = viewModel::exportPdf) {
Icon(Icons.Filled.PictureAsPdf, contentDescription = "PDF erzeugen")
}
}
// "Fertig" beendet die Bearbeitung bereits jetzt (Inhalte sind ohnehin per
// Autosave gesichert, kein expliziter Speichern-Button nötig).
Button(onClick = onFinished) { Text("Fertig") }
},
)
@@ -10,6 +10,8 @@ import androidx.navigation.toRoute
import de.medapp.wunddoku.data.repository.DocumentationRepository
import de.medapp.wunddoku.di.wunddokuApplication
import de.medapp.wunddoku.domain.model.Documentation
import de.medapp.wunddoku.pdf.PdfExportError
import de.medapp.wunddoku.pdf.PdfExportResult
import de.medapp.wunddoku.ui.navigation.Screen
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -32,6 +34,11 @@ data class DocumentationEditUiState(
val photoImportError: String? = null,
/** != null, solange die Löschbestätigung für ein Foto angezeigt werden soll. */
val pendingDeletePhotoId: String? = null,
/** true, während ein PDF-Export läuft (Ladeindikator, Button währenddessen deaktiviert). */
val pdfExportInProgress: Boolean = false,
/** Kurze, fachlich übersetzte Erfolgs-/Fehlermeldung nach einem Exportversuch - einmalig als
* Snackbar angezeigt (s. Screen), danach über [DocumentationEditViewModel.dismissPdfExportMessage] geleert. */
val pdfExportMessage: String? = null,
) {
val blockCount: Int get() = documentation?.blocks?.size ?: 0
}
@@ -221,6 +228,40 @@ class DocumentationEditViewModel(
}
}
/**
* Löst den PDF-Export an (PDF-001/002). Rein zum manuellen Testen der Export-Funktionalität
* gedacht - Öffnen in einem externen Reader (PDF-005) und Teilen (PDF-006) sind explizit
* nicht Teil dieses Schritts, die erzeugte Datei liegt ausschließlich im internen
* App-Speicher. Keine rohe Exception erreicht die UI: [PdfExportResult] wird hier in eine
* kurze, verständliche Meldung übersetzt.
*/
fun exportPdf() {
viewModelScope.launch {
_uiState.update { it.copy(pdfExportInProgress = true, pdfExportMessage = null) }
val result = repository.exportPdf(documentationId)
_uiState.update {
it.copy(
pdfExportInProgress = false,
pdfExportMessage = when (result) {
is PdfExportResult.Success -> "PDF erzeugt: ${result.file.name}"
is PdfExportResult.Failure -> "PDF-Erzeugung fehlgeschlagen: ${result.error.toUserMessage()}"
},
)
}
}
}
fun dismissPdfExportMessage() {
_uiState.update { it.copy(pdfExportMessage = null) }
}
private fun PdfExportError.toUserMessage(): String = when (this) {
PdfExportError.DocumentationNotFound -> "Die Dokumentation wurde nicht gefunden."
is PdfExportError.DirectoryCreationFailed -> "Zielverzeichnis konnte nicht angelegt werden."
is PdfExportError.WriteFailed -> "Beim Schreiben der PDF-Datei ist ein Fehler aufgetreten."
is PdfExportError.Unexpected -> "Es ist ein unerwarteter Fehler aufgetreten."
}
/**
* Lädt eine speicherschonende Vorschauversion eines Fotos für die Anzeige im Fotogitter.
* Bewusst NICHT im zentralen UiState gehalten (kein `Bitmap`-Feld in