// 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(int id) { private static readonly Dictionary _notifications = []; private static readonly ObservableCollection _notificationsCollection = []; #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 AppNotifications { get { return _notificationsCollection; } } #endregion #region internal statics internal static void LoadFromSettings() { _notifications.Clear(); if (Properties.Settings.Default.Notifications != null) { // 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 notifications, System.Collections.Concurrent.ConcurrentDictionary currentShipcalls, ToastViewModel vm, LoginResult loginResult) { bool result = false; foreach (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; // filter out notifications for shipcalls where we are not nomiated/assigned if (!notification.ParticipantId.HasValue) { bool iAmAssigned = false; foreach (ParticipantAssignment p in currentShipcalls[notification.ShipcallId].AssignedParticipants.Values) { if (p.ParticipantId.Equals(App.Participant.Id)) { iAmAssigned = true; break; } } if (!iAmAssigned) continue; } // filter out notifications the user is not interested in if((notification.Type != null) && !loginResult.NotifyOn.Contains(notification.Type.Value)) continue; if (!_notificationsCollection.Where(x => x.Id == notification.Id).Any()) { List 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.MissingData: ap.NotificationDisplay = Resources.Resources.textMissingData; break; case misc.Model.NotificationType.Cancelled: ap.NotificationDisplay = Resources.Resources.textCancelled; 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.TryAdd(notification.Id, ap)) { App.Current.Dispatcher.Invoke(() => { vm.ShowAppNotification(toastText, options); }); result = true; } } } if (result) SaveNotifications(); // store notification ids in config array on change return result; } internal static void SaveNotifications() { if (Properties.Settings.Default.Notifications == null) Properties.Settings.Default.Notifications = []; else Properties.Settings.Default.Notifications.Clear(); foreach (int notification_id in _notifications.Keys) { Properties.Settings.Default.Notifications.Add(notification_id.ToString()); } } #endregion } }