Prepare Pro 1.0.1 imports and license tooling

This commit is contained in:
livio committed 2026-09-28 19:41:57 +02:00
1 parent 62f28c5621
commit 0b5017739c
13 files changed
+437 -48

No files matched your search

+128 -3
View File
@@ -10,6 +10,7 @@
@inject MapExportService MapExportService
@inject CloudSyncService CloudSyncService
@inject AppUpdateService AppUpdateService
@inject IncomingFileService IncomingFiles
@inject AppLocalizationService L10n
@inject EntitlementService Entitlements
@implements IAsyncDisposable
@@ -741,6 +742,7 @@
private bool NavigationMenuOpen { get; set; }
private bool HasUnsavedChanges { get; set; }
private DotNetObjectReference<Home>? MapCallbackReference { get; set; }
private bool MapReady { get; set; }
private bool IncludeCadastralParcels { get; set; } = true;
private bool IncludeKmlSheetLayer { get; set; }
private bool ShowImportedSheetLayer { get; set; }
@@ -830,6 +832,7 @@
protected override async Task OnInitializedAsync()
{
IncomingFiles.FileAvailable += OnIncomingFileAvailable;
L10n.Load();
L10n.ApplyCulture();
await Entitlements.LoadAsync();
@@ -878,6 +881,7 @@
{
MapCallbackReference = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("lmeMap.init", MapCallbackReference);
MapReady = true;
await ApplyLocalizationToMapAsync();
await ApplyEntitlementsToMapAsync();
await ApplyFlagPreferencesToMapAsync();
@@ -886,6 +890,7 @@
await MigrateLegacyProjectAsync();
var offlineCount = await JS.InvokeAsync<int>("lmeMap.getOfflineTileStats");
OfflineStatus = offlineCount == 0 ? "Nessuna tessera salvata." : $"{offlineCount} tessere disponibili offline.";
await ProcessIncomingFilesAsync();
await InvokeAsync(StateHasChanged);
}
}
@@ -1582,7 +1587,11 @@
private async Task RestoreBackup()
{
if (!CanUse(AppFeatures.ProjectArchive)) { await ShowProRequiredAsync(); return; }
var file = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Scegli backup LME" });
var file = await FilePicker.Default.PickAsync(new PickOptions
{
PickerTitle = "Scegli backup LME (.lmebackup.zip)",
FileTypes = BackupPickerFileTypes
});
if (file is null) return;
var path = await CopyPickedFileToCacheAsync(file);
var count = await ProjectStorageService.RestoreBackupAsync(path);
@@ -1593,7 +1602,11 @@
private async Task ImportProject()
{
if (!CanUse(AppFeatures.ProjectArchive)) { await ShowProRequiredAsync(); return; }
var file = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Scegli progetto LME" });
var file = await FilePicker.Default.PickAsync(new PickOptions
{
PickerTitle = "Scegli progetto LME (.lmeproject.json)",
FileTypes = ProjectPickerFileTypes
});
if (file is null) return;
var path = await CopyPickedFileToCacheAsync(file);
var imported = await ProjectStorageService.ImportProjectAsync(path);
@@ -1612,6 +1625,117 @@
return path;
}
private void OnIncomingFileAvailable()
{
if (MapReady)
{
_ = InvokeAsync(ProcessIncomingFilesAsync);
}
}
private async Task ProcessIncomingFilesAsync()
{
while (IncomingFiles.TryDequeue(out var incoming) && incoming is not null)
{
try
{
var lowerName = incoming.FileName.ToLowerInvariant();
if (lowerName.EndsWith(".kml", StringComparison.Ordinal)
|| lowerName.EndsWith(".kmz", StringComparison.Ordinal)
|| lowerName.EndsWith(".geojson", StringComparison.Ordinal)
|| await IsGeoJsonAsync(incoming.Path))
{
var content = Convert.ToBase64String(await File.ReadAllBytesAsync(incoming.Path));
await JS.InvokeAsync<string>("lmeMap.importExternalGisFile", incoming.FileName, content, incoming.ContentType);
continue;
}
if (lowerName.EndsWith(".zip", StringComparison.Ordinal))
{
if (!CanUse(AppFeatures.ProjectArchive))
{
await ShowProRequiredAsync();
continue;
}
var count = await ProjectStorageService.RestoreBackupAsync(incoming.Path);
await RefreshProjectsAsync();
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Backup aperto: {count} progetti ripristinati.");
continue;
}
if (lowerName.EndsWith(".json", StringComparison.Ordinal))
{
if (!CanUse(AppFeatures.ProjectArchive))
{
await ShowProRequiredAsync();
continue;
}
var imported = await ProjectStorageService.ImportProjectAsync(incoming.Path);
await RefreshProjectsAsync();
SelectedProjectId = imported.Id;
await RefreshProjectVersionsAsync(true);
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Progetto aperto: {imported.Name}.");
continue;
}
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Formato non riconosciuto: {incoming.FileName}.");
}
catch (Exception error)
{
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Impossibile aprire {incoming.FileName}: {error.GetBaseException().Message}");
}
finally
{
try { File.Delete(incoming.Path); } catch { }
}
}
}
private static async Task<bool> IsGeoJsonAsync(string path)
{
if (!Path.GetExtension(path).Equals(".json", StringComparison.OrdinalIgnoreCase))
{
return false;
}
try
{
await using var stream = File.OpenRead(path);
using var document = await System.Text.Json.JsonDocument.ParseAsync(stream);
if (!document.RootElement.TryGetProperty("type", out var typeProperty))
{
return false;
}
var type = typeProperty.GetString();
return type is "Feature" or "FeatureCollection" or "Point" or "MultiPoint"
or "LineString" or "MultiLineString" or "Polygon" or "MultiPolygon"
or "GeometryCollection";
}
catch
{
return false;
}
}
private static readonly FilePickerFileType ProjectPickerFileTypes = new(new Dictionary<DevicePlatform, IEnumerable<string>>
{
[DevicePlatform.Android] = ["application/json", "text/json", "text/plain", "application/octet-stream"],
[DevicePlatform.WinUI] = [".json"],
[DevicePlatform.iOS] = ["public.json", "public.data"],
[DevicePlatform.macOS] = ["public.json", "public.data"]
});
private static readonly FilePickerFileType BackupPickerFileTypes = new(new Dictionary<DevicePlatform, IEnumerable<string>>
{
[DevicePlatform.Android] = ["application/zip", "application/x-zip-compressed", "application/octet-stream"],
[DevicePlatform.WinUI] = [".zip"],
[DevicePlatform.iOS] = ["public.zip-archive", "public.data"],
[DevicePlatform.macOS] = ["public.zip-archive", "public.data"]
});
private async Task LoadProjectVersion()
{
if (SelectedProject is null || string.IsNullOrWhiteSpace(SelectedVersionId)) return;
@@ -1928,7 +2052,7 @@
UpdateStatus = $"Installata {AppUpdateService.CurrentVersion}. Disponibile {update.Version}.";
UpdateDownloadUrl = update.DownloadUrl;
}
else UpdateStatus = $"Versione {AppUpdateService.CurrentVersion} aggiornata. Ultima release Gitea: {update.Version}.";
else UpdateStatus = $"Versione {AppUpdateService.CurrentVersion} aggiornata. Ultima release Pro su Gitea: {update.Version}.";
}
catch (Exception error) { UpdateStatus = $"Controllo non riuscito: {error.GetBaseException().Message}"; }
finally { UpdateBusy = false; }
@@ -2349,6 +2473,7 @@
public ValueTask DisposeAsync()
{
IncomingFiles.FileAvailable -= OnIncomingFileAvailable;
CancelCatalogWork();
LocationService.StopTracking();
GpsLookupCancellation?.Cancel();