sharee.bike-App/TINKLib/ViewModel/Login/LoginPageViewModel.cs

438 lines
13 KiB
C#
Raw Normal View History

2023-04-19 12:14:14 +02:00
using System;
2022-08-30 15:42:25 +02:00
using System.Collections.Generic;
2021-05-13 20:03:07 +02:00
using System.ComponentModel;
2022-08-30 15:42:25 +02:00
using System.Linq;
2021-05-13 20:03:07 +02:00
using System.Threading.Tasks;
2022-08-30 15:42:25 +02:00
using System.Windows.Input;
2021-05-13 20:03:07 +02:00
using Plugin.Connectivity;
2022-08-30 15:42:25 +02:00
using Serilog;
2021-05-13 20:03:07 +02:00
using TINK.Model;
2022-08-30 15:42:25 +02:00
using TINK.Model.User;
using TINK.Model.User.Account;
2021-05-13 20:03:07 +02:00
using TINK.MultilingualResources;
2022-08-30 15:42:25 +02:00
using TINK.Repository.Exception;
using TINK.View;
using TINK.ViewModel.Map;
using Xamarin.Forms;
2021-05-13 20:03:07 +02:00
namespace TINK.ViewModel
{
2022-09-06 16:08:19 +02:00
public class LoginPageViewModel : INotifyPropertyChanged
{
/// <summary>
/// Reference on view service to show modal notifications and to perform navigation.
/// </summary>
private readonly IViewService m_oViewService;
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Reference on the tink app instance. </summary>
private ITinkApp TinkApp { get; }
/// <summary>
/// Holds the mail address candidate being entered by user.
/// </summary>
private string m_strMailAddress;
/// <summary>
/// Holds the password candidate being entered by user.
/// </summary>
private string m_strPassword;
private bool m_bMailAndPasswordCandidatesOk = false;
/// <summary> Holds a reference to the external trigger service. </summary>
private Action<string> OpenUrlInExternalBrowser { get; }
/// <summary>
/// Event which notifies clients about changed properties.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
2023-07-19 10:10:36 +02:00
/// <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)));
}
}
2022-09-06 16:08:19 +02:00
/// <summary>
///
/// </summary>
/// <param name="p_oSetCredentials">Delegate to set new mail address and password to model.</param>
/// <param name="p_oUser">Mail address of user if login succeeded.</param>
/// <param name="openUrlInExternalBrowser">Action to open an external browser.</param>
2023-04-19 12:14:14 +02:00
/// <param name="p_oViewService">Interface to actuate methods on GUI.</param>
2022-09-06 16:08:19 +02:00
/// <param name="p_oNavigation">Interface to navigate.</param>
public LoginPageViewModel(
ITinkApp tinkApp,
Action<string> openUrlInExternalBrowser,
IViewService p_oViewService)
{
TinkApp = tinkApp
?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available.");
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
OpenUrlInExternalBrowser = openUrlInExternalBrowser
?? throw new ArgumentException("Can not instantiate login page view model- object. No user external browse service available.");
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
m_oViewService = p_oViewService
?? throw new ArgumentException("Can not instantiate login page view model- object. No view available.");
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
m_strMailAddress = tinkApp.ActiveUser.Mail;
m_strPassword = tinkApp.ActiveUser.Password;
IsRegisterTargetsInfoVisible = new List<string> { Model.Services.CopriApi.ServerUris.CopriServerUriList.TINK_LIVE, Model.Services.CopriApi.ServerUris.CopriServerUriList.TINK_DEVEL }.Contains(tinkApp.Uris.ActiveUri.AbsoluteUri);
tinkApp.ActiveUser.StateChanged += OnStateChanged;
}
/// <summary>
/// Login state changed.
/// </summary>
/// <param name="p_oSender"></param>
/// <param name="p_oEventArgs"></param>
private void OnStateChanged(object p_oSender, EventArgs p_oEventArgs)
{
var l_oPropertyChanged = PropertyChanged;
if (l_oPropertyChanged != null)
{
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(IsLoggedOut)));
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(IsLoginRequestAllowed)));
2023-09-28 15:37:44 +02:00
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(MailAddress)));
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(Password)));
2022-09-06 16:08:19 +02:00
}
}
/// <summary> Gets a value indicating whether user is logged out or not.</summary>
public bool IsLoggedOut { get { return !TinkApp.ActiveUser.IsLoggedIn; } }
/// <summary> Gets a value indicating whether user can try to login.</summary>
public bool IsLoginRequestAllowed
{
get
{
return !TinkApp.ActiveUser.IsLoggedIn
&& m_bMailAndPasswordCandidatesOk;
}
}
/// <summary> Gets mail address of user.</summary>
public string MailAddress
{
get
{
return m_strMailAddress;
}
set
{
m_strMailAddress = value;
UpdateAndFireIfRequiredOnEnteringMailOrPwd();
}
}
/// <summary> Gets password user.</summary>
public string Password
{
get
{
return m_strPassword;
}
set
{
m_strPassword = value;
UpdateAndFireIfRequiredOnEnteringMailOrPwd();
}
}
/// <summary>Update on password or mailaddress set. </summary>
private void UpdateAndFireIfRequiredOnEnteringMailOrPwd()
{
var l_bLastMailAndPasswordCandidatesOk = m_bMailAndPasswordCandidatesOk;
m_bMailAndPasswordCandidatesOk = Validator.ValidateMailAndPasswordDelegate(MailAddress, Password).ValidElement == Elements.Account;
if (m_bMailAndPasswordCandidatesOk != l_bLastMailAndPasswordCandidatesOk)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoginRequestAllowed)));
}
2023-07-19 10:10:36 +02:00
}
2022-09-06 16:08:19 +02:00
/// <summary>
/// Command object to bind login button to view model.
/// </summary>
public ICommand OnLoginRequest
{
get
{
return new Command(async () => await Login());
}
}
/// <summary> Processes request to register a new user. </summary>
public ICommand OnRegisterRequest => new Command(async () =>
{
if (CrossConnectivity.Current.IsConnected)
{
await m_oViewService.PushAsync(ViewTypes.RegisterPage);
}
else
{
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.MessageHintTitle,
AppResources.ErrorNoWeb,
2022-09-06 16:08:19 +02:00
AppResources.MessageAnswerOk);
}
});
/// <summary> Processes request to recover password. </summary>
public ICommand OnPasswordForgottonRequest => new Command(async () =>
{
if (CrossConnectivity.Current.IsConnected)
{
await m_oViewService.PushAsync(ViewTypes.PasswordForgottenPage);
}
else
{
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.MessageHintTitle,
AppResources.ErrorNoWeb,
2022-09-06 16:08:19 +02:00
AppResources.MessageAnswerOk);
}
});
/// <summary>
/// User request to log in.
/// </summary>
public async Task Login()
{
2023-09-22 11:38:42 +02:00
if (!IsLoginRequestAllowed)
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
if(TinkApp.ActiveUser.IsLoggedIn)
2022-09-06 16:08:19 +02:00
{
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.ErrorLoginTitle,
2023-09-22 11:38:42 +02:00
string.Format(AppResources.ErrorLoginAlreadyLoggedIn,TinkApp.ActiveUser.Mail),
2022-09-06 16:08:19 +02:00
AppResources.MessageAnswerOk);
}
2023-09-22 11:38:42 +02:00
else if (!m_bMailAndPasswordCandidatesOk)
{
2022-09-06 16:08:19 +02:00
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.ErrorLoginTitle,
2023-09-22 11:38:42 +02:00
AppResources.ErrorLoginInvalidMailOrPasswortInput,
2022-09-06 16:08:19 +02:00
AppResources.MessageAnswerOk);
}
2023-09-22 11:38:42 +02:00
return;
}
else
{
IsIdle = false;
StatusInfoText = AppResources.ActivityTextOneMomentPlease;
IAccount account = new EmptyAccount();
try
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
Log.ForContext<LoginPageViewModel>().Information("User taped login button.");
try
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
TinkApp.ActiveUser.CheckIsPasswordValid(MailAddress, Password);
2022-09-06 16:08:19 +02:00
2023-09-22 11:38:42 +02:00
// Do login.
account = await TinkApp.GetConnector(CrossConnectivity.Current.IsConnected).Command.DoLogin(MailAddress, Password, TinkApp.ActiveUser.DeviceId);
await TinkApp.ActiveUser.Login(account);
// Update map page filter because user might be of both groups TINK an Konrad.
TinkApp.GroupFilterMapPage =
GroupFilterMapPageHelper.CreateUpdated(
TinkApp.GroupFilterMapPage,
TinkApp.ActiveUser.DoFilter(TinkApp.FilterGroupSetting.DoFilter()));
// Update settings page filter because user might be of both groups TINK an Konrad.
TinkApp.FilterGroupSetting.DoFilter(TinkApp.ActiveUser.Group);
// Persist new settings.
TinkApp.Save();
TinkApp.UpdateConnector();
2022-09-06 16:08:19 +02:00
}
2023-09-22 11:38:42 +02:00
catch (InvalidAuthorizationResponseException l_oException)
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
// Copri response is invalid.
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
2022-09-06 16:08:19 +02:00
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.ErrorLoginTitle,
2023-09-22 11:38:42 +02:00
AppResources.ErrorLoginInvalidAuthorization,
AppResources.MessageAnswerOk);
IsIdle = true;
StatusInfoText = string.Empty;
return;
2022-09-06 16:08:19 +02:00
}
2023-09-22 11:38:42 +02:00
catch (UnsupportedCopriVersionDetectedException)
2022-09-06 16:08:19 +02:00
{
await m_oViewService.DisplayAlert(
2023-08-31 12:20:06 +02:00
AppResources.ErrorLoginTitle,
2023-09-22 11:38:42 +02:00
string.Format(
AppResources.MessageAppVersionIsOutdated,
TinkApp.Flavor.GetDisplayName()),
2022-09-06 16:08:19 +02:00
AppResources.MessageAnswerOk);
2023-09-22 11:38:42 +02:00
IsIdle = true;
StatusInfoText = string.Empty;
return;
2022-09-06 16:08:19 +02:00
}
2023-09-22 11:38:42 +02:00
catch (Exception l_oException)
{
// Copri server is not reachable.
if (l_oException is WebConnectFailureException)
{
Log.ForContext<LoginPageViewModel>().Information("Login failed (web communication exception). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorNoConnectionTitle,
AppResources.ErrorNoWeb,
AppResources.MessageAnswerOk);
}
else if (l_oException is UsernamePasswordInvalidException)
{
// Cookie is empty.
Log.ForContext<LoginPageViewModel>().Error("Login failed (empty cookie). {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
string.Format(AppResources.ErrorLoginNoCookie, l_oException.Message),
AppResources.MessageAnswerOk);
}
else
{
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
await m_oViewService.DisplayAlert(
AppResources.ErrorLoginTitle,
l_oException.Message,
AppResources.MessageAnswerOk);
}
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
// Display information that login succeeded.
Log.ForContext<LoginPageViewModel>().Information("Login succeeded. {@tinkApp.ActiveUser}.", TinkApp.ActiveUser);
var title = TinkApp.ActiveUser.Group.Intersect(new List<string> { Model.Connector.FilterHelper.CARGOBIKE, Model.Connector.FilterHelper.CITYBIKE }).Any()
? string.Format(AppResources.MessageLoginWelcomeTitleGroup, TinkApp.ActiveUser.GetUserGroupDisplayName())
: string.Format(AppResources.MessageLoginWelcomeTitle);
await m_oViewService.DisplayAlert(
title,
string.Format(AppResources.MessageLoginWelcome, TinkApp.ActiveUser.Mail),
AppResources.MessageAnswerOk);
2023-09-28 15:37:44 +02:00
//clear MailAdress and Password user input
MailAddress = string.Empty;
Password = string.Empty;
2023-09-22 11:38:42 +02:00
}
catch (Exception p_oException)
{
Log.ForContext<LoginPageViewModel>().Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
2022-09-06 16:08:19 +02:00
2023-07-19 10:10:36 +02:00
IsIdle = true;
StatusInfoText = string.Empty;
2022-09-06 16:08:19 +02:00
return;
}
2023-09-22 11:38:42 +02:00
try
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
{
// No need to show "Anleitung TINK Räder" because user can not use tink.
await m_oViewService.ShowPage("//MapPage");
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
2021-05-13 20:03:07 +02:00
2023-09-22 11:38:42 +02:00
// Switch to map page
await m_oViewService.ShowPage("//MapPage");
}
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);
2023-07-19 10:10:36 +02:00
2023-09-22 11:38:42 +02:00
IsIdle = true;
StatusInfoText = string.Empty;
return;
}
2021-05-13 20:03:07 +02:00
2023-09-22 11:38:42 +02:00
IsIdle = true;
StatusInfoText = string.Empty;
}
2022-09-06 16:08:19 +02:00
}
/// <summary> Holds whether TINK/ Copri info is shown.</summary>
public bool IsRegisterTargetsInfoVisible { get; private set; }
/// <summary> Text providing info about TINK/ konrad registration. </summary>
public FormattedString RegisterTargetsInfo
{
get
{
var l_oHint = new FormattedString();
l_oHint.Spans.Add(new Span { Text = AppResources.MarkingLoginInstructionsTinkKonradTitle });
l_oHint.Spans.Add(new Span { Text = AppResources.MarkingLoginInstructionsTinkKonradMessage });
return l_oHint;
}
}
/// <summary> Opens login page.</summary>
public void RegisterRequest(string url)
{
try
{
OpenUrlInExternalBrowser(url);
}
catch (Exception p_oException)
{
Log.Error("Ein unerwarteter Fehler ist auf der Login Seite beim Öffnen eines Browsers, Seite {url}, aufgetreten. {@Exception}", url, p_oException);
return;
}
}
}
2021-05-13 20:03:07 +02:00
}