1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +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();
@ -295,7 +296,7 @@ namespace Artemis.UI.Screens
PluginSetting<bool> setupWizardCompleted = _settingsService.GetSetting("UI.SetupWizardCompleted", false); PluginSetting<bool> setupWizardCompleted = _settingsService.GetSetting("UI.SetupWizardCompleted", false);
if (!setupWizardCompleted.Value) if (!setupWizardCompleted.Value)
ShowSetupWizard(); ShowSetupWizard();
base.OnInitialActivate(); base.OnInitialActivate();
} }

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,18 +26,23 @@ 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,
IWindowManager windowManager, IWindowManager windowManager,
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"
} }