1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 01:42:02 +00:00

Updating - Added UI for updating (actual update not yet implemented)

Shared UI - Added message service for easy access to the snackbar 🍟
This commit is contained in:
SpoinkyNL 2021-01-10 00:20:01 +01:00
parent 45fef11a67
commit 883fccef7b
21 changed files with 777 additions and 54 deletions

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Storage.Entities.Plugins; using Artemis.Core.Services.Core;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Artemis.Core namespace Artemis.Core
@ -40,6 +40,14 @@ namespace Artemis.Core
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", Version = new Version(2, 0) Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", Version = new Version(2, 0)
}; };
/// <summary>
/// The build information related to the currently running Artemis build
/// <para>Information is retrieved from <c>buildinfo.json</c></para>
/// </summary>
public static readonly BuildInfo BuildInfo = File.Exists(Path.Combine(ApplicationFolder, "buildinfo.json"))
? JsonConvert.DeserializeObject<BuildInfo>(File.ReadAllText(Path.Combine(ApplicationFolder, "buildinfo.json")))
: new BuildInfo();
/// <summary> /// <summary>
/// The plugin used by core components of Artemis /// The plugin used by core components of Artemis
/// </summary> /// </summary>
@ -52,10 +60,11 @@ namespace Artemis.Core
{ {
Converters = new List<JsonConverter> {new SKColorConverter(), new ForgivingIntConverter()} Converters = new List<JsonConverter> {new SKColorConverter(), new ForgivingIntConverter()}
}; };
internal static JsonSerializerSettings JsonConvertTypedSettings = new() internal static JsonSerializerSettings JsonConvertTypedSettings = new()
{ {
TypeNameHandling = TypeNameHandling.All, TypeNameHandling = TypeNameHandling.All,
Converters = new List<JsonConverter> { new SKColorConverter(), new ForgivingIntConverter() } Converters = new List<JsonConverter> {new SKColorConverter(), new ForgivingIntConverter()}
}; };
/// <summary> /// <summary>

View File

@ -0,0 +1,31 @@
using Newtonsoft.Json;
namespace Artemis.Core.Services.Core
{
/// <summary>
/// Represents build information related to the currently running Artemis build
/// </summary>
public class BuildInfo
{
/// <summary>
/// Gets the unique ID of this build
/// </summary>
public int BuildId { get; internal set; }
/// <summary>
/// Gets the build number. This contains the date and the build count for that day.
/// <example>Per example 20210108.4</example>
/// </summary>
public double BuildNumber { get; internal set; }
/// <summary>
/// Gets the branch of the triggering repo the build was created for.
/// </summary>
public string SourceBranch { get; internal set; } = null!;
/// <summary>
/// Gets the commit ID used to create this build
/// </summary>
public string SourceVersion { get; internal set; } = null!;
}
}

View File

