瀏覽代碼

Offline mode

master
Flo Smilari 4 年之前
父節點
當前提交
d5f3e67021

+ 27
- 6
Pages/AccountPage.razor 查看文件

</div> </div>
</div> </div>
@if (string.IsNullOrEmpty(FromRoute)) {
<div class="row no-gutters align-items-end justify-content-center w-100" style="padding-bottom:2em">
<MatButton Class="w-100" Raised="true" @onclick="SaveUserData">@i18n["Save"]</MatButton>
@if (!string.IsNullOrEmpty(FromRoute)) {
<div class="row no-gutters justify-content-end w-100">
<MatRipple class="inputfile-mat-ripple" Color="@MatRippleColor.Default" @onclick="SaveUserData" Style="background: lightgrey; width: 64px; height: 64px; border-radius: 32px; align-items: flex-end; justify-content: center; display: inline-flex;">
<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="M8,8.5c0.83,0,1.5-0.67,1.5-1.5S8.83,5.5,8,5.5S6.5,6.17,6.5,7S7.17,8.5,8,8.5z M12.28,13.66 C10.98,12.9,9.5,12.5,8,12.5s-2.98,0.4-4.28,1.16c-0.14,0.08-0.22,0.25-0.22,0.42v0.41h9v-0.41 C12.5,13.91,12.41,13.74,12.28,13.66z" opacity=".3" />
<path d="M8,10c1.66,0,3-1.34,3-3S9.66,4,8,4S5,5.34,5,7S6.34,10,8,10z M8,5.5c0.83,0,1.5,0.67,1.5,1.5S8.83,8.5,8,8.5 S6.5,7.83,6.5,7S7.17,5.5,8,5.5z M13.03,12.37C11.56,11.5,9.84,11,8,11s-3.56,0.5-5.03,1.37C2.36,12.72,2,13.39,2,14.09V16h12 v-1.91C14,13.39,13.64,12.72,13.03,12.37z M12.5,14.5h-9v-0.41c0-0.18,0.09-0.34,0.22-0.42C5.02,12.9,6.5,12.5,8,12.5 s2.98,0.4,4.28,1.16c0.14,0.08,0.22,0.25,0.22,0.42V14.5z" />
</g>
<polygon points="18,7.75 16.25,7.75 16.25,6 14.75,6 14.75,7.75 13,7.75 13,9.25 14.75,9.25 14.75,11 16.25,11 16.25,9.25 18,9.25" />
</g>
</svg>
</label>
</MatRipple>
</div> </div>
} else {
<div class="row no-gutters align-items-end justify-content-center w-100" style="padding-bottom:2em"> <div class="row no-gutters align-items-end justify-content-center w-100" style="padding-bottom:2em">
<div class="col" style="padding-right:0.5em"> <div class="col" style="padding-right:0.5em">
<MatButton Class="w-100" Outlined="true" @onclick="Cancel">@i18n["Cancel"]</MatButton> <MatButton Class="w-100" Outlined="true" @onclick="Cancel">@i18n["Cancel"]</MatButton>
<MatButton Class="w-100" Raised="true" @onclick="Next">@i18n["Send"]</MatButton> <MatButton Class="w-100" Raised="true" @onclick="Next">@i18n["Send"]</MatButton>
</div> </div>
</div> </div>
} else {
<div class="row no-gutters align-items-end justify-content-center w-100" style="padding-bottom:2em">
<MatButton Class="w-100" Raised="true" @onclick="SaveUserDataAndClose">@i18n["Save"]</MatButton>
</div>
} }
</div> </div>
PageHistoryManager.OnBeforeNavigateBack = new EventCallback(this, (Action)OnBeforeNavigateBack); PageHistoryManager.OnBeforeNavigateBack = new EventCallback(this, (Action)OnBeforeNavigateBack);
PageHistoryManager.AddPageToHistory(NavigationManager.Uri); PageHistoryManager.AddPageToHistory(NavigationManager.Uri);
Account = await GetUserData(); Account = await GetUserData();
if (ReportDataProvider.Report != null) {
if (!string.IsNullOrEmpty(FromRoute) && ReportDataProvider.Report != null) {
UserDataProvider.mapUserData(Account, ReportDataProvider.Report); UserDataProvider.mapUserData(Account, ReportDataProvider.Report);
} }
StateHasChanged(); StateHasChanged();
} }
} }
private async void SaveUserData() {
private async Task SaveUserData() {
await UserDataProvider.Save(Account); await UserDataProvider.Save(Account);
}
private async void SaveUserDataAndClose() {
await SaveUserData();
NavigationManager.NavigateTo("caritas_services"); NavigationManager.NavigateTo("caritas_services");
} }
private async Task<UserData> GetUserData() { private async Task<UserData> GetUserData() {
return await UserDataProvider.Get(); return await UserDataProvider.Get();
} }

