1082 lines
38 KiB
C#
1082 lines
38 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using CommunityToolkit.Maui.Core.Extensions;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
using gehGassiApp.Core.Helper;
|
|
using gehGassiApp.Core.Interfaces;
|
|
using gehGassiApp.Core.Messaging;
|
|
using gehGassiApp.Domain.Common;
|
|
using gehGassiApp.Domain.Users;
|
|
using gehGassiApp.Helper;
|
|
using gehGassiApp.Resources;
|
|
using gehGassiApp.Services;
|
|
using gehGassiApp.Views;
|
|
using gehGassiApp.Views.More;
|
|
using Microsoft.Maui.Graphics.Platform;
|
|
using Microsoft.Maui.Handlers;
|
|
using IImage = Microsoft.Maui.Graphics.IImage;
|
|
|
|
#if ANDROID
|
|
using gehGassiApp.Platforms.Android;
|
|
#endif
|
|
|
|
namespace gehGassiApp.ViewModels
|
|
{
|
|
/// <summary>
|
|
/// Viewmodel für das Profile eines Dogwalkers
|
|
/// </summary>
|
|
public partial class ProfileWalkerViewModel : MenuViewModel
|
|
{
|
|
private bool _countryHandlerEnabled;
|
|
private readonly IUserService _userService;
|
|
private readonly ICountryService _countryService;
|
|
private readonly ILocationService _locationService;
|
|
private readonly IWalkerProfileService _walkerProfileService;
|
|
private readonly IDialogService _dialogService;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="userService">Instanz eines IUserService</param>
|
|
/// <param name="countryService">Instanz eines ICountryService</param>
|
|
/// <param name="locationService">Instanz eines ILocationService</param>
|
|
/// <param name="walkerProfileService">Instanz eines IWalkerProfileService</param>
|
|
public ProfileWalkerViewModel(IUserService userService, ICountryService countryService, ILocationService locationService, IWalkerProfileService walkerProfileService, IDialogService dialogService)
|
|
{
|
|
_userService = userService;
|
|
_countryService = countryService;
|
|
_locationService = locationService;
|
|
_walkerProfileService = walkerProfileService;
|
|
_dialogService = dialogService;
|
|
Title = Text.View_Title_Profile;
|
|
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_WalkerProfile or Core.Messaging.Messages.Refresh_All)
|
|
{
|
|
_refresh = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aktueller Benutzer
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private AppUser _appUser;
|
|
|
|
/// <summary>
|
|
/// Dogwalker-Profil
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private DogWalkerProfile _walkerProfile;
|
|
|
|
/// <summary>
|
|
/// Pfad zum Foto
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private string _photoPath;
|
|
|
|
/// <summary>
|
|
/// Handelt es sich um ein neues Foto?
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _isNewPhoto;
|
|
|
|
/// <summary>
|
|
/// Hat der Benutzer ein Profilbild?
|
|
/// </summary>
|
|
public bool HasPhoto => !string.IsNullOrWhiteSpace(AppUser?.Photo);
|
|
|
|
/// <summary>
|
|
/// Liste der Länder
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<Country> _countries;
|
|
|
|
/// <summary>
|
|
/// Gewähltes Land
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private Country _selectedCountry;
|
|
|
|
/// <summary>
|
|
/// Liste der Bundesländer
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<State> _states;
|
|
|
|
/// <summary>
|
|
/// Gewähltes Bundesland
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private State _selectedState;
|
|
|
|
/// <summary>
|
|
/// Gibt an ob ein Land und Bundesland gewhält wurde
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _addressSelectionValid;
|
|
|
|
/// <summary>
|
|
/// Liste der Geschlechter
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<SexItem> _sexItems;
|
|
|
|
/// <summary>
|
|
/// Gewähltes Geschlecht
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private SexItem _selectedSex;
|
|
|
|
/// <summary>
|
|
/// Geburtstag - Als string
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private string _birthDay;
|
|
|
|
/// <summary>
|
|
/// Geburtstag - Als Datum
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private DateTime? _birthDate;
|
|
|
|
/// <summary>
|
|
/// Maximales Datum für Geburtstag
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private string _maxDate;
|
|
|
|
/// <summary>
|
|
/// Liste der Länder für Nationalität
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<Country> _nationalitites;
|
|
|
|
/// <summary>
|
|
/// Gewähltes Land für Nationalität
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private Country _selectedNationality;
|
|
|
|
/// <summary>
|
|
/// Liste der Länder für Hauptwohnsitz
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<Country> _mainResidences;
|
|
|
|
/// <summary>
|
|
/// Gewähltes Land für Hauptwohnsitz
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private Country _selectedMainResidence;
|
|
|
|
/// <summary>
|
|
/// Gibt an ob sich Daten geändert haben
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _hasChanges;
|
|
|
|
/// <summary>
|
|
/// Original AppUser für Change Detection
|
|
/// </summary>
|
|
private AppUser _originalAppUser;
|
|
|
|
/// <summary>
|
|
/// Original WalkerProfile für Change Detection
|
|
/// </summary>
|
|
private DogWalkerProfile _originalWalkerProfile;
|
|
|
|
/// <summary>
|
|
/// Original State für Change Detection
|
|
/// </summary>
|
|
private class OriginalWalkerProfileState
|
|
{
|
|
public string FirstName { get; set; } = "";
|
|
public string LastName { get; set; } = "";
|
|
public string Photo { get; set; } = "";
|
|
public DateTime? BirthDate { get; set; }
|
|
public string AddressLine1 { get; set; } = "";
|
|
public string AddressLine2 { get; set; } = "";
|
|
public string Zip { get; set; } = "";
|
|
public string City { get; set; } = "";
|
|
public string Mobile { get; set; } = "";
|
|
public string Phone { get; set; } = "";
|
|
public string CountryIso2 { get; set; } = "";
|
|
public string StateCode { get; set; } = "";
|
|
public Sex Sex { get; set; }
|
|
public string NationalityIso2 { get; set; } = "";
|
|
public string MainResidenceIso2 { get; set; } = "";
|
|
|
|
// Walker Profile Properties
|
|
public string About { get; set; } = "";
|
|
public double Radius { get; set; }
|
|
public DogWalkRequestType AllowedRequests { get; set; }
|
|
public bool PuppyAllowed { get; set; }
|
|
public bool DifficultAllowed { get; set; }
|
|
public DogAggressionLevel AggressionLevel { get; set; }
|
|
public DogSize AcceptedSize { get; set; }
|
|
public decimal PriceWalk { get; set; }
|
|
public decimal PriceDay { get; set; }
|
|
public decimal PriceSitting { get; set; }
|
|
public bool NotAvailable { get; set; }
|
|
public string NotAvailableInfo { get; set; } = "";
|
|
}
|
|
|
|
private OriginalWalkerProfileState _originalState;
|
|
|
|
/// <summary>
|
|
/// Soll der Bottomsheet angezeigt werden?
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private bool _showForm;
|
|
|
|
/// <summary>
|
|
/// Typ des Preises der bearbeitet werden soll
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private WalkServiceType _priceType;
|
|
|
|
/// <summary>
|
|
/// Preis der bearbeitet werden soll
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private decimal _price;
|
|
|
|
/// <summary>
|
|
/// Titel des Formulars
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private string _formTitle;
|
|
|
|
#region Extended
|
|
|
|
partial void OnSelectedCountryChanged(Country value)
|
|
{
|
|
if (!_countryHandlerEnabled)
|
|
return;
|
|
|
|
AddressSelectionValid = false;
|
|
SelectedState = null;
|
|
if (value != null && !string.IsNullOrWhiteSpace(value.Iso2))
|
|
{
|
|
var states = _countryService.GetStatesAsync(value.Iso2).Result;
|
|
States = states.ToObservableCollection();
|
|
}
|
|
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnSelectedStateChanged(State value)
|
|
{
|
|
if (value != null)
|
|
AddressSelectionValid = true;
|
|
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnBirthDateChanged(DateTime? value)
|
|
{
|
|
if(IsLoading)
|
|
return;
|
|
if (value.HasValue)
|
|
BirthDay = value.Value.ToString("d", CultureInfo.CurrentCulture);
|
|
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnSelectedSexChanged(SexItem value)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnSelectedNationalityChanged(Country value)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnSelectedMainResidenceChanged(Country value)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnAppUserChanged(AppUser value)
|
|
{
|
|
// Entferne alte Event-Handler falls vorhanden
|
|
if (_appUser != null)
|
|
{
|
|
_appUser.PropertyChanged -= OnAppUserPropertyChanged;
|
|
if (_appUser.Address != null)
|
|
_appUser.Address.PropertyChanged -= OnAppUserPropertyChanged;
|
|
if (_appUser.Contact != null)
|
|
_appUser.Contact.PropertyChanged -= OnAppUserPropertyChanged;
|
|
}
|
|
|
|
// Registriere neue Event-Handler
|
|
if (value != null)
|
|
{
|
|
value.PropertyChanged += OnAppUserPropertyChanged;
|
|
if (value.Address != null)
|
|
value.Address.PropertyChanged += OnAppUserPropertyChanged;
|
|
if (value.Contact != null)
|
|
value.Contact.PropertyChanged += OnAppUserPropertyChanged;
|
|
}
|
|
|
|
CheckForChanges();
|
|
OnPropertyChanged(nameof(HasPhoto));
|
|
}
|
|
|
|
partial void OnWalkerProfileChanged(DogWalkerProfile value)
|
|
{
|
|
// Entferne alte Event-Handler falls vorhanden
|
|
if (_walkerProfile != null)
|
|
{
|
|
_walkerProfile.PropertyChanged -= OnWalkerProfilePropertyChanged;
|
|
}
|
|
|
|
// Registriere neue Event-Handler
|
|
if (value != null)
|
|
{
|
|
value.PropertyChanged += OnWalkerProfilePropertyChanged;
|
|
}
|
|
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnIsNewPhotoChanged(bool value)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
partial void OnPhotoPathChanged(string value)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Commands
|
|
[RelayCommand]
|
|
public async Task ChoosePhotoOption()
|
|
{
|
|
try
|
|
{
|
|
var actionToTak = await _dialogService.ShowActionsAsync(
|
|
Text.Action_ProfilePicture_Title,
|
|
Text.Button_Cancel,
|
|
null,
|
|
[
|
|
Text.Action_PhotoCamera,
|
|
Text.Action_PhotoGallery
|
|
]
|
|
);
|
|
|
|
if (actionToTak == Text.Action_PhotoCamera)
|
|
{
|
|
await TakePicture();
|
|
}
|
|
else if (actionToTak == Text.Action_PhotoGallery)
|
|
{
|
|
await SelectPicture();
|
|
}
|
|
else
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine(ex.Message);
|
|
await TrackErrorAsync(ex, true);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Foto erstellen
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
[RelayCommand]
|
|
public async Task TakePicture()
|
|
{
|
|
try
|
|
{
|
|
var status = await CheckAndRequestCameraPermission();
|
|
if (status == PermissionStatus.Granted)
|
|
{
|
|
var photo = await MediaPicker.CapturePhotoAsync();
|
|
await LoadPhotoAsync(photo);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine(ex.Message);
|
|
await TrackErrorAsync(ex, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Foto Laden
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
[RelayCommand]
|
|
public async Task SelectPicture()
|
|
{
|
|
try
|
|
{
|
|
var status = await CheckAndRequestPicturePermission();
|
|
if (status == PermissionStatus.Granted)
|
|
{
|
|
var photo = await MediaPicker.PickPhotoAsync();
|
|
await LoadPhotoAsync(photo);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine(ex.Message);
|
|
await TrackErrorAsync(ex, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bild rechts drehen
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[RelayCommand]
|
|
public async Task RotateRight()
|
|
{
|
|
if (IsNewPhoto == false || string.IsNullOrWhiteSpace(PhotoPath))
|
|
return;
|
|
|
|
var extension = ".png";
|
|
if (!string.IsNullOrWhiteSpace(PhotoPath))
|
|
{
|
|
extension = Path.GetExtension(PhotoPath);
|
|
}
|
|
|
|
var newFile = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():n}{extension}");
|
|
await using (var resultStream = ImageHelper.RotateRight(PhotoPath))
|
|
{
|
|
await using var newStream = File.OpenWrite(newFile);
|
|
resultStream.Position = 0;
|
|
await resultStream.CopyToAsync(newStream);
|
|
}
|
|
|
|
PhotoPath = newFile;
|
|
AppUser.Photo = PhotoPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bild links drehen
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[RelayCommand]
|
|
public async Task RotateLeft()
|
|
{
|
|
if (IsNewPhoto == false || string.IsNullOrWhiteSpace(PhotoPath))
|
|
return;
|
|
|
|
var extension = ".png";
|
|
if (!string.IsNullOrWhiteSpace(PhotoPath))
|
|
{
|
|
extension = Path.GetExtension(PhotoPath);
|
|
}
|
|
|
|
var newFile = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():n}{extension}");
|
|
await using (var resultStream = ImageHelper.RotateLeft(PhotoPath))
|
|
{
|
|
await using var newStream = File.OpenWrite(newFile);
|
|
resultStream.Position = 0;
|
|
await resultStream.CopyToAsync(newStream);
|
|
}
|
|
|
|
PhotoPath = newFile;
|
|
AppUser.Photo = PhotoPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bild vertikal spiegeln
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[RelayCommand]
|
|
public async Task FlipVertical()
|
|
{
|
|
if (IsNewPhoto == false || string.IsNullOrWhiteSpace(PhotoPath))
|
|
return;
|
|
|
|
var extension = ".png";
|
|
if (!string.IsNullOrWhiteSpace(PhotoPath))
|
|
{
|
|
extension = Path.GetExtension(PhotoPath);
|
|
}
|
|
|
|
var newFile = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():n}{extension}");
|
|
await using (var resultStream = ImageHelper.FlipVertically(PhotoPath))
|
|
{
|
|
await using var newStream = File.OpenWrite(newFile);
|
|
resultStream.Position = 0;
|
|
await resultStream.CopyToAsync(newStream);
|
|
}
|
|
|
|
PhotoPath = newFile;
|
|
AppUser.Photo = PhotoPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command für die Anzeige der Walkingtimes
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public async Task ShowWalkingTimes()
|
|
{
|
|
await Shell.Current.GoToAsync($"{nameof(WalkingTimesView)}", Core.Common.Constants.AnimateNavigation);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command für das Öffnen des Kalenders für den Geburtstag
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public void ShowBirthdayCalendar(object obj)
|
|
{
|
|
if (obj is DatePicker picker)
|
|
{
|
|
picker.Date = DateTime.TryParse(BirthDay, CultureInfo.CurrentCulture, out var _date) ? _date : DateTime.Now;
|
|
#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>
|
|
/// Speichern
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
[RelayCommand]
|
|
public async Task Save()
|
|
{
|
|
IsSaving = true;
|
|
try
|
|
{
|
|
if (string.IsNullOrWhiteSpace(BirthDay))
|
|
{
|
|
AppUser.BirthDate = null;
|
|
}
|
|
else
|
|
{
|
|
if (DateTime.TryParse(BirthDay, CultureInfo.CurrentCulture, out var _date))
|
|
{
|
|
AppUser.BirthDate = new DateTimeOffset(_date, TimeSpan.Zero);
|
|
}
|
|
}
|
|
if (!AppUser.JsonEquals(App.CurrentAppUser) || !WalkerProfile.JsonEquals(App.CurrentWalkerProfile))
|
|
{
|
|
var location = await _locationService.GetLocationByAddressAsync(AppUser.Address);
|
|
if (location != null)
|
|
{
|
|
AppUser.Lat = location.Latitude;
|
|
AppUser.Lng = location.Longitude;
|
|
}
|
|
|
|
//Foto behandeln
|
|
if (!string.IsNullOrWhiteSpace(PhotoPath) && !PhotoPath.StartsWithHttp())
|
|
{
|
|
var extension = Path.GetExtension(PhotoPath);
|
|
var photoFileName = Path.Combine(FileSystem.AppDataDirectory, $"photo-{Guid.NewGuid():N}{extension}");
|
|
await using var stream = File.OpenRead(PhotoPath);
|
|
await using var streamWrite = File.OpenWrite(photoFileName);
|
|
await stream.CopyToAsync(streamWrite);
|
|
AppUser.Photo = photoFileName;
|
|
}
|
|
else
|
|
{
|
|
if (!AppUser.Photo.StartsWithHttp())
|
|
{
|
|
//Altes Foto löschen
|
|
try
|
|
{
|
|
File.Delete(AppUser.Photo);
|
|
}
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); }
|
|
AppUser.Photo = string.Empty;
|
|
}
|
|
}
|
|
|
|
AppUser.Address.CountryCode = SelectedCountry.Iso2;
|
|
AppUser.Address.State = SelectedState.Code;
|
|
AppUser.Sex = SelectedSex.Value;
|
|
AppUser.NationalityCode = SelectedNationality != null ? SelectedNationality.Iso2 : string.Empty;
|
|
AppUser.MainResidenceCode = SelectedMainResidence != null ? SelectedMainResidence.Iso2 : string.Empty;
|
|
|
|
using var cts = new CancellationTokenSource(Core.Common.Constants.CreateTimeout);
|
|
var appUser = await _userService.UpdateAppUserAsync(AppUser, App.CurrentUser.AccessToken, cts.Token);
|
|
App.CurrentAppUser = appUser;
|
|
|
|
WalkerProfile.Radius = Math.Floor(WalkerProfile.Radius);
|
|
var walkerProfile = await _walkerProfileService.UpdateAsync(WalkerProfile, App.CurrentUser.AccessToken, cts.Token);
|
|
App.CurrentWalkerProfile = walkerProfile;
|
|
|
|
}
|
|
//PropertyChangedEx(nameof(CurrentAppUser));
|
|
|
|
WeakReferenceMessenger.Default.Send(new RefreshViewModelMessage(Core.Messaging.Messages.Refresh_Home));
|
|
var stackCount = Shell.Current.Navigation.NavigationStack.Count;
|
|
if (stackCount > 1)
|
|
{
|
|
await Shell.Current.Navigation.PopToRootAsync();
|
|
}
|
|
await Shell.Current.GoToAsync($"//{nameof(HomeWalkerView)}", Core.Common.Constants.AnimateNavigation);
|
|
|
|
//AppUser = null;
|
|
//PhotoPath = string.Empty;
|
|
//SelectedCountry = null;
|
|
//SelectedState = null;
|
|
}
|
|
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); }
|
|
IsSaving = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command für die Anzeige der Hilfe zu Preisen
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public async Task PriceHelp()
|
|
{
|
|
var menu = MenuSelected.Walks.ToString();
|
|
var title = Text.View_Title_PriceHelp;
|
|
await Shell.Current.GoToAsync($"{nameof(ShowTextPageView)}?code={Core.Common.Constants.PagePublicRequestPrice}&menu={menu}&title={title}", Core.Common.Constants.AnimateNavigation);
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void EditPrice(WalkServiceType serviceType)
|
|
{
|
|
switch (serviceType)
|
|
{
|
|
case WalkServiceType.Walking:
|
|
Price = WalkerProfile.PriceWalk;
|
|
FormTitle = $"{Resources.Text.Walker_Service_Walking}: {Resources.Text.Common_PricePerHour}";
|
|
break;
|
|
case WalkServiceType.Sitting:
|
|
Price = WalkerProfile.PriceSitting;
|
|
FormTitle = $"{Resources.Text.Walker_Service_Sitting}: {Resources.Text.Common_Price24Hours}";
|
|
break;
|
|
case WalkServiceType.DayCare:
|
|
Price = WalkerProfile.PriceDay;
|
|
FormTitle = $"{Resources.Text.Walker_Service_DayCare}: {Resources.Text.Common_PricePerHour}";
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(serviceType), serviceType, null);
|
|
}
|
|
PriceType = serviceType;
|
|
ShowForm = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preis erhöhen
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public void IncreasePrice()
|
|
{
|
|
if (Price < 1000)
|
|
Price += 1;
|
|
if (Price > 1000)
|
|
Price = 1000;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preis verringern
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public void DecreasePrice()
|
|
{
|
|
if (Price > 0)
|
|
Price -= 1;
|
|
if (Price < 0)
|
|
Price = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command für das Abbrechen der Bearbeitung eines Preises
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public void CancelPrice()
|
|
{
|
|
ShowForm = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command für das Setzen eines Preises
|
|
/// </summary>
|
|
[RelayCommand]
|
|
public async Task SavePrice()
|
|
{
|
|
switch (PriceType)
|
|
{
|
|
case WalkServiceType.Walking:
|
|
WalkerProfile.PriceWalk = Price;
|
|
break;
|
|
case WalkServiceType.Sitting:
|
|
WalkerProfile.PriceSitting = Price;
|
|
break;
|
|
case WalkServiceType.DayCare:
|
|
WalkerProfile.PriceDay = Price;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
ShowForm = false;
|
|
await Task.Delay(250);
|
|
Price = 0m;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Laden der initialen Daten
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
private async Task LoadAsync()
|
|
{
|
|
_countryHandlerEnabled = false;
|
|
var countries = await _countryService.GetCountriesAsync();
|
|
Countries = countries.ToObservableCollection();
|
|
|
|
var nationalities = await _countryService.GetNationalitiesAsync();
|
|
Nationalitites = nationalities.ToObservableCollection();
|
|
|
|
var residences = await _countryService.GetCountriesAsync();
|
|
MainResidences = residences.ToObservableCollection();
|
|
|
|
var sexItems = EnumHelper.GetSexList();
|
|
SexItems = sexItems.ToObservableCollection();
|
|
|
|
using var cts = new CancellationTokenSource(Core.Common.Constants.ListTimeout);
|
|
var appUser = await _userService.GetAppUserAsync(App.CurrentUser.AccessToken, cts.Token, false);
|
|
|
|
AppUser = appUser;
|
|
PhotoPath = AppUser.Photo;
|
|
|
|
SelectedCountry = Countries.First(c => c.Iso2 == AppUser.Address.CountryCode);
|
|
var states = await _countryService.GetStatesAsync(SelectedCountry.Iso2);
|
|
States = states.ToObservableCollection();
|
|
SelectedState = States.First(c => c.Code == AppUser.Address.State);
|
|
|
|
SelectedSex = SexItems.First(c => c.Value == AppUser.Sex);
|
|
|
|
SelectedNationality = !string.IsNullOrWhiteSpace(AppUser.NationalityCode) ? Nationalitites.FirstOrDefault(c => c.Iso2 == AppUser.NationalityCode) : null;
|
|
SelectedMainResidence = !string.IsNullOrWhiteSpace(AppUser.MainResidenceCode) ? MainResidences.FirstOrDefault(c => c.Iso2 == AppUser.MainResidenceCode) : null;
|
|
|
|
if (SelectedCountry != null && SelectedState != null)
|
|
AddressSelectionValid = true;
|
|
|
|
BirthDay = string.Empty;
|
|
if (AppUser.BirthDate.HasValue)
|
|
{
|
|
BirthDay = AppUser.BirthDate.Value.ToString("d", CultureInfo.CurrentCulture);
|
|
}
|
|
MaxDate = DateTime.Now.AddYears(-18).ToString("d", CultureInfo.InvariantCulture);
|
|
|
|
_countryHandlerEnabled = true;
|
|
|
|
var walkerProfile = await _walkerProfileService.GetAsync(App.CurrentUser.AccessToken, cts.Token, false);
|
|
WalkerProfile = walkerProfile;
|
|
|
|
// Speichere ursprüngliche Werte für Change Detection
|
|
SaveOriginalValues();
|
|
|
|
IsLoading = false;
|
|
}
|
|
|
|
#region Event Handlers for Change Detection
|
|
|
|
/// <summary>
|
|
/// Wird aufgerufen wenn sich eine Property des AppUser oder seiner Unterobjekte ändert
|
|
/// </summary>
|
|
private void OnAppUserPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
{
|
|
CheckForChanges();
|
|
|
|
// HasPhoto aktualisieren wenn sich die Photo Property ändert
|
|
if (e.PropertyName == nameof(AppUser.Photo))
|
|
{
|
|
OnPropertyChanged(nameof(HasPhoto));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wird aufgerufen wenn sich eine Property des WalkerProfile ändert
|
|
/// </summary>
|
|
private void OnWalkerProfilePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
{
|
|
CheckForChanges();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper
|
|
|
|
/// <summary>
|
|
/// Prüft ob sich Daten geändert haben
|
|
/// </summary>
|
|
private void CheckForChanges()
|
|
{
|
|
if (IsLoading || _originalState == null || AppUser == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
// Prüfe AppUser Properties
|
|
var hasChanges =
|
|
AppUser.FirstName != _originalState.FirstName ||
|
|
AppUser.LastName != _originalState.LastName ||
|
|
AppUser.Photo != _originalState.Photo ||
|
|
BirthDate != _originalState.BirthDate;
|
|
|
|
// Prüfe Address Properties
|
|
if (AppUser.Address != null)
|
|
{
|
|
hasChanges = hasChanges ||
|
|
(AppUser.Address.AddressLine1 ?? "") != _originalState.AddressLine1 ||
|
|
(AppUser.Address.AddressLine2 ?? "") != _originalState.AddressLine2 ||
|
|
(AppUser.Address.Zip ?? "") != _originalState.Zip ||
|
|
(AppUser.Address.City ?? "") != _originalState.City;
|
|
}
|
|
|
|
// Prüfe Contact Properties
|
|
if (AppUser.Contact != null)
|
|
{
|
|
hasChanges = hasChanges ||
|
|
(AppUser.Contact.Mobile ?? "") != _originalState.Mobile ||
|
|
(AppUser.Contact.Phone ?? "") != _originalState.Phone;
|
|
}
|
|
|
|
// Prüfe Walker Profile Properties
|
|
if (WalkerProfile != null)
|
|
{
|
|
hasChanges = hasChanges ||
|
|
(WalkerProfile.About ?? "") != _originalState.About ||
|
|
WalkerProfile.Radius != _originalState.Radius ||
|
|
WalkerProfile.AllowedRequests != _originalState.AllowedRequests ||
|
|
WalkerProfile.PuppyAllowed != _originalState.PuppyAllowed ||
|
|
WalkerProfile.DifficultAllowed != _originalState.DifficultAllowed ||
|
|
WalkerProfile.AggressionLevel != _originalState.AggressionLevel ||
|
|
WalkerProfile.AcceptedSize != _originalState.AcceptedSize ||
|
|
WalkerProfile.PriceWalk != _originalState.PriceWalk ||
|
|
WalkerProfile.PriceDay != _originalState.PriceDay ||
|
|
WalkerProfile.PriceSitting != _originalState.PriceSitting ||
|
|
WalkerProfile.NotAvailable != _originalState.NotAvailable ||
|
|
(WalkerProfile.NotAvailableInfo ?? "") != _originalState.NotAvailableInfo;
|
|
}
|
|
|
|
// Prüfe Selections
|
|
if (SelectedCountry?.Iso2 != _originalState.CountryIso2)
|
|
hasChanges = true;
|
|
if (SelectedState?.Code != _originalState.StateCode)
|
|
hasChanges = true;
|
|
if (SelectedSex?.Value != _originalState.Sex)
|
|
hasChanges = true;
|
|
if (SelectedNationality?.Iso2 != _originalState.NationalityIso2)
|
|
hasChanges = true;
|
|
if (SelectedMainResidence?.Iso2 != _originalState.MainResidenceIso2)
|
|
hasChanges = true;
|
|
|
|
// Prüfe ob neues Foto vorhanden
|
|
if (IsNewPhoto && !string.IsNullOrWhiteSpace(PhotoPath))
|
|
hasChanges = true;
|
|
|
|
HasChanges = hasChanges;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// Fallback: Bei Fehlern Changes auf true setzen
|
|
HasChanges = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Speichert die ursprünglichen Werte für Change Detection
|
|
/// </summary>
|
|
private void SaveOriginalValues()
|
|
{
|
|
try
|
|
{
|
|
_originalState = new OriginalWalkerProfileState
|
|
{
|
|
FirstName = AppUser?.FirstName ?? "",
|
|
LastName = AppUser?.LastName ?? "",
|
|
Photo = AppUser?.Photo ?? "",
|
|
BirthDate = BirthDate,
|
|
AddressLine1 = AppUser?.Address?.AddressLine1 ?? "",
|
|
AddressLine2 = AppUser?.Address?.AddressLine2 ?? "",
|
|
Zip = AppUser?.Address?.Zip ?? "",
|
|
City = AppUser?.Address?.City ?? "",
|
|
Mobile = AppUser?.Contact?.Mobile ?? "",
|
|
Phone = AppUser?.Contact?.Phone ?? "",
|
|
CountryIso2 = SelectedCountry?.Iso2 ?? "",
|
|
StateCode = SelectedState?.Code ?? "",
|
|
Sex = SelectedSex?.Value ?? Sex.Undefined,
|
|
NationalityIso2 = SelectedNationality?.Iso2 ?? "",
|
|
MainResidenceIso2 = SelectedMainResidence?.Iso2 ?? "",
|
|
|
|
// Walker Profile Properties
|
|
About = WalkerProfile?.About ?? "",
|
|
Radius = WalkerProfile?.Radius ?? 0,
|
|
AllowedRequests = WalkerProfile?.AllowedRequests ?? DogWalkRequestType.Walking,
|
|
PuppyAllowed = WalkerProfile?.PuppyAllowed ?? false,
|
|
DifficultAllowed = WalkerProfile?.DifficultAllowed ?? false,
|
|
AggressionLevel = WalkerProfile?.AggressionLevel ?? DogAggressionLevel.Low,
|
|
AcceptedSize = WalkerProfile?.AcceptedSize ?? DogSize.Small,
|
|
PriceWalk = WalkerProfile?.PriceWalk ?? 0,
|
|
PriceDay = WalkerProfile?.PriceDay ?? 0,
|
|
PriceSitting = WalkerProfile?.PriceSitting ?? 0,
|
|
NotAvailable = WalkerProfile?.NotAvailable ?? false,
|
|
NotAvailableInfo = WalkerProfile?.NotAvailableInfo ?? ""
|
|
};
|
|
}
|
|
catch (Exception)
|
|
{
|
|
_originalState = new OriginalWalkerProfileState();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Laden eines Fotos
|
|
/// </summary>
|
|
/// <param name="photo">Foto</param>
|
|
/// <returns>Task</returns>
|
|
private async Task LoadPhotoAsync(FileBase photo)
|
|
{
|
|
// canceled
|
|
if (photo == null)
|
|
{
|
|
PhotoPath = null;
|
|
return;
|
|
}
|
|
|
|
IsNewPhoto = true;
|
|
|
|
var extension = ".png";
|
|
if (!string.IsNullOrWhiteSpace(photo.FileName))
|
|
{
|
|
extension = Path.GetExtension(photo.FileName);
|
|
}
|
|
|
|
var localFilePath = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():n}{extension}");
|
|
await using (var sourceStream = await photo.OpenReadAsync())
|
|
{
|
|
await using (var localFileStream = File.OpenWrite(localFilePath))
|
|
{
|
|
await sourceStream.CopyToAsync(localFileStream);
|
|
}
|
|
}
|
|
|
|
var newFile = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():n}{extension}");
|
|
|
|
//Bild drehen wenn nötig
|
|
await using (var resultStream = ImageHelper.FixImageOrientation(localFilePath, out var _width, out var _height))
|
|
{
|
|
await using var newStream = File.OpenWrite(newFile);
|
|
resultStream.Position = 0;
|
|
await resultStream.CopyToAsync(newStream);
|
|
}
|
|
|
|
IImage image;
|
|
await using (var openStream = File.OpenRead(newFile))
|
|
{
|
|
image = PlatformImage.FromStream(openStream);
|
|
}
|
|
|
|
if (image != null)
|
|
{
|
|
var newImage = image.Resize(800, 800, ResizeMode.Bleed);
|
|
using (var memStream = new MemoryStream())
|
|
{
|
|
await newImage.SaveAsync(memStream);
|
|
await using var newStream = File.OpenWrite(newFile);
|
|
memStream.Position = 0;
|
|
await memStream.CopyToAsync(newStream);
|
|
}
|
|
}
|
|
|
|
PhotoPath = newFile;
|
|
AppUser.Photo = PhotoPath;
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
#region Initialisierung
|
|
|
|
/// <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("ProfileWalker");
|
|
|
|
if (_refresh)
|
|
{
|
|
IsLoading = true;
|
|
ShowForm = false;
|
|
IsBusy = true;
|
|
IsNewPhoto = false;
|
|
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
|
|
await LoadAsync();
|
|
|
|
// Speichere Original-Werte für Change Detection
|
|
SaveOriginalValues();
|
|
HasChanges = false;
|
|
|
|
IsBusy = false;
|
|
_refresh = 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;
|
|
IsNewPhoto = false;
|
|
|
|
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();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|