-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Leo.Wpf.App | ||
{ | ||
public interface IEchoWindowService | ||
{ | ||
void Show(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using Leo.Wpf.App.ViewModels; | ||
using Leo.Wpf.App.Views; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using System.Windows; | ||
|
||
namespace Leo.Wpf.App.Services | ||
{ | ||
internal sealed class EchoWindowService(IServiceProvider _services) : IEchoWindowService | ||
{ | ||
public void Show() | ||
{ | ||
var viewModel = _services.GetRequiredService<EchoViewModel>(); | ||
var window = new EchoWindow(viewModel) | ||
{ | ||
Owner = Application.Current.MainWindow, | ||
WindowStartupLocation = WindowStartupLocation.CenterOwner, | ||
}; | ||
window.Show(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
using CommunityToolkit.Mvvm.ComponentModel; | ||
using CommunityToolkit.Mvvm.Input; | ||
using Leo.UI.Options; | ||
using Leo.UI.Services; | ||
using Microsoft.Extensions.Options; | ||
using Microsoft.Net.Http.Headers; | ||
using System.Collections.ObjectModel; | ||
using System.Net.WebSockets; | ||
using System.Text; | ||
|
||
namespace Leo.Wpf.App.ViewModels | ||
{ | ||
public partial class EchoViewModel : ObservableValidator, IDisposable | ||
{ | ||
private const string DEFAULT_WS_PATH = "/ws/echo"; | ||
private readonly IAuthenticationService _authenticationService; | ||
private ClientWebSocket? _ws; | ||
|
||
[ObservableProperty] | ||
private Uri? _address; | ||
|
||
[ObservableProperty] | ||
[NotifyCanExecuteChangedFor(nameof(SendCommand))] | ||
private string? _message; | ||
|
||
[ObservableProperty] | ||
private ObservableCollection<PingPong> _pingPongs = []; | ||
|
||
[ObservableProperty] | ||
[NotifyCanExecuteChangedFor(nameof(SendCommand))] | ||
private string? _connectButtonSate = "Connect"; | ||
|
||
private WebSocketState? State { get { return _ws?.State; } } | ||
|
||
public EchoViewModel(IOptions<WebOptions> webOptions, IAuthenticationService authenticationService) | ||
{ | ||
Address = new UriBuilder(webOptions.Value.BaseAddress!) | ||
{ | ||
Scheme = Uri.UriSchemeWs, | ||
Path = DEFAULT_WS_PATH | ||
}.Uri; | ||
_authenticationService = authenticationService; | ||
} | ||
|
||
[RelayCommand] | ||
private async Task ConnectAsync() | ||
{ | ||
if (_ws?.State == WebSocketState.Open) | ||
{ | ||
ConnectButtonSate = "Connect"; | ||
await DisconnectAsync(); | ||
var direction = "<-"; | ||
var message = $"Disconnected from {Address}"; | ||
AppendListView(direction, message); | ||
} | ||
else | ||
{ | ||
_ws = new ClientWebSocket(); | ||
var result = await _authenticationService.ExecuteAsync(); | ||
_ws.Options.SetRequestHeader(HeaderNames.Authorization, $"Bearer {result.IdToken}"); | ||
_ws.Options.KeepAliveInterval = TimeSpan.FromMinutes(5); | ||
|
||
await _ws.ConnectAsync(Address!, CancellationToken.None); | ||
ConnectButtonSate = "Disconnect"; | ||
|
||
PingPongs.Clear(); | ||
var message = $"Connected to {Address}"; | ||
var direction = "->"; | ||
AppendListView(direction, message); | ||
} | ||
} | ||
|
||
[RelayCommand(CanExecute = nameof(CanSend))] | ||
private async Task SendAsync() | ||
{ | ||
var cancellationToken = CancellationToken.None; | ||
await _ws!.SendAsync( | ||
new ArraySegment<byte>(Encoding.UTF8.GetBytes(Message!)), | ||
WebSocketMessageType.Text, | ||
WebSocketMessageFlags.EndOfMessage, | ||
cancellationToken); | ||
AppendListView("->", Message!); | ||
|
||
var BUFFER_SIZE = 1024 * 4; | ||
var buffer = new byte[BUFFER_SIZE]; | ||
var msg = new List<byte>(BUFFER_SIZE); | ||
var recv = await _ws!.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken); | ||
msg.AddRange(new ArraySegment<byte>(buffer, 0, recv.Count)); | ||
while (!recv.EndOfMessage) | ||
{ | ||
recv = await _ws!.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken); | ||
msg.AddRange(new ArraySegment<byte>(buffer, 0, recv.Count)); | ||
} | ||
AppendListView("<-", Encoding.UTF8.GetString(msg.ToArray())); | ||
} | ||
|
||
private bool CanSend() | ||
{ | ||
return !string.IsNullOrEmpty(Message) && | ||
string.Equals("Disconnect", ConnectButtonSate) && | ||
State == WebSocketState.Open; | ||
} | ||
|
||
private void AppendListView(string direction, string message) | ||
{ | ||
PingPongs.Insert(0, new PingPong { Icon = direction, Message = message, Time = DateTime.Now }); | ||
} | ||
|
||
private async Task DisconnectAsync() | ||
{ | ||
if (_ws?.State == WebSocketState.Open) | ||
{ | ||
await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "NormalClosure", CancellationToken.None); | ||
_ws.Dispose(); | ||
_ws = null; | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_ws?.Dispose(); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
public partial class PingPong : ObservableObject | ||
{ | ||
[ObservableProperty] | ||
private string? _icon; | ||
|
||
[ObservableProperty] | ||
private string? _message; | ||
|
||
[ObservableProperty] | ||
private DateTime? _time; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<Window x:Class="Leo.Wpf.App.Views.EchoWindow" | ||
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:viewModels="clr-namespace:Leo.Wpf.App.ViewModels" | ||
mc:Ignorable="d" | ||
d:DataContext="{d:DesignInstance viewModels:EchoViewModel}" | ||
Title="EchoWindow" Height="450" Width="800"> | ||
<Window.Resources> | ||
<Style x:Key="BaseStyle"> | ||
<Setter Property="Control.Margin" Value="10,5,5,10" /> | ||
<Setter Property="Control.MinWidth" Value="80" /> | ||
<Setter Property="Control.MinHeight" Value="30" /> | ||
<Setter Property="Control.VerticalContentAlignment" Value="Center" /> | ||
</Style> | ||
<Style TargetType="TextBox" BasedOn="{StaticResource BaseStyle}"> | ||
</Style> | ||
<Style TargetType="Button" BasedOn="{StaticResource BaseStyle}"></Style> | ||
</Window.Resources> | ||
<Grid> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="*"/> | ||
<ColumnDefinition Width="Auto"/> | ||
</Grid.ColumnDefinitions> | ||
<Grid.RowDefinitions> | ||
<RowDefinition Height="Auto" /> | ||
<RowDefinition Height="*" /> | ||
<RowDefinition Height="Auto" /> | ||
<RowDefinition Height="*" /> | ||
</Grid.RowDefinitions> | ||
<TextBox Grid.Row="0" Grid.Column="0" | ||
x:Name="txtAddress" | ||
Text="{Binding Address}"/> | ||
<Button Grid.Row="0" Grid.Column="1" | ||
x:Name="btnConnect" | ||
Content="{Binding ConnectButtonSate}" | ||
Command="{Binding ConnectCommand}" | ||
Style="{StaticResource BaseStyle}" /> | ||
<TabControl Grid.Row="1" Grid.ColumnSpan="2"> | ||
<TabItem Header="Message"> | ||
<TextBox Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}" | ||
VerticalContentAlignment="Top" | ||
ScrollViewer.HorizontalScrollBarVisibility="Auto" | ||
ScrollViewer.VerticalScrollBarVisibility="Auto" | ||
AcceptsReturn="True"/> | ||
</TabItem> | ||
<TabItem Header="Params" /> | ||
<TabItem Header="Headers" /> | ||
<TabItem Header="Settings" /> | ||
</TabControl> | ||
<Button Grid.Row="2" Grid.Column="1" | ||
Content="Send" | ||
Command="{Binding SendCommand}"/> | ||
<DataGrid Grid.Row="3" Grid.ColumnSpan="2" | ||
IsReadOnly="True" | ||
AutoGenerateColumns="False" | ||
ItemsSource="{Binding PingPongs}"> | ||
<DataGrid.Columns> | ||
<DataGridTextColumn Header="Icon" Binding="{Binding Icon}" /> | ||
<DataGridTextColumn Header="Message" Binding="{Binding Message}" /> | ||
<DataGridTextColumn Header="Time" Binding="{Binding Time, StringFormat=HH:mm:ss}" /> | ||
</DataGrid.Columns> | ||
</DataGrid> | ||
</Grid> | ||
</Window> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Leo.Wpf.App.ViewModels; | ||
using System.Windows; | ||
|
||
namespace Leo.Wpf.App.Views | ||
{ | ||
/// <summary> | ||
/// Interaction logic for EchoWindow.xaml | ||
/// </summary> | ||
public partial class EchoWindow : Window | ||
{ | ||
public EchoWindow(EchoViewModel viewModel) | ||
{ | ||
InitializeComponent(); | ||
DataContext = viewModel; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters