-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: integrate the tray app with RPC #19
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
using DependencyPropertyGenerator; | ||
using Microsoft.UI.Xaml; | ||
using Microsoft.UI.Xaml.Data; | ||
|
||
namespace Coder.Desktop.App.Converters; | ||
|
||
[DependencyProperty<object>("TrueValue", DefaultValue = true)] | ||
[DependencyProperty<object>("FalseValue", DefaultValue = true)] | ||
public partial class BoolToObjectConverter : DependencyObject, IValueConverter | ||
{ | ||
public object Convert(object value, Type targetType, object parameter, string language) | ||
{ | ||
return value is true ? TrueValue : FalseValue; | ||
} | ||
|
||
public object ConvertBack(object value, Type targetType, object parameter, string language) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.UI.Xaml; | ||
|
||
namespace Coder.Desktop.App.Converters; | ||
|
||
public partial class BoolToVisibilityConverter : BoolToObjectConverter | ||
{ | ||
public BoolToVisibilityConverter() | ||
{ | ||
TrueValue = Visibility.Visible; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're using these converters to allow the VpnLifecycle to control visibility -- would it be more readable to have these conversions operate directly on the lifecycle? So, instead of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a bunch of converters to replace a lot of the random bools in the ViewModel. |
||
FalseValue = Visibility.Collapsed; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.UI.Xaml; | ||
|
||
namespace Coder.Desktop.App.Converters; | ||
|
||
public partial class InverseBoolToVisibilityConverter : BoolToObjectConverter | ||
{ | ||
public InverseBoolToVisibilityConverter() | ||
{ | ||
TrueValue = Visibility.Collapsed; | ||
FalseValue = Visibility.Visible; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using Windows.ApplicationModel.DataTransfer; | ||
using Windows.UI; | ||
using CommunityToolkit.Mvvm.ComponentModel; | ||
using CommunityToolkit.Mvvm.Input; | ||
using Microsoft.UI.Xaml; | ||
using Microsoft.UI.Xaml.Controls; | ||
using Microsoft.UI.Xaml.Controls.Primitives; | ||
using Microsoft.UI.Xaml.Media; | ||
|
||
namespace Coder.Desktop.App.Models; | ||
|
||
public enum AgentConnectionStatus | ||
{ | ||
Green, | ||
Red, | ||
Gray, | ||
} | ||
|
||
public partial class AgentModel : ObservableObject | ||
deansheather marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
[ObservableProperty] | ||
[NotifyPropertyChangedFor(nameof(FullHostname))] | ||
public required partial string Hostname { get; set; } | ||
|
||
[ObservableProperty] | ||
[NotifyPropertyChangedFor(nameof(FullHostname))] | ||
public required partial string HostnameSuffix { get; set; } // including leading dot | ||
|
||
public string FullHostname => Hostname + HostnameSuffix; | ||
|
||
[ObservableProperty] | ||
[NotifyPropertyChangedFor(nameof(ConnectionStatusColor))] | ||
public required partial AgentConnectionStatus ConnectionStatus { get; set; } | ||
|
||
public Brush ConnectionStatusColor => ConnectionStatus switch | ||
{ | ||
AgentConnectionStatus.Green => new SolidColorBrush(Color.FromArgb(255, 52, 199, 89)), | ||
AgentConnectionStatus.Red => new SolidColorBrush(Color.FromArgb(255, 255, 59, 48)), | ||
_ => new SolidColorBrush(Color.FromArgb(255, 142, 142, 147)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this color stuff is related to how this visually appears, and shouldn't be in the model -- it should be in the view or perhaps view model. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've put this into a converter as well |
||
}; | ||
|
||
[ObservableProperty] | ||
public required partial string DashboardUrl { get; set; } | ||
|
||
[RelayCommand] | ||
private void CopyHostname(object parameter) | ||
{ | ||
var dataPackage = new DataPackage | ||
{ | ||
RequestedOperation = DataPackageOperation.Copy, | ||
}; | ||
dataPackage.SetText(FullHostname); | ||
Clipboard.SetContent(dataPackage); | ||
|
||
if (parameter is not FrameworkElement frameworkElement) return; | ||
deansheather marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var flyout = new Flyout | ||
{ | ||
Content = new TextBlock | ||
{ | ||
Text = "DNS Copied", | ||
Margin = new Thickness(4), | ||
}, | ||
}; | ||
FlyoutBase.SetAttachedFlyout(frameworkElement, flyout); | ||
FlyoutBase.ShowAttachedFlyout(frameworkElement); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
namespace Coder.Desktop.App.Models; | ||
|
||
public enum CredentialState | ||
{ | ||
Invalid, | ||
Valid, | ||
} | ||
|
||
public class CredentialModel | ||
{ | ||
public CredentialState State { get; set; } = CredentialState.Invalid; | ||
|
||
public string? CoderUrl { get; set; } | ||
public string? ApiToken { get; set; } | ||
|
||
public CredentialModel Clone() | ||
{ | ||
return new CredentialModel | ||
{ | ||
State = State, | ||
CoderUrl = CoderUrl, | ||
ApiToken = ApiToken, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Coder.Desktop.App.Models; | ||
|
||
public enum RpcLifecycle | ||
{ | ||
Disconnected, | ||
Connecting, | ||
Connected, | ||
} | ||
|
||
public enum VpnLifecycle | ||
{ | ||
Stopped, | ||
Starting, | ||
Started, | ||
Stopping, | ||
} | ||
|
||
public class RpcModel | ||
{ | ||
public RpcLifecycle RpcLifecycle { get; set; } = RpcLifecycle.Disconnected; | ||
|
||
public VpnLifecycle VpnLifecycle { get; set; } = VpnLifecycle.Stopped; | ||
|
||
// TODO: write a type for this or maybe use the Rpc Type directly | ||
public List<object> VisibleAgents { get; set; } = []; | ||
deansheather marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public RpcModel Clone() | ||
{ | ||
return new RpcModel | ||
{ | ||
RpcLifecycle = RpcLifecycle, | ||
VpnLifecycle = VpnLifecycle, | ||
VisibleAgents = VisibleAgents, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System.Threading.Tasks; | ||
using Coder.Desktop.App.Services; | ||
using CommunityToolkit.Mvvm.ComponentModel; | ||
using CommunityToolkit.Mvvm.Input; | ||
|
||
namespace Coder.Desktop.App.Models; | ||
deansheather marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public partial class TrayWindowDisconnectedViewModel : ObservableObject | ||
{ | ||
private readonly IRpcController _rpcController; | ||
|
||
[ObservableProperty] | ||
public partial bool ReconnectButtonEnabled { get; set; } = true; | ||
|
||
public TrayWindowDisconnectedViewModel(IRpcController rpcController) | ||
{ | ||
_rpcController = rpcController; | ||
_rpcController.StateChanged += (_, rpcModel) => UpdateFromRpcModel(rpcModel); | ||
} | ||
|
||
private void UpdateFromRpcModel(RpcModel rpcModel) | ||
{ | ||
ReconnectButtonEnabled = rpcModel.RpcLifecycle != RpcLifecycle.Disconnected; | ||
} | ||
|
||
[RelayCommand] | ||
public async Task Reconnect() | ||
{ | ||
await _rpcController.Reconnect(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using CommunityToolkit.Mvvm.ComponentModel; | ||
using CommunityToolkit.Mvvm.Input; | ||
|
||
namespace Coder.Desktop.App.Models; | ||
|
||
public partial class TrayWindowLoginRequiredViewModel : ObservableObject | ||
{ | ||
[RelayCommand] | ||
public void Login() | ||
spikecurtis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// TODO: open the login window | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want the view models to be singletons as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to. They use other singletons as a source of truth for their data so there isn't really a point to making an interface for them.
Writing an interface for a ViewModel is also a pain AFAIK because of all of the observable property stuff that would need to be added to the interface for each property.