diff --git a/src/SiteMonitor/Controls/Loading.axaml b/src/SiteMonitor/Controls/Loading.axaml new file mode 100644 index 0000000..778ef48 --- /dev/null +++ b/src/SiteMonitor/Controls/Loading.axaml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/src/SiteMonitor/Controls/Loading.axaml.cs b/src/SiteMonitor/Controls/Loading.axaml.cs new file mode 100644 index 0000000..2890eb7 --- /dev/null +++ b/src/SiteMonitor/Controls/Loading.axaml.cs @@ -0,0 +1,15 @@ +using Avalonia.Controls; + +namespace SiteMonitor.Controls; + +/// +/// A loading icon. +/// +public partial class Loading : UserControl { + /// + /// Initializes a new instance of the class. + /// + public Loading() { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/SiteMonitor/Models/Configuration.cs b/src/SiteMonitor/Models/Configuration.cs index 1d2843e..acb17ab 100644 --- a/src/SiteMonitor/Models/Configuration.cs +++ b/src/SiteMonitor/Models/Configuration.cs @@ -20,6 +20,16 @@ public class Configuration { /// public string? ServerAddress { get; set; } + /// + /// The server username. + /// + public string? ServerUsername { get; set; } + + /// + /// The server username's password. + /// + public string? ServerPassword { get; set; } + /// /// The singleton instance of the class. /// @@ -35,6 +45,10 @@ public static Configuration Instance { private static Configuration? ReadConfiguration() { try { + if (!Directory.Exists(Path.GetDirectoryName(S_CONFIG_LOCATION))) { + Directory.CreateDirectory(Path.GetDirectoryName(S_CONFIG_LOCATION)!); + } + string json = File.ReadAllText(S_CONFIG_LOCATION); return JsonConvert.DeserializeObject(json); } diff --git a/src/SiteMonitor/ServiceCollectionExtensions.cs b/src/SiteMonitor/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..17cbf80 --- /dev/null +++ b/src/SiteMonitor/ServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; + +using SiteMonitor.ViewModels; + +namespace SiteMonitor; + +/// +/// A wrapper that contains the registered services. +/// +public static class ServiceCollectionExtensions { + /// + /// Adds the services used throughout the application. + /// + /// The services collection to initialize. + public static void AddCommonServices(this IServiceCollection collection) { + // View models + collection.AddTransient(); + collection.AddTransient(); + } +} \ No newline at end of file diff --git a/src/SiteMonitor/SiteMonitor.csproj b/src/SiteMonitor/SiteMonitor.csproj index d1ea71b..1b4e6f5 100644 --- a/src/SiteMonitor/SiteMonitor.csproj +++ b/src/SiteMonitor/SiteMonitor.csproj @@ -26,13 +26,13 @@ - - - - + + + + - + diff --git a/src/SiteMonitor/ViewModels/MainWindowViewModel.cs b/src/SiteMonitor/ViewModels/MainWindowViewModel.cs index 4fad562..12749d2 100644 --- a/src/SiteMonitor/ViewModels/MainWindowViewModel.cs +++ b/src/SiteMonitor/ViewModels/MainWindowViewModel.cs @@ -42,6 +42,8 @@ public MainWindowViewModel() { Task.Factory.StartNew(PingServer); Task.Factory.StartNew(PingSite); ServerAddress = Configuration.Instance.ServerAddress; + SshUsername = Configuration.Instance.ServerUsername; + SshPassword = Configuration.Instance.ServerPassword; } /// @@ -137,7 +139,14 @@ public bool IsDisplayingAdvancedCommands { /// public string? SshUsername { get => _sshUsername; - set => this.RaiseAndSetIfChanged(ref _sshUsername, value); + set { + Configuration.Instance.ServerUsername = value; + this.RaiseAndSetIfChanged(ref _sshUsername, value); + try { + Configuration.WriteConfiguration(); + } + catch { } + } } /// @@ -145,7 +154,14 @@ public string? SshUsername { /// public string? SshPassword { get => _sshPassword; - set => this.RaiseAndSetIfChanged(ref _sshPassword, value); + set { + Configuration.Instance.ServerPassword = value; + this.RaiseAndSetIfChanged(ref _sshPassword, value); + try { + Configuration.WriteConfiguration(); + } + catch { } + } } /// @@ -178,9 +194,10 @@ private async Task PingSite() { WebsiteUp = await SendHeadRequest("https://nullinside.com"); ApiUp = await SendHeadRequest("https://nullinside.com/api/v1/featureToggle"); NullUp = await SendHeadRequest("https://nullinside.com/null/v1/database/migration"); - ChatTimestamp = await SendGetRequest("https://nullinside.com/twitch-bot/v1/bot/chat/timestamp"); + (HttpStatusCode, string?) chat = await SendGetRequest("https://nullinside.com/twitch-bot/v1/bot/chat/timestamp"); bool chatNotUpdating = false; - if (null != ChatTimestamp) { + if (HttpStatusCode.OK == chat.Item1 && null != chat.Item2) { + ChatTimestamp = chat.Item2; string parsed = ChatTimestamp.Trim('"'); if (DateTime.TryParse(parsed, out DateTime time)) { string timestamp = time.ToLocalTime().ToString(CultureInfo.InvariantCulture); @@ -190,6 +207,10 @@ private async Task PingSite() { chatNotUpdating = diff > TimeSpan.FromMinutes(5); } } + else { + ChatTimestamp = null; + chatNotUpdating = true; + } if ((!WebsiteUp || !ApiUp || !NullUp || chatNotUpdating) && IsMinimized) { WindowState = WindowState.Normal; @@ -224,7 +245,7 @@ private async Task SendHeadRequest(string address) { /// /// The address to send the request to. /// The content of the response. - private async Task SendGetRequest(string address) { + private async Task<(HttpStatusCode, string?)> SendGetRequest(string address) { try { var handler = new HttpClientHandler(); handler.AutomaticDecompression = ~DecompressionMethods.None; @@ -232,10 +253,10 @@ private async Task SendHeadRequest(string address) { using var request = new HttpRequestMessage(HttpMethod.Get, address); request.Headers.TryAddWithoutValidation("user-agent", Nullinside.Api.Common.Constants.FAKE_USER_AGENT); HttpResponseMessage response = await httpClient.SendAsync(request); - return await response.Content.ReadAsStringAsync(); + return (response.StatusCode, await response.Content.ReadAsStringAsync()); } catch { - return null; + return (HttpStatusCode.InternalServerError, null); } } diff --git a/src/SiteMonitor/ViewModels/NewVersionWindowViewModel.cs b/src/SiteMonitor/ViewModels/NewVersionWindowViewModel.cs index 8027a53..d1da320 100644 --- a/src/SiteMonitor/ViewModels/NewVersionWindowViewModel.cs +++ b/src/SiteMonitor/ViewModels/NewVersionWindowViewModel.cs @@ -17,6 +17,11 @@ namespace SiteMonitor.ViewModels; /// The view model for the class. /// public class NewVersionWindowViewModel : ViewModelBase { + /// + /// True if updating the application currently, false otherwise. + /// + private bool _isUpdating; + /// /// The local version of the software. /// @@ -36,9 +41,10 @@ public class NewVersionWindowViewModel : ViewModelBase { /// Initializes a new instance of the class. /// public NewVersionWindowViewModel() { - OpenBrowser = ReactiveCommand.Create(LaunchBrowser); + UpdateSoftware = ReactiveCommand.Create(StartUpdateSoftware); CloseWindow = ReactiveCommand.Create(CloseWindowCommand); + // asynchronously determine the current version number. Task.Factory.StartNew(async () => { GithubLatestReleaseJson? version = await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "nullinside-site-monitor"); @@ -69,9 +75,17 @@ public string? ServerVersion { } /// - /// A command to open the browser window at the current update's location. + /// True if updating the application currently, false otherwise. + /// + public bool IsUpdating { + get => _isUpdating; + set => this.RaiseAndSetIfChanged(ref _isUpdating, value); + } + + /// + /// A command to update the software. /// - public ICommand OpenBrowser { get; } + public ICommand UpdateSoftware { get; } /// /// A command to close the current window. @@ -89,11 +103,16 @@ private void CloseWindowCommand(Window self) { /// /// Launches the web browser at the new release page. /// - private void LaunchBrowser() { - if (string.IsNullOrWhiteSpace(_newVersionUrl)) { - return; - } - - Process.Start("explorer", _newVersionUrl); + private void StartUpdateSoftware() { + IsUpdating = true; + GitHubUpdateManager.PrepareUpdate() + .ContinueWith(_ => { + if (string.IsNullOrWhiteSpace(_newVersionUrl)) { + return; + } + + Process.Start("explorer", _newVersionUrl); + GitHubUpdateManager.ExitApplicationToUpdate(); + }).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/SiteMonitor/Views/MainWindow.axaml.cs b/src/SiteMonitor/Views/MainWindow.axaml.cs index 06fc365..ff365e1 100644 --- a/src/SiteMonitor/Views/MainWindow.axaml.cs +++ b/src/SiteMonitor/Views/MainWindow.axaml.cs @@ -5,10 +5,10 @@ using Avalonia.Controls; using Avalonia.Interactivity; -using Nullinside.Api.Common.Desktop; -#if !DEBUG using Microsoft.Extensions.DependencyInjection; +using Nullinside.Api.Common.Desktop; +#if !DEBUG using Avalonia.Threading; using SiteMonitor.ViewModels; @@ -44,10 +44,15 @@ public MainWindow() { protected override void OnInitialized() { base.OnInitialized(); + // Register all the services needed for the application to run + var collection = new ServiceCollection(); + collection.AddCommonServices(); + ServiceProvider = collection.BuildServiceProvider(); + // handle the command line arguments for updating the application if applicable. string[] args = Environment.GetCommandLineArgs(); if (args.Contains("--update")) { - _ = GitHubUpdateManager.PerformUpdateAndRestart("nullinside-development-group", "twitch-streaming-tools", args[2], "windows-x64.zip"); + _ = GitHubUpdateManager.PerformUpdateAndRestart("nullinside-development-group", "nullinside-site-monitor", args[2], "windows-x64.zip"); return; } diff --git a/src/SiteMonitor/Views/NewVersionWindow.axaml b/src/SiteMonitor/Views/NewVersionWindow.axaml index 8170ab1..ed05fd0 100644 --- a/src/SiteMonitor/Views/NewVersionWindow.axaml +++ b/src/SiteMonitor/Views/NewVersionWindow.axaml @@ -3,34 +3,55 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:SiteMonitor.ViewModels" + xmlns:controls="clr-namespace:SiteMonitor.Controls" x:Name="Window" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" x:Class="SiteMonitor.Views.NewVersionWindow" x:DataType="viewModels:NewVersionWindowViewModel" WindowStartupLocation="CenterOwner" - Topmost="True" SizeToContent="WidthAndHeight" Icon="/Assets/update.png" - Title="New Version Available"> + Title="New Version Available" + Background="rgb(26, 24, 23)"> - - - There is a new version of the software available, would you like to download it? - - - - - - - - - - - + + + + + There is a new version of the software available, would you like to download it? + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file