diff --git a/src/[ApplicationNameUpperCamelCase]/App.axaml b/src/[ApplicationNameUpperCamelCase]/App.axaml index d36aef5..c28b9e7 100644 --- a/src/[ApplicationNameUpperCamelCase]/App.axaml +++ b/src/[ApplicationNameUpperCamelCase]/App.axaml @@ -1,12 +1,12 @@ - + @@ -16,7 +16,7 @@ + ToolTipText="ApplicationNameUpperCamelCase"> diff --git a/src/[ApplicationNameUpperCamelCase]/App.axaml.cs b/src/[ApplicationNameUpperCamelCase]/App.axaml.cs index b45bdc3..c645390 100644 --- a/src/[ApplicationNameUpperCamelCase]/App.axaml.cs +++ b/src/[ApplicationNameUpperCamelCase]/App.axaml.cs @@ -1,14 +1,12 @@ using System; - +using ApplicationNameUpperCamelCase.ViewModels; +using ApplicationNameUpperCamelCase.Views; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using SiteMonitor.ViewModels; -using SiteMonitor.Views; - -namespace SiteMonitor; +namespace ApplicationNameUpperCamelCase; /// /// Main entry point of the application. diff --git a/src/[ApplicationNameUpperCamelCase]/Models/Configuration.cs b/src/[ApplicationNameUpperCamelCase]/Models/Configuration.cs index 2c76f46..480f35b 100644 --- a/src/[ApplicationNameUpperCamelCase]/Models/Configuration.cs +++ b/src/[ApplicationNameUpperCamelCase]/Models/Configuration.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; -namespace SiteMonitor.Models; +namespace ApplicationNameUpperCamelCase.Models; /// /// The configuration of the application. diff --git a/src/[ApplicationNameUpperCamelCase]/Program.cs b/src/[ApplicationNameUpperCamelCase]/Program.cs index d0cbb2d..0a059e9 100644 --- a/src/[ApplicationNameUpperCamelCase]/Program.cs +++ b/src/[ApplicationNameUpperCamelCase]/Program.cs @@ -3,7 +3,7 @@ using Avalonia; using Avalonia.ReactiveUI; -namespace SiteMonitor; +namespace ApplicationNameUpperCamelCase; internal sealed class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any diff --git a/src/[ApplicationNameUpperCamelCase]/ViewLocator.cs b/src/[ApplicationNameUpperCamelCase]/ViewLocator.cs index cb5a9dc..fec0809 100644 --- a/src/[ApplicationNameUpperCamelCase]/ViewLocator.cs +++ b/src/[ApplicationNameUpperCamelCase]/ViewLocator.cs @@ -1,11 +1,9 @@ using System; - +using ApplicationNameUpperCamelCase.ViewModels; using Avalonia.Controls; using Avalonia.Controls.Templates; -using SiteMonitor.ViewModels; - -namespace SiteMonitor; +namespace ApplicationNameUpperCamelCase; /// /// Pre-generated for us. diff --git a/src/[ApplicationNameUpperCamelCase]/ViewModels/MainWindowViewModel.cs b/src/[ApplicationNameUpperCamelCase]/ViewModels/MainWindowViewModel.cs index 9aca394..e1ca944 100644 --- a/src/[ApplicationNameUpperCamelCase]/ViewModels/MainWindowViewModel.cs +++ b/src/[ApplicationNameUpperCamelCase]/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; @@ -10,17 +11,29 @@ using ReactiveUI; -using SiteMonitor.Models; +using ApplicationNameUpperCamelCase.Models; -namespace SiteMonitor.ViewModels; +namespace ApplicationNameUpperCamelCase.ViewModels; /// /// The view model for the main UI. /// public class MainWindowViewModel : ViewModelBase { + /// + /// True if the application is updating, false otherwise. + /// + private bool _isUpdating; /// /// Initializes a new instance of the class. /// public MainWindowViewModel() { + _isUpdating = Environment.GetCommandLineArgs().ToList().Contains("--update"); + } + /// + /// True if the application is updating, false otherwise. + /// + public bool IsUpdating { + get => _isUpdating; + set => this.RaiseAndSetIfChanged(ref _isUpdating, value); } } \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/ViewModels/NewVersionWindowViewModel.cs b/src/[ApplicationNameUpperCamelCase]/ViewModels/NewVersionWindowViewModel.cs index 8027a53..48046ac 100644 --- a/src/[ApplicationNameUpperCamelCase]/ViewModels/NewVersionWindowViewModel.cs +++ b/src/[ApplicationNameUpperCamelCase]/ViewModels/NewVersionWindowViewModel.cs @@ -1,7 +1,7 @@ using System.Diagnostics; using System.Threading.Tasks; using System.Windows.Input; - +using ApplicationNameUpperCamelCase.Views; using Avalonia.Controls; using Avalonia.Threading; @@ -9,14 +9,17 @@ using ReactiveUI; -using SiteMonitor.Views; - -namespace SiteMonitor.ViewModels; +namespace ApplicationNameUpperCamelCase.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,12 +39,12 @@ 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); Task.Factory.StartNew(async () => { GithubLatestReleaseJson? version = - await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "nullinside-site-monitor"); + await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "ApplicationNameUpperCamelCase"); if (null == version) { return; @@ -69,9 +72,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 ICommand OpenBrowser { get; } + public bool IsUpdating { + get => _isUpdating; + set => this.RaiseAndSetIfChanged(ref _isUpdating, value); + } + + /// + /// A command to update the software. + /// + public ICommand UpdateSoftware { get; } /// /// A command to close the current window. @@ -89,11 +100,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/[ApplicationNameUpperCamelCase]/ViewModels/ViewModelBase.cs b/src/[ApplicationNameUpperCamelCase]/ViewModels/ViewModelBase.cs index 1e04501..cf0491b 100644 --- a/src/[ApplicationNameUpperCamelCase]/ViewModels/ViewModelBase.cs +++ b/src/[ApplicationNameUpperCamelCase]/ViewModels/ViewModelBase.cs @@ -1,6 +1,6 @@ using ReactiveUI; -namespace SiteMonitor.ViewModels; +namespace ApplicationNameUpperCamelCase.ViewModels; /// /// A base class for all view models. diff --git a/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml b/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml new file mode 100644 index 0000000..cb2e1d0 --- /dev/null +++ b/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml.cs b/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml.cs new file mode 100644 index 0000000..4414644 --- /dev/null +++ b/src/[ApplicationNameUpperCamelCase]/Views/Loading.axaml.cs @@ -0,0 +1,16 @@ +using Avalonia; +using Avalonia.Controls; + +namespace ApplicationNameUpperCamelCase.Views; + +/// +/// 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/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml b/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml index 6fe8683..a212228 100644 --- a/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml +++ b/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml @@ -2,18 +2,24 @@ 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:SiteMonitor.ViewModels" + xmlns:viewModels="clr-namespace:ApplicationNameUpperCamelCase.ViewModels" + xmlns:views="clr-namespace:ApplicationNameUpperCamelCase.Views" mc:Ignorable="d" d:DesignWidth="475" d:DesignHeight="300" + MinWidth="475" + MinHeight="300" Width="475" Height="300" - CanResize="False" + CanResize="True" WindowStartupLocation="CenterScreen" - x:Class="SiteMonitor.Views.MainWindow" + ExtendClientAreaToDecorationsHint="True" + ExtendClientAreaChromeHints="NoChrome" + ExtendClientAreaTitleBarHeightHint="-1" + x:Class="ApplicationNameUpperCamelCase.Views.MainWindow" x:DataType="viewModels:MainWindowViewModel" Icon="/Assets/logo.ico" - Title="Site Monitor"> + Title="ApplicationNameUpperCamelCase"> + + + + + + + + + + + \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml.cs b/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml.cs index e552282..d852669 100644 --- a/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml.cs +++ b/src/[ApplicationNameUpperCamelCase]/Views/MainWindow.axaml.cs @@ -1,15 +1,19 @@ using System; +using System.Linq; using System.Reflection; using System.Threading.Tasks; - +using ApplicationNameUpperCamelCase.ViewModels; using Avalonia.Controls; using Avalonia.Threading; using Nullinside.Api.Common.Desktop; -using SiteMonitor.ViewModels; - -namespace SiteMonitor.Views; +#if !DEBUG +using Microsoft.Extensions.DependencyInjection; +#else +using Avalonia; +#endif +namespace ApplicationNameUpperCamelCase.Views; /// /// The main application window. @@ -20,6 +24,9 @@ public partial class MainWindow : Window { /// public MainWindow() { InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif } /// @@ -28,9 +35,19 @@ public MainWindow() { protected override void OnInitialized() { base.OnInitialized(); + var args = Environment.GetCommandLineArgs().ToList(); + if (args.Contains("--update")) { + _ = GitHubUpdateManager.PerformUpdateAndRestart("nullinside-development-group", "ApplicationNameUpperCamelCase", args[2], "windows-x64.zip"); + return; + } + + if (args.Contains("--justUpdated")) { + _ = GitHubUpdateManager.CleanupUpdate(); + } + Task.Factory.StartNew(async () => { GithubLatestReleaseJson? serverVersion = - await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "nullinside-site-monitor"); + await GitHubUpdateManager.GetLatestVersion("nullinside-development-group", "ApplicationNameUpperCamelCase"); string? localVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(); if (null == serverVersion || string.IsNullOrWhiteSpace(serverVersion.name) || string.IsNullOrWhiteSpace(localVersion)) { @@ -43,18 +60,33 @@ protected override void OnInitialized() { } if (serverVersion.name?.Equals(localVersion, StringComparison.InvariantCultureIgnoreCase) ?? true) { +// Had to add this because code clean up tools were removing the "redundant" return statement. +// which was causing the check to always be ignored. +#if !DEBUG + return; +#endif + } + +#if !DEBUG + var vm = ServiceProvider?.GetRequiredService(); + if (null == vm) { return; } - Dispatcher.UIThread.Post(async () => { - var versionWindow = new NewVersionWindow { - DataContext = new NewVersionWindowViewModel { - LocalVersion = localVersion - } - }; + vm.LocalVersion = localVersion; + Dispatcher.UIThread.Post(async void () => { + try { + var versionWindow = new NewVersionWindow { + DataContext = vm + }; - await versionWindow.ShowDialog(this); + await versionWindow.ShowDialog(this); + } + catch { + // do nothing, don't crash + } }); +#endif }); } } \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml b/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml index a9ddd7d..fedc8a2 100644 --- a/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml +++ b/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml @@ -2,34 +2,61 @@ 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:SiteMonitor.ViewModels" + xmlns:viewModels="clr-namespace:ApplicationNameUpperCamelCase.ViewModels" + xmlns:views="clr-namespace:ApplicationNameUpperCamelCase.Views" x:Name="Window" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" - x:Class="SiteMonitor.Views.NewVersionWindow" + x:Class="ApplicationNameUpperCamelCase.Views.NewVersionWindow" x:DataType="viewModels:NewVersionWindowViewModel" WindowStartupLocation="CenterOwner" + ExtendClientAreaToDecorationsHint="True" + ExtendClientAreaChromeHints="NoChrome" + ExtendClientAreaTitleBarHeightHint="-1" 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 diff --git a/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml.cs b/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml.cs index b5c1bcf..be4d452 100644 --- a/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml.cs +++ b/src/[ApplicationNameUpperCamelCase]/Views/NewVersionWindow.axaml.cs @@ -1,15 +1,16 @@ using Avalonia.Controls; -namespace SiteMonitor.Views; - -/// -/// The new version number dialog. -/// -public partial class NewVersionWindow : Window { +namespace ApplicationNameUpperCamelCase.Views +{ /// - /// Initializes a new instance of the class. + /// The new version number dialog. /// - public NewVersionWindow() { - InitializeComponent(); + public partial class NewVersionWindow : Window { + /// + /// Initializes a new instance of the class. + /// + public NewVersionWindow() { + InitializeComponent(); + } } } \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml b/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml new file mode 100644 index 0000000..54aa4f7 --- /dev/null +++ b/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml.cs b/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml.cs new file mode 100644 index 0000000..4610609 --- /dev/null +++ b/src/[ApplicationNameUpperCamelCase]/Views/WindowsTitleBar.axaml.cs @@ -0,0 +1,142 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Media; + +namespace ApplicationNameUpperCamelCase.Views; + +/// +/// The window menu bar at the top of the UI +/// +/// From: https://github.com/FrankenApps/Avalonia-CustomTitleBarTemplate.git +public partial class WindowsTitleBar : UserControl { + /// + /// A flag indicating whether the system bar should be seemless with other content or have its own vertical space. + /// + public static readonly StyledProperty IS_SEAMLESS_PROPERTY = + AvaloniaProperty.Register(nameof(IsSeamless)); + + private readonly Button _closeButton; + private readonly NativeMenuBar _defaultMenuBar; + private readonly Button _maximizeButton; + private readonly Path _maximizeIcon; + private readonly ToolTip _maximizeToolTip; + private readonly Button _minimizeButton; + private readonly NativeMenuBar _seamlessMenuBar; + private readonly TextBlock _systemChromeTitle; + + private readonly DockPanel _titleBar; + private readonly DockPanel _titleBarBackground; + private readonly Image _windowIcon; + + /// + /// Initializes a new instance of the class. + /// + public WindowsTitleBar() { + InitializeComponent(); + _minimizeButton = this.FindControl