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.Services.CopriApi.Exception; using TINK.View; namespace TINK.ViewModel.Bikes.Bike.CopriLock.RequestHandler { using IRequestHandler = BluetoothLock.IRequestHandler; /// Bike is reserved, lock is closed and and connected to app. /// /// Occures when /// - biks was reserved out of reach and is in reach now /// - bike is is reserved while in reach /// public class ReservedClosed : Base, IRequestHandler { /// Provides info about the smart device (phone, tablet, ...) /// View model to be used for progress report and unlocking/ locking view. public ReservedClosed( IBikeInfoMutable selectedBike, Func isConnectedDelegate, Func connectorFactory, Func 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 } /// Cancel reservation. public async Task HandleRequestOption1() => await CancelReservation(); /// Open lock and book bike. public async Task HandleRequestOption2() => await OpenLockAndDoBook(); /// Cancel reservation. public async Task 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().Information("User selected reserved bike {Id} in order to cancel reservation but action was canceled.", SelectedBike.Id); BikesViewModel.IsIdle = true; return this; } Log.ForContext().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); // 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 InvalidAuthorizationResponseException) { // Copri response is invalid. Log.ForContext().Error("User selected reserved bike {Id} but canceling reservation failed (Invalid auth. response).", SelectedBike.Id); await ViewService.DisplayAlert( AppResources.MessageCancelReservationBikeErrorGeneralTitle, exception.Message, AppResources.MessageAnswerOk); } else if (exception is WebConnectFailureException || exception is RequestNotCachableException) { // Copri server is not reachable. Log.ForContext().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().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().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); } /// Open lock and book bike. public async Task 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().Information("User selected requested bike {bike} in order to book but action was canceled.", SelectedBike); BikesViewModel.IsIdle = true; return this; } Log.ForContext().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().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().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().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); } } }