Add phone number validation in message view models and update UI to display error messages

This commit is contained in:
Max Mannstein 2025-07-22 16:28:09 +02:00
parent 0ee468ef8c
commit ce2fdba811
4 changed files with 132 additions and 4 deletions

View File

@ -1,4 +1,5 @@
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.Web;
using CommunityToolkit.Maui.Core.Extensions;
using CommunityToolkit.Mvvm.ComponentModel;
@ -28,6 +29,13 @@ namespace gehGassiApp.ViewModels.Messages
private readonly IFavouriteService _favouriteService;
private string _conversationId;
// Regex pattern für Telefonnummer-Erkennung
private static readonly Regex PhoneNumberRegex = new Regex(
@"(\+\d{1,4}[\s\-\(\)]*(?:\(0\)[\s\-]*)?[\d\s\-\(\)\/]{7,15})|" + // Internationale Nummern mit Ländercode
@"(0\d{2,5}[\s\-\/]*\d{4,12})|" + // Deutsche Festnetz-/Mobilnummern
@"(\d{10,15})", // Reine Zahlenketten (10-15 Ziffern)
RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>
/// Erstellt eine Instanz
/// </summary>
@ -50,6 +58,40 @@ namespace gehGassiApp.ViewModels.Messages
_favouriteService = favouriteService;
Title = Text.View_Title_Messages;
SelectedMenu = MenuSelected.Messages;
// PropertyChanged Event für Message registrieren
PropertyChanged += OnPropertyChanged;
}
/// <summary>
/// Handler für PropertyChanged Events
/// </summary>
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Message))
{
ValidateForPhoneNumbers(Message);
}
}
/// <summary>
/// Validiert den Text auf Telefonnummern
/// </summary>
/// <param name="text">Zu validierender Text</param>
private void ValidateForPhoneNumbers(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
HasPhoneNumber = false;
PhoneNumberErrorMessage = string.Empty;
return;
}
var hasPhone = PhoneNumberRegex.IsMatch(text);
HasPhoneNumber = hasPhone;
PhoneNumberErrorMessage = hasPhone
? "Aus Sicherheitsgründen können keine Telefonnummern geteilt werden. Bitte nutzen Sie unseren integrierten Messenger."
: string.Empty;
}
/// <summary>
@ -84,6 +126,18 @@ namespace gehGassiApp.ViewModels.Messages
[ObservableProperty]
private string _message;
/// <summary>
/// Gibt an, ob eine Telefonnummer im Text erkannt wurde
/// </summary>
[ObservableProperty]
private bool _hasPhoneNumber;
/// <summary>
/// Fehlermeldung für Telefonnummer-Validierung
/// </summary>
[ObservableProperty]
private string _phoneNumberErrorMessage;
/// <summary>
/// Wird pull to refresh ausgeführt?
/// </summary>
@ -98,7 +152,7 @@ namespace gehGassiApp.ViewModels.Messages
[RelayCommand]
public async Task Send()
{
if (!string.IsNullOrWhiteSpace(Message))
if (!string.IsNullOrWhiteSpace(Message) && !HasPhoneNumber)
{
IsBusy = true;
using var cts = new CancellationTokenSource(Core.Common.Constants.CreateTimeout);

View File

@ -1,4 +1,5 @@
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.Web;
using CommunityToolkit.Maui.Core.Extensions;
using CommunityToolkit.Mvvm.ComponentModel;
@ -30,6 +31,13 @@ namespace gehGassiApp.ViewModels.Messages
private readonly IFavouriteService _favouriteService;
private string _conversationId;
// Regex pattern für Telefonnummer-Erkennung
private static readonly Regex PhoneNumberRegex = new Regex(
@"(\+\d{1,4}[\s\-\(\)]*(?:\(0\)[\s\-]*)?[\d\s\-\(\)\/]{7,15})|" + // Internationale Nummern mit Ländercode
@"(0\d{2,5}[\s\-\/]*\d{4,12})|" + // Deutsche Festnetz-/Mobilnummern
@"(\d{10,15})", // Reine Zahlenketten (10-15 Ziffern)
RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>
/// Erstellt eine Instanz
@ -53,6 +61,40 @@ namespace gehGassiApp.ViewModels.Messages
_favouriteService = favouriteService;
Title = Text.View_Title_Messages;
SelectedMenu = MenuSelected.Messages;
// PropertyChanged Event für Message registrieren
PropertyChanged += OnPropertyChanged;
}
/// <summary>
/// Handler für PropertyChanged Events
/// </summary>
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Message))
{
ValidateForPhoneNumbers(Message);
}
}
/// <summary>
/// Validiert den Text auf Telefonnummern
/// </summary>
/// <param name="text">Zu validierender Text</param>
private void ValidateForPhoneNumbers(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
HasPhoneNumber = false;
PhoneNumberErrorMessage = string.Empty;
return;
}
var hasPhone = PhoneNumberRegex.IsMatch(text);
HasPhoneNumber = hasPhone;
PhoneNumberErrorMessage = hasPhone
? "Aus Sicherheitsgründen können keine Telefonnummern geteilt werden. Bitte nutzen Sie unseren integrierten Messenger."
: string.Empty;
}
/// <summary>
@ -87,6 +129,18 @@ namespace gehGassiApp.ViewModels.Messages
[ObservableProperty]
private string _message;
/// <summary>
/// Gibt an, ob eine Telefonnummer im Text erkannt wurde
/// </summary>
[ObservableProperty]
private bool _hasPhoneNumber;
/// <summary>
/// Fehlermeldung für Telefonnummer-Validierung
/// </summary>
[ObservableProperty]
private string _phoneNumberErrorMessage;
/// <summary>
/// Wird pull to refresh ausgeführt?
/// </summary>
@ -101,7 +155,7 @@ namespace gehGassiApp.ViewModels.Messages
[RelayCommand]
public async Task Send()
{
if (!string.IsNullOrWhiteSpace(Message))
if (!string.IsNullOrWhiteSpace(Message) && !HasPhoneNumber)
{
IsBusy = true;
using var cts = new CancellationTokenSource(Core.Common.Constants.CreateTimeout);

View File

@ -17,7 +17,7 @@
</controls:BaseContentPage.TitleView>
<ContentPage.Content>
<Grid RowDefinitions="*,Auto" HorizontalOptions="Fill" VerticalOptions="FillAndExpand" Padding="20,10,20,0" RowSpacing="0">
<Grid RowDefinitions="*,Auto,Auto" HorizontalOptions="Fill" VerticalOptions="FillAndExpand" Padding="20,10,20,0" RowSpacing="0">
<Grid Grid.Row="0" VerticalOptions="FillAndExpand" HorizontalOptions="Fill">
<CollectionView x:Name="messages" ItemsSource="{Binding Messages}" ItemTemplate="{StaticResource MessageIosDataTemplateSelector}" HorizontalOptions="Fill" VerticalOptions="Fill"
@ -54,11 +54,21 @@
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
<Binding Source="{x:Reference notEmptyValidation}" Path="IsValid"></Binding>
<Binding Path="IsBusy" Converter="{StaticResource InvertedBoolConverter}"></Binding>
<Binding Path="HasPhoneNumber" Converter="{StaticResource InvertedBoolConverter}"></Binding>
</MultiBinding>
</ImageButton.IsEnabled>
</ImageButton>
</Grid>
</Border>
<!-- Fehlermeldung für Telefonnummer-Validierung -->
<Label Grid.Row="2"
Text="{Binding PhoneNumberErrorMessage}"
IsVisible="{Binding HasPhoneNumber}"
TextColor="Red"
FontSize="12"
Margin="0,5,0,0"
HorizontalOptions="Fill" />
</Grid>
</ContentPage.Content>

View File

@ -19,7 +19,7 @@
<ContentPage.Content>
<Grid RowDefinitions="*,Auto" Padding="20" RowSpacing="20" HorizontalOptions="Fill" VerticalOptions="FillAndExpand">
<Grid RowDefinitions="*,Auto,Auto" Padding="20" RowSpacing="20" HorizontalOptions="Fill" VerticalOptions="FillAndExpand">
<RefreshView Grid.Row="0" IsRefreshing="{Binding IsRefreshing}" Command="{Binding RefreshCommand}">
<Grid HorizontalOptions="Fill" VerticalOptions="FillAndExpand">
<CollectionView x:Name="messages" ItemsSource="{Binding MessageGroups}" ItemTemplate="{StaticResource MessageDataTemplateSelector}" HorizontalOptions="Fill" IsGrouped="True"
@ -59,11 +59,21 @@
<MultiBinding Converter="{StaticResource AllTrueMultiConverter}">
<Binding Source="{x:Reference notEmptyValidation}" Path="IsValid"></Binding>
<Binding Path="IsBusy" Converter="{StaticResource InvertedBoolConverter}"></Binding>
<Binding Path="HasPhoneNumber" Converter="{StaticResource InvertedBoolConverter}"></Binding>
</MultiBinding>
</ImageButton.IsEnabled>
</ImageButton>
</Grid>
</Border>
<!-- Fehlermeldung für Telefonnummer-Validierung -->
<Label Grid.Row="2"
Text="{Binding PhoneNumberErrorMessage}"
IsVisible="{Binding HasPhoneNumber}"
TextColor="Red"
FontSize="12"
Margin="0,5,0,0"
HorizontalOptions="Fill" />
</Grid>
</ContentPage.Content>