Storage - Replace LiteDB with SQLite + EF Core (#843)

Storage - Added LiteDB to SQLite migration
UI - Try to die a bit more gracefully
Core - Delay start watching plugins for hot reload after initializing
UI - Simplify category management logic
UI - Avoid crash during profile icon load
Storage - Fix entry metadata retrieval
This commit is contained in:
RobertBeekman authored and GitHub committed 2024-03-13 20:19:21 +01:00
1 parent 9132301dbf
commit 551921db9f
130 files changed
+2965 -1135

No files matched your search

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Core;
@@ -15,7 +16,6 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// </summary>
public static readonly ProfileCategory Empty = new("Empty", -1);
private readonly List<ProfileConfiguration> _profileConfigurations = new();
private bool _isCollapsed;
private bool _isSuspended;
private string _name;
@@ -31,14 +31,16 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
_name = name;
_order = order;
Entity = new ProfileCategoryEntity();
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(_profileConfigurations);
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>([]);
Save();
}
internal ProfileCategory(ProfileCategoryEntity entity)
{
_name = null!;
Entity = entity;
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(_profileConfigurations);
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>([]);
Load();
}
@@ -83,7 +85,7 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// <summary>
/// Gets a read only collection of the profiles inside this category
/// </summary>
public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; }
public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; private set; }
/// <summary>
/// Gets the unique ID of this category
@@ -96,29 +98,21 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// <summary>
/// Adds a profile configuration to this category
/// </summary>
public void AddProfileConfiguration(ProfileConfiguration configuration, int? targetIndex)
public void AddProfileConfiguration(ProfileConfiguration configuration, ProfileConfiguration? target)
{
// TODO: Look into this, it doesn't seem to make sense
// Removing the original will shift every item in the list forwards, keep that in mind with the target index
if (configuration.Category == this && targetIndex != null && targetIndex.Value > _profileConfigurations.IndexOf(configuration))
targetIndex -= 1;
List<ProfileConfiguration> targetList = ProfileConfigurations.Where(c => c!= configuration).ToList();
configuration.Category.RemoveProfileConfiguration(configuration);
if (targetIndex != null)
{
targetIndex = Math.Clamp(targetIndex.Value, 0, _profileConfigurations.Count);
_profileConfigurations.Insert(targetIndex.Value, configuration);
}
if (target != null)
targetList.Insert(targetList.IndexOf(target), configuration);
else
{
_profileConfigurations.Add(configuration);
}
targetList.Add(configuration);
configuration.Category = this;
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(targetList);
for (int index = 0; index < _profileConfigurations.Count; index++)
_profileConfigurations[index].Order = index;
for (int index = 0; index < ProfileConfigurations.Count; index++)
ProfileConfigurations[index].Order = index;
OnProfileConfigurationAdded(new ProfileConfigurationEventArgs(configuration));
}
@@ -156,11 +150,10 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
internal void RemoveProfileConfiguration(ProfileConfiguration configuration)
{
if (!_profileConfigurations.Remove(configuration))
return;
for (int index = 0; index < _profileConfigurations.Count; index++)
_profileConfigurations[index].Order = index;
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(ProfileConfigurations.Where(pc => pc != configuration).ToList());
for (int index = 0; index < ProfileConfigurations.Count; index++)
ProfileConfigurations[index].Order = index;
OnProfileConfigurationRemoved(new ProfileConfigurationEventArgs(configuration));
}
@@ -174,9 +167,7 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
IsSuspended = Entity.IsSuspended;
Order = Entity.Order;
_profileConfigurations.Clear();
foreach (ProfileConfigurationEntity entityProfileConfiguration in Entity.ProfileConfigurations)
_profileConfigurations.Add(new ProfileConfiguration(this, entityProfileConfiguration));
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(Entity.ProfileConfigurations.Select(pc => new ProfileConfiguration(this, pc)).ToList());
}
/// <inheritdoc />
@@ -186,13 +177,10 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
Entity.IsCollapsed = IsCollapsed;
Entity.IsSuspended = IsSuspended;
Entity.Order = Order;
Entity.ProfileConfigurations.Clear();
foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
{
profileConfiguration.Save();
Entity.ProfileConfigurations.Add(profileConfiguration.Entity);
}
}
#endregion
@@ -37,13 +37,13 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable,
_name = name;
_category = category;
Entity = new ProfileConfigurationEntity();
Entity = new ProfileContainerEntity();
Icon = new ProfileConfigurationIcon(Entity);
Icon.SetIconByName(icon);
ActivationCondition = new NodeScript<bool>("Activate profile", "Whether or not the profile should be active", this);
}
internal ProfileConfiguration(ProfileCategory category, ProfileConfigurationEntity entity)
internal ProfileConfiguration(ProfileCategory category, ProfileContainerEntity entity)
{
// Will be loaded from the entity
_name = null!;
@@ -192,12 +192,12 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable,
/// <summary>
/// Gets the entity used by this profile config
/// </summary>
public ProfileConfigurationEntity Entity { get; }
public ProfileContainerEntity Entity { get; }
/// <summary>
/// Gets the ID of the profile of this profile configuration
/// </summary>
public Guid ProfileId => Entity.ProfileId;
public Guid ProfileId => Entity.Profile.Id;
#region Overrides of BreakableModel
@@ -265,8 +265,8 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable,
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ModuleId);
IsMissingModule = Module == null && Entity.ModuleId != null;
Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ProfileConfiguration.ModuleId);
IsMissingModule = Module == null && Entity.ProfileConfiguration.ModuleId != null;
}
/// <inheritdoc />
@@ -284,20 +284,20 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable,
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Name = Entity.Name;
IsSuspended = Entity.IsSuspended;
ActivationBehaviour = (ActivationBehaviour) Entity.ActivationBehaviour;
HotkeyMode = (ProfileConfigurationHotkeyMode) Entity.HotkeyMode;
FadeInAndOut = Entity.FadeInAndOut;
Order = Entity.Order;
Name = Entity.ProfileConfiguration.Name;
IsSuspended = Entity.ProfileConfiguration.IsSuspended;
ActivationBehaviour = (ActivationBehaviour) Entity.ProfileConfiguration.ActivationBehaviour;
HotkeyMode = (ProfileConfigurationHotkeyMode) Entity.ProfileConfiguration.HotkeyMode;
FadeInAndOut = Entity.ProfileConfiguration.FadeInAndOut;
Order = Entity.ProfileConfiguration.Order;
Icon.Load();
if (Entity.ActivationCondition != null)
ActivationCondition.LoadFromEntity(Entity.ActivationCondition);
if (Entity.ProfileConfiguration.ActivationCondition != null)
ActivationCondition.LoadFromEntity(Entity.ProfileConfiguration.ActivationCondition);
EnableHotkey = Entity.EnableHotkey != null ? new Hotkey(Entity.EnableHotkey) : null;
DisableHotkey = Entity.DisableHotkey != null ? new Hotkey(Entity.DisableHotkey) : null;
EnableHotkey = Entity.ProfileConfiguration.EnableHotkey != null ? new Hotkey(Entity.ProfileConfiguration.EnableHotkey) : null;
DisableHotkey = Entity.ProfileConfiguration.DisableHotkey != null ? new Hotkey(Entity.ProfileConfiguration.DisableHotkey) : null;
}
/// <inheritdoc />
@@ -306,26 +306,26 @@ public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable,
if (_disposed)
throw new ObjectDisposedException("ProfileConfiguration");
Entity.Name = Name;
Entity.IsSuspended = IsSuspended;
Entity.ActivationBehaviour = (int) ActivationBehaviour;
Entity.HotkeyMode = (int) HotkeyMode;
Entity.ProfileCategoryId = Category.Entity.Id;
Entity.FadeInAndOut = FadeInAndOut;
Entity.Order = Order;
Entity.ProfileConfiguration.Name = Name;
Entity.ProfileConfiguration.IsSuspended = IsSuspended;
Entity.ProfileConfiguration.ActivationBehaviour = (int) ActivationBehaviour;
Entity.ProfileConfiguration.HotkeyMode = (int) HotkeyMode;
Entity.ProfileConfiguration.ProfileCategoryId = Category.Entity.Id;
Entity.ProfileConfiguration.FadeInAndOut = FadeInAndOut;
Entity.ProfileConfiguration.Order = Order;
Icon.Save();
ActivationCondition.Save();
Entity.ActivationCondition = ActivationCondition.Entity;
Entity.ProfileConfiguration.ActivationCondition = ActivationCondition.Entity;
EnableHotkey?.Save();
Entity.EnableHotkey = EnableHotkey?.Entity;
Entity.ProfileConfiguration.EnableHotkey = EnableHotkey?.Entity;
DisableHotkey?.Save();
Entity.DisableHotkey = DisableHotkey?.Entity;
Entity.ProfileConfiguration.DisableHotkey = DisableHotkey?.Entity;
if (!IsMissingModule)
Entity.ModuleId = Module?.Id;
Entity.ProfileConfiguration.ModuleId = Module?.Id;
}
#endregion
@@ -10,13 +10,13 @@ namespace Artemis.Core;
/// </summary>
public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
{
private readonly ProfileConfigurationEntity _entity;
private readonly ProfileContainerEntity _entity;
private bool _fill;
private string? _iconName;
private Stream? _iconStream;
private byte[]? _iconBytes;
private ProfileConfigurationIconType _iconType;
internal ProfileConfigurationIcon(ProfileConfigurationEntity entity)
internal ProfileConfigurationIcon(ProfileContainerEntity entity)
{
_entity = entity;
}
@@ -48,6 +48,15 @@ public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
set => SetAndNotify(ref _fill, value);
}
/// <summary>
/// Gets or sets the icon bytes if <see cref="IconType" /> is <see cref="ProfileConfigurationIconType.BitmapImage" />
/// </summary>
public byte[]? IconBytes
{
get => _iconBytes;
private set => SetAndNotify(ref _iconBytes, value);
}
/// <summary>
/// Updates the <see cref="IconName" /> to the provided value and changes the <see cref="IconType" /> is
/// <see cref="ProfileConfigurationIconType.MaterialIcon" />
@@ -55,9 +64,9 @@ public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
/// <param name="iconName">The name of the icon</param>
public void SetIconByName(string iconName)
{
if (iconName == null) throw new ArgumentNullException(nameof(iconName));
ArgumentNullException.ThrowIfNull(iconName);
_iconStream?.Dispose();
IconBytes = null;
IconName = iconName;
IconType = ProfileConfigurationIconType.MaterialIcon;
@@ -65,42 +74,27 @@ public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
}
/// <summary>
/// Updates the stream returned by <see cref="GetIconStream" /> to the provided stream
/// Updates the <see cref="IconBytes" /> to the provided value and changes the <see cref="IconType" /> is
/// </summary>
/// <param name="stream">The stream to copy</param>
public void SetIconByStream(Stream stream)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
ArgumentNullException.ThrowIfNull(stream);
_iconStream?.Dispose();
_iconStream = new MemoryStream();
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(_iconStream);
_iconStream.Seek(0, SeekOrigin.Begin);
using (MemoryStream ms = new())
{
stream.CopyTo(ms);
IconBytes = ms.ToArray();
}
IconName = null;
IconType = ProfileConfigurationIconType.BitmapImage;
OnIconUpdated();
}
/// <summary>
/// Creates a copy of the stream containing the icon
/// </summary>
/// <returns>A stream containing the icon</returns>
public Stream? GetIconStream()
{
if (_iconStream == null)
return null;
MemoryStream stream = new();
_iconStream.CopyTo(stream);
stream.Seek(0, SeekOrigin.Begin);
_iconStream.Seek(0, SeekOrigin.Begin);
return stream;
}
/// <summary>
/// Occurs when the icon was updated
/// </summary>
@@ -119,21 +113,24 @@ public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
/// <inheritdoc />
public void Load()
{
IconType = (ProfileConfigurationIconType) _entity.IconType;
Fill = _entity.IconFill;
if (IconType != ProfileConfigurationIconType.MaterialIcon)
return;
IconType = (ProfileConfigurationIconType) _entity.ProfileConfiguration.IconType;
Fill = _entity.ProfileConfiguration.IconFill;
IconName = _entity.MaterialIcon;
if (IconType == ProfileConfigurationIconType.MaterialIcon)
IconName = _entity.ProfileConfiguration.MaterialIcon;
else
IconBytes = _entity.Icon;
OnIconUpdated();
}
/// <inheritdoc />
public void Save()
{
_entity.IconType = (int) IconType;
_entity.MaterialIcon = IconType == ProfileConfigurationIconType.MaterialIcon ? IconName : null;
_entity.IconFill = Fill;
_entity.ProfileConfiguration.IconType = (int) IconType;
_entity.ProfileConfiguration.MaterialIcon = IconType == ProfileConfigurationIconType.MaterialIcon ? IconName : null;
_entity.ProfileConfiguration.IconFill = Fill;
_entity.Icon = IconBytes ?? Array.Empty<byte>();
}
#endregion
@@ -14,7 +14,7 @@ public class ArtemisDeviceInputIdentifier
/// used by
/// </param>
/// <param name="identifier">A value used to identify the device</param>
internal ArtemisDeviceInputIdentifier(string inputProvider, object identifier)
internal ArtemisDeviceInputIdentifier(string inputProvider, string identifier)
{
InputProvider = inputProvider;
Identifier = identifier;
@@ -28,5 +28,5 @@ public class ArtemisDeviceInputIdentifier
/// <summary>
/// Gets or sets a value used to identify the device
/// </summary>
public object Identifier { get; set; }
public string Identifier { get; set; }
}