Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7e3782f

Browse files
authored
chore: add connect/disconnect notifications on Coder Connect (#140)
1 parentb8d7993 commit7e3782f

File tree

3 files changed

+103
-9
lines changed

3 files changed

+103
-9
lines changed

‎App/App.xaml.cs‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
namespaceCoder.Desktop.App;
2929

30-
publicpartialclassApp:Application,IDispatcherQueueManager
30+
publicpartialclassApp:Application,IDispatcherQueueManager,INotificationHandler
3131
{
3232
privateconststringMutagenControllerConfigSection="MutagenController";
3333
privateconststringUpdaterConfigSection="Updater";
@@ -91,6 +91,7 @@ public App()
9191
services.AddSingleton<IAgentApiClientFactory,AgentApiClientFactory>();
9292

9393
services.AddSingleton<IDispatcherQueueManager>(_=>this);
94+
services.AddSingleton<INotificationHandler>(_=>this);
9495
services.AddSingleton<ICredentialBackend>(_=>
9596
newWindowsCredentialBackend(WindowsCredentialBackend.CoderCredentialsTargetName));
9697
services.AddSingleton<ICredentialManager,CredentialManager>();
@@ -335,4 +336,13 @@ public void RunInUiThread(DispatcherQueueHandler action)
335336
}
336337
dispatcherQueue.TryEnqueue(action);
337338
}
339+
340+
publicvoidHandleNotificationActivation(IDictionary<string,string>args)
341+
{
342+
varapp=(App)Current;
343+
if(app!=null&&app.TrayWindow!=null)
344+
{
345+
app.TrayWindow.Tray_Open();
346+
}
347+
}
338348
}

‎App/Services/UserNotifier.cs‎

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
usingSystem.Collections.Generic;
44
usingSystem.Threading;
55
usingSystem.Threading.Tasks;
6+
usingCoder.Desktop.App.Views;
67
usingMicrosoft.Extensions.Logging;
78
usingMicrosoft.Windows.AppNotifications;
89
usingMicrosoft.Windows.AppNotifications.Builder;
@@ -20,17 +21,40 @@ public interface IUserNotifier : INotificationHandler, IAsyncDisposable
2021
publicvoidUnregisterHandler(stringname);
2122

2223
publicTaskShowErrorNotification(stringtitle,stringmessage,CancellationTokenct=default);
23-
publicTaskShowActionNotification(stringtitle,stringmessage,stringhandlerName,IDictionary<string,string>?args=null,CancellationTokenct=default);
24+
/// <summary>
25+
/// This method allows to display a Windows-native notification with an action defined in
26+
/// <paramref name="handlerName"/> and provided <paramref name="args"/>.
27+
/// </summary>
28+
/// <param name="title">Title of the notification.</param>
29+
/// <param name="message">Message to be displayed in the notification body.</param>
30+
/// <param name="handlerName">Handler should be e.g. <c>nameof(Handler)</c> where <c>Handler</c>
31+
/// implements <see cref="Coder.Desktop.App.Services.INotificationHandler" />.
32+
/// If handler is <c>null</c> the action will open Coder Desktop.</param>
33+
/// <param name="args">Arguments to be provided to the handler when executing the action.</param>
34+
publicTaskShowActionNotification(stringtitle,stringmessage,string?handlerName,IDictionary<string,string>?args=null,CancellationTokenct=default);
2435
}
2536

26-
publicclassUserNotifier(ILogger<UserNotifier>logger,IDispatcherQueueManagerdispatcherQueueManager):IUserNotifier
37+
publicclassUserNotifier:IUserNotifier
2738
{
2839
privateconststringCoderNotificationHandler="CoderNotificationHandler";
40+
privateconststringDefaultNotificationHandler="DefaultNotificationHandler";
2941

3042
privatereadonlyAppNotificationManager_notificationManager=AppNotificationManager.Default;
43+
privatereadonlyILogger<UserNotifier>_logger;
44+
privatereadonlyIDispatcherQueueManager_dispatcherQueueManager;
3145

3246
privateConcurrentDictionary<string,INotificationHandler>Handlers{get;}=new();
3347

48+
publicUserNotifier(ILogger<UserNotifier>logger,IDispatcherQueueManagerdispatcherQueueManager,
49+
INotificationHandlernotificationHandler)
50+
{
51+
_logger=logger;
52+
_dispatcherQueueManager=dispatcherQueueManager;
53+
vardefaultHandlerAdded=Handlers.TryAdd(DefaultNotificationHandler,notificationHandler);
54+
if(!defaultHandlerAdded)
55+
thrownewException($"UserNotifier failed to be initialized with{nameof(DefaultNotificationHandler)}");
56+
}
57+
3458
publicValueTaskDisposeAsync()
3559
{
3660
returnValueTask.CompletedTask;
@@ -50,6 +74,8 @@ public void RegisterHandler(string name, INotificationHandler handler)
5074

5175
publicvoidUnregisterHandler(stringname)
5276
{
77+
if(name==nameof(DefaultNotificationHandler))
78+
thrownewInvalidOperationException($"You cannot remove '{name}'.");
5379
if(!Handlers.TryRemove(name,out_))
5480
thrownewInvalidOperationException($"No handler with the name '{name}' is registered.");
5581
}
@@ -61,8 +87,11 @@ public Task ShowErrorNotification(string title, string message, CancellationToke
6187
returnTask.CompletedTask;
6288
}
6389

64-
publicTaskShowActionNotification(stringtitle,stringmessage,stringhandlerName,IDictionary<string,string>?args=null,CancellationTokenct=default)
90+
publicTaskShowActionNotification(stringtitle,stringmessage,string?handlerName,IDictionary<string,string>?args=null,CancellationTokenct=default)
6591
{
92+
if(handlerName==null)
93+
handlerName=nameof(DefaultNotificationHandler);// Use default handler if no handler name is provided
94+
6695
if(!Handlers.TryGetValue(handlerName,out_))
6796
thrownewInvalidOperationException($"No action handler with the name '{handlerName}' is registered.");
6897

@@ -90,19 +119,19 @@ public void HandleNotificationActivation(IDictionary<string, string> args)
90119

91120
if(!Handlers.TryGetValue(handlerName,outvarhandler))
92121
{
93-
logger.LogWarning("no action handler '{HandlerName}' found for notification activation, ignoring",handlerName);
122+
_logger.LogWarning("no action handler '{HandlerName}' found for notification activation, ignoring",handlerName);
94123
return;
95124
}
96125

97-
dispatcherQueueManager.RunInUiThread(()=>
126+
_dispatcherQueueManager.RunInUiThread(()=>
98127
{
99128
try
100129
{
101130
handler.HandleNotificationActivation(args);
102131
}
103132
catch(Exceptionex)
104133
{
105-
logger.LogWarning(ex,"could not handle activation for notification with handler '{HandlerName}",handlerName);
134+
_logger.LogWarning(ex,"could not handle activation for notification with handler '{HandlerName}",handlerName);
106135
}
107136
});
108137
}

‎App/Views/TrayWindow.xaml.cs‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
usingMicrosoft.UI.Windowing;
1010
usingMicrosoft.UI.Xaml;
1111
usingMicrosoft.UI.Xaml.Controls;
12+
usingMicrosoft.UI.Xaml.Documents;
1213
usingMicrosoft.UI.Xaml.Media.Animation;
1314
usingSystem;
15+
usingSystem.Collections.Generic;
1416
usingSystem.Runtime.InteropServices;
17+
usingSystem.Threading;
1518
usingSystem.Threading.Tasks;
1619
usingWindows.Graphics;
1720
usingWindows.System;
@@ -33,17 +36,23 @@ public sealed partial class TrayWindow : Window
3336
privateint_lastWindowHeight;
3437
privateStoryboard?_currentSb;
3538

39+
privateVpnLifecyclecurVpnLifecycle=VpnLifecycle.Stopped;
40+
privateRpcLifecyclecurRpcLifecycle=RpcLifecycle.Disconnected;
41+
3642
privatereadonlyIRpcController_rpcController;
3743
privatereadonlyICredentialManager_credentialManager;
3844
privatereadonlyISyncSessionController_syncSessionController;
3945
privatereadonlyIUpdateController_updateController;
46+
privatereadonlyIUserNotifier_userNotifier;
4047
privatereadonlyTrayWindowLoadingPage_loadingPage;
4148
privatereadonlyTrayWindowDisconnectedPage_disconnectedPage;
4249
privatereadonlyTrayWindowLoginRequiredPage_loginRequiredPage;
4350
privatereadonlyTrayWindowMainPage_mainPage;
4451

45-
publicTrayWindow(IRpcControllerrpcController,ICredentialManagercredentialManager,
52+
publicTrayWindow(
53+
IRpcControllerrpcController,ICredentialManagercredentialManager,
4654
ISyncSessionControllersyncSessionController,IUpdateControllerupdateController,
55+
IUserNotifieruserNotifier,
4756
TrayWindowLoadingPageloadingPage,
4857
TrayWindowDisconnectedPagedisconnectedPage,TrayWindowLoginRequiredPageloginRequiredPage,
4958
TrayWindowMainPagemainPage)
@@ -52,6 +61,7 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
5261
_credentialManager=credentialManager;
5362
_syncSessionController=syncSessionController;
5463
_updateController=updateController;
64+
_userNotifier=userNotifier;
5565
_loadingPage=loadingPage;
5666
_disconnectedPage=disconnectedPage;
5767
_loginRequiredPage=loginRequiredPage;
@@ -142,9 +152,54 @@ private void SetPageByState(RpcModel rpcModel, CredentialModel credentialModel,
142152
}
143153
}
144154

155+
privatevoidMaybeNotifyUser(RpcModelrpcModel)
156+
{
157+
// This method is called when the state changes, but we don't want to notify
158+
// the user if the state hasn't changed.
159+
varisRpcLifecycleChanged=rpcModel.RpcLifecycle==RpcLifecycle.Disconnected&&curRpcLifecycle!=rpcModel.RpcLifecycle;
160+
varisVpnLifecycleChanged=(rpcModel.VpnLifecycle==VpnLifecycle.Started||rpcModel.VpnLifecycle==VpnLifecycle.Stopped)&&curVpnLifecycle!=rpcModel.VpnLifecycle;
161+
162+
if(!isRpcLifecycleChanged&&!isVpnLifecycleChanged)
163+
{
164+
return;
165+
}
166+
167+
varoldRpcLifeycle=curRpcLifecycle;
168+
varoldVpnLifecycle=curVpnLifecycle;
169+
curRpcLifecycle=rpcModel.RpcLifecycle;
170+
curVpnLifecycle=rpcModel.VpnLifecycle;
171+
172+
varmessages=newList<string>();
173+
174+
if(oldRpcLifeycle!=RpcLifecycle.Disconnected&&curRpcLifecycle==RpcLifecycle.Disconnected)
175+
{
176+
messages.Add("Disconnected from Coder background service.");
177+
}
178+
179+
if(oldVpnLifecycle!=curVpnLifecycle)
180+
{
181+
switch(curVpnLifecycle)
182+
{
183+
caseVpnLifecycle.Started:
184+
messages.Add("Coder Connect started.");
185+
break;
186+
caseVpnLifecycle.Stopped:
187+
messages.Add("Coder Connect stopped.");
188+
break;
189+
}
190+
}
191+
192+
if(messages.Count==0)return;
193+
if(_aw.IsVisible)return;
194+
195+
varmessage=string.Join(" ",messages);
196+
_userNotifier.ShowActionNotification(message,string.Empty,null,null,CancellationToken.None);
197+
}
198+
145199
privatevoidRpcController_StateChanged(object?_,RpcModelmodel)
146200
{
147201
SetPageByState(model,_credentialManager.GetCachedCredentials(),_syncSessionController.GetState());
202+
MaybeNotifyUser(model);
148203
}
149204

150205
privatevoidCredentialManager_CredentialsChanged(object?_,CredentialModelmodel)
@@ -297,7 +352,7 @@ private void Window_Activated(object sender, WindowActivatedEventArgs e)
297352
}
298353

299354
[RelayCommand]
300-
privatevoidTray_Open()
355+
publicvoidTray_Open()
301356
{
302357
MoveResizeAndActivate();
303358
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp