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.State; 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; public class DisposableClosed : Base, IRequestHandler { /// Provides info about the smart device (phone, tablet, ...). /// View model to be used for progress report and unlocking/ locking view. public DisposableClosed( IBikeInfoMutable selectedBike, Func isConnectedDelegate, Func connectorFactory, Func viewUpdateManager, ISmartDevice smartDevice, IViewService viewService, IBikesViewModel bikesViewModel, IUser activeUser) : base( selectedBike, AppResources.ActionOpenLockAndRentBike, // Button text: "Schloss öffnen & Rad mieten" true, // Show copri button to enable booking and opening isConnectedDelegate, connectorFactory, viewUpdateManager, smartDevice, viewService, bikesViewModel, activeUser) { LockitButtonText = AppResources.ActionReserveBike; // Copri text: "Rad reservieren" IsLockitButtonVisible = true; } /// Reserve bike and connect to lock. public async Task HandleRequestOption1() => await BookAndRelease(); public async Task HandleRequestOption2() => await Reserve(); /// Book bike and open lock. public async Task BookAndRelease() { BikesViewModel.IsIdle = false; // Ask whether to really book bike? var alertResult = await ViewService.DisplayAlert( string.Empty, string.Format(AppResources.QuestionOpenLockAndBookBike, SelectedBike.GetFullDisplayName()), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); if (alertResult == false) { // User aborted booking process Log.ForContext().Information("User selected bike {bike} in order to book and release bike from station but action was canceled.", SelectedBike); BikesViewModel.IsIdle = true; return this; } // Book and release bike from station. Log.ForContext().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().StopAsync(); // 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 bike {id} but booking failed (Copri server not reachable).", SelectedBike.Id); await ViewService.DisplayAlert( AppResources.ErrorNoConnectionTitle, AppResources.ErrorNoWeb, AppResources.MessageAnswerOk); } else { Log.ForContext().Error("User selected bike {id} but booking failed. {@exception}", SelectedBike.Id, exception); await ViewService.DisplayAlert( AppResources.ErrorRentingBikeTitle, exception.Message, AppResources.MessageAnswerOk); } BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartAsync(); // 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 released bike {bike} successfully.", SelectedBike); BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartAsync(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } /// Reserve bike. public async Task Reserve() { BikesViewModel.IsIdle = false; // Ask whether to really reserve bike? var alertResult = await ViewService.DisplayAlert( string.Empty, string.Format( AppResources.QuestionReserveBike, SelectedBike.GetFullDisplayName(), SelectedBike.TariffDescription?.MaxReservationTimeSpan.TotalMinutes ?? 0), AppResources.MessageAnswerYes, AppResources.MessageAnswerNo); if (alertResult == false) { // User aborted booking process Log.ForContext().Information("User selected centered bike {bike} in order to reserve but action was canceled.", SelectedBike); BikesViewModel.IsIdle = true; return this; } Log.ForContext().Information("Request to reserve for bike {bike} detected.", SelectedBike); BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; // Stop polling before requesting bike. await ViewUpdateManager().StopAsync(); 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().Information("Request declined because maximum count of bikes {l_oException.MaxBikesCount} already requested/ booked.", (exception as BookingDeclinedException).MaxBikesCount); await ViewService.DisplayAlert( AppResources.MessageHintTitle, string.Format(AppResources.ErrorReservingBikeTooManyReservationsRentals, SelectedBike.GetFullDisplayName(), (exception as BookingDeclinedException).MaxBikesCount), AppResources.MessageAnswerOk); } else if (exception is WebConnectFailureException || exception is RequestNotCachableException) { // Copri server is not reachable. Log.ForContext().Information("User selected centered bike {bike} but reserving failed (Copri server not reachable).", SelectedBike); await ViewService.DisplayAlert( AppResources.ErrorNoConnectionTitle, AppResources.ErrorNoWeb, AppResources.MessageAnswerOk); } else { Log.ForContext().Error("User selected centered bike {bike} but reserving failed. {@exception}", SelectedBike, exception); await ViewService.DisplayAlert( AppResources.ErrorReservingBikeTitle, exception.Message, AppResources.MessageAnswerOk); } // Restart polling again. BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; return this; } Log.ForContext().Information("User reserved bike {bike} successfully.", SelectedBike); BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater; await ViewUpdateManager().StartAsync(); // Restart polling again. BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; // Unlock GUI return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); } } }