mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
UI - Rename each OS assembly to Artemis 2
UI - Added auto-run UI - Added auto-update
This commit is contained in:
parent
20af5e9c21
commit
faf5302975
@ -67,6 +67,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cprofiling/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cscriptingproviders_005Cscripts/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Csettings/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=providers_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rgb_002Enet/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Ccolorquantizer/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Ccolorquantizer_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
using Artemis.Core.SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class that can provide one or more graphics <see cref="IManagedGraphicsContext" /> instances by name.
|
||||
/// </summary>
|
||||
public interface IGraphicsContextProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the graphics context.
|
||||
/// </summary>
|
||||
string GraphicsContextName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the managed graphics context this provider provides.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An instance of the resulting managed graphics context if successfully created; otherwise
|
||||
/// <see langword="null" />.
|
||||
/// </returns>
|
||||
IManagedGraphicsContext? GetGraphicsContext();
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class that can provide one or more graphics <see cref="IManagedGraphicsContext"/> instances by name.
|
||||
/// </summary>
|
||||
public interface IGraphicsContextProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read only collection containing the names of all the graphics contexts supported by this provider.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<string> GraphicsContextNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a managed graphics context by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the graphics context.</param>
|
||||
/// <returns>If found, an instance of the managed graphics context with the given <paramref name="name"/>; otherwise <see langword="null"/>.</returns>
|
||||
IManagedGraphicsContext? GetGraphicsContext(string name);
|
||||
}
|
||||
@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Artemis.Core.DeviceProviders;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.Services.Models;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using Artemis.Storage.Entities.Surface;
|
||||
@ -363,15 +364,15 @@ namespace Artemis.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
IGraphicsContextProvider? provider = _kernel.TryGet<IGraphicsContextProvider>();
|
||||
if (provider == null)
|
||||
List<IGraphicsContextProvider> providers = _kernel.Get<List<IGraphicsContextProvider>>();
|
||||
if (!providers.Any())
|
||||
{
|
||||
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
||||
UpdateGraphicsContext(null);
|
||||
return;
|
||||
}
|
||||
|
||||
IManagedGraphicsContext? context = provider.GetGraphicsContext(_preferredGraphicsContext.Value);
|
||||
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
|
||||
if (context == null)
|
||||
{
|
||||
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
||||
|
||||
@ -5,21 +5,24 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
<OutputPath>bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
|
||||
<AssemblyName>Artemis 2</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
<None Remove=".gitignore"/>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -5,21 +5,24 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
<OutputPath>bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
|
||||
<AssemblyName>Artemis 2</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
<None Remove=".gitignore"/>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
21
src/Artemis.UI.Shared/Providers/IAutoRunProvider.cs
Normal file
21
src/Artemis.UI.Shared/Providers/IAutoRunProvider.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Shared.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a provider for custom cursors.
|
||||
/// </summary>
|
||||
public interface IAutoRunProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously enables auto-run.
|
||||
/// </summary>
|
||||
/// <param name="recreate">A boolean indicating whether the auto-run configuration should be recreated (the auto run delay changed)</param>
|
||||
/// <param name="autoRunDelay">The delay in seconds before the application should start (if supported)</param>
|
||||
Task EnableAutoRun(bool recreate, int autoRunDelay);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously disables auto-run.
|
||||
/// </summary>
|
||||
Task DisableAutoRun();
|
||||
}
|
||||
31
src/Artemis.UI.Shared/Providers/IUpdateProvider.cs
Normal file
31
src/Artemis.UI.Shared/Providers/IUpdateProvider.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Shared.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a provider for custom cursors.
|
||||
/// </summary>
|
||||
public interface IUpdateProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously checks whether an update is available.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <returns>A task returning <see langword="true" /> if an update is available; otherwise <see langword="false" />.</returns>
|
||||
Task<bool> CheckForUpdate(string channel);
|
||||
|
||||
/// <summary>
|
||||
/// Applies any available updates.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <param name="silent">Whether or not to update silently.</param>
|
||||
Task ApplyUpdate(string channel, bool silent);
|
||||
|
||||
/// <summary>
|
||||
/// Offer to install the update to the user.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to use when checking updates (i.e. master or development)</param>
|
||||
/// <param name="windowOpen">A boolean indicating whether the main window is open.</param>
|
||||
/// <returns>A task returning <see langword="true" /> if the user chose to update; otherwise <see langword="false" />.</returns>
|
||||
Task OfferUpdate(string channel, bool windowOpen);
|
||||
}
|
||||
@ -4,7 +4,6 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
|
||||
Icon="/Assets/Images/Logo/application.ico"
|
||||
Title="{Binding Title}"
|
||||
ExtendClientAreaToDecorationsHint="True"
|
||||
Width="800"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Windows.Ninject;
|
||||
using Artemis.UI.Windows.Providers;
|
||||
using Artemis.UI.Windows.Providers.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@ -1,42 +1,52 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
<OutputPath>bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
|
||||
<Company>Artemis RGB</Company>
|
||||
<Product>Artemis 2.0</Product>
|
||||
<ApplicationIcon>..\Artemis.UI\Assets\Images\Logo\application.ico</ApplicationIcon>
|
||||
<AssemblyName>Artemis 2</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
<None Remove=".gitignore"/>
|
||||
<None Remove="Assets\Cursors\aero_crosshair.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_crosshair_minus.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_crosshair_plus.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_drag.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_drag_ew.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_fill.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_pen_min.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_pen_plus.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_rotate.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_rotate_bl.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_rotate_br.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_rotate_tl.cur"/>
|
||||
<None Remove="Assets\Cursors\aero_rotate_tr.cur"/>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<None Remove=".gitignore" />
|
||||
<None Remove="Assets\Cursors\aero_crosshair.cur" />
|
||||
<None Remove="Assets\Cursors\aero_crosshair_minus.cur" />
|
||||
<None Remove="Assets\Cursors\aero_crosshair_plus.cur" />
|
||||
<None Remove="Assets\Cursors\aero_drag.cur" />
|
||||
<None Remove="Assets\Cursors\aero_drag_ew.cur" />
|
||||
<None Remove="Assets\Cursors\aero_fill.cur" />
|
||||
<None Remove="Assets\Cursors\aero_pen_min.cur" />
|
||||
<None Remove="Assets\Cursors\aero_pen_plus.cur" />
|
||||
<None Remove="Assets\Cursors\aero_rotate.cur" />
|
||||
<None Remove="Assets\Cursors\aero_rotate_bl.cur" />
|
||||
<None Remove="Assets\Cursors\aero_rotate_br.cur" />
|
||||
<None Remove="Assets\Cursors\aero_rotate_tl.cur" />
|
||||
<None Remove="Assets\Cursors\aero_rotate_tr.cur" />
|
||||
<None Include="..\Artemis.UI\Assets\Images\Logo\application.ico">
|
||||
<Link>application.ico</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15"/>
|
||||
<PackageReference Include="Avalonia.Win32" Version="0.10.15"/>
|
||||
<PackageReference Include="Microsoft.Win32" Version="2.0.1"/>
|
||||
<PackageReference Include="RawInput.Sharp" Version="0.0.4"/>
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50"/>
|
||||
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.1-preview.1"/>
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||
<PackageReference Include="Avalonia.Win32" Version="0.10.15" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.Win32" Version="2.0.1" />
|
||||
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
|
||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.88.1-preview.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj"/>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
BIN
src/Artemis.UI.Windows/Assets/autorun.xml
Normal file
BIN
src/Artemis.UI.Windows/Assets/autorun.xml
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB |
279
src/Artemis.UI.Windows/Models/DevOpsBuilds.cs
Normal file
279
src/Artemis.UI.Windows/Models/DevOpsBuilds.cs
Normal file
@ -0,0 +1,279 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Windows.Models
|
||||
{
|
||||
public class DevOpsBuilds
|
||||
{
|
||||
[JsonProperty("count")]
|
||||
public long Count { get; set; }
|
||||
|
||||
[JsonProperty("value")]
|
||||
public List<DevOpsBuild> Builds { get; set; }
|
||||
}
|
||||
|
||||
public class DevOpsBuild
|
||||
{
|
||||
[JsonProperty("_links")]
|
||||
public BuildLinks Links { get; set; }
|
||||
|
||||
[JsonProperty("properties")]
|
||||
public Properties Properties { get; set; }
|
||||
|
||||
[JsonProperty("tags")]
|
||||
public List<object> Tags { get; set; }
|
||||
|
||||
[JsonProperty("validationResults")]
|
||||
public List<object> ValidationResults { get; set; }
|
||||
|
||||
[JsonProperty("plans")]
|
||||
public List<Plan> Plans { get; set; }
|
||||
|
||||
[JsonProperty("triggerInfo")]
|
||||
public TriggerInfo TriggerInfo { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("buildNumber")]
|
||||
public string BuildNumber { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("result")]
|
||||
public string Result { get; set; }
|
||||
|
||||
[JsonProperty("queueTime")]
|
||||
public DateTimeOffset QueueTime { get; set; }
|
||||
|
||||
[JsonProperty("startTime")]
|
||||
public DateTimeOffset StartTime { get; set; }
|
||||
|
||||
[JsonProperty("finishTime")]
|
||||
public DateTimeOffset FinishTime { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("definition")]
|
||||
public Definition Definition { get; set; }
|
||||
|
||||
[JsonProperty("buildNumberRevision")]
|
||||
public long BuildNumberRevision { get; set; }
|
||||
|
||||
[JsonProperty("project")]
|
||||
public Project Project { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("sourceBranch")]
|
||||
public string SourceBranch { get; set; }
|
||||
|
||||
[JsonProperty("sourceVersion")]
|
||||
public string SourceVersion { get; set; }
|
||||
|
||||
[JsonProperty("priority")]
|
||||
public string Priority { get; set; }
|
||||
|
||||
[JsonProperty("reason")]
|
||||
public string Reason { get; set; }
|
||||
|
||||
[JsonProperty("requestedFor")]
|
||||
public LastChangedBy RequestedFor { get; set; }
|
||||
|
||||
[JsonProperty("requestedBy")]
|
||||
public LastChangedBy RequestedBy { get; set; }
|
||||
|
||||
[JsonProperty("lastChangedDate")]
|
||||
public DateTimeOffset LastChangedDate { get; set; }
|
||||
|
||||
[JsonProperty("lastChangedBy")]
|
||||
public LastChangedBy LastChangedBy { get; set; }
|
||||
|
||||
[JsonProperty("orchestrationPlan")]
|
||||
public Plan OrchestrationPlan { get; set; }
|
||||
|
||||
[JsonProperty("logs")]
|
||||
public Logs Logs { get; set; }
|
||||
|
||||
[JsonProperty("repository")]
|
||||
public Repository Repository { get; set; }
|
||||
|
||||
[JsonProperty("keepForever")]
|
||||
public bool KeepForever { get; set; }
|
||||
|
||||
[JsonProperty("retainedByRelease")]
|
||||
public bool RetainedByRelease { get; set; }
|
||||
|
||||
[JsonProperty("triggeredByBuild")]
|
||||
public object TriggeredByBuild { get; set; }
|
||||
}
|
||||
|
||||
public class Definition
|
||||
{
|
||||
[JsonProperty("drafts")]
|
||||
public List<object> Drafts { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("queueStatus")]
|
||||
public string QueueStatus { get; set; }
|
||||
|
||||
[JsonProperty("revision")]
|
||||
public long Revision { get; set; }
|
||||
|
||||
[JsonProperty("project")]
|
||||
public Project Project { get; set; }
|
||||
}
|
||||
|
||||
public class Project
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("state")]
|
||||
public string State { get; set; }
|
||||
|
||||
[JsonProperty("revision")]
|
||||
public long Revision { get; set; }
|
||||
|
||||
[JsonProperty("visibility")]
|
||||
public string Visibility { get; set; }
|
||||
|
||||
[JsonProperty("lastUpdateTime")]
|
||||
public DateTimeOffset LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
public class LastChangedBy
|
||||
{
|
||||
[JsonProperty("displayName")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("_links")]
|
||||
public LastChangedByLinks Links { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty("uniqueName")]
|
||||
public object UniqueName { get; set; }
|
||||
|
||||
[JsonProperty("imageUrl")]
|
||||
public object ImageUrl { get; set; }
|
||||
|
||||
[JsonProperty("descriptor")]
|
||||
public string Descriptor { get; set; }
|
||||
}
|
||||
|
||||
public class LastChangedByLinks
|
||||
{
|
||||
[JsonProperty("avatar")]
|
||||
public Badge Avatar { get; set; }
|
||||
}
|
||||
|
||||
public class Badge
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public Uri Href { get; set; }
|
||||
}
|
||||
|
||||
public class BuildLinks
|
||||
{
|
||||
[JsonProperty("self")]
|
||||
public Badge Self { get; set; }
|
||||
|
||||
[JsonProperty("web")]
|
||||
public Badge Web { get; set; }
|
||||
|
||||
[JsonProperty("sourceVersionDisplayUri")]
|
||||
public Badge SourceVersionDisplayUri { get; set; }
|
||||
|
||||
[JsonProperty("timeline")]
|
||||
public Badge Timeline { get; set; }
|
||||
|
||||
[JsonProperty("badge")]
|
||||
public Badge Badge { get; set; }
|
||||
}
|
||||
|
||||
public class Logs
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
}
|
||||
|
||||
public class Plan
|
||||
{
|
||||
[JsonProperty("planId")]
|
||||
public Guid PlanId { get; set; }
|
||||
}
|
||||
|
||||
public class Properties
|
||||
{
|
||||
}
|
||||
|
||||
public class Repository
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("clean")]
|
||||
public object Clean { get; set; }
|
||||
|
||||
[JsonProperty("checkoutSubmodules")]
|
||||
public bool CheckoutSubmodules { get; set; }
|
||||
}
|
||||
|
||||
public class TriggerInfo
|
||||
{
|
||||
[JsonProperty("ci.sourceBranch")]
|
||||
public string CiSourceBranch { get; set; }
|
||||
|
||||
[JsonProperty("ci.sourceSha")]
|
||||
public string CiSourceSha { get; set; }
|
||||
|
||||
[JsonProperty("ci.message")]
|
||||
public string CiMessage { get; set; }
|
||||
|
||||
[JsonProperty("ci.triggerRepository")]
|
||||
public string CiTriggerRepository { get; set; }
|
||||
}
|
||||
}
|
||||
244
src/Artemis.UI.Windows/Models/GitHubDifference.cs
Normal file
244
src/Artemis.UI.Windows/Models/GitHubDifference.cs
Normal file
@ -0,0 +1,244 @@
|
||||
#nullable disable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.UI.Windows.Models
|
||||
{
|
||||
public class GitHubDifference
|
||||
{
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("permalink_url")]
|
||||
public Uri PermalinkUrl { get; set; }
|
||||
|
||||
[JsonProperty("diff_url")]
|
||||
public Uri DiffUrl { get; set; }
|
||||
|
||||
[JsonProperty("patch_url")]
|
||||
public Uri PatchUrl { get; set; }
|
||||
|
||||
[JsonProperty("base_commit")]
|
||||
public BaseCommitClass BaseCommit { get; set; }
|
||||
|
||||
[JsonProperty("merge_base_commit")]
|
||||
public BaseCommitClass MergeBaseCommit { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("ahead_by")]
|
||||
public long AheadBy { get; set; }
|
||||
|
||||
[JsonProperty("behind_by")]
|
||||
public long BehindBy { get; set; }
|
||||
|
||||
[JsonProperty("total_commits")]
|
||||
public long TotalCommits { get; set; }
|
||||
|
||||
[JsonProperty("commits")]
|
||||
public List<BaseCommitClass> Commits { get; set; }
|
||||
|
||||
[JsonProperty("files")]
|
||||
public List<File> Files { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitClass
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId { get; set; }
|
||||
|
||||
[JsonProperty("commit")]
|
||||
public BaseCommitCommit Commit { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("comments_url")]
|
||||
public Uri CommentsUrl { get; set; }
|
||||
|
||||
[JsonProperty("author")]
|
||||
public BaseCommitAuthor Author { get; set; }
|
||||
|
||||
[JsonProperty("committer")]
|
||||
public BaseCommitAuthor Committer { get; set; }
|
||||
|
||||
[JsonProperty("parents")]
|
||||
public List<Parent> Parents { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitAuthor
|
||||
{
|
||||
[JsonProperty("login")]
|
||||
public string Login { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId { get; set; }
|
||||
|
||||
[JsonProperty("avatar_url")]
|
||||
public Uri AvatarUrl { get; set; }
|
||||
|
||||
[JsonProperty("gravatar_id")]
|
||||
public string GravatarId { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("followers_url")]
|
||||
public Uri FollowersUrl { get; set; }
|
||||
|
||||
[JsonProperty("following_url")]
|
||||
public string FollowingUrl { get; set; }
|
||||
|
||||
[JsonProperty("gists_url")]
|
||||
public string GistsUrl { get; set; }
|
||||
|
||||
[JsonProperty("starred_url")]
|
||||
public string StarredUrl { get; set; }
|
||||
|
||||
[JsonProperty("subscriptions_url")]
|
||||
public Uri SubscriptionsUrl { get; set; }
|
||||
|
||||
[JsonProperty("organizations_url")]
|
||||
public Uri OrganizationsUrl { get; set; }
|
||||
|
||||
[JsonProperty("repos_url")]
|
||||
public Uri ReposUrl { get; set; }
|
||||
|
||||
[JsonProperty("events_url")]
|
||||
public string EventsUrl { get; set; }
|
||||
|
||||
[JsonProperty("received_events_url")]
|
||||
public Uri ReceivedEventsUrl { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("site_admin")]
|
||||
public bool SiteAdmin { get; set; }
|
||||
}
|
||||
|
||||
public class BaseCommitCommit
|
||||
{
|
||||
[JsonProperty("author")]
|
||||
public PurpleAuthor Author { get; set; }
|
||||
|
||||
[JsonProperty("committer")]
|
||||
public PurpleAuthor Committer { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonProperty("tree")]
|
||||
public Tree Tree { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("comment_count")]
|
||||
public long CommentCount { get; set; }
|
||||
|
||||
[JsonProperty("verification")]
|
||||
public Verification Verification { get; set; }
|
||||
}
|
||||
|
||||
public class PurpleAuthor
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("date")]
|
||||
public DateTimeOffset Date { get; set; }
|
||||
}
|
||||
|
||||
public class Tree
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
}
|
||||
|
||||
public class Verification
|
||||
{
|
||||
[JsonProperty("verified")]
|
||||
public bool Verified { get; set; }
|
||||
|
||||
[JsonProperty("reason")]
|
||||
public string Reason { get; set; }
|
||||
|
||||
[JsonProperty("signature")]
|
||||
public string Signature { get; set; }
|
||||
|
||||
[JsonProperty("payload")]
|
||||
public string Payload { get; set; }
|
||||
}
|
||||
|
||||
public class Parent
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public Uri HtmlUrl { get; set; }
|
||||
}
|
||||
|
||||
public class File
|
||||
{
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("filename")]
|
||||
public string Filename { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("additions")]
|
||||
public long Additions { get; set; }
|
||||
|
||||
[JsonProperty("deletions")]
|
||||
public long Deletions { get; set; }
|
||||
|
||||
[JsonProperty("changes")]
|
||||
public long Changes { get; set; }
|
||||
|
||||
[JsonProperty("blob_url")]
|
||||
public Uri BlobUrl { get; set; }
|
||||
|
||||
[JsonProperty("raw_url")]
|
||||
public Uri RawUrl { get; set; }
|
||||
|
||||
[JsonProperty("contents_url")]
|
||||
public Uri ContentsUrl { get; set; }
|
||||
|
||||
[JsonProperty("patch")]
|
||||
public string Patch { get; set; }
|
||||
|
||||
[JsonProperty("previous_filename", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string PreviousFilename { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Artemis.UI.Windows.Providers;
|
||||
using Ninject.Modules;
|
||||
@ -14,6 +15,8 @@ public class WindowsModule : NinjectModule
|
||||
{
|
||||
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
|
||||
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
|
||||
Kernel!.Bind<IUpdateProvider>().To<UpdateProvider>().InSingletonScope();
|
||||
Kernel!.Bind<IAutoRunProvider>().To<AutoRunProvider>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
126
src/Artemis.UI.Windows/Providers/AutoRunProvider.cs
Normal file
126
src/Artemis.UI.Windows/Providers/AutoRunProvider.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace Artemis.UI.Windows.Providers;
|
||||
|
||||
public class AutoRunProvider : IAutoRunProvider
|
||||
{
|
||||
private readonly IAssetLoader _assetLoader;
|
||||
|
||||
public AutoRunProvider(IAssetLoader assetLoader)
|
||||
{
|
||||
_assetLoader = assetLoader;
|
||||
}
|
||||
|
||||
private async Task<bool> IsAutoRunTaskCreated()
|
||||
{
|
||||
Process schtasks = new()
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||
Arguments = "/TN \"Artemis 2 autorun\""
|
||||
}
|
||||
};
|
||||
|
||||
schtasks.Start();
|
||||
await schtasks.WaitForExitAsync();
|
||||
return schtasks.ExitCode == 0;
|
||||
}
|
||||
|
||||
private async Task CreateAutoRunTask(TimeSpan autoRunDelay)
|
||||
{
|
||||
await using Stream taskFile = _assetLoader.Open(new Uri("avares://Artemis 2/Assets/autorun.xml"));
|
||||
|
||||
XDocument document = await XDocument.LoadAsync(taskFile, LoadOptions.None, CancellationToken.None);
|
||||
XElement task = document.Descendants().First();
|
||||
|
||||
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Date")
|
||||
.SetValue(DateTime.Now);
|
||||
task.Descendants().First(d => d.Name.LocalName == "RegistrationInfo").Descendants().First(d => d.Name.LocalName == "Author")
|
||||
.SetValue(WindowsIdentity.GetCurrent().Name);
|
||||
|
||||
task.Descendants().First(d => d.Name.LocalName == "Triggers").Descendants().First(d => d.Name.LocalName == "LogonTrigger").Descendants().First(d => d.Name.LocalName == "Delay")
|
||||
.SetValue(autoRunDelay);
|
||||
|
||||
task.Descendants().First(d => d.Name.LocalName == "Principals").Descendants().First(d => d.Name.LocalName == "Principal").Descendants().First(d => d.Name.LocalName == "UserId")
|
||||
.SetValue(WindowsIdentity.GetCurrent().User!.Value);
|
||||
|
||||
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "WorkingDirectory")
|
||||
.SetValue(Constants.ApplicationFolder);
|
||||
task.Descendants().First(d => d.Name.LocalName == "Actions").Descendants().First(d => d.Name.LocalName == "Exec").Descendants().First(d => d.Name.LocalName == "Command")
|
||||
.SetValue("\"" + Constants.ExecutablePath + "\"");
|
||||
|
||||
string xmlPath = Path.GetTempFileName();
|
||||
await using (Stream fileStream = new FileStream(xmlPath, FileMode.Create))
|
||||
await document.SaveAsync(fileStream, SaveOptions.None, CancellationToken.None);
|
||||
|
||||
Process schtasks = new()
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||
Arguments = $"/Create /XML \"{xmlPath}\" /tn \"Artemis 2 autorun\" /F"
|
||||
}
|
||||
};
|
||||
|
||||
schtasks.Start();
|
||||
await schtasks.WaitForExitAsync();
|
||||
|
||||
File.Delete(xmlPath);
|
||||
}
|
||||
|
||||
private async Task RemoveAutoRunTask()
|
||||
{
|
||||
Process schtasks = new()
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
FileName = Path.Combine(Environment.SystemDirectory, "schtasks.exe"),
|
||||
Arguments = "/Delete /TN \"Artemis 2 autorun\" /f"
|
||||
}
|
||||
};
|
||||
|
||||
schtasks.Start();
|
||||
await schtasks.WaitForExitAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task EnableAutoRun(bool recreate, int autoRunDelay)
|
||||
{
|
||||
// if (Constants.BuildInfo.IsLocalBuild)
|
||||
// return;
|
||||
|
||||
// Create or remove the task if necessary
|
||||
bool taskCreated = false;
|
||||
if (!recreate)
|
||||
taskCreated = await IsAutoRunTaskCreated();
|
||||
if (!taskCreated)
|
||||
await CreateAutoRunTask(TimeSpan.FromSeconds(autoRunDelay));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DisableAutoRun()
|
||||
{
|
||||
bool taskCreated = await IsAutoRunTaskCreated();
|
||||
if (taskCreated)
|
||||
await RemoveAutoRunTask();
|
||||
}
|
||||
}
|
||||
@ -11,9 +11,9 @@ public class CursorProvider : ICursorProvider
|
||||
{
|
||||
public CursorProvider(IAssetLoader assetLoader)
|
||||
{
|
||||
Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
|
||||
Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
|
||||
DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
|
||||
Rotate = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_rotate.png"))), new PixelPoint(21, 10));
|
||||
Drag = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
|
||||
DragHorizontal = new Cursor(new Bitmap(assetLoader.Open(new Uri("avares://Artemis 2/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
|
||||
}
|
||||
|
||||
public Cursor Rotate { get; }
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using Artemis.UI.Windows.SkiaSharp;
|
||||
|
||||
@ -9,18 +8,11 @@ public class GraphicsContextProvider : IGraphicsContextProvider
|
||||
{
|
||||
private VulkanContext? _vulkanContext;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<string> GraphicsContextNames => new List<string> {"Vulkan"}.AsReadOnly();
|
||||
public string GraphicsContextName => "Vulkan";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IManagedGraphicsContext? GetGraphicsContext(string name)
|
||||
public IManagedGraphicsContext? GetGraphicsContext()
|
||||
{
|
||||
if (name == "Vulkan")
|
||||
{
|
||||
_vulkanContext ??= new VulkanContext();
|
||||
return _vulkanContext;
|
||||
}
|
||||
|
||||
return null;
|
||||
_vulkanContext ??= new VulkanContext();
|
||||
return _vulkanContext;
|
||||
}
|
||||
}
|
||||
216
src/Artemis.UI.Windows/Providers/UpdateProvider.cs
Normal file
216
src/Artemis.UI.Windows/Providers/UpdateProvider.cs
Normal file
@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.MainWindow;
|
||||
using Artemis.UI.Windows.Models;
|
||||
using Artemis.UI.Windows.Screens.Update;
|
||||
using Avalonia.Threading;
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using Serilog;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Artemis.UI.Windows.Providers;
|
||||
|
||||
public class UpdateProvider : IUpdateProvider, IDisposable
|
||||
{
|
||||
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
|
||||
private const string InstallerUrl = "https://builds.artemis-rgb.com/binaries/Artemis.Installer.exe";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IMainWindowService _mainWindowService;
|
||||
|
||||
public UpdateProvider(ILogger logger, IWindowService windowService, IMainWindowService mainWindowService)
|
||||
{
|
||||
_logger = logger;
|
||||
_windowService = windowService;
|
||||
_mainWindowService = mainWindowService;
|
||||
|
||||
ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated;
|
||||
}
|
||||
|
||||
private async void ToastNotificationManagerCompatOnOnActivated(ToastNotificationActivatedEventArgsCompat e)
|
||||
{
|
||||
ToastArguments args = ToastArguments.Parse(e.Argument);
|
||||
string channel = args.Get("channel");
|
||||
string action = "view-changes";
|
||||
if (args.Contains("action"))
|
||||
action = args.Get("action");
|
||||
|
||||
if (action == "install")
|
||||
await RunInstaller(channel, true);
|
||||
else if (action == "view-changes")
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
_mainWindowService.OpenMainWindow();
|
||||
await OfferUpdate(channel, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunInstaller(string channel, bool silent)
|
||||
{
|
||||
_logger.Information("ApplyUpdate: Applying update");
|
||||
|
||||
// Ensure the installer is up-to-date, get installer build info
|
||||
DevOpsBuild? buildInfo = await GetBuildInfo(6);
|
||||
string installerPath = Path.Combine(Core.Constants.DataFolder, "installer", "Artemis.Installer.exe");
|
||||
|
||||
// Always update installer if it is missing ^^
|
||||
if (!File.Exists(installerPath))
|
||||
await UpdateInstaller();
|
||||
// Compare the creation date of the installer with the build date and update if needed
|
||||
else
|
||||
{
|
||||
if (buildInfo != null && File.GetLastWriteTime(installerPath) < buildInfo.FinishTime)
|
||||
await UpdateInstaller();
|
||||
}
|
||||
|
||||
_logger.Information("ApplyUpdate: Running installer at {InstallerPath}", installerPath);
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(installerPath, "-autoupdate")
|
||||
{
|
||||
UseShellExecute = true,
|
||||
Verb = "runas"
|
||||
});
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
if (e.NativeErrorCode == 0x4c7)
|
||||
_logger.Warning("ApplyUpdate: Operation was cancelled, user likely clicked No in UAC dialog");
|
||||
else
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateInstaller()
|
||||
{
|
||||
string installerDirectory = Path.Combine(Core.Constants.DataFolder, "installer");
|
||||
string installerPath = Path.Combine(installerDirectory, "Artemis.Installer.exe");
|
||||
|
||||
_logger.Information("UpdateInstaller: Downloading installer from {DownloadUrl}", InstallerUrl);
|
||||
using HttpClient client = new();
|
||||
HttpResponseMessage httpResponseMessage = await client.GetAsync(InstallerUrl);
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
throw new ArtemisUIException($"Failed to download installer, status code {httpResponseMessage.StatusCode}");
|
||||
|
||||
_logger.Information("UpdateInstaller: Writing installer file to {InstallerPath}", installerPath);
|
||||
if (File.Exists(installerPath))
|
||||
File.Delete(installerPath);
|
||||
|
||||
Core.Utilities.CreateAccessibleDirectory(installerDirectory);
|
||||
await using FileStream fs = new(installerPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
await httpResponseMessage.Content.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> CheckForUpdate(string channel)
|
||||
{
|
||||
DevOpsBuild? buildInfo = await GetBuildInfo(1);
|
||||
if (buildInfo == null)
|
||||
return false;
|
||||
|
||||
double buildNumber = double.Parse(buildInfo.BuildNumber, CultureInfo.InvariantCulture);
|
||||
string buildNumberDisplay = buildNumber.ToString(CultureInfo.InvariantCulture);
|
||||
_logger.Information("Latest build is {BuildNumber}, we're running {LocalBuildNumber}", buildNumberDisplay, Core.Constants.BuildInfo.BuildNumberDisplay);
|
||||
|
||||
return buildNumber > Core.Constants.BuildInfo.BuildNumber;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ApplyUpdate(string channel, bool silent)
|
||||
{
|
||||
await RunInstaller(channel, silent);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task OfferUpdate(string channel, bool windowOpen)
|
||||
{
|
||||
if (windowOpen)
|
||||
{
|
||||
bool update = await _windowService.ShowDialogAsync<UpdateDialogViewModel, bool>(("channel", channel));
|
||||
if (update)
|
||||
await RunInstaller(channel, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowDesktopNotification(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowDesktopNotification(string channel)
|
||||
{
|
||||
new ToastContentBuilder()
|
||||
.AddArgument("channel", channel)
|
||||
.AddText("An update is available")
|
||||
.AddButton(new ToastButton().SetContent("Install").AddArgument("action", "install").SetBackgroundActivation())
|
||||
.AddButton(new ToastButton().SetContent("View changes").AddArgument("action", "view-changes"))
|
||||
.AddHeroImage(new Uri(@"C:\Repos\Artemis\src\Artemis.UI\Assets\Images\home-banner.png"))
|
||||
.Show();
|
||||
}
|
||||
|
||||
public async Task<DevOpsBuild?> GetBuildInfo(int buildDefinition, string? buildNumber = null)
|
||||
{
|
||||
Url request = ApiUrl.AppendPathSegments("build", "builds")
|
||||
.SetQueryParam("definitions", buildDefinition)
|
||||
.SetQueryParam("resultFilter", "succeeded")
|
||||
.SetQueryParam("$top", 1)
|
||||
.SetQueryParam("api-version", "6.1-preview.6");
|
||||
|
||||
if (buildNumber != null)
|
||||
request = request.SetQueryParam("buildNumber", buildNumber);
|
||||
|
||||
try
|
||||
{
|
||||
DevOpsBuilds result = await request.GetJsonAsync<DevOpsBuilds>();
|
||||
try
|
||||
{
|
||||
return result.Builds.FirstOrDefault();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (FlurlHttpException e)
|
||||
{
|
||||
_logger.Warning("GetBuildInfo: Getting build info, request returned {StatusCode}", e.StatusCode);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<GitHubDifference> GetBuildDifferences(DevOpsBuild a, DevOpsBuild b)
|
||||
{
|
||||
return await "https://api.github.com"
|
||||
.AppendPathSegments("repos", "Artemis-RGB", "Artemis", "compare")
|
||||
.AppendPathSegment(a.SourceVersion + "..." + b.SourceVersion)
|
||||
.WithHeader("User-Agent", "Artemis 2")
|
||||
.WithHeader("Accept", "application/vnd.github.v3+json")
|
||||
.GetJsonAsync<GitHubDifference>();
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
ToastNotificationManagerCompat.OnActivated -= ToastNotificationManagerCompatOnOnActivated;
|
||||
ToastNotificationManagerCompat.Uninstall();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
70
src/Artemis.UI.Windows/Screens/Update/UpdateDialogView.axaml
Normal file
70
src/Artemis.UI.Windows/Screens/Update/UpdateDialogView.axaml
Normal file
@ -0,0 +1,70 @@
|
||||
<Window 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"
|
||||
xmlns:update="clr-namespace:Artemis.UI.Windows.Screens.Update"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Windows.Screens.Update.UpdateDialogView"
|
||||
x:DataType="update:UpdateDialogViewModel"
|
||||
Title="Artemis | Update available"
|
||||
Width="750"
|
||||
MinWidth="750"
|
||||
Height="500"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
|
||||
|
||||
<TextBlock Grid.Row="0" Classes="h4">
|
||||
A new Artemis update is available! 🥳
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding RetrievingChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<TextBlock>Retrieving changes...</TextBlock>
|
||||
<ProgressBar IsIndeterminate="True"></ProgressBar>
|
||||
</StackPanel>
|
||||
|
||||
<Border Grid.Row="1" Classes="card" IsVisible="{CompiledBinding !RetrievingChanges}">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="You are currently running build " />
|
||||
<TextBlock Text="{CompiledBinding CurrentBuild, Mode=OneWay}"></TextBlock>
|
||||
<TextBlock Text=" while the latest build is " />
|
||||
<TextBlock Text="{CompiledBinding LatestBuild, Mode=OneWay}"></TextBlock>
|
||||
<TextBlock Text="." />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="Updating Artemis will give you the latest bug(fixes), features and improvements." />
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<TextBlock Classes="h5" IsVisible="{CompiledBinding HasChanges}">
|
||||
Changelog (auto-generated)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalAlignment="Top" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" IsVisible="{CompiledBinding HasChanges}">
|
||||
<ItemsControl Items="{CompiledBinding Changes}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<TextBlock Text="•" Margin="10 0" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding}" TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding !HasChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<TextBlock HorizontalAlignment="Center">We couldn't retrieve any changes</TextBlock>
|
||||
<controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis/commits/master" HorizontalAlignment="Center">View online</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Spacing="5" Grid.Row="2" Margin="0 15 0 0">
|
||||
<Button Classes="accent" Command="{CompiledBinding Install}">Install update</Button>
|
||||
<Button Command="{CompiledBinding AskLater}">Ask later</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@ -0,0 +1,22 @@
|
||||
using Artemis.UI.Shared;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Windows.Screens.Update;
|
||||
|
||||
public partial class UpdateDialogView : ReactiveCoreWindow<UpdateDialogViewModel>
|
||||
{
|
||||
public UpdateDialogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
119
src/Artemis.UI.Windows/Screens/Update/UpdateDialogViewModel.cs
Normal file
119
src/Artemis.UI.Windows/Screens/Update/UpdateDialogViewModel.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Windows.Models;
|
||||
using Artemis.UI.Windows.Providers;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Windows.Screens.Update;
|
||||
|
||||
public class UpdateDialogViewModel : DialogViewModelBase<bool>
|
||||
{
|
||||
private readonly UpdateProvider _updateProvider;
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
// Based on https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#skipping-ci-for-individual-commits
|
||||
private readonly string[] _excludedCommitMessages =
|
||||
{
|
||||
"[skip ci]",
|
||||
"[ci skip]",
|
||||
"skip-checks: true",
|
||||
"skip-checks:true",
|
||||
"[skip azurepipelines]",
|
||||
"[azurepipelines skip]",
|
||||
"[skip azpipelines]",
|
||||
"[azpipelines skip]",
|
||||
"[skip azp]",
|
||||
"[azp skip]",
|
||||
"***NO_CI***"
|
||||
};
|
||||
|
||||
private bool _retrievingChanges;
|
||||
private bool _hasChanges;
|
||||
private string? _latestBuild;
|
||||
|
||||
public UpdateDialogViewModel(string channel, IUpdateProvider updateProvider, INotificationService notificationService)
|
||||
{
|
||||
_updateProvider = (UpdateProvider) updateProvider;
|
||||
_notificationService = notificationService;
|
||||
|
||||
Channel = channel;
|
||||
CurrentBuild = Core.Constants.BuildInfo.BuildNumberDisplay;
|
||||
|
||||
this.WhenActivated((CompositeDisposable _) => Dispatcher.UIThread.InvokeAsync(GetBuildChanges));
|
||||
Install = ReactiveCommand.Create(() => Close(true));
|
||||
AskLater = ReactiveCommand.Create(() => Close(false));
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> Install { get; }
|
||||
public ReactiveCommand<Unit, Unit> AskLater { get; }
|
||||
|
||||
public string Channel { get; }
|
||||
public string CurrentBuild { get; }
|
||||
|
||||
public ObservableCollection<string> Changes { get; } = new();
|
||||
|
||||
public bool RetrievingChanges
|
||||
{
|
||||
get => _retrievingChanges;
|
||||
set => RaiseAndSetIfChanged(ref _retrievingChanges, value);
|
||||
}
|
||||
|
||||
public bool HasChanges
|
||||
{
|
||||
get => _hasChanges;
|
||||
set => RaiseAndSetIfChanged(ref _hasChanges, value);
|
||||
}
|
||||
|
||||
public string? LatestBuild
|
||||
{
|
||||
get => _latestBuild;
|
||||
set => RaiseAndSetIfChanged(ref _latestBuild, value);
|
||||
}
|
||||
|
||||
private async Task GetBuildChanges()
|
||||
{
|
||||
try
|
||||
{
|
||||
RetrievingChanges = true;
|
||||
Task<DevOpsBuild?> currentTask = _updateProvider.GetBuildInfo(1, CurrentBuild);
|
||||
Task<DevOpsBuild?> latestTask = _updateProvider.GetBuildInfo(1);
|
||||
|
||||
DevOpsBuild? current = await currentTask;
|
||||
DevOpsBuild? latest = await latestTask;
|
||||
|
||||
LatestBuild = latest?.BuildNumber;
|
||||
if (current != null && latest != null)
|
||||
{
|
||||
GitHubDifference difference = await _updateProvider.GetBuildDifferences(current, latest);
|
||||
|
||||
// Only take commits with one parents (no merges)
|
||||
Changes.Clear();
|
||||
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
|
||||
.SelectMany(c => c.Commit.Message.Split("\n"))
|
||||
.Select(m => m.Trim())
|
||||
.Where(m => !string.IsNullOrWhiteSpace(m) && !_excludedCommitMessages.Contains(m))
|
||||
.OrderBy(m => m)
|
||||
);
|
||||
HasChanges = Changes.Any();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.CreateNotification().WithTitle("Failed to retrieve build changes").WithMessage(e.Message).WithSeverity(NotificationSeverity.Error).Show();
|
||||
}
|
||||
finally
|
||||
{
|
||||
RetrievingChanges = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net6.0-windows7.0": {
|
||||
"net6.0-windows10.0.17763": {
|
||||
"Avalonia": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.10.15, )",
|
||||
@ -65,6 +65,18 @@
|
||||
"System.Numerics.Vectors": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Toolkit.Uwp.Notifications": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.1.2, )",
|
||||
"resolved": "7.1.2",
|
||||
"contentHash": "cVBRDG8g0K4sBRHFJoNQjwZduDUz+cXP33erIQjAa8fGKF4DsWIuOAQCYGFoXpdXIXjxoaUYuGSxAG6QCvOdtQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.Registry": "4.7.0",
|
||||
"System.Drawing.Common": "4.7.0",
|
||||
"System.Reflection.Emit": "4.7.0",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.1, )",
|
||||
@ -461,12 +473,21 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.Registry": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==",
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "4.7.0",
|
||||
"System.Security.Principal.Windows": "4.7.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
"Microsoft.NETCore.Platforms": "3.1.0"
|
||||
}
|
||||
},
|
||||
"NETStandard.Library": {
|
||||
@ -977,11 +998,11 @@
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "AiJFxxVPdeITstiRS5aAu8+8Dpf5NawTMoapZ53Gfirml24p7HIfhjmCRxdXnmmf3IUA3AX3CcW7G73CjWxW/Q==",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0",
|
||||
"Microsoft.Win32.SystemEvents": "4.5.0"
|
||||
"Microsoft.NETCore.Platforms": "3.1.0",
|
||||
"Microsoft.Win32.SystemEvents": "4.7.0"
|
||||
}
|
||||
},
|
||||
"System.Dynamic.Runtime": {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 172 KiB |
@ -21,14 +21,15 @@ namespace Artemis.UI.Screens.Root
|
||||
{
|
||||
public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvider
|
||||
{
|
||||
private readonly IAssetLoader _assetLoader;
|
||||
private readonly DefaultTitleBarViewModel _defaultTitleBarViewModel;
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IDebugService _debugService;
|
||||
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ISidebarVmFactory _sidebarVmFactory;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IDebugService _debugService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private readonly IAssetLoader _assetLoader;
|
||||
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
|
||||
private readonly ISidebarVmFactory _sidebarVmFactory;
|
||||
private SidebarViewModel? _sidebarViewModel;
|
||||
private ViewModelBase? _titleBarViewModel;
|
||||
|
||||
@ -38,6 +39,7 @@ namespace Artemis.UI.Screens.Root
|
||||
IWindowService windowService,
|
||||
IMainWindowService mainWindowService,
|
||||
IDebugService debugService,
|
||||
IUpdateService updateService,
|
||||
IAssetLoader assetLoader,
|
||||
DefaultTitleBarViewModel defaultTitleBarViewModel,
|
||||
ISidebarVmFactory sidebarVmFactory)
|
||||
@ -48,6 +50,7 @@ namespace Artemis.UI.Screens.Root
|
||||
_settingsService = settingsService;
|
||||
_windowService = windowService;
|
||||
_debugService = debugService;
|
||||
_updateService = updateService;
|
||||
_assetLoader = assetLoader;
|
||||
_defaultTitleBarViewModel = defaultTitleBarViewModel;
|
||||
_sidebarVmFactory = sidebarVmFactory;
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
|
||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:behaviors="clr-namespace:Artemis.UI.Shared.Behaviors;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
|
||||
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
|
||||
x:DataType="settings:GeneralTabViewModel">
|
||||
@ -18,40 +20,46 @@
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Auto-run on startup</TextBlock>
|
||||
</StackPanel>
|
||||
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
<StackPanel IsVisible="{CompiledBinding IsAutoRunSupported}">
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Auto-run on startup</TextBlock>
|
||||
</StackPanel>
|
||||
<ToggleSwitch Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" IsChecked="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Hide window on auto-run</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Hide window on auto-run</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding !UIShowOnStartup.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" MinWidth="0" Margin="0 -10"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Startup delay</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Set the amount of seconds to wait before auto-running Artemis.
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<TextBox Text="{CompiledBinding UIAutoRunDelay.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120" />
|
||||
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>Startup delay</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
Set the amount of seconds to wait before auto-running Artemis.
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<controls:NumberBox IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120">
|
||||
<Interaction.Behaviors>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding UIAutoRunDelay.Value}"/>
|
||||
</Interaction.Behaviors>
|
||||
</controls:NumberBox>
|
||||
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
@ -103,48 +111,69 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBox Text="{CompiledBinding WebServerPort.Value}" Width="150" />
|
||||
<controls:NumberBox Width="150">
|
||||
<Interaction.Behaviors>
|
||||
<behaviors:LostFocusNumberBoxBindingBehavior Value="{CompiledBinding WebServerPort.Value}"/>
|
||||
</Interaction.Behaviors>
|
||||
</controls:NumberBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Update settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Updating
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Check for updates
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If enabled, we'll check for updates on startup and periodically while running.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
<StackPanel IsVisible="{CompiledBinding IsUpdatingSupported}">
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
Updating
|
||||
</TextBlock>
|
||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Check for updates
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If enabled, we'll check for updates on startup and periodically while running.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsChecked="{CompiledBinding UICheckForUpdates.Value}" MinWidth="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock>
|
||||
Auto-install updates
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" TextWrapping="Wrap">
|
||||
If enabled, new updates will automatically be installed.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ToggleSwitch IsEnabled="{CompiledBinding UICheckForUpdates.Value}" IsChecked="{CompiledBinding UIAutoUpdate.Value}" MinWidth="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Classes="card-separator" />
|
||||
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Update
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Use the button on the right to check for updates now.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{Binding OfferUpdatesIfFound}" Width="150" Content="Check now" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||
<TextBlock>
|
||||
Update
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
Use the button on the right to check for updates now.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<Button Command="{CompiledBinding CheckForUpdate}" Width="150" Content="Check now" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Profile editor settings -->
|
||||
<TextBlock Classes="h4" Margin="0 15">
|
||||
|
||||
@ -9,14 +9,15 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.LayerBrushes;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.StartupWizard;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using FluentAvalonia.Styling;
|
||||
using Ninject;
|
||||
using ReactiveUI;
|
||||
using Serilog.Events;
|
||||
@ -29,20 +30,28 @@ namespace Artemis.UI.Screens.Settings
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDebugService _debugService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private readonly IAutoRunProvider? _autoRunProvider;
|
||||
|
||||
public GeneralTabViewModel(IKernel kernel, ISettingsService settingsService, IPluginManagementService pluginManagementService, IDebugService debugService, IWindowService windowService)
|
||||
public GeneralTabViewModel(IKernel kernel,
|
||||
ISettingsService settingsService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IDebugService debugService,
|
||||
IWindowService windowService,
|
||||
IUpdateService updateService)
|
||||
{
|
||||
DisplayName = "General";
|
||||
_settingsService = settingsService;
|
||||
_debugService = debugService;
|
||||
_windowService = windowService;
|
||||
_updateService = updateService;
|
||||
_autoRunProvider = kernel.TryGet<IAutoRunProvider>();
|
||||
|
||||
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
|
||||
List<IGraphicsContextProvider> graphicsContextProviders = kernel.Get<List<IGraphicsContextProvider>>();
|
||||
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
|
||||
GraphicsContexts = new ObservableCollection<string> {"Software"};
|
||||
IGraphicsContextProvider? graphicsContextProvider = kernel.TryGet<IGraphicsContextProvider>();
|
||||
if (graphicsContextProvider != null)
|
||||
GraphicsContexts.AddRange(graphicsContextProvider.GraphicsContextNames);
|
||||
GraphicsContexts.AddRange(graphicsContextProviders.Select(p => p.GraphicsContextName));
|
||||
|
||||
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||
{
|
||||
@ -55,6 +64,21 @@ namespace Artemis.UI.Screens.Settings
|
||||
ShowSetupWizard = ReactiveCommand.CreateFromTask(ExecuteShowSetupWizard);
|
||||
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
|
||||
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder);
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
UIAutoRun.SettingChanged += UIAutoRunOnSettingChanged;
|
||||
UIAutoRunDelay.SettingChanged += UIAutoRunDelayOnSettingChanged;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(ApplyAutoRun);
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
UIAutoRun.SettingChanged -= UIAutoRunOnSettingChanged;
|
||||
UIAutoRunDelay.SettingChanged -= UIAutoRunDelayOnSettingChanged;
|
||||
|
||||
_settingsService.SaveAllSettings();
|
||||
}).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ShowLogs { get; }
|
||||
@ -63,6 +87,9 @@ namespace Artemis.UI.Screens.Settings
|
||||
public ReactiveCommand<Unit, Unit> ShowDebugger { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShowDataFolder { get; }
|
||||
|
||||
public bool IsAutoRunSupported => _autoRunProvider != null;
|
||||
public bool IsUpdatingSupported => _updateService.UpdatingSupported;
|
||||
|
||||
public ObservableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
|
||||
public ObservableCollection<string> GraphicsContexts { get; }
|
||||
|
||||
@ -116,6 +143,7 @@ namespace Artemis.UI.Screens.Settings
|
||||
public PluginSetting<int> UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15);
|
||||
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
|
||||
public PluginSetting<bool> UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", true);
|
||||
public PluginSetting<bool> UIAutoUpdate => _settingsService.GetSetting("UI.AutoUpdate", false);
|
||||
public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
|
||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
||||
@ -123,26 +151,17 @@ namespace Artemis.UI.Screens.Settings
|
||||
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
||||
|
||||
#region General
|
||||
|
||||
private void ExecuteShowLogs()
|
||||
{
|
||||
OpenFolder(Constants.LogsFolder);
|
||||
Utilities.OpenFolder(Constants.LogsFolder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Updating
|
||||
|
||||
private Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
|
||||
private async Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
await _updateService.ManualUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
private async Task ExecuteShowSetupWizard()
|
||||
{
|
||||
await _windowService.ShowDialogAsync<StartupWizardViewModel, bool>();
|
||||
@ -153,19 +172,46 @@ namespace Artemis.UI.Screens.Settings
|
||||
_debugService.ShowDebugger();
|
||||
}
|
||||
|
||||
|
||||
private void ExecuteShowDataFolder()
|
||||
{
|
||||
OpenFolder(Constants.DataFolder);
|
||||
Utilities.OpenFolder(Constants.DataFolder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OpenFolder(string path)
|
||||
private async Task ApplyAutoRun()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
if (_autoRunProvider == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path);
|
||||
if (UIAutoRun.Value)
|
||||
await _autoRunProvider.EnableAutoRun(false, UIAutoRunDelay.Value);
|
||||
else
|
||||
await _autoRunProvider.DisableAutoRun();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("Failed to apply auto-run", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private async void UIAutoRunOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
await ApplyAutoRun();
|
||||
}
|
||||
|
||||
private async void UIAutoRunDelayOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (_autoRunProvider == null || !UIAutoRun.Value)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await _autoRunProvider.EnableAutoRun(true, UIAutoRunDelay.Value);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_windowService.ShowExceptionDialog("Failed to apply auto-run", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
src/Artemis.UI/Services/Interfaces/IUpdateService.cs
Normal file
22
src/Artemis.UI/Services/Interfaces/IUpdateService.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Artemis.UI.Services.Interfaces;
|
||||
|
||||
public interface IUpdateService : IArtemisUIService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether updating is supported.
|
||||
/// </summary>
|
||||
bool UpdatingSupported { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether auto-updating is suspended.
|
||||
/// </summary>
|
||||
bool SuspendAutoUpdate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Manually checks for updates and offers to install it if found.
|
||||
/// </summary>
|
||||
/// <returns>Whether an update was found, regardless of whether the user chose to install it.</returns>
|
||||
Task ManualUpdate();
|
||||
}
|
||||
113
src/Artemis.UI/Services/UpdateService.cs
Normal file
113
src/Artemis.UI/Services/UpdateService.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Services.Interfaces;
|
||||
using Artemis.UI.Shared.Providers;
|
||||
using Artemis.UI.Shared.Services.MainWindow;
|
||||
using Avalonia.Threading;
|
||||
using Ninject;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.UI.Services;
|
||||
|
||||
public class UpdateService : IUpdateService
|
||||
{
|
||||
private const double UPDATE_CHECK_INTERVAL = 10000; // once per hour
|
||||
|
||||
private readonly PluginSetting<bool> _autoUpdate;
|
||||
private readonly PluginSetting<bool> _checkForUpdates;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMainWindowService _mainWindowService;
|
||||
private readonly IUpdateProvider? _updateProvider;
|
||||
|
||||
public UpdateService(ILogger logger, IKernel kernel, ISettingsService settingsService, IMainWindowService mainWindowService)
|
||||
{
|
||||
_logger = logger;
|
||||
_mainWindowService = mainWindowService;
|
||||
|
||||
if (!Constants.BuildInfo.IsLocalBuild)
|
||||
_updateProvider = kernel.TryGet<IUpdateProvider>();
|
||||
|
||||
_checkForUpdates = settingsService.GetSetting("UI.CheckForUpdates", true);
|
||||
_autoUpdate = settingsService.GetSetting("UI.AutoUpdate", false);
|
||||
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
|
||||
_mainWindowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
|
||||
|
||||
Timer timer = new(UPDATE_CHECK_INTERVAL);
|
||||
timer.Elapsed += TimerOnElapsed;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private async void TimerOnElapsed(object? sender, ElapsedEventArgs e)
|
||||
{
|
||||
await AutoUpdate();
|
||||
}
|
||||
|
||||
private async void CheckForUpdatesOnSettingChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// Run an auto-update as soon as the setting gets changed to enabled
|
||||
if (_checkForUpdates.Value)
|
||||
await AutoUpdate();
|
||||
}
|
||||
|
||||
private async void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||
{
|
||||
// await AutoUpdate();
|
||||
}
|
||||
|
||||
private async Task AutoUpdate()
|
||||
{
|
||||
if (_updateProvider == null || !_checkForUpdates.Value || SuspendAutoUpdate)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
bool updateAvailable = await _updateProvider.CheckForUpdate("master");
|
||||
if (!updateAvailable)
|
||||
return;
|
||||
|
||||
// Only offer it once per session
|
||||
SuspendAutoUpdate = true;
|
||||
|
||||
// If the window is open show the changelog, don't auto-update while the user is busy
|
||||
if (_mainWindowService.IsMainWindowOpen)
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
// Call OpenMainWindow anyway to focus the main window
|
||||
_mainWindowService.OpenMainWindow();
|
||||
await _updateProvider.OfferUpdate("master", true);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If the window is closed but auto-update is enabled, update silently
|
||||
if (_autoUpdate.Value)
|
||||
await _updateProvider.ApplyUpdate("master", true);
|
||||
// If auto-update is disabled the update provider can show a notification and handle the rest
|
||||
else
|
||||
await _updateProvider.OfferUpdate("master", false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "Auto update failed");
|
||||
}
|
||||
}
|
||||
|
||||
public bool SuspendAutoUpdate { get; set; }
|
||||
public bool UpdatingSupported => _updateProvider != null;
|
||||
|
||||
public async Task ManualUpdate()
|
||||
{
|
||||
if (_updateProvider == null || !_mainWindowService.IsMainWindowOpen)
|
||||
return;
|
||||
|
||||
bool updateAvailable = await _updateProvider.CheckForUpdate("master");
|
||||
if (!updateAvailable)
|
||||
return;
|
||||
|
||||
await _updateProvider.OfferUpdate("master", true);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user