gehgassi_app/gehGassiApp/gehGassiApp/ViewModels/ProfileWalkerViewModel.cs

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
}
}