1347 lines
52 KiB
C#

using System.Collections.ObjectModel;
using System.Text.Json;
using System.Web;
using CommunityToolkit.Maui.Core.Extensions;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using gehGassi.Dto;
using gehGassi.Dto.Common;
using gehGassi.Dto.Walks;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Mapper;
using gehGassiApp.Core.Messaging;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.Dogs;
using gehGassiApp.Domain.Lookup;
using gehGassiApp.Domain.Walkers;
using gehGassiApp.Resources;
using gehGassiApp.Services;
using gehGassiApp.Views.Dogs;
using gehGassiApp.Views.More;
using gehGassiApp.Views.Payments;
using gehGassiApp.Views.Walks;
using Microsoft.Maui.Handlers;
namespace gehGassiApp.ViewModels.Walks
{
/// <summary>
/// Viewmodel für das Buchen eines Walks
/// </summary>
public partial class BookWalkViewModel : MenuViewModel, IQueryAttributable
{
private readonly IDogWalkerService _dogWalkerService;
private readonly IDogService _dogService;
private readonly ICountryService _countryService;
private readonly ILocationService _locationService;
private readonly IWalkService _walkService;
private readonly IWalkingTimeService _walkingTimeService;
private readonly IDialogService _dialogService;
private readonly IDispatcher _dispatcher;
private string _walkerId;
private bool _countryHandlerEnabled;
private bool _bookNow;
private int _addressChangedCounter;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="dogWalkerService">Instanz eines IDogWalkerService</param>
/// <param name="dogService">Instanz eines IDogService</param>
/// <param name="countryService">Instanz eines ICountryService</param>
/// <param name="locationService">Instanz eines ILocationService</param>
/// <param name="walkService">Instanz eines IWalkService</param>
/// <param name="walkingTimeService">Instanz eines IWalkingTimeService</param>
/// <param name="dialogService">Instanz eines IDialogService</param>
/// <param name="dispatcher">Instanz eines IDispatcher</param>
public BookWalkViewModel(IDogWalkerService dogWalkerService, IDogService dogService, ICountryService countryService, ILocationService locationService, IWalkService walkService,
IWalkingTimeService walkingTimeService, IDialogService dialogService, IDispatcher dispatcher)
{
_dogWalkerService = dogWalkerService;
_dogService = dogService;
_countryService = countryService;
_locationService = locationService;
_walkService = walkService;
_walkingTimeService = walkingTimeService;
_dialogService = dialogService;
_dispatcher = dispatcher;
_bookNow = false;
_addressChangedCounter = 0;
Dogs = new ObservableCollection<Dog>();
SelectedDogs = new ObservableCollection<object>();
DogsSelected = false;
Title = Text.View_Title_Walker;
SelectedMenu = MenuSelected.Walkers;
IsLoading = true;
//Event wenn Refresh für diesen View gesetzt werden soll
if (WeakReferenceMessenger.Default.IsRegistered<RefreshViewModelMessage>(this) == false)
WeakReferenceMessenger.Default.Register<RefreshViewModelMessage>(this, (recipient, message) =>
{
if (message.Value is Core.Messaging.Messages.Refresh_BookWalk or Core.Messaging.Messages.Refresh_All)
{
_refresh = true;
}
if (message.Value is Core.Messaging.Messages.Special_BookNow)
{
_bookNow = true;
}
});
}
/// <summary>
/// Benutzer hat keinen Hund
/// </summary>
[ObservableProperty]
private bool _hasNoDog;
/// <summary>
/// Liste der verfügbaren Hunde
/// </summary>
[ObservableProperty]
private ObservableCollection<Dog> _dogs;
/// <summary>
/// Liste der gewählten Hunde
/// </summary>
[ObservableProperty]
private ObservableCollection<object> _selectedDogs;
/// <summary>
/// Wurde zumindest ein Hund gewählt
/// </summary>
[ObservableProperty]
private bool _dogsSelected;
[ObservableProperty]
private DogWalkerInfo _walker;
/// <summary>
/// Walk möglich
/// </summary>
[ObservableProperty]
private bool _walkAllowed;
/// <summary>
/// Sitting möglich
/// </summary>
[ObservableProperty]
private bool _sittingAllowed;
[ObservableProperty]
private ObservableCollection<WalkingTime> _walkingTimes;
[ObservableProperty]
private WalkServiceType _serviceType;
[ObservableProperty]
private DateTime _startDate;
[ObservableProperty]
private TimeSpan _startTime;
[ObservableProperty]
private DateTime _endDate;
[ObservableProperty]
private TimeSpan _endTime;
[ObservableProperty]
private Address _pickupAddress;
[ObservableProperty]
private ObservableCollection<Country> _pickupCountries;
[ObservableProperty]
private ObservableCollection<State> _pickupStates;
[ObservableProperty]
private Country _selectedPickupCountry;
[ObservableProperty]
private State _selectedPickupState;
[ObservableProperty]
private bool _pickupAddressValid;
[ObservableProperty]
private bool _showPickupAddress;
[ObservableProperty]
private Address _returnAddress;
[ObservableProperty]
private ObservableCollection<Country> _returnCountries;
[ObservableProperty]
private ObservableCollection<State> _returnStates;
[ObservableProperty]
private Country _selectedReturnCountry;
[ObservableProperty]
private State _selectedReturnState;
[ObservableProperty]
private bool _returnAddressValid;
[ObservableProperty]
private bool _showReturnAddress;
[ObservableProperty]
private string _info;
[ObservableProperty]
private decimal _price;
[ObservableProperty]
private bool _directBooking;
#region Extended
partial void OnServiceTypeChanged(WalkServiceType value)
{
if (IsBusy || _refresh)
return;
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnSelectedPickupCountryChanged(Country value)
{
if (IsBusy || _refresh)
return;
if (!_countryHandlerEnabled)
return;
_dispatcher.DispatchAsync(async () =>
{
PickupAddressValid = false;
SelectedPickupState = null;
if (value != null && !string.IsNullOrWhiteSpace(value.Iso2))
{
var states = await _countryService.GetStatesAsync(value.Iso2);
PickupStates = states.ToObservableCollection();
}
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnSelectedPickupStateChanged(State value)
{
if (IsBusy || _refresh)
return;
PickupAddressValid = SelectedPickupState != null;
_dispatcher.DispatchAsync(async () =>
{
PickupAddress.CountryCode = SelectedPickupCountry.Iso2;
PickupAddress.State = SelectedPickupState != null ? SelectedPickupState.Code : string.Empty;
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnPickupAddressChanged(Address value)
{
if (IsBusy || _refresh)
return;
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnSelectedReturnCountryChanged(Country value)
{
if (!_countryHandlerEnabled)
return;
if (IsBusy || _refresh)
return;
_dispatcher.DispatchAsync(async () =>
{
ReturnAddressValid = false;
SelectedReturnState = null;
if (value != null && !string.IsNullOrWhiteSpace(value.Iso2))
{
var states = await _countryService.GetStatesAsync(value.Iso2);
ReturnStates = states.ToObservableCollection();
}
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnSelectedReturnStateChanged(State value)
{
if (IsBusy || _refresh)
return;
ReturnAddressValid = SelectedReturnState != null;
_dispatcher.DispatchAsync(async () =>
{
ReturnAddress.CountryCode = SelectedReturnCountry.Iso2;
ReturnAddress.State = SelectedReturnState != null ? SelectedReturnState.Code : string.Empty;
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnReturnAddressChanged(Address value)
{
if (IsBusy || _refresh)
return;
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
partial void OnStartDateChanged(DateTime value)
{
if (IsBusy || _refresh)
return;
if (StartDate.Date == DateTime.Now.Date && StartTime.Hours <= DateTime.Now.Hour)
{
var helpDate = new DateTime(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds);
helpDate = helpDate.AddHours(1);
StartDate = helpDate.Date;
StartTime = helpDate.TimeOfDay;
}
HandleStartAndEndDateChange();
}
partial void OnStartTimeChanged(TimeSpan value)
{
if (IsBusy || _refresh)
return;
if (StartDate.Date == DateTime.Now.Date && StartTime.Hours <= DateTime.Now.Hour)
{
var helpDate = new DateTime(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds);
helpDate = helpDate.AddHours(1);
StartDate = helpDate.Date;
StartTime = helpDate.TimeOfDay;
}
HandleStartAndEndDateChange();
}
partial void OnEndDateChanged(DateTime value)
{
if (IsBusy || _refresh)
return;
HandleStartAndEndDateChange();
}
partial void OnEndTimeChanged(TimeSpan value)
{
if (IsBusy || _refresh)
return;
HandleStartAndEndDateChange();
}
/// <summary>
/// Behandeln der Änderungen für Startdatum und Zeit wie euch Enddatum und Zeit
/// </summary>
private void HandleStartAndEndDateChange()
{
if (IsBusy || _refresh)
return;
var helpDate = new DateTime(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds);
if (EndDate <= StartDate)
{
if (EndTime <= StartTime)
{
helpDate = helpDate.AddMinutes(60);
EndDate = helpDate.Date;
EndTime = helpDate.TimeOfDay;
}
}
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
#endregion
#region Commands
/// <summary>
/// Command wenn sich die Hundeauswahl geändert hat
/// </summary>
[RelayCommand]
public void DogSelectionChanged()
{
DogsSelected = SelectedDogs.Count > 0;
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
/// <summary>
/// Service-Typ auswählen
/// </summary>
/// <param name="serviceType"></param>
[RelayCommand]
public void SelectServiceType(WalkServiceType serviceType)
{
ServiceType = serviceType;
_dispatcher.DispatchAsync(async () =>
{
await CheckErrorAsync();
CalculatePrice();
});
}
/// <summary>
/// Command für das Anlegen eines Hundes
/// </summary>
/// <returns></returns>
[RelayCommand]
public async Task CreateDog()
{
await Shell.Current.GoToAsync($"{nameof(DogAddView)}", Core.Common.Constants.AnimateNavigation);
}
/// <summary>
/// Toggle der Adressanzeige
/// </summary>
[RelayCommand]
public void TogglePickupAddress()
{
ShowPickupAddress = !ShowPickupAddress;
}
/// <summary>
/// Toggle der Adressanzeige
/// </summary>
[RelayCommand]
public void ToggleReturnAddress()
{
ShowReturnAddress = !ShowReturnAddress;
}
/// <summary>
/// Aktuelle Adresse basierend auf Geo-Location holen
/// </summary>
/// <returns>Task</returns>
[RelayCommand]
public async Task SetPickupAddress()
{
var address = await _locationService.GetAddressByCurrentLocationAsync();
if (address != null)
{
PickupAddress = address;
SelectedPickupCountry = PickupCountries.First(c => c.Iso2 == PickupAddress.CountryCode);
var states = await _countryService.GetStatesAsync(SelectedPickupCountry.Iso2);
PickupStates = states.ToObservableCollection();
SelectedPickupState = PickupStates.First(c => c.Code == PickupAddress.State);
await CheckErrorAsync();
CalculatePrice();
}
}
/// <summary>
/// Command wenn sich die Abholadresse ändert
/// </summary>
/// <returns></returns>
[RelayCommand]
public async Task PickupAddressChanged()
{
if(_addressChangedCounter <= 8)
_addressChangedCounter += 1;
if (_addressChangedCounter > 8)
{
await CheckErrorAsync();
CalculatePrice();
}
}
/// <summary>
/// Command wenn sich die Rückgabeadresse ändert
/// </summary>
/// <returns></returns>
[RelayCommand]
public async Task ReturnAddressChanged()
{
if (_addressChangedCounter <= 8)
_addressChangedCounter += 1;
if (_addressChangedCounter > 8)
{
await CheckErrorAsync();
CalculatePrice();
}
}
/// <summary>
/// Aktuelle Adresse basierend auf Geo-Location holen
/// </summary>
/// <returns>Task</returns>
[RelayCommand]
public async Task SetReturnAddress()
{
var address = await _locationService.GetAddressByCurrentLocationAsync();
if (address != null)
{
ReturnAddress = address;
SelectedReturnCountry = ReturnCountries.First(c => c.Iso2 == ReturnAddress.CountryCode);
var states = await _countryService.GetStatesAsync(SelectedReturnCountry.Iso2);
ReturnStates = states.ToObservableCollection();
SelectedReturnState = ReturnStates.First(c => c.Code == ReturnAddress.State);
await CheckErrorAsync();
CalculatePrice();
}
}
/// <summary>
/// Kopiert die Rückgabeadresse in die Abholadresse
/// </summary>
[RelayCommand]
public async Task CopyReturnAddress()
{
PickupAddress.AddressLine1 = ReturnAddress.AddressLine1;
PickupAddress.AddressLine2 = ReturnAddress.AddressLine2;
PickupAddress.City = ReturnAddress.City;
PickupAddress.Zip = ReturnAddress.Zip;
SelectedPickupCountry = PickupCountries.First(c => c.Iso2 == SelectedReturnCountry.Iso2);
var states = await _countryService.GetStatesAsync(SelectedReturnCountry.Iso2);
PickupStates = states.ToObservableCollection();
SelectedPickupState = PickupStates.First(c => c.Code == SelectedReturnState.Code);
ShowPickupAddress = false;
}
/// <summary>
/// Kopiert die Abholadresse in die Rückgabeadresse
/// </summary>
[RelayCommand]
public async Task CopyPickupAddress()
{
ReturnAddress.AddressLine1 = PickupAddress.AddressLine1;
ReturnAddress.AddressLine2 = PickupAddress.AddressLine2;
ReturnAddress.City = PickupAddress.City;
ReturnAddress.Zip = PickupAddress.Zip;
SelectedReturnCountry = ReturnCountries.First(c => c.Iso2 == SelectedPickupCountry.Iso2);
var states = await _countryService.GetStatesAsync(SelectedReturnCountry.Iso2);
ReturnStates = states.ToObservableCollection();
SelectedReturnState = ReturnStates.First(c => c.Code == SelectedPickupState.Code);
ShowReturnAddress = false;
}
/// <summary>
/// Auf Fehler prüfen
/// </summary>
/// <returns></returns>
[RelayCommand]
public async Task CheckErrors()
{
await CheckErrorAsync();
}
/// <summary>
/// Command für das Öffnen des Kalenders
/// </summary>
[RelayCommand]
public void ShowCalendar(object obj)
{
if (obj is DatePicker picker)
{
#if ANDROID
//https://github.com/dotnet/maui/issues/8946
if (picker.Handler is IDatePickerHandler handler)
handler.PlatformView.PerformClick();
#endif
#if IOS
picker.Focus();
#endif
}
}
/// <summary>
/// Sofort-Buchen eines Walks
/// </summary>
/// <returns>Task</returns>
[RelayCommand]
public async Task BookNow()
{
if (!HasError && DirectBooking)
{
if (string.IsNullOrWhiteSpace(App.CurrentAppUser.PaymentId))
{
//Der User hat noch keinen PaymentUser...
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_AddPaymentUser));
await Shell.Current.GoToAsync($"{nameof(AddPaymentUserView)}", Core.Common.Constants.AnimateNavigation);
return;
}
var userOk = await _dialogService.ShowAlertAsync(Resources.Text.Common_Book, string.Format(Resources.Text.Walk_BookNow_Question, Walker.FirstName, Price.ToString("N2")), Resources.Text.Common_Yes, Resources.Text.Common_No);
if (userOk)
{
IsSaving = true;
await _dispatcher.DispatchAsync(async () =>
{
var selectedDogs = SelectedDogs.Cast<Dog>().ToList();
var dogWalkJsonList = selectedDogs.Select(selectedDog => new DogWalkJson() { Id = selectedDog.Id, Name = selectedDog.Name }).ToList();
var helpStartDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds, DateTimeOffset.Now.Offset);
var helpEndDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, EndTime.Seconds, DateTimeOffset.Now.Offset);
var walkCreateDto = new WalkCreateDto()
{
Type = WalkTypeDto.Scheduled,
ServiceType = (WalkServiceTypeDto)ServiceType,
DogWalkerId = Walker.Id,
DogOwnerId = App.CurrentAppUser.Id,
DogsJson = JsonSerializer.Serialize(dogWalkJsonList, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
DogCount = selectedDogs.Count,
Start = helpStartDate,
End = helpEndDate,
PickupAddress = PickupAddress.ToDto(),
ReturnAddress = ReturnAddress.ToDto(),
Price = Price,
Info = Info
};
#region Pickupaddress
//Lat & Lng für die Adressen setzen
var lat = App.CurrentAppUser.Lat;
var lng = App.CurrentAppUser.Lng;
var location = await _locationService.GetLocationByAddressAsync(PickupAddress);
if (location != null)
{
lat = location.Latitude;
lng = location.Longitude;
}
walkCreateDto.PickupAddressLat = lat;
walkCreateDto.PickupAddressLng = lng;
#endregion
#region ReturnAddress
lat = App.CurrentAppUser.Lat;
lng = App.CurrentAppUser.Lng;
location = await _locationService.GetLocationByAddressAsync(ReturnAddress);
if (location != null)
{
lat = location.Latitude;
lng = location.Longitude;
}
walkCreateDto.ReturnAddressLat = lat;
walkCreateDto.ReturnAddressLng = lng;
#endregion
//Jetzt am Server anlegen. Wird bei Erfolg auch lokal gespeichert.
using var cts = new CancellationTokenSource(Core.Common.Constants.CreateTimeout);
var walkResponse = await _walkService.CreateWalkDirectAsync(walkCreateDto, App.CurrentUser.AccessToken, cts.Token);
if (walkResponse.Success)
{
if (walkResponse.Value.PaymentStatus == PaymentStatus.Authorized)
{
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Walks));
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Home));
var stackCount = Shell.Current.Navigation.NavigationStack.Count;
if (stackCount > 1)
{
//await Shell.Current.GoToAsync("..");
if (DeviceInfo.Platform == DevicePlatform.Android)
await Shell.Current.Navigation.PopToRootAsync();
}
await Shell.Current.GoToAsync($"//{nameof(WalksView)}", Core.Common.Constants.AnimateNavigation);
}
else
{
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Home));
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Walks));
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_WalksList));
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Payment));
var menu = SelectedMenu.ToString();
await Shell.Current.GoToAsync($"{nameof(PayWalkView)}?walkId={walkResponse.Value.Id}&menu={menu}", Core.Common.Constants.AnimateNavigation);
}
}
else
{
if (walkResponse.ErrorCode == CommunicationErrors.Walk_NotPossible)
await _dialogService.ShowAlertAsync(Resources.Text.Common_Error, walkResponse.ErrorMessage, Resources.Text.Button_Ok);
else
await _dialogService.ShowAlertAsync(Resources.Text.Common_Error, Resources.Text.Walk_Book_Error, Resources.Text.Button_Ok);
}
});
IsSaving = false;
}
}
}
/// <summary>
/// Anfrage für einen Walk stellen
/// </summary>
/// <returns>Task</returns>
[RelayCommand]
public async Task RequestNow()
{
if (!HasError)
{
var userOk = await _dialogService.ShowAlertAsync(Resources.Text.Common_Request, string.Format(Resources.Text.Walk_Request_Question, Walker.FirstName, Price.ToString("N2")), Resources.Text.Common_Yes, Resources.Text.Common_No);
if (userOk)
{
IsSaving = true;
await _dispatcher.DispatchAsync(async () =>
{
var selectedDogs = SelectedDogs.Cast<Dog>().ToList();
var dogWalkJsonList = selectedDogs.Select(selectedDog => new DogWalkJson() { Id = selectedDog.Id, Name = selectedDog.Name }).ToList();
var helpStartDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds, DateTimeOffset.Now.Offset);
var helpEndDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, EndTime.Seconds, DateTimeOffset.Now.Offset);
var walkCreateDto = new WalkCreateDto()
{
Type = WalkTypeDto.Direct,
ServiceType = (WalkServiceTypeDto)ServiceType,
DogWalkerId = Walker.Id,
DogOwnerId = App.CurrentAppUser.Id,
DogsJson = JsonSerializer.Serialize(dogWalkJsonList, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
DogCount = selectedDogs.Count,
Start = helpStartDate,
End = helpEndDate,
PickupAddress = PickupAddress.ToDto(),
ReturnAddress = ReturnAddress.ToDto(),
Price = Price,
Info = Info
};
#region Pickupaddress
//Lat & Lng für die Adressen setzen
var lat = App.CurrentAppUser.Lat;
var lng = App.CurrentAppUser.Lng;
var location = await _locationService.GetLocationByAddressAsync(PickupAddress);
if (location != null)
{
lat = location.Latitude;
lng = location.Longitude;
}
walkCreateDto.PickupAddressLat = lat;
walkCreateDto.PickupAddressLng = lng;
#endregion
#region ReturnAddress
lat = App.CurrentAppUser.Lat;
lng = App.CurrentAppUser.Lng;
location = await _locationService.GetLocationByAddressAsync(ReturnAddress);
if (location != null)
{
lat = location.Latitude;
lng = location.Longitude;
}
walkCreateDto.ReturnAddressLat = lat;
walkCreateDto.ReturnAddressLng = lng;
#endregion
//Jetzt am Server anlegen. Wird bei Erfolg auch lokal gespeichert.
using var cts = new CancellationTokenSource(Core.Common.Constants.CreateTimeout);
var walkResponse = await _walkService.CreateWalkRequestAsync(walkCreateDto, App.CurrentUser.AccessToken, cts.Token);
if (walkResponse.Success)
{
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Walks));
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Home));
var stackCount = Shell.Current.Navigation.NavigationStack.Count;
if (stackCount > 1)
{
//await Shell.Current.GoToAsync("..");
await Shell.Current.Navigation.PopToRootAsync();
}
await Shell.Current.GoToAsync($"//{nameof(WalksView)}", Core.Common.Constants.AnimateNavigation);
}
else
{
if(walkResponse.ErrorCode == CommunicationErrors.Walk_NotPossible)
await _dialogService.ShowAlertAsync(Resources.Text.Common_Error, walkResponse.ErrorMessage, Resources.Text.Button_Ok);
else
await _dialogService.ShowAlertAsync(Resources.Text.Common_Error, Resources.Text.Walk_Book_Error, Resources.Text.Button_Ok);
}
});
IsSaving = false;
}
}
}
#endregion
/// <summary>
/// Laden der initialen Daten
/// </summary>
/// <returns></returns>
public async Task LoadAsync()
{
await _dispatcher.DispatchAsync(async () =>
{
IsBusy = true;
WalkAllowed = false;
SittingAllowed = false;
ServiceType = WalkServiceType.Walking;
Price = 0m;
DirectBooking = false;
DogsSelected = false;
SelectedDogs = new ObservableCollection<object>();
await LoadDogsAsync();
await LoadWalkerAsync();
if (Walker == null)
{
var stackCount = Shell.Current.Navigation.NavigationStack.Count;
if (stackCount > 1)
{
await Shell.Current.Navigation.PopToRootAsync();
}
}
if(Walker.AllowedRequests == DogWalkRequestType.Walking || Walker.AllowedRequests == DogWalkRequestType.Both)
ServiceType = WalkServiceType.Walking;
else if(Walker.AllowedRequests == DogWalkRequestType.Sitting)
ServiceType = WalkServiceType.Sitting;
switch (Walker.AllowedRequests)
{
case DogWalkRequestType.None:
break;
case DogWalkRequestType.Walking:
WalkAllowed = true;
break;
case DogWalkRequestType.Sitting:
SittingAllowed = true;
break;
case DogWalkRequestType.Both:
WalkAllowed = true;
SittingAllowed = true;
break;
default:
throw new ArgumentOutOfRangeException();
}
var dateNow = DateTimeOffset.UtcNow.LocalDateTime;
var dateHelper = new DateTimeOffset(dateNow.Year, dateNow.Month, dateNow.Day, dateNow.Hour, 0, 0, DateTimeOffset.Now.Offset);
dateHelper = dateHelper.AddDays(1);
StartDate = dateHelper.Date;
StartTime = dateHelper.TimeOfDay;
dateHelper = dateHelper.AddHours(1);
EndDate = dateHelper.Date;
EndTime = dateHelper.TimeOfDay;
_countryHandlerEnabled = false;
PickupAddress = App.CurrentAppUser.Address.Clone();
ReturnAddress = App.CurrentAppUser.Address.Clone();
var countries = await _countryService.GetCountriesAsync();
PickupCountries = countries.ToObservableCollection();
SelectedPickupCountry = PickupCountries.First(c => c.Iso2 == PickupAddress.CountryCode);
var states = await _countryService.GetStatesAsync(PickupAddress.CountryCode);
PickupStates = states.ToObservableCollection();
SelectedPickupState = PickupStates.First(c => c.Code == PickupAddress.State);
countries = await _countryService.GetCountriesAsync();
ReturnCountries = countries.ToObservableCollection();
SelectedReturnCountry = ReturnCountries.First(c => c.Iso2 == ReturnAddress.CountryCode);
states = await _countryService.GetStatesAsync(ReturnAddress.CountryCode);
ReturnStates = states.ToObservableCollection();
SelectedReturnState = ReturnStates.First(c => c.Code == ReturnAddress.State);
if (SelectedPickupCountry != null && SelectedPickupState != null)
PickupAddressValid = true;
if (SelectedReturnCountry != null && SelectedReturnState != null)
ReturnAddressValid = true;
_countryHandlerEnabled = true;
IsBusy = false;
await CheckErrorAsync();
CalculatePrice();
IsLoading = false;
});
}
/// <summary>
/// Laden der Hunde des Benutzers
/// </summary>
/// <returns>Task</returns>
private async Task LoadDogsAsync()
{
using var cts = new CancellationTokenSource(Core.Common.Constants.ListTimeout);
//Zuerst die Rassen laden, falls diese noch nicht synchronisiert wurden...
var dogs = await _dogService.GetDogsAsync(App.CurrentAppUser.Id, App.CurrentUser.AccessToken, cts.Token);
if (dogs != null && dogs.Any())
{
Dogs = dogs.ToObservableCollection();
}
else
{
Dogs?.Clear();
}
HasNoDog = Dogs.Count == 0;
}
/// <summary>
/// Walker laden
/// </summary>
/// <returns></returns>
private async Task LoadWalkerAsync()
{
using var cts = new CancellationTokenSource(Core.Common.Constants.ListTimeout);
var walkerResult = await _dogWalkerService.GetDogWalkerAsync(_walkerId, App.CurrentUser.AccessToken, cts.Token);
if (walkerResult.Success && walkerResult.Value != null)
{
var walker = walkerResult.Value;
if (CurrentLocation.Lat != null && CurrentLocation.Lng != null)
walker.Distance = Location.CalculateDistance(CurrentLocation.Lat.Value, CurrentLocation.Lng.Value, walker.Lat, walker.Lng, DistanceUnits.Kilometers);
Walker = walker;
Title = string.Format(Resources.Text.BookWalk_Title, Walker.FirstName);
var walkingTimesResult = await _walkingTimeService.GetAllAsync(Walker.Id, true, App.CurrentUser.AccessToken, cts.Token);
if (walkingTimesResult.Success && walkingTimesResult.Value.Any(c => c.Enabled))
{
WalkingTimes = walkingTimesResult.Value.Where(c => c.Enabled).ToObservableCollection();
}
else
{
WalkingTimes = null;
}
}
else
{
Walker = null;
}
}
#region Initialisierung
/// <inheritdoc />
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
_walkerId = HttpUtility.UrlDecode(query["id"].ToString());
var selectedMenu = HttpUtility.UrlDecode(query["menu"].ToString());
SelectedMenu = (MenuSelected)Enum.Parse(typeof(MenuSelected), selectedMenu);
}
/// <summary>
/// Initialisieren des Viewmodels.
/// Soll überschrieben werden um lazy loading in Viewmodels ermöglicht wird
/// </summary>
/// <returns>Task</returns>
public override async Task InitializeAsync(object sender)
{
await base.InitializeAsync(sender);
IsBusy = false;
HasError = false;
ErrorMessage = string.Empty;
await TrackPageViewAsync("BookWalk");
if (App.AppMode == AppMode.DogOwner)
{
CheckSubscription(AppMode.DogOwner);
}
if (_refresh)
{
_addressChangedCounter = 0;
IsLoading = true;
IsBusy = true;
WalkAllowed = false;
SittingAllowed = false;
Info = string.Empty;
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
//await LoadAsync();
#pragma warning disable CS4014
Task.Run(LoadAsync);
#pragma warning restore CS4014
_refresh = false;
}
else
{
if (_bookNow)
{
await BookNow();
_bookNow = false;
}
}
}
/// <summary>
/// Entladen des Viewmodels.
/// Soll überschrieben werden
/// </summary>
/// <returns>Task</returns>
public override Task DisappearingAsync(object sender)
{
IsBusy = false;
HasError = false;
ErrorMessage = string.Empty;
_addressChangedCounter = 0;
return base.DisappearingAsync(sender);
}
#endregion
#region Resumed
/// <summary>
/// Eventhandler wenn die App aus dem Sleepmode kommt
/// </summary>
/// <returns></returns>
internal override Task AppResumed()
{
return base.AppResumed();
//await _dispatcher.DispatchAsync(async () =>
//{
// _addressChangedCounter = 0;
// IsLoading = true;
// await LoadAsync();
//});
}
#endregion
#region Helper
/// <summary>
/// Eingaben auf "Fehler" prüfen
/// </summary>
/// <returns></returns>
private async Task CheckErrorAsync()
{
if (IsLoading)
{
HasError = true;
ErrorMessage = string.Empty;
return;
}
if (HasNoDog)
{
HasError = false;
ErrorMessage = string.Empty;
return;
}
//1. Prüfen ob mindestens ein Hunnd ausgewählt wurde
if (!DogsSelected)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_NoDog;
return;
}
//1.1 Walker gerade nicht verfügbar
if (Walker.NotAvailable)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_WalkerNotAvailable, Walker.FirstName);
return;
}
//2. Servicetype prüfen
if (Walker.AllowedRequests != DogWalkRequestType.Both)
{
//2.1 Prüfen ob der gewählte Service vom Walker angeboten wird
if (ServiceType == WalkServiceType.Walking && Walker.AllowedRequests != DogWalkRequestType.Walking)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_Walker_NoWalk, Walker.FirstName);
return;
}
//2.2 Prüfen ob der gewählte Service vom Walker angeboten wird
if (ServiceType == WalkServiceType.Sitting && Walker.AllowedRequests != DogWalkRequestType.Sitting)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_Walker_NoSitting, Walker.FirstName);
return;
}
}
//3. Hunde prüfen. Größe und Aggro...
var selectedDogs = SelectedDogs.Cast<Dog>().ToList();
foreach (var selectedDog in selectedDogs)
{
if (selectedDog.Size > Walker.AcceptedSize)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_DogSize, selectedDog.Name, Walker.FirstName);
return;
}
if (selectedDog.IsAggressiv && !Walker.DifficultAllowed)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_Dog_NoAggression, Walker.FirstName, selectedDog.Name);
return;
}
if (selectedDog.IsAggressiv && Walker.DifficultAllowed && selectedDog.AggressionLevel > Walker.AggressionLevel)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_Dog_AggressionLevel, selectedDog.Name, Walker.FirstName);
return;
}
if (selectedDog.BirthDate.HasValue && selectedDog.BirthDate.Value < DateTimeOffset.UtcNow)
{
var age = (DateTimeOffset.UtcNow - selectedDog.BirthDate.Value).TotalDays;
if (age <= 126 && !Walker.PuppyAllowed)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_Dog_NoPuppy, Walker.FirstName, selectedDog.Name);
return;
}
}
}
//4. Adressen prüfen & Radius des Walker berücksichtigen.
if (!PickupAddressValid)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_PickupAddress;
return;
}
if (string.IsNullOrWhiteSpace(PickupAddress.AddressLine1) || string.IsNullOrWhiteSpace(PickupAddress.Zip) || string.IsNullOrWhiteSpace(PickupAddress.City))
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_PickupAddress;
return;
}
if (!ReturnAddressValid)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_ReturnAddress;
return;
}
if (string.IsNullOrWhiteSpace(ReturnAddress.AddressLine1) || string.IsNullOrWhiteSpace(ReturnAddress.Zip) || string.IsNullOrWhiteSpace(ReturnAddress.City))
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_ReturnAddress;
return;
}
var pickupLocation = await _locationService.GetLocationByAddressAsync(PickupAddress);
if (pickupLocation == null)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_PickupAddress;
return;
}
var returnLocation = await _locationService.GetLocationByAddressAsync(ReturnAddress);
if (returnLocation == null)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_ReturnAddress;
return;
}
var pickupDistance = Location.CalculateDistance(pickupLocation.Latitude, pickupLocation.Longitude, Walker.Lat, Walker.Lng, DistanceUnits.Kilometers);
var returnDistance = Location.CalculateDistance(returnLocation.Latitude, returnLocation.Longitude, Walker.Lat, Walker.Lng, DistanceUnits.Kilometers);
if (pickupDistance > Walker.Radius)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_PickupDistance, Walker.FirstName, Walker.Radius.ToString("N2"), pickupDistance.ToString("N2"));
return;
}
if (returnDistance > Walker.Radius)
{
HasError = true;
ErrorMessage = string.Format(Resources.Text.Walk_Book_Error_ReturnDistance, Walker.FirstName, Walker.Radius.ToString("N2"), returnDistance.ToString("N2"));
return;
}
if (ServiceType == WalkServiceType.Walking && StartDate.Date != EndDate.Date)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_Walking;
return;
}
if (ServiceType == WalkServiceType.DayCare)
{
if ((EndDate.Date == StartDate.Date))
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_DayCare;
return;
}
var endDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, 0, DateTimeOffset.Now.Offset);
var startDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, 0, DateTimeOffset.Now.Offset);
if ((endDate - startDate).TotalHours > 24)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_DayCare;
return;
}
}
if (ServiceType == WalkServiceType.Sitting)
{
var endDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, 0, DateTimeOffset.Now.Offset);
var startDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, 0, DateTimeOffset.Now.Offset);
if ((endDate - startDate).TotalHours < 24)
{
HasError = true;
ErrorMessage = Resources.Text.Walk_Book_Error_Sitting;
return;
}
}
HasError = false;
}
/// <summary>
/// Hilfsmethode zum Kalkulieren des Preises
/// </summary>
private void CalculatePrice()
{
if (!HasError)
{
var dogCount = SelectedDogs != null ? SelectedDogs.Count : 0;
if (ServiceType == WalkServiceType.Walking)
{
var endDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, 0, DateTimeOffset.Now.Offset);
var startDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, 0, DateTimeOffset.Now.Offset);
var totalHours = (endDate - startDate).TotalHours;
var price = ((decimal)totalHours * Walker.PriceWalk) * dogCount;
_dispatcher.Dispatch(() =>
{
Price = price;
});
}
if (ServiceType == WalkServiceType.DayCare)
{
var endDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, 0, DateTimeOffset.Now.Offset);
var startDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, 0, DateTimeOffset.Now.Offset);
var totalHours = (endDate - startDate).TotalHours;
var price = ((decimal)totalHours * Walker.PriceDay) * dogCount;
_dispatcher.Dispatch(() =>
{
Price = price;
});
}
if (ServiceType == WalkServiceType.Sitting)
{
var endDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, 0, DateTimeOffset.Now.Offset);
var startDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, 0, DateTimeOffset.Now.Offset);
var totalDays = Math.Round((endDate - startDate).TotalDays, MidpointRounding.ToPositiveInfinity);
var price = ((decimal)totalDays * Walker.PriceSitting) * dogCount;
_dispatcher.Dispatch(() =>
{
Price = price;
});
}
_dispatcher.DispatchAsync(async () =>
{
await CheckDirectBookingAsync();
});
}
else
Price = 0m;
}
/// <summary>
/// Prüfen ob ein direktes buchen möglich ist.
/// Das geht für Walks und nur dann wennd er Temrin in den Openingtimes ist und diese frei sind
/// </summary>
/// <returns></returns>
private async Task CheckDirectBookingAsync()
{
if (!HasError)
{
if (ServiceType == WalkServiceType.Walking)
{
if (WalkingTimes != null && WalkingTimes.Any())
{
var walkingDays = WalkingTimes.Where(c => c.Day == StartDate.DayOfWeek).ToList();
if (walkingDays.Any())
{
foreach (var walkingDay in walkingDays)
{
var available = walkingDay.Start <= StartTime && walkingDay.End >= EndTime;
if (available)
{
//Grundsätzlich kann man das Datum buchen. Nun online sehen ob es frei ist...
var helpStartDate = new DateTimeOffset(StartDate.Year, StartDate.Month, StartDate.Day, StartTime.Hours, StartTime.Minutes, StartTime.Seconds, DateTimeOffset.Now.Offset);
var helpEndDate = new DateTimeOffset(EndDate.Year, EndDate.Month, EndDate.Day, EndTime.Hours, EndTime.Minutes, EndTime.Seconds, DateTimeOffset.Now.Offset);
using var cts = new CancellationTokenSource(Core.Common.Constants.ListTimeout);
var isPossible = await _walkService.IsWalkPossibleAsync(Walker.Id, helpStartDate, helpEndDate, App.CurrentUser.AccessToken, cts.Token);
DirectBooking = isPossible;
return;
}
}
}
}
}
}
DirectBooking = false;
}
#endregion
}
}