+ 1
- 0
Pages/CaritasServiceFundVeloFoundConclusion.razor 查看文件

Toaster.ShowError(response.Message, response.GetDataAsFormattedList()); Toaster.ShowError(response.Message, response.GetDataAsFormattedList());
} else { } else {
Toaster.ShowWarning(i18n.GetString("Warning.NoConnection.Title"), i18n.GetString("Warning.NoConnection.Msg")); Toaster.ShowWarning(i18n.GetString("Warning.NoConnection.Title"), i18n.GetString("Warning.NoConnection.Msg"));
PageHistoryManager.Reset();
} }
StateHasChanged(); StateHasChanged();
AppState.NotifyChanged(); AppState.NotifyChanged();

+ 1
- 0
Pages/CaritasServiceFundVeloLostFoundPage.razor 查看文件

protected override void OnInitialized() { protected override void OnInitialized() {
PageHistoryManager.AddPageToHistory(NavigationManager.Uri); PageHistoryManager.AddPageToHistory(NavigationManager.Uri);
ReportDataProvider.Report = null; ReportDataProvider.Report = null;
ReportDataProvider.ReportRepositoryItem = null;
base.OnInitialized(); base.OnInitialized();
} }

+ 15
- 6
Pages/CaritasServiceFundVeloMissingConclusion.razor 查看文件