@ -1,13 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions; using Artemis.Core.DataModelExpansions;
using Artemis.Core.Ninject; using Artemis.Core.Ninject;
using Artemis.Core.Services.Core;
using Artemis.Storage; using Artemis.Storage;
using HidSharp; using HidSharp;
using Newtonsoft.Json;
using Ninject; using Ninject;
using RGB.NET.Core; using RGB.NET.Core;
using Serilog; using Serilog;
@ -37,9 +40,17 @@ namespace Artemis.Core.Services
private DateTime _lastExceptionLog; private DateTime _lastExceptionLog;
private List<Module> _modules = new(); private List<Module> _modules = new();
// ReSharper disable UnusedParameter.Local - Storage migration and module service are injected early to ensure it runs before anything else // ReSharper disable UnusedParameter.Local
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginManagementService pluginManagementService, public CoreService(IKernel kernel,
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService) ILogger logger,
StorageMigrationService _, // injected to ensure migration runs early
ISettingsService settingsService,
IPluginManagementService pluginManagementService,
IRgbService rgbService,
ISurfaceService surfaceService,
IProfileService profileService,
IModuleService moduleService // injected to ensure module priorities get applied
)
{ {
Kernel = kernel; Kernel = kernel;
Constants.CorePlugin.Kernel = kernel; Constants.CorePlugin.Kernel = kernel;
@ -82,7 +93,8 @@ namespace Artemis.Core.Services
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized."); throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
AssemblyInformationalVersionAttribute? versionAttribute = typeof(CoreService).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>(); AssemblyInformationalVersionAttribute? versionAttribute = typeof(CoreService).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
_logger.Information("Initializing Artemis Core version {version}", versionAttribute?.InformationalVersion); _logger.Information("Initializing Artemis Core version {version}, build {buildNumber} branch {branch}.", versionAttribute?.InformationalVersion, Constants.BuildInfo.BuildNumber,
Constants.BuildInfo.SourceBranch);
// This should prevent a certain someone from removing HidSharp as an unused dependency as well // This should prevent a certain someone from removing HidSharp as an unused dependency as well
_logger.Information("Forcing plugins to use HidSharp {hidSharpVersion}", Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version); _logger.Information("Forcing plugins to use HidSharp {hidSharpVersion}", Assembly.GetAssembly(typeof(HidDevice))!.GetName().Version);

View File

@ -15,4 +15,5 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdialog/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdialog/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cwindow/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=utilities/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,115 @@
using System;
using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Shared.Services
{
/// <summary>
/// Providers messaging functionality
/// </summary>
public interface IMessageService : IArtemisSharedUIService
{
/// <summary>
/// Gets the main snackbar message queue used by <see cref="ShowMessage(object)" /> and its overloads
/// </summary>
ISnackbarMessageQueue MainMessageQueue { get; }
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
void ShowMessage(object content);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
void ShowMessage(object content, object actionContent, Action actionHandler);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler" />.</param>
void ShowMessage<TArgument>(
object content,
object actionContent,
Action<TArgument> actionHandler,
TArgument actionArgument);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="neverConsiderToBeDuplicate">
/// Subsequent, duplicate messages queued within a short time span will
/// be discarded. To override this behaviour and ensure the message always gets displayed set to <c>true</c>.
/// </param>
void ShowMessage(object content, bool neverConsiderToBeDuplicate);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
/// <param name="promote">The message will promoted to the front of the queue.</param>
void ShowMessage(object content, object actionContent, Action actionHandler, bool promote);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler" />.</param>
/// <param name="promote">The message will be promoted to the front of the queue and never considered to be a duplicate.</param>
void ShowMessage<TArgument>(
object content,
object actionContent,
Action<TArgument> actionHandler,
TArgument actionArgument,
bool promote);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler" />.</param>
/// <param name="promote">The message will be promoted to the front of the queue.</param>
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
/// <param name="durationOverride">Message show duration override.</param>
void ShowMessage<TArgument>(
object content,
object actionContent,
Action<TArgument> actionHandler,
TArgument actionArgument,
bool promote,
bool neverConsiderToBeDuplicate,
TimeSpan? durationOverride = null);
/// <summary>
/// Queues a notification message for display in a snackbar.
/// </summary>
/// <param name="content">Message.</param>
/// <param name="actionContent">Content for the action button.</param>
/// <param name="actionHandler">Call back to be executed if user clicks the action button.</param>
/// <param name="actionArgument">Argument to pass to <paramref name="actionHandler" />.</param>
/// <param name="promote">The message will promoted to the front of the queue.</param>
/// <param name="neverConsiderToBeDuplicate">The message will never be considered a duplicate.</param>
/// <param name="durationOverride">Message show duration override.</param>
void ShowMessage(
object content,
object actionContent,
Action<object> actionHandler,
object actionArgument,
bool promote,
bool neverConsiderToBeDuplicate,
TimeSpan? durationOverride = null);
}
}

View File

@ -0,0 +1,67 @@
using System;
using MaterialDesignThemes.Wpf;
namespace Artemis.UI.Shared.Services
{
internal class MessageService : IMessageService
{
public ISnackbarMessageQueue MainMessageQueue { get; }
public MessageService(ISnackbarMessageQueue mainMessageQueue)
{
MainMessageQueue = mainMessageQueue;
}
public void ShowMessage(object content)
{
MainMessageQueue.Enqueue(content);
}
public void ShowMessage(object content, object actionContent, Action actionHandler)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler);
}
public void ShowMessage<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler, actionArgument);
}
public void ShowMessage(object content, bool neverConsiderToBeDuplicate)
{
MainMessageQueue.Enqueue(content, neverConsiderToBeDuplicate);
}
public void ShowMessage(object content, object actionContent, Action actionHandler, bool promote)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler, promote);
}
public void ShowMessage<TArgument>(object content, object actionContent, Action<TArgument> actionHandler, TArgument actionArgument, bool promote)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler, actionArgument, promote);
}
public void ShowMessage<TArgument>(object content,
object actionContent,
Action<TArgument> actionHandler,
TArgument actionArgument,
bool promote,
bool neverConsiderToBeDuplicate,
TimeSpan? durationOverride = null)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler, actionArgument, promote, neverConsiderToBeDuplicate, durationOverride);
}
public void ShowMessage(object content,
object actionContent,
Action<object> actionHandler,
object actionArgument,
bool promote,
bool neverConsiderToBeDuplicate,
TimeSpan? durationOverride = null)
{
MainMessageQueue.Enqueue(content, actionContent, actionHandler, actionArgument, promote, neverConsiderToBeDuplicate, durationOverride);
}
}
}

View File

@ -0,0 +1,38 @@
using System;
namespace Artemis.UI.Shared.Services
{
/// <summary>
/// Represents a class that manages a main window, used by the <see cref="IWindowService" /> to control the state of
/// the main window.
/// </summary>
public interface IMainWindowManager
{
/// <summary>
/// Gets a boolean indicating whether the main window is currently open
/// </summary>
bool IsMainWindowOpen { get; }
/// <summary>
/// Opens the main window
/// </summary>
/// <returns></returns>
bool OpenMainWindow();
/// <summary>
/// Closes the main window
/// </summary>
/// <returns></returns>
bool CloseMainWindow();
/// <summary>
/// Occurs when the main window has been opened
/// </summary>
public event EventHandler? MainWindowOpened;
/// <summary>
/// Occurs when the main window has been closed
/// </summary>
public event EventHandler? MainWindowClosed;
}
}

