Custom toast control, colored by type

This commit is contained in:
Daniel Schick 2024-12-23 18:39:24 +01:00
parent a648cc2e71
commit 1fd87edd6e
11 changed files with 270 additions and 68 deletions

View File

@ -13,9 +13,9 @@ namespace BreCalClient
{
internal class AppNotification
{
private static Dictionary<int, AppNotification> _notifications = new();
private static readonly Dictionary<int, AppNotification> _notifications = new();
private readonly int _id;
private static ObservableCollection<AppNotification> _notificationsCollection = new();
private static readonly ObservableCollection<AppNotification> _notificationsCollection = new();
public AppNotification(int id)
{
@ -31,6 +31,11 @@ namespace BreCalClient
get; private set;
}
public string? NotificationDisplay
{
get; private set;
}
public string? NotificationDate
{
get; private set;
@ -56,6 +61,12 @@ namespace BreCalClient
get; private set;
}
public string? Message
{
get; private set;
}
public static ObservableCollection<AppNotification> AppNotifications { get { return _notificationsCollection; } }
#endregion
@ -94,48 +105,61 @@ namespace BreCalClient
if (!_notificationsCollection.Where(x => x.Id == notification.Id).Any())
{
AppNotification ap = new(notification.Id);
ap.NotificationType = notification.Type.ToString();
ap.Ship = currentShipcalls[notification.ShipcallId]?.Ship?.Name;
ap.ShipcallType = currentShipcalls[notification.ShipcallId]?.Shipcall?.Type.ToString();
ap.ETA = currentShipcalls[notification.ShipcallId]?.GetETAETD(true);
List<AppNotification> newList = new(_notificationsCollection);
AppNotification ap = new(notification.Id)
{
NotificationType = notification.Type.ToString(),
NotificationDate = notification.Created.ToString(),
Ship = currentShipcalls[notification.ShipcallId]?.Ship?.Name,
ShipcallType = currentShipcalls[notification.ShipcallId]?.Shipcall?.Type.ToString(),
ETA = currentShipcalls[notification.ShipcallId]?.GetETAETD(true)
};
Times? agencyTimes = currentShipcalls[notification.ShipcallId]?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
ap.Berth = currentShipcalls[notification.ShipcallId]?.GetBerthText(agencyTimes);
ap.Message = notification.Message;
System.Diagnostics.Trace.WriteLine($"Notification {notification.Id} Type {notification.Type}");
MessageOptions options = new();
options.FontSize = 14;
options.ShowCloseButton = true;
switch(notification.Type) // TODO: Set toast color and icon
MessageOptions options = new()
{
case misc.Model.NotificationType.TimeConflict:
FontSize = 14,
ShowCloseButton = true,
Tag = ap
};
break;
case misc.Model.NotificationType.TimeConflictResolved:
newList.Add(ap);
newList.Sort((a, b) => (a.NotificationDate ?? "").CompareTo(b.NotificationDate));
_notificationsCollection.Clear();
foreach(AppNotification newAp in newList)
_notificationsCollection.Add(newAp);
break;
ap.NotificationDisplay = string.Empty;
switch(notification.Type)
{
case misc.Model.NotificationType.Assignment:
break;
ap.NotificationDisplay = Resources.Resources.textAssignment; break;
case misc.Model.NotificationType.Next24h:
break;
ap.NotificationDisplay = Resources.Resources.textNext24h; break;
case misc.Model.NotificationType.TimeConflict:
ap.NotificationDisplay = Resources.Resources.textTimeConflict; break;
case misc.Model.NotificationType.TimeConflictResolved:
ap.NotificationDisplay = Resources.Resources.textTimeConflictResolved; break;
case misc.Model.NotificationType.Unassigned:
break;
ap.NotificationDisplay = Resources.Resources.textUnassigned; break;
}
_notificationsCollection.Add(ap);
string toastText = ap.NotificationDisplay + "\n";
string toastText = $"{ap.Ship} ({ap.ShipcallType}) - {ap.ETA} - {ap.Berth}";
toastText += $"{ap.Ship} ({ap.ShipcallType}) - {ap.ETA} - {ap.Berth}";
if (!string.IsNullOrEmpty(ap.Message))
toastText += $" \n{ap.Message}";
if (!_notifications.ContainsKey(notification.Id))
{
_notifications.Add(notification.Id, ap);
App.Current.Dispatcher.Invoke(() =>
{
vm.ShowInformation(toastText, options);
vm.ShowAppNotification(toastText, options);
});
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2024- schick Informatik
// Description:
//
using ToastNotifications;
using ToastNotifications.Core;
namespace BreCalClient
{
public static class AppNotificationExtension
{
public static void ShowAppNotification(this Notifier notifier, string message)
{
notifier.Notify(() => new AppNotificationMessage(message));
}
public static void ShowAppNotification(this Notifier notifier, string message, MessageOptions displayOptions)
{
notifier.Notify(() => new AppNotificationMessage(message, displayOptions));
}
}
}

View File

@ -0,0 +1,30 @@
using System.Windows;
using ToastNotifications.Core;
using ToastNotifications.Messages.Core;
namespace BreCalClient
{
public class AppNotificationMessage : MessageBase<AppNotificationPart>
{
public AppNotificationMessage(string message) : this(message, new MessageOptions())
{
}
public AppNotificationMessage(string message, MessageOptions options) : base(message, options)
{
}
protected override AppNotificationPart CreateDisplayPart()
{
return new AppNotificationPart(this);
}
protected override void UpdateDisplayOptions(AppNotificationPart displayPart, MessageOptions options)
{
// if (options.FontSize != null)
// displayPart.Text.FontSize = options.FontSize.Value;
// displayPart.CloseButton.Visibility = options.ShowCloseButton ? Visibility.Visible : Visibility.Collapsed;
}
}
}

View File

@ -0,0 +1,31 @@
<core:NotificationDisplayPart x:Class="BreCalClient.AppNotificationPart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:core="clr-namespace:ToastNotifications.Core;assembly=ToastNotifications"
mc:Ignorable="d" d:DesignWidth="250" >
<Border x:Name="ContentWrapper" Style="{DynamicResource NotificationBorder}" Background="{DynamicResource ErrorColorBrush}">
<Grid x:Name="ContentContainer">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="Icon" Width="24" Height="24">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource ErrorIcon}" />
</Rectangle.Fill>
</Rectangle>
<TextBlock x:Name="Text" Text="{Binding Message, Mode=OneTime}" Style="{StaticResource NotificationText}" Grid.Column="1" />
<Button x:Name="CloseButton" Style="{StaticResource NotificationCloseButton}" Padding="1" Grid.Column="2" Click="OnClose" Visibility="Hidden">
<Rectangle Style="{StaticResource CloseButtonIcon}" Margin="2">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill" Visual="{StaticResource CloseIcon}" />
</Rectangle.OpacityMask>
</Rectangle>
</Button>
</Grid>
</Border>
</core:NotificationDisplayPart>

View File

@ -0,0 +1,51 @@
using BreCalClient.misc.Model;
using System.Windows;
using System.Windows.Media;
using ToastNotifications.Core;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for NotificationPart.xaml
/// </summary>
public partial class AppNotificationPart : NotificationDisplayPart
{
public AppNotificationPart(AppNotificationMessage appNotification)
{
InitializeComponent();
Bind(appNotification);
if (appNotification.Options.Tag is AppNotification ap)
{
switch (ap.NotificationType)
{
case "TimeConflict":
this.ContentWrapper.Background = Brushes.Red;
break;
case "TimeConflictResolved":
this.ContentWrapper.Background = Brushes.Green;
break;
case "Assignment":
this.ContentWrapper.Background = Brushes.Blue;
break;
case "Next24h":
this.ContentWrapper.Background = Brushes.DarkOrange;
break;
case "Unassigned":
this.ContentWrapper.Background = Brushes.Gray;
break;
default:
break;
}
}
}
private void OnClose(object sender, RoutedEventArgs e)
{
Notification.Close();
}
}
}

View File

@ -13,7 +13,7 @@
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<local:ENIDataGrid x:Name="dataGridNotifications" Grid.Row="0" SelectionMode="Single" IsReadOnly="True" AlternatingRowBackground="LightBlue" AutoGenerateColumns="False"
<local:ENIDataGrid x:Name="dataGridNotifications" Grid.Row="0" SelectionMode="Single" IsReadOnly="True" AutoGenerateColumns="False"
CanUserAddRows="False" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<local:ENIDataGrid.RowStyle>
<Style TargetType="DataGridRow">
@ -38,7 +38,7 @@
</local:ENIDataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textType}" Binding="{Binding Path=NotificationType}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textType}" Binding="{Binding Path=NotificationDisplay}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textDate}" Binding="{Binding Path=NotificationDate}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textShip}" Binding="{Binding Path=Ship}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textShipcall}" Binding="{Binding Path=ShipcallType}" IsReadOnly="True"/>

View File

@ -390,6 +390,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Participant assigned to shipcall.
/// </summary>
public static string textAssignment {
get {
return ResourceManager.GetString("textAssignment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Berth.
/// </summary>
@ -903,6 +912,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Relevant next 24hrs.
/// </summary>
public static string textNext24h {
get {
return ResourceManager.GetString("textNext24h", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Notifications.
/// </summary>
@ -1344,6 +1362,24 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Participants disagree on times.
/// </summary>
public static string textTimeConflict {
get {
return ResourceManager.GetString("textTimeConflict", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Times conflict resolved.
/// </summary>
public static string textTimeConflictResolved {
get {
return ResourceManager.GetString("textTimeConflictResolved", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Timestamp.
/// </summary>
@ -1434,6 +1470,15 @@ namespace BreCalClient.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Participant unassigned from shipcall.
/// </summary>
public static string textUnassigned {
get {
return ResourceManager.GetString("textUnassigned", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to User login.
/// </summary>

View File

@ -574,4 +574,19 @@
<data name="textShowNotifications" xml:space="preserve">
<value>Benachrichtigungen anzeigen</value>
</data>
<data name="textAssignment" xml:space="preserve">
<value>Teilnehmer wurde nominiert</value>
</data>
<data name="textNext24h" xml:space="preserve">
<value>Relevant für Morgenrunde (24hrs)</value>
</data>
<data name="textTimeConflict" xml:space="preserve">
<value>"Ampel"-Regel(n) wurde verletzt</value>
</data>
<data name="textTimeConflictResolved" xml:space="preserve">
<value>Zeitliche Konflikte aufgelöst</value>
</data>
<data name="textUnassigned" xml:space="preserve">
<value>Nominierung des Teilnehmer entfernt</value>
</data>
</root>

View File

@ -616,13 +616,28 @@
<data name="bell3" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>bell3.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="textAssignment" xml:space="preserve">
<value>Participant assigned to shipcall</value>
</data>
<data name="textDate" xml:space="preserve">
<value>Date</value>
</data>
<data name="textNext24h" xml:space="preserve">
<value>Relevant next 24hrs</value>
</data>
<data name="textShipcall" xml:space="preserve">
<value>Shipcall</value>
</data>
<data name="textShowNotifications" xml:space="preserve">
<value>Show notificiations</value>
</data>
<data name="textTimeConflict" xml:space="preserve">
<value>Participants disagree on times</value>
</data>
<data name="textTimeConflictResolved" xml:space="preserve">
<value>Times conflict resolved</value>
</data>
<data name="textUnassigned" xml:space="preserve">
<value>Participant unassigned from shipcall</value>
</data>
</root>

View File

@ -98,7 +98,7 @@ namespace BreCalClient
private async void DataGridShips_CreateRequested()
{
ShipModel shipModel = new((ShipModel.LastEditShip != null) ? ShipModel.LastEditShip : new Ship());
ShipModel shipModel = new(ShipModel.LastEditShip ?? new Ship());
EditShipDialog esd = new()
{

View File

@ -30,7 +30,7 @@ namespace BreCalClient
offsetY: 100);
cfg.LifetimeSupervisor = new TimeAndCountBasedLifetimeSupervisor(
notificationLifetime: TimeSpan.FromSeconds(6),
notificationLifetime: TimeSpan.FromSeconds(30),
maximumNotificationCount: MaximumNotificationCount.FromCount(6));
cfg.Dispatcher = Application.Current.Dispatcher;
@ -47,54 +47,22 @@ namespace BreCalClient
_notifier.Dispose();
}
public void ShowInformation(string message)
public void ShowAppNotification(string message, MessageOptions options)
{
_notifier.ShowInformation(message);
_notifier?.ShowAppNotification(message, options);
}
public void ShowInformation(string message, MessageOptions opts)
public void ShowAppNotification(string message)
{
_notifier.ShowInformation(message, opts);
_notifier?.ShowAppNotification(message);
}
public void ShowSuccess(string message)
{
_notifier.ShowSuccess(message);
}
public void ShowSuccess(string message, MessageOptions opts)
{
_notifier.ShowSuccess(message, opts);
}
internal void ClearMessages(string msg)
{
_notifier.ClearMessages(new ClearByMessage(msg));
}
public void ShowWarning(string message, MessageOptions opts)
{
_notifier.ShowWarning(message, opts);
}
public void ShowError(string message)
{
_notifier.ShowError(message);
}
public void ShowError(string message, MessageOptions opts)
{
_notifier.ShowError(message, opts);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string? propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ClearAll()