mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-19 03:27:29 +02:00
Version 3.0.337
This commit is contained in:
parent
fd0e63cf10
commit
573fe77e12
2336 changed files with 33688 additions and 86082 deletions
|
@ -1,18 +1,15 @@
|
|||
|
||||
using Plugin.Connectivity;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Plugin.Connectivity;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Settings;
|
||||
using System.Linq;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.ViewModel.Info;
|
||||
using TINK.ViewModel.Settings;
|
||||
|
||||
namespace TINK.ViewModel.Account
|
||||
{
|
||||
|
@ -393,7 +390,7 @@ namespace TINK.ViewModel.Account
|
|||
|
||||
await m_oViewUpdateManager.StopUpdatePeridically();
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
|
|
|
@ -5,7 +5,7 @@ using TINK.Model.Device;
|
|||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes.Bike.BC.RequestHandler;
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BC
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC
|
|||
/// Todo: Check which events are received here and filter, to avoid event storm.
|
||||
/// </summary>
|
||||
/// <param name="p_strNameOfProp"></param>
|
||||
public override void OnSelectedBikeStateChanged ()
|
||||
public override void OnSelectedBikeStateChanged()
|
||||
{
|
||||
RequestHandler = RequestHandlerFactory.Create(
|
||||
Bike,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
|
||||
|
@ -14,9 +13,6 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
/// </summary>
|
||||
public IBikesViewModel BikesViewModel { get; set; }
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public abstract InUseStateEnum State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the button to reserve bike is visible or not.
|
||||
/// </summary>
|
||||
|
@ -54,7 +50,7 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
private bool isConnected;
|
||||
|
||||
/// <summary>Gets the is connected state. </summary>
|
||||
public bool IsConnected
|
||||
public bool IsConnected
|
||||
{
|
||||
get => isConnected;
|
||||
set
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bikes.Bike.BC;
|
||||
using TINK.Model.State;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BC;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.View;
|
||||
|
@ -11,9 +10,6 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
{
|
||||
public class Booked : IRequestHandler
|
||||
{
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary>
|
||||
/// If a bike is booked unbooking can not be done by though app.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using TINK.Model.Device;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
||||
{
|
||||
|
@ -28,10 +27,6 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
IUser activeUser) : base(selectedBike, selectedBike.State.Value.GetActionText(), true, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary> Request bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequest()
|
||||
{
|
||||
|
@ -84,16 +79,16 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Reservieren des Rads!",
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<Disposable>().Error("User selected availalbe bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert("Fehler beim Reservieren des Rads!", l_oException.Message, "OK");
|
||||
await ViewService.DisplayAlert(AppResources.MessageReservingBikeErrorGeneralTitle, l_oException.Message, AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = string.Empty; // Todo: Remove this statement because in catch block ActionText is already set to empty above.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
|
@ -16,19 +16,18 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser)
|
||||
{
|
||||
State = state;
|
||||
ButtonText = state.GetActionText();
|
||||
IsIdle = true;
|
||||
ViewService = viewService;
|
||||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
}
|
||||
public InUseStateEnum State { get; }
|
||||
|
||||
public bool IsButtonVisible => true;
|
||||
|
||||
public bool IsIdle { get; private set; }
|
||||
|
||||
public string ButtonText => State.GetActionText();
|
||||
public string ButtonText { get; private set; }
|
||||
|
||||
public string ActionText { get => BikesViewModel.ActionText; private set => BikesViewModel.ActionText = value; }
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using TINK.Model.Device;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
||||
{
|
||||
|
@ -29,9 +27,6 @@ namespace TINK.ViewModel.Bikes.Bike.BC.RequestHandler
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Executes user request to cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequest()
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using TINK.Model.Device;
|
|||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes.Bike.BC.RequestHandler;
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BC
|
||||
{
|
||||
|
|
|
@ -3,7 +3,6 @@ using System;
|
|||
using System.ComponentModel;
|
||||
using System.Text.RegularExpressions;
|
||||
#if !USEFLYOUT
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
|
@ -13,7 +12,7 @@ using TINK.MultilingualResources;
|
|||
using TINK.View;
|
||||
using Xamarin.Forms;
|
||||
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
{
|
||||
|
@ -117,7 +116,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
|
||||
ViewService = viewService;
|
||||
|
||||
Bike = selectedBike
|
||||
Bike = selectedBike
|
||||
?? throw new ArgumentException(string.Format("Can not construct {0}- object, bike object is null.", typeof(BikeViewModelBase)));
|
||||
|
||||
ActiveUser = activeUser
|
||||
|
@ -127,7 +126,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
?? throw new ArgumentException(string.Format("Can not construct {0}- object, user object is null.", typeof(IInUseStateInfoProvider)));
|
||||
|
||||
selectedBike.PropertyChanged +=
|
||||
(sender, eventargs) => OnSelectedBikePropertyChanged(eventargs.PropertyName);
|
||||
(sender, eventargs) => OnSelectedBikePropertyChanged(eventargs.PropertyName);
|
||||
|
||||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
|
@ -141,7 +140,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// </summary>
|
||||
/// <param name="p_strNameOfProp"></param>
|
||||
private void OnSelectedBikePropertyChanged(string p_strNameOfProp)
|
||||
{
|
||||
{
|
||||
if (p_strNameOfProp == nameof(State))
|
||||
{
|
||||
OnSelectedBikeStateChanged(); // Notify derived class about change of state.
|
||||
|
@ -174,6 +173,9 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// </summary>
|
||||
public string Name => Bike.GetDisplayName();
|
||||
|
||||
public string TypeOfBike => Bike.GetDisplayTypeOfBike();
|
||||
|
||||
public string WheelType => Bike.GetDisplayWheelType();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique Id of bike or an empty string, if no name is defined to avoid duplicate display of id.
|
||||
|
@ -183,10 +185,39 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// <summary>
|
||||
/// Gets the unique Id of bike used by derived model to determine which bike to remove.
|
||||
/// </summary>
|
||||
public string Id=> Bike.Id;
|
||||
public string Id => Bike.Id;
|
||||
|
||||
public bool IsBikeWithCopriLock => Bike.LockModel == Model.Bikes.BikeInfoNS.BikeNS.LockModel.Sigo;
|
||||
|
||||
/// Returns if type of bike is a cargo pedelec bike.
|
||||
public bool IsBatteryChargeVisible =>
|
||||
Bike.Drive.Type == Model.Bikes.BikeInfoNS.DriveNS.DriveType.Pedelec
|
||||
&& (!Bike.Drive.Battery.IsHidden.HasValue /* no value means show battery level */ || Bike.Drive.Battery.IsHidden.Value == false);
|
||||
|
||||
/// Gets the image path for bike type Citybike, CargoLong, Trike or Pedelec.
|
||||
public string DisplayedBikeImageSourceString => $"bike_{Bike.TypeOfBike}_{Bike.Drive.Type}_{Bike.WheelType}.png";
|
||||
|
||||
/// <summary>
|
||||
/// Returns status of a bike as text.
|
||||
/// Gets the current charge level.
|
||||
/// </summary>
|
||||
public string CurrentChargeBars => Bike.Drive.Type == Model.Bikes.BikeInfoNS.DriveNS.DriveType.Pedelec
|
||||
? Bike.Drive.Battery.CurrentChargeBars?.ToString() ?? string.Empty
|
||||
: string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value if current charge level is low ( <= 1 ).
|
||||
/// </summary>
|
||||
public bool IsCurrentChargeLow => this.CurrentChargeBars == "1" || this.CurrentChargeBars == "0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current charge level.
|
||||
/// </summary>
|
||||
public string MaxChargeBars => Bike.Drive.Type == Model.Bikes.BikeInfoNS.DriveNS.DriveType.Pedelec
|
||||
? Bike.Drive.Battery.MaxChargeBars?.ToString() ?? string.Empty
|
||||
: string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Returns status of a bike as text (binds to GUI).
|
||||
/// </summary>
|
||||
/// <todo> Log invalid states for diagnose purposes.</todo>
|
||||
public string StateText
|
||||
|
@ -195,25 +226,28 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
{
|
||||
switch (Bike.State.Value)
|
||||
{
|
||||
case InUseStateEnum.FeedbackPending:
|
||||
return AppResources.StatusTextFeedbackPending;
|
||||
|
||||
case InUseStateEnum.Disposable:
|
||||
return AppResources.StatusTextAvailable;
|
||||
}
|
||||
|
||||
|
||||
if (!ActiveUser.IsLoggedIn)
|
||||
{
|
||||
// Nobody is logged in.
|
||||
switch (Bike.State.Value)
|
||||
{
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
return GetReservedInfo(
|
||||
Bike.State.RemainingTime,
|
||||
Bike.StationId,
|
||||
Bike.State.RemainingTime,
|
||||
Bike.StationId,
|
||||
null); // Hide reservation code because no one but active user should see code
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
return GetBookedInfo(
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
null); // Hide reservation code because no one but active user should see code
|
||||
|
||||
default:
|
||||
|
@ -226,16 +260,16 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
case InUseStateEnum.Reserved:
|
||||
return Bike.State.MailAddress == ActiveUser.Mail
|
||||
? GetReservedInfo(
|
||||
Bike.State.RemainingTime,
|
||||
Bike.State.RemainingTime,
|
||||
Bike.StationId,
|
||||
Bike.State.Code)
|
||||
: "Fahrrad bereits reserviert durch anderen Nutzer.";
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
case InUseStateEnum.Booked:
|
||||
return Bike.State.MailAddress == ActiveUser.Mail
|
||||
? GetBookedInfo(
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
Bike.State.From,
|
||||
Bike.StationId,
|
||||
Bike.State.Code)
|
||||
: "Fahrrad bereits gebucht durch anderen Nutzer.";
|
||||
|
||||
|
@ -296,7 +330,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
return Color.Default;
|
||||
}
|
||||
|
||||
var l_oSelectedBikeState = Bike.State;
|
||||
var l_oSelectedBikeState = Bike.State;
|
||||
switch (l_oSelectedBikeState.Value)
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
|
@ -337,7 +371,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
return;
|
||||
}
|
||||
|
||||
OpenUrlInBrowser(url);
|
||||
OpenUrlInBrowser(url);
|
||||
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
|
@ -362,7 +396,8 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
return matches.Count > 0
|
||||
? matches[0].Value
|
||||
: string.Empty;
|
||||
} catch (Exception e)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.ForContext<BikeViewModelBase>().Error("Extracting URL failed. {Exception}", e);
|
||||
return string.Empty;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
{
|
||||
|
@ -23,13 +23,13 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
Model.Bike.BC.BikeInfoMutable bikeInfo,
|
||||
Model.Bikes.BikeInfoNS.BC.BikeInfoMutable bikeInfo,
|
||||
IUser activeUser,
|
||||
IInUseStateInfoProvider stateInfoProvider,
|
||||
IBikesViewModel bikesViewModel,
|
||||
Action<string> openUrlInBrowser)
|
||||
{
|
||||
if (bikeInfo is Model.Bike.BluetoothLock.BikeInfoMutable)
|
||||
if (bikeInfo is Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable)
|
||||
{
|
||||
return new BluetoothLock.BikeViewModel(
|
||||
isConnectedDelegate,
|
||||
|
@ -40,18 +40,17 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikeInfo as Model.Bike.BluetoothLock.BikeInfoMutable,
|
||||
bikeInfo as Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable,
|
||||
activeUser,
|
||||
stateInfoProvider,
|
||||
bikesViewModel,
|
||||
openUrlInBrowser);
|
||||
}
|
||||
if (bikeInfo is Model.Bike.CopriLock.BikeInfoMutable)
|
||||
}
|
||||
if (bikeInfo is Model.Bikes.BikeInfoNS.CopriLock.BikeInfoMutable)
|
||||
{
|
||||
return new CopriLock.BikeViewModel(
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
bikeRemoveDelegate,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using BikeInfoMutable = TINK.Model.Bike.BluetoothLock.BikeInfoMutable;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
|
@ -186,7 +186,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
|
||||
RaisePropertyChangedEvent(
|
||||
lastHandler,
|
||||
lastStateText,
|
||||
lastStateText,
|
||||
lastStateColor);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
using System;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable>
|
||||
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs the reqest handler base.
|
||||
|
@ -18,7 +17,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public Base(
|
||||
Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable selectedBike,
|
||||
Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike,
|
||||
string buttonText,
|
||||
bool isCopriButtonVisible,
|
||||
Func<bool> isConnectedDelegate,
|
||||
|
@ -42,9 +41,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
protected ILocksService LockService { get; }
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public abstract override InUseStateEnum State { get; }
|
||||
|
||||
public string LockitButtonText { get; protected set; }
|
||||
|
||||
public bool IsLockitButtonVisible { get; protected set; }
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -36,10 +34,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
selectedBike,
|
||||
AppResources.ActionReturn, // Copri button text "Miete beenden"
|
||||
true, // Show button to enabled returning of bike.
|
||||
isConnectedDelegate,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
|
@ -53,9 +51,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReturnBike();
|
||||
|
||||
|
@ -223,14 +218,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedClosed>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -253,12 +248,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, bookingFinished?.Co2Saving);
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -34,8 +32,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IUser activeUser) :
|
||||
base(
|
||||
selectedBike,
|
||||
nameof(BookedDisconnected),
|
||||
false,
|
||||
nameof(BookedDisconnected),
|
||||
false,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
|
@ -47,21 +45,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true;
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Scan for lock.</summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLock();
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<BookedDisconnected>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
|
@ -113,7 +108,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
@ -180,7 +175,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (retryCount < 2)
|
||||
{
|
||||
message = AppResources.ErrorBookedSearchMessage;
|
||||
}
|
||||
}
|
||||
else if (retryCount < 3)
|
||||
{
|
||||
message = AppResources.ErrorBookedSearchMessageEscalationLevel1;
|
||||
|
@ -218,7 +213,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
Log.ForContext<BookedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageConnectLockErrorTitle,
|
||||
$"Schlossstatus des gemieteten Rads konnte nicht ermittelt werden.",
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.Logging;
|
||||
using TINK.View;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -35,10 +34,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
selectedBike,
|
||||
AppResources.ActionCloseAndReturn, // Copri button text: "Schloss schließen & Miete beenden"
|
||||
true, // Show button to allow user to return bike.
|
||||
isConnectedDelegate,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
|
@ -52,9 +51,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show button to allow user to lock bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockAndReturnBike();
|
||||
|
||||
|
@ -96,13 +92,13 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (result == false)
|
||||
{
|
||||
// User aborted closing and returning bike process
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
|
||||
|
@ -124,17 +120,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
// Unlock bike.
|
||||
// Start of closing lock and returing bike sequence.
|
||||
Log.ForContext<BookedOpen>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Clear logging memory sink to avoid passing log data not related to returning of bike to backend.
|
||||
// Log data is passed to backend when calling CopriCallsHttps.DoReturn().
|
||||
MemoryStackSink.ClearMessages();
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Notify COPRI about start reaturning bike
|
||||
// Notify COPRI about start returning bike sequence: "request=booking_update ... &lock_state=locking ..."
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartReturningBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.StartReturningBike(
|
||||
|
@ -159,7 +158,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -176,7 +175,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())
|
||||
?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -189,6 +189,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
|
@ -256,7 +257,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
||||
// Check locking state.
|
||||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
|
||||
|
@ -266,7 +268,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
// Notify COPRI about closing failure: "request=booking_update ... &lock_state=unlocked ..."
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
|
@ -280,7 +285,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
@ -289,7 +294,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask});
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask });
|
||||
}
|
||||
catch (Exception innerExWhenAll)
|
||||
{
|
||||
|
@ -305,6 +310,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geolocation information.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
|
@ -332,9 +338,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
// Notify COPRI about end of rental: "request=booking_update ... "&state=available" ... &lock_state=locked ..."
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
|
@ -343,14 +348,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null,
|
||||
SmartDevice);
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
|
@ -406,8 +411,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
@ -418,14 +423,14 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -436,12 +441,20 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(
|
||||
SelectedBike.Drive?.Battery,
|
||||
bookingFinished?.Co2Saving);
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
@ -491,6 +504,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Clear logging memory sink to avoid passing log data not related to returning of bike to backend.
|
||||
// Log data is passed to backend when calling CopriCallsHttps.DoReturn().
|
||||
MemoryStackSink.ClearMessages();
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
|
@ -611,12 +628,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
@ -649,6 +666,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -52,9 +50,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
using System;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -49,9 +48,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReserveBookAndOpen();
|
||||
|
||||
|
@ -84,7 +80,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
|
@ -111,18 +107,18 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableDisconnected>().Information("User selected availalbe bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Reservieren des Rads!",
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableDisconnected>().Error("User selected availalbe bike {bike} but reserving failed. {@l_oException}", SelectedBike, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Reservieren des Rads!",
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageReservingBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
|
@ -138,9 +134,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextSearchingLock;
|
||||
try
|
||||
{
|
||||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(1));
|
||||
result = await LockService.ConnectAsync(
|
||||
new LockInfoAuthTdo.Builder { Id = SelectedBike.LockInfo.Id, Guid = SelectedBike.LockInfo.Guid, K_seed = SelectedBike.LockInfo.Seed, K_u = SelectedBike.LockInfo.UserKey }.Build(),
|
||||
LockService.TimeOut.GetSingleConnect(1));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
@ -254,6 +250,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -331,7 +328,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -58,13 +56,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false;
|
||||
}
|
||||
|
||||
/// <summary>Books bike by reserving bike, opening lock and booking bike.</summary>
|
||||
/// <returns>Next request handler.</returns>
|
||||
|
@ -196,6 +191,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
// Notify corpi about unlock action in order to start booking.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoBook(SelectedBike);
|
||||
|
@ -219,8 +215,8 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<DisposableOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedTwoLines, l_oException.Message),
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedTwoLines, l_oException.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
|
@ -18,11 +18,9 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
|
||||
State = copriState;
|
||||
|
||||
ErrorText = errorText;
|
||||
|
||||
Log.Error($"{errorText}. Copri state is {State} and lock state is {lockingState}.");
|
||||
Log.Error($"{errorText}. Copri state is {copriState} and lock state is {lockingState}.");
|
||||
}
|
||||
|
||||
/// <summary>View model to be used for progress report and unlocking/ locking view.</summary>
|
||||
|
@ -34,10 +32,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
public bool IsConnected => false;
|
||||
|
||||
public InUseStateEnum State { get; }
|
||||
|
||||
private LockingState LockingState { get; }
|
||||
|
||||
public bool IsButtonVisible => false;
|
||||
|
||||
public string ButtonText => GetType().Name;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel)
|
||||
{
|
||||
State = state;
|
||||
ButtonText = BC.StateToText.GetActionText(state);
|
||||
ViewService = viewService;
|
||||
BikesViewModel = bikesViewModel
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null.");
|
||||
|
@ -23,13 +23,11 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <summary>View model to be used for progress report and unlocking/ locking view.</summary>
|
||||
public IBikesViewModel BikesViewModel { get; }
|
||||
|
||||
public InUseStateEnum State { get; }
|
||||
|
||||
public bool IsButtonVisible => true;
|
||||
|
||||
public bool IsLockitButtonVisible => false;
|
||||
|
||||
public string ButtonText => BC.StateToText.GetActionText(State);
|
||||
public string ButtonText { get; private set; }
|
||||
|
||||
public string LockitButtonText => GetType().Name;
|
||||
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -41,7 +39,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
selectedBike,
|
||||
AppResources.ActionCancelRequest, // Copri button text: "Reservierung abbrechen"
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
|
@ -55,9 +53,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
|
@ -75,7 +70,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
|
@ -106,33 +101,33 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Aufheben der Reservierung!",
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Aufheben der Reservierung!",
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, l_oException);
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Aufheben der Reservierung!",
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -203,7 +198,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -220,6 +215,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -297,7 +293,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -49,9 +47,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
|
@ -100,21 +95,27 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", l_oException.Message, "OK");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedDisconnected>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Aufheben der Reservierung!",
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, l_oException);
|
||||
await ViewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", l_oException.Message, "OK");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
|
@ -178,7 +179,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
@ -252,7 +253,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
message = AppResources.ErrorReservedSearchMessage;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
message = AppResources.ErrorReservedSearchMessageEscalationLevel1;
|
||||
}
|
||||
|
@ -263,7 +264,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
message,
|
||||
"", // bool IsReportLevelVerbose ? exception.Message : string.Empty, // or use ActiveUser.DebugLevel.HasFlag(Permissions.ReportLevel) instead?
|
||||
AppResources.MessageAnswerRetry,
|
||||
AppResources.MessageAnswerCancel );
|
||||
AppResources.MessageAnswerCancel);
|
||||
}
|
||||
|
||||
if (continueConnect)
|
||||
|
@ -283,7 +284,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
if (result?.State == null)
|
||||
{
|
||||
Log.ForContext<ReservedDisconnected>().Information("Lock for bike {bike} not found.", SelectedBike);
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorConnectTitle,
|
||||
|
@ -376,6 +377,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -453,7 +455,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -22,7 +20,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
/// This should never during ILOCKIT is connected to app because
|
||||
/// - manually opening lock is not possible when lock is connected
|
||||
/// - two devices can not simultaneously conect to same lock.
|
||||
public class ReservedOpen : Base, IRequestHandler
|
||||
public class ReservedOpen : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
|
@ -38,7 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
"Rad zurückgeben oder mieten",
|
||||
"Rad zurückgeben oder mieten",
|
||||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
|
@ -54,9 +52,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = activeUser.DebugLevel > 0; // Will be visible in future version of user with leveraged privileges.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockOrDoBook();
|
||||
|
||||
|
@ -131,7 +126,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
Log.ForContext<ReservedOpen>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Format(AppResources.MessageErrorLockIsClosedTwoLines, l_oException.Message),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -222,7 +217,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -244,26 +239,32 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", l_oException.Message, "OK");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (l_oException is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedOpen>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Aufheben der Reservierung!",
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedOpen>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, l_oException);
|
||||
await ViewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", l_oException.Message, "OK");
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
@ -396,9 +397,10 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
}
|
||||
finally
|
||||
{
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
@ -52,9 +50,6 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
using System;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using Serilog;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <returns>Request handler.</returns>
|
||||
public static IRequestHandler Create(
|
||||
Model.Bikes.Bike.BC.IBikeInfoMutable selectedBike,
|
||||
Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
|
@ -36,7 +36,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
|
|||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser)
|
||||
{
|
||||
if (!(selectedBike is Model.Bikes.Bike.BluetoothLock.IBikeInfoMutable selectedBluetoothLockBike))
|
||||
if (!(selectedBike is Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBluetoothLockBike))
|
||||
return null;
|
||||
|
||||
switch (selectedBluetoothLockBike.State.Value)
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using BikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfoMutable;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
||||
{
|
||||
|
@ -25,8 +23,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
/// <summary> Notifies GUI about changes. </summary>
|
||||
public override event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private IGeolocation Geolocation { get; }
|
||||
|
||||
/// <summary> Holds object which manages requests. </summary>
|
||||
private IRequestHandler RequestHandler { get; set; }
|
||||
|
||||
|
@ -87,7 +83,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
public BikeViewModel(
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Action<string> bikeRemoveDelegate,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
|
@ -103,7 +98,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
selectedBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
|
@ -113,9 +107,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
selectedBike.State.Value,
|
||||
viewService,
|
||||
bikesViewModel);
|
||||
|
||||
Geolocation = geolocation
|
||||
?? throw new ArgumentException($"Can not instantiate {this.GetType().Name}-object. Parameter {nameof(geolocation)} can not be null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -130,7 +121,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
Bike,
|
||||
IsConnectedDelegate,
|
||||
ConnectorFactory,
|
||||
Geolocation,
|
||||
ViewUpdateManager,
|
||||
SmartDevice,
|
||||
ViewService,
|
||||
|
@ -153,10 +143,12 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
public string LockitButtonText => RequestHandler.LockitButtonText;
|
||||
|
||||
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
|
||||
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1()));
|
||||
public System.Windows.Input.ICommand OnButtonClicked =>
|
||||
new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1()));
|
||||
|
||||
/// <summary> Processes request to perform a ILockIt action (unlock bike and lock bike). </summary>
|
||||
public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2()));
|
||||
public System.Windows.Input.ICommand OnLockitButtonClicked =>
|
||||
new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2()));
|
||||
|
||||
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
|
||||
private async Task ClickButton(Task<IRequestHandler> handleRequest)
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
using System;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.Bike.CopriLock.IBikeInfoMutable>
|
||||
public abstract class Base : BC.RequestHandler.Base<Model.Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs the reqest handler base.
|
||||
|
@ -17,28 +15,19 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public Base(
|
||||
Model.Bikes.Bike.CopriLock.IBikeInfoMutable selectedBike,
|
||||
Model.Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable selectedBike,
|
||||
string buttonText,
|
||||
bool isCopriButtonVisible,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(selectedBike, buttonText, isCopriButtonVisible, isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser)
|
||||
{
|
||||
Geolocation = geolocation
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}-object. Parameter {nameof(geolocation)} must not be null.");
|
||||
}
|
||||
|
||||
protected IGeolocation Geolocation { get; }
|
||||
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public abstract override InUseStateEnum State { get; }
|
||||
|
||||
public string LockitButtonText { get; protected set; }
|
||||
|
||||
public bool IsLockitButtonVisible { get; protected set; }
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Services.CopriLock.Exception;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
|
@ -25,18 +21,16 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionReturn, // Copri button text "Miete beenden"
|
||||
true, // Show button to enabled returning of bike.
|
||||
isConnectedDelegate,
|
||||
selectedBike,
|
||||
nameof(BookedClosed),
|
||||
false, // Lock can only be closed manually and returning is performed by placing bike into the station.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
|
@ -47,173 +41,20 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await ReturnBike();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLock();
|
||||
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> ReturnBike()
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted returning bike process
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {l_oId} in order to return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<BookedClosed>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = "Returning bike...";
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(SelectedBike);
|
||||
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returning failed. COPRI returned an not at station error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockClosedNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedClosed>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
Log.ForContext<BookedClosed>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
/// <summary> Open bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
|
@ -225,6 +66,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.OpenLockAsync(SelectedBike);
|
||||
|
@ -233,33 +75,17 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedClosed>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageOpeningLockErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
AppResources.ErrorOpenLockBoldWasBlockedMessage,
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
@ -267,20 +93,14 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
"OK");
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// When bold is blocked lock is still closed even if exception occurres.
|
||||
// In all other cases state is supposed to be unknown. Example: Lock is out of reach and no more bluetooth connected.
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedClosed>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
|
@ -288,7 +108,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.CopriLock;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class BookedDefault : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedDefault(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) :
|
||||
base(
|
||||
selectedBike,
|
||||
nameof(BookedDefault),
|
||||
false,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionSearchLock;
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Scan for lock.</summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await ConnectLock();
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<BookedDefault>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary> Scan for lock.</summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> ConnectLock()
|
||||
{
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedDefault>().Information("Request to search {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before getting new auth-values.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
var bikesInfo = (await ConnectorFactory(IsConnected).Query.GetBikesOccupiedAsync())?.Response?.Where(bike => bike.Id == SelectedBike.Id).ToArray();
|
||||
|
||||
var bikeInfo = bikesInfo.Length > 0 ? bikesInfo[0] as BikeInfo : null;
|
||||
SelectedBike.LockInfo.State = bikeInfo != null ? bikeInfo.LockInfo.State: SelectedBike.LockInfo.State;
|
||||
|
||||
Log.ForContext<BookedDefault>().Information($"State for bike {SelectedBike.Id} updated successfully. Value is {SelectedBike.LockInfo.State}.");
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,13 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Services.CopriLock.Exception;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
|
@ -25,358 +22,94 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Services.Geolocation.IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseAndReturn, // Copri button text: "Schloss schließen & Miete beenden"
|
||||
true, // Show button to allow user to return bike.
|
||||
nameof(BookedOpen),
|
||||
false, // Lock can only be closed manually and returning is performed by placing bike into the station.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".
|
||||
IsLockitButtonVisible = true; // Show button to allow user to lock bike.
|
||||
LockitButtonText = AppResources.ActionOpenAndPause; // Lock is open but show button anyway to be less prone to errors.
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockAndReturnBike();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await OpenLock();
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> CloseLockAndReturnBike()
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
// Prevent concurrent interaction
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted closing and returning bike process
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
Log.ForContext<BookedOpen>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary> Open bike. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedOpen>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
Log.ForContext<BookedOpen>().Information("User request to unlock bike {bike}. For locking state {state} this request is unexpected.", SelectedBike, SelectedBike?.LockInfo?.State);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.ReturnAndCloseAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
smartDevice: SmartDevice);
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
await ConnectorFactory(IsConnected).Command.OpenLockAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
Log.ForContext<BookedOpen>().Information("User selected bike {id} but opening lock failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.MessageOpeningLockErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.CloseLockAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
using System;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
|
@ -23,7 +22,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
|
@ -31,30 +29,26 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // Button text: "Schloss öffnen & Rad mieten"
|
||||
true, // Show copri button to enable reserving and opening
|
||||
true, // Show copri button to enable booking and opening
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
LockitButtonText = AppResources.ActionRequest; // Copri text: "Rad reservieren"
|
||||
IsLockitButtonVisible = true;
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await BookAndOpen();
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await BookAndRelease();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await Reserve();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> BookAndOpen()
|
||||
/// <summary>Book bike and open lock.</summary>
|
||||
public async Task<IRequestHandler> BookAndRelease()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
|
@ -68,52 +62,141 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {bike} in order to reserve but did deny to book bike.", SelectedBike);
|
||||
Log.ForContext<DisposableClosed>().Information("User selected bike {bike} in order to book and release bike from station but action was cancelled.", SelectedBike);
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {bike} in order to book.", SelectedBike);
|
||||
// Book and release bike from station.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected bike {bike} in order to book and release bike from station.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Book bike prior to opening lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextRentingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.BookAndOpenAync(SelectedBike);
|
||||
await ConnectorFactory(IsConnected).Command.BookAndOpenAync(SelectedBike);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected recently requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<DisposableClosed>().Information("User selected bike {id} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableClosed>().Error("User selected recently requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<DisposableClosed>().Error("User selected bike {id} but booking failed. {@exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
string.Empty,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("User booked and released bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary>Reserve bike.</summary>
|
||||
public async Task<IRequestHandler> Reserve()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really reserve bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposableClosed>().Information("User selected availalbe bike {bike} in order to reserve but action was canceled.", SelectedBike);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("Request to reserve for bike {bike} detected.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposableClosed>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposableClosed>().Information("User selected availalbe bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageReservingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposableClosed>().Error("User selected availalbe bike {bike} but reserving failed. {@exception}", SelectedBike, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageReservingBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<DisposableClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
|
@ -121,15 +204,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<DisposableClosed>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
using System;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class DisposabledAway : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public DisposabledAway(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionRequest, // Copri text: "Rad reservieren"
|
||||
true, // Show copri button to enable reserving and opening
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = GetType().Name;
|
||||
IsLockitButtonVisible = false; // If bike is not reserved/ booked app can not connect to lock
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await Reserve();
|
||||
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await UnsupportedRequest();
|
||||
|
||||
/// <summary>Reserve bike and connect to lock.</summary>
|
||||
public async Task<IRequestHandler> Reserve()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Ask whether to really reserve bike?
|
||||
var alertResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), StateRequestedInfo.MaximumReserveTime.Minutes),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (alertResult == false)
|
||||
{
|
||||
// User aborted booking process
|
||||
Log.ForContext<DisposabledAway>().Information("User selected availalbe bike {bike} in order to reserve but action was canceled.", SelectedBike);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
Log.ForContext<DisposabledAway>().Information("Request to book and open lock for bike {bike} detected.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
||||
// Stop polling before requesting bike.
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReservingBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoReserve(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is BookingDeclinedException)
|
||||
{
|
||||
// Too many bikes booked.
|
||||
Log.ForContext<DisposabledAway>().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageReservationBikeErrorTooManyReservationsRentals, SelectedBike.Id, (exception as BookingDeclinedException).MaxBikesCount),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<DisposabledAway>().Information("User selected availalbe bike {bike} but reserving failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Reservieren des Rads!",
|
||||
string.Format("{0}\r\n{1}", exception.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<DisposabledAway>().Error("User selected availalbe bike {bike} but reserving failed. {@l_oException}", SelectedBike, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Reservieren des Rads!",
|
||||
exception.Message,
|
||||
"OK");
|
||||
}
|
||||
|
||||
// Restart polling again.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<DisposabledAway>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<DisposabledAway>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
using IRequestHandler = BluetoothLock.IRequestHandler;
|
||||
|
||||
public class FeedbackPending : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public FeedbackPending(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
nameof(FeedbackPending),
|
||||
false, // No other action but give feedback allowed.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionGiveFeedback;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Return bike. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await UnsupportedRequest();
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await GiveFeedback();
|
||||
|
||||
/// <summary> Requst is not supported, button should be disabled. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IRequestHandler> UnsupportedRequest()
|
||||
{
|
||||
Log.ForContext<BookedClosed>().Error("Click of unsupported button click detected.");
|
||||
return await Task.FromResult<IRequestHandler>(this);
|
||||
}
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> GiveFeedback()
|
||||
{
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(SelectedBike.Drive?.Battery, SelectedBike?.BookingFinishedModel?.Co2Saving);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextSubmittingFeedback;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto
|
||||
{
|
||||
BikeId = SelectedBike.Id,
|
||||
CurrentChargeBars = feedback.CurrentChargeBars,
|
||||
IsBikeBroken = feedback.IsBikeBroken,
|
||||
Message = feedback.Message
|
||||
},
|
||||
SelectedBike?.OperatorUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<FeedbackPending>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<FeedbackPending>().Error("Submitting feedback for bike {bike} failed. {@Exception}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Feedback was given successfully.
|
||||
// Set state from FeedbackPending to Available.
|
||||
SelectedBike.State.Load(Model.State.InUseStateEnum.Disposable);
|
||||
|
||||
if (SelectedBike?.BookingFinishedModel?.MiniSurvey?.Questions?.Count > 0)
|
||||
{
|
||||
// No need to restrart polling again because different page is shown.
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
using TINK.Services.CopriLock.Exception;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
||||
{
|
||||
|
@ -29,7 +27,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
Services.Geolocation.IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
|
@ -40,7 +37,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
true, // Show button to enable canceling reservation.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
|
@ -51,9 +47,6 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
IsLockitButtonVisible = true; // Show "Öffnen" button to enable unlocking
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Cancel reservation. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CancelReservation();
|
||||
|
||||
|
@ -65,21 +58,21 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
{
|
||||
BikesViewModel.IsIdle = false; // Lock list to avoid multiple taps while copri action is pending.
|
||||
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
var result = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCancelReservation, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
if (result == false)
|
||||
{
|
||||
// User aborted cancel process
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {Id} in order to cancel reservation but action was canceled.", SelectedBike.Id);
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {l_oId} in order to cancel reservation.", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Information("User selected reserved bike {Id} in order to cancel reservation.", SelectedBike.Id);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -94,53 +87,53 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is InvalidAuthorizationResponseException)
|
||||
if (exception is InvalidAuthorizationResponseException)
|
||||
{
|
||||
// Copri response is invalid.
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Aufheben der Reservierung!",
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (l_oException is WebConnectFailureException)
|
||||
else if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {l_oId} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAlert(
|
||||
"Verbingungsfehler beim Aufheben der Reservierung!",
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
"OK");
|
||||
Log.ForContext<BikesViewModel>().Information("User selected reserved bike {Id} but cancel reservation failed (Copri server not reachable).", SelectedBike.Id);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageCancelReservationBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {l_oId} but cancel reservation failed. {@l_oException}.", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<BikesViewModel>().Error("User selected reserved bike {Id} but cancel reservation failed. {@Exception}.", SelectedBike.Id, exception);
|
||||
await ViewService.DisplayAlert(
|
||||
"Fehler beim Aufheben der Reservierung!",
|
||||
l_oException.Message,
|
||||
"OK");
|
||||
AppResources.MessageCancelReservationBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = "";
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {l_oId} successfully.", SelectedBike.Id);
|
||||
|
||||
Log.ForContext<BikesViewModel>().Information("User canceled reservation of bike {Id} successfully.", SelectedBike.Id);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Open lock and book bike. </summary>
|
||||
|
@ -163,7 +156,7 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
return this;
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {bike} in order to book.", SelectedBike);
|
||||
|
||||
// Stop polling before cancel request.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -176,49 +169,45 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
|
|||
{
|
||||
await ConnectorFactory(IsConnected).Command.BookAndOpenAync(SelectedBike);
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (l_oException is WebConnectFailureException)
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {l_oId} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
Log.ForContext<ReservedClosed>().Information("User selected requested bike {Id} but booking failed (Copri server not reachable).", SelectedBike.Id);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorConnectionTitle,
|
||||
WebConnectFailureException.GetHintToPossibleExceptionsReasons,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {l_oId} but reserving failed. {@l_oException}", SelectedBike.Id, l_oException);
|
||||
Log.ForContext<ReservedClosed>().Error("User selected requested bike {Id} but reserving failed. {@Exception}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageRentingBikeErrorGeneralTitle,
|
||||
exception.Message,
|
||||
string.Empty,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
Log.ForContext<ReservedClosed>().Information("User reserved bike {bike} successfully.", SelectedBike);
|
||||
Log.ForContext<ReservedClosed>().Information("User booked and opened bike {bike} successfully.", SelectedBike.Id);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
using System;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler;
|
||||
using TINK.Model.User;
|
||||
using Serilog;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Bike.CopriLock;
|
||||
using TINK.Model.Bikes.Bike.CopriLock;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
||||
{
|
||||
|
@ -24,112 +22,73 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
/// <returns>Request handler.</returns>
|
||||
public static BluetoothLock.IRequestHandler Create(
|
||||
Model.Bikes.Bike.BC.IBikeInfoMutable selectedBike,
|
||||
Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser)
|
||||
{
|
||||
if (!(selectedBike is BikeInfoMutable selectedBluetoothLockBike))
|
||||
return null;
|
||||
|
||||
switch (selectedBluetoothLockBike.State.Value)
|
||||
if (!(selectedBike is IBikeInfoMutable selectedCopriLock))
|
||||
{
|
||||
Log.Error("Bike of unexpected type {type} detected.", selectedBike?.GetType());
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (selectedCopriLock.State.Value)
|
||||
{
|
||||
// User has rented this bike before. Bike is now available but feedback was not yet given.
|
||||
case Model.State.InUseStateEnum.FeedbackPending:
|
||||
|
||||
return new FeedbackPending(
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
case Model.State.InUseStateEnum.Disposable:
|
||||
|
||||
// Bike is reserved, selecte action depending on lock state.
|
||||
switch (selectedBluetoothLockBike.LockInfo.State)
|
||||
{
|
||||
case LockingState.Closed:
|
||||
// Unexepected state detected.
|
||||
// This state is unexpected because connection is closed
|
||||
// - when reservation is canceled or
|
||||
// - when bike is returned.
|
||||
Log.Error("Unexpected state {BookingState}/ {LockingState} detected.", selectedBluetoothLockBike.State.Value, selectedBluetoothLockBike.LockInfo.State);
|
||||
return new DisposableClosed(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
default:
|
||||
return new DisposabledAway(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
}
|
||||
return new DisposableClosed(
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
case Model.State.InUseStateEnum.Reserved:
|
||||
|
||||
// Bike is reserved, selecte action depending on lock state.
|
||||
switch (selectedBluetoothLockBike.LockInfo.State)
|
||||
{
|
||||
case LockingState.Closed:
|
||||
// Lock could not be opened after reserving bike.
|
||||
return new ReservedClosed(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
default:
|
||||
return new ReservedClosed(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
}
|
||||
// Lock could not be opened after reserving bike.
|
||||
return new ReservedClosed(
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
case Model.State.InUseStateEnum.Booked:
|
||||
|
||||
// Bike is booked, selecte action depending on lock state.
|
||||
switch (selectedBluetoothLockBike.LockInfo.State)
|
||||
var lockState = selectedCopriLock.LockInfo.State;
|
||||
switch (lockState)
|
||||
{
|
||||
case LockingState.Closed:
|
||||
// Ride was paused.
|
||||
// Frame lock is closed. User paused ride and closed lock.
|
||||
return new BookedClosed(
|
||||
selectedBluetoothLockBike,
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
case LockingState.Open:
|
||||
// User wants to return bike/ pause ride.
|
||||
return new BookedOpen(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
|
@ -137,26 +96,28 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
|
|||
activeUser);
|
||||
|
||||
default:
|
||||
return new BookedDefault(
|
||||
selectedBluetoothLockBike,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
if (lockState != LockingState.Open)
|
||||
Log.Error("Unexpected locking {LockingState} of a rented bike with copri lock bike detected.", selectedCopriLock.LockInfo.State);
|
||||
|
||||
return new BookedOpen(
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser);
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
// Unexpected copri state detected.
|
||||
Log.Error("Unexpected locking {BookingState}/ {LockingState} detected.", selectedBluetoothLockBike.State.Value, selectedBluetoothLockBike.LockInfo.State);
|
||||
return new DisposabledAway(
|
||||
selectedBluetoothLockBike,
|
||||
Log.Error("Unexpected locking {BookingState}/ {LockingState} of a copri lock bike detected.", selectedCopriLock.State.Value, selectedCopriLock.LockInfo.State);
|
||||
return new DisposableClosed(
|
||||
selectedCopriLock,
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using TINK.Model.State;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
{
|
||||
/// <summary>
|
||||
/// Base interface for Copri and ILockIt request handler.
|
||||
|
@ -8,9 +6,6 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// </summary>
|
||||
public interface IRequestHandlerBase
|
||||
{
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
InUseStateEnum State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the copri button which is managed by request handler is visible or not.
|
||||
/// </summary>
|
||||
|
@ -28,6 +23,6 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
bool IsConnected { get; }
|
||||
|
||||
/// <summary> Gets if the bike has to be remvoed after action has been completed. </summary>
|
||||
bool IsRemoveBikeRequired { get; }
|
||||
bool IsRemoveBikeRequired { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using TINK.Model.Bikes.Bike;
|
||||
using TINK.Model.Bikes.BikeInfoNS;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
? new ObservableCollection<string>(tariff.InfoEntries.OrderBy(x => x.Key).Where(x => x.Value.Key != AGBKEY).Select(x => x.Value.Value))
|
||||
: new ObservableCollection<string>();
|
||||
|
||||
OperatorAgb = tariff?.InfoEntries != null
|
||||
OperatorAgb = tariff?.InfoEntries != null
|
||||
&& tariff.InfoEntries.Select(x => x.Value.Key).Contains(AGBKEY)
|
||||
? tariff?.InfoEntries.FirstOrDefault(x => x.Value.Key == AGBKEY).Value.Value
|
||||
: string.Empty;
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.User;
|
||||
using TINK.Services.Permissions;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes.Bike;
|
||||
using TINK.ViewModel.Bikes.Bike.BC;
|
||||
using TINK.Services.Permissions;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Device;
|
||||
|
||||
namespace TINK.ViewModel.Bikes
|
||||
{
|
||||
|
@ -23,7 +22,7 @@ namespace TINK.ViewModel.Bikes
|
|||
{
|
||||
/// <summary> Provides info about the smart device (phone, tablet, ...).</summary>
|
||||
protected ISmartDevice SmartDevice;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reference on view service to show modal notifications and to perform navigation.
|
||||
/// </summary>
|
||||
|
@ -111,7 +110,7 @@ namespace TINK.ViewModel.Bikes
|
|||
{
|
||||
User = user
|
||||
?? throw new ArgumentException("Can not instantiate bikes page view model- object. No user available.");
|
||||
|
||||
|
||||
RuntimePlatform = runtimPlatform
|
||||
?? throw new ArgumentException("Can not instantiate bikes page view model- object. No runtime platform information available.");
|
||||
|
||||
|
@ -343,11 +342,11 @@ namespace TINK.ViewModel.Bikes
|
|||
if (statusInfoText == StatusInfoText)
|
||||
{
|
||||
// Nothing to do because value did not change.
|
||||
Log.ForContext<BikeViewModel>().Debug($"Property {nameof(ActionText)} set to value \"{actionText}\" but {nameof(StatusInfoText)} did not change.");
|
||||
Log.ForContext<BikesViewModel>().Debug($"Property {nameof(ActionText)} set to value \"{actionText}\" but {nameof(StatusInfoText)} did not change.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.ForContext<BikeViewModel>().Debug($"Property {nameof(ActionText)} set to value \"{actionText}\" .");
|
||||
Log.ForContext<BikesViewModel>().Debug($"Property {nameof(ActionText)} set to value \"{actionText}\" .");
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(StatusInfoText)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
using TINK.Model.Bike;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using TINK.Model.User;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Settings;
|
||||
using System;
|
||||
using Serilog;
|
||||
using System.Threading;
|
||||
using TINK.Model;
|
||||
using TINK.View;
|
||||
using Xamarin.Forms;
|
||||
using System.Linq;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.Permissions;
|
||||
using TINK.Model.Station;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.Permissions;
|
||||
using TINK.Settings;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.BikesAtStation
|
||||
{
|
||||
|
@ -34,21 +34,19 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
/// <summary>
|
||||
/// Holds the selected station;
|
||||
/// </summary>
|
||||
private readonly IStation m_oStation;
|
||||
private IStation Station { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs bike collection view model.
|
||||
/// </summary>
|
||||
/// <param name="user">Mail address of active user.</param>
|
||||
/// <param name="isReportLevelVerbose">True if report level is verbose, false if not.</param>
|
||||
/// <param name="permissions">Holds object to query location permisions.</param>
|
||||
/// <param name="bluetoothLE">Holds object to query bluetooth state.</param>
|
||||
/// <param name="runtimPlatform">Specifies on which platform code is run.</param>
|
||||
/// <param name="isConnectedDelegate">Returns if mobile is connected to web or not.</param>
|
||||
/// <param name="p_oConnector">Connects system to copri.</param>
|
||||
/// <param name="connectorFactory">Connects system to copri.</param>
|
||||
/// <param name="lockService">Service to control lock retrieve info.</param>
|
||||
/// <param name="polling"> Holds whether to poll or not and the periode leght is polling is on. </param>
|
||||
/// <param name="l_oBikesAll">All bikes at given station.</param>
|
||||
/// <param name="openUrlInExternalBrowser">Action to open an external browser.</param>
|
||||
/// <param name="postAction">Executes actions on GUI thread.</param>
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>
|
||||
|
@ -67,17 +65,15 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
Action<string> openUrlInExternalBrowser,
|
||||
Action<SendOrPostCallback, object> postAction,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, openUrlInExternalBrowser, () => new BikeAtStationInUseStateInfoProvider())
|
||||
IViewService viewService) : base(user, permissions, bluetoothLE, runtimPlatform, isConnectedDelegate, connectorFactory, geolocation, lockService, polling, postAction, smartDevice, viewService, openUrlInExternalBrowser, () => new BikeAtStationInUseStateInfoProvider())
|
||||
{
|
||||
m_oStation = selectedStation;
|
||||
Station = selectedStation ?? new NullStation();
|
||||
|
||||
Title = string.Format(m_oStation?.StationName != null
|
||||
? m_oStation.StationName
|
||||
: string.Empty);
|
||||
|
||||
StationDetailText = string.Format(m_oStation?.Id != null
|
||||
? string.Format(AppResources.MarkingBikesAtStationStationId, m_oStation.Id)
|
||||
: string.Empty); ;
|
||||
Title = Station.StationName;
|
||||
|
||||
StationDetailText = Station.Id != null
|
||||
? string.Format(AppResources.MarkingBikesAtStationStationId, Station.Id)
|
||||
: string.Empty;
|
||||
|
||||
CollectionChanged += (sender, eventargs) =>
|
||||
{
|
||||
|
@ -102,7 +98,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
{
|
||||
get
|
||||
{
|
||||
return Count > 0
|
||||
return Count > 0
|
||||
&& !ActiveUser.IsLoggedIn;
|
||||
}
|
||||
}
|
||||
|
@ -111,12 +107,12 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
/// Informs about need to log in before requesting an bike.
|
||||
/// </summary>
|
||||
public string LoginRequiredHintText
|
||||
=> ActiveUser.IsLoggedIn
|
||||
=> ActiveUser.IsLoggedIn
|
||||
? string.Empty
|
||||
: AppResources.MarkingLoginRequiredToRerserve;
|
||||
|
||||
public string ContactSupportHintText
|
||||
=> string.Format(AppResources.MarkingContactSupport, m_oStation?.OperatorData?.Name ?? "Operator");
|
||||
=> string.Format(AppResources.MarkingContactSupport, Station.OperatorData?.Name ?? "Operator");
|
||||
|
||||
/// <summary>
|
||||
/// Returns if info about the fact that user did not request or book any bikes is visible or not.
|
||||
|
@ -213,7 +209,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
/// </summary>
|
||||
public async Task OnAppearing()
|
||||
{
|
||||
Log.ForContext<BikesAtStationPageViewModel>().Information($"Bikes at station {m_oStation?.StationName} is appearing, either due to tap on a station or to app being shown again.");
|
||||
Log.ForContext<BikesAtStationPageViewModel>().Information($"Bikes at station {Station.StationName} is appearing, either due to tap on a station or to app being shown again.");
|
||||
|
||||
ActionText = "Einen Moment bitte...";
|
||||
|
||||
|
@ -223,13 +219,13 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
ActionText = AppResources.ActivityTextBikesAtStationGetBikes;
|
||||
|
||||
var bikesAll = await ConnectorFactory(IsConnected).Query.GetBikesAsync();
|
||||
|
||||
|
||||
Exception = bikesAll.Exception; // Update communication error from query for bikes at station.
|
||||
|
||||
var bikesAtStation = bikesAll.Response.GetAtStation(m_oStation.Id);
|
||||
var bikesAtStation = bikesAll.Response.GetAtStation(Station.Id);
|
||||
var lockIdList = bikesAtStation
|
||||
.GetLockIt()
|
||||
.Cast<BikeInfo>()
|
||||
.Cast<Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo>()
|
||||
.Select(x => x.LockInfo)
|
||||
.ToList();
|
||||
|
||||
|
@ -260,7 +256,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
if (!dialogResult)
|
||||
{
|
||||
// User decided not to give access to locations permissions.
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { m_oStation});
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { Station });
|
||||
|
||||
await OnAppearing(() => UpdateTask());
|
||||
|
||||
|
@ -281,7 +277,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
AppResources.MessageBikesManagementLocationActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { m_oStation });
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { Station });
|
||||
|
||||
await OnAppearing(() => UpdateTask());
|
||||
|
||||
|
@ -297,7 +293,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { m_oStation });
|
||||
BikeCollection.Update(bikesAtStation, new List<IStation> { Station });
|
||||
|
||||
await OnAppearing(() => UpdateTask());
|
||||
|
||||
|
@ -324,7 +320,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
var locksInfo = lockIdList.UpdateById(locksInfoTdo);
|
||||
|
||||
BikeCollection.Update(bikesAtStation.UpdateLockInfo(locksInfo), new List<IStation> { m_oStation });
|
||||
BikeCollection.Update(bikesAtStation.UpdateLockInfo(locksInfo), new List<IStation> { Station });
|
||||
|
||||
// Backup GUI synchronization context.
|
||||
await OnAppearing(() => UpdateTask());
|
||||
|
@ -339,6 +335,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
PostAction(
|
||||
unused =>
|
||||
{
|
||||
Log.ForContext<BikesAtStationPageViewModel>().Debug("Updating action text...");
|
||||
ActionText = AppResources.ActivityTextUpdating;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
},
|
||||
|
@ -346,7 +343,7 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
|
||||
var result = ConnectorFactory(IsConnected).Query.GetBikesAsync().Result;
|
||||
|
||||
BikeCollection bikes = result.Response.GetAtStation(m_oStation.Id);
|
||||
BikeCollection bikes = result.Response.GetAtStation(Station.Id);
|
||||
|
||||
var exception = result.Exception;
|
||||
if (exception != null)
|
||||
|
@ -357,11 +354,12 @@ namespace TINK.ViewModel.BikesAtStation
|
|||
PostAction(
|
||||
unused =>
|
||||
{
|
||||
BikeCollection.Update(bikes, new List<IStation> { m_oStation });
|
||||
Log.ForContext<BikesAtStationPageViewModel>().Debug("Updating bikes at station...");
|
||||
BikeCollection.Update(bikes, new List<IStation> { Station });
|
||||
Exception = result.Exception;
|
||||
ActionText = string.Empty;
|
||||
},
|
||||
null);
|
||||
null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
using Plugin.Messaging;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Plugin.Messaging;
|
||||
using Serilog;
|
||||
using TINK.Model.Services.CopriApi.ServerUris;
|
||||
using TINK.Model.Station;
|
||||
using TINK.MultilingualResources;
|
||||
|
@ -17,6 +17,11 @@ namespace TINK.ViewModel.Info
|
|||
/// <summary> View model for contact page.</summary>
|
||||
public class ContactPageViewModel : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Mail address for app related support.
|
||||
/// </summary>
|
||||
public const string APPSUPPORTMAILADDRESS = "hotline@sharee.bike";
|
||||
|
||||
/// <summary> Reference on view service to show modal notifications and to perform navigation. </summary>
|
||||
private IViewService ViewService { get; }
|
||||
|
||||
|
@ -84,17 +89,41 @@ namespace TINK.ViewModel.Info
|
|||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary> Command object to bind login button to view model. </summary>
|
||||
/// <summary> Command object to bind mail button to view model. </summary>
|
||||
public ICommand OnMailRequest
|
||||
=> new Command(
|
||||
async () => await DoSendMail(),
|
||||
async () =>
|
||||
{
|
||||
if (await DoSendMailToOperator())
|
||||
{
|
||||
// Mail sent to operator or no mailer availble.
|
||||
return;
|
||||
}
|
||||
|
||||
await DoSendMailAppRelated(
|
||||
new List<string> { MailAddressText },
|
||||
MailAddressText.ToUpper() != APPSUPPORTMAILADDRESS.ToUpper()
|
||||
? new List<string> { APPSUPPORTMAILADDRESS }
|
||||
: new List<string>());
|
||||
},
|
||||
() => IsSendMailAvailable);
|
||||
|
||||
/// <summary> Command object to bind mail app releated button to model. </summary>
|
||||
public ICommand OnSendAppFeedbackMailRequest
|
||||
=> new Command(
|
||||
async () => await DoSendMailToOperator(),
|
||||
() => IsSendMailAvailable);
|
||||
|
||||
/// <summary> Command object to bind mail app releated button to model. </summary>
|
||||
public ICommand OnMailAppRelatedRequest
|
||||
=> new Command(
|
||||
async () => await DoSendMailAppRelated(new List<string> { APPSUPPORTMAILADDRESS }, new List<string>()),
|
||||
() => IsSendMailAvailable);
|
||||
|
||||
/// <summary>True if sending mail is possible.</summary>
|
||||
public bool IsSendMailAvailable =>
|
||||
CrossMessaging.Current.EmailMessenger.CanSendEmail;
|
||||
|
||||
|
||||
|
||||
/// <summary>cTrue if doing a phone call is possible.</summary>
|
||||
public bool IsDoPhoncallAvailable
|
||||
|
@ -116,31 +145,52 @@ namespace TINK.ViewModel.Info
|
|||
public bool IsOperatorInfoAvaliable
|
||||
=> MailAddressText.Length > 0 || PhoneNumberText.Length > 0;
|
||||
|
||||
/// <summary> Request to do a phone call. </summary>
|
||||
public async Task DoSendMail()
|
||||
/// <returns> Returns true if eithe mail was sent or if no mailer available.</returns>
|
||||
public async Task<bool> DoSendMailToOperator()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsSendMailAvailable)
|
||||
{
|
||||
// Nothing to do because email can not be sent.
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
var tinkMail = await ViewService.DisplayAlert(
|
||||
// Ask whether mail is operator or app specific.
|
||||
var operatorRelatedMail = await ViewService.DisplayAlert(
|
||||
AppResources.QuestionTitle,
|
||||
string.Format(AppResources.QuestionSupportmailSubject, GetAppName(ActiveUri)),
|
||||
string.Format(AppResources.QuestionSupportmailAnswerOperator, GetAppName(ActiveUri)),
|
||||
string.Format(AppResources.QuestionSupportmailAnswerApp, GetAppName(ActiveUri)));
|
||||
|
||||
if (tinkMail)
|
||||
if (operatorRelatedMail)
|
||||
{
|
||||
// Send TINK- related support mail.
|
||||
// Send operator related support mail.
|
||||
await Email.ComposeAsync(new EmailMessage { To = new List<string> { MailAddressText }, Subject = $"{GetAppName(ActiveUri)} Anfrage" });
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Send a tink app related mail
|
||||
return false;
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error("An unexpected error occurred sending mail to operator. {@Exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.ErrorSupportmailMailingFailed,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Request to send a app related mail. </summary>
|
||||
public async Task DoSendMailAppRelated(List<string> to, List<string> cc)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Send app related mail
|
||||
var appendFile = false;
|
||||
appendFile = await ViewService.DisplayAlert(
|
||||
AppResources.QuestionTitle,
|
||||
|
@ -148,28 +198,34 @@ namespace TINK.ViewModel.Info
|
|||
AppResources.QuestionAnswerYes,
|
||||
AppResources.QuestionAnswerNo);
|
||||
|
||||
var message = new EmailMessage { To = new List<string> { MailAddressText }, Subject = $"{GetAppName(ActiveUri)}-App Anfrage" };
|
||||
var message = new EmailMessage
|
||||
{
|
||||
To = to,
|
||||
Cc = cc,
|
||||
Subject = $"{GetAppName(ActiveUri)}-App Anfrage"
|
||||
};
|
||||
|
||||
if (appendFile == false)
|
||||
{
|
||||
// Send a tink app related mail
|
||||
// Send without attachment
|
||||
await Email.ComposeAsync(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send with attachment.
|
||||
var logFileName = string.Empty;
|
||||
try
|
||||
{
|
||||
logFileName = CreateAttachment();
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.ErrorSupportmailCreateAttachment,
|
||||
l_oException.Message,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
Log.Error("An error occurred creating attachment. {@l_oException)", l_oException);
|
||||
Log.Error("An error occurred creating attachment. {@Exception)", exception);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(logFileName))
|
||||
|
@ -180,19 +236,18 @@ namespace TINK.ViewModel.Info
|
|||
// Send a tink app related mail
|
||||
await Email.ComposeAsync(message);
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error("An unexpected error occurred sending mail. {@Exception}", p_oException);
|
||||
Log.Error("An unexpected error occurred sending mail. {@Exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.ErrorSupportmailMailingFailed,
|
||||
p_oException.Message,
|
||||
AppResources.ErrorSupportmailMailingFailed,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Command object to bind login button to view model. </summary>
|
||||
public ICommand OnSelectStationRequest
|
||||
#if USEFLYOUT
|
||||
|
@ -243,13 +298,13 @@ namespace TINK.ViewModel.Info
|
|||
CrossMessaging.Current.PhoneDialer.MakePhoneCall(PhoneNumberText);
|
||||
}
|
||||
}
|
||||
catch (Exception p_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error("An unexpected error occurred doing a phone call. {@Exception}", p_oException);
|
||||
Log.Error("An unexpected error occurred doing a phone call. {@Exception}", exception);
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageWaring,
|
||||
AppResources.ErrorSupportmailPhoningFailed,
|
||||
p_oException.Message,
|
||||
AppResources.ErrorSupportmailPhoningFailed,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using TINK.View;
|
|||
using TINK.Model.Station;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model;
|
||||
using Serilog;
|
||||
|
@ -22,6 +22,7 @@ using TINK.MultilingualResources;
|
|||
using TINK.ViewModel.Info;
|
||||
using TINK.Repository;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
|
||||
namespace TINK.ViewModel.Contact
|
||||
{
|
||||
|
@ -385,6 +386,8 @@ namespace TINK.ViewModel.Contact
|
|||
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
|
||||
await TinkApp.GetConnector(IsConnected).Command.DoLogout();
|
||||
TinkApp.ActiveUser.Logout();
|
||||
}
|
||||
|
@ -483,16 +486,16 @@ namespace TINK.ViewModel.Contact
|
|||
"OK");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of station color for all stations.
|
||||
/// </summary>
|
||||
/// <param name="stationsId">Station id list to get color for.</param>
|
||||
/// <returns></returns>
|
||||
private static IList<Color> GetStationColors(
|
||||
IEnumerable<string> stationsId,
|
||||
BikeCollection bikesAll)
|
||||
private static IList<Color> GetStationColors(
|
||||
IEnumerable<string> stationsId,
|
||||
BikeCollection bikesAll)
|
||||
{
|
||||
if (stationsId == null)
|
||||
{
|
||||
|
@ -513,7 +516,7 @@ namespace TINK.ViewModel.Contact
|
|||
{
|
||||
// Get color of given station.
|
||||
var bikesAtStation = bikesAll.Where(x => x.StationId == stationId).ToList();
|
||||
if (bikesAtStation.FirstOrDefault(x => x.State.Value != Model.State.InUseStateEnum.Disposable) != null)
|
||||
if (bikesAtStation.FirstOrDefault(x => x.State.Value.IsOccupied()) != null)
|
||||
{
|
||||
// There is at least one requested or booked bike
|
||||
colors.Add(Color.LightBlue);
|
||||
|
|
|
@ -1,30 +1,56 @@
|
|||
|
||||
using Serilog;
|
||||
using TINK.Repository.Request;
|
||||
|
||||
namespace TINK.ViewModel.Login
|
||||
{
|
||||
/// <summary> Manages the copri web view when user is logged in. </summary>
|
||||
public class ManageAccountViewModel
|
||||
{
|
||||
|
||||
/// <summary> Holds the auth cookie of the user logged in.</summary>
|
||||
private string AuthCookie { get; }
|
||||
private string SessionCookie { get; }
|
||||
|
||||
/// <summary> Holds the merchant id.</summary>
|
||||
private string MerchantId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current ui two letter ISO language name.
|
||||
/// </summary>
|
||||
private string UiIsoLanguageName { get; }
|
||||
|
||||
/// <summary> Holds the name of the host.</summary>
|
||||
private string HostName { get; }
|
||||
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
public ManageAccountViewModel(
|
||||
string authCookie,
|
||||
string sessionCookie,
|
||||
string merchantId,
|
||||
string uiIsoLangugageName,
|
||||
string hostName)
|
||||
{
|
||||
AuthCookie = authCookie;
|
||||
SessionCookie = sessionCookie;
|
||||
MerchantId = merchantId;
|
||||
UiIsoLanguageName = uiIsoLangugageName;
|
||||
HostName = hostName;
|
||||
}
|
||||
|
||||
/// <summary> Get Uri of web view managing user account. </summary>
|
||||
public string Uri =>
|
||||
$"https://{HostName}?sessionid={AuthCookie}{MerchantId}";
|
||||
public string Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
var sessionIdQueryElement = QueryBuilderHelper.GetSessionIdQueryElement("?", MerchantId, SessionCookie);
|
||||
|
||||
string GetUriText()
|
||||
=> !string.IsNullOrEmpty(sessionIdQueryElement)
|
||||
? $"https://{HostName}{sessionIdQueryElement}{QueryBuilderHelper.GetLanguageQueryElement("&", UiIsoLanguageName)}"
|
||||
: $"https://{HostName}";
|
||||
|
||||
Log.ForContext<ManageAccountViewModel>().Debug($"Request to open url {GetUriText()} to get privacy info.");
|
||||
|
||||
return GetUriText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using TINK.Services.CopriApi.ServerUris;
|
||||
using Serilog;
|
||||
using TINK.Repository.Request;
|
||||
|
||||
namespace TINK.ViewModel.CopriWebView
|
||||
{
|
||||
|
@ -11,16 +12,38 @@ namespace TINK.ViewModel.CopriWebView
|
|||
/// <summary> Holds the name of the host.</summary>
|
||||
private string HostName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current ui two letter ISO language name.
|
||||
/// </summary>
|
||||
private string UiIsoLanguageName { get; }
|
||||
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
public PasswordForgottonViewModel(
|
||||
string merchantId,
|
||||
string uiIsoLangugageName,
|
||||
string hostName)
|
||||
{
|
||||
MerchantId = merchantId;
|
||||
UiIsoLanguageName = uiIsoLangugageName;
|
||||
HostName = hostName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Get Uri of web view providing password forgotton functionality. </summary>
|
||||
public string Uri =>
|
||||
$"https://{HostName}/{HostName.GetAppFolderName()}/Account?sessionid={MerchantId}";
|
||||
public string Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
var sessionIdQueryElement = QueryBuilderHelper.GetSessionIdQueryElement("?", MerchantId);
|
||||
|
||||
string GetUriText()
|
||||
=> !string.IsNullOrEmpty(sessionIdQueryElement)
|
||||
? $"https://{HostName}/app/Account{sessionIdQueryElement}{QueryBuilderHelper.GetLanguageQueryElement("&", UiIsoLanguageName)}"
|
||||
: $"https://{HostName}/app/Account";
|
||||
|
||||
Log.ForContext<PasswordForgottonViewModel>().Debug($"Request to open url {GetUriText()} to get privacy info.");
|
||||
|
||||
return GetUriText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using TINK.Services.CopriApi.ServerUris;
|
||||
using Serilog;
|
||||
using TINK.Repository.Request;
|
||||
|
||||
namespace TINK.ViewModel.CopriWebView
|
||||
{
|
||||
|
@ -10,17 +11,37 @@ namespace TINK.ViewModel.CopriWebView
|
|||
/// <summary> Holds the name of the host.</summary>
|
||||
private string HostName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current ui two letter ISO language name.
|
||||
/// </summary>
|
||||
private string UiIsoLanguageName { get; }
|
||||
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
public RegisterPageViewModel(
|
||||
string merchantId,
|
||||
string uiIsoLangugageName,
|
||||
string hostName)
|
||||
{
|
||||
HostName = hostName;
|
||||
UiIsoLanguageName = uiIsoLangugageName;
|
||||
MerchantId = merchantId;
|
||||
}
|
||||
|
||||
/// <summary>Get Uri of web view for creating account.</summary>
|
||||
public string Uri =>
|
||||
$"https://{HostName}/{HostName.GetAppFolderName()}/Account/1.%20Kundendaten?sessionid={MerchantId}";
|
||||
}
|
||||
|
||||
/// <summary>Get Uri of web view for creating account.</summary>
|
||||
public string Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
var sessionIdQueryElement = QueryBuilderHelper.GetSessionIdQueryElement("?", MerchantId);
|
||||
|
||||
string GetUriText()
|
||||
=> !string.IsNullOrEmpty(sessionIdQueryElement)
|
||||
? $"https://{HostName}/app/Account/1.%20Kundendaten{sessionIdQueryElement}{QueryBuilderHelper.GetLanguageQueryElement("&", UiIsoLanguageName)}"
|
||||
: $"https://{HostName}/app/Account/1.%20Kundendaten";
|
||||
|
||||
Log.ForContext<RegisterPageViewModel>().Debug($"Request to open url {GetUriText()} to get privacy info.");
|
||||
return GetUriText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Contact
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ namespace TINK.ViewModel.Contact
|
|||
: await Task.FromResult(ViewModelHelper.FromBody("No fees resource available. Resource path is null or empty."))
|
||||
};
|
||||
|
||||
TypesOfBikesText = new HtmlWebViewSource
|
||||
TypesOfBikesText = new HtmlWebViewSource
|
||||
{
|
||||
Html = !string.IsNullOrEmpty(BikesResourcePath)
|
||||
? await ViewModelHelper.GetSource($"https://{HostName}/{BikesResourcePath}" /*"site/bike_info.html"*/, IsSiteCachingOn)
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.Settings;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using System.Linq;
|
||||
using TINK.Model;
|
||||
using Xamarin.Forms;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Permissions;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
using TINK.MultilingualResources;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.Permissions;
|
||||
using TINK.Settings;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.FindBike
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ namespace TINK.ViewModel.FindBike
|
|||
private string bikeIdUserInput = string.Empty;
|
||||
|
||||
/// <summary> Text entered by user to specify a bike.</summary>
|
||||
public string BikeIdUserInput
|
||||
public string BikeIdUserInput
|
||||
{
|
||||
get => bikeIdUserInput;
|
||||
set
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.ViewModel.FindBike
|
|||
|
||||
/// <summary> Do not allow to select bike if id is not set.</summary>
|
||||
public bool IsSelectBikeEnabled => IsIdle && BikeIdUserInput != null && BikeIdUserInput.Length > 0;
|
||||
|
||||
|
||||
/// <summary> Hide id input fields as soon as bike is found.</summary>
|
||||
public bool IsSelectBikeVisible => BikeCollection != null && BikeCollection.Count == 0;
|
||||
|
||||
|
@ -131,13 +131,13 @@ namespace TINK.ViewModel.FindBike
|
|||
|
||||
Exception = bikes.Exception; // Update communication error from query for bikes occupied.
|
||||
Bikes = bikes.Response;
|
||||
|
||||
|
||||
ActionText = "";
|
||||
IsIdle = true;
|
||||
}
|
||||
|
||||
/// <summary> Command object to bind select bike button to view model. </summary>
|
||||
public System.Windows.Input.ICommand OnSelectBikeRequest => new Xamarin.Forms.Command(async () => await SelectBike(), () => IsSelectBikeEnabled);
|
||||
public System.Windows.Input.ICommand OnSelectBikeRequest => new Xamarin.Forms.Command(async () => await SelectBike(), () => IsSelectBikeEnabled);
|
||||
|
||||
/// <summary> Select a bike by ID</summary>
|
||||
public async Task SelectBike()
|
||||
|
@ -149,17 +149,17 @@ namespace TINK.ViewModel.FindBike
|
|||
if (selectedBike == null)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageErrorSelectBikeNoBikeFound, BikeIdUserInput),
|
||||
AppResources.MessageTitleHint,
|
||||
string.Format(AppResources.MessageErrorSelectBikeNoBikeFound, BikeIdUserInput),
|
||||
AppResources.MessageAnswerOk);
|
||||
return;
|
||||
}
|
||||
|
||||
var bikeCollection = new BikeCollection(new Dictionary<string, Model.Bike.BC.BikeInfo> { { selectedBike.Id, selectedBike } });
|
||||
var bikeCollection = new BikeCollection(new Dictionary<string, Model.Bikes.BikeInfoNS.BC.BikeInfo> { { selectedBike.Id, selectedBike } });
|
||||
|
||||
var lockIdList = bikeCollection
|
||||
.GetLockIt()
|
||||
.Cast<BikeInfo>()
|
||||
.Cast<Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo>()
|
||||
.Select(x => x.LockInfo)
|
||||
.ToList();
|
||||
|
||||
|
@ -171,7 +171,7 @@ namespace TINK.ViewModel.FindBike
|
|||
// Check bluetooth and location permission and states
|
||||
ActionText = AppResources.ActivityTextCheckBluetoothState;
|
||||
|
||||
if (bikeCollection.FirstOrDefault(x => x is BikeInfo btBike) != null
|
||||
if (bikeCollection.FirstOrDefault(x => x is Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo btBike) != null
|
||||
&& RuntimePlatform == Device.Android)
|
||||
{
|
||||
// Check location permission
|
||||
|
@ -263,11 +263,12 @@ namespace TINK.ViewModel.FindBike
|
|||
|
||||
ActionText = "";
|
||||
IsIdle = true;
|
||||
} catch (Exception exception)
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorSelectBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageErrorSelectBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
Log.ForContext<FindBikePageViewModel>().Error("Running command to select bike failed. {Exception}", exception);
|
||||
|
@ -300,7 +301,7 @@ namespace TINK.ViewModel.FindBike
|
|||
var selectedBike = bikes.FirstOrDefault(x => x.Id.Equals(BikeIdUserInput.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
bikes = selectedBike != null
|
||||
? new BikeCollection(new Dictionary<string, Model.Bike.BC.BikeInfo> { { selectedBike.Id, selectedBike } })
|
||||
? new BikeCollection(new Dictionary<string, Model.Bikes.BikeInfoNS.BC.BikeInfo> { { selectedBike.Id, selectedBike } })
|
||||
: new BikeCollection();
|
||||
|
||||
PostAction(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using TINK.Settings;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Settings;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using TINK.Settings;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Settings;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -9,7 +9,7 @@ namespace TINK.ViewModel
|
|||
{
|
||||
public async Task StartUpdateAyncPeridically(PollingParameters p_oPeriode)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopUpdatePeridically()
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace TINK.ViewModel.Info.BikeInfo
|
|||
{
|
||||
var l_oCount = PagesCountProvider();
|
||||
|
||||
return l_oCount > 0 ? (double)CurrentPageIndex / l_oCount : 0;
|
||||
return l_oCount > 0 ? (double)CurrentPageIndex / l_oCount : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace TINK.ViewModel.Info.BikeInfo
|
|||
}
|
||||
|
||||
/// <summary> Returns one based index of the current page. </summary>
|
||||
private int CurrentPageIndex { get; }
|
||||
private int CurrentPageIndex { get; }
|
||||
|
||||
/// <summary> Gets the count of carousel pages. </summary>
|
||||
private Func<int> PagesCountProvider { get; }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Services.CopriApi.ServerUris;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
|
@ -26,8 +26,17 @@ namespace TINK.ViewModel.Info
|
|||
|
||||
private string ImpressResourcePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current ui two letter ISO language name.
|
||||
/// </summary>
|
||||
private string UiIsoLanguageName { get; }
|
||||
|
||||
/// <summary> Constructs Info view model</summary>
|
||||
/// <param name="isSiteCachingOn">Holds value wether site caching is on or off.</param>
|
||||
/// <param name="agbResourcePath"> Agb resouce path received from backend.</param>
|
||||
/// <param name="privacyResourcePath"> Privacy resouce path received from backend.</param>
|
||||
/// <param name="impressResourcePath"> Impress resouce path received from backend.</param>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <param name="resourceProvider">Delegate to get an an embedded html ressource. Used as fallback if download from web page does not work and cache is empty.</param>
|
||||
public InfoPageViewModel(
|
||||
string hostName,
|
||||
|
@ -35,13 +44,15 @@ namespace TINK.ViewModel.Info
|
|||
string privacyResourcePath,
|
||||
string impressResourcePath,
|
||||
bool isSiteCachingOn,
|
||||
string uiIsoLangugageName,
|
||||
Func<string, string> resourceProvider)
|
||||
{
|
||||
HostName = hostName;
|
||||
AgbResourcePath = agbResourcePath;
|
||||
PrivacyResourcePath = privacyResourcePath;
|
||||
ImpressResourcePath = impressResourcePath;
|
||||
PrivacyResourcePath = privacyResourcePath;
|
||||
ImpressResourcePath = impressResourcePath;
|
||||
IsSiteCachingOn = isSiteCachingOn;
|
||||
UiIsoLanguageName = uiIsoLangugageName;
|
||||
|
||||
InfoAgb = new HtmlWebViewSource { Html = "<html>Loading...</html>" };
|
||||
|
||||
|
@ -52,25 +63,61 @@ namespace TINK.ViewModel.Info
|
|||
/// <summary> Called when page is shown. </summary>
|
||||
public async void OnAppearing()
|
||||
{
|
||||
InfoAgb = await GetAgb(HostName, AgbResourcePath, IsSiteCachingOn);
|
||||
|
||||
InfoPrivacy = new HtmlWebViewSource
|
||||
// Gets privacy info from server.
|
||||
async Task<HtmlWebViewSource> GetInfoPrivacy()
|
||||
{
|
||||
Html = !string.IsNullOrEmpty(PrivacyResourcePath)
|
||||
? await ViewModelHelper.GetSource(
|
||||
$"https://{HostName}/{PrivacyResourcePath}", // "site/privacy.html"
|
||||
IsSiteCachingOn)
|
||||
: await Task.FromResult(ViewModelHelper.FromBody("No privacy resource available. Resource path is null or empty."))
|
||||
};
|
||||
string GetUriText()
|
||||
=> $"https://{HostName}/{PrivacyResourcePath}";
|
||||
|
||||
InfoImpressum = new HtmlWebViewSource
|
||||
if (string.IsNullOrEmpty(PrivacyResourcePath))
|
||||
{
|
||||
// Information to access ressource is missing
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = await Task.FromResult(ViewModelHelper.FromBody("No privacy resource available. Resource path is null or empty."))
|
||||
};
|
||||
}
|
||||
|
||||
Log.Debug($"Request to open url {GetUriText()} to get privacy info.");
|
||||
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = await ViewModelHelper.GetSource(
|
||||
GetUriText(), // "site/privacy.html"
|
||||
IsSiteCachingOn)
|
||||
};
|
||||
}
|
||||
|
||||
// Gets impress info from server.
|
||||
async Task<HtmlWebViewSource> GetImpressum()
|
||||
{
|
||||
Html = !string.IsNullOrEmpty(ImpressResourcePath)
|
||||
? await ViewModelHelper.GetSource(
|
||||
$"https://{HostName}/{ImpressResourcePath}", // "site/impress.html"
|
||||
IsSiteCachingOn)
|
||||
: await Task.FromResult(ViewModelHelper.FromBody("No impress resource available. Resource path is null or empty."))
|
||||
};
|
||||
string GetUriText()
|
||||
=> $"https://{HostName}/{ImpressResourcePath}";
|
||||
|
||||
if (string.IsNullOrEmpty(ImpressResourcePath))
|
||||
{
|
||||
// Information to access ressource is missing
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = await Task.FromResult(ViewModelHelper.FromBody("No impress resource available. Resource path is null or empty."))
|
||||
};
|
||||
}
|
||||
|
||||
Log.Debug($"Request to open url {GetUriText()} to get impress info.");
|
||||
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = await ViewModelHelper.GetSource(
|
||||
GetUriText(), // "site/privacy.html"
|
||||
IsSiteCachingOn)
|
||||
};
|
||||
}
|
||||
|
||||
InfoAgb = await GetAgb(HostName, AgbResourcePath, IsSiteCachingOn, UiIsoLanguageName);
|
||||
|
||||
InfoPrivacy = await GetInfoPrivacy();
|
||||
|
||||
InfoImpressum = await GetImpressum();
|
||||
}
|
||||
|
||||
/// <summary> Gets the AGBs</summary>
|
||||
|
@ -79,15 +126,27 @@ namespace TINK.ViewModel.Info
|
|||
public static async Task<HtmlWebViewSource> GetAgb(
|
||||
string hostName,
|
||||
string agbResourcePath,
|
||||
bool isSiteCachingOn)
|
||||
bool isSiteCachingOn,
|
||||
string uiIsoLangugageName)
|
||||
{
|
||||
string GetUriText()
|
||||
=> $"https://{hostName}/{agbResourcePath}";
|
||||
|
||||
if (string.IsNullOrEmpty(agbResourcePath))
|
||||
{
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = await Task.FromResult(ViewModelHelper.FromBody("No terms resource available. Resource path is null or empty."))
|
||||
};
|
||||
}
|
||||
|
||||
Log.Debug($"Request to open url {GetUriText()} to get AGB infos.");
|
||||
|
||||
return new HtmlWebViewSource
|
||||
{
|
||||
Html = !string.IsNullOrEmpty(agbResourcePath)
|
||||
? await ViewModelHelper.GetSource(
|
||||
$"https://{hostName}/{agbResourcePath}", // "agb.html"
|
||||
Html = await ViewModelHelper.GetSource(
|
||||
GetUriText(), // "agb.html"
|
||||
isSiteCachingOn)
|
||||
: await Task.FromResult(ViewModelHelper.FromBody("No terms resource available. Resource path is null or empty."))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -100,12 +159,13 @@ namespace TINK.ViewModel.Info
|
|||
Html = ResourceProvider("HtmlResouces.V02.InfoLicenses.html")
|
||||
.Replace("CURRENT_VERSION_TINKAPP", DependencyService.Get<IAppInfo>().Version.ToString())
|
||||
.Replace("ACTIVE_APPNAME", AppInfo.Name)
|
||||
.Replace("APPSUPPORTMAILADDRESS", ContactPageViewModel.APPSUPPORTMAILADDRESS)
|
||||
};
|
||||
|
||||
/// <summary> Privacy text.</summary>
|
||||
private HtmlWebViewSource infoImpress;
|
||||
/// <summary> Gets the privacy related information. </summary>
|
||||
public HtmlWebViewSource InfoImpressum
|
||||
public HtmlWebViewSource InfoImpressum
|
||||
{
|
||||
get => infoImpress;
|
||||
set
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
using TINK.View;
|
||||
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 System.ComponentModel;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Repository.Exception;
|
||||
using Serilog;
|
||||
using TINK.ViewModel.Map;
|
||||
using Plugin.Connectivity;
|
||||
using TINK.Model;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Info;
|
||||
using TINK.ViewModel.Map;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -114,15 +114,15 @@ namespace TINK.ViewModel
|
|||
{
|
||||
get
|
||||
{
|
||||
return !TinkApp.ActiveUser.IsLoggedIn
|
||||
return !TinkApp.ActiveUser.IsLoggedIn
|
||||
&& m_bMailAndPasswordCandidatesOk;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets mail address of user.</summary>
|
||||
public string MailAddress
|
||||
public string MailAddress
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
return m_strMailAddress;
|
||||
}
|
||||
|
@ -183,8 +183,8 @@ namespace TINK.ViewModel
|
|||
else
|
||||
{
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageLoginRegisterNoNet,
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageLoginRegisterNoNet,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
});
|
||||
|
@ -248,7 +248,7 @@ namespace TINK.ViewModel
|
|||
Log.ForContext<LoginPageViewModel>().Error("Login failed (invalid. auth. response). {@l_oException}.", l_oException);
|
||||
|
||||
await m_oViewService.DisplayAlert(
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
@ -274,20 +274,21 @@ namespace TINK.ViewModel
|
|||
AppResources.MessageLoginConnectionErrorTitle,
|
||||
string.Format("{0}\r\n{1}", l_oException.Message, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
AppResources.MessageAnswerOk);
|
||||
} else if (l_oException is UsernamePasswordInvalidException)
|
||||
}
|
||||
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),
|
||||
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,
|
||||
AppResources.MessageLoginErrorTitle,
|
||||
l_oException.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -303,7 +304,7 @@ namespace TINK.ViewModel
|
|||
: string.Format(AppResources.MessageLoginWelcomeTitle);
|
||||
|
||||
await m_oViewService.DisplayAlert(
|
||||
title,
|
||||
title,
|
||||
string.Format(AppResources.MessageLoginWelcome, TinkApp.ActiveUser.Mail),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
@ -356,7 +357,7 @@ namespace TINK.ViewModel
|
|||
{
|
||||
var l_oHint = new FormattedString();
|
||||
l_oHint.Spans.Add(new Span { Text = AppResources.MarkingLoginInstructionsTinkKonradTitle });
|
||||
l_oHint.Spans.Add(new Span { Text = AppResources.MarkingLoginInstructionsTinkKonradMessage });
|
||||
l_oHint.Spans.Add(new Span { Text = AppResources.MarkingLoginInstructionsTinkKonradMessage });
|
||||
|
||||
return l_oHint;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using TINK.Model;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.Map
|
||||
{
|
||||
|
@ -18,10 +16,14 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
public Color TinkColor => Color.Default;
|
||||
|
||||
public Color NoTinkColor => Color.Default;
|
||||
|
||||
public bool IsKonradEnabled => false;
|
||||
|
||||
public Color KonradColor => Color.Default;
|
||||
|
||||
public Color NoKonradColor => Color.Default;
|
||||
|
||||
public bool IsToggleVisible => false;
|
||||
|
||||
public string CurrentFilter => string.Empty;
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace TINK.ViewModel.Map
|
|||
/// <param name="filterCollection">Filter collection to get group from.</param>
|
||||
/// <returns>List of active filters.</returns>
|
||||
/// <todo>Rename to ToFilterList</todo>
|
||||
public IList<string> GetGroup()
|
||||
public IList<string> GetGroup()
|
||||
{
|
||||
return this.Where(x => x.Value == FilterState.On).Select(x => x.Key).ToList();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
private IDictionary<string, FilterState> FilterDictionary { get; }
|
||||
|
||||
public FilterState this[string key] { get => FilterDictionary[key]; set => FilterDictionary[key] = value ; }
|
||||
public FilterState this[string key] { get => FilterDictionary[key]; set => FilterDictionary[key] = value; }
|
||||
|
||||
public ICollection<string> Keys => FilterDictionary.Keys;
|
||||
|
||||
|
@ -43,7 +43,7 @@ namespace TINK.ViewModel.Map
|
|||
public bool IsReadOnly => true;
|
||||
|
||||
public void Add(string key, FilterState value) => throw new System.NotImplementedException();
|
||||
|
||||
|
||||
public void Add(KeyValuePair<string, FilterState> item) => throw new System.NotImplementedException();
|
||||
|
||||
public void Clear() => throw new System.NotImplementedException();
|
||||
|
@ -52,7 +52,7 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
public bool ContainsKey(string key) => FilterDictionary.ContainsKey(key);
|
||||
|
||||
public void CopyTo(KeyValuePair<string, FilterState>[] array, int arrayIndex) => FilterDictionary.CopyTo(array, arrayIndex);
|
||||
public void CopyTo(KeyValuePair<string, FilterState>[] array, int arrayIndex) => FilterDictionary.CopyTo(array, arrayIndex);
|
||||
|
||||
public IEnumerator<KeyValuePair<string, FilterState>> GetEnumerator() => FilterDictionary.GetEnumerator();
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
public interface ITinkKonradToggleViewModel
|
||||
{
|
||||
IGroupFilterMapPage FilterDictionary { get; }
|
||||
IGroupFilterMapPage FilterDictionary { get; }
|
||||
|
||||
string CurrentFilter { get; }
|
||||
|
||||
|
@ -12,10 +12,14 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
Color TinkColor { get; }
|
||||
|
||||
Color NoTinkColor { get; }
|
||||
|
||||
bool IsKonradEnabled { get; }
|
||||
|
||||
Color KonradColor { get; }
|
||||
|
||||
Color NoKonradColor { get; }
|
||||
|
||||
bool IsToggleVisible { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using TINK.View;
|
|||
using TINK.Model.Station;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Model;
|
||||
using Serilog;
|
||||
|
@ -26,6 +26,7 @@ using TINK.Services.BluetoothLock;
|
|||
using TINK.ViewModel.Info;
|
||||
using TINK.Repository;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Model.State;
|
||||
|
||||
#if !TRYNOTBACKSTYLE
|
||||
#endif
|
||||
|
@ -103,7 +104,8 @@ namespace TINK.ViewModel.Map
|
|||
IGeolocation GeolocationService { get; }
|
||||
|
||||
/// <summary> False if user tabed on station marker to show bikes at a given station.</summary>
|
||||
public bool IsMapPageEnabled {
|
||||
public bool IsMapPageEnabled
|
||||
{
|
||||
get => isMapPageEnabled;
|
||||
private set
|
||||
{
|
||||
|
@ -173,6 +175,8 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TinkColor)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(KonradColor)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NoTinkColor)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NoKonradColor)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsToggleVisible)));
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +214,7 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
var l_oPin = new Pin
|
||||
{
|
||||
|
||||
|
||||
Position = new Xamarin.Forms.GoogleMaps.Position(station.Position.Latitude, station.Position.Longitude),
|
||||
Label = long.TryParse(station.Id, out long stationId) && stationId > CUSTOM_ICONS_COUNT
|
||||
? station.GetStationName()
|
||||
|
@ -234,13 +238,13 @@ namespace TINK.ViewModel.Map
|
|||
for (int pinIndex = 0; pinIndex < stationsColorList.Count; pinIndex++)
|
||||
{
|
||||
|
||||
var indexPartPrefix = int.TryParse(Pins[pinIndex].Tag.ToString(), out int stationId)
|
||||
var indexPartPrefix = int.TryParse(Pins[pinIndex].Tag.ToString(), out int stationId)
|
||||
&& stationId <= CUSTOM_ICONS_COUNT
|
||||
? $"{stationId}" // there is a station marker with index letter for given station id
|
||||
: "Open"; // there is no station marker. Use open marker.
|
||||
|
||||
var colorPartPrefix = GetRessourceNameColorPart(stationsColorList[pinIndex]);
|
||||
|
||||
|
||||
var l_iName = $"{indexPartPrefix.ToString().PadLeft(2, '0')}_{colorPartPrefix}{(DeviceInfo.Platform == DevicePlatform.Android ? ".png" : string.Empty)}";
|
||||
try
|
||||
{
|
||||
|
@ -307,7 +311,7 @@ namespace TINK.ViewModel.Map
|
|||
Polling = TinkApp.Polling;
|
||||
|
||||
Log.ForContext<MapPageViewModel>().Information(
|
||||
$"{(Polling != null && Polling.IsActivated ? $"Map page is appearing. Update periode is {Polling.Periode.TotalSeconds} sec." : "Map page is appearing. Polling is off.")}" +
|
||||
$"{(Polling != null && Polling.IsActivated ? $"Map page is appearing. Update periode is {Polling.Periode.TotalSeconds} sec." : "Map page is appearing. Polling is off.")}" +
|
||||
$"Current UI language is {Thread.CurrentThread.CurrentUICulture.Name}.");
|
||||
|
||||
// Update map page filter
|
||||
|
@ -329,7 +333,7 @@ namespace TINK.ViewModel.Map
|
|||
await ViewService.DisplayAlert(
|
||||
"Information",
|
||||
resultStationsAndBikes.GeneralData.MerchantMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
AppResources.MessageAnswerOk);
|
||||
WasMerchantMessageAlreadyShown = true;
|
||||
}
|
||||
|
||||
|
@ -424,6 +428,7 @@ namespace TINK.ViewModel.Map
|
|||
AppResources.MessageMapPageErrorAuthcookieUndefined,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
IsConnected = TinkApp.GetIsConnected();
|
||||
await TinkApp.GetConnector(IsConnected).Command.DoLogout();
|
||||
TinkApp.ActiveUser.Logout();
|
||||
}
|
||||
|
@ -496,9 +501,9 @@ namespace TINK.ViewModel.Map
|
|||
if (currentLocation == null)
|
||||
return null;
|
||||
|
||||
return Model.Map.MapSpanFactory.Create(
|
||||
PositionFactory.Create(currentLocation.Latitude, currentLocation.Longitude),
|
||||
TinkApp.ActiveMapSpan.Radius.Kilometers);
|
||||
return Model.Map.MapSpanFactory.Create(
|
||||
PositionFactory.Create(currentLocation.Latitude, currentLocation.Longitude),
|
||||
TinkApp.ActiveMapSpan.Radius.Kilometers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -645,7 +650,7 @@ namespace TINK.ViewModel.Map
|
|||
// Lock action to prevent multiple instances of "BikeAtStation" being opened.
|
||||
IsMapPageEnabled = false;
|
||||
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
TinkApp.SelectedStation = TinkApp.Stations.FirstOrDefault(x => x.Id == selectedStationId)
|
||||
?? new Station(selectedStationId, new List<string>(), null); // Station might not be in list StationDictinaly because this list is not updatd in background task.
|
||||
|
||||
#if TRYNOTBACKSTYLE
|
||||
|
@ -701,7 +706,7 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
// Get color of given station.
|
||||
var bikesAtStation = bikesAll.Where(x => x.StationId == stationId).ToList();
|
||||
if (bikesAtStation.FirstOrDefault(x => x.State.Value != Model.State.InUseStateEnum.Disposable) != null)
|
||||
if (bikesAtStation.FirstOrDefault(x => x.State.Value.IsOccupied()) != null)
|
||||
{
|
||||
// There is at least one requested or booked bike
|
||||
colors.Add(Color.LightBlue);
|
||||
|
@ -865,14 +870,14 @@ namespace TINK.ViewModel.Map
|
|||
}
|
||||
|
||||
/// <summary> User request to toggle from TINK to Konrad. </summary>
|
||||
private async Task ActivateFilter(string p_strSelectedFilter)
|
||||
private async Task ActivateFilter(string selectedFilter)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsMapPageEnabled = false;
|
||||
IsRunning = true;
|
||||
|
||||
Log.ForContext<MapPageViewModel>().Information($"Request to toggle to \"{p_strSelectedFilter}\".");
|
||||
Log.ForContext<MapPageViewModel>().Information($"Request to toggle to \"{selectedFilter}\".");
|
||||
|
||||
// Stop polling.
|
||||
ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
|
@ -927,7 +932,7 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageBikesManagementBluetoothActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
ActionText = "";
|
||||
IsRunning = false;
|
||||
|
@ -996,7 +1001,7 @@ namespace TINK.ViewModel.Map
|
|||
ActionText = "";
|
||||
IsRunning = false;
|
||||
IsMapPageEnabled = true;
|
||||
Log.ForContext<MapPageViewModel>().Information($"Toggle to \"{p_strSelectedFilter}\" done.");
|
||||
Log.ForContext<MapPageViewModel>().Information($"Toggle to \"{selectedFilter}\" done.");
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
|
@ -1018,6 +1023,10 @@ namespace TINK.ViewModel.Map
|
|||
|
||||
public Color KonradColor => tinkKonradToggleViewModel.KonradColor;
|
||||
|
||||
public Color NoTinkColor => tinkKonradToggleViewModel.NoTinkColor;
|
||||
|
||||
public Color NoKonradColor => tinkKonradToggleViewModel.NoKonradColor;
|
||||
|
||||
public bool IsToggleVisible => tinkKonradToggleViewModel.IsToggleVisible;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,8 @@ namespace TINK.ViewModel.Map
|
|||
public IGroupFilterMapPage FilterDictionary { get; }
|
||||
|
||||
/// <summary> Gets the activated filter.</summary>
|
||||
public string CurrentFilter {
|
||||
public string CurrentFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
return FilterDictionary.FirstOrDefault(x => x.Value == FilterState.On).Key ?? string.Empty;
|
||||
|
@ -32,17 +33,18 @@ namespace TINK.ViewModel.Map
|
|||
public bool IsTinkEnabled => !string.IsNullOrEmpty(CurrentFilter) && CurrentFilter.GetBikeCategory() != FilterHelper.CARGOBIKE;
|
||||
|
||||
/// <summary> Gets color of the TINK button. </summary>
|
||||
public Color TinkColor => CurrentFilter.GetBikeCategory() == FilterHelper.CARGOBIKE ? Color.Blue : Color.Gray;
|
||||
|
||||
public Color TinkColor => CurrentFilter.GetBikeCategory() == FilterHelper.CARGOBIKE ? Color.White : Color.FromRgba(0, 0, 0, 0);
|
||||
public Color NoTinkColor => CurrentFilter.GetBikeCategory() == FilterHelper.CARGOBIKE ? Color.FromRgb(210, 17, 20) : Color.White;
|
||||
/// <summary> Gets value whether Konrad is enabled or not. </summary>
|
||||
public bool IsKonradEnabled => !string.IsNullOrEmpty(CurrentFilter) && CurrentFilter.GetBikeCategory() != FilterHelper.CITYBIKE;
|
||||
|
||||
/// <summary> Gets color of the Konrad button. </summary>
|
||||
public Color KonradColor => CurrentFilter.GetBikeCategory() == FilterHelper.CITYBIKE ? Color.Red : Color.Gray;
|
||||
public Color KonradColor => CurrentFilter.GetBikeCategory() == FilterHelper.CITYBIKE ? Color.White : Color.FromRgba(0, 0, 0, 0);
|
||||
public Color NoKonradColor => CurrentFilter.GetBikeCategory() == FilterHelper.CITYBIKE ? Color.FromRgb(210, 17, 20) : Color.White;
|
||||
|
||||
/// <summary> Gets whether toggle functionality is visible or not. </summary>
|
||||
public bool IsToggleVisible =>
|
||||
FilterDictionary.Select(x => x.Key).ContainsGroupId(FilterHelper.CITYBIKE)
|
||||
public bool IsToggleVisible =>
|
||||
FilterDictionary.Select(x => x.Key).ContainsGroupId(FilterHelper.CITYBIKE)
|
||||
&& FilterDictionary.Select(x => x.Key).ContainsGroupId(FilterHelper.CARGOBIKE)
|
||||
&& (IsTinkEnabled || IsKonradEnabled);
|
||||
|
||||
|
@ -56,27 +58,27 @@ namespace TINK.ViewModel.Map
|
|||
/// <returns></returns>
|
||||
public TinkKonradToggleViewModel DoToggle()
|
||||
{
|
||||
var l_oCurrentFilterSet = FilterDictionary.ToArray();
|
||||
var currentFilterSet = FilterDictionary.ToArray();
|
||||
|
||||
if (l_oCurrentFilterSet.Length < 2)
|
||||
if (currentFilterSet.Length < 2)
|
||||
{
|
||||
// There is nothing to toggle because filter set contains only one element.
|
||||
return new TinkKonradToggleViewModel(FilterDictionary);
|
||||
}
|
||||
|
||||
var l_oCurrentState = l_oCurrentFilterSet[l_oCurrentFilterSet.Length - 1].Value == FilterState.On
|
||||
var currentState = currentFilterSet[currentFilterSet.Length - 1].Value == FilterState.On
|
||||
? FilterState.On
|
||||
: FilterState.Off;
|
||||
|
||||
var l_oToggledFilterSet = new Dictionary<string, FilterState>();
|
||||
var toggledFilterSet = new Dictionary<string, FilterState>();
|
||||
|
||||
foreach (var l_oFilterElement in l_oCurrentFilterSet)
|
||||
foreach (var filterElement in currentFilterSet)
|
||||
{
|
||||
l_oToggledFilterSet.Add(l_oFilterElement.Key, l_oCurrentState);
|
||||
l_oCurrentState = l_oFilterElement.Value;
|
||||
toggledFilterSet.Add(filterElement.Key, currentState);
|
||||
currentState = filterElement.Value;
|
||||
}
|
||||
|
||||
return new TinkKonradToggleViewModel(new GroupFilterMapPage(l_oToggledFilterSet));
|
||||
return new TinkKonradToggleViewModel(new GroupFilterMapPage(toggledFilterSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.MultilingualResources;
|
||||
|
@ -53,7 +53,7 @@ namespace TINK.ViewModel.MiniSurvey
|
|||
|
||||
MiniSurvey.Questions.Add(
|
||||
"q1",
|
||||
new MiniSurveyModel.QuestionModel
|
||||
new QuestionModel
|
||||
{
|
||||
Text = "1. Was war der Hauptzweck dieser Ausleihe?",
|
||||
Type = MiniSurveyModel.Type.SingleAnswer
|
||||
|
@ -61,7 +61,7 @@ namespace TINK.ViewModel.MiniSurvey
|
|||
|
||||
MiniSurvey.Questions.Add(
|
||||
"q2",
|
||||
new MiniSurveyModel.QuestionModel
|
||||
new QuestionModel
|
||||
{
|
||||
Text = "2. Welches Verkehrsmittel hätten Sie ansonsten benutzt?",
|
||||
Type = MiniSurveyModel.Type.SingleAnswer
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.ViewModel.MiniSurvey
|
|||
|
||||
MiniSurvey.Questions.Add(
|
||||
"q3",
|
||||
new MiniSurveyModel.QuestionModel
|
||||
new QuestionModel
|
||||
{
|
||||
Text = "3. Haben Sie Anmerkungen oder Anregungen?",
|
||||
Type = MiniSurveyModel.Type.CustomText
|
||||
|
@ -120,7 +120,7 @@ namespace TINK.ViewModel.MiniSurvey
|
|||
public string Footer { get; set; }
|
||||
|
||||
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
|
||||
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () =>
|
||||
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () =>
|
||||
{
|
||||
Log.ForContext<MiniSurveyViewModel>().Information($"User result {this[0].Answer.Value}, {this[1].Answer.Value}");
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace TINK.ViewModel.MiniSurvey.Question
|
|||
KeyValuePair<string, string> question,
|
||||
Dictionary<string, string> answers)
|
||||
{
|
||||
Question = question;
|
||||
Question = question;
|
||||
Answers = answers ?? new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace TINK.ViewModel.MiniSurvey.Question
|
|||
public KeyValuePair<string, string> Answer { get; private set; }
|
||||
|
||||
/// <summary> Holds the list of possible answers exposed to GUI. </summary>
|
||||
public string AnswerText
|
||||
public string AnswerText
|
||||
{
|
||||
get => null;
|
||||
set => Answer = new KeyValuePair<string, string>(value, null);
|
||||
|
|
|
@ -36,10 +36,10 @@ namespace TINK.ViewModel
|
|||
}
|
||||
|
||||
return string.Format(AppResources.StatusTextReservationExpiredCodeLocationMaxReservationTime, code, stationId, StateRequestedInfo.MaximumReserveTime.Minutes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(stationId))
|
||||
{
|
||||
{
|
||||
if (!string.IsNullOrEmpty(code))
|
||||
{
|
||||
return string.Format(
|
||||
|
@ -56,7 +56,7 @@ namespace TINK.ViewModel
|
|||
}
|
||||
|
||||
return string.Format(
|
||||
AppResources.StatusTextReservationExpiredRemaining,
|
||||
AppResources.StatusTextReservationExpiredRemaining,
|
||||
remainingTime.Value.Minutes);
|
||||
}
|
||||
|
||||
|
@ -76,11 +76,11 @@ namespace TINK.ViewModel
|
|||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(code))
|
||||
{
|
||||
if(!string.IsNullOrEmpty(stationId))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stationId))
|
||||
{
|
||||
return string.Format(
|
||||
AppResources.StatusTextBookedCodeLocationSince,
|
||||
AppResources.StatusTextBookedCodeLocationSince,
|
||||
code,
|
||||
ViewModelHelper.GetStationName(stationId),
|
||||
from.Value.ToString(BikeViewModelBase.TIMEFORMAT));
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.User;
|
||||
using TINK.View;
|
||||
using TINK.Settings;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using System.Linq;
|
||||
using TINK.Model;
|
||||
using Xamarin.Forms;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Permissions;
|
||||
using Plugin.BLE.Abstractions.Contracts;
|
||||
using TINK.MultilingualResources;
|
||||
using Serilog;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.User;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Tdo;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.Permissions;
|
||||
using TINK.Settings;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Bikes;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.MyBikes
|
||||
{
|
||||
|
@ -112,7 +112,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
|
||||
var lockIdList = bikesOccupied.Response
|
||||
.GetLockIt()
|
||||
.Cast<BikeInfo>()
|
||||
.Cast<Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo>()
|
||||
.Select(x => x.LockInfo)
|
||||
.ToList();
|
||||
|
||||
|
@ -124,7 +124,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
// Check bluetooth and location permission and states
|
||||
ActionText = AppResources.ActivityTextCheckBluetoothState;
|
||||
|
||||
if (bikesOccupied.Response.FirstOrDefault(x => x is BikeInfo btBike) != null
|
||||
if (bikesOccupied.Response.FirstOrDefault(x => x is Model.Bikes.BikeInfoNS.BluetoothLock.BikeInfo btBike) != null
|
||||
&& RuntimePlatform == Device.Android)
|
||||
{
|
||||
// Check location permission
|
||||
|
@ -162,7 +162,7 @@ namespace TINK.ViewModel.MyBikes
|
|||
if (Geolocation.IsGeolcationEnabled == false)
|
||||
{
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageTitleHint,
|
||||
AppResources.MessageBikesManagementLocationActivation,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
|
@ -16,7 +16,7 @@ namespace TINK.ViewModel
|
|||
private CancellationTokenSource CanellationTokenSource { get; }
|
||||
|
||||
/// <summary> Task to perform update. </summary>
|
||||
private Task UpdateTask { get; }
|
||||
private Task UpdateTask { get; }
|
||||
|
||||
/// <summary> Action which performs an update.</summary>
|
||||
private Action UpdateAction { get; }
|
||||
|
@ -55,9 +55,10 @@ namespace TINK.ViewModel
|
|||
|
||||
UpdateAction();
|
||||
|
||||
Log.ForContext<PollingUpdateTask>().Debug($"Update cycle {cycleIndex}, context {GetType().Name} finised at {DateTime.Now}.");
|
||||
cycleIndex = cycleIndex < int.MaxValue ? ++cycleIndex : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
CanellationTokenSource.Token);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using TINK.Settings;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Settings;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ namespace TINK.ViewModel
|
|||
UpdateAction = updateAction ?? throw new ArgumentException(
|
||||
$"Can not construct {GetType().Name}- obect. Argument {nameof(updateAction)} must not be null.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when page is shown.
|
||||
/// Actuates and awaits the first update process and starts a task wich actuate the subseqent update tasks.
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace TINK.Model.Connector
|
|||
/// Fired whenever a property changes.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
/// <summary>Maps uris to user fiendly descriptions.</summary>
|
||||
private Dictionary<string, string> uriToServerText;
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ namespace TINK.ViewModel.Settings
|
|||
/// <param name="isEnabled">If filter does not apply because user does not belong to group (TINK, Konrad, ...) filter is deactivated.</param>
|
||||
/// <param name="labelText">Text of the switch describing the filter.</param>
|
||||
public FilterItemMutable(
|
||||
string key,
|
||||
FilterState filterState,
|
||||
bool isEnabled,
|
||||
string key,
|
||||
FilterState filterState,
|
||||
bool isEnabled,
|
||||
string labelText)
|
||||
{
|
||||
Text = labelText;
|
||||
|
@ -30,7 +30,7 @@ namespace TINK.ViewModel.Settings
|
|||
public string Text { get; }
|
||||
|
||||
/// <summary> True if switch can be toggeled.</summary>
|
||||
public bool IsEnabled { get; }
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
/// <summary> True if switch is on.</summary>
|
||||
public bool IsActivated
|
||||
|
|
|
@ -20,8 +20,8 @@ namespace TINK.ViewModel.Settings
|
|||
|
||||
public string ConnectTimeoutSecText { get => ConnectTimeout.TotalSeconds.ToString(); }
|
||||
|
||||
public double ConnectTimeoutSec
|
||||
{
|
||||
public double ConnectTimeoutSec
|
||||
{
|
||||
get => ConnectTimeout.TotalSeconds;
|
||||
|
||||
set
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
|
||||
namespace TINK.ViewModel.Settings
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ namespace TINK.ViewModel.Settings
|
|||
}
|
||||
|
||||
/// <summary> List of display texts of services.</summary>
|
||||
public IList<string> ServicesTextList => ServiceToText.Select(x => x.Value).OrderBy(x => x).ToList();
|
||||
public IList<string> ServicesTextList => ServiceToText.Select(x => x.Value).OrderBy(x => x).ToList();
|
||||
|
||||
/// <summary> Active locks service.</summary>
|
||||
public string ActiveText
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using TINK.Model;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Settings;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Map;
|
||||
using TINK.ViewModel.Settings;
|
||||
using System.Linq;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using Xamarin.Forms;
|
||||
using TINK.Services;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -90,7 +90,7 @@ namespace TINK.ViewModel
|
|||
ExternalFolder = TinkApp.ExternalFolder;
|
||||
|
||||
IsLogToExternalFolderVisible = !string.IsNullOrEmpty(ExternalFolder);
|
||||
|
||||
|
||||
LogToExternalFolderDisplayValue = IsLogToExternalFolderVisible ? TinkApp.LogToExternalFolder : false;
|
||||
|
||||
IsSiteCachingOnDisplayValue = TinkApp.IsSiteCachingOn;
|
||||
|
@ -148,8 +148,8 @@ namespace TINK.ViewModel
|
|||
|
||||
GeolocationServices = new ServicesViewModel(
|
||||
GeoloctionServicesContainer.Select(x => x.GetType().FullName),
|
||||
new Dictionary<string, string> {
|
||||
{ typeof(LastKnownGeolocationService).FullName, "LastKnowGeolocation" },
|
||||
new Dictionary<string, string> {
|
||||
{ typeof(LastKnownGeolocationService).FullName, "LastKnowGeolocation" },
|
||||
{ typeof(GeolocationAccuracyMediumService).FullName, "Medium Accuracy" },
|
||||
{ typeof(GeolocationAccuracyHighService).FullName, "High Accuracy" },
|
||||
{ typeof(GeolocationAccuracyBestService).FullName, "Best Accuracy" },
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
using MonkeyCache.FileStore;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Repository.Exception;
|
||||
using MonkeyCache.FileStore;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BC;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.User;
|
||||
using Xamarin.Forms;
|
||||
|
||||
using TINK.Model.Bikes.Bike.BC;
|
||||
using TINK.Repository;
|
||||
using System.Net;
|
||||
using TINK.MultilingualResources;
|
||||
using System.Linq;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Exception;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
|
@ -80,6 +79,11 @@ namespace TINK.ViewModel
|
|||
public static string GetDisplayName(this IBikeInfoMutable bike)
|
||||
=> $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}" : $"{bike.Id}")}";
|
||||
|
||||
public static string GetDisplayTypeOfBike(this IBikeInfoMutable bike)
|
||||
=> $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.TypeOfBike}" : $"{bike.Id}")}";
|
||||
public static string GetDisplayWheelType(this IBikeInfoMutable bike)
|
||||
=> $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.WheelType}" : $"{bike.Id}")}";
|
||||
|
||||
/// <summary> Get the display id of a bike.</summary>
|
||||
/// <remarks>
|
||||
/// If name is empty id is used as name. For this reason return nothing in this case to avoid duplicate output of id.
|
||||
|
@ -98,6 +102,7 @@ namespace TINK.ViewModel
|
|||
{
|
||||
switch (state)
|
||||
{
|
||||
case InUseStateEnum.FeedbackPending:
|
||||
case InUseStateEnum.Disposable:
|
||||
return Color.Default;
|
||||
|
||||
|
@ -106,6 +111,7 @@ namespace TINK.ViewModel
|
|||
|
||||
case InUseStateEnum.Booked:
|
||||
return Color.Green;
|
||||
|
||||
default:
|
||||
return Color.Default;
|
||||
}
|
||||
|
@ -158,11 +164,11 @@ namespace TINK.ViewModel
|
|||
return AppResources.ActivityTextErrorDeserializationException;
|
||||
if (exception is WebException webException)
|
||||
{
|
||||
return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse
|
||||
? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription)
|
||||
: string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString());
|
||||
return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse
|
||||
? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription)
|
||||
: string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString());
|
||||
}
|
||||
|
||||
|
||||
return AppResources.ActivityTextErrorException;
|
||||
#endif
|
||||
|
||||
|
@ -180,7 +186,7 @@ namespace TINK.ViewModel
|
|||
if (aggregateException.InnerExceptions.Count == 1)
|
||||
return aggregateException.InnerExceptions[0].Message;
|
||||
|
||||
return new AggregateException().Message + "\r\n"+ string.Join("\r\n", aggregateException.InnerExceptions.Select(x => x.Message));
|
||||
return new AggregateException().Message + "\r\n" + string.Join("\r\n", aggregateException.InnerExceptions.Select(x => x.Message));
|
||||
}
|
||||
|
||||
/// <summary> Gets message that logged in user has not booked any bikes. </summary>
|
||||
|
@ -291,7 +297,7 @@ namespace TINK.ViewModel
|
|||
|
||||
default:
|
||||
// Add resource to cache.
|
||||
Barrel.Current.Add(key: resourceUrl, data: htmlContent, expireIn: isSiteCachingOn ? TimeSpan.FromDays(1) : TimeSpan.FromMilliseconds(1) );
|
||||
Barrel.Current.Add(key: resourceUrl, data: htmlContent, expireIn: isSiteCachingOn ? TimeSpan.FromDays(1) : TimeSpan.FromMilliseconds(1));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
using TINK.View;
|
||||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using TINK.View;
|
||||
using TINK.ViewModel.Info;
|
||||
using TINK.Model.User.Account;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace TINK.ViewModel.WhatsNew.Agb
|
||||
{
|
||||
|
@ -20,21 +19,28 @@ namespace TINK.ViewModel.WhatsNew.Agb
|
|||
/// <summary> Holds value wether site caching is on or off.</summary>
|
||||
bool IsSiteCachingOn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current ui two letter ISO language name.
|
||||
/// </summary>
|
||||
private string UiIsoLanguageName { get; }
|
||||
|
||||
/// <summary> Constructs AGB view model</summary>
|
||||
/// <param name="isSiteCachingOn">Holds value wether site caching is on or off.</param>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <param name="resourceProvider">Delegate to get an an embedded html ressource. Used as fallback if download from web page does not work and cache is empty.</param>
|
||||
/// <param name="p_oViewService">View service to close page.</param>
|
||||
/// <param name="viewService">View service to close page.</param>
|
||||
public AgbViewModel(
|
||||
string hostName,
|
||||
bool isSiteCachingOn,
|
||||
string uiIsoLangugageName,
|
||||
Func<string, string> resourceProvider,
|
||||
IViewService p_oViewService)
|
||||
IViewService viewService)
|
||||
{
|
||||
HostName = hostName;
|
||||
|
||||
IsSiteCachingOn = isSiteCachingOn;
|
||||
UiIsoLanguageName = uiIsoLangugageName;
|
||||
|
||||
ViewService = p_oViewService
|
||||
ViewService = viewService
|
||||
?? throw new ArgumentException($"Can not instantiate {typeof(WhatsNewViewModel)}-object. No view available.");
|
||||
|
||||
ResourceProvider = resourceProvider
|
||||
|
@ -61,7 +67,7 @@ namespace TINK.ViewModel.WhatsNew.Agb
|
|||
/// <summary> Called when page is shown. </summary>
|
||||
public async Task OnAppearing()
|
||||
{
|
||||
InfoAgb = await InfoPageViewModel.GetAgb(HostName, "agbResourcePath", IsSiteCachingOn);
|
||||
InfoAgb = await InfoPageViewModel.GetAgb(HostName, "agbResourcePath", IsSiteCachingOn, UiIsoLanguageName);
|
||||
}
|
||||
|
||||
/// <summary> User clicks OK button.</summary>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using TINK.View;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using TINK.View;
|
||||
using Xamarin.Forms;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace TINK.ViewModel.WhatsNew
|
||||
{
|
||||
|
@ -16,7 +17,7 @@ namespace TINK.ViewModel.WhatsNew
|
|||
/// <param name="p_oViewService">View service to show agb- page.</param>
|
||||
public WhatsNewViewModel(
|
||||
Version currentVersion,
|
||||
string whatsNewText,
|
||||
IDictionary<Version, string> whatsNewText,
|
||||
bool isShowAgbRequired,
|
||||
Action showMasterDetail,
|
||||
IViewService p_oViewService)
|
||||
|
@ -29,13 +30,13 @@ namespace TINK.ViewModel.WhatsNew
|
|||
|
||||
CurrentVersion = currentVersion;
|
||||
WhatsNewText = new FormattedString();
|
||||
WhatsNewText.Spans.Add(new Span { Text = whatsNewText });
|
||||
WhatsNewText.Spans.Add(new Span { Text = GetWhatNextHtmlText(whatsNewText) });
|
||||
IsAgbChangedVisible = isShowAgbRequired;
|
||||
}
|
||||
|
||||
public Version CurrentVersion { get; }
|
||||
|
||||
public FormattedString WhatsNewText {get;}
|
||||
public FormattedString WhatsNewText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Title of the WhatsNewPage.
|
||||
|
@ -57,7 +58,7 @@ namespace TINK.ViewModel.WhatsNew
|
|||
var l_oHint = new FormattedString();
|
||||
l_oHint.Spans.Add(new Span { Text = "AGBs ", ForegroundColor = ViewModelHelper.LINK_COLOR });
|
||||
l_oHint.Spans.Add(new Span { Text = "überarbeitet.\r\n" });
|
||||
|
||||
|
||||
return l_oHint;
|
||||
}
|
||||
}
|
||||
|
@ -99,5 +100,8 @@ namespace TINK.ViewModel.WhatsNew
|
|||
|
||||
/// <summary>Reference to view service object.</summary>
|
||||
private Action ShowMasterDetail { get; }
|
||||
|
||||
public static string GetWhatNextHtmlText(IDictionary<Version, string> whatsNew)
|
||||
=> string.Join("", whatsNew.Select(element => $"<p><b>{element.Key}</b><br/>{element.Value}</p>").ToArray());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue