Merge branch 'feature/toast_notifications' into develop

This commit is contained in:
Daniel Schick 2025-01-10 11:17:11 +01:00
commit 0c6c3a048d
27 changed files with 905 additions and 68 deletions

View File

@ -1,8 +1,8 @@
//---------------------- //----------------------
// <auto-generated> // <auto-generated>
// Generated REST API Client Code Generator v1.16.0.0 on 10.12.2024 08:56:03 // Generated REST API Client Code Generator v1.17.0.0 on 19.12.2024 11:34:56
// Using the tool OpenAPI Generator v7.9.0 // Using the tool OpenAPI Generator v7.10.0
// </auto-generated> // </auto-generated>
//---------------------- //----------------------
@ -3829,7 +3829,7 @@ namespace BreCalClient.misc.Client
if (response.Headers != null) if (response.Headers != null)
{ {
var filePath = string.IsNullOrEmpty(_configuration.TempFolderPath) var filePath = string.IsNullOrEmpty(_configuration.TempFolderPath)
? Path.GetTempPath() ? global::System.IO.Path.GetTempPath()
: _configuration.TempFolderPath; : _configuration.TempFolderPath;
var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$"); var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$");
foreach (var header in response.Headers) foreach (var header in response.Headers)
@ -4069,7 +4069,7 @@ namespace BreCalClient.misc.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, global::System.IO.Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -4139,7 +4139,7 @@ namespace BreCalClient.misc.Client
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
MaxTimeout = configuration.Timeout, Timeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
@ -4209,11 +4209,11 @@ namespace BreCalClient.misc.Client
return result; return result;
} }
} }
private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult) private async Task<RestResponse<T>> DeserializeRestResponseFromPolicyAsync<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult, CancellationToken cancellationToken = default)
{ {
if (policyResult.Outcome == OutcomeType.Successful) if (policyResult.Outcome == OutcomeType.Successful)
{ {
return client.Deserialize<T>(policyResult.Result); return await client.Deserialize<T>(policyResult.Result, cancellationToken);
} }
else else
{ {
@ -4243,7 +4243,7 @@ namespace BreCalClient.misc.Client
{ {
var policy = RetryConfiguration.RetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request)); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
return Task.FromResult(DeserializeRestResponseFromPolicy<T>(client, request, policyResult)); return DeserializeRestResponseFromPolicyAsync<T>(client, request, policyResult);
} }
else else
{ {
@ -4264,7 +4264,7 @@ namespace BreCalClient.misc.Client
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.AsyncRetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
return DeserializeRestResponseFromPolicy<T>(client, request, policyResult); return await DeserializeRestResponseFromPolicyAsync<T>(client, request, policyResult, cancellationToken);
} }
else else
{ {
@ -5000,7 +5000,7 @@ namespace BreCalClient.misc.Client
{ {
}; };
// Setting Timeout has side effects (forces ApiClient creation). // Setting Timeout has side effects (forces ApiClient creation).
Timeout = 100000; Timeout = TimeSpan.FromSeconds(100);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class /// Initializes a new instance of the <see cref="Configuration" /> class
@ -5072,9 +5072,9 @@ namespace BreCalClient.misc.Client
/// </summary> /// </summary>
public virtual IDictionary<string, string> DefaultHeaders { get; set; } public virtual IDictionary<string, string> DefaultHeaders { get; set; }
/// <summary> /// <summary>
/// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. /// Gets or sets the HTTP timeout of ApiClient. Defaults to 100 seconds.
/// </summary> /// </summary>
public virtual int Timeout { get; set; } public virtual TimeSpan Timeout { get; set; }
/// <summary> /// <summary>
/// Gets or sets the proxy /// Gets or sets the proxy
/// </summary> /// </summary>
@ -5713,10 +5713,10 @@ namespace BreCalClient.misc.Client
/// <value>Temp folder path.</value> /// <value>Temp folder path.</value>
string TempFolderPath { get; } string TempFolderPath { get; }
/// <summary> /// <summary>
/// Gets the HTTP connection timeout (in milliseconds) /// Gets the HTTP connection timeout.
/// </summary> /// </summary>
/// <value>HTTP connection timeout.</value> /// <value>HTTP connection timeout.</value>
int Timeout { get; } TimeSpan Timeout { get; }
/// <summary> /// <summary>
/// Gets the proxy. /// Gets the proxy.
/// </summary> /// </summary>
@ -7097,24 +7097,26 @@ namespace BreCalClient.misc.Model
public partial class Notification : IValidatableObject public partial class Notification : IValidatableObject
{ {
/// <summary> /// <summary>
/// Gets or Sets NotificationType /// Gets or Sets Type
/// </summary> /// </summary>
[DataMember(Name = "notification_type", EmitDefaultValue = true)] [DataMember(Name = "type", EmitDefaultValue = true)]
public NotificationType? NotificationType { get; set; } public NotificationType? Type { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Notification" /> class. /// Initializes a new instance of the <see cref="Notification" /> class.
/// </summary> /// </summary>
/// <param name="id">id.</param> /// <param name="id">id.</param>
/// <param name="shipcallId">shipcallId.</param> /// <param name="shipcallId">shipcallId.</param>
/// <param name="notificationType">notificationType.</param> /// <param name="participantId">participantId.</param>
/// <param name="type">type.</param>
/// <param name="message">message.</param> /// <param name="message">message.</param>
/// <param name="created">Readonly field set by the database when notification was created.</param> /// <param name="created">Readonly field set by the database when notification was created.</param>
/// <param name="modified">Readonly field set by the database when notification was last modified.</param> /// <param name="modified">Readonly field set by the database when notification was last modified.</param>
public Notification(int id = default(int), int shipcallId = default(int), NotificationType? notificationType = default(NotificationType?), string message = default(string), DateTime created = default(DateTime), DateTime? modified = default(DateTime?)) public Notification(int id = default(int), int shipcallId = default(int), int? participantId = default(int?), NotificationType? type = default(NotificationType?), string message = default(string), DateTime created = default(DateTime), DateTime? modified = default(DateTime?))
{ {
this.Id = id; this.Id = id;
this.ShipcallId = shipcallId; this.ShipcallId = shipcallId;
this.NotificationType = notificationType; this.ParticipantId = participantId;
this.Type = type;
this.Message = message; this.Message = message;
this.Created = created; this.Created = created;
this.Modified = modified; this.Modified = modified;
@ -7136,6 +7138,14 @@ namespace BreCalClient.misc.Model
[DataMember(Name = "shipcall_id", EmitDefaultValue = true)] [DataMember(Name = "shipcall_id", EmitDefaultValue = true)]
public int ShipcallId { get; set; } public int ShipcallId { get; set; }
/// <summary> /// <summary>
/// Gets or Sets ParticipantId
/// </summary>
/*
<example>9</example>
*/
[DataMember(Name = "participant_id", EmitDefaultValue = true)]
public int? ParticipantId { get; set; }
/// <summary>
/// Gets or Sets Message /// Gets or Sets Message
/// </summary> /// </summary>
/* /*
@ -7171,7 +7181,8 @@ namespace BreCalClient.misc.Model
sb.Append("class Notification {\n"); sb.Append("class Notification {\n");
sb.Append(" Id: ").Append(Id).Append("\n"); sb.Append(" Id: ").Append(Id).Append("\n");
sb.Append(" ShipcallId: ").Append(ShipcallId).Append("\n"); sb.Append(" ShipcallId: ").Append(ShipcallId).Append("\n");
sb.Append(" NotificationType: ").Append(NotificationType).Append("\n"); sb.Append(" ParticipantId: ").Append(ParticipantId).Append("\n");
sb.Append(" Type: ").Append(Type).Append("\n");
sb.Append(" Message: ").Append(Message).Append("\n"); sb.Append(" Message: ").Append(Message).Append("\n");
sb.Append(" Created: ").Append(Created).Append("\n"); sb.Append(" Created: ").Append(Created).Append("\n");
sb.Append(" Modified: ").Append(Modified).Append("\n"); sb.Append(" Modified: ").Append(Modified).Append("\n");

View File

@ -1550,7 +1550,11 @@ components:
type: integer type: integer
example: 5 example: 5
nullable: false nullable: false
notification_type: participant_id:
type: integer
example: 9
nullable: true
type:
$ref: '#/components/schemas/NotificationType' $ref: '#/components/schemas/NotificationType'
message: message:
type: string type: string
@ -1569,7 +1573,8 @@ components:
example: example:
id: 42 id: 42
shipcall_id: 5 shipcall_id: 5
notification_type: next24h participant_id: 9
type: next24h
message: Shipcall may be relevant to you in the next 24 hours message: Shipcall may be relevant to you in the next 24 hours
created: '2023-08-21T08:23:35Z' created: '2023-08-21T08:23:35Z'
modified: '2023-08-21T08:23:35Z' modified: '2023-08-21T08:23:35Z'
@ -1581,13 +1586,14 @@ components:
example: example:
- id: 42 - id: 42
shipcall_id: 5 shipcall_id: 5
notification_type: time_conflict participant_id: 9
type: time_conflict
message: Entry XY violates rule Z message: Entry XY violates rule Z
created: '2023-08-21T08:23:35Z' created: '2023-08-21T08:23:35Z'
modified: '2023-08-21T08:23:35Z' modified: '2023-08-21T08:23:35Z'
- id: 43 - id: 43
shipcall_id: 7 shipcall_id: 7
notification_type: time_conflict type: time_conflict
message: Entry AB violates rule C message: Entry AB violates rule C
created: '2023-08-21T08:23:35Z' created: '2023-08-21T08:23:35Z'
modified: '2023-08-21T08:23:35Z' modified: '2023-08-21T08:23:35Z'

View File

@ -1 +1 @@
1.7.0.0 1.7.0.3

View File

@ -30,6 +30,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="10" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
@ -47,7 +48,7 @@
Informatikbüro Daniel Schick Informatikbüro Daniel Schick
</Hyperlink> </Hyperlink>
</TextBlock> </TextBlock>
<Border BorderThickness="0 0 0 2" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" BorderBrush="Gray" />
<Label FontWeight="DemiBold" Grid.Row="3" Grid.Column="0" Content="{x:Static p:Resources.textChangeContactInfo}" HorizontalContentAlignment="Right"/> <Label FontWeight="DemiBold" Grid.Row="3" Grid.Column="0" Content="{x:Static p:Resources.textChangeContactInfo}" HorizontalContentAlignment="Right"/>
<Label Grid.Row="4" Grid.Column="0" Content="{x:Static p:Resources.textEmail}" HorizontalContentAlignment="Right" /> <Label Grid.Row="4" Grid.Column="0" Content="{x:Static p:Resources.textEmail}" HorizontalContentAlignment="Right" />
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textPhone}" HorizontalContentAlignment="Right" /> <Label Grid.Row="5" Grid.Column="0" Content="{x:Static p:Resources.textPhone}" HorizontalContentAlignment="Right" />
@ -68,10 +69,10 @@
<xctk:WatermarkPasswordBox Watermark="{x:Static p:Resources.textNewPassword}" Grid.Column="1" Grid.Row="13" Margin="2" x:Name="wpBoxNewPassword" TextChanged="wpBoxOldPassword_TextChanged"/> <xctk:WatermarkPasswordBox Watermark="{x:Static p:Resources.textNewPassword}" Grid.Column="1" Grid.Row="13" Margin="2" x:Name="wpBoxNewPassword" TextChanged="wpBoxOldPassword_TextChanged"/>
<xctk:WatermarkPasswordBox Watermark="{x:Static p:Resources.textRepeatNewPassword}" Grid.Column="1" Grid.Row="14" Margin="2" x:Name="wpBoxNewPasswordRepeat" TextChanged="wpBoxOldPassword_TextChanged"/> <xctk:WatermarkPasswordBox Watermark="{x:Static p:Resources.textRepeatNewPassword}" Grid.Column="1" Grid.Row="14" Margin="2" x:Name="wpBoxNewPasswordRepeat" TextChanged="wpBoxOldPassword_TextChanged"/>
<Button x:Name="buttonChangePassword" Click="buttonChangePassword_Click" Grid.Column="1" Grid.Row="15" Margin="2" Content="{x:Static p:Resources.textChangePassword}" Width="120" HorizontalAlignment="Left" IsEnabled="False" /> <Button x:Name="buttonChangePassword" Click="buttonChangePassword_Click" Grid.Column="1" Grid.Row="15" Margin="2" Content="{x:Static p:Resources.textChangePassword}" Width="120" HorizontalAlignment="Left" IsEnabled="False" />
<Border BorderThickness="0 0 0 2" Grid.Row="16" Grid.Column="0" Grid.ColumnSpan="2" BorderBrush="Gray" />
<Button x:Name="buttonClose" Click="buttonClose_Click" Content="{x:Static p:Resources.textClose}" Width="80" Margin="2" Grid.Column="1" Grid.Row="17" HorizontalAlignment="Right" /> <Button x:Name="buttonClose" Click="buttonClose_Click" Content="{x:Static p:Resources.textClose}" Width="80" Margin="2" Grid.Column="1" Grid.Row="18" HorizontalAlignment="Right" />
</Grid> </Grid>
</Window> </Window>

View File

@ -86,6 +86,12 @@
<setting name="FilterCriteriaMap" serializeAs="String"> <setting name="FilterCriteriaMap" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="W5Top" serializeAs="String">
<value>0</value>
</setting>
<setting name="W5Left" serializeAs="String">
<value>0</value>
</setting>
</BreCalClient.Properties.Settings> </BreCalClient.Properties.Settings>
</userSettings> </userSettings>
</configuration> </configuration>

View File

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BreCalClient" xmlns:local="clr-namespace:BreCalClient"
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
StartupUri="MainWindow.xaml" Exit="Application_Exit" Startup="Application_Startup" > StartupUri="MainWindow.xaml" Exit="Application_Exit" Startup="Application_Startup" >
<Application.Resources> <Application.Resources>
@ -14,6 +15,95 @@
<sys:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">10</sys:Double> <sys:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">10</sys:Double>
<sys:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">10</sys:Double> <sys:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">10</sys:Double>
<Color x:Key="InformationColor">#147ec9</Color>
<SolidColorBrush x:Key="InformationColorBrush" Color="{StaticResource InformationColor}" options:Freeze="True" />
<Color x:Key="SuccessColor">#11ad45</Color>
<SolidColorBrush x:Key="SuccessColorBrush" Color="{StaticResource SuccessColor}" options:Freeze="True" />
<Color x:Key="ErrorColor">#e60914</Color>
<SolidColorBrush x:Key="ErrorColorBrush" Color="{StaticResource ErrorColor}" options:Freeze="True" />
<Color x:Key="WarningColor">#f5a300</Color>
<SolidColorBrush x:Key="WarningColorBrush" Color="{StaticResource WarningColor}" options:Freeze="True" />
<Canvas x:Key="InformationIcon" Width="24" Height="24">
<Path Data="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z" Fill="White" />
</Canvas>
<Canvas x:Key="SuccessIcon" Width="24" Height="24">
<Path Data="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" Fill="White" />
</Canvas>
<Canvas x:Key="ErrorIcon" Width="24" Height="24">
<Path Data="M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z" Fill="White" />
</Canvas>
<Canvas x:Key="WarningIcon" Width="24" Height="24">
<Path Data="M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16" Fill="White" />
</Canvas>
<Canvas x:Key="CloseIcon" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="31.6666" Height="31.6667" Canvas.Left="22.1666" Canvas.Top="22.1667" Stretch="Fill" Fill="#FF000000" Data="F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z "/>
</Canvas>
<Style TargetType="Border" x:Key="NotificationBorder">
<Setter Property="Padding" Value="5" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity="0.5" ShadowDepth="1" BlurRadius="2" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Rectangle" x:Key="NotificationIcon">
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Margin" Value="0,5,5,5" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Fill" Value="White"/>
</Style>
<Style TargetType="TextBlock" x:Key="NotificationText">
<Setter Property="Foreground" Value="White" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
<Style TargetType="{x:Type Button}" x:Key="NotificationCloseButton">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#FFF" />
<Setter Property="FontSize" Value="15" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#33000000" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#77000000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Rectangle" x:Key="CloseButtonIcon">
<Setter Property="Width" Value="10"/>
<Setter Property="Height" Value="10"/>
<Setter Property="Fill" Value="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}" />
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>

View File

@ -0,0 +1,181 @@
// Copyright (c) 2024- schick Informatik
// Description: Helper (static) class to handle polled API notifications
//
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ToastNotifications.Core;
using BreCalClient.misc.Model;
namespace BreCalClient
{
internal class AppNotification
{
private static readonly Dictionary<int, AppNotification> _notifications = new();
private readonly int _id;
private static readonly ObservableCollection<AppNotification> _notificationsCollection = new();
public AppNotification(int id)
{
_id = id;
}
#region Properties
public int Id { get { return _id; } }
public string? NotificationType
{
get; private set;
}
public string? NotificationDisplay
{
get; private set;
}
public string? NotificationDate
{
get; private set;
}
public string? Ship
{
get; private set;
}
public string? ShipcallType
{
get; private set;
}
public string? Berth
{
get; private set;
}
public string? ETA
{
get; private set;
}
public string? Message
{
get; private set;
}
public static ObservableCollection<AppNotification> AppNotifications { get { return _notificationsCollection; } }
#endregion
#region internal statics
internal static void LoadFromSettings()
{
_notifications.Clear();
// load notification ids that have been processed
foreach (string? notification_id in Properties.Settings.Default.Notifications)
{
if(Int32.TryParse(notification_id, out int result))
_notifications.Add(result, new AppNotification(result));
}
}
internal static void Clear()
{
_notifications.Clear();
SaveNotifications();
}
internal static bool UpdateNotifications(List<BreCalClient.misc.Model.Notification> notifications, System.Collections.Concurrent.ConcurrentDictionary<int, ShipcallControlModel> currentShipcalls, ToastViewModel vm)
{
bool result = false;
foreach (BreCalClient.misc.Model.Notification notification in notifications)
{
if (notification.ParticipantId.HasValue && notification.ParticipantId.Value != App.Participant.Id) // not meant for us
continue;
if (!currentShipcalls.ContainsKey(notification.ShipcallId)) // not one of our shipcalls (maybe for another port or filtered)
continue;
if (!_notificationsCollection.Where(x => x.Id == notification.Id).Any())
{
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()
{
FontSize = 14,
ShowCloseButton = true,
Tag = ap
};
newList.Add(ap);
newList.Sort((a, b) => (a.NotificationDate ?? "").CompareTo(b.NotificationDate));
_notificationsCollection.Clear();
foreach(AppNotification newAp in newList)
_notificationsCollection.Add(newAp);
ap.NotificationDisplay = string.Empty;
switch(notification.Type)
{
case misc.Model.NotificationType.Assignment:
ap.NotificationDisplay = Resources.Resources.textAssignment; break;
case misc.Model.NotificationType.Next24h:
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:
ap.NotificationDisplay = Resources.Resources.textUnassigned; break;
}
string toastText = ap.NotificationDisplay + "\n";
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.ShowAppNotification(toastText, options);
});
}
}
}
return result;
}
internal static void SaveNotifications()
{
Properties.Settings.Default.Notifications.Clear();
foreach (int notification_id in _notifications.Keys)
{
Properties.Settings.Default.Notifications.Add(notification_id.ToString());
}
}
#endregion
}
}

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

@ -8,8 +8,8 @@
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<StartupObject>BreCalClient.App</StartupObject> <StartupObject>BreCalClient.App</StartupObject>
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
<AssemblyVersion>1.7.0.0</AssemblyVersion> <AssemblyVersion>1.7.0.3</AssemblyVersion>
<FileVersion>1.7.0.0</FileVersion> <FileVersion>1.7.0.3</FileVersion>
<Title>Bremen calling client</Title> <Title>Bremen calling client</Title>
<Description>A Windows WPF client for the Bremen calling API.</Description> <Description>A Windows WPF client for the Bremen calling API.</Description>
<ApplicationIcon>containership.ico</ApplicationIcon> <ApplicationIcon>containership.ico</ApplicationIcon>
@ -26,6 +26,7 @@
<None Remove="Resources\arrow_up_blue.png" /> <None Remove="Resources\arrow_up_blue.png" />
<None Remove="Resources\arrow_up_green.png" /> <None Remove="Resources\arrow_up_green.png" />
<None Remove="Resources\arrow_up_red.png" /> <None Remove="Resources\arrow_up_red.png" />
<None Remove="Resources\bell3.png" />
<None Remove="Resources\check.png" /> <None Remove="Resources\check.png" />
<None Remove="Resources\clipboard.png" /> <None Remove="Resources\clipboard.png" />
<None Remove="Resources\clock.png" /> <None Remove="Resources\clock.png" />
@ -84,6 +85,7 @@
<Resource Include="Resources\arrow_up_blue.png" /> <Resource Include="Resources\arrow_up_blue.png" />
<Resource Include="Resources\arrow_up_green.png" /> <Resource Include="Resources\arrow_up_green.png" />
<Resource Include="Resources\arrow_up_red.png" /> <Resource Include="Resources\arrow_up_red.png" />
<Resource Include="Resources\bell3.png" />
<Resource Include="Resources\check.png" /> <Resource Include="Resources\check.png" />
<Resource Include="Resources\clipboard.png" /> <Resource Include="Resources\clipboard.png" />
<Resource Include="Resources\clock.png" /> <Resource Include="Resources\clock.png" />
@ -118,10 +120,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="JsonSubTypes" Version="2.0.1" /> <PackageReference Include="JsonSubTypes" Version="2.0.1" />
<PackageReference Include="log4net" Version="2.0.17" /> <PackageReference Include="log4net" Version="3.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="8.4.1" /> <PackageReference Include="Polly" Version="8.5.0" />
<PackageReference Include="RestSharp" Version="112.0.0" /> <PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="ToastNotifications" Version="2.5.1" />
<PackageReference Include="ToastNotifications.Messages" Version="2.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,5 +1,5 @@
// Copyright (c) 2024- schick Informatik // Copyright (c) 2024- schick Informatik
// Description: Window to show (complete) list of current shipcall histories // Description:
// //
using BreCalClient.misc.Api; using BreCalClient.misc.Api;

View File

@ -124,6 +124,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" /> <ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="26" /> <ColumnDefinition Width="26" />
@ -157,19 +159,25 @@
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="9"/> <Separator Grid.Column="9"/>
<StatusBarItem Grid.Column="10"> <StatusBarItem Grid.Column="10">
<Button x:Name="buttonNotifications" Click="buttonNotifications_Click" Width="20" ToolTip="{x:Static p:Resources.textShowNotifications}">
<Image Source="./Resources/bell3.png"/>
</Button>
</StatusBarItem>
<Separator Grid.Column="9"/>
<StatusBarItem Grid.Column="12">
<TextBlock Name="labelStatusBar"></TextBlock> <TextBlock Name="labelStatusBar"></TextBlock>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="11"/> <Separator Grid.Column="13"/>
<StatusBarItem Grid.Column="12"> <StatusBarItem Grid.Column="14">
<Button x:Name="buttonManualRefresh" Width="20" Click="buttonManualRefresh_Click" ToolTip="{x:Static p:Resources.textTriggerManualRefresh}"> <Button x:Name="buttonManualRefresh" Width="20" Click="buttonManualRefresh_Click" ToolTip="{x:Static p:Resources.textTriggerManualRefresh}">
<Image Source="./Resources/nav_refresh_green.png"/> <Image Source="./Resources/nav_refresh_green.png"/>
</Button> </Button>
</StatusBarItem> </StatusBarItem>
<StatusBarItem Grid.Column="13"> <StatusBarItem Grid.Column="15">
<TextBlock x:Name="labelLatestUpdate" /> <TextBlock x:Name="labelLatestUpdate" />
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="14"/> <Separator Grid.Column="16"/>
<StatusBarItem Grid.Column="15"> <StatusBarItem Grid.Column="17">
<ProgressBar Name="generalProgressStatus" Width="90" Height="16" Foreground="LightGray"/> <ProgressBar Name="generalProgressStatus" Width="90" Height="16" Foreground="LightGray"/>
</StatusBarItem> </StatusBarItem>
</StatusBar> </StatusBar>

View File

@ -36,8 +36,11 @@ namespace BreCalClient
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private readonly ILog _log = LogManager.GetLogger(typeof(MainWindow)); private readonly ILog _log = LogManager.GetLogger(typeof(MainWindow));
private readonly ToastViewModel _vm;
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30; private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
private const int SHIPS_UPDATE_INTERVAL_SECONDS = 120; private const int SHIPS_UPDATE_INTERVAL_SECONDS = 120;
private const int CHECK_NOTIFICATIONS_INTERVAL_SECONDS = 5;
private const int PROGRESS_STEPS = 50; private const int PROGRESS_STEPS = 50;
#region Fields #region Fields
@ -68,6 +71,7 @@ namespace BreCalClient
// private bool _filterChanged = false; // private bool _filterChanged = false;
// private bool _sequenceChanged = false; // private bool _sequenceChanged = false;
private HistoryDialog? _historyDialog; private HistoryDialog? _historyDialog;
private NotificationDialog? _notificationDialog;
#endregion #endregion
@ -122,6 +126,13 @@ namespace BreCalClient
RetryConfiguration.AsyncRetryPolicy = retryPolicy; RetryConfiguration.AsyncRetryPolicy = retryPolicy;
this.generalProgressStatus.Maximum = PROGRESS_STEPS; this.generalProgressStatus.Maximum = PROGRESS_STEPS;
_vm = new ToastViewModel();
this.Unloaded += MainWindow_Unloaded;
}
private void MainWindow_Unloaded(object sender, RoutedEventArgs e)
{
_vm.OnUnloaded();
} }
#endregion #endregion
@ -443,6 +454,21 @@ namespace BreCalClient
} }
} }
private void buttonNotifications_Click(object sender, RoutedEventArgs e)
{
if (_notificationDialog == null)
{
_notificationDialog = new NotificationDialog();
_notificationDialog.AppNotifications = AppNotification.AppNotifications;
_notificationDialog.Closed += (sender, e) => { this._notificationDialog = null; };
_notificationDialog.Show();
}
else
{
_notificationDialog.Activate();
}
}
private void buttonManualRefresh_Click(object sender, RoutedEventArgs e) private void buttonManualRefresh_Click(object sender, RoutedEventArgs e)
{ {
_refreshImmediately = true; // set flag to avoid timer loop termination _refreshImmediately = true; // set flag to avoid timer loop termination
@ -510,6 +536,7 @@ namespace BreCalClient
_ = Task.Run(() => RefreshShipcalls()); _ = Task.Run(() => RefreshShipcalls());
_ = Task.Run(() => RefreshShips()); _ = Task.Run(() => RefreshShips());
_ = Task.Run(() => CheckNotifications());
} }
@ -633,6 +660,19 @@ namespace BreCalClient
} }
} }
public async Task CheckNotifications()
{
while (true)
{
Thread.Sleep(CHECK_NOTIFICATIONS_INTERVAL_SECONDS * 1000);
if (_loginResult?.NotifyPopup ?? false)
{
List<Notification> notifications = await _staticApi.NotificationsGetAsync();
AppNotification.UpdateNotifications(notifications, _allShipcallsDict, _vm);
}
}
}
#endregion #endregion
#region basic operations #region basic operations

View File

@ -0,0 +1,61 @@
<Window x:Class="BreCalClient.NotificationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:p = "clr-namespace:BreCalClient.Resources"
mc:Ignorable="d" Left="{local:SettingBinding W5Left}" Top="{local:SettingBinding W5Top}"
Title="{x:Static p:Resources.textNotifications}" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<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">
<Style.Triggers>
<DataTrigger Binding="{Binding NotificationType}" Value="Assignment">
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding NotificationType}" Value="Next24h">
<Setter Property="Foreground" Value="DarkOrange"/>
</DataTrigger>
<DataTrigger Binding="{Binding NotificationType}" Value="TimeConflict">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding NotificationType}" Value="TimeConflictResolved">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding NotificationType}" Value="Unassigned">
<Setter Property="Foreground" Value="DarkGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:ENIDataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" 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"/>
<DataGridTextColumn Header="ETA/ETD" Binding="{Binding Path=ETA}" IsReadOnly="True"/>
<DataGridTextColumn Header="{x:Static p:Resources.textBerth}" Binding="{Binding Path=Berth}" IsReadOnly="True"/>
</DataGrid.Columns>
</local:ENIDataGrid>
<Grid Grid.Row="1" Grid.Column="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width=".2*" />
</Grid.ColumnDefinitions>
<Button x:Name="buttonClose" Click="buttonClose_Click" Content="{x:Static p:Resources.textClose}" Width="80" Margin="2" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Right" />
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,44 @@
// Copyright (c) 2024- schick Informatik
// Description:
//
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for NotificationDialog.xaml
/// </summary>
public partial class NotificationDialog : Window
{
public NotificationDialog()
{
InitializeComponent();
}
internal ObservableCollection<AppNotification>? AppNotifications { get; set; }
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.dataGridNotifications.ItemsSource = AppNotifications;
}
}
}

View File

@ -4,8 +4,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
--> -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<ApplicationRevision>2</ApplicationRevision> <ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.7.0.0</ApplicationVersion> <ApplicationVersion>1.7.0.3</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled> <BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut> <CreateDesktopShortcut>True</CreateDesktopShortcut>
@ -38,7 +38,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<UpdateMode>Foreground</UpdateMode> <UpdateMode>Foreground</UpdateMode>
<UpdateRequired>True</UpdateRequired> <UpdateRequired>True</UpdateRequired>
<WebPageFileName>Publish.html</WebPageFileName> <WebPageFileName>Publish.html</WebPageFileName>
<MinimumRequiredVersion>1.7.0.0</MinimumRequiredVersion> <MinimumRequiredVersion>1.7.0.3</MinimumRequiredVersion>
<SkipPublishVerification>false</SkipPublishVerification> <SkipPublishVerification>false</SkipPublishVerification>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -9,20 +9,20 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace BreCalClient.Properties { namespace BreCalClient.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default { public static Settings Default {
get { get {
return defaultInstance; return defaultInstance;
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("#1D751F")] [global::System.Configuration.DefaultSettingValueAttribute("#1D751F")]
@ -31,7 +31,7 @@ namespace BreCalClient.Properties {
return ((string)(this["BG_COLOR"])); return ((string)(this["BG_COLOR"]));
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("!!Bremen calling Testversion!!")] [global::System.Configuration.DefaultSettingValueAttribute("!!Bremen calling Testversion!!")]
@ -40,7 +40,7 @@ namespace BreCalClient.Properties {
return ((string)(this["APP_TITLE"])); return ((string)(this["APP_TITLE"]));
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://www.textbausteine.net/")] [global::System.Configuration.DefaultSettingValueAttribute("https://www.textbausteine.net/")]
@ -49,7 +49,7 @@ namespace BreCalClient.Properties {
return ((string)(this["LOGO_IMAGE_URL"])); return ((string)(this["LOGO_IMAGE_URL"]));
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")] [global::System.Configuration.DefaultSettingValueAttribute("")]
@ -61,7 +61,7 @@ namespace BreCalClient.Properties {
this["FilterCriteria"] = value; this["FilterCriteria"] = value;
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://brecaldevel.bsmd-emswe.eu")] [global::System.Configuration.DefaultSettingValueAttribute("https://brecaldevel.bsmd-emswe.eu")]
@ -70,7 +70,7 @@ namespace BreCalClient.Properties {
return ((string)(this["API_URL"])); return ((string)(this["API_URL"]));
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("800")] [global::System.Configuration.DefaultSettingValueAttribute("800")]
@ -82,7 +82,7 @@ namespace BreCalClient.Properties {
this["Width"] = value; this["Width"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("450")] [global::System.Configuration.DefaultSettingValueAttribute("450")]
@ -94,7 +94,7 @@ namespace BreCalClient.Properties {
this["Height"] = value; this["Height"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -106,7 +106,7 @@ namespace BreCalClient.Properties {
this["Left"] = value; this["Left"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -118,7 +118,7 @@ namespace BreCalClient.Properties {
this["Top"] = value; this["Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -130,7 +130,7 @@ namespace BreCalClient.Properties {
this["W1Left"] = value; this["W1Left"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -142,7 +142,7 @@ namespace BreCalClient.Properties {
this["W1Top"] = value; this["W1Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -154,7 +154,7 @@ namespace BreCalClient.Properties {
this["W2Left"] = value; this["W2Left"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -166,7 +166,7 @@ namespace BreCalClient.Properties {
this["W2Top"] = value; this["W2Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -178,7 +178,7 @@ namespace BreCalClient.Properties {
this["W3Left"] = value; this["W3Left"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -190,7 +190,7 @@ namespace BreCalClient.Properties {
this["W3Top"] = value; this["W3Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -202,7 +202,7 @@ namespace BreCalClient.Properties {
this["W4Left"] = value; this["W4Left"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")] [global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -214,7 +214,7 @@ namespace BreCalClient.Properties {
this["W4Top"] = value; this["W4Top"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")] [global::System.Configuration.DefaultSettingValueAttribute("")]
@ -226,5 +226,40 @@ namespace BreCalClient.Properties {
this["FilterCriteriaMap"] = value; this["FilterCriteriaMap"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Specialized.StringCollection Notifications {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["Notifications"]));
}
set {
this["Notifications"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W5Top {
get {
return ((double)(this["W5Top"]));
}
set {
this["W5Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public double W5Left {
get {
return ((double)(this["W5Left"]));
}
set {
this["W5Left"] = value;
}
}
} }
} }

View File

@ -56,5 +56,14 @@
<Setting Name="FilterCriteriaMap" Type="System.String" Scope="User"> <Setting Name="FilterCriteriaMap" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="Notifications" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="W5Top" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="W5Left" Type="System.Double" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -167,6 +167,16 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] bell3 {
get {
object obj = ResourceManager.GetObject("bell3", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@ -380,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> /// <summary>
/// Looks up a localized string similar to Berth. /// Looks up a localized string similar to Berth.
/// </summary> /// </summary>
@ -560,6 +579,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Date.
/// </summary>
public static string textDate {
get {
return ResourceManager.GetString("textDate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Delete. /// Looks up a localized string similar to Delete.
/// </summary> /// </summary>
@ -884,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> /// <summary>
/// Looks up a localized string similar to Notifications. /// Looks up a localized string similar to Notifications.
/// </summary> /// </summary>
@ -1208,6 +1245,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Shipcall.
/// </summary>
public static string textShipcall {
get {
return ResourceManager.GetString("textShipcall", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Ship length. /// Looks up a localized string similar to Ship length.
/// </summary> /// </summary>
@ -1244,6 +1290,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Show notificiations.
/// </summary>
public static string textShowNotifications {
get {
return ResourceManager.GetString("textShowNotifications", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Sort order. /// Looks up a localized string similar to Sort order.
/// </summary> /// </summary>
@ -1307,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> /// <summary>
/// Looks up a localized string similar to Timestamp. /// Looks up a localized string similar to Timestamp.
/// </summary> /// </summary>
@ -1397,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> /// <summary>
/// Looks up a localized string similar to User login. /// Looks up a localized string similar to User login.
/// </summary> /// </summary>

View File

@ -565,4 +565,28 @@
<data name="textNotifyPush" xml:space="preserve"> <data name="textNotifyPush" xml:space="preserve">
<value>Banner / Push Benachrichtigung in App</value> <value>Banner / Push Benachrichtigung in App</value>
</data> </data>
<data name="textDate" xml:space="preserve">
<value>Datum</value>
</data>
<data name="textShipcall" xml:space="preserve">
<value>Anlauf</value>
</data>
<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> </root>

View File

@ -613,4 +613,31 @@
<data name="textNotifyPush" xml:space="preserve"> <data name="textNotifyPush" xml:space="preserve">
<value>Notify by push notification in app</value> <value>Notify by push notification in app</value>
</data> </data>
<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> </root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -98,7 +98,7 @@ namespace BreCalClient
private async void DataGridShips_CreateRequested() 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() EditShipDialog esd = new()
{ {

View File

@ -0,0 +1,73 @@
// Copyright (c) 2024- schick Informatik
// Description:
//
using System;
using System.ComponentModel;
using System.Windows;
using ToastNotifications;
using ToastNotifications.Core;
using ToastNotifications.Lifetime;
using ToastNotifications.Lifetime.Clear;
using ToastNotifications.Messages;
using ToastNotifications.Position;
namespace BreCalClient
{
internal class ToastViewModel : INotifyPropertyChanged
{
private readonly Notifier _notifier;
public ToastViewModel()
{
_notifier = new Notifier(cfg =>
{
cfg.PositionProvider = new WindowPositionProvider(
parentWindow: Application.Current.MainWindow,
corner: Corner.BottomRight,
offsetX: 25,
offsetY: 100);
cfg.LifetimeSupervisor = new TimeAndCountBasedLifetimeSupervisor(
notificationLifetime: TimeSpan.FromSeconds(30),
maximumNotificationCount: MaximumNotificationCount.FromCount(6));
cfg.Dispatcher = Application.Current.Dispatcher;
cfg.DisplayOptions.TopMost = false;
cfg.DisplayOptions.Width = 250;
});
_notifier.ClearMessages(new ClearAll());
}
public void OnUnloaded()
{
_notifier.Dispose();
}
public void ShowAppNotification(string message, MessageOptions options)
{
_notifier?.ShowAppNotification(message, options);
}
public void ShowAppNotification(string message)
{
_notifier?.ShowAppNotification(message);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string? propertyName = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ClearAll()
{
_notifier.ClearMessages(new ClearAll());
}
}
}

View File

@ -6,8 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>Resources\lock_preferences.ico</ApplicationIcon> <ApplicationIcon>Resources\lock_preferences.ico</ApplicationIcon>
<FileVersion>1.7.0.0</FileVersion> <FileVersion>1.7.0.3</FileVersion>
<AssemblyVersion>1.7.0.0</AssemblyVersion> <AssemblyVersion>1.7.0.3</AssemblyVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>