View File

@ -0,0 +1,41 @@
using System;
namespace Artemis.UI.Shared.Services
{
/// <summary>
/// A service that allows you to view, monitor and manage the open/close state of the main window
/// </summary>
public interface IWindowService : IArtemisSharedUIService
{
/// <summary>
/// Gets a boolean indicating whether the main window is currently open
/// </summary>
bool IsMainWindowOpen { get; }
/// <summary>
/// Sets up the main window manager that controls the state of the main window
/// </summary>
/// <param name="mainWindowManager">The main window manager to use to control the state of the main window</param>
void ConfigureMainWindowManager(IMainWindowManager mainWindowManager);
/// <summary>
/// Opens the main window if it is not already open
/// </summary>
void OpenMainWindow();
/// <summary>
/// Closes the main window if it is not already closed
/// </summary>
void CloseMainWindow();
/// <summary>
/// Occurs when the main window has been opened
/// </summary>
public event EventHandler? MainWindowOpened;
/// <summary>
/// Occurs when the main window has been closed
/// </summary>
public event EventHandler? MainWindowClosed;
}
}

View File

@ -0,0 +1,82 @@
using System;
namespace Artemis.UI.Shared.Services
{
internal class WindowService : IWindowService
{
private IMainWindowManager? _mainWindowManager;
protected virtual void OnMainWindowOpened()
{
MainWindowOpened?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnMainWindowClosed()
{
MainWindowClosed?.Invoke(this, EventArgs.Empty);
}
private void SyncWithManager()
{
if (_mainWindowManager == null)
return;
if (IsMainWindowOpen && !_mainWindowManager.IsMainWindowOpen)
{
IsMainWindowOpen = false;
OnMainWindowClosed();
}
if (!IsMainWindowOpen && _mainWindowManager.IsMainWindowOpen)
{
IsMainWindowOpen = true;
OnMainWindowOpened();
}
}
private void HandleMainWindowOpened(object? sender, EventArgs e)
{
SyncWithManager();
}
private void HandleMainWindowClosed(object? sender, EventArgs e)
{
SyncWithManager();
}
public bool IsMainWindowOpen { get; private set; }
public void ConfigureMainWindowManager(IMainWindowManager mainWindowManager)
{
if (mainWindowManager == null) throw new ArgumentNullException(nameof(mainWindowManager));
if (_mainWindowManager != null)
{
_mainWindowManager.MainWindowOpened -= HandleMainWindowOpened;
_mainWindowManager.MainWindowClosed -= HandleMainWindowClosed;
}
_mainWindowManager = mainWindowManager;
_mainWindowManager.MainWindowOpened += HandleMainWindowOpened;
_mainWindowManager.MainWindowClosed += HandleMainWindowClosed;
// Sync up with the new manager's state
SyncWithManager();
}
public void OpenMainWindow()
{
IsMainWindowOpen = true;
OnMainWindowOpened();
}
public void CloseMainWindow()
{
IsMainWindowOpen = false;
OnMainWindowClosed();
}
public event EventHandler? MainWindowOpened;
public event EventHandler? MainWindowClosed;
}
}

View File

@ -8,16 +8,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{ {
public class ProfileExportViewModel : DialogViewModelBase public class ProfileExportViewModel : DialogViewModelBase
{ {
private readonly ISnackbarMessageQueue _mainMessageQueue;
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly IMessageService _messageService;
public ProfileExportViewModel(ProfileDescriptor profileDescriptor, IProfileService profileService, ISnackbarMessageQueue mainMessageQueue) public ProfileExportViewModel(ProfileDescriptor profileDescriptor, IProfileService profileService, IMessageService messageService)
{ {
ProfileDescriptor = profileDescriptor; ProfileDescriptor = profileDescriptor;
_profileService = profileService; _profileService = profileService;
_mainMessageQueue = mainMessageQueue; _messageService = messageService;
} }
public ProfileDescriptor ProfileDescriptor { get; } public ProfileDescriptor ProfileDescriptor { get; }
@ -26,7 +25,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{ {
string encoded = _profileService.ExportProfile(ProfileDescriptor); string encoded = _profileService.ExportProfile(ProfileDescriptor);
Clipboard.SetText(encoded); Clipboard.SetText(encoded);
_mainMessageQueue.Enqueue("Profile contents exported to clipboard."); _messageService.ShowMessage("Profile contents exported to clipboard.");
Session.Close(); Session.Close();
} }

View File

@ -11,15 +11,16 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs
{ {
private readonly ISnackbarMessageQueue _mainMessageQueue; private readonly ISnackbarMessageQueue _mainMessageQueue;
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly IMessageService _messageService;
private string _profileJson; private string _profileJson;
public ProfileImportViewModel(ProfileModule profileModule, IProfileService profileService, ISnackbarMessageQueue mainMessageQueue) public ProfileImportViewModel(ProfileModule profileModule, IProfileService profileService, IMessageService messageService)
{ {
ProfileModule = profileModule; ProfileModule = profileModule;
Document = new TextDocument(); Document = new TextDocument();
_profileService = profileService; _profileService = profileService;
_mainMessageQueue = mainMessageQueue; _messageService = messageService;
} }
public ProfileModule ProfileModule { get; } public ProfileModule ProfileModule { get; }
@ -34,7 +35,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Dialogs
public void Accept() public void Accept()
{ {
ProfileDescriptor descriptor = _profileService.ImportProfile(Document.Text, ProfileModule); ProfileDescriptor descriptor = _profileService.ImportProfile(Document.Text, ProfileModule);
_mainMessageQueue.Enqueue("Profile imported."); _messageService.ShowMessage("Profile imported.");
Session.Close(descriptor); Session.Close(descriptor);
} }
} }

View File

@ -22,10 +22,10 @@ namespace Artemis.UI.Screens.ProfileEditor
public class ProfileEditorViewModel : Screen public class ProfileEditorViewModel : Screen
{ {
private readonly IModuleService _moduleService; private readonly IModuleService _moduleService;
private readonly IMessageService _messageService;
private readonly IProfileEditorService _profileEditorService; private readonly IProfileEditorService _profileEditorService;
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private PluginSetting<GridLength> _bottomPanelsHeight; private PluginSetting<GridLength> _bottomPanelsHeight;
private PluginSetting<GridLength> _dataModelConditionsHeight; private PluginSetting<GridLength> _dataModelConditionsHeight;
private DisplayConditionsViewModel _displayConditionsViewModel; private DisplayConditionsViewModel _displayConditionsViewModel;
@ -47,13 +47,13 @@ namespace Artemis.UI.Screens.ProfileEditor
IDialogService dialogService, IDialogService dialogService,
ISettingsService settingsService, ISettingsService settingsService,
IModuleService moduleService, IModuleService moduleService,
ISnackbarMessageQueue snackbarMessageQueue) IMessageService messageService)
{ {
_profileEditorService = profileEditorService; _profileEditorService = profileEditorService;
_profileService = profileService; _profileService = profileService;
_settingsService = settingsService; _settingsService = settingsService;
_moduleService = moduleService; _moduleService = moduleService;
_snackbarMessageQueue = snackbarMessageQueue; _messageService = messageService;
DisplayName = "PROFILE EDITOR"; DisplayName = "PROFILE EDITOR";
Module = module; Module = module;
@ -242,7 +242,7 @@ namespace Artemis.UI.Screens.ProfileEditor
if (!_profileEditorService.UndoUpdateProfile()) if (!_profileEditorService.UndoUpdateProfile())
{ {
_snackbarMessageQueue.Enqueue("Nothing to undo"); _messageService.ShowMessage("Nothing to undo");
return; return;
} }
@ -256,7 +256,7 @@ namespace Artemis.UI.Screens.ProfileEditor
focusedElement?.Focus(); focusedElement?.Focus();
}); });
_snackbarMessageQueue.Enqueue("Undid profile update", "REDO", Redo); _messageService.ShowMessage("Undid profile update", "REDO", Redo);
} }
public void Redo() public void Redo()
@ -269,7 +269,7 @@ namespace Artemis.UI.Screens.ProfileEditor
if (!_profileEditorService.RedoUpdateProfile()) if (!_profileEditorService.RedoUpdateProfile())
{ {
_snackbarMessageQueue.Enqueue("Nothing to redo"); _messageService.ShowMessage("Nothing to redo");
return; return;
} }
@ -283,7 +283,7 @@ namespace Artemis.UI.Screens.ProfileEditor
focusedElement?.Focus(); focusedElement?.Focus();
}); });
_snackbarMessageQueue.Enqueue("Redid profile update", "UNDO", Undo); _messageService.ShowMessage("Redid profile update", "UNDO", Undo);
} }
protected override void OnInitialActivate() protected override void OnInitialActivate()

