2022-10-26 20:53:18 +02:00
using System ;
2022-04-25 22:15:15 +02:00
using System.Threading.Tasks ;
2022-08-30 15:42:25 +02:00
using Serilog ;
using TINK.Model.Bikes.BikeInfoNS.CopriLock ;
2022-04-25 22:15:15 +02:00
using TINK.Model.Connector ;
2022-08-30 15:42:25 +02:00
using TINK.Model.Device ;
using TINK.Model.User ;
using TINK.MultilingualResources ;
2022-04-25 22:15:15 +02:00
using TINK.Repository.Exception ;
2022-10-26 20:53:18 +02:00
using TINK.Services.CopriApi.Exception ;
2022-04-25 22:15:15 +02:00
using TINK.View ;
namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler
{
2022-09-06 16:08:19 +02:00
using IRequestHandler = BluetoothLock . IRequestHandler ;
2023-04-19 12:14:14 +02:00
/// <summary> Bike is reserved, lock is closed and connected to app. </summary>
2022-09-06 16:08:19 +02:00
/// <remarks>
2023-04-19 12:14:14 +02:00
/// Occurs when
/// - bike was reserved out of reach and is in reach now
/// - bike is reserved while in reach
2022-09-06 16:08:19 +02:00
/// </remarks>
public class ReservedClosed : 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 ReservedClosed (
IBikeInfoMutable selectedBike ,
Func < bool > isConnectedDelegate ,
Func < bool , IConnector > connectorFactory ,
Func < IPollingUpdateTaskManager > viewUpdateManager ,
ISmartDevice smartDevice ,
IViewService viewService ,
IBikesViewModel bikesViewModel ,
IUser activeUser ) : base (
selectedBike ,
AppResources . ActionCancelRequest , // Copri button text: "Reservierung abbrechen"
true , // Show button to enable canceling reservation.
isConnectedDelegate ,
connectorFactory ,
viewUpdateManager ,
smartDevice ,
viewService ,
bikesViewModel ,
activeUser )
{
LockitButtonText = AppResources . ActionOpenAndBook ; // Button text: "Schloss öffnen & Rad mieten"
IsLockitButtonVisible = true ; // Show "Öffnen" button to enable unlocking
}
/// <summary> Cancel reservation. </summary>
public async Task < IRequestHandler > HandleRequestOption1 ( ) = > await CancelReservation ( ) ;
/// <summary> Open lock and book bike. </summary>
public async Task < IRequestHandler > HandleRequestOption2 ( ) = > await OpenLockAndDoBook ( ) ;
/// <summary> Cancel reservation. </summary>
public async Task < IRequestHandler > CancelReservation ( )
{
BikesViewModel . IsIdle = false ; // Lock list to avoid multiple taps while copri action is pending.
var result = await ViewService . DisplayAlert (
string . Empty ,
string . Format ( AppResources . QuestionCancelReservation , SelectedBike . GetFullDisplayName ( ) ) ,
AppResources . QuestionAnswerYes ,
AppResources . QuestionAnswerNo ) ;
if ( result = = false )
{
// User aborted cancel process
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 {Id} in order to cancel reservation." , SelectedBike . Id ) ;
// Stop polling before cancel request.
BikesViewModel . ActionText = AppResources . ActivityTextOneMomentPlease ;
await ViewUpdateManager ( ) . StopUpdatePeridically ( ) ;
BikesViewModel . ActionText = AppResources . ActivityTextCancelingReservation ;
IsConnected = IsConnectedDelegate ( ) ;
try
{
await ConnectorFactory ( IsConnected ) . Command . DoCancelReservation ( SelectedBike ) ;
2023-04-19 12:14:14 +02:00
// If canceling bike succeeds remove bike because it is not ready to be booked again
2022-09-06 16:08:19 +02:00
IsRemoveBikeRequired = true ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
if ( exception is InvalidAuthorizationResponseException )
{
// Copri response is invalid.
Log . ForContext < BikesViewModel > ( ) . Error ( "User selected reserved bike {Id} but canceling reservation failed (Invalid auth. response)." , SelectedBike . Id ) ;
await ViewService . DisplayAlert (
AppResources . MessageCancelReservationBikeErrorGeneralTitle ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
}
2022-10-26 20:53:18 +02:00
else if ( exception is WebConnectFailureException
| | exception is RequestNotCachableException )
2022-09-06 16:08:19 +02:00
{
// Copri server is not reachable.
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 {Id} but cancel reservation failed. {@Exception}." , SelectedBike . Id , exception ) ;
await ViewService . DisplayAlert (
AppResources . MessageCancelReservationBikeErrorGeneralTitle ,
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 , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
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 , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
/// <summary> Open lock and book bike. </summary>
public async Task < IRequestHandler > OpenLockAndDoBook ( )
{
BikesViewModel . IsIdle = false ;
// Ask whether to really book bike?
var l_oResult = await ViewService . DisplayAlert (
string . Empty ,
string . Format ( AppResources . QuestionOpenLockAndBookBike , SelectedBike . GetFullDisplayName ( ) ) ,
AppResources . MessageAnswerYes ,
AppResources . MessageAnswerNo ) ;
if ( l_oResult = = false )
{
// User aborted booking process
Log . ForContext < ReservedClosed > ( ) . Information ( "User selected requested bike {bike} in order to book but action was canceled." , SelectedBike ) ;
BikesViewModel . IsIdle = true ;
return this ;
}
Log . ForContext < ReservedClosed > ( ) . Information ( "User selected requested bike {bike} in order to book." , SelectedBike ) ;
// Stop polling before cancel request.
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 ) ;
}
catch ( Exception exception )
{
BikesViewModel . ActionText = string . Empty ;
if ( exception is WebConnectFailureException )
{
// Copri server is not reachable.
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 ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
}
else
{
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 ,
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 , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
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 , ViewUpdateManager , SmartDevice , ViewService , BikesViewModel , ActiveUser ) ;
}
}
2022-04-25 22:15:15 +02:00
}