| @@ -72,16 +72,28 @@ | |||
| </div> | |||
| @if (OnlineStatusProvider.Online) { | |||
| <div class="mat-layout-grid-cell mat-layout-grid-cell-span-1 justify-content-center" style="text-align: center"> | |||
| <MatRipple class="inputfile-mat-ripple" Color="@MatRippleColor.Default" @onclick="InitializeMapPosition"> | |||
| <label> | |||
| <svg xmlns="http://www.w3.org/2000/svg" height="48px" width="48px" viewBox="0 0 24 24" fill="#000000"> | |||
| <path d="M0 0h24v24H0V0z" fill="none" /> | |||
| <path d="M13 3.06V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06c-.46-4.17-3.77-7.48-7.94-7.94zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z" /> | |||
| <circle cx="12" cy="12" opacity=".3" r="2" /> | |||
| <path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z" /> | |||
| </svg> | |||
| </label> | |||
| </MatRipple> | |||
| <div> | |||
| <MatRipple class="inputfile-mat-ripple" Color="@MatRippleColor.Default" @onclick="InitializeDeviceMapPosition"> | |||
| <label> | |||
| <svg xmlns="http://www.w3.org/2000/svg" height="48px" width="48px" viewBox="0 0 24 24" fill="#000000"> | |||
| <path d="M0 0h24v24H0V0z" fill="none" /> | |||
| <path d="M13 3.06V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06c-.46-4.17-3.77-7.48-7.94-7.94zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z" /> | |||
| <circle cx="12" cy="12" opacity=".3" r="2" /> | |||
| <path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z" /> | |||
| </svg> | |||
| </label> | |||
| </MatRipple> | |||
| </div> | |||
| <div style="margin-top:1rem"> | |||
| <MatRipple class="inputfile-mat-ripple" Color="@MatRippleColor.Default" @onclick="InitializeBicycleMapPosition"> | |||
| <label> | |||
| <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 20 20" height="48px" viewBox="0 0 20 20" width="48px" fill="#000000"> | |||
| <g><rect fill="none" height="20" width="20" /></g> | |||
| <g><g><path d="M15.5,9h-0.68l-1.58-4.34C13.1,4.26,12.72,4,12.3,4H10v1h2.3l1.46,4H8.75L8.38,8H10V7H6v1h1.32l1.46,4H7.95 C7.7,10.19,6.13,8.86,4.2,9.01c-1.64,0.13-3.01,1.46-3.18,3.1C0.8,14.25,2.41,16,4.5,16c1.79,0,3.21-1.29,3.45-3h4.1 c0.25,1.81,1.83,3.14,3.75,2.99c1.64-0.13,3.01-1.46,3.18-3.1C19.2,10.75,17.59,9,15.5,9z M6.95,13c-0.23,1.15-1.22,2-2.45,2 C3.1,15,2,13.9,2,12.5S3.1,10,4.5,10c1.23,0,2.23,0.85,2.45,2H4v1H6.95z M12.05,12H9.84l-0.73-2h3.92 C12.5,10.52,12.16,11.22,12.05,12z M15.5,15c-1.4,0-2.5-1.1-2.5-2.5c0-0.94,0.5-1.73,1.24-2.16l1.03,2.83l0.94-0.34l-1.02-2.8 C15.3,10.02,15.4,10,15.5,10c1.4,0,2.5,1.1,2.5,2.5S16.9,15,15.5,15z" /></g></g> | |||
| </svg> | |||
| </label> | |||
| </MatRipple> | |||
| </div> | |||
| </div> | |||
| <div class="mat-layout-grid-cell mat-layout-grid-cell-span-3"> | |||
| <div class="w-100 fv-osm-tile map-wrapper"> | |||
| @@ -220,7 +232,7 @@ | |||
| LatLng coordinates = new LatLng(report.GeographicInfo.Latitude, report.GeographicInfo.Longitude); | |||
| MouseEvent mouseEvent = new MouseEvent(); | |||
| mouseEvent.LatLng = coordinates; | |||
| await InitializeMapPosition(); | |||
| await InitializeBicycleMapPosition(); | |||
| await AddBicycleMarkerOnClickPosition(mouseEvent); | |||
| StateHasChanged(); | |||
| } | |||
| @@ -301,13 +313,14 @@ | |||
| } | |||
| } | |||
| private void refreshGUIFromDto() { | |||
| private async void refreshGUIFromDto() { | |||
| FoundReport report = ReportDataProvider.GetFoundReport(); | |||
| bicycleGeoPosition.Address = report.GeographicInfo.Address; | |||
| bicycleGeoPosition.Zip = report.GeographicInfo.Postcode; | |||
| bicycleGeoPosition.City = report.GeographicInfo.Town; | |||
| bicycleGeoPosition.Latitude = report.GeographicInfo.Latitude; | |||
| bicycleGeoPosition.Longitude = report.GeographicInfo.Longitude; | |||
| bicycleGeoPosition.DisplayCity = await GetFormattedAddressZipAndTown(ReportDataProvider); | |||
| imgUrl = report.FotoString; | |||
| selectedColor = Array.Find(Colors, color => color.Id == report.FarbeId); | |||
| selectedBrand = String.IsNullOrEmpty(report.NeueMarke) ? Array.Find(Brands, brand => brand.Id == report.MarkeId) : new Brand(-999, report.NeueMarke); | |||
| @@ -315,6 +328,7 @@ | |||
| frameNumber = report.RahmenNummer; | |||
| remark = report.Bemerkung; | |||
| abholadresseIsNotContact = !report.AbholadresseIstKontakt; | |||
| StateHasChanged(); | |||
| } | |||
| private void updateDtoFromGUI() { | |||
| @@ -38,8 +38,7 @@ namespace cwebplusApp.Pages { | |||
| private NominatimService NominatimService { get; set; } | |||
| private static NominatimReverseAddress addressDto; | |||
| private static NominatimReverseAddress AddressDto { get => addressDto; set { addressDto = value; } } | |||
| private NominatimReverseAddress addressDto; | |||
| public CaritasServiceFundVeloKeyDataPageBase() : base() { | |||
| @@ -66,14 +65,16 @@ namespace cwebplusApp.Pages { | |||
| await this.mapRef.OnClick(async (MouseEvent mouseEvent) => await OnMouseMapClicked(mouseEvent)); | |||
| } | |||
| protected async Task InitializeMapPosition() { | |||
| protected async Task InitializeDeviceMapPosition() { | |||
| CreateBicycleMarkerOptions(); | |||
| await AddEventsToMap(); | |||
| if (this.bicycleGeoPosition.Latitude == 0 && this.bicycleGeoPosition.Longitude == 0) { | |||
| await ShowDeviceGeoLocation(); | |||
| } else { | |||
| await ShowBicycleGeoLocation(); | |||
| } | |||
| await ShowDeviceGeoLocation(); | |||
| } | |||
| protected async Task InitializeBicycleMapPosition() { | |||
| CreateBicycleMarkerOptions(); | |||
| await AddEventsToMap(); | |||
| await ShowBicycleGeoLocation(); | |||
| } | |||
| protected async Task<string> GetFormattedAddressZipAndTown(ReportDataProvider ReportDataProvider) { | |||
| @@ -81,6 +82,12 @@ namespace cwebplusApp.Pages { | |||
| this.bicycleGeoPosition.Latitude = ReportDataProvider.GetFoundReport().GeographicInfo.Latitude; | |||
| this.bicycleGeoPosition.Longitude = ReportDataProvider.GetFoundReport().GeographicInfo.Longitude; | |||
| addressDto = await NominatimService.GetAddressForCoordinates(this.bicycleGeoPosition.Latitude, this.bicycleGeoPosition.Longitude); | |||
| if (addressDto == null) { | |||
| addressDto = new(); | |||
| addressDto.address = new(); | |||
| addressDto.address.postcode = ReportDataProvider.GetFoundReport().GeographicInfo.Postcode; | |||
| addressDto.address.town = ReportDataProvider.GetFoundReport().GeographicInfo.Town; | |||
| } | |||
| } | |||
| return GetFormattedAddressZipAndTown(addressDto); | |||
| } | |||
| @@ -126,11 +133,6 @@ namespace cwebplusApp.Pages { | |||
| if (this.bicycleGeoPosition.Latitude != 0 && this.bicycleGeoPosition.Longitude != 0) { | |||
| LatLng geoPosition = new(this.bicycleGeoPosition.Latitude, this.bicycleGeoPosition.Longitude); | |||
| if (this.devicePositionMarker != null) { | |||
| await devicePositionMarker.Remove(); | |||
| } | |||
| this.devicePositionMarker = await this.MarkerFactory.CreateAndAddToMap(geoPosition, this.mapRef); | |||
| await this.mapRef.SetZoom(16); | |||
| await this.mapRef.SetView(geoPosition); | |||
| } | |||
| @@ -145,7 +147,7 @@ namespace cwebplusApp.Pages { | |||
| } | |||
| private static string SplitAndGetFirstPostcode(string postcode) { | |||
| return postcode.Split("-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)[0]; | |||
| return String.IsNullOrEmpty(postcode)? "": postcode.Split("-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)[0]; | |||
| } | |||
| private async Task OnMouseMapClicked(MouseEvent mouseEvent) { | |||
| @@ -61,13 +61,14 @@ | |||
| @code { | |||
| private int NbrPendingReports; | |||
| private int NbrPendingReports = 0; | |||
| protected async override void OnInitialized() { | |||
| base.OnInitialized(); | |||
| PageHistoryManager.AddPageToHistory(NavigationManager.Uri); | |||
| ReportDataProvider.Report = null; | |||
| ReportDataProvider.ReportRepositoryItem = null; | |||
| StateHasChanged(); | |||
| NbrPendingReports = await ReportRepositoryService.GetNbrOfPendingReports(); | |||
| StateHasChanged(); | |||
| } | |||
| @@ -93,6 +94,6 @@ | |||
| } | |||
| private string getOpacity() { | |||
| return NbrPendingReports > 0 ? "opacity:1.0" : "opacity:0.5"; | |||
| return NbrPendingReports > 0 ? "opacity:1.0" : "opacity:0.5"; | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| @inject ReportDataProvider ReportDataProvider | |||
| @inject ReportRepositoryService ReportRepositoryService | |||
| @inject IStringLocalizer<Resources> i18n; | |||
| @inject Toaster Toaster; | |||
| <div class="row px-3 h-100"> | |||
| <div class="row no-gutters align-items-start w-100" style="padding-top: 1rem;"> | |||
| @@ -30,9 +31,9 @@ | |||
| </div> | |||
| <div style="padding-top: 0.8rem">@context.ID</div> | |||
| </td> | |||
| <td>@translateType(context.ReportType)</td> | |||
| <td>@ReportRepositoryService.GetCurrentDateTimeFromMillis(context.ID)</td> | |||
| <td>@getAddress(context)</td> | |||
| <td style="@getTypeBackgroundStyle(context.ReportType)">@translateType(context.ReportType)</td> | |||
| <td style="white-space: nowrap">@ReportRepositoryService.GetCurrentDateTimeFromMillis(context.ID)</td> | |||
| <td style="white-space: nowrap">@getAddress(context)</td> | |||
| <td>@getPicture(context)</td> | |||
| <td> | |||
| <MatIconButton Icon="delete_forever" OnClick="@(_ => DeleteReport(context))"></MatIconButton> | |||
| @@ -73,6 +74,11 @@ | |||
| return i18n.GetString("ReportType." + type); | |||
| } | |||
| private string getTypeBackgroundStyle(ReportRepositoryItem.Type type) { | |||
| return ReportRepositoryItem.Type.FOUND.Equals(type) ? "background: linear-gradient(45deg, darkseagreen, transparent)" : "background: linear-gradient(45deg, darksalmon, transparent)"; | |||
| } | |||
| private string getAddress(ReportRepositoryItem item) { | |||
| return ReportRepositoryItem.Type.FOUND.Equals(item.ReportType) ? ((FoundReportRepositoryItem)item).Report.GeographicInfo.Address : ""; | |||
| } | |||
| @@ -82,8 +88,15 @@ | |||
| return ""; | |||
| } | |||
| private void DeleteReport(ReportRepositoryItem item) { | |||
| private async void DeleteReport(ReportRepositoryItem item) { | |||
| try { | |||
| await ReportRepositoryService.DeleteReport(item); | |||
| pendingReports.Remove(item); | |||
| StateHasChanged(); | |||
| Toaster.ShowSuccess(i18n.GetString("Success.DeleteReport.Title"), i18n.GetString("Success.DeleteReport.Msg", item.ID)); | |||
| } catch (Exception ex) { | |||
| Toaster.ShowWarning(i18n.GetString("Error.DeleteReport.Title"), i18n.GetString("Error.DeleteReport.Msg", item.ID)); | |||
| } | |||
| } | |||
| private void SendReport(ReportRepositoryItem item) { | |||
| @@ -7,8 +7,10 @@ using MatBlazor; | |||
| using Microsoft.AspNetCore.Components.WebAssembly.Hosting; | |||
| using Microsoft.Extensions.DependencyInjection; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Net.Http; | |||
| using System.Threading.Tasks; | |||
| using TG.Blazor.IndexedDB; | |||
| namespace CaritasPWA { | |||
| public class Program { | |||
| @@ -18,12 +20,12 @@ namespace CaritasPWA { | |||
| builder.Services.AddMatBlazor(); | |||
| builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); | |||
| builder.Services.AddSingleton<IBicycleRestService, BicycleRestService>(); | |||
| builder.Services.AddScoped<IBicycleRestService, BicycleRestService>(); | |||
| builder.Services.AddSingleton<AppState>(); | |||
| builder.Services.AddSingleton<PageHistoryManager>(); | |||
| builder.Services.AddSingleton<ReportDataProvider>(); | |||
| builder.Services.AddSingleton<OnlineStatusProvider>(); | |||
| builder.Services.AddSingleton<ReportRepositoryService>(); | |||
| builder.Services.AddScoped<ReportRepositoryService>(); | |||
| builder.Services.AddScoped<Toaster>(); | |||
| builder.Services.AddScoped<UserDataProvider>(); | |||
| builder.Services.AddScoped<MasterDataService>(); | |||
| @@ -44,6 +46,28 @@ namespace CaritasPWA { | |||
| config.HideTransitionDuration = 150; | |||
| }); | |||
| builder.Services.AddIndexedDB(dbStore => { | |||
| dbStore.DbName = "CwebPlusAppDB"; | |||
| dbStore.Version = 1; | |||
| dbStore.Stores.Add(new StoreSchema { | |||
| Name = "FoundReportRepositoryItems", | |||
| PrimaryKey = new IndexSpec { Name = "id", KeyPath = "id", Auto = true }, | |||
| Indexes = new List<IndexSpec> { | |||
| new IndexSpec{Name="serverRefNbr", KeyPath = "serverRefNbr", Auto=false}, | |||
| new IndexSpec{Name="status", KeyPath = "status", Auto=false} | |||
| } | |||
| }); | |||
| dbStore.Stores.Add(new StoreSchema { | |||
| Name = "MissingReportRepositoryItems", | |||
| PrimaryKey = new IndexSpec { Name = "id", KeyPath = "id", Auto = true }, | |||
| Indexes = new List<IndexSpec> { | |||
| new IndexSpec{Name="serverRefNbr", KeyPath = "serverRefNbr", Auto=false}, | |||
| new IndexSpec{Name="status", KeyPath = "status", Auto=false} | |||
| } | |||
| }); | |||
| }); | |||
| await builder.Build().RunAsync(); | |||
| } | |||
| @@ -4,8 +4,16 @@ | |||
| private FoundReport report; | |||
| public FoundReport Report { get => report; set => report = value; } | |||
| public FoundReportRepositoryItem() : base(Type.FOUND, null) { | |||
| } | |||
| public FoundReportRepositoryItem(FoundReport _report, long identifier) : base(Type.FOUND, identifier) { | |||
| this.report = _report; | |||
| } | |||
| public override FoundReport GetReport() { | |||
| return report; | |||
| } | |||
| } | |||
| } | |||
| @@ -4,8 +4,15 @@ | |||
| private MissingReport report; | |||
| public MissingReport Report { get => report; set => report = value; } | |||
| public MissingReportRepositoryItem() : base(Type.MISSING, null) { | |||
| } | |||
| public MissingReportRepositoryItem(MissingReport _report, long identifier) : base(Type.MISSING, identifier) { | |||
| this.report = _report; | |||
| } | |||
| public override MissingReport GetReport() { | |||
| return report; | |||
| } | |||
| } | |||
| } | |||
| @@ -4,21 +4,23 @@ namespace cwebplusApp.Shared.Models { | |||
| public abstract class ReportRepositoryItem { | |||
| public enum Type { FOUND, MISSING } | |||
| public enum State { PENDING, TRANSMITTED } | |||
| private long id; | |||
| private string serverRefNbr; | |||
| private bool transmitted; | |||
| private State status; | |||
| private Type reportType; | |||
| public long ID { get => id; set => id = value; } | |||
| public string ServerRefNbr { get => serverRefNbr; set => serverRefNbr = value; } | |||
| public bool Transmitted { get => transmitted; set => transmitted = value; } | |||
| public State Status { get => status; set => status = value; } | |||
| public Type ReportType { get => reportType; set => reportType = value; } | |||
| public ReportRepositoryItem(Type _reportType, long identifier) { | |||
| this.id = identifier; | |||
| public ReportRepositoryItem(Type _reportType, long? identifier) { | |||
| this.id = identifier.HasValue ? identifier.Value : 0; | |||
| this.reportType = _reportType; | |||
| this.transmitted = false; | |||
| this.status = State.PENDING; | |||
| } | |||
| public override bool Equals(Object obj) { | |||
| @@ -37,5 +39,7 @@ namespace cwebplusApp.Shared.Models { | |||
| public override string ToString() { | |||
| return base.ToString(); | |||
| } | |||
| public abstract Report GetReport(); | |||
| } | |||
| } | |||
| @@ -273,6 +273,12 @@ | |||
| <data name="DevelopedBy" xml:space="preserve"> | |||
| <value>Entwickelt durch</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Die Meldung mit der ID {0:d} konnte nicht gelöscht werden!</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Löschung der Meldung fehlgeschlagen!</value> | |||
| </data> | |||
| <data name="Error.GeoLocation.Msg" xml:space="preserve"> | |||
| <value>Die Geoposition konnte nicht ermittelt werden. Fehlermeldung : {0:s}</value> | |||
| </data> | |||
| @@ -462,6 +468,12 @@ | |||
| <data name="Specifications" xml:space="preserve"> | |||
| <value>Angaben</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Die Meldung mit der ID {0:d} wurde erfolgreich gelöscht!</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Meldung gelöscht!</value> | |||
| </data> | |||
| <data name="Type" xml:space="preserve"> | |||
| <value>Typ</value> | |||
| </data> | |||
| @@ -273,6 +273,12 @@ | |||
| <data name="DevelopedBy" xml:space="preserve"> | |||
| <value>Développé par</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Le rapport avec l'ID {0:d} n'a pas pu être supprimé !</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Title" xml:space="preserve"> | |||
| <value>La suppression du rapport a échoué !</value> | |||
| </data> | |||
| <data name="Error.GeoLocation.Msg" xml:space="preserve"> | |||
| <value>La géoposition n'a pas pu être déterminée. Message d'erreur : {0:s}</value> | |||
| </data> | |||
| @@ -462,6 +468,12 @@ | |||
| <data name="Specifications" xml:space="preserve"> | |||
| <value>Description détaillée</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Le rapport avec l'ID {0:d} a été supprimé avec succès!</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Rapport supprimé!</value> | |||
| </data> | |||
| <data name="Type" xml:space="preserve"> | |||
| <value>Typ</value> | |||
| </data> | |||
| @@ -273,6 +273,12 @@ | |||
| <data name="DevelopedBy" xml:space="preserve"> | |||
| <value>Sviluppata da</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Il rapporto con ID {0:d} non può essere cancellato!</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Cancellazione del rapporto fallita!</value> | |||
| </data> | |||
| <data name="Error.GeoLocation.Msg" xml:space="preserve"> | |||
| <value>Non è stato possibile determinare la posizione geografica. Messaggio d'errore: {0:s}</value> | |||
| </data> | |||
| @@ -462,6 +468,12 @@ | |||
| <data name="Specifications" xml:space="preserve"> | |||
| <value>Descrizione dettagliata</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>Il rapporto con l'ID {0:d} è stato cancellato con successo!</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Rapporto cancellato!</value> | |||
| </data> | |||
| <data name="Type" xml:space="preserve"> | |||
| <value>Tipo</value> | |||
| </data> | |||
| @@ -273,6 +273,12 @@ | |||
| <data name="DevelopedBy" xml:space="preserve"> | |||
| <value>Developed by</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>The Report with ID {0:d} could not be deleted!</value> | |||
| </data> | |||
| <data name="Error.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Report deletion failed!</value> | |||
| </data> | |||
| <data name="Error.GeoLocation.Msg" xml:space="preserve"> | |||
| <value>The Geo Location couldn't be get. ErrorMessage : {0:s}</value> | |||
| </data> | |||
| @@ -462,6 +468,12 @@ | |||
| <data name="Specifications" xml:space="preserve"> | |||
| <value>Specifications</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Msg" xml:space="preserve"> | |||
| <value>The Report with ID {0:d} was successfully deleted!</value> | |||
| </data> | |||
| <data name="Success.DeleteReport.Title" xml:space="preserve"> | |||
| <value>Report deleted!</value> | |||
| </data> | |||
| <data name="Type" xml:space="preserve"> | |||
| <value>Type</value> | |||
| </data> | |||
| @@ -88,7 +88,7 @@ namespace cwebplusApp.Shared.Services { | |||
| public async Task<ReportResponse> SendFoundReport(Report report) { | |||
| string subResourceUrl = configuration.GetValue<string>("subresource_url_foundreport"); | |||
| return await SendReport(report, subResourceUrl, new FoundReportRepositoryItem((FoundReport)report, ReportRepositoryService.GetCurrentTimeInMillis())); | |||
| return await SendReport(report, subResourceUrl, new FoundReportRepositoryItem((FoundReport)report, 0)); | |||
| } | |||
| public async Task<ReportResponse> SendFoundReport(FoundReportRepositoryItem reportRepositoryItem) { | |||
| @@ -98,7 +98,7 @@ namespace cwebplusApp.Shared.Services { | |||
| public async Task<ReportResponse> SendMissingReport(Report report) { | |||
| string subResourceUrl = configuration.GetValue<string>("subresource_url_missingreport"); | |||
| return await SendReport(report, subResourceUrl, new MissingReportRepositoryItem((MissingReport)report, ReportRepositoryService.GetCurrentTimeInMillis())); | |||
| return await SendReport(report, subResourceUrl, new MissingReportRepositoryItem((MissingReport)report, 0)); | |||
| } | |||
| public async Task<ReportResponse> SendMissingReport(MissingReportRepositoryItem reportRepositoryItem) { | |||
| @@ -149,7 +149,7 @@ namespace cwebplusApp.Shared.Services { | |||
| protected async Task<ReportResponse> SendReport(Report report, string subResourceUrl, ReportRepositoryItem reportRepositoryItem) { | |||
| ReportResponse response = null; | |||
| if (onlineStatusProvider.Online && !reportRepositoryItem.Transmitted) { | |||
| if (onlineStatusProvider.Online && ReportRepositoryItem.State.PENDING.Equals(reportRepositoryItem.Status)) { | |||
| if (httpClient != null) { | |||
| if (!String.IsNullOrEmpty(subResourceUrl)) { | |||
| string reportJson = JsonConvert.SerializeObject(report); | |||
| @@ -160,7 +160,7 @@ namespace cwebplusApp.Shared.Services { | |||
| response.StatusCode = httpResult.StatusCode; | |||
| if (HttpStatusCode.OK == response.StatusCode) { | |||
| reportRepositoryItem.ServerRefNbr = response.Data[0]; | |||
| reportRepositoryItem.Transmitted = true; | |||
| reportRepositoryItem.Status = ReportRepositoryItem.State.TRANSMITTED; | |||
| await reportRepositoryService.SaveReport(reportRepositoryItem); | |||
| } | |||
| return response; | |||
| @@ -1,22 +1,23 @@ | |||
| using cwebplusApp.Shared.Models; | |||
| using Microsoft.JSInterop; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Threading.Tasks; | |||
| using TG.Blazor.IndexedDB; | |||
| namespace cwebplusApp.Shared.Services { | |||
| public sealed class ReportRepositoryService { | |||
| private const string KeyNameFoundReports = "foundReportRepository"; | |||
| private const string KeyNameMissingReports = "missingReportRepository"; | |||
| private const string FoundReportRepository_Store_Name = "FoundReportRepositoryItems"; | |||
| private const string MissingReportRepository_Store_Name = "MissingReportRepositoryItems"; | |||
| private readonly IJSRuntime jsRuntime; | |||
| private bool initialized; | |||
| public event EventHandler Changed; | |||
| private readonly IndexedDBManager dbManager; | |||
| public ReportRepositoryService(IJSRuntime jsRuntime) { | |||
| public ReportRepositoryService(IJSRuntime jsRuntime, IndexedDBManager _dbManager) { | |||
| this.jsRuntime = jsRuntime; | |||
| this.dbManager = _dbManager; | |||
| } | |||
| public async Task SaveReport(ReportRepositoryItem reportRepositoryItem) { | |||
| @@ -28,97 +29,86 @@ namespace cwebplusApp.Shared.Services { | |||
| } | |||
| public async ValueTask<List<FoundReportRepositoryItem>> GetFoundReports() { | |||
| // Register the Storage event handler. This handler calls OnStorageUpdated when the storage changed. | |||
| // This way, you can reload the settings when another instance of the application (tab / window) save the settings | |||
| if (!initialized) { | |||
| // Create a reference to the current object, so the JS function can call the public method "OnStorageUpdated" | |||
| var reference = DotNetObjectReference.Create(this); | |||
| await this.jsRuntime.InvokeVoidAsync("BlazorRegisterStorageEvent", reference); | |||
| this.initialized = true; | |||
| } | |||
| // Read the JSON string that contains the data from the local storage | |||
| List<FoundReportRepositoryItem> result; | |||
| var str = await this.jsRuntime.InvokeAsync<string>("BlazorGetLocalStorage", KeyNameFoundReports); | |||
| if (str != null) { | |||
| result = JsonConvert.DeserializeObject<List<FoundReportRepositoryItem>>(str) ?? new(); | |||
| } else { | |||
| result = new(); | |||
| } | |||
| return result; | |||
| return await this.dbManager.GetRecords<FoundReportRepositoryItem>(FoundReportRepository_Store_Name); | |||
| } | |||
| public async ValueTask<List<FoundReportRepositoryItem>> GetPendingFoundReports() { | |||
| List<FoundReportRepositoryItem> pendingFoundReps = new(); | |||
| List<FoundReportRepositoryItem> foundReps = await GetFoundReports(); | |||
| foreach (FoundReportRepositoryItem report in foundReps) { | |||
| if (!report.Transmitted) { | |||
| pendingFoundReps.Add(report); | |||
| } | |||
| } | |||
| return pendingFoundReps; | |||
| var indexSearch = new StoreIndexQuery<ReportRepositoryItem.State> { | |||
| Storename = FoundReportRepository_Store_Name, | |||
| IndexName = "status", | |||
| QueryValue = ReportRepositoryItem.State.PENDING | |||
| }; | |||
| return new List<FoundReportRepositoryItem>(await this.dbManager.GetAllRecordsByIndex<ReportRepositoryItem.State, FoundReportRepositoryItem>(indexSearch)); | |||
| } | |||
| public async ValueTask<List<MissingReportRepositoryItem>> GetMissingReports() { | |||
| // Register the Storage event handler. This handler calls OnStorageUpdated when the storage changed. | |||
| // This way, you can reload the settings when another instance of the application (tab / window) save the settings | |||
| if (!initialized) { | |||
| // Create a reference to the current object, so the JS function can call the public method "OnStorageUpdated" | |||
| var reference = DotNetObjectReference.Create(this); | |||
| await this.jsRuntime.InvokeVoidAsync("BlazorRegisterStorageEvent", reference); | |||
| this.initialized = true; | |||
| } | |||
| // Read the JSON string that contains the data from the local storage | |||
| List<MissingReportRepositoryItem> result; | |||
| var str = await this.jsRuntime.InvokeAsync<string>("BlazorGetLocalStorage", KeyNameMissingReports); | |||
| if (str != null) { | |||
| result = JsonConvert.DeserializeObject<List<MissingReportRepositoryItem>>(str) ?? new(); | |||
| } else { | |||
| result = new(); | |||
| } | |||
| return result; | |||
| public async ValueTask<List<FoundReportRepositoryItem>> GetTransmittedFoundReports() { | |||
| var indexSearch = new StoreIndexQuery<ReportRepositoryItem.State> { | |||
| Storename = FoundReportRepository_Store_Name, | |||
| IndexName = "status", | |||
| QueryValue = ReportRepositoryItem.State.TRANSMITTED | |||
| }; | |||
| return new List<FoundReportRepositoryItem>(await this.dbManager.GetAllRecordsByIndex<ReportRepositoryItem.State, FoundReportRepositoryItem>(indexSearch)); | |||
| } | |||
| public async ValueTask<List<MissingReportRepositoryItem>> GetPendingMissingReports() { | |||
| List<MissingReportRepositoryItem> pendingMissingReps = new(); | |||
| List<MissingReportRepositoryItem> missingReps = await GetMissingReports(); | |||
| foreach (MissingReportRepositoryItem report in missingReps) { | |||
| if (!report.Transmitted) { | |||
| pendingMissingReps.Add(report); | |||
| } | |||
| } | |||
| return pendingMissingReps; | |||
| public async ValueTask<List<MissingReportRepositoryItem>> GetMissingReports() { | |||
| return await this.dbManager.GetRecords<MissingReportRepositoryItem>(MissingReportRepository_Store_Name); | |||
| } | |||
| public async Task SaveFoundReport(FoundReportRepositoryItem foundReportRepositoryItem) { | |||
| List<FoundReportRepositoryItem> foundReps = await GetFoundReports(); | |||
| foundReps.Remove(foundReportRepositoryItem); | |||
| foundReps.Add(foundReportRepositoryItem); | |||
| var json = JsonConvert.SerializeObject(foundReps); | |||
| await jsRuntime.InvokeVoidAsync("BlazorSetLocalStorage", KeyNameFoundReports, json); | |||
| public async ValueTask<List<MissingReportRepositoryItem>> GetPendingMissingReports() { | |||
| var indexSearch = new StoreIndexQuery<ReportRepositoryItem.State> { | |||
| Storename = MissingReportRepository_Store_Name, | |||
| IndexName = "status", | |||
| QueryValue = ReportRepositoryItem.State.PENDING | |||
| }; | |||
| return new List<MissingReportRepositoryItem>(await this.dbManager.GetAllRecordsByIndex<ReportRepositoryItem.State, MissingReportRepositoryItem>(indexSearch)); | |||
| } | |||
| public async Task SaveMissingReport(MissingReportRepositoryItem missingReportRepositoryItem) { | |||
| List<MissingReportRepositoryItem> missingReps = await GetMissingReports(); | |||
| missingReps.Remove(missingReportRepositoryItem); | |||
| missingReps.Add(missingReportRepositoryItem); | |||
| var json = JsonConvert.SerializeObject(missingReps); | |||
| await jsRuntime.InvokeVoidAsync("BlazorSetLocalStorage", KeyNameMissingReports, json); | |||
| public async ValueTask<List<MissingReportRepositoryItem>> GetTransmittedMissingReports() { | |||
| var indexSearch = new StoreIndexQuery<ReportRepositoryItem.State> { | |||
| Storename = MissingReportRepository_Store_Name, | |||
| IndexName = "status", | |||
| QueryValue = ReportRepositoryItem.State.TRANSMITTED | |||
| }; | |||
| return new List<MissingReportRepositoryItem>(await this.dbManager.GetAllRecordsByIndex<ReportRepositoryItem.State, MissingReportRepositoryItem>(indexSearch)); | |||
| } | |||
| public async ValueTask<int> GetNbrOfPendingReports() { | |||
| List<FoundReportRepositoryItem> foundReportRepositoryItems = await GetPendingFoundReports(); | |||
| IList<FoundReportRepositoryItem> foundReportRepositoryItems = await GetPendingFoundReports(); | |||
| List<MissingReportRepositoryItem> missingReportRepositoryItems = await GetPendingMissingReports(); | |||
| return foundReportRepositoryItems.Count + missingReportRepositoryItems.Count; | |||
| } | |||
| // This method is called from BlazorRegisterStorageEvent when the storage changed | |||
| [JSInvokable] | |||
| public void OnStorageUpdated(string key) { | |||
| if (key == KeyNameMissingReports || key == KeyNameFoundReports) { | |||
| // Reset the settings. The next call to Get will reload the data | |||
| Changed?.Invoke(this, EventArgs.Empty); | |||
| public async Task SaveFoundReport(FoundReportRepositoryItem foundReportRepositoryItem) { | |||
| var newRecord = new StoreRecord<FoundReportRepositoryItem> { | |||
| Storename = FoundReportRepository_Store_Name, | |||
| Data = foundReportRepositoryItem | |||
| }; | |||
| if (foundReportRepositoryItem.ID == 0) { | |||
| foundReportRepositoryItem.ID = GetCurrentTimeInMillis(); | |||
| await this.dbManager.AddRecord(newRecord); | |||
| } else { | |||
| await this.dbManager.UpdateRecord(newRecord); | |||
| } | |||
| } | |||
| public async Task SaveMissingReport(MissingReportRepositoryItem missingReportRepositoryItem) { | |||
| var newRecord = new StoreRecord<MissingReportRepositoryItem> { | |||
| Storename = MissingReportRepository_Store_Name, | |||
| Data = missingReportRepositoryItem | |||
| }; | |||
| if (missingReportRepositoryItem.ID == 0) { | |||
| missingReportRepositoryItem.ID = GetCurrentTimeInMillis(); | |||
| await this.dbManager.AddRecord(newRecord); | |||
| } else { | |||
| await this.dbManager.UpdateRecord(newRecord); | |||
| } | |||
| } | |||
| public async Task DeleteReport(ReportRepositoryItem reportRepositoryItem) { | |||
| if (reportRepositoryItem is FoundReportRepositoryItem) { | |||
| await this.dbManager.DeleteRecord(FoundReportRepository_Store_Name, reportRepositoryItem.ID); | |||
| } else { | |||
| await this.dbManager.DeleteRecord(MissingReportRepository_Store_Name, reportRepositoryItem.ID); | |||
| } | |||
| } | |||
| @@ -4,12 +4,14 @@ | |||
| @using Microsoft.AspNetCore.Components.Routing | |||
| @using Microsoft.AspNetCore.Components.Web | |||
| @using Microsoft.AspNetCore.Components.WebAssembly.Http | |||
| @using Microsoft.AspNetCore.Components.WebAssembly.Services | |||
| @using Microsoft.Extensions.Localization | |||
| @using Microsoft.JSInterop | |||
| @using MatBlazor | |||
| @using BlazorAnimate | |||
| @using FisSst.BlazorMaps | |||
| @using BlazorGeolocation | |||
| @using TG.Blazor.IndexedDB | |||
| @using CaritasPWA | |||
| @using cwebplusApp.Shared | |||
| @using cwebplusApp.Shared.ResourceFiles | |||
| @@ -23,6 +23,7 @@ | |||
| <PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.9" /> | |||
| <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
| <PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" /> | |||
| <PackageReference Include="TG.Blazor.IndexedDB" Version="1.5.0-preview" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| @@ -33,6 +33,7 @@ | |||
| <a class="dismiss">🗙</a> | |||
| </div> | |||
| <script src="_framework/blazor.webassembly.js"></script> | |||
| <script src="_content/TG.Blazor.IndexedDB/indexedDb.Blazor.js"></script> | |||
| <script> | |||
| if ('serviceWorker' in navigator) { | |||
| navigator.serviceWorker.register('service-worker.js') | |||
| @@ -45,6 +45,7 @@ const assets = [ | |||
| '_content/matblazor/dist/matblazor.js', | |||
| '_content/blazoranimate/blazoranimateinterop.js', | |||
| '_content/blazoranimate/aos.css', | |||
| '_content/TG.Blazor.IndexedDB/indexedDb.Blazor.js', | |||
| '_framework/blazor.webassembly.js', | |||
| '_framework/blazor.boot.json', | |||
| '_framework/dotnet.5.0.9.js', | |||
| @@ -92,7 +93,7 @@ self.addEventListener('activate', event => { | |||
| // fetch events (Network first strategy, no cache update after install) | |||
| self.addEventListener('fetch', function (event) { | |||
| event.respondWith(fromNetwork(event.request, 6000).catch(function () { | |||
| event.respondWith(fromNetwork(event.request, 15000).catch(function () { | |||
| return fromCache(event.request); | |||
| })); | |||
| }); | |||