View File

@ -14,6 +14,7 @@ using Artemis.UI.Screens.Sidebar;
using Artemis.UI.Screens.StartupWizard; using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services; using Artemis.UI.Services;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using Artemis.UI.Utilities; using Artemis.UI.Utilities;
using MaterialDesignExtensions.Controls; using MaterialDesignExtensions.Controls;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
@ -25,6 +26,7 @@ namespace Artemis.UI.Screens
public sealed class RootViewModel : Conductor<IScreen>, IDisposable public sealed class RootViewModel : Conductor<IScreen>, IDisposable
{ {
private readonly IRegistrationService _builtInRegistrationService; private readonly IRegistrationService _builtInRegistrationService;
private readonly IMessageService _messageService;
private readonly PluginSetting<ApplicationColorScheme> _colorScheme; private readonly PluginSetting<ApplicationColorScheme> _colorScheme;
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
@ -34,7 +36,6 @@ namespace Artemis.UI.Screens
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly Timer _frameTimeUpdateTimer; private readonly Timer _frameTimeUpdateTimer;
private readonly SidebarViewModel _sidebarViewModel; private readonly SidebarViewModel _sidebarViewModel;
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private readonly ThemeWatcher _themeWatcher; private readonly ThemeWatcher _themeWatcher;
private readonly PluginSetting<WindowSize> _windowSize; private readonly PluginSetting<WindowSize> _windowSize;
private bool _activeItemReady; private bool _activeItemReady;
@ -52,7 +53,7 @@ namespace Artemis.UI.Screens
IWindowManager windowManager, IWindowManager windowManager,
IDebugService debugService, IDebugService debugService,
IRegistrationService builtInRegistrationService, IRegistrationService builtInRegistrationService,
ISnackbarMessageQueue snackbarMessageQueue, IMessageService messageService,
SidebarViewModel sidebarViewModel) SidebarViewModel sidebarViewModel)
{ {
_kernel = kernel; _kernel = kernel;
@ -62,7 +63,7 @@ namespace Artemis.UI.Screens
_windowManager = windowManager; _windowManager = windowManager;
_debugService = debugService; _debugService = debugService;
_builtInRegistrationService = builtInRegistrationService; _builtInRegistrationService = builtInRegistrationService;
_snackbarMessageQueue = snackbarMessageQueue; _messageService = messageService;
_sidebarViewModel = sidebarViewModel; _sidebarViewModel = sidebarViewModel;
_frameTimeUpdateTimer = new Timer(500); _frameTimeUpdateTimer = new Timer(500);
@ -79,7 +80,7 @@ namespace Artemis.UI.Screens
PinSidebar = _settingsService.GetSetting("UI.PinSidebar", false); PinSidebar = _settingsService.GetSetting("UI.PinSidebar", false);
AssemblyInformationalVersionAttribute versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>(); AssemblyInformationalVersionAttribute versionAttribute = typeof(RootViewModel).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
WindowTitle = $"Artemis {versionAttribute?.InformationalVersion}"; WindowTitle = $"Artemis {versionAttribute?.InformationalVersion} build {Constants.BuildInfo.BuildNumber}";
} }
public PluginSetting<bool> PinSidebar { get; } public PluginSetting<bool> PinSidebar { get; }
@ -275,7 +276,7 @@ namespace Artemis.UI.Screens
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
MainMessageQueue = _snackbarMessageQueue; MainMessageQueue = _messageService.MainMessageQueue;
UpdateFrameTime(); UpdateFrameTime();
_builtInRegistrationService.RegisterBuiltInDataModelDisplays(); _builtInRegistrationService.RegisterBuiltInDataModelDisplays();

