Enhance PDF upload UX with progress and state handling
Refactor the PDF upload button to dynamically adjust its appearance and behavior based on `_isPageReady` and `_isPdfUploadInProgress` states. Add a spinner to indicate upload progress and disable the `<InputFile>` component during uploads or when the page is not ready. Introduce `_isPdfUploadInProgress` state to prevent multiple uploads and ensure consistent UI updates. Update the `OnPdfFileSelectedAsync` method to handle this state, including a `finally` block to reset it and trigger UI updates. Consolidate redundant code for disabled states and improve conditional rendering for better maintainability. Enhance visual feedback for loading states and make minor style adjustments for consistency.
This commit is contained in:
@@ -159,29 +159,43 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
@* PDF Upload *@
|
@* PDF Upload *@
|
||||||
@if (_isPageReady)
|
<label class="pdf-toolbar__btn"
|
||||||
{
|
title='@L("UploadPdf", "PDF hochladen")'
|
||||||
<label class="pdf-toolbar__btn"
|
style="cursor: @((_isPageReady && !_isPdfUploadInProgress) ? "pointer" : "not-allowed");
|
||||||
title='@L("UploadPdf", "PDF hochladen")'
|
opacity:@((_isPageReady && !_isPdfUploadInProgress) ? "1" : "0.6");
|
||||||
style="cursor: pointer; display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.75rem; font-size: 0.75rem; font-weight: 500;">
|
display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.75rem; font-size: 0.75rem; font-weight: 500;">
|
||||||
|
@if (_isPdfUploadInProgress)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" style="width:0.8rem;height:0.8rem;border-width:2px;"></span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
||||||
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
}
|
||||||
|
@if (_isPdfUploadInProgress)
|
||||||
|
{
|
||||||
@L("UploadPdf", "PDF hochladen")
|
@L("UploadPdf", "PDF hochladen")
|
||||||
<InputFile OnChange="OnPdfFileSelectedAsync"
|
@:
|
||||||
accept=".pdf"
|
@:...
|
||||||
style="display: none;" />
|
}
|
||||||
</label>
|
else if (_isPageReady)
|
||||||
}
|
{
|
||||||
else
|
@L("UploadPdf", "PDF hochladen")
|
||||||
{
|
}
|
||||||
<span class="pdf-toolbar__btn"
|
else
|
||||||
style="opacity:0.6; cursor:not-allowed; display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.75rem; font-size: 0.75rem; font-weight: 500;">
|
{
|
||||||
<span class="spinner-border spinner-border-sm" role="status" style="width:0.8rem;height:0.8rem;border-width:2px;"></span>
|
@L("Loading", "Wird geladen")
|
||||||
@L("Loading", "Wird geladen")…
|
@:
|
||||||
</span>
|
@:...
|
||||||
}
|
}
|
||||||
|
<InputFile OnChange="OnPdfFileSelectedAsync"
|
||||||
|
accept=".pdf"
|
||||||
|
disabled="@(!_isPageReady || _isPdfUploadInProgress)"
|
||||||
|
style="display: none;" />
|
||||||
|
</label>
|
||||||
|
|
||||||
@if (_pdfLoaded && _signatureFields.Count > 0)
|
@if (_pdfLoaded && _signatureFields.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -267,24 +281,42 @@
|
|||||||
<p class="text-white mb-4" style="opacity: 0.75; font-size: 0.85rem;">
|
<p class="text-white mb-4" style="opacity: 0.75; font-size: 0.85rem;">
|
||||||
@L("SenderEditor_UploadPdfToPlaceFields", "Laden Sie eine PDF-Datei hoch, um Signaturfelder zu platzieren.")
|
@L("SenderEditor_UploadPdfToPlaceFields", "Laden Sie eine PDF-Datei hoch, um Signaturfelder zu platzieren.")
|
||||||
</p>
|
</p>
|
||||||
@if (_isPageReady)
|
<label class="btn btn-light btn-sm"
|
||||||
{
|
style="cursor: @((_isPageReady && !_isPdfUploadInProgress) ? "pointer" : "not-allowed");
|
||||||
<label class="btn btn-light btn-sm" style="cursor: pointer; font-weight: 500;">
|
opacity:@((_isPageReady && !_isPdfUploadInProgress) ? "1" : "0.8");
|
||||||
|
font-weight: 500;">
|
||||||
|
@if (_isPdfUploadInProgress)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm me-1" role="status" style="width:0.8rem;height:0.8rem;border-width:2px;"></span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="me-1" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="me-1" viewBox="0 0 16 16">
|
||||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
|
||||||
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
}
|
||||||
|
@if (_isPdfUploadInProgress)
|
||||||
|
{
|
||||||
@L("UploadPdf", "PDF hochladen")
|
@L("UploadPdf", "PDF hochladen")
|
||||||
<InputFile OnChange="OnPdfFileSelectedAsync" accept=".pdf" style="display: none;" />
|
@:
|
||||||
</label>
|
@:...
|
||||||
}
|
}
|
||||||
else
|
else if (_isPageReady)
|
||||||
{
|
{
|
||||||
<button class="btn btn-light btn-sm" type="button" disabled>
|
@L("UploadPdf", "PDF hochladen")
|
||||||
<span class="spinner-border spinner-border-sm me-1" role="status" style="width:0.8rem;height:0.8rem;border-width:2px;"></span>
|
}
|
||||||
@L("Loading", "Wird geladen")…
|
else
|
||||||
</button>
|
{
|
||||||
}
|
@L("Loading", "Wird geladen")
|
||||||
|
@:
|
||||||
|
@:...
|
||||||
|
}
|
||||||
|
<InputFile OnChange="OnPdfFileSelectedAsync"
|
||||||
|
accept=".pdf"
|
||||||
|
disabled="@(!_isPageReady || _isPdfUploadInProgress)"
|
||||||
|
style="display: none;" />
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -490,6 +522,7 @@
|
|||||||
// ── State ──
|
// ── State ──
|
||||||
bool _pdfLoaded = false;
|
bool _pdfLoaded = false;
|
||||||
bool _isPageReady;
|
bool _isPageReady;
|
||||||
|
bool _isPdfUploadInProgress;
|
||||||
string _fileName = string.Empty;
|
string _fileName = string.Empty;
|
||||||
string? _errorMessage;
|
string? _errorMessage;
|
||||||
byte[]? _originalPdfBytes; // Pristine upload — never modified, used as base for redraw
|
byte[]? _originalPdfBytes; // Pristine upload — never modified, used as base for redraw
|
||||||
@@ -658,6 +691,9 @@
|
|||||||
if (!_isPageReady)
|
if (!_isPageReady)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (_isPdfUploadInProgress)
|
||||||
|
return;
|
||||||
|
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
var file = e.File;
|
var file = e.File;
|
||||||
if (file is null) return;
|
if (file is null) return;
|
||||||
@@ -670,6 +706,9 @@
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_isPdfUploadInProgress = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
const long maxBytes = 50 * 1024 * 1024;
|
const long maxBytes = 50 * 1024 * 1024;
|
||||||
using var ms = new System.IO.MemoryStream();
|
using var ms = new System.IO.MemoryStream();
|
||||||
await file.OpenReadStream(maxBytes).CopyToAsync(ms);
|
await file.OpenReadStream(maxBytes).CopyToAsync(ms);
|
||||||
@@ -695,6 +734,11 @@
|
|||||||
ex.Message);
|
ex.Message);
|
||||||
Logger.LogError(ex, "Failed to load PDF file");
|
Logger.LogError(ex, "Failed to load PDF file");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isPdfUploadInProgress = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Placement mode ──
|
// ── Placement mode ──
|
||||||
|
|||||||
Reference in New Issue
Block a user