Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add workspace app icons to tray window #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
5 changes: 3 additions & 2 deletions App/App.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
Expand All @@ -16,7 +16,7 @@
<!-- To use CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute: -->
<LangVersion>preview</LangVersion>
<!-- We have our own implementation of main with exception handling -->
<DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN;DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>

<AssemblyName>Coder Desktop</AssemblyName>
<ApplicationIcon>coder.ico</ApplicationIcon>
Expand Down Expand Up @@ -57,6 +57,7 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
<PackageReference Include="DependencyPropertyGenerator" Version="1.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
8 changes: 7 additions & 1 deletion App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Coder.Desktop.App.Views;
using Coder.Desktop.App.Views.Pages;
using Coder.Desktop.CoderSdk.Agent;
using Coder.Desktop.CoderSdk.Coder;
using Coder.Desktop.Vpn;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -19,9 +20,9 @@
using Microsoft.UI.Xaml;
using Microsoft.Win32;
using Microsoft.Windows.AppLifecycle;
using Microsoft.Windows.AppNotifications;
using Serilog;
using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs;
using Microsoft.Windows.AppNotifications;

namespace Coder.Desktop.App;

Expand Down Expand Up @@ -64,8 +65,11 @@ public App()
loggerConfig.ReadFrom.Configuration(builder.Configuration);
});

services.AddSingleton<ICoderApiClientFactory, CoderApiClientFactory>();
services.AddSingleton<IAgentApiClientFactory, AgentApiClientFactory>();

services.AddSingleton<ICredentialBackend>(_ =>
new WindowsCredentialBackend(WindowsCredentialBackend.CoderCredentialsTargetName));
services.AddSingleton<ICredentialManager, CredentialManager>();
services.AddSingleton<IRpcController, RpcController>();

Expand Down Expand Up @@ -95,6 +99,8 @@ public App()
services.AddTransient<TrayWindowLoginRequiredPage>();
services.AddTransient<TrayWindowLoginRequiredViewModel>();
services.AddTransient<TrayWindowLoginRequiredPage>();
services.AddSingleton<IAgentAppViewModelFactory, AgentAppViewModelFactory>();
services.AddSingleton<IAgentViewModelFactory, AgentViewModelFactory>();
services.AddTransient<TrayWindowViewModel>();
services.AddTransient<TrayWindowMainPage>();
services.AddTransient<TrayWindow>();
Expand Down
31 changes: 31 additions & 0 deletions App/Controls/ExpandChevron.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>

<UserControl
x:Class="Coder.Desktop.App.Controls.ExpandChevron"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:animatedVisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
mc:Ignorable="d">

<Grid>
<AnimatedIcon
Grid.Column="0"
x:Name="ChevronIcon"
Width="16"
Height="16"
Margin="0,0,8,0"
RenderTransformOrigin="0.5, 0.5"
Foreground="{x:Bind Foreground, Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AnimatedIcon.State="NormalOff">

<animatedVisuals:AnimatedChevronRightDownSmallVisualSource />
<AnimatedIcon.FallbackIconSource>
<FontIconSource Glyph="&#xE76C;" />
</AnimatedIcon.FallbackIconSource>
</AnimatedIcon>
</Grid>
</UserControl>
19 changes: 19 additions & 0 deletions App/Controls/ExpandChevron.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using DependencyPropertyGenerator;
using Microsoft.UI.Xaml.Controls;

namespace Coder.Desktop.App.Controls;

[DependencyProperty<bool>("IsOpen", DefaultValue = false)]
public sealed partial class ExpandChevron : UserControl
{
public ExpandChevron()
{
InitializeComponent();
}

partial void OnIsOpenChanged(bool oldValue, bool newValue)
{
var newState = newValue ? "NormalOn" : "NormalOff";
AnimatedIcon.SetState(ChevronIcon, newState);
}
}
51 changes: 51 additions & 0 deletions App/Controls/ExpandContent.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>

<UserControl
x:Class="Coder.Desktop.App.Controls.ExpandContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="using:CommunityToolkit.WinUI"
mc:Ignorable="d">