View File

@ -195,6 +195,76 @@
</StackPanel> </StackPanel>
</materialDesign:Card> </materialDesign:Card>
<!-- Update settings -->
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Updating</TextBlock>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel Margin="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Check for updates</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" 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">
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding CheckForUpdates}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Automatically install updates</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
If enabled updates are installed automatically without asking first.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ToggleButton Style="{StaticResource MaterialDesignSwitchToggleButton}" IsChecked="{Binding AutoInstallUpdates}" IsEnabled="{Binding CheckForUpdates}" />
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Update</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}">
Use the button on the right to check for updates now.
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Command="{s:Action OfferUpdatesIfFound}" Width="150">
CHECK NOW
</Button>
</StackPanel>
</Grid>
</StackPanel>
</materialDesign:Card>
<!-- Profile editor settings --> <!-- Profile editor settings -->
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Profile editor</TextBlock> <TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Profile editor</TextBlock>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0"> <materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">

View File

@ -8,9 +8,11 @@ using Artemis.Core;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Screens.StartupWizard; using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using MaterialDesignThemes.Wpf;
using Ninject; using Ninject;
using Serilog.Events; using Serilog.Events;
using Stylet; using Stylet;
@ -24,10 +26,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly IUpdateService _updateService;
private readonly IMessageService _messageService;
private List<Tuple<string, double>> _renderScales; private List<Tuple<string, double>> _renderScales;
private List<int> _sampleSizes; private List<int> _sampleSizes;
private List<Tuple<string, int>> _targetFrameRates; private List<Tuple<string, int>> _targetFrameRates;
private readonly PluginSetting<LayerBrushReference> _defaultLayerBrushDescriptor; private readonly PluginSetting<LayerBrushReference> _defaultLayerBrushDescriptor;
private bool _canOfferUpdatesIfFound = true;
public GeneralSettingsTabViewModel( public GeneralSettingsTabViewModel(
IKernel kernel, IKernel kernel,
@ -35,7 +40,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
IDialogService dialogService, IDialogService dialogService,
IDebugService debugService, IDebugService debugService,
ISettingsService settingsService, ISettingsService settingsService,
IPluginManagementService pluginManagementService) IUpdateService updateService,
IPluginManagementService pluginManagementService,
IMessageService messageService)
{ {
DisplayName = "GENERAL"; DisplayName = "GENERAL";
@ -44,6 +51,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
_dialogService = dialogService; _dialogService = dialogService;
_debugService = debugService; _debugService = debugService;
_settingsService = settingsService; _settingsService = settingsService;
_updateService = updateService;
_messageService = messageService;
LogLevels = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel))); LogLevels = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
ColorSchemes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme))); ColorSchemes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme)));
@ -124,6 +133,31 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
} }
} }
public bool CheckForUpdates
{
get => _settingsService.GetSetting("UI.CheckForUpdates", true).Value;
set
{
_settingsService.GetSetting("UI.CheckForUpdates", true).Value = value;
_settingsService.GetSetting("UI.CheckForUpdates", true).Save();
NotifyOfPropertyChange(nameof(CheckForUpdates));
if (!value)
AutoInstallUpdates = false;
}
}
public bool AutoInstallUpdates
{
get => _settingsService.GetSetting("UI.AutoInstallUpdates", false).Value;
set
{
_settingsService.GetSetting("UI.AutoInstallUpdates", false).Value = value;
_settingsService.GetSetting("UI.AutoInstallUpdates", false).Save();
NotifyOfPropertyChange(nameof(AutoInstallUpdates));
}
}
public bool ShowDataModelValues public bool ShowDataModelValues
{ {
get => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false).Value; get => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false).Value;
@ -196,6 +230,12 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
} }
} }
public bool CanOfferUpdatesIfFound
{
get => _canOfferUpdatesIfFound;
set => SetAndNotify(ref _canOfferUpdatesIfFound, value);
}
public void ShowDebugger() public void ShowDebugger()
{ {
_debugService.ShowDebugger(); _debugService.ShowDebugger();
@ -230,6 +270,20 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
} }
} }
public async void OfferUpdatesIfFound()
{
if (!CanOfferUpdatesIfFound)
return;
CanOfferUpdatesIfFound = false;
bool updateFound = await _updateService.OfferUpdatesIfFound();
if (!updateFound)
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
else
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
CanOfferUpdatesIfFound = true;
}
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
Task.Run(ApplyAutorun); Task.Run(ApplyAutorun);

