diff --git a/source/CPDLCPlugin/Messages/OpenConnectionRequestWindowRequest.cs b/source/CPDLCPlugin/Messages/OpenConnectionRequestWindowRequest.cs new file mode 100644 index 0000000..2d705f2 --- /dev/null +++ b/source/CPDLCPlugin/Messages/OpenConnectionRequestWindowRequest.cs @@ -0,0 +1,50 @@ +using CPDLCPlugin.Configuration; +using CPDLCPlugin.ViewModels; +using CPDLCPlugin.Windows; +using MediatR; + +namespace CPDLCPlugin.Messages; + +public record OpenConnectionRequestWindowRequest(string Callsign) : IRequest; + +public class OpenConnectionRequestWindowRequestHandler( + WindowManager windowManager, + PluginConfiguration pluginConfiguration, + IMediator mediator, + Plugin plugin, + IErrorReporter errorReporter) + : IRequestHandler +{ + public Task Handle(OpenConnectionRequestWindowRequest request, CancellationToken cancellationToken) + { + // Each window is specific to a callsign, so we always want a fresh window + windowManager.TryRemoveWindow(WindowKeys.ConnectionRequest); + + windowManager.FocusOrCreateWindow( + WindowKeys.ConnectionRequest, + "CPDLC Connection Request", + windowHandle => + { + var currentStationIdentifier = plugin.ConnectionManager?.StationIdentifier; + var viewModel = new ConnectionRequestViewModel( + mediator, + errorReporter, + windowHandle, + request.Callsign, + pluginConfiguration.Stations, + string.IsNullOrEmpty(currentStationIdentifier) ? pluginConfiguration.Stations.First() : currentStationIdentifier!); + + var control = new ConnectionRequestView(viewModel); + return control; + }, + configureForm: form => + { + form.HasMinimizeButton = false; + form.MinimizeBox = false; + form.HasCloseButton = false; + form.HasSendBackButton = false; + }); + + return Task.CompletedTask; + } +} diff --git a/source/CPDLCPlugin/Messages/RebuildCpdlcStatusLabelItemsRequest.cs b/source/CPDLCPlugin/Messages/RebuildCpdlcStatusLabelItemsRequest.cs index ea689f4..feb5399 100644 --- a/source/CPDLCPlugin/Messages/RebuildCpdlcStatusLabelItemsRequest.cs +++ b/source/CPDLCPlugin/Messages/RebuildCpdlcStatusLabelItemsRequest.cs @@ -84,7 +84,21 @@ public async Task Handle(RebuildCpdlcStatusLabelItemsRequest request, Cancellati if (isOnAcarsNetwork && connection is null) { text = "."; - // TODO: Left click will initiate a manual connection + onMouseClick = args => + { + try + { + if (args.Button != CustomLabelItemMouseButton.Left) + return; + + mediator.Send(new OpenConnectionRequestWindowRequest(callsign)); + args.Handled = true; + } + catch (Exception ex) + { + errorReporter.ReportError(ex, "Error opening CPDLC connection request window"); + } + }; } else if (connection is not null && connection.DataAuthorityState == DataAuthorityState.NextDataAuthority) { diff --git a/source/CPDLCPlugin/Messages/SendConnectionRequestRequest.cs b/source/CPDLCPlugin/Messages/SendConnectionRequestRequest.cs new file mode 100644 index 0000000..a482da2 --- /dev/null +++ b/source/CPDLCPlugin/Messages/SendConnectionRequestRequest.cs @@ -0,0 +1,26 @@ +using MediatR; +using Serilog; + +namespace CPDLCPlugin.Messages; + +public record SendConnectionRequestRequest(string Callsign, string StationId) : IRequest; + +public class SendConnectionRequestRequestHandler(Plugin plugin, ILogger logger) + : IRequestHandler +{ + public async Task Handle(SendConnectionRequestRequest request, CancellationToken cancellationToken) + { + logger.Information("Sending connection request for {StationId} to {Callsign}", request.StationId, request.Callsign); + + if (plugin.ConnectionManager is null || !plugin.ConnectionManager.IsConnected) + { + logger.Warning("Not connected to server"); + return; + } + + await plugin.ConnectionManager.RequestConnection( + request.Callsign, + request.StationId, + cancellationToken); + } +} diff --git a/source/CPDLCPlugin/Server/SignalRConnectionManager.cs b/source/CPDLCPlugin/Server/SignalRConnectionManager.cs index 618d1fb..ba93224 100644 --- a/source/CPDLCPlugin/Server/SignalRConnectionManager.cs +++ b/source/CPDLCPlugin/Server/SignalRConnectionManager.cs @@ -180,6 +180,16 @@ await connection.InvokeAsync( cancellationToken: cancellationToken); } + public async Task RequestConnection(string callsign, string stationId, CancellationToken cancellationToken) + { + var connection = GetConnectedOrThrow(); + await connection.InvokeAsync( + "RequestConnection", + callsign, + stationId, + cancellationToken); + } + public async Task GetConnectedAircraft(CancellationToken cancellationToken) { var connection = GetConnectedOrThrow(); diff --git a/source/CPDLCPlugin/ViewModels/ConnectionRequestViewModel.cs b/source/CPDLCPlugin/ViewModels/ConnectionRequestViewModel.cs new file mode 100644 index 0000000..f570ca2 --- /dev/null +++ b/source/CPDLCPlugin/ViewModels/ConnectionRequestViewModel.cs @@ -0,0 +1,61 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CPDLCPlugin.Messages; +using MediatR; + +namespace CPDLCPlugin.ViewModels; + +public partial class ConnectionRequestViewModel : ObservableObject +{ + readonly IMediator _mediator; + readonly IErrorReporter _errorReporter; + readonly IWindowHandle _windowHandle; + + [ObservableProperty] string callsign; + [ObservableProperty] string[] availableStationIdentifiers; + [ObservableProperty] string selectedStationIdentifier; + + // Example data for the XAML designer. Do not use at runtime. + public ConnectionRequestViewModel() + { + Callsign = "QFA123AB"; + AvailableStationIdentifiers = ["YBBB", "YMMM"]; + SelectedStationIdentifier = "YBBB"; + } + + public ConnectionRequestViewModel( + IMediator mediator, + IErrorReporter errorReporter, + IWindowHandle windowHandle, + string callsign, + string[] availableStationIdentifiers, + string selectedStationIdentifier) + { + _mediator = mediator; + _errorReporter = errorReporter; + _windowHandle = windowHandle; + Callsign = callsign; + AvailableStationIdentifiers = availableStationIdentifiers; + SelectedStationIdentifier = selectedStationIdentifier; + } + + [RelayCommand] + async Task Confirm() + { + try + { + await _mediator.Send(new SendConnectionRequestRequest(Callsign, SelectedStationIdentifier)); + _windowHandle.Close(); + } + catch (Exception e) + { + _errorReporter.ReportError(e); + } + } + + [RelayCommand] + void Cancel() + { + _windowHandle.Close(); + } +} diff --git a/source/CPDLCPlugin/Windows/ConnectionRequestView.xaml b/source/CPDLCPlugin/Windows/ConnectionRequestView.xaml new file mode 100644 index 0000000..8bd5696 --- /dev/null +++ b/source/CPDLCPlugin/Windows/ConnectionRequestView.xaml @@ -0,0 +1,75 @@ + + + + + + + + + +