-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathUserNotifier.cs
More file actions
144 lines (124 loc) · 5.9 KB
/
Copy pathUserNotifier.cs
File metadata and controls
144 lines (124 loc) · 5.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Coder.Desktop.App.Views;
using Microsoft.Extensions.Logging;
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
namespace Coder.Desktop.App.Services;
public interface INotificationHandler
{
public void HandleNotificationActivation(IDictionary<string, string> args);
}
// This interface is meant to protect the default
// notification handler from being overriden by DI.
public interface IDefaultNotificationHandler : INotificationHandler
{
}
public interface IUserNotifier : INotificationHandler, IAsyncDisposable
{
public void RegisterHandler(string name, INotificationHandler handler);
public void UnregisterHandler(string name);
public Task ShowErrorNotification(string title, string message, CancellationToken ct = default);
/// <summary>
/// This method allows to display a Windows-native notification with an action defined in
/// <paramref name="handlerName"/> and provided <paramref name="args"/>.
/// </summary>
/// <param name="title">Title of the notification.</param>
/// <param name="message">Message to be displayed in the notification body.</param>
/// <param name="handlerName">Handler should be e.g. <c>nameof(Handler)</c> where <c>Handler</c>
/// implements <see cref="Coder.Desktop.App.Services.INotificationHandler" />.
/// If handler is <c>null</c> the action will open Coder Desktop.</param>
/// <param name="args">Arguments to be provided to the handler when executing the action.</param>
public Task ShowActionNotification(string title, string message, string? handlerName, IDictionary<string, string>? args = null, CancellationToken ct = default);
}
public class UserNotifier : IUserNotifier
{
private const string CoderNotificationHandler = "CoderNotificationHandler";
private const string DefaultNotificationHandler = "DefaultNotificationHandler";
private readonly AppNotificationManager _notificationManager = AppNotificationManager.Default;
private readonly ILogger<UserNotifier> _logger;
private readonly IDispatcherQueueManager _dispatcherQueueManager;
private ConcurrentDictionary<string, INotificationHandler> Handlers { get; } = new();
public UserNotifier(ILogger<UserNotifier> logger, IDispatcherQueueManager dispatcherQueueManager,
IDefaultNotificationHandler notificationHandler)
{
_logger = logger;
_dispatcherQueueManager = dispatcherQueueManager;
var defaultHandlerAdded = Handlers.TryAdd(DefaultNotificationHandler, notificationHandler);
if (!defaultHandlerAdded)
throw new Exception($"UserNotifier failed to be initialized with {nameof(DefaultNotificationHandler)}");
}
public ValueTask DisposeAsync()
{
return ValueTask.CompletedTask;
}
public void RegisterHandler(string name, INotificationHandler handler)
{
if (handler is null)
throw new ArgumentNullException(nameof(handler));
if (handler is IUserNotifier)
throw new ArgumentException("Handler cannot be an IUserNotifier", nameof(handler));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name cannot be null or whitespace", nameof(name));
if (!Handlers.TryAdd(name, handler))
throw new InvalidOperationException($"A handler with the name '{name}' is already registered.");
}
public void UnregisterHandler(string name)
{
if (name == nameof(DefaultNotificationHandler))
throw new InvalidOperationException($"You cannot remove '{name}'.");
if (!Handlers.TryRemove(name, out _))
throw new InvalidOperationException($"No handler with the name '{name}' is registered.");
}
public Task ShowErrorNotification(string title, string message, CancellationToken ct = default)
{
var builder = new AppNotificationBuilder().AddText(title).AddText(message);
_notificationManager.Show(builder.BuildNotification());
return Task.CompletedTask;
}
public Task ShowActionNotification(string title, string message, string? handlerName, IDictionary<string, string>? args = null, CancellationToken ct = default)
{
if (handlerName == null)
handlerName = nameof(DefaultNotificationHandler); // Use default handler if no handler name is provided
if (!Handlers.TryGetValue(handlerName, out _))
throw new InvalidOperationException($"No action handler with the name '{handlerName}' is registered.");
var builder = new AppNotificationBuilder()
.AddText(title)
.AddText(message)
.AddArgument(CoderNotificationHandler, handlerName);
if (args != null)
foreach (var arg in args)
{
if (arg.Key == CoderNotificationHandler)
continue;
builder.AddArgument(arg.Key, arg.Value);
}
_notificationManager.Show(builder.BuildNotification());
return Task.CompletedTask;
}
public void HandleNotificationActivation(IDictionary<string, string> args)
{
if (!args.TryGetValue(CoderNotificationHandler, out var handlerName))
// Not an action notification, ignore
return;
if (!Handlers.TryGetValue(handlerName, out var handler))
{
_logger.LogWarning("no action handler '{HandlerName}' found for notification activation, ignoring", handlerName);
return;
}
_dispatcherQueueManager.RunInUiThread(() =>
{
try
{
handler.HandleNotificationActivation(args);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "could not handle activation for notification with handler '{HandlerName}", handlerName);
}
});
}
}