<Grid x:Name="CollapsiblePanel" Opacity="0" Visibility="Collapsed" toolkit:UIElementExtensions.ClipToBounds="True">
<Grid.RenderTransform>
<TranslateTransform x:Name="SlideTransform" Y="-10" />
</Grid.RenderTransform>

<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="ExpandedState">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="CollapsiblePanel"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:0.2" />
<DoubleAnimation
Storyboard.TargetName="SlideTransform"
Storyboard.TargetProperty="Y"
To="0"
Duration="0:0:0.2" />
</Storyboard>
</VisualState>

<VisualState x:Name="CollapsedState">
<Storyboard Completed="{x:Bind CollapseAnimation_Completed}">
<DoubleAnimation
Storyboard.TargetName="CollapsiblePanel"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0:0:0.2" />
<DoubleAnimation
Storyboard.TargetName="SlideTransform"
Storyboard.TargetProperty="Y"
To="-10"
Duration="0:0:0.2" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>
39 changes: 39 additions & 0 deletions App/Controls/ExpandContent.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using DependencyPropertyGenerator;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;

namespace Coder.Desktop.App.Controls;

[ContentProperty(Name = nameof(Children))]
[DependencyProperty<bool>("IsOpen", DefaultValue = false)]
public sealed partial class ExpandContent : UserControl
{
public UIElementCollection Children => CollapsiblePanel.Children;

public ExpandContent()
{
InitializeComponent();
}

public void CollapseAnimation_Completed(object? sender, object args)
{
// Hide the panel completely when the collapse animation is done. This
// cannot be done with keyframes for some reason.
//
// Without this, the space will still be reserved for the panel.
CollapsiblePanel.Visibility = Visibility.Collapsed;
}

partial void OnIsOpenChanged(bool oldValue, bool newValue)
{
var newState = newValue ? "ExpandedState" : "CollapsedState";

// The animation can't set visibility when starting or ending the
// animation.
if (newValue)
CollapsiblePanel.Visibility = Visibility.Visible;

VisualStateManager.GoToState(this, newState, true);
}
}
13 changes: 13 additions & 0 deletions App/Converters/DependencyObjectSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ private void UpdateSelectedObject()
ClearValue(SelectedObjectProperty);
}

private static void VerifyReferencesProperty(IObservableVector<DependencyObject> references)
{
// Ensure unique keys and that the values are DependencyObjectSelectorItem<K, V>.
var items = references.OfType<DependencyObjectSelectorItem<TK, TV>>().ToArray();
var keys = items.Select(i => i.Key).Distinct().ToArray();
if (keys.Length != references.Count)
throw new ArgumentException("ObservableCollection Keys must be unique.");
}

// Called when the References property is replaced.
private static void ReferencesPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
Expand All @@ -166,12 +175,16 @@ private static void ReferencesPropertyChanged(DependencyObject obj, DependencyPr
oldValue.VectorChanged -= self.OnVectorChangedReferences;
var newValue = args.NewValue as DependencyObjectCollection;
if (newValue != null)
{
VerifyReferencesProperty(newValue);
newValue.VectorChanged += self.OnVectorChangedReferences;
}
}

// Called when the References collection changes without being replaced.
private void OnVectorChangedReferences(IObservableVector<DependencyObject> sender, IVectorChangedEventArgs args)
{
VerifyReferencesProperty(sender);
UpdateSelectedObject();
}

Expand Down
8 changes: 5 additions & 3 deletions App/Models/CredentialModel.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
using System;

namespace Coder.Desktop.App.Models;

public enum CredentialState
{
// Unknown means "we haven't checked yet"
Unknown,

// Invalid means "we checked and there's either no saved credentials or they are not valid"
// Invalid means "we checked and there's either no saved credentials, or they are not valid"
Invalid,

// Valid means "we checked and there are saved credentials and they are valid"
// Valid means "we checked and there are saved credentials, and they are valid"
Valid,
}

public class CredentialModel
{
public CredentialState State { get; init; } = CredentialState.Unknown;

public string? CoderUrl { get; init; }
public Uri? CoderUrl { get; init; }
public string? ApiToken { get; init; }

public string? Username { get; init; }
Expand Down
2 changes: 0 additions & 2 deletions App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ private static void Main(string[] args)
notificationManager.NotificationInvoked += app.HandleNotification;
notificationManager.Register();
if (activationArgs.Kind != ExtendedActivationKind.Launch)
{
// this means we were activated without having already launched, so handle
// the activation as well.
app.OnActivated(null, activationArgs);
}
});
}
catch (Exception e)
Expand Down
37 changes: 26 additions & 11 deletions App/Services/CredentialManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class RawCredentials
[JsonSerializable(typeof(RawCredentials))]
public partial class RawCredentialsJsonContext : JsonSerializerContext;