View File

@ -20,17 +20,17 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{ {
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue; private IMessageService _messageService;
private bool _enabling; private bool _enabling;
public PluginFeatureViewModel(PluginFeature feature, public PluginFeatureViewModel(PluginFeature feature,
IDialogService dialogService, IDialogService dialogService,
IPluginManagementService pluginManagementService, IPluginManagementService pluginManagementService,
ISnackbarMessageQueue snackbarMessageQueue) IMessageService messageService)
{ {
_dialogService = dialogService; _dialogService = dialogService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_snackbarMessageQueue = snackbarMessageQueue; _messageService = messageService;
Feature = feature; Feature = feature;
Icon = GetIconKind(); Icon = GetIconKind();
@ -109,7 +109,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
} }
catch (Exception e) catch (Exception e)
{ {
_snackbarMessageQueue.Enqueue($"Failed to enable {Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder); _messageService.ShowMessage($"Failed to enable {Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
} }
finally finally
{ {

View File

@ -19,7 +19,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
private readonly IDialogService _dialogService; private readonly IDialogService _dialogService;
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly ISettingsVmFactory _settingsVmFactory; private readonly ISettingsVmFactory _settingsVmFactory;
private readonly ISnackbarMessageQueue _snackbarMessageQueue; private readonly IMessageService _messageService;
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private bool _enabling; private bool _enabling;
private Plugin _plugin; private Plugin _plugin;
@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
IWindowManager windowManager, IWindowManager windowManager,
IDialogService dialogService, IDialogService dialogService,
IPluginManagementService pluginManagementService, IPluginManagementService pluginManagementService,
ISnackbarMessageQueue snackbarMessageQueue) IMessageService messageService)
{ {
Plugin = plugin; Plugin = plugin;
@ -37,7 +37,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
_windowManager = windowManager; _windowManager = windowManager;
_dialogService = dialogService; _dialogService = dialogService;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_snackbarMessageQueue = snackbarMessageQueue; _messageService = messageService;
Icon = PluginUtilities.GetPluginIcon(Plugin, Plugin.Info.Icon); Icon = PluginUtilities.GetPluginIcon(Plugin, Plugin.Info.Icon);
} }
@ -130,7 +130,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
} }
catch (Exception e) catch (Exception e)
{ {
_snackbarMessageQueue.Enqueue($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder); _messageService.ShowMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}", "VIEW LOGS", ShowLogsFolder);
} }
finally finally
{ {

View File

@ -13,17 +13,17 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
public class SurfaceDeviceDetectInputViewModel : DialogViewModelBase public class SurfaceDeviceDetectInputViewModel : DialogViewModelBase
{ {
private readonly IInputService _inputService; private readonly IInputService _inputService;
private readonly IMessageService _messageService;
private readonly ListLedGroup _ledGroup; private readonly ListLedGroup _ledGroup;
private readonly ISnackbarMessageQueue _mainMessageQueue;
public SurfaceDeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, ISnackbarMessageQueue mainMessageQueue) public SurfaceDeviceDetectInputViewModel(ArtemisDevice device, IInputService inputService, IMessageService messageService)
{ {
Device = device; Device = device;
Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input"; Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Detect input";
IsMouse = Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse; IsMouse = Device.RgbDevice.DeviceInfo.DeviceType == RGBDeviceType.Mouse;
_inputService = inputService; _inputService = inputService;
_mainMessageQueue = mainMessageQueue; _messageService = messageService;
_inputService.IdentifyDevice(Device); _inputService.IdentifyDevice(Device);
_inputService.DeviceIdentified += InputServiceOnDeviceIdentified; _inputService.DeviceIdentified += InputServiceOnDeviceIdentified;
@ -49,7 +49,7 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
private void InputServiceOnDeviceIdentified(object sender, EventArgs e) private void InputServiceOnDeviceIdentified(object sender, EventArgs e)
{ {
Session?.Close(true); Session?.Close(true);
_mainMessageQueue.Enqueue($"{Device.RgbDevice.DeviceInfo.DeviceName} identified 😁"); _messageService.ShowMessage($"{Device.RgbDevice.DeviceInfo.DeviceName} identified 😁");
} }
} }
} }

View File

@ -1,13 +1,16 @@
using Artemis.Core.Services; using System;
using Artemis.Core.Services;
using Artemis.UI.Events; using Artemis.UI.Events;
using Artemis.UI.Screens.Splash; using Artemis.UI.Screens.Splash;
using Artemis.UI.Services;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using Ninject; using Ninject;
using Stylet; using Stylet;
namespace Artemis.UI.Screens namespace Artemis.UI.Screens
{ {
public class TrayViewModel : Screen public class TrayViewModel : Screen, IMainWindowManager
{ {
private readonly IDebugService _debugService; private readonly IDebugService _debugService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
@ -15,8 +18,16 @@ namespace Artemis.UI.Screens
private readonly IWindowManager _windowManager; private readonly IWindowManager _windowManager;
private bool _canShowRootViewModel; private bool _canShowRootViewModel;
private SplashViewModel _splashViewModel; private SplashViewModel _splashViewModel;
private RootViewModel _rootViewModel;
public TrayViewModel(IKernel kernel, IWindowManager windowManager, IEventAggregator eventAggregator, ICoreService coreService, IDebugService debugService, ISettingsService settingsService) public TrayViewModel(IKernel kernel,
IWindowManager windowManager,
IWindowService windowService,
IUpdateService updateService,
IEventAggregator eventAggregator,
ICoreService coreService,
IDebugService debugService,
ISettingsService settingsService)
{ {
_kernel = kernel; _kernel = kernel;
_windowManager = windowManager; _windowManager = windowManager;
@ -24,13 +35,16 @@ namespace Artemis.UI.Screens
_debugService = debugService; _debugService = debugService;
CanShowRootViewModel = true; CanShowRootViewModel = true;
windowService.ConfigureMainWindowManager(this);
bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun"); bool autoRunning = Bootstrapper.StartupArguments.Contains("--autorun");
bool showOnAutoRun = settingsService.GetSetting("UI.ShowOnStartup", true).Value; bool showOnAutoRun = settingsService.GetSetting("UI.ShowOnStartup", true).Value;
if (!autoRunning || showOnAutoRun) if (!autoRunning || showOnAutoRun)
{ {
ShowSplashScreen(); ShowSplashScreen();
coreService.Initialized += (sender, args) => TrayBringToForeground(); coreService.Initialized += (_, _) => TrayBringToForeground();
} }
updateService.AutoUpdate();
} }
public bool CanShowRootViewModel public bool CanShowRootViewModel
@ -53,10 +67,12 @@ namespace Artemis.UI.Screens
{ {
_splashViewModel?.RequestClose(); _splashViewModel?.RequestClose();
_splashViewModel = null; _splashViewModel = null;
RootViewModel rootViewModel = _kernel.Get<RootViewModel>(); _rootViewModel = _kernel.Get<RootViewModel>();
rootViewModel.Closed += RootViewModelOnClosed; _rootViewModel.Closed += RootViewModelOnClosed;
_windowManager.ShowWindow(rootViewModel); _windowManager.ShowWindow(_rootViewModel);
}); });
OnMainWindowOpened();
} }
public void TrayActivateSidebarItem(string sidebarItem) public void TrayActivateSidebarItem(string sidebarItem)
@ -86,7 +102,53 @@ namespace Artemis.UI.Screens
private void RootViewModelOnClosed(object sender, CloseEventArgs e) private void RootViewModelOnClosed(object sender, CloseEventArgs e)
{ {
_rootViewModel.Closed -= RootViewModelOnClosed;
_rootViewModel = null;
CanShowRootViewModel = true; CanShowRootViewModel = true;
OnMainWindowClosed();
} }
#region Implementation of IMainWindowManager
/// <inheritdoc />
public bool IsMainWindowOpen { get; private set; }
/// <inheritdoc />
public bool OpenMainWindow()
{
if (CanShowRootViewModel)
return false;
TrayBringToForeground();
return true;
}
/// <inheritdoc />
public bool CloseMainWindow()
{
_rootViewModel.RequestClose();
return _rootViewModel.ScreenState == ScreenState.Closed;
}
/// <inheritdoc />
public event EventHandler MainWindowOpened;
/// <inheritdoc />
public event EventHandler MainWindowClosed;
protected virtual void OnMainWindowOpened()
{
IsMainWindowOpen = true;
MainWindowOpened?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnMainWindowClosed()
{
IsMainWindowOpen = false;
MainWindowClosed?.Invoke(this, EventArgs.Empty);
}
#endregion
} }
} }

