Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/SiteMonitor/Controls/Loading.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d" Width="200" Height="200"
x:Class="SiteMonitor.Controls.Loading">
<UserControl.Styles>
<Style Selector="Rectangle.textColor">
<Setter Property="Fill" Value="rgb(204, 200, 175)" />
<Style.Animations>
<Animation Duration="0:0:1" IterationCount="INFINITE">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="RotateTransform.Angle" Value="0.0" />
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="Opacity" Value="1.0" />
<Setter Property="RotateTransform.Angle" Value="90.0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="RotateTransform.Angle" Value="180.0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</UserControl.Styles>

<Rectangle Classes="textColor" Width="50" Height="50" />
</UserControl>
15 changes: 15 additions & 0 deletions src/SiteMonitor/Controls/Loading.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Avalonia.Controls;

namespace SiteMonitor.Controls;

/// <summary>
/// A loading icon.
/// </summary>
public partial class Loading : UserControl {
/// <summary>
/// Initializes a new instance of the <see cref="Loading" /> class.
/// </summary>
public Loading() {
InitializeComponent();
}
}
14 changes: 14 additions & 0 deletions src/SiteMonitor/Models/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ public class Configuration {
/// </summary>
public string? ServerAddress { get; set; }

/// <summary>
/// The server username.
/// </summary>
public string? ServerUsername { get; set; }

/// <summary>
/// The server username's password.
/// </summary>
public string? ServerPassword { get; set; }

/// <summary>
/// The singleton instance of the class.
/// </summary>
Expand All @@ -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<Configuration>(json);
}
Expand Down
20 changes: 20 additions & 0 deletions src/SiteMonitor/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;

using SiteMonitor.ViewModels;

namespace SiteMonitor;

/// <summary>
/// A wrapper that contains the registered services.
/// </summary>
public static class ServiceCollectionExtensions {
/// <summary>
/// Adds the services used throughout the application.
/// </summary>
/// <param name="collection">The services collection to initialize.</param>
public static void AddCommonServices(this IServiceCollection collection) {
// View models
collection.AddTransient<MainWindowViewModel>();
collection.AddTransient<NewVersionWindowViewModel>();
}
}
10 changes: 5 additions & 5 deletions src/SiteMonitor/SiteMonitor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.1"/>
<PackageReference Include="Avalonia.Desktop" Version="11.3.1"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.1"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.1"/>
<PackageReference Include="Avalonia" Version="11.3.2"/>
<PackageReference Include="Avalonia.Desktop" Version="11.3.2"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.2"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.2"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.1"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="11.3.1"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="11.3.2"/>
</ItemGroup>

<ItemGroup>
Expand Down
35 changes: 28 additions & 7 deletions src/SiteMonitor/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -137,15 +139,29 @@ public bool IsDisplayingAdvancedCommands {
/// </summary>
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 { }
}
}

/// <summary>
/// The password to use for the SSH session for commands.
/// </summary>
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 { }
}
}

/// <summary>
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -224,18 +245,18 @@ private async Task<bool> SendHeadRequest(string address) {
/// </summary>
/// <param name="address">The address to send the request to.</param>
/// <returns>The content of the response.</returns>
private async Task<string?> SendGetRequest(string address) {
private async Task<(HttpStatusCode, string?)> SendGetRequest(string address) {
try {
var handler = new HttpClientHandler();
handler.AutomaticDecompression = ~DecompressionMethods.None;
using var httpClient = new HttpClient(handler);
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);
}
}

Expand Down
37 changes: 28 additions & 9 deletions src/SiteMonitor/ViewModels/NewVersionWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ namespace SiteMonitor.ViewModels;
/// The view model for the <seealso cref="NewVersionWindow" /> class.
/// </summary>
public class NewVersionWindowViewModel : ViewModelBase {
/// <summary>
/// True if updating the application currently, false otherwise.
/// </summary>
private bool _isUpdating;

/// <summary>
/// The local version of the software.
/// </summary>
Expand All @@ -36,9 +41,10 @@ public class NewVersionWindowViewModel : ViewModelBase {
/// Initializes a new instance of the <see cref="NewVersionWindowViewModel" /> class.
/// </summary>
public NewVersionWindowViewModel() {
OpenBrowser = ReactiveCommand.Create(LaunchBrowser);
UpdateSoftware = ReactiveCommand.Create(StartUpdateSoftware);
CloseWindow = ReactiveCommand.Create<Window>(CloseWindowCommand);

// asynchronously determine the current version number.
Task.Factory.StartNew(async () => {
GithubLatestReleaseJson? version =
await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "nullinside-site-monitor");
Expand Down Expand Up @@ -69,9 +75,17 @@ public string? ServerVersion {
}

/// <summary>
/// A command to open the browser window at the current update's location.
/// True if updating the application currently, false otherwise.
/// </summary>
public bool IsUpdating {
get => _isUpdating;
set => this.RaiseAndSetIfChanged(ref _isUpdating, value);
}

/// <summary>
/// A command to update the software.
/// </summary>
public ICommand OpenBrowser { get; }
public ICommand UpdateSoftware { get; }

/// <summary>
/// A command to close the current window.
Expand All @@ -89,11 +103,16 @@ private void CloseWindowCommand(Window self) {
/// <summary>
/// Launches the web browser at the new release page.
/// </summary>
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);
}
}
11 changes: 8 additions & 3 deletions src/SiteMonitor/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
57 changes: 39 additions & 18 deletions src/SiteMonitor/Views/NewVersionWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<viewModels:NewVersionWindowViewModel />
</Design.DataContext>

<StackPanel Margin="10">
<TextBlock TextWrapping="Wrap" Margin="0 0 0 10">There is a new version of the software available, would you like to download it?</TextBlock>
<StackPanel Orientation="Horizontal">
<Label>App Version: </Label>
<TextBlock Text="{Binding LocalVersion}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Server Version: </Label>
<TextBlock Text="{Binding ServerVersion}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding OpenBrowser}" Margin="0 10 0 0">Open Browser...</Button>
<Button Command="{Binding CloseWindow}" CommandParameter="{Binding ElementName=Window}" Margin="10 10 0 0">Close</Button>
<DockPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Name="ContentWrapper">
<StackPanel>
<!-- The normal panel -->
<StackPanel Margin="10" IsVisible="{Binding !IsUpdating}">
<TextBlock TextWrapping="Wrap" Margin="0 0 0 10">There is a new version of the software available, would you like to download it?</TextBlock>
<StackPanel Orientation="Horizontal">
<Label>App Version: </Label>
<TextBlock Text="{Binding LocalVersion}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Server Version: </Label>
<TextBlock Text="{Binding ServerVersion}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding UpdateSoftware}" Margin="0 10 0 0">Install New Version</Button>
<Button Command="{Binding CloseWindow}" CommandParameter="{Binding ElementName=Window}"
Margin="10 10 0 0">
Close
</Button>
</StackPanel>
</StackPanel>

<!-- The waiting to update display -->
<StackPanel IsVisible="{Binding IsUpdating}">
<Label HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="32"
FontWeight="Bold"
Foreground="rgb(204, 200, 175)">
Updating
</Label>
<controls:Loading Width="100" Height="100" />
</StackPanel>
</StackPanel>
</StackPanel>
</DockPanel>
</Window>