public interface ICredentialManager
public interface ICredentialManager : ICoderApiClientCredentialProvider
{
public event EventHandler<CredentialModel> CredentialsChanged;

Expand Down Expand Up @@ -59,7 +59,8 @@ public interface ICredentialBackend
/// </summary>
public class CredentialManager : ICredentialManager
{
private const string CredentialsTargetName = "Coder.Desktop.App.Credentials";
private readonly ICredentialBackend Backend;
private readonly ICoderApiClientFactory CoderApiClientFactory;

// _opLock is held for the full duration of SetCredentials, and partially
// during LoadCredentials. _opLock protects _inFlightLoad, _loadCts, and
Expand All @@ -79,14 +80,6 @@ public class CredentialManager : ICredentialManager
// immediate).
private volatile CredentialModel? _latestCredentials;

private ICredentialBackend Backend { get; } = new WindowsCredentialBackend(CredentialsTargetName);

private ICoderApiClientFactory CoderApiClientFactory { get; } = new CoderApiClientFactory();

public CredentialManager()
{
}

public CredentialManager(ICredentialBackend backend, ICoderApiClientFactory coderApiClientFactory)
{
Backend = backend;
Expand All @@ -108,6 +101,20 @@ public CredentialModel GetCachedCredentials()
};
}

// Implements ICoderApiClientCredentialProvider
public CoderApiClientCredential? GetCoderApiClientCredential()
{
var latestCreds = _latestCredentials;
if (latestCreds is not { State: CredentialState.Valid } || latestCreds.CoderUrl is null)
return null;

return new CoderApiClientCredential
{
CoderUrl = latestCreds.CoderUrl,
ApiToken = latestCreds.ApiToken ?? "",
};
}

public async Task<string?> GetSignInUri()
{
try
Expand Down Expand Up @@ -253,6 +260,12 @@ private async Task<CredentialModel> PopulateModel(RawCredentials? credentials, C
State = CredentialState.Invalid,
};

if (!Uri.TryCreate(credentials.CoderUrl, UriKind.Absolute, out var uri))
return new CredentialModel
{
State = CredentialState.Invalid,
};

BuildInfo buildInfo;
User me;
try
Expand All @@ -279,7 +292,7 @@ private async Task<CredentialModel> PopulateModel(RawCredentials? credentials, C
return new CredentialModel
{
State = CredentialState.Valid,
CoderUrl = credentials.CoderUrl,
CoderUrl = uri,
ApiToken = credentials.ApiToken,
Username = me.Username,
};
Expand All @@ -298,6 +311,8 @@ private void UpdateState(CredentialModel newModel)

public class WindowsCredentialBackend : ICredentialBackend
{
public const string CoderCredentialsTargetName = "Coder.Desktop.App.Credentials";

private readonly string _credentialsTargetName;

public WindowsCredentialBackend(string credentialsTargetName)
Expand Down
2 changes: 1 addition & 1 deletion App/Services/RpcController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public async Task StartVpn(CancellationToken ct = default)
{
Start = new StartRequest
{
CoderUrl = credentials.CoderUrl,
CoderUrl = credentials.CoderUrl?.ToString(),
ApiToken = credentials.ApiToken,
},
}, ct);
Expand Down
1 change: 0 additions & 1 deletion App/Services/UserNotifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,3 @@ public Task ShowErrorNotification(string title, string message, CancellationToke
return Task.CompletedTask;
}
}

2 changes: 1 addition & 1 deletion App/DisplayScale.cs → App/Utils/DisplayScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Microsoft.UI.Xaml;
using WinRT.Interop;

namespace Coder.Desktop.App;
namespace Coder.Desktop.App.Utils;

/// <summary>
/// A static utility class to house methods related to the visual scale of the display monitor.
Expand Down
Loading
Loading