View File

@ -0,0 +1,140 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using Newtonsoft.Json.Linq;
using Serilog;
namespace Artemis.UI.Services
{
public class UpdateService : IUpdateService
{
private const string ApiUrl = "https://dev.azure.com/artemis-rgb/Artemis/_apis/";
private readonly PluginSetting<bool> _autoInstallUpdates;
private readonly PluginSetting<bool> _checkForUpdates;
private readonly ILogger _logger;
private readonly IDialogService _dialogService;
private readonly IWindowService _windowService;
public UpdateService(ILogger logger, ISettingsService settingsService, IDialogService dialogService, IWindowService windowService)
{
_logger = logger;
_dialogService = dialogService;
_windowService = windowService;
_windowService.MainWindowOpened += WindowServiceOnMainWindowOpened;
_checkForUpdates = settingsService.GetSetting("UI.CheckForUpdates", true);
_autoInstallUpdates = settingsService.GetSetting("UI.AutoInstallUpdates", false);
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
}
public async Task<double> GetLatestBuildNumber()
{
// TODO: The URL is hardcoded, that should change in the future
string latestBuildUrl = ApiUrl + "build/builds?api-version=6.1-preview.6&branchName=refs/heads/master&resultFilter=succeeded&$top=1";
_logger.Debug("Getting latest build number from {latestBuildUrl}", latestBuildUrl);
// Make the request
using HttpClient client = new();
HttpResponseMessage httpResponseMessage = await client.GetAsync(latestBuildUrl);
// Ensure it returned correctly
if (!httpResponseMessage.IsSuccessStatusCode)
{
_logger.Warning("Failed to check for updates, request returned {statusCode}", httpResponseMessage.StatusCode);
return 0;
}
// Parse the response
string response = await httpResponseMessage.Content.ReadAsStringAsync();
try
{
JToken buildNumberToken = JObject.Parse(response).SelectToken("value[0].buildNumber");
if (buildNumberToken != null)
return buildNumberToken.Value<double>();
_logger.Warning("Failed to find build number at \"value[0].buildNumber\"");
return 0;
}
catch (Exception e)
{
_logger.Warning(e, "Failed to retrieve build info JSON");
return 0;
}
}
public async Task<bool> OfferUpdatesIfFound()
{
_logger.Information("Checking for updates");
double buildNumber = await GetLatestBuildNumber();
_logger.Information("Latest build is {buildNumber}, we're running {localBuildNumber}", buildNumber, Constants.BuildInfo.BuildNumber);
if (buildNumber < Constants.BuildInfo.BuildNumber)
return false;
if (_windowService.IsMainWindowOpen)
{
}
else
{
}
return true;
}
public async Task<bool> IsUpdateAvailable()
{
double buildNumber = await GetLatestBuildNumber();
return buildNumber > Constants.BuildInfo.BuildNumber;
}
public void ApplyUpdate()
{
throw new NotImplementedException();
}
public async Task<bool> AutoUpdate()
{
if (!_checkForUpdates.Value)
return false;
return await OfferUpdatesIfFound();
}
#region Event handlers
private void CheckForUpdatesOnSettingChanged(object sender, EventArgs e)
{
// Run an auto-update as soon as the setting gets changed to enabled
if (_checkForUpdates.Value)
AutoUpdate();
}
private void WindowServiceOnMainWindowOpened(object? sender, EventArgs e)
{
_logger.Information("Main window opened!");
}
#endregion
}
public interface IUpdateService : IArtemisUIService
{
Task<bool> OfferUpdatesIfFound();
Task<bool> IsUpdateAvailable();
void ApplyUpdate();
/// <summary>
/// If auto-update is enabled this will offer updates if found
/// </summary>
Task<bool> AutoUpdate();
}
}

View File

@ -1,6 +1,6 @@
{ {
"BuildId": 0, "BuildId": 0,
"BuildNumber": 0, "BuildNumber": 0,
"SourceBranch": "", "SourceBranch": "local",
"SourceVersion": "" "SourceVersion": "local"
} }