App Settings now chooses one of six ToastArea positions (left/right × top/center/bottom), defaulting to the existing bottom-right. The setting is persisted per profile and the toast area moves immediately; edge gutters keep bottom notifications above the footer. ToastPositionIntegrationTests covers live repositioning and restart persistence. 617 tests green, clean build with 0 warnings. Reference scan: OBS Notifier supports user-configured notification placement: https://github.com/DmitriySalnikov/OBSNotifier
67 lines
2.5 KiB
C#
67 lines
2.5 KiB
C#
using System.Windows;
|
|
using System.Windows.Threading;
|
|
using Notification.Core;
|
|
using Notification.Wpf;
|
|
using Notification.Wpf.Constants;
|
|
using ytLive.Helpers;
|
|
|
|
namespace ytLive.Services;
|
|
|
|
/// <summary>
|
|
/// Toast notifications rendered inside the main window's <c>ToastArea</c>, whose
|
|
/// position is configurable in App Settings (bottom-right by default). Info/success auto-dismiss after ~4s,
|
|
/// warnings ~8s, errors stay until dismissed. Calls from any thread are
|
|
/// marshalled to the captured dispatcher.
|
|
/// </summary>
|
|
public sealed class NotificationService : INotificationService
|
|
{
|
|
public const string AreaName = "ToastArea";
|
|
|
|
internal static readonly IReadOnlyDictionary<AppNotificationSeverity, (string BackgroundHex, double ExpirationSeconds)> Cards
|
|
= new Dictionary<AppNotificationSeverity, (string, double)>
|
|
{
|
|
[AppNotificationSeverity.Info] = (ThemeBrush.Hex("Brush.InfoContainer"), 4),
|
|
[AppNotificationSeverity.Success] = (ThemeBrush.Hex("Brush.SuccessContainer"), 4),
|
|
[AppNotificationSeverity.Warning] = (ThemeBrush.Hex("Brush.WarningContainer"), 8),
|
|
[AppNotificationSeverity.Error] = (ThemeBrush.Hex("Brush.ErrorContainer"), 0),
|
|
};
|
|
|
|
private readonly NotificationManager _manager = new();
|
|
private readonly Dispatcher _dispatcher;
|
|
|
|
public NotificationService()
|
|
: this(Application.Current?.Dispatcher)
|
|
{
|
|
}
|
|
|
|
internal NotificationService(Dispatcher? dispatcher)
|
|
{
|
|
_dispatcher = dispatcher ?? Dispatcher.CurrentDispatcher;
|
|
NotificationConstants.NotificationCornerRadius = new CornerRadius(8);
|
|
NotificationConstants.KeepNotificationVisibleOnMouseOver = true;
|
|
}
|
|
|
|
public void Show(string title, string message, AppNotificationSeverity severity)
|
|
{
|
|
var request = BuildRequest(title, message, severity);
|
|
_dispatcher.Invoke(() => _manager.Show(request));
|
|
}
|
|
|
|
internal static NotificationRequest BuildRequest(string title, string message, AppNotificationSeverity severity)
|
|
{
|
|
var (backgroundHex, expirationSeconds) = Cards[severity];
|
|
var builder = NotificationBuilder
|
|
.Create(title, message)
|
|
.InArea(AreaName)
|
|
.WithBackground(NotificationColor.FromHex(backgroundHex))
|
|
.WithForeground(NotificationColor.FromHex(ThemeBrush.Hex("Brush.OnSurface")));
|
|
|
|
if (severity == AppNotificationSeverity.Error)
|
|
builder.NeverExpires();
|
|
else
|
|
builder.ExpiresInSeconds(expirationSeconds);
|
|
|
|
return builder.Build();
|
|
}
|
|
}
|