diff --git a/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/Notification.cs b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/Notification.cs
new file mode 100644
index 0000000..7fb09f3
--- /dev/null
+++ b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/Notification.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FRESHMusicPlayer.Handlers.Notifications
+{
+ public class Notification
+ {
+ ///
+ /// Whether the notification should appear on top of other notifications
+ ///
+ public bool IsImportant { get; set; } = false;
+ ///
+ /// Whether the notification should forcefully open the Notification pane
+ ///
+ public bool DisplayAsToast { get; set; } = false;
+ ///
+ /// Whether the user has seen the notification
+ ///
+ public bool Read { get; set; } = false;
+ ///
+ /// The actual content of the notification
+ ///
+ public string ContentText { get; set; } = string.Empty;
+ ///
+ /// The text to show in the button
+ ///
+ public string ButtonText { get; set; } = string.Empty;
+ ///
+ /// The type of notification
+ ///
+ public NotificationType Type { get; set; } = NotificationType.Generic;
+ ///
+ /// Invoked whenever the notification's button has been clicked. Return true to close the notification.
+ ///
+ public Func OnButtonClicked { get; set; } = null;
+
+ public bool ShouldButtonBeVisible => !string.IsNullOrEmpty(ButtonText) && OnButtonClicked is not null;
+ }
+ public enum NotificationType
+ {
+ ///
+ /// The task the notification is associated with succeeded - green border
+ ///
+ Success,
+ ///
+ /// The notification displays information - no border
+ ///
+ Information,
+ ///
+ /// The task the notification is associated with failed - red border
+ ///
+ Failure,
+ ///
+ /// A generic notification
+ ///
+ Generic
+ }
+}
diff --git a/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/NotificationHandler.cs b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/NotificationHandler.cs
new file mode 100644
index 0000000..74c43a5
--- /dev/null
+++ b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/Handlers/Notifications/NotificationHandler.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FRESHMusicPlayer.Handlers.Notifications
+{
+ ///
+ /// Handles FMP's notifications
+ ///
+ public class NotificationHandler
+ {
+ ///
+ /// Gets the currently visible notifications for the window.
+ ///
+ public List Notifications { get; private set; } = new List();
+ ///
+ /// Raised whenever a notification is added, removed, or updated.
+ ///
+ public event EventHandler NotificationInvalidate;
+
+ public void Add(Notification box)
+ {
+ Notifications.Add(box);
+ NotificationInvalidate?.Invoke(null, EventArgs.Empty);
+ }
+ public void Update(Notification box)
+ {
+ int notificationindex = Notifications.IndexOf(box);
+ if (Notifications[notificationindex] != null)
+ {
+ Notifications[notificationindex] = box;
+ }
+ NotificationInvalidate?.Invoke(null, EventArgs.Empty);
+ }
+ public void Remove(Notification box)
+ {
+ Notifications.Remove(box);
+ NotificationInvalidate?.Invoke(null, EventArgs.Empty);
+ }
+ public void ClearAll()
+ {
+ Notifications.Clear();
+ NotificationInvalidate?.Invoke(null, EventArgs.Empty);
+ }
+
+ }
+}
diff --git a/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/ViewModels/MainWindowViewModel.cs b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/ViewModels/MainWindowViewModel.cs
index 6ffc0db..d07883c 100644
--- a/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/ViewModels/MainWindowViewModel.cs
+++ b/FRESHMusicPlayer/FRESHMusicPlayer-Avalonia/ViewModels/MainWindowViewModel.cs
@@ -3,6 +3,7 @@ using ATL.Playlist;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using AvaloniaPrimatives = Avalonia.Controls.Primitives;
using Avalonia.Data.Converters;
using Avalonia.Media;
using Avalonia.Media.Imaging;
@@ -10,6 +11,7 @@ using Avalonia.Threading;
using FRESHMusicPlayer.Handlers;
using FRESHMusicPlayer.Handlers.Configuration;
using FRESHMusicPlayer.Handlers.Integrations;
+using FRESHMusicPlayer.Handlers.Notifications;
using FRESHMusicPlayer.Views;
using LiteDB;
using ReactiveUI;
@@ -22,6 +24,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Timers;
+using Avalonia.Markup.Xaml;
namespace FRESHMusicPlayer.ViewModels
{
@@ -33,7 +36,7 @@ namespace FRESHMusicPlayer.ViewModels
public ConfigurationFile Config { get; private set; }
public Track CurrentTrack { get; private set; }
public IntegrationHandler Integrations { get; private set; } = new();
-
+ public NotificationHandler Notifications { get; private set; } = new();
private Window Window
{
@@ -62,6 +65,11 @@ namespace FRESHMusicPlayer.ViewModels
set => this.RaiseAndSetIfChanged(ref windowTitle, value);
}
+ public ObservableCollection VisibleNotifications => new(Notifications.Notifications);
+ public bool AreThereAnyNotifications => Notifications.Notifications.Count > 0;
+
+ public void ClearAllNotificationsCommand() => Notifications.ClearAll();
+
#region Core
private void Player_SongException(object sender, PlaybackExceptionEventArgs e)
{
@@ -342,6 +350,7 @@ namespace FRESHMusicPlayer.ViewModels
Player.SongStopped += Player_SongStopped;
Player.SongException += Player_SongException;
ProgressTimer.Elapsed += ProgressTimer_Elapsed; // TODO: put this in a more logical place
+ Notifications.NotificationInvalidate += Notifications_NotificationInvalidate;
LoggingHandler.Log("Handling config...");
Config = Program.Config; // HACK: this is a hack
Volume = Config?.Volume ?? 1f;
@@ -374,6 +383,20 @@ namespace FRESHMusicPlayer.ViewModels
await PerformAutoImport();
}
+ private void Notifications_NotificationInvalidate(object sender, EventArgs e)
+ {
+ this.RaisePropertyChanged(nameof(VisibleNotifications));
+ this.RaisePropertyChanged(nameof(AreThereAnyNotifications));
+ foreach (Notification box in Notifications.Notifications)
+ {
+ if (box.DisplayAsToast && !box.Read)
+ {
+ var button = Window.FindControl