Implement change detection in ProfileViewModel and ProfileWalkerViewModel; add original state tracking and update UI to reflect changes with a floating save button in ProfileView and ProfileWalkerView.
This commit is contained in:
parent
5c85ce545f
commit
fb1756b82b
@ -159,6 +159,39 @@ namespace gehGassiApp.ViewModels
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private Country _selectedMainResidence;
|
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>
|
||||||
|
/// State Class für Change Detection
|
||||||
|
/// </summary>
|
||||||
|
private class OriginalProfileState
|
||||||
|
{
|
||||||
|
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; } = "";
|
||||||
|
} private OriginalProfileState _originalState;
|
||||||
|
|
||||||
#region Extended
|
#region Extended
|
||||||
|
|
||||||
partial void OnSelectedCountryChanged(Country value)
|
partial void OnSelectedCountryChanged(Country value)
|
||||||
@ -173,12 +206,16 @@ namespace gehGassiApp.ViewModels
|
|||||||
var states = _countryService.GetStatesAsync(value.Iso2).Result;
|
var states = _countryService.GetStatesAsync(value.Iso2).Result;
|
||||||
States = states.ToObservableCollection();
|
States = states.ToObservableCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedStateChanged(State value)
|
partial void OnSelectedStateChanged(State value)
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
AddressSelectionValid = true;
|
AddressSelectionValid = true;
|
||||||
|
|
||||||
|
CheckForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnBirthDateChanged(DateTime? value)
|
partial void OnBirthDateChanged(DateTime? value)
|
||||||
@ -187,6 +224,58 @@ namespace gehGassiApp.ViewModels
|
|||||||
return;
|
return;
|
||||||
if (value.HasValue)
|
if (value.HasValue)
|
||||||
BirthDay = value.Value.ToString("d", CultureInfo.CurrentCulture);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnIsNewPhotoChanged(bool value)
|
||||||
|
{
|
||||||
|
CheckForChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnPhotoPathChanged(string value)
|
||||||
|
{
|
||||||
|
CheckForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -545,11 +634,118 @@ namespace gehGassiApp.ViewModels
|
|||||||
|
|
||||||
_countryHandlerEnabled = true;
|
_countryHandlerEnabled = true;
|
||||||
|
|
||||||
|
// Speichere ursprüngliche Werte für Change Detection
|
||||||
|
SaveOriginalValues();
|
||||||
|
|
||||||
IsLoading = false;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Helper
|
#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 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 OriginalProfileState
|
||||||
|
{
|
||||||
|
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 ?? ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_originalState = new OriginalProfileState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Laden eines Fotos
|
/// Laden eines Fotos
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -638,6 +834,11 @@ namespace gehGassiApp.ViewModels
|
|||||||
IsNewPhoto = false;
|
IsNewPhoto = false;
|
||||||
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
|
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
|
|
||||||
|
// Speichere Original-Werte für Change Detection
|
||||||
|
SaveOriginalValues();
|
||||||
|
HasChanges = false;
|
||||||
|
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -172,6 +172,60 @@ namespace gehGassiApp.ViewModels
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private Country _selectedMainResidence;
|
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>
|
/// <summary>
|
||||||
/// Soll der Bottomsheet angezeigt werden?
|
/// Soll der Bottomsheet angezeigt werden?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -210,12 +264,16 @@ namespace gehGassiApp.ViewModels
|
|||||||
var states = _countryService.GetStatesAsync(value.Iso2).Result;
|
var states = _countryService.GetStatesAsync(value.Iso2).Result;
|
||||||
States = states.ToObservableCollection();
|
States = states.ToObservableCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedStateChanged(State value)
|
partial void OnSelectedStateChanged(State value)
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
AddressSelectionValid = true;
|
AddressSelectionValid = true;
|
||||||
|
|
||||||
|
CheckForChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnBirthDateChanged(DateTime? value)
|
partial void OnBirthDateChanged(DateTime? value)
|
||||||
@ -224,6 +282,75 @@ namespace gehGassiApp.ViewModels
|
|||||||
return;
|
return;
|
||||||
if (value.HasValue)
|
if (value.HasValue)
|
||||||
BirthDay = value.Value.ToString("d", CultureInfo.CurrentCulture);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
#endregion
|
||||||
@ -656,11 +783,158 @@ namespace gehGassiApp.ViewModels
|
|||||||
var walkerProfile = await _walkerProfileService.GetAsync(App.CurrentUser.AccessToken, cts.Token, false);
|
var walkerProfile = await _walkerProfileService.GetAsync(App.CurrentUser.AccessToken, cts.Token, false);
|
||||||
WalkerProfile = walkerProfile;
|
WalkerProfile = walkerProfile;
|
||||||
|
|
||||||
|
// Speichere ursprüngliche Werte für Change Detection
|
||||||
|
SaveOriginalValues();
|
||||||
|
|
||||||
IsLoading = false;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wird aufgerufen wenn sich eine Property des WalkerProfile ändert
|
||||||
|
/// </summary>
|
||||||
|
private void OnWalkerProfilePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CheckForChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Helper
|
#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>
|
/// <summary>
|
||||||
/// Laden eines Fotos
|
/// Laden eines Fotos
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -752,6 +1026,11 @@ namespace gehGassiApp.ViewModels
|
|||||||
IsNewPhoto = false;
|
IsNewPhoto = false;
|
||||||
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
|
await Task.Delay(Core.Common.Constants.AnimationDelayDetails);
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
|
|
||||||
|
// Speichere Original-Werte für Change Detection
|
||||||
|
SaveOriginalValues();
|
||||||
|
HasChanges = false;
|
||||||
|
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
_refresh = false;
|
_refresh = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,10 +19,13 @@
|
|||||||
</controls:BaseContentPage.TitleView>
|
</controls:BaseContentPage.TitleView>
|
||||||
|
|
||||||
<ContentPage.Content>
|
<ContentPage.Content>
|
||||||
<Grid>
|
<AbsoluteLayout>
|
||||||
|
<!-- Main scrollable content (full screen) -->
|
||||||
<ScrollView
|
<ScrollView
|
||||||
x:Name="rootScrollView"
|
x:Name="rootScrollView"
|
||||||
Padding="20"
|
AbsoluteLayout.LayoutBounds="0,0,1,1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
Padding="20,20,20,100"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
IsVisible="{Binding IsNotLoading}">
|
IsVisible="{Binding IsNotLoading}">
|
||||||
<VerticalStackLayout
|
<VerticalStackLayout
|
||||||
@ -485,10 +488,34 @@
|
|||||||
Scale="1"
|
Scale="1"
|
||||||
VerticalOptions="Center" />
|
VerticalOptions="Center" />
|
||||||
|
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<!-- Loading Indicator -->
|
||||||
|
<VerticalStackLayout
|
||||||
|
AbsoluteLayout.LayoutBounds="0,0,1,1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="{Binding IsLoading}"
|
||||||
|
VerticalOptions="Center">
|
||||||
|
<ActivityIndicator
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
IsRunning="{Binding IsLoading}"
|
||||||
|
IsVisible="{Binding IsLoading}"
|
||||||
|
Scale="2"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
|
<!-- Floating Save Button (over content) -->
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnSave"
|
x:Name="btnSave"
|
||||||
|
Margin="0,0,0,20"
|
||||||
|
AbsoluteLayout.LayoutBounds="0.5,1,300,60"
|
||||||
|
AbsoluteLayout.LayoutFlags="PositionProportional"
|
||||||
Command="{Binding SaveCommand}"
|
Command="{Binding SaveCommand}"
|
||||||
Text="{Static res:Text.Button_Done}">
|
HorizontalOptions="Center"
|
||||||
|
IsVisible="{Binding HasChanges}"
|
||||||
|
Text="{x:Static res:Text.Button_Done}"
|
||||||
|
VerticalOptions="End">
|
||||||
<Button.IsEnabled>
|
<Button.IsEnabled>
|
||||||
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
|
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
|
||||||
<Binding Path="IsValid" Source="{x:Reference firstNameValidation}" />
|
<Binding Path="IsValid" Source="{x:Reference firstNameValidation}" />
|
||||||
@ -506,16 +533,6 @@
|
|||||||
</Button.IsEnabled>
|
</Button.IsEnabled>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</VerticalStackLayout>
|
</AbsoluteLayout>
|
||||||
</ScrollView>
|
|
||||||
<VerticalStackLayout IsVisible="{Binding IsLoading}" VerticalOptions="Center">
|
|
||||||
<ActivityIndicator
|
|
||||||
HorizontalOptions="Center"
|
|
||||||
IsRunning="{Binding IsLoading}"
|
|
||||||
IsVisible="{Binding IsLoading}"
|
|
||||||
Scale="2"
|
|
||||||
VerticalOptions="Center" />
|
|
||||||
</VerticalStackLayout>
|
|
||||||
</Grid>
|
|
||||||
</ContentPage.Content>
|
</ContentPage.Content>
|
||||||
</controls:BaseContentPage>
|
</controls:BaseContentPage>
|
||||||
@ -20,10 +20,13 @@
|
|||||||
</controls:BaseContentPage.TitleView>
|
</controls:BaseContentPage.TitleView>
|
||||||
|
|
||||||
<ContentPage.Content>
|
<ContentPage.Content>
|
||||||
<Grid>
|
<AbsoluteLayout>
|
||||||
|
<!-- Main scrollable content (full screen) -->
|
||||||
<ScrollView
|
<ScrollView
|
||||||
x:Name="rootScrollView"
|
x:Name="rootScrollView"
|
||||||
Padding="20"
|
AbsoluteLayout.LayoutBounds="0,0,1,1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
Padding="20,20,20,100"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
IsVisible="{Binding IsNotLoading}"
|
IsVisible="{Binding IsNotLoading}"
|
||||||
VerticalOptions="FillAndExpand">
|
VerticalOptions="FillAndExpand">
|
||||||
@ -992,10 +995,34 @@
|
|||||||
Scale="1"
|
Scale="1"
|
||||||
VerticalOptions="Center" />
|
VerticalOptions="Center" />
|
||||||
|
|
||||||
|
</VerticalStackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<!-- Loading Indicator -->
|
||||||
|
<VerticalStackLayout
|
||||||
|
AbsoluteLayout.LayoutBounds="0,0,1,1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="{Binding IsLoading}"
|
||||||
|
VerticalOptions="CenterAndExpand">
|
||||||
|
<ActivityIndicator
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
IsRunning="{Binding IsLoading}"
|
||||||
|
IsVisible="{Binding IsLoading}"
|
||||||
|
Scale="2"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
|
<!-- Floating Save Button (over content) -->
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnSave"
|
x:Name="btnSave"
|
||||||
|
Margin="0,0,0,20"
|
||||||
|
AbsoluteLayout.LayoutBounds="0.5,1,300,60"
|
||||||
|
AbsoluteLayout.LayoutFlags="PositionProportional"
|
||||||
Command="{Binding SaveCommand}"
|
Command="{Binding SaveCommand}"
|
||||||
Text="{Static res:Text.Button_Done}">
|
HorizontalOptions="Center"
|
||||||
|
IsVisible="{Binding HasChanges}"
|
||||||
|
Text="{x:Static res:Text.Button_Done}"
|
||||||
|
VerticalOptions="End">
|
||||||
<Button.IsEnabled>
|
<Button.IsEnabled>
|
||||||
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
|
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
|
||||||
<Binding Path="IsValid" Source="{x:Reference firstNameValidation}" />
|
<Binding Path="IsValid" Source="{x:Reference firstNameValidation}" />
|
||||||
@ -1008,25 +1035,12 @@
|
|||||||
<Binding Path="IsValid" Source="{x:Reference mobileValidation}" />
|
<Binding Path="IsValid" Source="{x:Reference mobileValidation}" />
|
||||||
<Binding Path="IsValid" Source="{x:Reference phoneValidation}" />
|
<Binding Path="IsValid" Source="{x:Reference phoneValidation}" />
|
||||||
<Binding Path="AddressSelectionValid" />
|
<Binding Path="AddressSelectionValid" />
|
||||||
<!--<Binding Source="{x:Reference priceWalkValidation}" Path="IsValid"></Binding>
|
|
||||||
<Binding Source="{x:Reference priceDayValidation}" Path="IsValid"></Binding>
|
|
||||||
<Binding Source="{x:Reference priceSittingValidation}" Path="IsValid"></Binding>-->
|
|
||||||
<Binding Path="IsNotBusy" />
|
<Binding Path="IsNotBusy" />
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</Button.IsEnabled>
|
</Button.IsEnabled>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</VerticalStackLayout>
|
</AbsoluteLayout>
|
||||||
</ScrollView>
|
|
||||||
<VerticalStackLayout IsVisible="{Binding IsLoading}" VerticalOptions="CenterAndExpand">
|
|
||||||
<ActivityIndicator
|
|
||||||
HorizontalOptions="Center"
|
|
||||||
IsRunning="{Binding IsLoading}"
|
|
||||||
IsVisible="{Binding IsLoading}"
|
|
||||||
Scale="2"
|
|
||||||
VerticalOptions="Center" />
|
|
||||||
</VerticalStackLayout>
|
|
||||||
</Grid>
|
|
||||||
</ContentPage.Content>
|
</ContentPage.Content>
|
||||||
|
|
||||||
<controls:BaseContentPage.BottomSheet>
|
<controls:BaseContentPage.BottomSheet>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user