mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-19 11:37:28 +02:00
Version 3.0.369
This commit is contained in:
parent
1a58bf58d3
commit
f5cf9bb22f
70 changed files with 1130 additions and 773 deletions
|
@ -1,4 +1,3 @@
|
|||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -43,6 +42,47 @@ namespace TINK.ViewModel.Account
|
|||
/// <summary> Reference on the tink app instance. </summary>
|
||||
private ITinkApp TinkApp { get; }
|
||||
|
||||
/// <summary> Used to block more than on copri requests at a given time.</summary>
|
||||
private bool isIdle = true;
|
||||
|
||||
/// <summary>
|
||||
/// True if any action can be performed (logout etc.)
|
||||
/// </summary>
|
||||
public virtual bool IsIdle
|
||||
{
|
||||
get => isIdle;
|
||||
set
|
||||
{
|
||||
if (value == isIdle)
|
||||
return;
|
||||
|
||||
Log.ForContext<AccountPageViewModel>().Debug($"Switch value of {nameof(IsIdle)} to {value}.");
|
||||
isIdle = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIdle)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsProcessWithRunningProcessView)));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProcessWithRunningProcessView => !isIdle;
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
private string statusInfoText;
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
public string StatusInfoText
|
||||
{
|
||||
get => statusInfoText;
|
||||
set
|
||||
{
|
||||
if (value == statusInfoText)
|
||||
return;
|
||||
|
||||
Log.ForContext<LoginPageViewModel>().Debug($"Switch value of {nameof(StatusInfoText)} to {value}.");
|
||||
statusInfoText = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Constructs a settings page view model object.</summary>
|
||||
/// <param name="tinkApp"> Reference to tink app model.</param>
|
||||
/// <param name="p_oUser"></param>
|
||||
|
@ -261,6 +301,8 @@ namespace TINK.ViewModel.Account
|
|||
/// </summary>
|
||||
public async Task Logout()
|
||||
{
|
||||
StatusInfoText = AppResources.ActivityTextOneMomentPlease;
|
||||
IsIdle = false;
|
||||
try
|
||||
{
|
||||
// Backup logout message before logout.
|
||||
|
@ -284,6 +326,9 @@ namespace TINK.ViewModel.Account
|
|||
|
||||
// Restart polling again.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
|
@ -303,6 +348,9 @@ namespace TINK.ViewModel.Account
|
|||
|
||||
// Restart polling again.
|
||||
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -314,8 +362,12 @@ namespace TINK.ViewModel.Account
|
|||
catch (Exception p_oException)
|
||||
{
|
||||
Log.Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Switch to map view after log out.
|
||||
|
@ -328,8 +380,14 @@ namespace TINK.ViewModel.Account
|
|||
catch (Exception p_oException)
|
||||
{
|
||||
Log.Error("An unexpected error occurred switching back to map page after displaying log out page. {@Exception}", p_oException);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -63,14 +63,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await DoBookOrClose();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
public async Task<IRequestHandler> DoBookOrClose()
|
||||
public async Task<IRequestHandler> CloseLockOrDoBook()
|
||||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
SelectedStation = selectedStation;
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedStationId)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedStationName)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MailAddressText)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OfficeHoursText)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PhoneNumberText)));
|
||||
|
@ -132,32 +134,14 @@ namespace TINK.ViewModel.Info
|
|||
return;
|
||||
}
|
||||
|
||||
// Ask whether mail is operator or app specific.
|
||||
var operatorRelatedMail = await ViewService.DisplayAlert(
|
||||
AppResources.QuestionTitle,
|
||||
string.Format(AppResources.QuestionSupportmailSubject, AppFlavorName),
|
||||
string.Format(AppResources.QuestionSupportmailAnswerOperator, AppFlavorName),
|
||||
string.Format(AppResources.QuestionSupportmailAnswerApp, AppFlavorName));
|
||||
|
||||
if (operatorRelatedMail)
|
||||
{
|
||||
// Send operator related support mail to operator.
|
||||
await Email.ComposeAsync(new EmailMessage
|
||||
{
|
||||
To = new List<string> { MailAddressText },
|
||||
Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Send app-related support mail to operator.
|
||||
// Send operator related support mail to operator.
|
||||
await Email.ComposeAsync(new EmailMessage
|
||||
{
|
||||
To = new List<string> { MailAddressText },
|
||||
Cc = APPSUPPORTMAILADDRESS.ToUpper() != MailAddressText.ToUpper() // do not sent copy if same mail address
|
||||
&& MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad
|
||||
? new List<string> { APPSUPPORTMAILADDRESS } : new List<string>(),
|
||||
Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
|
||||
&& MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad
|
||||
? new List<string> { APPSUPPORTMAILADDRESS } : new List<string>(),
|
||||
Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id)
|
||||
});
|
||||
|
||||
return;
|
||||
|
@ -180,12 +164,10 @@ namespace TINK.ViewModel.Info
|
|||
try
|
||||
{
|
||||
// Ask for permission to append diagnostics.
|
||||
var appendFile = false;
|
||||
appendFile = await ViewService.DisplayAlert(
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.QuestionSupportmailTitle,
|
||||
AppResources.QuestionSupportmailAttachment,
|
||||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionSupportmailAnswerNo);
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
var message = new EmailMessage
|
||||
{
|
||||
|
@ -193,13 +175,6 @@ namespace TINK.ViewModel.Info
|
|||
Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName)
|
||||
};
|
||||
|
||||
if (appendFile == false)
|
||||
{
|
||||
// Send without attachment
|
||||
await Email.ComposeAsync(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send with attachment.
|
||||
var logFileName = string.Empty;
|
||||
try
|
||||
|
@ -299,13 +274,18 @@ namespace TINK.ViewModel.Info
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Text providing the id of the selected station.</summary>
|
||||
public string SelectedStationId
|
||||
=> SelectedStation?.Id;
|
||||
|
||||
public string SelectedStationName
|
||||
=> SelectedStation?.StationName;
|
||||
|
||||
/// <summary> Text providing mail address and possilbe reasons to contact. </summary>
|
||||
/// <summary> Text providing the name of the operator of the selected station </summary>
|
||||
public string ProviderNameText
|
||||
=> string.Format("Betreiber: {0}", SelectedStation?.OperatorData?.Name)
|
||||
;
|
||||
/// <summary> Text providing mail address and possilbe reasons to contact. </summary>
|
||||
=> SelectedStation?.OperatorData?.Name;
|
||||
|
||||
/// <summary> Text providing mail address and possible reasons to contact. </summary>
|
||||
public FormattedString MailAddressAndMotivationsText
|
||||
{
|
||||
get
|
||||
|
@ -316,35 +296,6 @@ namespace TINK.ViewModel.Info
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary> Invitation to rate app.</summary>
|
||||
public FormattedString LikeTinkApp
|
||||
{
|
||||
get
|
||||
{
|
||||
var hint = new FormattedString();
|
||||
hint.Spans.Add(new Span { Text = string.Format(AppResources.MessageRateMail, AppFlavorName) });
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> User clicks rate button.</summary>
|
||||
public ICommand OnRateRequest
|
||||
=> new Command(() => RegisterRequest());
|
||||
|
||||
/// <summary> Opens login page.</summary>
|
||||
public void RegisterRequest()
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenUrlInExternalBrowser();
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
{
|
||||
Log.Error("Ein unerwarteter Fehler ist auf der Login Seite beim Öffnen eines Browsers, Seite {url}, aufgetreten. {@Exception}", p_oException);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Invitation to rate app.</summary>
|
||||
public FormattedString PhoneContactText
|
||||
{
|
||||
|
|
|
@ -22,6 +22,9 @@ using TINK.MultilingualResources;
|
|||
using TINK.Repository;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.ViewModel.Map;
|
||||
using TINK.Model.Stations.StationNS;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BC;
|
||||
|
||||
namespace TINK.ViewModel.Contact
|
||||
{
|
||||
|
@ -395,6 +398,7 @@ namespace TINK.ViewModel.Contact
|
|||
|
||||
var colors = GetStationColors(
|
||||
Pins.Select(x => x.Tag.ToString()).ToList(),
|
||||
resultStationsAndBikes.Response.StationsAll,
|
||||
resultStationsAndBikes.Response.BikesOccupied);
|
||||
|
||||
// Update pins color form count of bikes located at station.
|
||||
|
@ -491,10 +495,13 @@ namespace TINK.ViewModel.Contact
|
|||
/// Gets the list of station color for all stations.
|
||||
/// </summary>
|
||||
/// <param name="stationsId">Station id list to get color for.</param>
|
||||
/// <param name="stations">Station object dictionary to get count of available bike from for each station.</param>
|
||||
/// <param name="bikesReserved">Bike collection to get count of reserved/ rented bikes from for each station.</param>
|
||||
/// <returns></returns>
|
||||
private static IList<Color> GetStationColors(
|
||||
IEnumerable<string> stationsId,
|
||||
BikeCollection bikesAll)
|
||||
internal static IList<Color> GetStationColors(
|
||||
IEnumerable<string> stationsId,
|
||||
IEnumerable<IStation> stations,
|
||||
IEnumerable<BikeInfo> bikesReserved)
|
||||
{
|
||||
if (stationsId == null)
|
||||
{
|
||||
|
@ -502,11 +509,14 @@ namespace TINK.ViewModel.Contact
|
|||
return new List<Color>();
|
||||
}
|
||||
|
||||
if (bikesAll == null)
|
||||
if (stations == null)
|
||||
{
|
||||
// If object is null an error occurred querying bikes centered or bikes occupied which results in an unknown state.
|
||||
Log.ForContext<SelectStationPageViewModel>().Error("No bikes available to determine pins color.");
|
||||
return new List<Color>(stationsId.Select(x => Color.Blue));
|
||||
Log.ForContext<SelectStationPageViewModel>().Error("No stations info available to get count of bikes available to determine whether a pin is green or not.");
|
||||
}
|
||||
|
||||
if (bikesReserved == null)
|
||||
{
|
||||
Log.ForContext<SelectStationPageViewModel>().Error("No bikes info available to determine whether a pins is light blue or not.");
|
||||
}
|
||||
|
||||
// Get state for each station.
|
||||
|
@ -514,15 +524,14 @@ namespace TINK.ViewModel.Contact
|
|||
foreach (var stationId in stationsId)
|
||||
{
|
||||
// Get color of given station.
|
||||
var bikesAtStation = bikesAll.Where(x => x.StationId == stationId).ToList();
|
||||
if (bikesAtStation.FirstOrDefault(x => x.State.Value.IsOccupied()) != null)
|
||||
if (bikesReserved?.Where(x => x.StationId == stationId).Count() > 0)
|
||||
{
|
||||
// There is at least one requested or booked bike
|
||||
colors.Add(Color.LightBlue);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bikesAtStation.ToList().Count > 0)
|
||||
if (stations?.FirstOrDefault(x => x.Id == stationId)?.AvailableBikesCount > 0)
|
||||
{
|
||||
// There is at least one bike available
|
||||
colors.Add(Color.Green);
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace TINK.ViewModel
|
|||
private readonly IViewService m_oViewService;
|
||||
|
||||
#if BACKSTYLE
|
||||
/// <summary> Reference to naviagion object to navigate back to map page when login succeeded. </summary>
|
||||
/// <summary> Reference to navigation object to navigate back to map page when login succeeded. </summary>
|
||||
private INavigation m_oNavigation;
|
||||
#endif
|
||||
/// <summary> Reference on the tink app instance. </summary>
|
||||
|
@ -51,6 +51,47 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary> Used to block more than on copri requests at a given time.</summary>
|
||||
private bool isIdle = true;
|
||||
|
||||
/// <summary>
|
||||
/// True if any action can be performed (login etc.)
|
||||
/// </summary>
|
||||
public virtual bool IsIdle
|
||||
{
|
||||
get => isIdle;
|
||||
set
|
||||
{
|
||||
if (value == isIdle)
|
||||
return;
|
||||
|
||||
Log.ForContext<LoginPageViewModel>().Debug($"Switch value of {nameof(IsIdle)} to {value}.");
|
||||
isIdle = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIdle)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsProcessWithRunningProcessView)));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProcessWithRunningProcessView => !isIdle;
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
private string statusInfoText;
|
||||
|
||||
/// <summary> Holds info about current action. </summary>
|
||||
public string StatusInfoText
|
||||
{
|
||||
get => statusInfoText;
|
||||
set
|
||||
{
|
||||
if (value == statusInfoText)
|
||||
return;
|
||||
|
||||
Log.ForContext<LoginPageViewModel>().Debug($"Switch value of {nameof(StatusInfoText)} to {value}.");
|
||||
statusInfoText = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
|
@ -159,7 +200,7 @@ namespace TINK.ViewModel
|
|||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoginRequestAllowed)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command object to bind login button to view model.
|
||||
|
@ -213,6 +254,8 @@ namespace TINK.ViewModel
|
|||
public async Task Login()
|
||||
#endif
|
||||
{
|
||||
IsIdle = false;
|
||||
StatusInfoText = AppResources.ActivityTextOneMomentPlease;
|
||||
IAccount account = new EmptyAccount();
|
||||
try
|
||||
{
|
||||
|
@ -251,6 +294,8 @@ namespace TINK.ViewModel
|
|||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
catch (UnsupportedCopriVersionDetectedException)
|
||||
|
@ -262,6 +307,8 @@ namespace TINK.ViewModel
|
|||
TinkApp.Flavor.GetDisplayName()),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
|
@ -294,6 +341,8 @@ namespace TINK.ViewModel
|
|||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -312,6 +361,9 @@ namespace TINK.ViewModel
|
|||
catch (Exception p_oException)
|
||||
{
|
||||
Log.ForContext<LoginPageViewModel>().Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -326,6 +378,8 @@ namespace TINK.ViewModel
|
|||
#else
|
||||
await m_oViewService.ShowPage("//MapPage");
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -339,6 +393,9 @@ namespace TINK.ViewModel
|
|||
catch (Exception p_oException)
|
||||
{
|
||||
Log.ForContext<LoginPageViewModel>().Error("Ein unerwarteter Fehler ist auf der Seite Anleitung TINK Räder (nach Anmeldeseite) aufgetreten. {@Exception}", p_oException);
|
||||
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -346,6 +403,8 @@ namespace TINK.ViewModel
|
|||
// Navigate back to map page.
|
||||
await m_oNavigation.PopToRootAsync();
|
||||
#endif
|
||||
IsIdle = true;
|
||||
StatusInfoText = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary> Holds whether TINK/ Copri info is shown.</summary>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue