Skip to content

Commit 7ff7914

Browse files
committed
chore: added notifications for vpn lifecycle start/stop
1 parent 29943c8 commit 7ff7914

File tree

1 file changed

+66
-3
lines changed

1 file changed

+66
-3
lines changed

App/Views/TrayWindow.xaml.cs

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
using Microsoft.UI.Xaml.Controls;
1212
using Microsoft.UI.Xaml.Media.Animation;
1313
using System;
14+
using System.Collections.Generic;
1415
using System.Runtime.InteropServices;
16+
using System.Threading;
1517
using System.Threading.Tasks;
1618
using Windows.Graphics;
1719
using Windows.System;
@@ -21,7 +23,7 @@
2123

2224
namespace Coder.Desktop.App.Views;
2325

24-
public sealed partial class TrayWindow : Window
26+
public sealed partial class TrayWindow : Window, INotificationHandler
2527
{
2628
private const int WIDTH = 300;
2729

@@ -33,17 +35,25 @@ public sealed partial class TrayWindow : Window
3335
private int _lastWindowHeight;
3436
private Storyboard? _currentSb;
3537

38+
private VpnLifecycle prevVpnLifecycle = VpnLifecycle.Stopped;
39+
private RpcLifecycle prevRpcLifecycle = RpcLifecycle.Disconnected;
40+
41+
private NativeApi.POINT? _lastActivatePosition;
42+
3643
private readonly IRpcController _rpcController;
3744
private readonly ICredentialManager _credentialManager;
3845
private readonly ISyncSessionController _syncSessionController;
3946
private readonly IUpdateController _updateController;
47+
private readonly IUserNotifier _userNotifier;
4048
private readonly TrayWindowLoadingPage _loadingPage;
4149
private readonly TrayWindowDisconnectedPage _disconnectedPage;
4250
private readonly TrayWindowLoginRequiredPage _loginRequiredPage;
4351
private readonly TrayWindowMainPage _mainPage;
4452

45-
public TrayWindow(IRpcController rpcController, ICredentialManager credentialManager,
53+
public TrayWindow(
54+
IRpcController rpcController, ICredentialManager credentialManager,
4655
ISyncSessionController syncSessionController, IUpdateController updateController,
56+
IUserNotifier userNotifier,
4757
TrayWindowLoadingPage loadingPage,
4858
TrayWindowDisconnectedPage disconnectedPage, TrayWindowLoginRequiredPage loginRequiredPage,
4959
TrayWindowMainPage mainPage)
@@ -52,12 +62,14 @@ public TrayWindow(IRpcController rpcController, ICredentialManager credentialMan
5262
_credentialManager = credentialManager;
5363
_syncSessionController = syncSessionController;
5464
_updateController = updateController;
65+
_userNotifier = userNotifier;
5566
_loadingPage = loadingPage;
5667
_disconnectedPage = disconnectedPage;
5768
_loginRequiredPage = loginRequiredPage;
5869
_mainPage = mainPage;
5970

6071
InitializeComponent();
72+
_userNotifier.RegisterHandler("TrayWindow", this);
6173
AppWindow.Hide();
6274
Activated += Window_Activated;
6375
RootFrame.SizeChanged += RootFrame_SizeChanged;
@@ -142,9 +154,55 @@ private void SetPageByState(RpcModel rpcModel, CredentialModel credentialModel,
142154
}
143155
}
144156

157+
private void NotifyUser(RpcModel rpcModel)
158+
{
159+
// This method is called when the state changes, but we don't want to notify
160+
// the user if the state hasn't changed.
161+
var isRpcLifecycleChanged = rpcModel.RpcLifecycle != RpcLifecycle.Connecting && prevRpcLifecycle != rpcModel.RpcLifecycle;
162+
var isVpnLifecycleChanged = (rpcModel.VpnLifecycle == VpnLifecycle.Started || rpcModel.VpnLifecycle == VpnLifecycle.Stopped) && prevVpnLifecycle != rpcModel.VpnLifecycle;
163+
164+
if (!isRpcLifecycleChanged && !isVpnLifecycleChanged)
165+
{
166+
return;
167+
}
168+
var message = string.Empty;
169+
// Compose the message based on the lifecycle changes
170+
if (isRpcLifecycleChanged)
171+
message += rpcModel.RpcLifecycle switch
172+
{
173+
RpcLifecycle.Connected => "Connected to Coder vpn service.",
174+
RpcLifecycle.Disconnected => "Disconnected from Coder vpn service.",
175+
_ => "" // This will never be hit.
176+
};
177+
178+
if(message.Length > 0 && isVpnLifecycleChanged)
179+
message += " ";
180+
181+
if (isVpnLifecycleChanged)
182+
message += rpcModel.VpnLifecycle switch
183+
{
184+
VpnLifecycle.Started => "Coder Connect started.",
185+
VpnLifecycle.Stopped => "Coder Connect stopped.",
186+
_ => "" // This will never be hit.
187+
};
188+
189+
// Save state for the next notification check
190+
prevRpcLifecycle = rpcModel.RpcLifecycle;
191+
prevVpnLifecycle = rpcModel.VpnLifecycle;
192+
193+
if (_aw.IsVisible)
194+
{
195+
return; // No need to notify if the window is not visible.
196+
}
197+
198+
// Trigger notification
199+
_userNotifier.ShowActionNotification(message, string.Empty, nameof(TrayWindow), null, CancellationToken.None);
200+
}
201+
145202
private void RpcController_StateChanged(object? _, RpcModel model)
146203
{
147204
SetPageByState(model, _credentialManager.GetCachedCredentials(), _syncSessionController.GetState());
205+
NotifyUser(model);
148206
}
149207

150208
private void CredentialManager_CredentialsChanged(object? _, CredentialModel model)
@@ -316,6 +374,11 @@ private void Tray_Exit()
316374
_ = ((App)Application.Current).ExitApplication();
317375
}
318376

377+
public void HandleNotificationActivation(IDictionary<string, string> args)
378+
{
379+
Tray_Open();
380+
}
381+
319382
public static class NativeApi
320383
{
321384
[DllImport("dwmapi.dll")]
@@ -336,7 +399,7 @@ internal enum TaskbarPosition { Left, Top, Right, Bottom }
336399
internal readonly record struct TaskbarInfo(TaskbarPosition Position, int Gap, bool AutoHide);
337400

338401
// -----------------------------------------------------------------------------
339-
// Taskbar helpers ABM_GETTASKBARPOS / ABM_GETSTATE via SHAppBarMessage
402+
// Taskbar helpers ABM_GETTASKBARPOS / ABM_GETSTATE via SHAppBarMessage
340403
// -----------------------------------------------------------------------------
341404
private static TaskbarInfo GetTaskbarInfo(DisplayArea area)
342405
{

0 commit comments

Comments
 (0)