Skip to content

Commit

Permalink
Add EchoWindow.xaml
Browse files Browse the repository at this point in the history
  • Loading branch information
ousiax committed Dec 13, 2023
1 parent d030f46 commit dab9419
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 1 deletion.
7 changes: 7 additions & 0 deletions src/Leo.Wpf.App/Abstractions/IEchoWindowService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Leo.Wpf.App
{
public interface IEchoWindowService
{
void Show();
}
}
3 changes: 3 additions & 0 deletions src/Leo.Wpf.App/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public static IServiceCollection AddLeoViewModels(this IServiceCollection servic
services.AddTransient<CustomerEditorViewModel>();
services.AddTransient<ICustomerEditorWindowService, CustomerEditorWindowService>();

services.AddTransient<EchoViewModel>();
services.AddTransient<IEchoWindowService, EchoWindowService>();

return services;
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/Leo.Wpf.App/Services/EchoWindowService.cs
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();
}
}
}
137 changes: 137 additions & 0 deletions src/Leo.Wpf.App/ViewModels/EchoViewModel.cs
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;
}
}
}
9 changes: 9 additions & 0 deletions src/Leo.Wpf.App/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public sealed partial class MainWindowViewModel : ObservableRecipient, IDisposab
private readonly ICustomerEditorWindowService _customerEditorWindow;
private readonly INewCustomerDetailWindowService _newCustomerDetailWindow;
private readonly IFindWindowService _findWindow;
private readonly IEchoWindowService _echoWindow;

public MainWindowViewModel(
ICustomerService customerService,
Expand All @@ -32,6 +33,7 @@ public MainWindowViewModel(
ICustomerEditorWindowService customerEditorWindow,
INewCustomerDetailWindowService newCustomerDetailWindowService,
IFindWindowService findWindowService,
IEchoWindowService echoWindowService,
IMessenger messenger) : base(messenger)
{
_customerService = customerService;
Expand All @@ -41,6 +43,7 @@ public MainWindowViewModel(
_customerEditorWindow = customerEditorWindow;
_newCustomerDetailWindow = newCustomerDetailWindowService;
_findWindow = findWindowService;
_echoWindow = echoWindowService;

Messenger.Register<CustomerCreatedMessage>(this, (rcpt, msg) =>
{
Expand Down Expand Up @@ -88,6 +91,12 @@ private void FindCustomer()
_findWindow.ShowDialog();
}

[RelayCommand]
private void Echo()
{
_echoWindow.Show();
}

private static bool CanEditCustomer(CustomerViewModel? customer)
{
return customer != null;
Expand Down
66 changes: 66 additions & 0 deletions src/Leo.Wpf.App/Views/EchoWindow.xaml
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>
17 changes: 17 additions & 0 deletions src/Leo.Wpf.App/Views/EchoWindow.xaml.cs
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;
}
}
}
3 changes: 2 additions & 1 deletion src/Leo.Wpf.App/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
CommandParameter="{Binding CurrentCustomer}"/>
<Button Content="{StaticResource MainWindow.ToolBar.Button.Find.Content}"
Command="{Binding FindCustomerCommand}" />
<Button Content="{StaticResource MainWindow.ToolBar.Button.Echo.Content}" />
<Button Content="{StaticResource MainWindow.ToolBar.Button.Echo.Content}"
Command="{Binding EchoCommand}"/>
</ToolBar>
</ToolBarTray>
<StackPanel Grid.Row="1">
Expand Down

0 comments on commit dab9419

Please sign in to comment.