mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-14 14:26:28 +01:00
380 lines
15 KiB
C#
380 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
using Plugin.Connectivity;
|
|
using Serilog;
|
|
using TINK.Model;
|
|
using TINK.Model.User;
|
|
using TINK.Model.User.Account;
|
|
using TINK.MultilingualResources;
|
|
using TINK.Repository.Exception;
|
|
using TINK.View;
|
|
using TINK.ViewModel.Info;
|
|
using TINK.ViewModel.Map;
|
|
using Xamarin.Forms;
|
|
|
|
namespace TINK.ViewModel
|
|
{
|
|
public class LoginPageViewModel : INotifyPropertyChanged
|
|
{
|
|
/// <summary>
|
|
/// Reference on view service to show modal notifications and to perform navigation.
|
|
/// </summary>
|
|
private readonly IViewService m_oViewService;
|
|
|
|
#if BACKSTYLE
|
|
/// <summary> Reference to naviagion object to navigate back to map page when login succeeded. </summary>
|
|
private INavigation m_oNavigation;
|
|
#endif
|
|
/// <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;
|
|
|
|
/// <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>
|
|
/// <param name="p_oViewService">Interface to actuate methodes on GUI.</param>
|
|
/// <param name="p_oNavigation">Interface to navigate.</param>
|
|
public LoginPageViewModel(
|
|
ITinkApp tinkApp,
|
|
Action<string> openUrlInExternalBrowser,
|
|
#if !BACKSTYLE
|
|
IViewService p_oViewService)
|
|
#else
|
|
IViewService p_oViewService,
|
|
INavigation p_oNavigation)
|
|
#endif
|
|
{
|
|
TinkApp = tinkApp
|
|
?? throw new ArgumentException("Can not instantiate map page view model- object. No tink app object available.");
|
|
|
|
OpenUrlInExternalBrowser = openUrlInExternalBrowser
|
|
?? throw new ArgumentException("Can not instantiate login page view model- object. No user external browse service available.");
|
|
|
|
m_oViewService = p_oViewService
|
|
?? throw new ArgumentException("Can not instantiate login page view model- object. No view available.");
|
|
#if BACKSTYLE
|
|
m_oNavigation = p_oNavigation
|
|
?? throw new ArgumentException("Can not instantiate login page view model- object. No navigation service available.");
|
|
#endif
|
|
|
|
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)));
|
|
}
|
|
}
|
|
|
|
/// <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)));
|
|
}
|
|
}
|
|
|
|
/// <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(
|
|
AppResources.MessageTitleHint,
|
|
AppResources.MessageLoginRegisterNoNet,
|
|
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(
|
|
AppResources.MessageTitleHint,
|
|
AppResources.MessageLoginRecoverPassword,
|
|
AppResources.MessageAnswerOk);
|
|
}
|
|
});
|
|
|
|
/// <summary>
|
|
/// User request to log in.
|
|
/// </summary>
|
|
#if BACKSTYLE
|
|
public async void Login()
|
|
#else
|
|
public async Task Login()
|
|
#endif
|
|
{
|
|
IAccount account = new EmptyAccount();
|
|
try
|
|
{
|
|
Log.ForContext<LoginPageViewModel>().Information("User taped login button.");
|
|
|
|
try
|
|
{
|
|
TinkApp.ActiveUser.CheckIsPasswordValid(MailAddress, Password);
|
|
|
|
// 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();
|
|
}
|
|
catch (InvalidAuthorizationResponseException l_oException)
|
|
{
|
|
// Copri response is invalid.
|
|
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
|
|
|
|
await m_oViewService.DisplayAlert(
|
|
AppResources.MessageLoginErrorTitle,
|
|
l_oException.Message,
|
|
AppResources.MessageAnswerOk);
|
|
|
|
return;
|
|
}
|
|
catch (UnsupportedCopriVersionDetectedException)
|
|
{
|
|
await m_oViewService.DisplayAlert(
|
|
AppResources.MessageLoginErrorTitle,
|
|
string.Format(AppResources.MessageAppVersionIsOutdated, ContactPageViewModel.GetAppName(TinkApp.Uris.ActiveUri)),
|
|
AppResources.MessageAnswerOk);
|
|
|
|
return;
|
|
}
|
|
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.MessageLoginConnectionErrorTitle,
|
|
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
|
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.MessageLoginErrorTitle,
|
|
string.Format(AppResources.MessageLoginConnectionErrorMessage, l_oException.Message),
|
|
"OK");
|
|
}
|
|
else
|
|
{
|
|
Log.ForContext<LoginPageViewModel>().Error("Login failed. {@l_oException}.", l_oException);
|
|
await m_oViewService.DisplayAlert(
|
|
AppResources.MessageLoginErrorTitle,
|
|
l_oException.Message,
|
|
AppResources.MessageAnswerOk);
|
|
}
|
|
|
|
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);
|
|
}
|
|
catch (Exception p_oException)
|
|
{
|
|
Log.ForContext<LoginPageViewModel>().Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!TinkApp.ActiveUser.Group.Contains(Model.Connector.FilterHelper.CARGOBIKE))
|
|
{
|
|
// No need to show "Anleitung TINK Räder" because user can not use tink.
|
|
#if USEFLYOUT
|
|
|
|
m_oViewService.ShowPage(account.IsAgbAcknowledged ? ViewTypes.MapPage : ViewTypes.ManageAccountPage);
|
|
#else
|
|
await m_oViewService.ShowPage("//MapPage");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// Swich to map page
|
|
#if USEFLYOUT
|
|
m_oViewService.ShowPage(ViewTypes.BikeInfoCarouselPage, AppResources.MarkingLoginInstructions);
|
|
#else
|
|
await m_oViewService.ShowPage("//MapPage");
|
|
#endif
|
|
}
|
|
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);
|
|
return;
|
|
}
|
|
|
|
#if BACKSTYLE
|
|
// Navigate back to map page.
|
|
await m_oNavigation.PopToRootAsync();
|
|
#endif
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
}
|
|
}
|
|
}
|