Basic notification implementation

This commit is contained in:
Royce551 2021-07-10 22:29:26 -05:00
parent a2f589795b
commit f9ba816e03
5 changed files with 189 additions and 2 deletions

View file

@ -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
{
/// <summary>
/// Whether the notification should appear on top of other notifications
/// </summary>
public bool IsImportant { get; set; } = false;
/// <summary>
/// Whether the notification should forcefully open the Notification pane
/// </summary>
public bool DisplayAsToast { get; set; } = false;
/// <summary>
/// Whether the user has seen the notification
/// </summary>
public bool Read { get; set; } = false;
/// <summary>
/// The actual content of the notification
/// </summary>
public string ContentText { get; set; } = string.Empty;
/// <summary>
/// The text to show in the button
/// </summary>
public string ButtonText { get; set; } = string.Empty;
/// <summary>
/// The type of notification
/// </summary>
public NotificationType Type { get; set; } = NotificationType.Generic;
/// <summary>
/// Invoked whenever the notification's button has been clicked. Return true to close the notification.
/// </summary>
public Func<bool> OnButtonClicked { get; set; } = null;
public bool ShouldButtonBeVisible => !string.IsNullOrEmpty(ButtonText) && OnButtonClicked is not null;
}
public enum NotificationType
{
/// <summary>
/// The task the notification is associated with succeeded - green border
/// </summary>
Success,
/// <summary>
/// The notification displays information - no border
/// </summary>
Information,
/// <summary>
/// The task the notification is associated with failed - red border
/// </summary>
Failure,
/// <summary>
/// A generic notification
/// </summary>
Generic
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FRESHMusicPlayer.Handlers.Notifications
{
/// <summary>
/// Handles FMP's notifications
/// </summary>
public class NotificationHandler
{
/// <summary>
/// Gets the currently visible notifications for the window.
/// </summary>
public List<Notification> Notifications { get; private set; } = new List<Notification>();
/// <summary>
/// Raised whenever a notification is added, removed, or updated.
/// </summary>
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);
}
}
}

View file

@ -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<Notification> 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<Button>("NotificationButton");
button.ContextFlyout.ShowAt(button);
}
}
}
public async void CloseThings()
{
LoggingHandler.Log("FMP is shutting down!");

View file

@ -92,7 +92,31 @@
<Button Margin="0,5">
<Image Source="{DynamicResource Search}"/>
</Button>
<Button Margin="0,5">
<Button x:Name="NotificationButton" Click="OnShowNotificationButtonClick" Margin="0,5" IsVisible="{Binding AreThereAnyNotifications}">
<Button.ContextFlyout>
<Flyout ShowMode="Transient">
<DockPanel>
<DockPanel DockPanel.Dock="Top" LastChildFill="False" Margin="0,0,0,10">
<TextBlock DockPanel.Dock="Left" Text="Notifications" FontSize="16" VerticalAlignment="Center" FontWeight="Bold"/>
<Button DockPanel.Dock="Right" Content="Clear All" VerticalAlignment="Center" Command="{Binding ClearAllNotificationsCommand}"/>
</DockPanel>
<ListBox Items="{Binding VisibleNotifications}" MinWidth="300" MaxWidth="300">
<ListBox.DataTemplates>
<DataTemplate>
<StackPanel Background="Transparent" Margin="-10">
<TextBlock Text="{Binding ContentText}" TextWrapping="Wrap"/>
<Button Click="OnNotificationButtonClick" HorizontalAlignment="Center" IsVisible="{Binding ShouldButtonBeVisible}">
<Button.Content>
<TextBlock Text="{Binding ButtonText}"/>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
</DockPanel>
</Flyout>
</Button.ContextFlyout>
<Image Source="{DynamicResource Notification}"/>
</Button>
<Button Margin="0,5" Command="{Binding OpenQueueManagementCommand}" ToolTip.Tip="{x:Static resx:Resources.QueueManagement}">

View file

@ -13,6 +13,8 @@ using FRESHMusicPlayer.Utilities;
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
using FRESHMusicPlayer.Handlers.Notifications;
using Avalonia.Controls.Primitives;
namespace FRESHMusicPlayer.Views
{
@ -87,6 +89,21 @@ namespace FRESHMusicPlayer.Views
}
}
private void OnShowNotificationButtonClick(object sender, RoutedEventArgs e)
{
var button = this.FindControl<Button>("NotificationButton");
button.ContextFlyout.ShowAt(button);
}
private void OnNotificationButtonClick(object sender, RoutedEventArgs e)
{
var cmd = (Button)sender;
if (cmd.DataContext is Notification x)
{
if (x.OnButtonClicked?.Invoke() ?? true) ViewModel?.Notifications.Remove(x);
}
}
private void OnPointerWheelChanged(object sender, PointerWheelEventArgs e)
{
ViewModel.Volume += ((float)((e.Delta.Y) / 100) * 3);
@ -141,6 +158,19 @@ namespace FRESHMusicPlayer.Views
case Key.F4:
Topmost = !Topmost;
break;
case Key.F6:
ViewModel.Notifications.Add(new()
{
ContentText = "Catgirls are cute catgirls are cute catgirls are cute text wrap test text wrap test",
ButtonText = "Testing 1 2 3!",
DisplayAsToast = true,
OnButtonClicked = () =>
{
new MessageBox().SetStuff("lols", "wtf, that actually worked?").ShowDialog(this);
return true;
}
});
break;
}
}