mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-05 10:36:30 +01:00
443 lines
16 KiB
C#
443 lines
16 KiB
C#
|
|
|||
|
using Plugin.Connectivity;
|
|||
|
using Serilog;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using TINK.Model;
|
|||
|
using TINK.Model.Connector;
|
|||
|
using TINK.Model.Repository.Exception;
|
|||
|
using TINK.View;
|
|||
|
using TINK.ViewModel.Settings;
|
|||
|
using System.Linq;
|
|||
|
using TINK.MultilingualResources;
|
|||
|
|
|||
|
namespace TINK.ViewModel.Account
|
|||
|
{
|
|||
|
public class AccountPageViewModel : INotifyPropertyChanged
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Count of requested/ booked bikes of logged in user.
|
|||
|
/// </summary>
|
|||
|
private int? m_iMyBikesCount;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Reference on view servcie to show modal notifications and to perform navigation.
|
|||
|
/// </summary>
|
|||
|
private IViewService m_oViewService;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Holds the exception which occurred getting bikes occupied information.
|
|||
|
/// </summary>
|
|||
|
private Exception m_oException;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Fired if a property changes.
|
|||
|
/// </summary>
|
|||
|
public event PropertyChangedEventHandler PropertyChanged;
|
|||
|
|
|||
|
/// <summary> Object to manage update of view model objects from Copri.</summary>
|
|||
|
private IPollingUpdateTaskManager m_oViewUpdateManager;
|
|||
|
|
|||
|
/// <summary> Holds a reference to the external trigger service. </summary>
|
|||
|
private Action<string> OpenUrlInExternalBrowser { get; }
|
|||
|
|
|||
|
/// <summary> Reference on the tink app instance. </summary>
|
|||
|
private ITinkApp TinkApp { get; }
|
|||
|
|
|||
|
/// <summary> Constructs a settings page view model object.</summary>
|
|||
|
/// <param name="tinkApp"> Reference to tink app model.</param>
|
|||
|
/// <param name="p_oUser"></param>
|
|||
|
/// <param name="p_oDevice"></param>
|
|||
|
/// <param name="p_oFilterGroup">Filter to apply on stations and bikes.</param>
|
|||
|
/// <param name="p_oUris">Available copri server host uris including uri to use for next start.</param>
|
|||
|
/// <param name="p_oPolling"> Holds whether to poll or not and the periode leght is polling is on. </param>
|
|||
|
/// <param name="p_oDefaultPollingPeriode">Default polling periode lenght.</param>
|
|||
|
/// <param name="p_oMinimumLogEventLevel">Controls logging level.</param>
|
|||
|
/// <param name="p_oViewService">Interface to view</param>
|
|||
|
public AccountPageViewModel(
|
|||
|
ITinkApp tinkApp,
|
|||
|
Action<string> openUrlInExternalBrowser,
|
|||
|
IViewService p_oViewService)
|
|||
|
{
|
|||
|
TinkApp = tinkApp
|
|||
|
?? throw new ArgumentException("Can not instantiate settings page view model- object. No tink app object available.");
|
|||
|
|
|||
|
OpenUrlInExternalBrowser = openUrlInExternalBrowser
|
|||
|
?? throw new ArgumentException("Can not instantiate settings page view model- object. No user external browse service available.");
|
|||
|
|
|||
|
m_oViewService = p_oViewService
|
|||
|
?? throw new ArgumentException("Can not instantiate settings page view model- object. No user view service available.");
|
|||
|
|
|||
|
m_oViewUpdateManager = new IdlePollingUpdateTaskManager();
|
|||
|
|
|||
|
Polling = new PollingViewModel(TinkApp.Polling);
|
|||
|
|
|||
|
TinkApp.ActiveUser.StateChanged += OnStateChanged;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Log in state of user 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(LoggedInInfo)));
|
|||
|
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(IsBookingStateInfoVisible)));
|
|||
|
l_oPropertyChanged(this, new PropertyChangedEventArgs(nameof(BookingStateInfo)));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary> Holds information whether app is connected to web or not. </summary>
|
|||
|
private bool? isConnected = null;
|
|||
|
|
|||
|
/// <summary>Exposes the is connected state. </summary>
|
|||
|
private bool IsConnected
|
|||
|
{
|
|||
|
get => isConnected ?? false;
|
|||
|
set
|
|||
|
{
|
|||
|
var bookingStateInfo = BookingStateInfo;
|
|||
|
isConnected = value;
|
|||
|
if (bookingStateInfo == BookingStateInfo)
|
|||
|
{
|
|||
|
// Nothing to do.
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingStateInfo)));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the value of sessionn cookie (for debugging purposes).
|
|||
|
/// </summary>
|
|||
|
public string SessionCookie
|
|||
|
{
|
|||
|
get { return TinkApp.ActiveUser.SessionCookie; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets value whether a user is logged in or not.
|
|||
|
/// </summary>
|
|||
|
public bool IsLogoutPossible
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return m_oException == null
|
|||
|
&& TinkApp.ActiveUser.IsLoggedIn;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Is true if user is logged in.
|
|||
|
/// </summary>
|
|||
|
public bool IsLoggedIn
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return TinkApp.ActiveUser.IsLoggedIn;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shows info about logged in user if user is logged in. Text "Logged out" otherwise.
|
|||
|
/// </summary>
|
|||
|
public string LoggedInInfo
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (!TinkApp.ActiveUser.IsLoggedIn)
|
|||
|
{
|
|||
|
return AppResources.MarkingLoggedInStateInfoNotLoggedIn;
|
|||
|
}
|
|||
|
|
|||
|
return TinkApp.ActiveUser.Group.Intersect(new List<string> { FilterHelper.FILTERTINKGENERAL, FilterHelper.FILTERKONRAD }).Any()
|
|||
|
? string.Format(AppResources.MarkingLoggedInStateInfoLoggedInGroup, TinkApp.ActiveUser.Mail, TinkApp.ActiveUser.GetUserGroupDisplayName())
|
|||
|
: string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.ActiveUser.Mail);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a value indicating whether booking state info is avilable or not.
|
|||
|
/// </summary>
|
|||
|
public bool IsBookingStateInfoVisible
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return BookingStateInfo.Length > 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Count of bikes reserved and/ or occupied.
|
|||
|
/// </summary>
|
|||
|
private int? BikesOccupiedCount
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return m_iMyBikesCount;
|
|||
|
}
|
|||
|
|
|||
|
set
|
|||
|
{
|
|||
|
var isBookingStateInfoVisible = IsBookingStateInfoVisible;
|
|||
|
var bookingStateInfo = BookingStateInfo;
|
|||
|
m_iMyBikesCount = value;
|
|||
|
|
|||
|
if (isBookingStateInfoVisible != IsBookingStateInfoVisible)
|
|||
|
{
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsBookingStateInfoVisible)));
|
|||
|
}
|
|||
|
|
|||
|
if (bookingStateInfo != BookingStateInfo)
|
|||
|
{
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingStateInfo)));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shows info about reserved/ booked bikes if there are.
|
|||
|
/// </summary>
|
|||
|
public string BookingStateInfo
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (!TinkApp.ActiveUser.IsLoggedIn || !m_iMyBikesCount.HasValue || m_iMyBikesCount.Value <= 0)
|
|||
|
{
|
|||
|
// Either
|
|||
|
// - user is not logged in or
|
|||
|
// - user has no bikes requested/ booked
|
|||
|
return string.Empty;
|
|||
|
}
|
|||
|
|
|||
|
var bookingStateInfo = string.Format("Aktuell {0} Fahrräder reserviert/ gebucht.", m_iMyBikesCount.Value);
|
|||
|
|
|||
|
if (!IsConnected)
|
|||
|
{
|
|||
|
// Append offline info
|
|||
|
return $"{bookingStateInfo} Verbindungsstatus: Offline.";
|
|||
|
}
|
|||
|
|
|||
|
if (Exception != null)
|
|||
|
{
|
|||
|
// Append offline info
|
|||
|
return $"{bookingStateInfo} Verbindungstatus: Verbindung unterbrochen. ";
|
|||
|
}
|
|||
|
|
|||
|
return bookingStateInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Commang object to bind login button to view model.
|
|||
|
/// </summary>
|
|||
|
public System.Windows.Input.ICommand OnLogoutRequest
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return new Xamarin.Forms.Command(async () => await Logout(), () => IsLogoutPossible);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary> Processes request to view personal data.</summary>
|
|||
|
public System.Windows.Input.ICommand OnManageAccount => new Xamarin.Forms.Command(async () =>
|
|||
|
{
|
|||
|
if (CrossConnectivity.Current.IsConnected)
|
|||
|
{
|
|||
|
await m_oViewService.PushAsync(ViewTypes.ManageAccountPage);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
await m_oViewService.DisplayAlert(
|
|||
|
"Hinweis",
|
|||
|
"Bitte mit Internet verbinden zum Verwalten der persönlichen Daten.",
|
|||
|
"OK");
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Request to log out.
|
|||
|
/// </summary>
|
|||
|
public async Task Logout()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
// Backup logout message before logout.
|
|||
|
var l_oMessage = string.Format("Benutzer {0} abgemeldet.", TinkApp.ActiveUser.Mail);
|
|||
|
|
|||
|
// Stop polling before requesting bike.
|
|||
|
await m_oViewUpdateManager.StopUpdatePeridically();
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
// Log out user.
|
|||
|
IsConnected = TinkApp.GetIsConnected();
|
|||
|
await TinkApp.GetConnector(IsConnected).Command.DoLogout();
|
|||
|
}
|
|||
|
catch (Exception l_oException)
|
|||
|
{
|
|||
|
// Copri server is not reachable.
|
|||
|
if (l_oException is WebConnectFailureException)
|
|||
|
{
|
|||
|
await m_oViewService.DisplayAlert(
|
|||
|
"Verbingungsfehler bei Abmeldung!",
|
|||
|
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
|||
|
"OK");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
await m_oViewService.DisplayAlert("Fehler bei Abmeldung!", l_oException.Message, "OK");
|
|||
|
}
|
|||
|
|
|||
|
// Restart polling again.
|
|||
|
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
TinkApp.ActiveUser.Logout();
|
|||
|
|
|||
|
// Display information that log out was perfomrmed.
|
|||
|
await m_oViewService.DisplayAlert("Auf Wiedersehen!", l_oMessage, "OK");
|
|||
|
}
|
|||
|
catch (Exception p_oException)
|
|||
|
{
|
|||
|
Log.Error("An unexpected error occurred displaying log out page. {@Exception}", p_oException);
|
|||
|
return;
|
|||
|
}
|
|||
|
try
|
|||
|
{
|
|||
|
// Switch to map view after log out.
|
|||
|
m_oViewService.ShowPage(ViewTypes.MapPage);
|
|||
|
}
|
|||
|
catch (Exception p_oException)
|
|||
|
{
|
|||
|
Log.Error("An unexpected error occurred switching back to map page after displaying log out page. {@Exception}", p_oException);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Invoked when page is shown.
|
|||
|
/// Starts update process.
|
|||
|
/// </summary>
|
|||
|
public async Task OnAppearing()
|
|||
|
{
|
|||
|
IsConnected = TinkApp.GetIsConnected();
|
|||
|
BikesOccupiedCount = TinkApp.ActiveUser.IsLoggedIn
|
|||
|
? (await TinkApp.GetConnector(IsConnected).Query.GetBikesOccupiedAsync()).Response.Count
|
|||
|
: 0;
|
|||
|
|
|||
|
|
|||
|
m_oViewUpdateManager = new PollingUpdateTaskManager(
|
|||
|
() => GetType().Name,
|
|||
|
() =>
|
|||
|
{
|
|||
|
TinkApp.PostAction(
|
|||
|
unused => IsConnected = TinkApp.GetIsConnected(),
|
|||
|
null);
|
|||
|
|
|||
|
int? l_iBikesCount = null;
|
|||
|
|
|||
|
var bikesOccupied = TinkApp.GetConnector(IsConnected).Query.GetBikesOccupiedAsync().Result;
|
|||
|
l_iBikesCount = TinkApp.ActiveUser.IsLoggedIn
|
|||
|
? bikesOccupied.Response.Count
|
|||
|
: 0;
|
|||
|
|
|||
|
TinkApp.PostAction(
|
|||
|
unused =>
|
|||
|
{
|
|||
|
BikesOccupiedCount = l_iBikesCount;
|
|||
|
Exception = bikesOccupied.Exception;
|
|||
|
},
|
|||
|
null);
|
|||
|
}
|
|||
|
);
|
|||
|
try
|
|||
|
{
|
|||
|
// Update bikes at station or my bikes depending on context.
|
|||
|
await m_oViewUpdateManager.StartUpdateAyncPeridically(Polling.ToImmutable());
|
|||
|
}
|
|||
|
catch (Exception l_oExcetion)
|
|||
|
{
|
|||
|
Log.Error("Getting count of bikes on settings pags failed. {@l_oExcetion}.", l_oExcetion);
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Invoked when page is shutdown.
|
|||
|
/// Currently invoked by code behind, would be nice if called by XAML in future versions.
|
|||
|
/// </summary>
|
|||
|
public async Task OnDisappearing()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
Log.ForContext<AccountPageViewModel>().Information($"Entering {nameof(OnDisappearing)}...");
|
|||
|
|
|||
|
await m_oViewUpdateManager.StopUpdatePeridically();
|
|||
|
|
|||
|
}
|
|||
|
catch (Exception l_oException)
|
|||
|
{
|
|||
|
await m_oViewService.DisplayAlert(
|
|||
|
"Fehler",
|
|||
|
$"Ein unerwarteter Fehler ist aufgetreten. \r\n{l_oException.Message}",
|
|||
|
"OK");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Exception which occurred getting bike information.
|
|||
|
/// </summary>
|
|||
|
protected Exception Exception
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return m_oException;
|
|||
|
}
|
|||
|
|
|||
|
set
|
|||
|
{
|
|||
|
var l_oException = m_oException;
|
|||
|
var bookingStateInfo = BookingStateInfo;
|
|||
|
m_oException = value;
|
|||
|
if ((m_oException != null && l_oException == null)
|
|||
|
|| (m_oException == null && l_oException != null))
|
|||
|
{
|
|||
|
// Error information is available and was not or the other way round.
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLogoutPossible)));
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLogoutPossible)));
|
|||
|
}
|
|||
|
if (bookingStateInfo != BookingStateInfo)
|
|||
|
{
|
|||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BookingStateInfo)));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Polling periode.</summary>
|
|||
|
public PollingViewModel Polling { get; }
|
|||
|
|
|||
|
/// <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 Einstellungs- Seite beim Öffnen eines Browsers, Seite {url}, aufgetreten. {@Exception}", url, p_oException);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|