<h6 style="font-style:italic;padding-bottom:1em">@i18n["Info.Report.Transmitting"]</h6> <h6 style="font-style:italic;padding-bottom:1em">@i18n["Info.Report.Transmitting"]</h6>
</div> </div>
} else { } else {
if (responseOk) {
if (ResposeStatus.OK == responseStatus) {
<MatHeadline5 Style="font-family:Ubuntu; text-align:center">@i18n["FinishedTextMissing"]</MatHeadline5> <MatHeadline5 Style="font-family:Ubuntu; text-align:center">@i18n["FinishedTextMissing"]</MatHeadline5>
<MatSubtitle1 Class="w-100" Style="font-family:Ubuntu; text-align:left; font-weight:800">@i18n["FinishedTextMissing_Heading1"]</MatSubtitle1> <MatSubtitle1 Class="w-100" Style="font-family:Ubuntu; text-align:left; font-weight:800">@i18n["FinishedTextMissing_Heading1"]</MatSubtitle1>
<ul class="w-100"> <ul class="w-100">
@code { @code {
private bool responseOk = false;
enum ResposeStatus : ushort {
OK = 1,
Error = 10,
NoConnection = 20
}
private ResposeStatus responseStatus = ResposeStatus.NoConnection;
private bool running = true; private bool running = true;
ReportResponse response; ReportResponse response;
PageHistoryManager.AddPageToHistory(NavigationManager.Uri); PageHistoryManager.AddPageToHistory(NavigationManager.Uri);
try { try {
response = await IBicycleRestService.SendMissingReport(ReportDataProvider.Report); response = await IBicycleRestService.SendMissingReport(ReportDataProvider.Report);
responseOk = System.Net.HttpStatusCode.OK == response.StatusCode ? true : false;
responseStatus = response == null ? ResposeStatus.NoConnection : System.Net.HttpStatusCode.OK == response.StatusCode ? ResposeStatus.OK : ResposeStatus.Error;
} catch (HttpRequestException ex) { } catch (HttpRequestException ex) {
response = new(i18n.GetString("MissingBike"), new string[] { ex.Message }); response = new(i18n.GetString("MissingBike"), new string[] { ex.Message });
responseOk = false;
responseStatus = ResposeStatus.Error;
} }
running = false; running = false;
if (responseOk) {
if (ResposeStatus.OK == responseStatus) {
Toaster.ShowSuccess(i18n.GetString("MissingBike"), response.Message); Toaster.ShowSuccess(i18n.GetString("MissingBike"), response.Message);
PageHistoryManager.Reset(); PageHistoryManager.Reset();
} else {
} else if (ResposeStatus.Error == responseStatus) {
Toaster.ShowError(response.Message, response.GetDataAsFormattedList()); Toaster.ShowError(response.Message, response.GetDataAsFormattedList());
} else {
Toaster.ShowWarning(i18n.GetString("Warning.NoConnection.Title"), i18n.GetString("Warning.NoConnection.Msg"));
PageHistoryManager.Reset();
} }
StateHasChanged(); StateHasChanged();
AppState.NotifyChanged(); AppState.NotifyChanged();

+ 3
- 6
Pages/IndexPage.razor 查看文件

@inject MasterDataService MasterDataService; @inject MasterDataService MasterDataService;
@inject Toaster Toaster; @inject Toaster Toaster;
@inject IConfiguration Configuration; @inject IConfiguration Configuration;
@inject IBicycleRestService IBicycleRestService;
@inject OnlineStatusProvider OnlineStatusProvider;
@inject IBicycleRestService BicycleRestService;
<div class="row h-100 justify-content-center"> <div class="row h-100 justify-content-center">
<div class="row align-items-end vw-100 h-50"> <div class="row align-items-end vw-100 h-50">
<div class="col text-center"> <div class="col text-center">
<h3 style="font-style:italic;padding-bottom:1em">@i18n["Welcome"]</h3> <h3 style="font-style:italic;padding-bottom:1em">@i18n["Welcome"]</h3>
@*<div>
<MatButton Disabled="@btnDisabled" Raised="true" Style="width:50%" @onclick="@((e) => NavigateToNext())">@i18n["Login"]</MatButton>
</div>*@
</div> </div>
</div> </div>
<div class="row align-items-center justify-content-center vw-100 h-25"> <div class="row align-items-center justify-content-center vw-100 h-25">
showProgressCircle = true; showProgressCircle = true;
StateHasChanged(); StateHasChanged();
try { try {
IBicycleRestService.Initialize(Configuration, OnlineStatusProvider);
BicycleRestService.Initialize(Configuration);
await MasterDataService.SynchronizeMasterdata(); await MasterDataService.SynchronizeMasterdata();
await BicycleRestService.TrySendPendingReports();
} catch (Exception) { } catch (Exception) {
Toaster.ShowWarning(i18n.GetString("Warning.Masterdata.Title"), i18n.GetString("Warning.Masterdata.Msg")); Toaster.ShowWarning(i18n.GetString("Warning.Masterdata.Title"), i18n.GetString("Warning.Masterdata.Msg"));
} finally { } finally {

+ 1
- 0
Program.cs 查看文件

builder.Services.AddSingleton<PageHistoryManager>(); builder.Services.AddSingleton<PageHistoryManager>();
builder.Services.AddSingleton<ReportDataProvider>(); builder.Services.AddSingleton<ReportDataProvider>();
builder.Services.AddSingleton<OnlineStatusProvider>(); builder.Services.AddSingleton<OnlineStatusProvider>();
builder.Services.AddSingleton<ReportRepositoryService>();
builder.Services.AddScoped<Toaster>(); builder.Services.AddScoped<Toaster>();
builder.Services.AddScoped<UserDataProvider>(); builder.Services.AddScoped<UserDataProvider>();
builder.Services.AddScoped<MasterDataService>(); builder.Services.AddScoped<MasterDataService>();

+ 11
- 0
Shared/Models/FoundReportRepositoryItem.cs 查看文件

namespace cwebplusApp.Shared.Models {
public class FoundReportRepositoryItem : ReportRepositoryItem {
private FoundReport report;
public FoundReport Report { get => report; set => report = value; }
public FoundReportRepositoryItem(FoundReport _report, long identifier) : base(Type.FOUND, identifier) {
this.report = _report;
}
}
}

+ 11
- 0
Shared/Models/MissingReportRepositoryItem.cs 查看文件

namespace cwebplusApp.Shared.Models {
public class MissingReportRepositoryItem : ReportRepositoryItem {
private MissingReport report;
public MissingReport Report { get => report; set => report = value; }
public MissingReportRepositoryItem(MissingReport _report, long identifier) : base(Type.MISSING, identifier) {
this.report = _report;
}
}
}

+ 33
- 0
Shared/Models/ReportRepositoryItem.cs 查看文件

using System;
namespace cwebplusApp.Shared.Models {
public abstract class ReportRepositoryItem {
public enum Type { FOUND, MISSING }
private long id;
private string serverRefNbr;
private bool transmitted;
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 Type ReportType { get => reportType; set => reportType = value; }
public ReportRepositoryItem(Type _reportType, long identifier) {
this.id = identifier;
this.reportType = _reportType;
this.transmitted = false;
}
public override bool Equals(Object obj) {
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
return false;
} else {
ReportRepositoryItem rri = (ReportRepositoryItem)obj;
return (ID == rri.ID);
}
}
}
}

+ 21
- 13
Shared/NavMenu.razor 查看文件

private void LocationChanged(object sender, LocationChangedEventArgs e) { private void LocationChanged(object sender, LocationChangedEventArgs e) {
locUrl = i18n.GetString(e.Location.Replace(NavigationManager.BaseUri, "")); locUrl = i18n.GetString(e.Location.Replace(NavigationManager.BaseUri, ""));
if (IsInServicesUrl(e)) {
if (IsInServicesUrl(e.Location)) {
Index = 1; Index = 1;
} else if (e.Location.Contains("account")) { } else if (e.Location.Contains("account")) {
Index = 2; Index = 2;
} else if (e.Location.Contains("extras")) {
Index = 3;
} else if (e.Location.Contains("info")) { } else if (e.Location.Contains("info")) {
Index = 4; Index = 4;
} else { } else {
Index = 3;
Index = 0;
} }
StateHasChanged(); StateHasChanged();
} }
private bool IsInServicesUrl(LocationChangedEventArgs e) {
return (e.Location.Contains("caritas_services") || e.Location.Contains("lost_found") || e.Location.Contains("keydata")
|| e.Location.Contains("account/") || e.Location.Contains("conclusion_"));
private bool IsInServicesUrl(string location) {
return (location.Contains("caritas_services") || location.Contains("lost_found") || location.Contains("keydata")
|| location.Contains("account/") || location.Contains("conclusion_"));
} }
private bool HandleAppBarContainer() { private bool HandleAppBarContainer() {
string baseUri = NavigationManager.BaseUri; string baseUri = NavigationManager.BaseUri;
string delta = uri.Replace(baseUri, ""); string delta = uri.Replace(baseUri, "");
if (delta.Equals("caritas_services")) {
Index = 1;
} else if (delta.Equals("account")) {
Index = 2;
} else if (delta.Equals("info")) {
Index = 4;
if (delta == null || delta.Equals("")) {
return false;
} else { } else {
Index = 3;
if (IsInServicesUrl(delta)) {
Index = 1;
} else if (delta.Equals("account")) {
Index = 2;
} else if (delta.Equals("extras")) {
Index = 3;
} else if (delta.Equals("info")) {
Index = 4;
} else {
Index = 0;
}
return true;
} }
return true;
} }
private bool BackButtonDisabled() { private bool BackButtonDisabled() {

+ 75
- 19
Shared/Services/BicycleRestService.cs 查看文件

using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
private static readonly string VERSION = "v1"; private static readonly string VERSION = "v1";
private HttpClient httpClient; private HttpClient httpClient;
public IConfiguration Configuration { get; set; }
public OnlineStatusProvider OnlineStatusProvider { get; set; }
private IConfiguration configuration;
private OnlineStatusProvider onlineStatusProvider;
private ReportRepositoryService reportRepositoryService;
private ReportDataProvider reportDataProvider;
public void Initialize(IConfiguration configuration, OnlineStatusProvider onlineStatusProvider) {
this.Configuration = configuration;
this.OnlineStatusProvider = onlineStatusProvider;
string hostBaseUrl = Configuration.GetValue<string>("host_base_url");
public BicycleRestService(ReportRepositoryService _reportRepositoryService, OnlineStatusProvider _onlineStatusProvider,
ReportDataProvider _reportDataProvider) {
this.reportRepositoryService = _reportRepositoryService;
this.onlineStatusProvider = _onlineStatusProvider;
this.reportDataProvider = _reportDataProvider;
}
public void Initialize(IConfiguration _configuration) {
this.configuration = _configuration;
string hostBaseUrl = configuration.GetValue<string>("host_base_url");
if (!String.IsNullOrEmpty(hostBaseUrl)) { if (!String.IsNullOrEmpty(hostBaseUrl)) {
this.httpClient = new HttpClient { BaseAddress = new Uri(hostBaseUrl) }; this.httpClient = new HttpClient { BaseAddress = new Uri(hostBaseUrl) };
} }
public async Task<List<ColorItem>> GetColors() { public async Task<List<ColorItem>> GetColors() {
if (httpClient != null) { if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_colors");
string subResourceUrl = configuration.GetValue<string>("subresource_url_colors");
if (!String.IsNullOrEmpty(subResourceUrl)) { if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName)); HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
public async Task<List<BicycleType>> GetBicycleTypes() { public async Task<List<BicycleType>> GetBicycleTypes() {
if (httpClient != null) { if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_types");
string subResourceUrl = configuration.GetValue<string>("subresource_url_types");
if (!String.IsNullOrEmpty(subResourceUrl)) { if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName)); HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
public async Task<List<Brand>> GetBrands() { public async Task<List<Brand>> GetBrands() {
if (httpClient != null) { if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_brands");
string subResourceUrl = configuration.GetValue<string>("subresource_url_brands");
if (!String.IsNullOrEmpty(subResourceUrl)) { if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName)); HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
} }
public async Task<ReportResponse> SendFoundReport(Report report) { public async Task<ReportResponse> SendFoundReport(Report report) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_foundreport");
return await SendReport(report, subResourceUrl);
string subResourceUrl = configuration.GetValue<string>("subresource_url_foundreport");
return await SendReport(report, subResourceUrl, new FoundReportRepositoryItem((FoundReport)report, GetCurrentTimeInMillis()));
}
private async Task<ReportResponse> SendFoundReport(FoundReportRepositoryItem reportRepositoryItem) {
string subResourceUrl = configuration.GetValue<string>("subresource_url_foundreport");
return await SendReport(reportRepositoryItem.Report, subResourceUrl, reportRepositoryItem);
} }
public async Task<ReportResponse> SendMissingReport(Report report) { public async Task<ReportResponse> SendMissingReport(Report report) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_missingreport");
return await SendReport(report, subResourceUrl);
string subResourceUrl = configuration.GetValue<string>("subresource_url_missingreport");
return await SendReport(report, subResourceUrl, new MissingReportRepositoryItem((MissingReport)report, GetCurrentTimeInMillis()));
} }
protected async Task<ReportResponse> SendReport(Report report, string subResourceUrl) {
if (OnlineStatusProvider.Online) {
public async Task<ReportResponse> SendMissingReport(MissingReportRepositoryItem reportRepositoryItem) {
string subResourceUrl = configuration.GetValue<string>("subresource_url_missingreport");
return await SendReport(reportRepositoryItem.Report, subResourceUrl, reportRepositoryItem);
}
public async Task TrySendPendingReports() {
await TrySendFoundPendingReports();
await TrySendMissingPendingReports();
}
public async Task TrySendFoundPendingReports() {
List<FoundReportRepositoryItem> pendingFoundReports = await reportRepositoryService.GetPendingFoundReports();
foreach (FoundReportRepositoryItem item in pendingFoundReports) {
try {
ReportResponse response = await SendFoundReport(item);
} catch (Exception) {
continue;
}
}
}
public async Task TrySendMissingPendingReports() {
List<MissingReportRepositoryItem> pendingMissingReports = await reportRepositoryService.GetPendingMissingReports();
foreach (MissingReportRepositoryItem item in pendingMissingReports) {
try {
ReportResponse response = await SendMissingReport(item);
} catch (Exception) {
continue;
}
}
}
protected async Task<ReportResponse> SendReport(Report report, string subResourceUrl, ReportRepositoryItem reportRepositoryItem) {
ReportResponse response = null;
if (onlineStatusProvider.Online && !reportRepositoryItem.Transmitted) {
if (httpClient != null) { if (httpClient != null) {
if (!String.IsNullOrEmpty(subResourceUrl)) { if (!String.IsNullOrEmpty(subResourceUrl)) {
string reportJson = JsonConvert.SerializeObject(report); string reportJson = JsonConvert.SerializeObject(report);
HttpContent content = new StringContent(reportJson, Encoding.UTF8, "application/json"); HttpContent content = new StringContent(reportJson, Encoding.UTF8, "application/json");
HttpResponseMessage httpResult = await httpClient.PostAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName), content); HttpResponseMessage httpResult = await httpClient.PostAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName), content);
string msg = await httpResult.Content.ReadAsStringAsync(); string msg = await httpResult.Content.ReadAsStringAsync();
ReportResponse response = JsonConvert.DeserializeObject<ReportResponse>(msg);
response = JsonConvert.DeserializeObject<ReportResponse>(msg);
response.StatusCode = httpResult.StatusCode; response.StatusCode = httpResult.StatusCode;
if (HttpStatusCode.OK == response.StatusCode) {
reportRepositoryItem.ServerRefNbr = response.Data[0];
reportRepositoryItem.Transmitted = true;
await reportRepositoryService.SaveReport(reportRepositoryItem);
}
return response; return response;
} }
} }
throw new HttpRequestException("HTTP client not initialized!"); throw new HttpRequestException("HTTP client not initialized!");
} else {
//TODO: Save to app storage
return null;
} }
await reportRepositoryService.SaveReport(reportRepositoryItem);
return response;
}
private long GetCurrentTimeInMillis() {
return DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
} }
} }
} }

+ 7
- 1
Shared/Services/IBicycleRestService.cs 查看文件

namespace cwebplusApp.Shared.Services { namespace cwebplusApp.Shared.Services {
public interface IBicycleRestService { public interface IBicycleRestService {
void Initialize(IConfiguration configuration, OnlineStatusProvider onlineStatusProvider);
void Initialize(IConfiguration configuration);
Task<List<ColorItem>> GetColors(); Task<List<ColorItem>> GetColors();
Task<ReportResponse> SendFoundReport(Report report); Task<ReportResponse> SendFoundReport(Report report);
Task<ReportResponse> SendMissingReport(Report report); Task<ReportResponse> SendMissingReport(Report report);
Task TrySendPendingReports();
Task TrySendFoundPendingReports();
Task TrySendMissingPendingReports();
} }
} }

+ 0
- 21
Shared/Services/ILFBicycleRest.cs 查看文件

using cwebplusApp.Shared.Models;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace cwebplusApp.Shared.Services {
public interface ILFBicycleRest {
void Initialize(IConfiguration configuration);
Task<List<ColorItem>> GetColors();
Task<List<BicycleType>> GetBicycleTypes();
Task<List<Brand>> GetBrands();
Task<ReportResponse> SendFoundReport(Report report);
Task<ReportResponse> SendMissingReport(Report report);
}
}

+ 0
- 104
Shared/Services/LFBicycleRest.cs 查看文件

using cwebplusApp.Shared.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace cwebplusApp.Shared.Services {
// REST interface responsible to submit lost or found reports and get the available master data.
public class LFBicycleRest : ILFBicycleRest {
private static readonly string VERSION = "v1";
private HttpClient httpClient;
[Inject]
public IConfiguration Configuration { get; set; }
public void Initialize(IConfiguration configuration) {
this.Configuration = configuration;
string hostBaseUrl = Configuration.GetValue<string>("host_base_url");
if (!String.IsNullOrEmpty(hostBaseUrl)) {
this.httpClient = new HttpClient { BaseAddress = new Uri(hostBaseUrl) };
}
}
public async Task<List<ColorItem>> GetColors() {
if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_colors");
if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
if (httpResult.StatusCode == System.Net.HttpStatusCode.OK) {
ColorItem[] colors = JsonConvert.DeserializeObject<ColorItem[]>(await httpResult.Content.ReadAsStringAsync());
return new List<ColorItem>(colors);
}
throw new HttpRequestException("HTTP error " + httpResult.StatusCode);
}
}
throw new HttpRequestException("HTTP client not initialized!");
}
public async Task<List<BicycleType>> GetBicycleTypes() {
if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_types");
if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
if (httpResult.StatusCode == System.Net.HttpStatusCode.OK) {
BicycleType[] bicycleTypes = JsonConvert.DeserializeObject<BicycleType[]>(await httpResult.Content.ReadAsStringAsync());
return new List<BicycleType>(bicycleTypes);
}
throw new HttpRequestException("HTTP error " + httpResult.StatusCode);
}
}
throw new HttpRequestException("HTTP client not initialized!");
}
public async Task<List<Brand>> GetBrands() {
if (httpClient != null) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_brands");
if (!String.IsNullOrEmpty(subResourceUrl)) {
HttpResponseMessage httpResult = await httpClient.GetAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName));
if (httpResult.StatusCode == System.Net.HttpStatusCode.OK) {
Brand[] brands = JsonConvert.DeserializeObject<Brand[]>(await httpResult.Content.ReadAsStringAsync());
return new List<Brand>(brands);
}
throw new HttpRequestException("HTTP error " + httpResult.StatusCode);
}
}
throw new HttpRequestException("HTTP client not initialized!");
}
public async Task<ReportResponse> SendFoundReport(Report report) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_foundreport");
return await SendReport(report, subResourceUrl);
}
public async Task<ReportResponse> SendMissingReport(Report report) {
string subResourceUrl = Configuration.GetValue<string>("subresource_url_missingreport");
return await SendReport(report, subResourceUrl);
}
protected async Task<ReportResponse> SendReport(Report report, string subResourceUrl) {
if (httpClient != null) {
if (!String.IsNullOrEmpty(subResourceUrl)) {
string reportJson = JsonConvert.SerializeObject(report);
HttpContent content = new StringContent(reportJson, Encoding.UTF8, "application/json");
HttpResponseMessage httpResult = await httpClient.PostAsync(string.Format(subResourceUrl, VERSION, CultureInfo.CurrentCulture.TwoLetterISOLanguageName), content);
string msg = await httpResult.Content.ReadAsStringAsync();
ReportResponse response = JsonConvert.DeserializeObject<ReportResponse>(msg);
response.StatusCode = httpResult.StatusCode;
return response;
}
}
throw new HttpRequestException("HTTP client not initialized!");
}
}
}

