Permette di eliminare le foto di una scheda anche dopo il salvataggio
Finora la foto si poteva togliere solo prima di salvare la scheda. - La X compare su tutte le foto (non in sola lettura, non mentre la foto e' ancora in elaborazione). L'eliminazione avviene al salvataggio: uscendo senza salvare le foto restano. - Una foto gia' inviata si toglie prima dal server (nuovo endpoint steup/removeAttachment), poi dal dispositivo e da Scheda.ImageNames. Senza rete la X avvisa e non la toglie; se il server non conferma al salvataggio la foto resta ovunque e compare un avviso. - Una foto salvata ma non ancora inviata si elimina dal dispositivo (con la rete si prova anche a toglierla dal server, senza bloccare). - Togliere una foto salvata non la cancellava mai: il flag ToRemove finiva su un elemento appena rimosso dall'elenco. Ora le foto tolte stanno in una lista a parte fino al salvataggio; ToRemove e' stato eliminato. - FileManager.GetInspectionFiles: le foto in /toUpload si filtravano per nome, ma ImageNames contiene il percorso completo, quindi quelle salvate senza rete non ricomparivano mai riaprendo la scheda. Ora si accetta sia il percorso sia il nome, e il DTO porta anche i byte del file per l'invio. Richiede i Servizi con steup/removeAttachment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b2db0f2479
commit
c4c624667d
5 files changed
+118
-18
No files matched your search
@@ -16,15 +16,15 @@ public class FileManager(
|
|||||||
|
|
||||||
private async Task<AttachedDto> ConvertToDto(FileInfo file, AttachedDto.TypeAttached type, bool isFromToUpload)
|
private async Task<AttachedDto> ConvertToDto(FileInfo file, AttachedDto.TypeAttached type, bool isFromToUpload)
|
||||||
{
|
{
|
||||||
var (origUrl, thumbUrl) = await SaveAndCreateThumbAsync(
|
var bytes = await File.ReadAllBytesAsync(file.FullName);
|
||||||
await File.ReadAllBytesAsync(file.FullName),
|
var (origUrl, thumbUrl) = await SaveAndCreateThumbAsync(bytes, file.Name);
|
||||||
file.Name
|
|
||||||
);
|
|
||||||
|
|
||||||
return new AttachedDto
|
return new AttachedDto
|
||||||
{
|
{
|
||||||
Name = file.Name,
|
Name = file.Name,
|
||||||
Path = file.FullName,
|
Path = file.FullName,
|
||||||
|
// Servono per inviare le foto rimaste in /toUpload (salvate senza rete).
|
||||||
|
FileBytes = bytes,
|
||||||
TempPath = origUrl,
|
TempPath = origUrl,
|
||||||
ThumbPath = thumbUrl,
|
ThumbPath = thumbUrl,
|
||||||
Type = type,
|
Type = type,
|
||||||
@@ -101,7 +101,9 @@ public class FileManager(
|
|||||||
|
|
||||||
foreach (var file in toUploadFiles)
|
foreach (var file in toUploadFiles)
|
||||||
{
|
{
|
||||||
if (!fileNameFilter.Contains(file.Name))
|
// Scheda.ImageNames contiene il percorso completo restituito da SaveInspectionFile:
|
||||||
|
// confrontando solo il nome le foto salvate e non ancora inviate non comparivano mai.
|
||||||
|
if (!fileNameFilter.Contains(file.FullName) && !fileNameFilter.Contains(file.Name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -171,7 +171,10 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (item.p.ToUpload && !ReadOnly)
|
@* Rimovibile anche se gia' salvata o inviata. Non mentre e'
|
||||||
|
in elaborazione: l'indice della foto serve ancora al
|
||||||
|
caricamento in background. *@
|
||||||
|
@if (!ReadOnly && !item.p.ThumbPath.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
<button type="button" class="attach-item__remove"
|
<button type="button" class="attach-item__remove"
|
||||||
aria-label="@($"Rimuovi la foto {item.p.Name}")"
|
aria-label="@($"Rimuovi la foto {item.p.Name}")"
|
||||||
@@ -336,6 +339,9 @@
|
|||||||
// Gli allegati vivono in AttachedList finche' non si salva: li tracciamo con un flag.
|
// Gli allegati vivono in AttachedList finche' non si salva: li tracciamo con un flag.
|
||||||
private bool _attachmentsDirty;
|
private bool _attachmentsDirty;
|
||||||
private List<AttachedDto>? AttachedList { get; set; }
|
private List<AttachedDto>? AttachedList { get; set; }
|
||||||
|
// Foto gia' salvate tolte dal rilevatore: si eliminano davvero solo al salvataggio,
|
||||||
|
// cosi' uscendo senza salvare restano dove sono.
|
||||||
|
private readonly List<AttachedDto> _removedAttachments = [];
|
||||||
|
|
||||||
private IDisposable? _backRegistration;
|
private IDisposable? _backRegistration;
|
||||||
// Il tasto indietro puo' arrivare mentre il prompt di uscita e' gia' a video.
|
// Il tasto indietro puo' arrivare mentre il prompt di uscita e' gia' a video.
|
||||||
@@ -505,10 +511,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task Update(SaveSchedaResponseDto? apiResponse, bool preserveActivityId = false)
|
private async Task Update(SaveSchedaResponseDto? apiResponse, bool preserveActivityId = false)
|
||||||
{
|
|
||||||
if (!AttachedList.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
foreach (var attached in AttachedList!.Where(x => !x.SavedOnAppData))
|
|
||||||
{
|
{
|
||||||
var ispezione = new Ispezione
|
var ispezione = new Ispezione
|
||||||
{
|
{
|
||||||
@@ -517,7 +519,13 @@
|
|||||||
Rilevatore = UserSession.User.Username
|
Rilevatore = UserSession.User.Username
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!attached.ToRemove)
|
// Prima di toccare l'ActivityId: le foto gia' inviate vanno tolte dall'attivita'
|
||||||
|
// su cui sono state caricate.
|
||||||
|
await RemoveDeletedAttachments(ispezione);
|
||||||
|
|
||||||
|
if (!AttachedList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
foreach (var attached in AttachedList!.Where(x => !x.SavedOnAppData))
|
||||||
{
|
{
|
||||||
var fileNameAdded = await FileManager.SaveInspectionFile(
|
var fileNameAdded = await FileManager.SaveInspectionFile(
|
||||||
ispezione,
|
ispezione,
|
||||||
@@ -529,9 +537,6 @@
|
|||||||
if (fileNameAdded != null)
|
if (fileNameAdded != null)
|
||||||
Scheda.ImageNames.Add(fileNameAdded);
|
Scheda.ImageNames.Add(fileNameAdded);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
_ = FileManager.RemoveInspectionFile(ispezione, attached.Name!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Salvando una bozza non tocchiamo l'ActivityId: se la scheda era già sul server,
|
// Salvando una bozza non tocchiamo l'ActivityId: se la scheda era già sul server,
|
||||||
@@ -549,6 +554,70 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Elimina le foto salvate che il rilevatore ha tolto dalla scheda: dal server se
|
||||||
|
/// erano gia' state inviate, poi dal dispositivo e dall'elenco della scheda.
|
||||||
|
/// Se il server non conferma, la foto resta (sul server e sul dispositivo) invece
|
||||||
|
/// di sparire solo da qui.
|
||||||
|
/// </summary>
|
||||||
|
private async Task RemoveDeletedAttachments(Ispezione ispezione)
|
||||||
|
{
|
||||||
|
if (_removedAttachments.Count == 0) return;
|
||||||
|
|
||||||
|
var nonEliminate = 0;
|
||||||
|
|
||||||
|
foreach (var attached in _removedAttachments)
|
||||||
|
{
|
||||||
|
if (attached.Name.IsNullOrEmpty()) continue;
|
||||||
|
|
||||||
|
var giaInviata = !attached.ToUpload;
|
||||||
|
|
||||||
|
// Una foto ancora "da inviare" di norma non e' sul server, ma l'invio dalla
|
||||||
|
// card o dalla chiusura ispezione non la sposta fra le inviate: con la rete
|
||||||
|
// si prova comunque a toglierla, senza bloccare l'eliminazione se non c'e'.
|
||||||
|
if (Scheda.ActivityId.IsValorized() && (giaInviata || NetworkService.ConnectionAvailable))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await IntegrySteupService.RemoveFile(Scheda.ActivityId!, attached.Name!);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (giaInviata)
|
||||||
|
{
|
||||||
|
Logger.LogError(e, "Eliminazione della foto {FileName} dal server non riuscita", attached.Name);
|
||||||
|
nonEliminate++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogWarning(e, "Foto {FileName} non trovata sul server", attached.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileManager.RemoveInspectionFile(ispezione, attached.Name!);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Il file orfano non fa danni: conta che la foto esca dalla scheda.
|
||||||
|
Logger.LogError(e, "Eliminazione del file {FileName} non riuscita", attached.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheda.ImageNames?.RemoveAll(x => System.IO.Path.GetFileName(x) == attached.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
_removedAttachments.Clear();
|
||||||
|
|
||||||
|
if (nonEliminate > 0)
|
||||||
|
{
|
||||||
|
Snackbar.Add(nonEliminate == 1
|
||||||
|
? "Una foto già inviata non è stata eliminata: riprova con la connessione"
|
||||||
|
: $"{nonEliminate} foto già inviate non sono state eliminate: riprova con la connessione",
|
||||||
|
Severity.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UploadFile(Scheda scheda)
|
private async Task UploadFile(Scheda scheda)
|
||||||
{
|
{
|
||||||
if (AttachedList.IsNullOrEmpty()) return;
|
if (AttachedList.IsNullOrEmpty()) return;
|
||||||
@@ -703,7 +772,8 @@
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Salva il lavoro in corso solo sul dispositivo, marcato come da completare.
|
/// Salva il lavoro in corso solo sul dispositivo, marcato come da completare.
|
||||||
/// Niente chiamate al server e niente validazione: una bozza può essere incompleta.
|
/// Niente salvataggio sul server e niente validazione: una bozza può essere incompleta.
|
||||||
|
/// Unica eccezione: le foto già inviate tolte dal rilevatore si eliminano anche dal server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task SaveDraft()
|
private async Task SaveDraft()
|
||||||
{
|
{
|
||||||
@@ -929,8 +999,22 @@
|
|||||||
{
|
{
|
||||||
if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;
|
if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;
|
||||||
|
|
||||||
if (AttachedList[index].SavedOnAppData)
|
var attached = AttachedList[index];
|
||||||
AttachedList[index].ToRemove = true;
|
|
||||||
|
// Le foto appena scattate stanno solo in memoria: basta toglierle dall'elenco.
|
||||||
|
if (attached.SavedOnAppData)
|
||||||
|
{
|
||||||
|
// Una foto gia' inviata va tolta anche dal server: senza rete resterebbe
|
||||||
|
// nella scheda inviata pur sparendo dal dispositivo.
|
||||||
|
if (!attached.ToUpload && !NetworkService.ConnectionAvailable)
|
||||||
|
{
|
||||||
|
Snackbar.Clear();
|
||||||
|
Snackbar.Add("Per eliminare una foto già inviata serve la connessione", Severity.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_removedAttachments.Add(attached);
|
||||||
|
}
|
||||||
|
|
||||||
AttachedList.RemoveAt(index);
|
AttachedList.RemoveAt(index);
|
||||||
_attachmentsDirty = true;
|
_attachmentsDirty = true;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ public class AttachedDto
|
|||||||
public string? ThumbPath { get; set; }
|
public string? ThumbPath { get; set; }
|
||||||
|
|
||||||
public bool SavedOnAppData { get; set; }
|
public bool SavedOnAppData { get; set; }
|
||||||
public bool ToRemove { get; set; }
|
|
||||||
public bool ToUpload { get; set; }
|
public bool ToUpload { get; set; }
|
||||||
|
|
||||||
public Stream? FileContent =>
|
public Stream? FileContent =>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public interface IIntegrySteupService
|
|||||||
Task<SaveSchedaResponseDto?> SaveMultipleSchede(List<SaveRequestDto> request);
|
Task<SaveSchedaResponseDto?> SaveMultipleSchede(List<SaveRequestDto> request);
|
||||||
Task CompleteInspection(string activityId);
|
Task CompleteInspection(string activityId);
|
||||||
Task UploadFile(string activityId, byte[] file, string fileName);
|
Task UploadFile(string activityId, byte[] file, string fileName);
|
||||||
|
Task RemoveFile(string activityId, string fileName);
|
||||||
|
|
||||||
Task DeleteScheda(string activityId);
|
Task DeleteScheda(string activityId);
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,20 @@ public class IntegrySteupService(IIntegryApiRestClient integryApiRestClient) : I
|
|||||||
content.Dispose();
|
content.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toglie un allegato dalla scheda. Il server lo riconosce dal nome con cui
|
||||||
|
/// è stato caricato da <see cref="UploadFile"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Task RemoveFile(string activityId, string fileName) =>
|
||||||
|
integryApiRestClient.AuthorizedGet<object>(
|
||||||
|
$"{BaseRequest}/removeAttachment",
|
||||||
|
new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
{ "activityId", activityId },
|
||||||
|
{ "fileName", fileName }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
public Task DeleteScheda(string activityId) =>
|
public Task DeleteScheda(string activityId) =>
|
||||||
integryApiRestClient.AuthorizedGet<object>(
|
integryApiRestClient.AuthorizedGet<object>(
|
||||||
$"{BaseRequest}/deleteScheda",
|
$"{BaseRequest}/deleteScheda",
|
||||||
|
|||||||
Reference in new issue
Block a user