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_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_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/=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/=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/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Ccolorquantizer_005Cinterfaces/@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.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Artemis.Core.DeviceProviders;
|
using Artemis.Core.DeviceProviders;
|
||||||
|
using Artemis.Core.Providers;
|
||||||
using Artemis.Core.Services.Models;
|
using Artemis.Core.Services.Models;
|
||||||
using Artemis.Core.SkiaSharp;
|
using Artemis.Core.SkiaSharp;
|
||||||
using Artemis.Storage.Entities.Surface;
|
using Artemis.Storage.Entities.Surface;
|
||||||
@ -363,15 +364,15 @@ namespace Artemis.Core.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IGraphicsContextProvider? provider = _kernel.TryGet<IGraphicsContextProvider>();
|
List<IGraphicsContextProvider> providers = _kernel.Get<List<IGraphicsContextProvider>>();
|
||||||
if (provider == null)
|
if (!providers.Any())
|
||||||
{
|
{
|
||||||
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
_logger.Warning("No graphics context provider found, defaulting to software rendering");
|
||||||
UpdateGraphicsContext(null);
|
UpdateGraphicsContext(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IManagedGraphicsContext? context = provider.GetGraphicsContext(_preferredGraphicsContext.Value);
|
IManagedGraphicsContext? context = providers.FirstOrDefault(p => p.GraphicsContextName == _preferredGraphicsContext.Value)?.GetGraphicsContext();
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
_logger.Warning("No graphics context named '{Context}' found, defaulting to software rendering", _preferredGraphicsContext.Value);
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
|
<OutputPath>bin</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AssemblyName>Artemis 2</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
|
<OutputPath>bin</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AssemblyName>Artemis 2</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
|||||||
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"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||||
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
|
x:Class="Artemis.UI.Shared.Services.ExceptionDialogView"
|
||||||
Icon="/Assets/Images/Logo/application.ico"
|
|
||||||
Title="{Binding Title}"
|
Title="{Binding Title}"
|
||||||
ExtendClientAreaToDecorationsHint="True"
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
Width="800"
|
Width="800"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Windows.Ninject;
|
using Artemis.UI.Windows.Ninject;
|
||||||
|
using Artemis.UI.Windows.Providers;
|
||||||
using Artemis.UI.Windows.Providers.Input;
|
using Artemis.UI.Windows.Providers.Input;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Platforms>x64</Platforms>
|
<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>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
@ -22,6 +28,9 @@
|
|||||||
<None Remove="Assets\Cursors\aero_rotate_br.cur" />
|
<None Remove="Assets\Cursors\aero_rotate_br.cur" />
|
||||||
<None Remove="Assets\Cursors\aero_rotate_tl.cur" />
|
<None Remove="Assets\Cursors\aero_rotate_tl.cur" />
|
||||||
<None Remove="Assets\Cursors\aero_rotate_tr.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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
<PackageReference Include="Avalonia" Version="0.10.15" />
|
||||||
@ -30,6 +39,7 @@
|
|||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.15" />
|
||||||
<PackageReference Include="Avalonia.Win32" 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="Microsoft.Win32" Version="2.0.1" />
|
||||||
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
|
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
|
||||||
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
<PackageReference Include="ReactiveUI" Version="17.1.50" />
|
||||||
|
|||||||
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.Shared.Providers;
|
||||||
using Artemis.UI.Windows.Providers;
|
using Artemis.UI.Windows.Providers;
|
||||||
using Ninject.Modules;
|
using Ninject.Modules;
|
||||||
@ -14,6 +15,8 @@ public class WindowsModule : NinjectModule
|
|||||||
{
|
{
|
||||||
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
|
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
|
||||||
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
|
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
|
||||||
|
Kernel!.Bind<IUpdateProvider>().To<UpdateProvider>().InSingletonScope();
|
||||||
|
Kernel!.Bind<IAutoRunProvider>().To<AutoRunProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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)
|
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));
|
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.UI.Windows/Assets/Cursors/aero_drag.png"))), new PixelPoint(11, 3));
|
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.UI.Windows/Assets/Cursors/aero_drag_horizontal.png"))), new PixelPoint(16, 5));
|
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; }
|
public Cursor Rotate { get; }
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using Artemis.Core.Providers;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Core.SkiaSharp;
|
using Artemis.Core.SkiaSharp;
|
||||||
using Artemis.UI.Windows.SkiaSharp;
|
using Artemis.UI.Windows.SkiaSharp;
|
||||||
|
|
||||||
@ -9,18 +8,11 @@ public class GraphicsContextProvider : IGraphicsContextProvider
|
|||||||
{
|
{
|
||||||
private VulkanContext? _vulkanContext;
|
private VulkanContext? _vulkanContext;
|
||||||
|
|
||||||
/// <inheritdoc />
|
public string GraphicsContextName => "Vulkan";
|
||||||
public IReadOnlyCollection<string> GraphicsContextNames => new List<string> {"Vulkan"}.AsReadOnly();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
public IManagedGraphicsContext? GetGraphicsContext()
|
||||||
public IManagedGraphicsContext? GetGraphicsContext(string name)
|
|
||||||
{
|
|
||||||
if (name == "Vulkan")
|
|
||||||
{
|
{
|
||||||
_vulkanContext ??= new VulkanContext();
|
_vulkanContext ??= new VulkanContext();
|
||||||
return _vulkanContext;
|
return _vulkanContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net6.0-windows7.0": {
|
"net6.0-windows10.0.17763": {
|
||||||
"Avalonia": {
|
"Avalonia": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[0.10.15, )",
|
"requested": "[0.10.15, )",
|
||||||
@ -65,6 +65,18 @@
|
|||||||
"System.Numerics.Vectors": "4.5.0"
|
"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": {
|
"Microsoft.Win32": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.0.1, )",
|
"requested": "[2.0.1, )",
|
||||||
@ -461,12 +473,21 @@
|
|||||||
"System.Runtime": "4.3.0"
|
"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": {
|
"Microsoft.Win32.SystemEvents": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==",
|
"contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
"Microsoft.NETCore.Platforms": "3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"NETStandard.Library": {
|
"NETStandard.Library": {
|
||||||
@ -977,11 +998,11 @@
|
|||||||
},
|
},
|
||||||
"System.Drawing.Common": {
|
"System.Drawing.Common": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "AiJFxxVPdeITstiRS5aAu8+8Dpf5NawTMoapZ53Gfirml24p7HIfhjmCRxdXnmmf3IUA3AX3CcW7G73CjWxW/Q==",
|
"contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.NETCore.Platforms": "2.0.0",
|
"Microsoft.NETCore.Platforms": "3.1.0",
|
||||||
"Microsoft.Win32.SystemEvents": "4.5.0"
|
"Microsoft.Win32.SystemEvents": "4.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Dynamic.Runtime": {
|
"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
|
public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvider
|
||||||
{
|
{
|
||||||
private readonly IAssetLoader _assetLoader;
|
|
||||||
private readonly DefaultTitleBarViewModel _defaultTitleBarViewModel;
|
private readonly DefaultTitleBarViewModel _defaultTitleBarViewModel;
|
||||||
private readonly ICoreService _coreService;
|
private readonly ICoreService _coreService;
|
||||||
private readonly IDebugService _debugService;
|
|
||||||
private readonly IClassicDesktopStyleApplicationLifetime _lifeTime;
|
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly ISidebarVmFactory _sidebarVmFactory;
|
|
||||||
private readonly IWindowService _windowService;
|
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 SidebarViewModel? _sidebarViewModel;
|
||||||
private ViewModelBase? _titleBarViewModel;
|
private ViewModelBase? _titleBarViewModel;
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ namespace Artemis.UI.Screens.Root
|
|||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
IMainWindowService mainWindowService,
|
IMainWindowService mainWindowService,
|
||||||
IDebugService debugService,
|
IDebugService debugService,
|
||||||
|
IUpdateService updateService,
|
||||||
IAssetLoader assetLoader,
|
IAssetLoader assetLoader,
|
||||||
DefaultTitleBarViewModel defaultTitleBarViewModel,
|
DefaultTitleBarViewModel defaultTitleBarViewModel,
|
||||||
ISidebarVmFactory sidebarVmFactory)
|
ISidebarVmFactory sidebarVmFactory)
|
||||||
@ -48,6 +50,7 @@ namespace Artemis.UI.Screens.Root
|
|||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_debugService = debugService;
|
_debugService = debugService;
|
||||||
|
_updateService = updateService;
|
||||||
_assetLoader = assetLoader;
|
_assetLoader = assetLoader;
|
||||||
_defaultTitleBarViewModel = defaultTitleBarViewModel;
|
_defaultTitleBarViewModel = defaultTitleBarViewModel;
|
||||||
_sidebarVmFactory = sidebarVmFactory;
|
_sidebarVmFactory = sidebarVmFactory;
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
|
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
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"
|
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="2400"
|
||||||
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
|
x:Class="Artemis.UI.Screens.Settings.GeneralTabView"
|
||||||
x:DataType="settings:GeneralTabViewModel">
|
x:DataType="settings:GeneralTabViewModel">
|
||||||
@ -18,6 +20,7 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
<Border Classes="card" VerticalAlignment="Stretch" Margin="0,0,5,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
<StackPanel IsVisible="{CompiledBinding IsAutoRunSupported}">
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock>Auto-run on startup</TextBlock>
|
<TextBlock>Auto-run on startup</TextBlock>
|
||||||
@ -47,11 +50,16 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
<TextBox Text="{CompiledBinding UIAutoRunDelay.Value}" IsEnabled="{CompiledBinding UIAutoRun.Value}" Width="120" />
|
<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>
|
<TextBlock VerticalAlignment="Center" TextAlignment="Right" Width="30">sec</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Classes="card-separator" />
|
<Separator Classes="card-separator" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
@ -103,13 +111,18 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<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>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Update settings -->
|
<!-- Update settings -->
|
||||||
|
<StackPanel IsVisible="{CompiledBinding IsUpdatingSupported}">
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
Updating
|
Updating
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
@ -130,6 +143,21 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Separator Classes="card-separator" />
|
<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">
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,Auto">
|
||||||
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="0" VerticalAlignment="Center">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
@ -140,11 +168,12 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
<Button Command="{Binding OfferUpdatesIfFound}" Width="150" Content="Check now" />
|
<Button Command="{CompiledBinding CheckForUpdate}" Width="150" Content="Check now" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Profile editor settings -->
|
<!-- Profile editor settings -->
|
||||||
<TextBlock Classes="h4" Margin="0 15">
|
<TextBlock Classes="h4" Margin="0 15">
|
||||||
|
|||||||
@ -9,14 +9,15 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
|
using Artemis.Core.Providers;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Screens.StartupWizard;
|
using Artemis.UI.Screens.StartupWizard;
|
||||||
using Artemis.UI.Services.Interfaces;
|
using Artemis.UI.Services.Interfaces;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Providers;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Avalonia;
|
using Avalonia.Threading;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using FluentAvalonia.Styling;
|
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
@ -29,20 +30,28 @@ namespace Artemis.UI.Screens.Settings
|
|||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IDebugService _debugService;
|
private readonly IDebugService _debugService;
|
||||||
private readonly IWindowService _windowService;
|
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";
|
DisplayName = "General";
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_debugService = debugService;
|
_debugService = debugService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
_updateService = updateService;
|
||||||
|
_autoRunProvider = kernel.TryGet<IAutoRunProvider>();
|
||||||
|
|
||||||
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
|
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetFeaturesOfType<LayerBrushProvider>();
|
||||||
|
List<IGraphicsContextProvider> graphicsContextProviders = kernel.Get<List<IGraphicsContextProvider>>();
|
||||||
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
|
LayerBrushDescriptors = new ObservableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
|
||||||
GraphicsContexts = new ObservableCollection<string> {"Software"};
|
GraphicsContexts = new ObservableCollection<string> {"Software"};
|
||||||
IGraphicsContextProvider? graphicsContextProvider = kernel.TryGet<IGraphicsContextProvider>();
|
GraphicsContexts.AddRange(graphicsContextProviders.Select(p => p.GraphicsContextName));
|
||||||
if (graphicsContextProvider != null)
|
|
||||||
GraphicsContexts.AddRange(graphicsContextProvider.GraphicsContextNames);
|
|
||||||
|
|
||||||
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
|
||||||
{
|
{
|
||||||
@ -55,6 +64,21 @@ namespace Artemis.UI.Screens.Settings
|
|||||||
ShowSetupWizard = ReactiveCommand.CreateFromTask(ExecuteShowSetupWizard);
|
ShowSetupWizard = ReactiveCommand.CreateFromTask(ExecuteShowSetupWizard);
|
||||||
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
|
ShowDebugger = ReactiveCommand.Create(ExecuteShowDebugger);
|
||||||
ShowDataFolder = ReactiveCommand.Create(ExecuteShowDataFolder);
|
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; }
|
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> ShowDebugger { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ShowDataFolder { get; }
|
public ReactiveCommand<Unit, Unit> ShowDataFolder { get; }
|
||||||
|
|
||||||
|
public bool IsAutoRunSupported => _autoRunProvider != null;
|
||||||
|
public bool IsUpdatingSupported => _updateService.UpdatingSupported;
|
||||||
|
|
||||||
public ObservableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
|
public ObservableCollection<LayerBrushDescriptor> LayerBrushDescriptors { get; }
|
||||||
public ObservableCollection<string> GraphicsContexts { 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<int> UIAutoRunDelay => _settingsService.GetSetting("UI.AutoRunDelay", 15);
|
||||||
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
|
public PluginSetting<bool> UIShowOnStartup => _settingsService.GetSetting("UI.ShowOnStartup", true);
|
||||||
public PluginSetting<bool> UICheckForUpdates => _settingsService.GetSetting("UI.CheckForUpdates", 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<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
|
||||||
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
|
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
|
||||||
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
|
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> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
|
||||||
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);
|
||||||
|
|
||||||
#region General
|
|
||||||
|
|
||||||
private void ExecuteShowLogs()
|
private void ExecuteShowLogs()
|
||||||
{
|
{
|
||||||
OpenFolder(Constants.LogsFolder);
|
Utilities.OpenFolder(Constants.LogsFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Updating
|
private async Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
|
||||||
|
|
||||||
private Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
await _updateService.ManualUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Tools
|
|
||||||
|
|
||||||
private async Task ExecuteShowSetupWizard()
|
private async Task ExecuteShowSetupWizard()
|
||||||
{
|
{
|
||||||
await _windowService.ShowDialogAsync<StartupWizardViewModel, bool>();
|
await _windowService.ShowDialogAsync<StartupWizardViewModel, bool>();
|
||||||
@ -153,19 +172,46 @@ namespace Artemis.UI.Screens.Settings
|
|||||||
_debugService.ShowDebugger();
|
_debugService.ShowDebugger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ExecuteShowDataFolder()
|
private void ExecuteShowDataFolder()
|
||||||
{
|
{
|
||||||
OpenFolder(Constants.DataFolder);
|
Utilities.OpenFolder(Constants.DataFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
private async Task ApplyAutoRun()
|
||||||
|
{
|
||||||
|
if (_autoRunProvider == null)
|
||||||
|
return;
|
||||||
|
|
||||||
private void OpenFolder(string path)
|
try
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (UIAutoRun.Value)
|
||||||
|
await _autoRunProvider.EnableAutoRun(false, UIAutoRunDelay.Value);
|
||||||
|
else
|
||||||
|
await _autoRunProvider.DisableAutoRun();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", path);
|
_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