+ 3
- 0
Shared/Services/MasterDataService.cs 查看文件

} else if (key == KeyNameBcTypes) { } else if (key == KeyNameBcTypes) {
_bicycleTypes = null; _bicycleTypes = null;
Changed?.Invoke(this, EventArgs.Empty); Changed?.Invoke(this, EventArgs.Empty);
} else if (key == KeyNameBrands) {
_brands = null;
Changed?.Invoke(this, EventArgs.Empty);
} }
} }

+ 1
- 0
Shared/Services/ReportDataProvider.cs 查看文件

public class ReportDataProvider { public class ReportDataProvider {
public Report Report { get; set; } public Report Report { get; set; }
public ReportRepositoryItem ReportRepositoryItem { get; set; }
public FoundReport GetFoundReport() { public FoundReport GetFoundReport() {

+ 119
- 0
Shared/Services/ReportRepositoryService.cs 查看文件

using cwebplusApp.Shared.Models;
using Microsoft.JSInterop;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace cwebplusApp.Shared.Services {
public sealed class ReportRepositoryService {
private const string KeyNameFoundReports = "foundReportRepository";
private const string KeyNameMissingReports = "missingReportRepository";
private readonly IJSRuntime jsRuntime;
private bool initialized;
public event EventHandler Changed;
public ReportRepositoryService(IJSRuntime jsRuntime) {
this.jsRuntime = jsRuntime;
}
public async Task SaveReport(ReportRepositoryItem reportRepositoryItem) {
if (reportRepositoryItem is FoundReportRepositoryItem fItem) {
await SaveFoundReport(fItem);
} else if (reportRepositoryItem is MissingReportRepositoryItem mItem) {
await SaveMissingReport(mItem);
}
}
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;
}
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;
}
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<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 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 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);
}
// 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);
}
}
}
}

