Skip to content

Commit 88a4a97

Browse files
authored
feat: integrate the tray app with RPC (#19)
- Modularizes the tray window to use multiple Pages for the three different states: signed out, disconnected from RPC, normal - Adds the two new aforementioned pages - Adds a CredentialManager service for storing credentials in Windows Credential Manager - Adds a RpcController service for managing the connection to the backend service and reporting state changes to the ViewModel - Switches to using separate ViewModels in the Views - Switches to using Dependency Injection for instantiating Views, ViewModels and Services - Integrates the tray window into the new RpcController on Start/Stop interactions. Workspace agent updates will be handled in a separate PR Relates to #5 (I will do agent updates immediately after this)
1 parent 4c6d2bd commit 88a4a97

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1677
-454
lines changed

.idea/.idea.Coder.Desktop/.idea/codeStyles/Project.xml

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/.idea.Coder.Desktop/.idea/codeStyles/codeStyleConfig.xml

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

App/App.csproj

+8-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<Nullable>enable</Nullable>
1313
<EnableMsixTooling>true</EnableMsixTooling>
1414
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
15+
<!-- To use CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute: -->
16+
<LangVersion>preview</LangVersion>
1517
</PropertyGroup>
1618

1719
<ItemGroup>
@@ -37,22 +39,11 @@
3739
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3840
</PackageReference>
3941
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.2.0" />
42+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
4043
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
4144
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250108002" />
4245
</ItemGroup>
4346

44-
<ItemGroup>
45-
<Page Update="TrayIcon.xaml">
46-
<Generator>MSBuild:Compile</Generator>
47-
</Page>
48-
</ItemGroup>
49-
50-
<ItemGroup>
51-
<Page Update="HorizontalRule.xaml">
52-
<Generator>MSBuild:Compile</Generator>
53-
</Page>
54-
</ItemGroup>
55-
5647
<!--
5748
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
5849
Tools extension to be activated for this project even if the Windows App SDK Nuget
@@ -61,6 +52,11 @@
6152
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
6253
<ProjectCapability Include="Msix" />
6354
</ItemGroup>
55+
<ItemGroup>
56+
<ProjectReference Include="..\CoderSdk\CoderSdk.csproj" />
57+
<ProjectReference Include="..\Vpn.Proto\Vpn.Proto.csproj" />
58+
<ProjectReference Include="..\Vpn\Vpn.csproj" />
59+
</ItemGroup>
6460

6561
<!--
6662
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution

App/App.xaml.cs

+34-7
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,55 @@
1+
using System;
2+
using System.Diagnostics;
3+
using Coder.Desktop.App.Services;
4+
using Coder.Desktop.App.ViewModels;
5+
using Coder.Desktop.App.Views;
6+
using Coder.Desktop.App.Views.Pages;
7+
using Microsoft.Extensions.DependencyInjection;
18
using Microsoft.UI.Xaml;
29

310
namespace Coder.Desktop.App;
411

512
public partial class App : Application
613
{
7-
private TrayWindow? TrayWindow;
14+
private readonly IServiceProvider _services;
15+
private TrayWindow? _trayWindow;
16+
private readonly bool _handleClosedEvents = true;
817

918
public App()
1019
{
20+
var services = new ServiceCollection();
21+
services.AddSingleton<ICredentialManager, CredentialManager>();
22+
services.AddSingleton<IRpcController, RpcController>();
23+
24+
services.AddTransient<TrayWindowDisconnectedViewModel>();
25+
services.AddTransient<TrayWindowDisconnectedPage>();
26+
services.AddTransient<TrayWindowLoginRequiredViewModel>();
27+
services.AddTransient<TrayWindowLoginRequiredPage>();
28+
services.AddTransient<TrayWindowLoginRequiredViewModel>();
29+
services.AddTransient<TrayWindowLoginRequiredPage>();
30+
services.AddTransient<TrayWindowViewModel>();
31+
services.AddTransient<TrayWindowMainPage>();
32+
services.AddTransient<TrayWindow>();
33+
34+
_services = services.BuildServiceProvider();
35+
36+
#if DEBUG
37+
UnhandledException += (_, e) => { Debug.WriteLine(e.Exception.ToString()); };
38+
#endif
39+
1140
InitializeComponent();
1241
}
1342

14-
private bool HandleClosedEvents { get; } = true;
15-
1643
protected override void OnLaunched(LaunchActivatedEventArgs args)
1744
{
18-
TrayWindow = new TrayWindow();
19-
TrayWindow.Closed += (sender, args) =>
45+
_trayWindow = _services.GetRequiredService<TrayWindow>();
46+
_trayWindow.Closed += (sender, args) =>
2047
{
2148
// TODO: wire up HandleClosedEvents properly
22-
if (HandleClosedEvents)
49+
if (_handleClosedEvents)
2350
{
2451
args.Handled = true;
25-
TrayWindow.AppWindow.Hide();
52+
_trayWindow.AppWindow.Hide();
2653
}
2754
};
2855
}

App/HorizontalRule.xaml App/Controls/HorizontalRule.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22

33
<UserControl
4-
x:Class="Coder.Desktop.App.HorizontalRule"
4+
x:Class="Coder.Desktop.App.Controls.HorizontalRule"
55
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
66
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
77
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

App/HorizontalRule.xaml.cs App/Controls/HorizontalRule.xaml.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Microsoft.UI.Xaml.Controls;
22

3-
namespace Coder.Desktop.App;
3+
namespace Coder.Desktop.App.Controls;
44

55
public sealed partial class HorizontalRule : UserControl
66
{

App/TrayIcon.xaml App/Controls/TrayIcon.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22

33
<UserControl
4-
x:Class="Coder.Desktop.App.TrayIcon"
4+
x:Class="Coder.Desktop.App.Controls.TrayIcon"
55
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
66
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
77
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

App/TrayIcon.xaml.cs App/Controls/TrayIcon.xaml.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Microsoft.UI.Xaml.Controls;
77
using Microsoft.UI.Xaml.Media.Imaging;
88

9-
namespace Coder.Desktop.App;
9+
namespace Coder.Desktop.App.Controls;
1010

1111
[DependencyProperty<ICommand>("OpenCommand")]
1212
[DependencyProperty<ICommand>("ExitCommand")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using Windows.UI;
3+
using Coder.Desktop.App.ViewModels;
4+
using Microsoft.UI.Xaml.Data;
5+
using Microsoft.UI.Xaml.Media;
6+
7+
namespace Coder.Desktop.App.Converters;
8+
9+
public class AgentStatusToColorConverter : IValueConverter
10+
{
11+
private static readonly SolidColorBrush Green = new(Color.FromArgb(255, 52, 199, 89));
12+
private static readonly SolidColorBrush Red = new(Color.FromArgb(255, 255, 59, 48));
13+
private static readonly SolidColorBrush Gray = new(Color.FromArgb(255, 142, 142, 147));
14+
15+
public object Convert(object value, Type targetType, object parameter, string language)
16+
{
17+
if (value is not AgentConnectionStatus status) return Gray;
18+
19+
return status switch
20+
{
21+
AgentConnectionStatus.Green => Green,
22+
AgentConnectionStatus.Red => Red,
23+
_ => Gray,
24+
};
25+
}
26+
27+
public object ConvertBack(object value, Type targetType, object parameter, string language)
28+
{
29+
throw new NotImplementedException();
30+
}
31+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using DependencyPropertyGenerator;
3+
using Microsoft.UI.Xaml;
4+
using Microsoft.UI.Xaml.Data;
5+
6+
namespace Coder.Desktop.App.Converters;
7+
8+
[DependencyProperty<object>("TrueValue", DefaultValue = true)]
9+
[DependencyProperty<object>("FalseValue", DefaultValue = true)]
10+
public partial class BoolToObjectConverter : DependencyObject, IValueConverter
11+
{
12+
public object Convert(object value, Type targetType, object parameter, string language)
13+
{
14+
return value is true ? TrueValue : FalseValue;
15+
}
16+
17+
public object ConvertBack(object value, Type targetType, object parameter, string language)
18+
{
19+
throw new NotImplementedException();
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.UI.Xaml;
2+
3+
namespace Coder.Desktop.App.Converters;
4+
5+
public partial class BoolToVisibilityConverter : BoolToObjectConverter
6+
{
7+
public BoolToVisibilityConverter()
8+
{
9+
TrueValue = Visibility.Visible;
10+
FalseValue = Visibility.Collapsed;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.UI.Xaml;
2+
3+
namespace Coder.Desktop.App.Converters;
4+
5+
public partial class InverseBoolToVisibilityConverter : BoolToObjectConverter
6+
{
7+
public InverseBoolToVisibilityConverter()
8+
{
9+
TrueValue = Visibility.Collapsed;
10+
FalseValue = Visibility.Visible;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using Coder.Desktop.App.Models;
3+
using DependencyPropertyGenerator;
4+
using Microsoft.UI.Xaml;
5+
using Microsoft.UI.Xaml.Data;
6+
7+
namespace Coder.Desktop.App.Converters;
8+
9+
[DependencyProperty<bool>("Starting", DefaultValue = false)]
10+
[DependencyProperty<bool>("Started", DefaultValue = false)]
11+
[DependencyProperty<bool>("Stopping", DefaultValue = false)]
12+
[DependencyProperty<bool>("Stopped", DefaultValue = false)]
13+
public partial class VpnLifecycleToBoolConverter : DependencyObject, IValueConverter
14+
{
15+
public object Convert(object value, Type targetType, object parameter, string language)
16+
{
17+
if (value is not VpnLifecycle lifecycle) return Stopped;
18+
19+
return lifecycle switch
20+
{
21+
VpnLifecycle.Starting => Starting,
22+
VpnLifecycle.Started => Started,
23+
VpnLifecycle.Stopping => Stopping,
24+
VpnLifecycle.Stopped => Stopped,
25+
_ => Visibility.Collapsed,
26+
};
27+
}
28+
29+
public object ConvertBack(object value, Type targetType, object parameter, string language)
30+
{
31+
throw new NotImplementedException();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using Microsoft.UI.Xaml;
3+
using Microsoft.UI.Xaml.Data;
4+
5+
namespace Coder.Desktop.App.Converters;
6+
7+
public partial class VpnLifecycleToVisibilityConverter : VpnLifecycleToBoolConverter, IValueConverter
8+
{
9+
public new object Convert(object value, Type targetType, object parameter, string language)
10+
{
11+
var boolValue = base.Convert(value, targetType, parameter, language);
12+
return boolValue is true ? Visibility.Visible : Visibility.Collapsed;
13+
}
14+
}

App/Models/CredentialModel.cs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Coder.Desktop.App.Models;
2+
3+
public enum CredentialState
4+
{
5+
Invalid,
6+
Valid,
7+
}
8+
9+
public class CredentialModel
10+
{
11+
public CredentialState State { get; set; } = CredentialState.Invalid;
12+
13+
public string? CoderUrl { get; set; }
14+
public string? ApiToken { get; set; }
15+
16+
public CredentialModel Clone()
17+
{
18+
return new CredentialModel
19+
{
20+
State = State,
21+
CoderUrl = CoderUrl,
22+
ApiToken = ApiToken,
23+
};
24+
}
25+
}

App/Models/RpcModel.cs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Collections.Generic;
2+
3+
namespace Coder.Desktop.App.Models;
4+
5+
public enum RpcLifecycle
6+
{
7+
Disconnected,
8+
Connecting,
9+
Connected,
10+
}
11+
12+
public enum VpnLifecycle
13+
{
14+
Stopped,
15+
Starting,
16+
Started,
17+
Stopping,
18+
}
19+
20+
public class RpcModel
21+
{
22+
public RpcLifecycle RpcLifecycle { get; set; } = RpcLifecycle.Disconnected;
23+
24+
public VpnLifecycle VpnLifecycle { get; set; } = VpnLifecycle.Stopped;
25+
26+
public List<object> Agents { get; set; } = [];
27+
28+
public RpcModel Clone()
29+
{
30+
return new RpcModel
31+
{
32+
RpcLifecycle = RpcLifecycle,
33+
VpnLifecycle = VpnLifecycle,
34+
Agents = Agents,
35+
};
36+
}
37+
}

App/Package.appxmanifest

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
<Identity
1111
Name="925b49fc-4648-4967-b4e6-b5473061ee62"
1212
Publisher="CN=Coder Technologies Inc."
13-
Version="1.0.0.0"/>
13+
Version="1.0.0.0" />
1414

1515
<mp:PhoneIdentity PhoneProductId="925b49fc-4648-4967-b4e6-b5473061ee62"
16-
PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
16+
PhonePublisherId="00000000-0000-0000-0000-000000000000" />
1717

1818
<Properties>
1919
<DisplayName>Coder Desktop (Package)</DisplayName>
@@ -22,12 +22,12 @@
2222
</Properties>
2323

2424
<Dependencies>
25-
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
26-
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
25+
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
26+
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
2727
</Dependencies>
2828

2929
<Resources>
30-
<Resource Language="x-generate"/>
30+
<Resource Language="x-generate" />
3131
</Resources>
3232

3333
<Applications>
@@ -40,13 +40,13 @@
4040
BackgroundColor="transparent"
4141
Square150x150Logo="Images\Square150x150Logo.png"
4242
Square44x44Logo="Images\Square44x44Logo.png">
43-
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png"/>
44-
<uap:SplashScreen Image="Images\SplashScreen.png"/>
43+
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
44+
<uap:SplashScreen Image="Images\SplashScreen.png" />
4545
</uap:VisualElements>
4646
</Application>
4747
</Applications>
4848

4949
<Capabilities>
50-
<rescap:Capability Name="runFullTrust"/>
50+
<rescap:Capability Name="runFullTrust" />
5151
</Capabilities>
5252
</Package>

0 commit comments

Comments
 (0)