diff --git a/OpenSky.Agent.sln.DotSettings b/OpenSky.Agent.sln.DotSettings index 01f4390..ad724fa 100644 --- a/OpenSky.Agent.sln.DotSettings +++ b/OpenSky.Agent.sln.DotSettings @@ -585,6 +585,7 @@ OpenSky project ${CurrentDate.Year} True True True + True True True True diff --git a/OpenSky.Agent/OpenSky.Agent.csproj b/OpenSky.Agent/OpenSky.Agent.csproj index 694e6f1..01f12ce 100644 --- a/OpenSky.Agent/OpenSky.Agent.csproj +++ b/OpenSky.Agent/OpenSky.Agent.csproj @@ -167,6 +167,9 @@ + + AddAircraft.xaml + FlightTracking.xaml @@ -176,6 +179,7 @@ LoginNotification.xaml + @@ -237,6 +241,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/OpenSky.Agent/Views/AddAircraft.xaml b/OpenSky.Agent/Views/AddAircraft.xaml new file mode 100644 index 0000000..7036f12 --- /dev/null +++ b/OpenSky.Agent/Views/AddAircraft.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + Aircraft identification + + + + + + + + Aircraft currently loaded in the sim identified as: + + + + + + + + Add new aircraft + + + + + + + + + + Name + + Comments + + + + + + + + + diff --git a/OpenSky.Agent/Views/AddAircraft.xaml.cs b/OpenSky.Agent/Views/AddAircraft.xaml.cs new file mode 100644 index 0000000..11fad5a --- /dev/null +++ b/OpenSky.Agent/Views/AddAircraft.xaml.cs @@ -0,0 +1,110 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// OpenSky project 2021-2023 +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace OpenSky.Agent.Views +{ + using System.Windows; + + using OpenSky.Agent.Views.Models; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Simple add aircraft window for users. + /// + /// ------------------------------------------------------------------------------------------------- + public partial class AddAircraft + { + /// ------------------------------------------------------------------------------------------------- + /// + /// Initializes a new instance of the class. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private AddAircraft() + { + this.InitializeComponent(); + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// The single instance of this view. + /// + /// ------------------------------------------------------------------------------------------------- + private static AddAircraft Instance { get; set; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Open the add aircraft view or bring the existing instance into view. + /// + /// + /// sushi.at, 23/03/2021. + /// + /// ------------------------------------------------------------------------------------------------- + public static void Open() + { + if (Instance == null) + { + if (!UserSessionService.Instance.IsUserLoggedIn) + { + LoginNotification.Open(); + return; + } + + Instance = new AddAircraft(); + Instance.Closed += (_, _) => Instance = null; + Instance.Show(); + } + else + { + Instance.BringIntoView(); + Instance.Activate(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Add aircraft on loaded. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// + /// Source of the event. + /// + /// + /// Routed event information. + /// + /// ------------------------------------------------------------------------------------------------- + private void AddAircraftLoaded(object sender, RoutedEventArgs e) + { + if (this.DataContext is AddAircraftViewModel viewModel) + { + viewModel.ViewReference = this; + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// View model fired close window event. + /// + /// + /// sushi.at, 13/12/2023. + /// + /// + /// Source of the event. + /// + /// + /// An object to process. + /// + /// ------------------------------------------------------------------------------------------------- + private void ViewModelCloseWindow(object sender, object e) + { + this.Close(); + } + } +} \ No newline at end of file diff --git a/OpenSky.Agent/Views/AircraftTypes.xaml.cs b/OpenSky.Agent/Views/AircraftTypes.xaml.cs index 2cfc6c8..240fd9f 100644 --- a/OpenSky.Agent/Views/AircraftTypes.xaml.cs +++ b/OpenSky.Agent/Views/AircraftTypes.xaml.cs @@ -14,7 +14,7 @@ namespace OpenSky.Agent.Views /// ------------------------------------------------------------------------------------------------- /// - /// Plane identity collector window. + /// Aircraft types management window. /// /// ------------------------------------------------------------------------------------------------- public partial class AircraftTypes @@ -41,7 +41,7 @@ private AircraftTypes() /// ------------------------------------------------------------------------------------------------- /// - /// Open the plane identity collector view or bring the existing instance into view. + /// Open the aircraft type management view or bring the existing instance into view. /// /// /// sushi.at, 23/03/2021. diff --git a/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs b/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs new file mode 100644 index 0000000..2afae86 --- /dev/null +++ b/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs @@ -0,0 +1,480 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// OpenSky project 2021-2023 +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace OpenSky.Agent.Views.Models +{ + using System; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Linq; + using System.Threading; + using System.Windows; + + using OpenSky.Agent.Controls; + using OpenSky.Agent.Controls.Models; + using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Tools; + using OpenSky.Agent.Tools; + + using OpenSkyApi; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Add aircraft view model. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// + /// ------------------------------------------------------------------------------------------------- + public class AddAircraftViewModel : ViewModel + { + /// ------------------------------------------------------------------------------------------------- + /// + /// The optional comments (what mod was loaded, etc.). + /// + /// ------------------------------------------------------------------------------------------------- + private string comments = string.Empty; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Information describing the identified aircraft. + /// + /// ------------------------------------------------------------------------------------------------- + private string identifiedAircraftInfo = "Unknown"; + + /// ------------------------------------------------------------------------------------------------- + /// + /// The loading text. + /// + /// ------------------------------------------------------------------------------------------------- + private string loadingText; + + /// ------------------------------------------------------------------------------------------------- + /// + /// The name of the aircraft type. + /// + /// ------------------------------------------------------------------------------------------------- + private string name = string.Empty; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Initializes a new instance of the class. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + public AddAircraftViewModel() + { + // Initialize data structures + this.ExistingAircraftTypes = new ObservableCollection(); + + // Initialize commands + this.RefreshAircraftTypesCommand = new AsynchronousCommand(this.RefreshAircraftTypes); + this.IdentifyAircraftCommand = new Command(this.IdentifyAircraft); + this.AddAircraftTypeCommand = new AsynchronousCommand(this.AddAircraftType); + this.CancelCommand = new Command(this.CancelAddAircraft); + + // Execute initial commands + this.RefreshAircraftTypesCommand.DoExecute(null); + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Occurs when the view model wants to close the window. + /// + /// ------------------------------------------------------------------------------------------------- + public event EventHandler CloseWindow; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the add aircraft type command. + /// + /// ------------------------------------------------------------------------------------------------- + public AsynchronousCommand AddAircraftTypeCommand { get; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the cancel command. + /// + /// ------------------------------------------------------------------------------------------------- + public Command CancelCommand { get; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets or sets the optional comments (what mod was loaded, etc.). + /// + /// ------------------------------------------------------------------------------------------------- + public string Comments + { + get => this.comments; + + set + { + if (Equals(this.comments, value)) + { + return; + } + + this.comments = value; + this.NotifyPropertyChanged(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets a list of the existing aircraft types. + /// + /// ------------------------------------------------------------------------------------------------- + public ObservableCollection ExistingAircraftTypes { get; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets or sets information describing the identified aircraft. + /// + /// ------------------------------------------------------------------------------------------------- + public string IdentifiedAircraftInfo + { + get => this.identifiedAircraftInfo; + + set + { + if (Equals(this.identifiedAircraftInfo, value)) + { + return; + } + + this.identifiedAircraftInfo = value; + this.NotifyPropertyChanged(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the identify aircraft command. + /// + /// ------------------------------------------------------------------------------------------------- + public Command IdentifyAircraftCommand { get; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets or sets the loading text. + /// + /// ------------------------------------------------------------------------------------------------- + public string LoadingText + { + get => this.loadingText; + + set + { + if (Equals(this.loadingText, value)) + { + return; + } + + this.loadingText = value; + this.NotifyPropertyChanged(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets or sets the name of the aircraft type. + /// + /// ------------------------------------------------------------------------------------------------- + public string Name + { + get => this.name; + + set + { + if (Equals(this.name, value)) + { + return; + } + + this.name = value; + this.NotifyPropertyChanged(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the refresh aircraft types command. + /// + /// ------------------------------------------------------------------------------------------------- + public AsynchronousCommand RefreshAircraftTypesCommand { get; } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the simulator object. + /// + /// ------------------------------------------------------------------------------------------------- + public Agent.Simulator.Simulator Simulator => Agent.Simulator.Simulator.Instance; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Adds a new aircraft type. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void AddAircraftType() + { + if (!this.Simulator.Connected) + { + this.AddAircraftTypeCommand.ReportProgress( + () => + { + var notification = new OpenSkyNotification("Error ", "Not connected to sim!", MessageBoxButton.OK, ExtendedMessageBoxImage.Error, 30); + notification.SetErrorColorStyle(); + this.ViewReference.ShowNotification(notification); + }); + return; + } + + if (string.IsNullOrEmpty(this.Simulator.AircraftIdentity.AtcModel) || string.IsNullOrEmpty(this.Simulator.AircraftIdentity.AtcType)) + { + ExtendedMessageBoxResult? answer = null; + this.AddAircraftTypeCommand.ReportProgress( + () => + { + var messageBox = new OpenSkyMessageBox( + "Missing ATC identification", + "ATC aircraft model or type not set, are you connected to the simulator and is the aircraft loaded correctly? Are you sure you want to continue?", + MessageBoxButton.YesNo, + ExtendedMessageBoxImage.Hand); + messageBox.SetWarningColorStyle(); + messageBox.Closed += (_, _) => { answer = messageBox.Result; }; + this.ViewReference.ShowMessageBox(messageBox); + }); + while (answer == null && !SleepScheduler.IsShutdownInProgress) + { + Thread.Sleep(500); + } + + if (answer != ExtendedMessageBoxResult.Yes) + { + return; + } + } + + AircraftType matchedType = null; + foreach (var type in this.ExistingAircraftTypes) + { + if (type.MatchesAircraftInSimulator()) + { + matchedType = type; + break; + } + } + + if (matchedType != null) + { + ExtendedMessageBoxResult? answer = null; + this.AddAircraftTypeCommand.ReportProgress( + () => + { + var messageBox = new OpenSkyMessageBox( + "Add aircraft type", + $"The aircraft currently loaded in the sim seems to be a match for the existing type: {matchedType}\r\n\r\nAre you sure you want to add another type?", + MessageBoxButton.YesNo, + ExtendedMessageBoxImage.Question); + messageBox.Closed += (_, _) => + { + answer = messageBox.Result; + }; + this.ViewReference.ShowMessageBox(messageBox); + }); + while (answer == null && !SleepScheduler.IsShutdownInProgress) + { + Thread.Sleep(500); + } + + if (answer != ExtendedMessageBoxResult.Yes) + { + return; + } + } + + if (string.IsNullOrEmpty(this.Name) || this.Name.Length < 5) + { + this.AddAircraftTypeCommand.ReportProgress( + () => + { + var notification = new OpenSkyNotification("Error", "Name not specified or less than 5 characters!", MessageBoxButton.OK, ExtendedMessageBoxImage.Error, 30); + notification.SetErrorColorStyle(); + this.ViewReference.ShowNotification(notification); + }); + return; + } + + var newAircraftType = new AircraftType + { + Id = Guid.Empty, // API will assign this + UploaderID = "OpenSkyUser", // API will assign this + AtcType = !string.IsNullOrEmpty(this.Simulator.AircraftIdentity.AtcType) ? this.Simulator.AircraftIdentity.AtcType : "MISSING", + AtcModel = !string.IsNullOrEmpty(this.Simulator.AircraftIdentity.AtcModel) ? this.Simulator.AircraftIdentity.AtcModel : "MISSING", + EngineType = this.Simulator.AircraftIdentity.EngineType, + EngineCount = this.Simulator.AircraftIdentity.EngineCount, + EmptyWeight = Math.Round(this.Simulator.WeightAndBalance.EmptyWeight, 0), + FuelTotalCapacity = Math.Round(this.Simulator.WeightAndBalance.FuelTotalCapacity, 1), + FuelWeightPerGallon = Math.Round(this.Simulator.WeightAndBalance.FuelWeightPerGallon, 1), + MaxGrossWeight = Math.Round(this.Simulator.WeightAndBalance.MaxGrossWeight, 0), + FlapsAvailable = this.Simulator.AircraftIdentity.FlapsAvailable, + IsGearRetractable = this.Simulator.AircraftIdentity.GearRetractable, + Name = this.Name, + Comments = this.Comments, + Simulator = this.Simulator.SimulatorType + }; + + this.LoadingText = "Adding new aircraft"; + try + { + var result = AgentOpenSkyService.Instance.AddAircraftTypeAsync(null, newAircraftType).Result; + if (!result.IsError) + { + this.AddAircraftTypeCommand.ReportProgress( + () => + { + var messageBox = new OpenSkyMessageBox("New aircraft", result.Message, MessageBoxButton.OK, ExtendedMessageBoxImage.Check, 10); + this.ViewReference.ShowMessageBox(messageBox); + messageBox.Closed += (_, _) => { this.CloseWindow?.Invoke(this, null); }; + }); + } + else + { + this.AddAircraftTypeCommand.ReportProgress( + () => + { + Debug.WriteLine("Error adding new aircraft: " + result.Message); + if (!string.IsNullOrEmpty(result.ErrorDetails)) + { + Debug.WriteLine(result.ErrorDetails); + } + + var notification = new OpenSkyNotification("Error adding new aircraft", result.Message, MessageBoxButton.OK, ExtendedMessageBoxImage.Error, 30); + notification.SetErrorColorStyle(); + this.ViewReference.ShowNotification(notification); + }); + } + } + catch (Exception ex) + { + ex.HandleApiCallException(this.ViewReference, this.AddAircraftTypeCommand, "Error adding new aircraft type"); + } + finally + { + this.LoadingText = null; + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Cancel add aircraft. + /// + /// + /// sushi.at, 13/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void CancelAddAircraft() + { + this.CloseWindow?.Invoke(this, null); + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Identify aircraft currently loaded in the sim. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void IdentifyAircraft() + { + if (!this.Simulator.Connected) + { + this.IdentifiedAircraftInfo = "Not connected"; + var notification = new OpenSkyNotification("Identify aircraft", "Not connected to sim!", MessageBoxButton.OK, ExtendedMessageBoxImage.Error, 30); + notification.SetErrorColorStyle(); + this.ViewReference.ShowNotification(notification); + return; + } + + var foundMatch = false; + foreach (var type in this.ExistingAircraftTypes) + { + if (type.MatchesAircraftInSimulator()) + { + foundMatch = true; + this.IdentifiedAircraftInfo = $"{type.Name} [v{type.VersionNumber}]"; + } + } + + if (!foundMatch) + { + this.IdentifiedAircraftInfo = "No matching aircraft found!"; + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Refreshes the list of existing aircraft types. + /// + /// + /// sushi.at, 02/06/2021. + /// + /// ------------------------------------------------------------------------------------------------- + private void RefreshAircraftTypes() + { + this.LoadingText = "Refreshing aircraft types"; + try + { + var result = AgentOpenSkyService.Instance.GetSimulatorAircraftTypesAsync(this.Simulator.SimulatorType).Result; + if (!result.IsError) + { + this.RefreshAircraftTypesCommand.ReportProgress( + () => + { + this.ExistingAircraftTypes.Clear(); + foreach (var type in result.Data.OrderBy(t => t.Name).ThenBy(t => t.VersionNumber)) + { + this.ExistingAircraftTypes.Add(type); + } + + this.IdentifyAircraftCommand.DoExecute(null); + }); + } + else + { + this.RefreshAircraftTypesCommand.ReportProgress( + () => + { + Debug.WriteLine("Error refreshing aircraft types: " + result.Message); + if (!string.IsNullOrEmpty(result.ErrorDetails)) + { + Debug.WriteLine(result.ErrorDetails); + } + + var notification = new OpenSkyNotification("Error refreshing aircraft types", result.Message, MessageBoxButton.OK, ExtendedMessageBoxImage.Error, 30); + notification.SetErrorColorStyle(); + this.ViewReference.ShowNotification(notification); + }); + } + } + catch (Exception ex) + { + ex.HandleApiCallException(this.ViewReference, this.RefreshAircraftTypesCommand, "Error refreshing aircraft types"); + } + finally + { + this.LoadingText = null; + } + } + } +} \ No newline at end of file diff --git a/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs b/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs index 5f935d0..b742fea 100644 --- a/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs +++ b/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs @@ -608,7 +608,7 @@ public string EngineModel /// ------------------------------------------------------------------------------------------------- /// - /// Gets a list of of the existing aircraft types. + /// Gets a list of the existing aircraft types. /// /// ------------------------------------------------------------------------------------------------- public ObservableCollection ExistingAircraftTypes { get; } @@ -1030,7 +1030,7 @@ public bool RequiresManualLoading /// ------------------------------------------------------------------------------------------------- /// - /// Gets or sets the the selected aircraft type. + /// Gets or sets the selected aircraft type. /// /// ------------------------------------------------------------------------------------------------- public AircraftType SelectedAircraftType @@ -1214,7 +1214,7 @@ public void CloseUpgradeAircraft() /// ------------------------------------------------------------------------------------------------- /// - /// Adds the current plane identity to the CSV. + /// Adds the current aircraft type. /// /// /// sushi.at, 28/03/2021. diff --git a/OpenSky.Agent/Views/Models/StartupViewModel.cs b/OpenSky.Agent/Views/Models/StartupViewModel.cs index 2d98c5a..87cd14b 100644 --- a/OpenSky.Agent/Views/Models/StartupViewModel.cs +++ b/OpenSky.Agent/Views/Models/StartupViewModel.cs @@ -6,6 +6,9 @@ namespace OpenSky.Agent.Views.Models { +#if DEBUG + using DiscordRPC.Logging; +#endif using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -19,9 +22,6 @@ namespace OpenSky.Agent.Views.Models using System.Windows.Media.Imaging; using DiscordRPC; -#if DEBUG - using DiscordRPC.Logging; -#endif using JetBrains.Annotations; @@ -34,9 +34,7 @@ namespace OpenSky.Agent.Views.Models using OpenSkyApi; - using Simulator = Simulator.Simulator; -#if DEBUG -#endif + using Simulator = OpenSky.Agent.Simulator.Simulator; /// ------------------------------------------------------------------------------------------------- /// @@ -105,54 +103,40 @@ public class StartupViewModel : ViewModel /// ------------------------------------------------------------------------------------------------- /// - /// The notification icon. + /// The discord RPC client. /// /// ------------------------------------------------------------------------------------------------- - [NotNull] - private ImageSource notificationIcon; + private DiscordRpcClient discordRpcClient; /// ------------------------------------------------------------------------------------------------- /// - /// The notification status string. + /// The MOD commands visibility. /// /// ------------------------------------------------------------------------------------------------- - [NotNull] - private string notificationStatusString = "OpenSky is trying to connect to the simulator..."; + private Visibility modCommandVisibility = Visibility.Collapsed; /// ------------------------------------------------------------------------------------------------- /// - /// The notification icon visibility. + /// The notification icon. /// /// ------------------------------------------------------------------------------------------------- - private Visibility notificationVisibility = Visibility.Visible; + [NotNull] + private ImageSource notificationIcon; /// ------------------------------------------------------------------------------------------------- /// - /// The discord RPC client. + /// The notification status string. /// /// ------------------------------------------------------------------------------------------------- - private DiscordRpcClient discordRpcClient; + [NotNull] + private string notificationStatusString = "OpenSky is trying to connect to the simulator..."; /// ------------------------------------------------------------------------------------------------- /// - /// Gets or sets the discord RPC client. + /// The notification icon visibility. /// /// ------------------------------------------------------------------------------------------------- - public DiscordRpcClient DiscordRpcClient - { - get => this.discordRpcClient; - - private set - { - if (Equals(this.discordRpcClient, value)) - { - return; - } - - this.discordRpcClient = value; - this.NotifyPropertyChanged(); - } - } + private Visibility notificationVisibility = Visibility.Visible; /// ------------------------------------------------------------------------------------------------- /// @@ -192,6 +176,7 @@ public StartupViewModel() this.FlightTrackingCommand = new Command(this.OpenFlightTracking); this.TrackingDebugCommand = new Command(this.OpenTrackingDebug); this.SoundPackTesterCommand = new Command(this.OpenSoundPackTester); + this.AddAircraftCommand = new Command(this.OpenAddAircraft); this.AircraftTypesCommand = new Command(this.OpenAircraftTypes); this.SettingsCommand = new Command(this.OpenSettings); this.QuitCommand = new Command(this.Quit); @@ -213,141 +198,161 @@ public StartupViewModel() this.DiscordRpcClient = new DiscordRpcClient("918167200492314675"); #if DEBUG this.DiscordRpcClient.Logger = new ConsoleLogger() { Level = LogLevel.Info }; - this.DiscordRpcClient.OnReady += (_, e) => - { - Debug.WriteLine("Received Ready from user {0}", e.User.Username); - }; + this.DiscordRpcClient.OnReady += (_, e) => { Debug.WriteLine("Received Ready from user {0}", e.User.Username); }; - this.DiscordRpcClient.OnPresenceUpdate += (_, e) => - { - Debug.WriteLine("Received Update! {0}", e.Presence); - }; + this.DiscordRpcClient.OnPresenceUpdate += (_, e) => { Debug.WriteLine("Received Update! {0}", e.Presence); }; #endif this.DiscordRpcClient.Initialize(); - this.DiscordRpcClient.SetPresence(new RichPresence - { - State = "Not Connected", - Details = "Trying to connect to simulator", - Assets = new Assets + this.DiscordRpcClient.SetPresence( + new RichPresence { - LargeImageKey = "openskylogogrey512", - LargeImageText = "OpenSky Agent" - } - }); + State = "Not Connected", + Details = "Trying to connect to simulator", + Assets = new Assets + { + LargeImageKey = "openskylogogrey512", + LargeImageText = "OpenSky Agent" + } + }); // Check for new flight from API new Thread( - () => - { - if (UserSessionService.Instance.IsUserLoggedIn) - { - _ = UserSessionService.Instance.RefreshLinkedAccounts().Result; - _ = UserSessionService.Instance.RefreshUserAccountOverview().Result; - Simulator.Instance.OpenSkyUserName = UserSessionService.Instance.Username; - } - else + () => { - Simulator.Instance.OpenSkyUserName = null; - } + if (UserSessionService.Instance.IsUserLoggedIn) + { + _ = UserSessionService.Instance.UpdateUserRoles().Result; + _ = UserSessionService.Instance.RefreshLinkedAccounts().Result; + _ = UserSessionService.Instance.RefreshUserAccountOverview().Result; + Simulator.Instance.OpenSkyUserName = UserSessionService.Instance.Username; + UpdateGUIDelegate updateMenu = this.UpdateMainMenu; + Application.Current.Dispatcher.BeginInvoke(updateMenu); + } + else + { + Simulator.Instance.OpenSkyUserName = null; + } - UserSessionService.Instance.PropertyChanged += (sender, e) => - { - if (e.PropertyName == nameof(UserSessionService.Instance.IsUserLoggedIn)) + UserSessionService.Instance.PropertyChanged += (sender, e) => { - if (UserSessionService.Instance.IsUserLoggedIn) - { - _ = UserSessionService.Instance.RefreshLinkedAccounts().Result; - _ = UserSessionService.Instance.RefreshUserAccountOverview().Result; - Simulator.Instance.OpenSkyUserName = UserSessionService.Instance.Username; - } - else + if (e.PropertyName == nameof(UserSessionService.Instance.IsUserLoggedIn)) { - Simulator.Instance.OpenSkyUserName = null; + if (UserSessionService.Instance.IsUserLoggedIn) + { + _ = UserSessionService.Instance.UpdateUserRoles().Result; + _ = UserSessionService.Instance.RefreshLinkedAccounts().Result; + _ = UserSessionService.Instance.RefreshUserAccountOverview().Result; + Simulator.Instance.OpenSkyUserName = UserSessionService.Instance.Username; + UpdateGUIDelegate updateMenu = this.UpdateMainMenu; + Application.Current.Dispatcher.BeginInvoke(updateMenu); + } + else + { + Simulator.Instance.OpenSkyUserName = null; + } } - } - }; + }; - while (!SleepScheduler.IsShutdownInProgress) - { - if (UserSessionService.Instance.IsUserLoggedIn) + while (!SleepScheduler.IsShutdownInProgress) { - try + if (UserSessionService.Instance.IsUserLoggedIn) { - // Check for active flight - var result = AgentOpenSkyService.Instance.GetFlightAsync().Result; - if (!result.IsError) + try { - if (result.Data.Id != Guid.Empty) + // Check for active flight + var result = AgentOpenSkyService.Instance.GetFlightAsync().Result; + if (!result.IsError) { - UpdateGUIDelegate resetPausedFlights = () => this.PausedFlights.Clear(); - Application.Current.Dispatcher.BeginInvoke(resetPausedFlights); - - if (Simulator.Instance.Flight == null) - { - Simulator.Instance.Flight = result.Data; - Simulator.Instance.VatsimUserID = UserSessionService.Instance.LinkedAccounts?.VatsimID; - } - else + if (result.Data.Id != Guid.Empty) { - if (Simulator.Instance.Flight.Id != result.Data.Id) + UpdateGUIDelegate resetPausedFlights = () => this.PausedFlights.Clear(); + Application.Current.Dispatcher.BeginInvoke(resetPausedFlights); + + if (Simulator.Instance.Flight == null) { - // Different flight from current one? - Simulator.Instance.StopTracking(true); + Simulator.Instance.Flight = result.Data; + Simulator.Instance.VatsimUserID = UserSessionService.Instance.LinkedAccounts?.VatsimID; } - } - } - else - { - if (Simulator.Instance.Flight != null) - { - Simulator.Instance.Flight = null; - } - - // Check for paused flights - var pausedResult = AgentOpenSkyService.Instance.GetMyFlightsAsync().Result; - if (!result.IsError) - { - UpdateGUIDelegate addPausedFlights = () => + else { - this.PausedFlights.Clear(); - foreach (var flight in pausedResult.Data.Where(f => f.Paused.HasValue)) + if (Simulator.Instance.Flight.Id != result.Data.Id) { - this.PausedFlights.Add(flight); + // Different flight from current one? + Simulator.Instance.StopTracking(true); } - }; - Application.Current.Dispatcher.BeginInvoke(addPausedFlights); + } } else { - Debug.WriteLine("Error checking for paused flights: " + result.Message + "\r\n" + result.ErrorDetails); + if (Simulator.Instance.Flight != null) + { + Simulator.Instance.Flight = null; + } + + // Check for paused flights + var pausedResult = AgentOpenSkyService.Instance.GetMyFlightsAsync().Result; + if (!result.IsError) + { + UpdateGUIDelegate addPausedFlights = () => + { + this.PausedFlights.Clear(); + foreach (var flight in pausedResult.Data.Where(f => f.Paused.HasValue)) + { + this.PausedFlights.Add(flight); + } + }; + Application.Current.Dispatcher.BeginInvoke(addPausedFlights); + } + else + { + Debug.WriteLine("Error checking for paused flights: " + result.Message + "\r\n" + result.ErrorDetails); + } } } + else + { + Debug.WriteLine("Error checking for new flight: " + result.Message + "\r\n" + result.ErrorDetails); + } } - else + catch (Exception ex) { - Debug.WriteLine("Error checking for new flight: " + result.Message + "\r\n" + result.ErrorDetails); + Debug.WriteLine("Error checking for new flight: " + ex); } - - } - catch (Exception ex) - { - Debug.WriteLine("Error checking for new flight: " + ex); - } - } - this.NextFlightUpdateCheckSeconds = Simulator.Instance.Flight == null ? 30 : 120; - while (this.NextFlightUpdateCheckSeconds > 0 && !SleepScheduler.IsShutdownInProgress) - { - Thread.Sleep(1000); - if (this.NextFlightUpdateCheckSeconds > 0) + this.NextFlightUpdateCheckSeconds = Simulator.Instance.Flight == null ? 30 : 120; + while (this.NextFlightUpdateCheckSeconds > 0 && !SleepScheduler.IsShutdownInProgress) { - this.NextFlightUpdateCheckSeconds--; + Thread.Sleep(1000); + if (this.NextFlightUpdateCheckSeconds > 0) + { + this.NextFlightUpdateCheckSeconds--; + } } } - } - }) - { Name = "OpenSky.StartupViewModel.CheckForFlights" }.Start(); + }) + { Name = "OpenSky.StartupViewModel.CheckForFlights" }.Start(); + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets or sets the discord RPC client. + /// + /// ------------------------------------------------------------------------------------------------- + public DiscordRpcClient DiscordRpcClient + { + get => this.discordRpcClient; + + private set + { + if (Equals(this.discordRpcClient, value)) + { + return; + } + + this.discordRpcClient = value; + this.NotifyPropertyChanged(); + } } /// ------------------------------------------------------------------------------------------------- @@ -431,6 +436,55 @@ private set } } + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the MOD commands visibility. + /// + /// ------------------------------------------------------------------------------------------------- + public Visibility ModCommandVisibility + { + get => this.modCommandVisibility; + + private set + { + if (Equals(this.modCommandVisibility, value)) + { + return; + } + + this.modCommandVisibility = value; + this.NotifyPropertyChanged(); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// + /// The user logged in command visibility. + /// + /// ------------------------------------------------------------------------------------------------- + private Visibility userLoggedInCommandVisibility = Visibility.Collapsed; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the user logged in command visibility. + /// + /// ------------------------------------------------------------------------------------------------- + public Visibility UserLoggedInCommandVisibility + { + get => this.userLoggedInCommandVisibility; + + private set + { + if (Equals(this.userLoggedInCommandVisibility, value)) + { + return; + } + + this.userLoggedInCommandVisibility = value; + this.NotifyPropertyChanged(); + } + } + /// ------------------------------------------------------------------------------------------------- /// /// Gets the check for next flight now command. @@ -536,16 +590,17 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e this.NotificationIcon = this.greyIcon; this.NotificationStatusString = "OpenSky is trying to connect to the simulator..."; - this.DiscordRpcClient?.SetPresence(new RichPresence - { - State = "Not Connected", - Details = "Trying to connect to simulator", - Assets = new Assets + this.DiscordRpcClient?.SetPresence( + new RichPresence { - LargeImageKey = "openskylogogrey512", - LargeImageText = "OpenSky Agent" - } - }); + State = "Not Connected", + Details = "Trying to connect to simulator", + Assets = new Assets + { + LargeImageKey = "openskylogogrey512", + LargeImageText = "OpenSky Agent" + } + }); } else { @@ -557,16 +612,17 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e this.NotificationIcon = this.openSkyIcon; this.NotificationStatusString = "OpenSky is connected to the sim but not tracking a flight"; - this.DiscordRpcClient?.SetPresence(new RichPresence - { - State = "Idle", - Details = "Waiting for a flight", - Assets = new Assets + this.DiscordRpcClient?.SetPresence( + new RichPresence { - LargeImageKey = "openskylogo512", - LargeImageText = "OpenSky Agent" - } - }); + State = "Idle", + Details = "Waiting for a flight", + Assets = new Assets + { + LargeImageKey = "openskylogo512", + LargeImageText = "OpenSky Agent" + } + }); } else { @@ -574,16 +630,17 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e this.NotificationIcon = this.openSkyIcon; this.NotificationStatusString = $"OpenSky is preparing to track flight {Simulator.Instance.Flight?.FullFlightNumber}"; - this.DiscordRpcClient?.SetPresence(new RichPresence - { - State = Simulator.Instance.TrackingStatus.ToString(), - Details = $"Preparing flight {Simulator.Instance.Flight?.FullFlightNumber}", - Assets = new Assets + this.DiscordRpcClient?.SetPresence( + new RichPresence { - LargeImageKey = "openskylogo512", - LargeImageText = "OpenSky Agent" - } - }); + State = Simulator.Instance.TrackingStatus.ToString(), + Details = $"Preparing flight {Simulator.Instance.Flight?.FullFlightNumber}", + Assets = new Assets + { + LargeImageKey = "openskylogo512", + LargeImageText = "OpenSky Agent" + } + }); } } else if (Simulator.Instance.IsPaused) @@ -592,18 +649,19 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e this.NotificationIcon = this.pauseIcon; this.NotificationStatusString = $"OpenSky tracking and your flight {Simulator.Instance.Flight?.FullFlightNumber} are paused"; - this.DiscordRpcClient?.SetPresence(new RichPresence - { - State = $"Paused, {Simulator.Instance.FlightPhase}", - Details = $"Tracking flight {Simulator.Instance.Flight?.FullFlightNumber}", - Assets = new Assets + this.DiscordRpcClient?.SetPresence( + new RichPresence { - LargeImageKey = "openskylogo512", - LargeImageText = "OpenSky Agent", - SmallImageKey = "pause512", - SmallImageText = "Paused" - } - }); + State = $"Paused, {Simulator.Instance.FlightPhase}", + Details = $"Tracking flight {Simulator.Instance.Flight?.FullFlightNumber}", + Assets = new Assets + { + LargeImageKey = "openskylogo512", + LargeImageText = "OpenSky Agent", + SmallImageKey = "pause512", + SmallImageText = "Paused" + } + }); } else { @@ -611,18 +669,19 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e this.redFlashing = true; this.NotificationStatusString = $"OpenSky is tracking your flight {Simulator.Instance.Flight?.FullFlightNumber}"; - this.DiscordRpcClient?.SetPresence(new RichPresence - { - State = $"Recording, {Simulator.Instance.FlightPhase}", - Details = $"Tracking flight {Simulator.Instance.Flight?.FullFlightNumber}", - Assets = new Assets + this.DiscordRpcClient?.SetPresence( + new RichPresence { - LargeImageKey = "openskylogo512", - LargeImageText = "OpenSky Agent", - SmallImageKey = "record512", - SmallImageText = "Recording" - } - }); + State = $"Recording, {Simulator.Instance.FlightPhase}", + Details = $"Tracking flight {Simulator.Instance.Flight?.FullFlightNumber}", + Assets = new Assets + { + LargeImageKey = "openskylogo512", + LargeImageText = "OpenSky Agent", + SmallImageKey = "record512", + SmallImageText = "Recording" + } + }); new Thread( () => @@ -653,7 +712,7 @@ private void SimConnectPropertyChanged(object sender, PropertyChangedEventArgs e } } }) - { Name = "OpenSky.StartupViewModel.RedFlashing" }.Start(); + { Name = "OpenSky.StartupViewModel.RedFlashing" }.Start(); } } } @@ -758,6 +817,14 @@ public Visibility NotificationVisibility [NotNull] public Command TrackingDebugCommand { get; } + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the add aircraft command. + /// + /// ------------------------------------------------------------------------------------------------- + [NotNull] + public Command AddAircraftCommand { get; } + /// ------------------------------------------------------------------------------------------------- /// /// Gets the sound pack tester command. @@ -806,10 +873,8 @@ private void OpenAircraftTypes() /// ------------------------------------------------------------------------------------------------- private void OpenTrackingDebug() { -#if DEBUG Debug.WriteLine("Opening tracking debug view"); TrackingDebug.Open(); -#endif } /// ------------------------------------------------------------------------------------------------- @@ -822,10 +887,8 @@ private void OpenTrackingDebug() /// ------------------------------------------------------------------------------------------------- private void OpenSoundPackTester() { -#if DEBUG Debug.WriteLine("Opening sound pack tester view"); SoundPackTester.Open(); -#endif } /// ------------------------------------------------------------------------------------------------- @@ -856,6 +919,20 @@ private void OpenSettings() Settings.Open(); } + /// ------------------------------------------------------------------------------------------------- + /// + /// Opens the add aircraft view. + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void OpenAddAircraft() + { + Debug.WriteLine("Opening add aircraft view"); + AddAircraft.Open(); + } + /// ------------------------------------------------------------------------------------------------- /// /// Quits the application. @@ -875,5 +952,19 @@ private void Quit() }; ((App)Application.Current).RequestShutdown(cleanUp); } + + /// ------------------------------------------------------------------------------------------------- + /// + /// Updates the main menu (typically after a user login/logout and view model construction). + /// + /// + /// sushi.at, 12/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void UpdateMainMenu() + { + this.UserLoggedInCommandVisibility = UserSessionService.Instance.IsUserLoggedIn ? Visibility.Visible : Visibility.Collapsed; + this.ModCommandVisibility = UserSessionService.Instance.IsModerator ? Visibility.Visible : Visibility.Collapsed; + } } } \ No newline at end of file diff --git a/OpenSky.Agent/Views/Startup.xaml b/OpenSky.Agent/Views/Startup.xaml index 53bc8c4..640f83a 100644 --- a/OpenSky.Agent/Views/Startup.xaml +++ b/OpenSky.Agent/Views/Startup.xaml @@ -16,7 +16,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tb="http://www.hardcodet.net/taskbar" mc:Ignorable="d" - xmlns:debug="debug-mode" xmlns:models="clr-namespace:OpenSky.Agent.Views.Models" Title="Startup" Height="400" Width="600" ShowInTaskbar="False" ResizeMode="NoResize" WindowStyle="None" WindowStartupLocation="CenterScreen" AllowsTransparency="True" Background="#171a1d" Visibility="Visible"> @@ -60,14 +59,11 @@ - - - - - - - - + + + + +