+ 1
- 3
Shared/Services/UserDataProvider.cs 查看文件

namespace cwebplusApp.Shared.Services { namespace cwebplusApp.Shared.Services {
public sealed class UserDataProvider { public sealed class UserDataProvider {
private const string KeyName = "account";
private const string KeyName = "account";
private readonly IJSRuntime _jsRuntime; private readonly IJSRuntime _jsRuntime;
private bool _initialized; private bool _initialized;
public event EventHandler Changed; public event EventHandler Changed;
public bool AutoSave { get; set; } = true;
public UserDataProvider(IJSRuntime jsRuntime) { public UserDataProvider(IJSRuntime jsRuntime) {
_jsRuntime = jsRuntime; _jsRuntime = jsRuntime;
} }

+ 3
- 3
cwebplusApp.csproj 查看文件

<PackageReference Include="BlazorGeolocation" Version="0.1.1" /> <PackageReference Include="BlazorGeolocation" Version="0.1.1" />
<PackageReference Include="FisSst.BlazorMaps" Version="1.0.2" /> <PackageReference Include="FisSst.BlazorMaps" Version="1.0.2" />
<PackageReference Include="MatBlazor" Version="2.9.0-develop-042" /> <PackageReference Include="MatBlazor" Version="2.9.0-develop-042" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.9" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.9" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
</ItemGroup> </ItemGroup>

+ 34
- 17
wwwroot/service-worker.js 查看文件

'/conclusion_missing', '/conclusion_missing',
'/doneimage', '/doneimage',
'/failureimage', '/failureimage',
'/warningimage',
'favicon.ico', 'favicon.ico',
'images/batch_found.png', 'images/batch_found.png',
'images/batch_fundvelo.png', 'images/batch_fundvelo.png',
'_content/blazoranimate/aos.css', '_content/blazoranimate/aos.css',
'_framework/blazor.webassembly.js', '_framework/blazor.webassembly.js',
'_framework/blazor.boot.json', '_framework/blazor.boot.json',
'_framework/dotnet.5.0.8.js',
'_framework/dotnet.5.0.9.js',
'https://fonts.googleapis.com/css?family=Roboto:300,400,500', 'https://fonts.googleapis.com/css?family=Roboto:300,400,500',
'https://fonts.googleapis.com/icon?family=Material+Icons', 'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap', 'https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap',
}); });
// fetch events (appsettings are always first fetched from network) // fetch events (appsettings are always first fetched from network)
self.addEventListener('fetch', function (event) {
event.respondWith(networkOrCache(event.request).catch(function () {
}));
})
self.addEventListener('fetch', event => {
if (event.request.url.endsWith(appsettings_url)) {
fetch(event.request).then(function (response) {
return caches.open(staticCacheName).then(function (cache) {
console.log('update cache');
cache.put(event.request, response.clone());
return response;
});
}).catch(function () {
return useFallback();
});
} else {
event.respondWith(
caches.match(event.request).then(cacheRes => {
return cacheRes || fetch(event.request);
})
);
}
});
function networkOrCache(request) {
return fetch(request).then(function (response) {
return response.ok ? response : fromCache(request);
}).catch(function () {
return fromCache(request);
});
function useFallback() {
return Promise.resolve(new Response(Appsettings_Fallback, {
headers: {
'Content-Type': 'application/json'
}
}));
} }
function fromCache(request) {
return caches.open(staticCacheName).then(function (cache) {
return cache.match(request).then(function (matching) {
return matching || Promise.reject('request-not-in-cache');
});
});
var Appsettings_Fallback = {
"host_base_url": "https://integrate.dynalias.net:9443/Fundvelo/",
"subresource_url_colors": "api/{0}/{1}/fundvelo/colors",
"subresource_url_brands": "api/{0}/{1}/fundvelo/brands",
"subresource_url_types": "api/{0}/{1}/fundvelo/types",
"subresource_url_foundreport": "api/{0}/{1}/fundvelo/fundmeldung",
"subresource_url_missingreport": "api/{0}/{1}/fundvelo/suchauftrag"
} }

Loading…
取消
儲存