diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 7e0112af7..423f50797 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -434,7 +434,6 @@ internal class ProfileService : IProfileService } // A new GUID will be given on save - configurationEntity.FileIconId = Guid.Empty; ProfileConfiguration profileConfiguration = new(category, containerEntity); if (nameAffix != null) profileConfiguration.Name = $"{profileConfiguration.Name} - {nameAffix}"; diff --git a/src/Artemis.Storage.Migrator/Artemis.Storage.Migrator.csproj b/src/Artemis.Storage.Migrator/Artemis.Storage.Migrator.csproj index f35f98a79..dbc3a660d 100644 --- a/src/Artemis.Storage.Migrator/Artemis.Storage.Migrator.csproj +++ b/src/Artemis.Storage.Migrator/Artemis.Storage.Migrator.csproj @@ -13,6 +13,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/General/QueuedActionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/General/QueuedActionEntity.cs new file mode 100644 index 000000000..8244c57ec --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/General/QueuedActionEntity.cs @@ -0,0 +1,15 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.General; + +public class QueuedActionEntity +{ + public QueuedActionEntity() + { + Parameters = new Dictionary(); + } + + public Guid Id { get; set; } + public string Type { get; set; } = string.Empty; + public DateTimeOffset CreatedAt { get; set; } + + public Dictionary Parameters { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/General/ReleaseEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/General/ReleaseEntity.cs new file mode 100644 index 000000000..a0d9795bf --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/General/ReleaseEntity.cs @@ -0,0 +1,19 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.General; + +public class ReleaseEntity +{ + public Guid Id { get; set; } + + public string Version { get; set; } = string.Empty; + public DateTimeOffset? InstalledAt { get; set; } + + public Storage.Entities.General.ReleaseEntity Migrate() + { + return new Storage.Entities.General.ReleaseEntity() + { + Id = Id, + Version = Version, + InstalledAt = InstalledAt ?? DateTimeOffset.Now + }; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/General/ScriptConfigurationEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/General/ScriptConfigurationEntity.cs new file mode 100644 index 000000000..4c1743fb6 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/General/ScriptConfigurationEntity.cs @@ -0,0 +1,10 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.General; + +public class ScriptConfigurationEntity +{ + public Guid Id { get; set; } + + public string Name { get; set; } = string.Empty; + public string ScriptingProviderId { get; set; } = string.Empty; + public string? ScriptContent { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginEntity.cs new file mode 100644 index 000000000..779b42fff --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginEntity.cs @@ -0,0 +1,45 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Plugins; + +/// +/// Represents the configuration of a plugin, each plugin has one configuration +/// +public class PluginEntity +{ + public PluginEntity() + { + Features = new List(); + } + + public Guid Id { get; set; } + public bool IsEnabled { get; set; } + + public List Features { get; set; } + + public Artemis.Storage.Entities.Plugins.PluginEntity Migrate() + { + return new Artemis.Storage.Entities.Plugins.PluginEntity() + { + Id = Id, + IsEnabled = IsEnabled, + Features = Features.Select(f => f.Migrate()).ToList() + }; + } +} + +/// +/// Represents the configuration of a plugin feature, each feature has one configuration +/// +public class PluginFeatureEntity +{ + public string Type { get; set; } = string.Empty; + public bool IsEnabled { get; set; } + + public Artemis.Storage.Entities.Plugins.PluginFeatureEntity Migrate() + { + return new Artemis.Storage.Entities.Plugins.PluginFeatureEntity() + { + Type = Type, + IsEnabled = IsEnabled + }; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginSettingEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginSettingEntity.cs new file mode 100644 index 000000000..2d10b80e2 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Plugins/PluginSettingEntity.cs @@ -0,0 +1,24 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Plugins; + +/// +/// Represents the setting of a plugin, a plugin can have multiple settings +/// +public class PluginSettingEntity +{ + public Guid Id { get; set; } + public Guid PluginGuid { get; set; } + + public string Name { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + + public Artemis.Storage.Entities.Plugins.PluginSettingEntity Migrate() + { + return new Storage.Entities.Plugins.PluginSettingEntity + { + Id = Id, + PluginGuid = PluginGuid, + Name = Name, + Value = Value + }; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Abstract/RenderElementEntity.cs new file mode 100644 index 000000000..98f01a41e --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Abstract/RenderElementEntity.cs @@ -0,0 +1,14 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract; + +public abstract class RenderElementEntity +{ + public Guid Id { get; set; } + public Guid ParentId { get; set; } + + public List LayerEffects { get; set; } = new(); + + public IConditionEntity? DisplayCondition { get; set; } + public TimelineEntity? Timeline { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/CategoryAdaptionHintEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/CategoryAdaptionHintEntity.cs new file mode 100644 index 000000000..3576c5cde --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/CategoryAdaptionHintEntity.cs @@ -0,0 +1,10 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; + +public class CategoryAdaptionHintEntity : IAdaptionHintEntity +{ + public int Category { get; set; } + + public bool LimitAmount { get; set; } + public int Skip { get; set; } + public int Amount { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/DeviceAdaptionHintEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/DeviceAdaptionHintEntity.cs new file mode 100644 index 000000000..f7a078867 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/DeviceAdaptionHintEntity.cs @@ -0,0 +1,10 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; + +public class DeviceAdaptionHintEntity : IAdaptionHintEntity +{ + public int DeviceType { get; set; } + + public bool LimitAmount { get; set; } + public int Skip { get; set; } + public int Amount { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/IAdaptionHintEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/IAdaptionHintEntity.cs new file mode 100644 index 000000000..9ff03f4bd --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/IAdaptionHintEntity.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; + +[JsonDerivedType(typeof(CategoryAdaptionHintEntity), "Category")] +[JsonDerivedType(typeof(DeviceAdaptionHintEntity), "Device")] +[JsonDerivedType(typeof(KeyboardSectionAdaptionHintEntity), "KeyboardSection")] +[JsonDerivedType(typeof(SingleLedAdaptionHintEntity), "SingleLed")] +public interface IAdaptionHintEntity; \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/KeyboardSectionAdaptionHintEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/KeyboardSectionAdaptionHintEntity.cs new file mode 100644 index 000000000..075f3ad9a --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/KeyboardSectionAdaptionHintEntity.cs @@ -0,0 +1,6 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; + +public class KeyboardSectionAdaptionHintEntity : IAdaptionHintEntity +{ + public int Section { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/SingleLedAdaptionHintEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/SingleLedAdaptionHintEntity.cs new file mode 100644 index 000000000..98d192595 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/AdaptionHints/SingleLedAdaptionHintEntity.cs @@ -0,0 +1,10 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; + +public class SingleLedAdaptionHintEntity : IAdaptionHintEntity +{ + public int LedId { get; set; } + + public bool LimitAmount { get; set; } + public int Skip { get; set; } + public int Amount { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs new file mode 100644 index 000000000..e644c4331 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/AlwaysOnConditionEntity.cs @@ -0,0 +1,3 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +public class AlwaysOnConditionEntity : IConditionEntity; \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/EventConditionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/EventConditionEntity.cs new file mode 100644 index 000000000..2fea241b0 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/EventConditionEntity.cs @@ -0,0 +1,12 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +public class EventConditionEntity : IConditionEntity +{ + public int TriggerMode { get; set; } + public int OverlapMode { get; set; } + public int ToggleOffMode { get; set; } + public DataModelPathEntity? EventPath { get; set; } + public NodeScriptEntity? Script { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/IConditionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/IConditionEntity.cs new file mode 100644 index 000000000..bc78042aa --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/IConditionEntity.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +[JsonDerivedType(typeof(AlwaysOnConditionEntity), "AlwaysOn")] +[JsonDerivedType(typeof(EventConditionEntity), "Event")] +[JsonDerivedType(typeof(PlayOnceConditionEntity), "PlayOnce")] +[JsonDerivedType(typeof(StaticConditionEntity), "Static")] +public interface IConditionEntity; \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/PlayOnceConditionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/PlayOnceConditionEntity.cs new file mode 100644 index 000000000..82fb095b1 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/PlayOnceConditionEntity.cs @@ -0,0 +1,3 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +public class PlayOnceConditionEntity : IConditionEntity; \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/StaticConditionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/StaticConditionEntity.cs new file mode 100644 index 000000000..065bd92bd --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Conditions/StaticConditionEntity.cs @@ -0,0 +1,10 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; + +public class StaticConditionEntity : IConditionEntity +{ + public int PlayMode { get; set; } + public int StopMode { get; set; } + public NodeScriptEntity? Script { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataBindings/DataBindingEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataBindings/DataBindingEntity.cs new file mode 100644 index 000000000..74aa3851e --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataBindings/DataBindingEntity.cs @@ -0,0 +1,9 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.DataBindings; + +public class DataBindingEntity +{ + public bool IsEnabled { get; set; } + public NodeScriptEntity? NodeScript { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataModelPathEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataModelPathEntity.cs new file mode 100644 index 000000000..0783e603f --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/DataModelPathEntity.cs @@ -0,0 +1,8 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class DataModelPathEntity +{ + public string Path { get; set; } = string.Empty; + public string? DataModelId { get; set; } + public string? Type { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/FolderEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/FolderEntity.cs new file mode 100644 index 000000000..ef181ff5d --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/FolderEntity.cs @@ -0,0 +1,17 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract; +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class FolderEntity : RenderElementEntity +{ + public int Order { get; set; } + public string? Name { get; set; } + public bool IsExpanded { get; set; } + public bool Suspended { get; set; } + + [BsonRef("ProfileEntity")] + public ProfileEntity Profile { get; set; } = null!; + + public Guid ProfileId { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/KeyframeEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/KeyframeEntity.cs new file mode 100644 index 000000000..fae285769 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/KeyframeEntity.cs @@ -0,0 +1,9 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class KeyframeEntity +{ + public TimeSpan Position { get; set; } + public int Timeline { get; set; } + public string Value { get; set; } = string.Empty; + public int EasingFunction { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerBrushEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerBrushEntity.cs new file mode 100644 index 000000000..f0579e9ac --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerBrushEntity.cs @@ -0,0 +1,9 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class LayerBrushEntity +{ + public string ProviderId { get; set; } = string.Empty; + public string BrushType { get; set; } = string.Empty; + + public PropertyGroupEntity? PropertyGroup { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEffectEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEffectEntity.cs new file mode 100644 index 000000000..b3b8e5bdb --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEffectEntity.cs @@ -0,0 +1,12 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class LayerEffectEntity +{ + public string ProviderId { get; set; } = string.Empty; + public string EffectType { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public bool HasBeenRenamed { get; set; } + public int Order { get; set; } + + public PropertyGroupEntity? PropertyGroup { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEntity.cs new file mode 100644 index 000000000..57d5ea555 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LayerEntity.cs @@ -0,0 +1,30 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract; +using Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints; +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class LayerEntity : RenderElementEntity +{ + public LayerEntity() + { + Leds = new List(); + AdaptionHints = new List(); + } + + public int Order { get; set; } + public string? Name { get; set; } + public bool Suspended { get; set; } + + public List Leds { get; set; } + public List AdaptionHints { get; set; } + + public PropertyGroupEntity? GeneralPropertyGroup { get; set; } + public PropertyGroupEntity? TransformPropertyGroup { get; set; } + public LayerBrushEntity? LayerBrush { get; set; } + + [BsonRef("ProfileEntity")] + public ProfileEntity Profile { get; set; } = null!; + + public Guid ProfileId { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LedEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LedEntity.cs new file mode 100644 index 000000000..b456a138c --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/LedEntity.cs @@ -0,0 +1,36 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class LedEntity +{ + public string LedName { get; set; } = string.Empty; + public string DeviceIdentifier { get; set; } = string.Empty; + + public int? PhysicalLayout { get; set; } + + #region LedEntityEqualityComparer + + private sealed class LedEntityEqualityComparer : IEqualityComparer + { + public bool Equals(LedEntity? x, LedEntity? y) + { + if (ReferenceEquals(x, y)) + return true; + if (ReferenceEquals(x, null)) + return false; + if (ReferenceEquals(y, null)) + return false; + if (x.GetType() != y.GetType()) + return false; + return x.LedName == y.LedName && x.DeviceIdentifier == y.DeviceIdentifier && x.PhysicalLayout == y.PhysicalLayout; + } + + public int GetHashCode(LedEntity obj) + { + return HashCode.Combine(obj.LedName, obj.DeviceIdentifier, obj.PhysicalLayout); + } + } + + public static IEqualityComparer LedEntityComparer { get; } = new LedEntityEqualityComparer(); + + #endregion +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeConnectionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeConnectionEntity.cs new file mode 100644 index 000000000..d256cce6b --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeConnectionEntity.cs @@ -0,0 +1,29 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +public class NodeConnectionEntity +{ + public NodeConnectionEntity() + { + } + + public NodeConnectionEntity(NodeConnectionEntity nodeConnectionEntity) + { + SourceType = nodeConnectionEntity.SourceType; + SourceNode = nodeConnectionEntity.SourceNode; + TargetNode = nodeConnectionEntity.TargetNode; + SourcePinCollectionId = nodeConnectionEntity.SourcePinCollectionId; + SourcePinId = nodeConnectionEntity.SourcePinId; + TargetType = nodeConnectionEntity.TargetType; + TargetPinCollectionId = nodeConnectionEntity.TargetPinCollectionId; + TargetPinId = nodeConnectionEntity.TargetPinId; + } + + public string SourceType { get; set; } = string.Empty; + public Guid SourceNode { get; set; } + public Guid TargetNode { get; set; } + public int SourcePinCollectionId { get; set; } + public int SourcePinId { get; set; } + public string TargetType { get; set; } = string.Empty; + public int TargetPinCollectionId { get; set; } + public int TargetPinId { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeEntity.cs new file mode 100644 index 000000000..b25e9b0f7 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeEntity.cs @@ -0,0 +1,38 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +public class NodeEntity +{ + public NodeEntity() + { + PinCollections = new List(); + } + + public NodeEntity(NodeEntity nodeEntity) + { + Id = nodeEntity.Id; + Type = nodeEntity.Type; + ProviderId = nodeEntity.ProviderId; + + Name = nodeEntity.Name; + Description = nodeEntity.Description; + IsExitNode = nodeEntity.IsExitNode; + X = nodeEntity.X; + Y = nodeEntity.Y; + Storage = nodeEntity.Storage; + + PinCollections = nodeEntity.PinCollections.Select(p => new NodePinCollectionEntity(p)).ToList(); + } + + public Guid Id { get; set; } + public string Type { get; set; } = string.Empty; + public string ProviderId { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public bool IsExitNode { get; set; } + public double X { get; set; } + public double Y { get; set; } + public string Storage { get; set; } = string.Empty; + + public List PinCollections { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodePinCollectionEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodePinCollectionEntity.cs new file mode 100644 index 000000000..cdec98e6a --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodePinCollectionEntity.cs @@ -0,0 +1,19 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +public class NodePinCollectionEntity +{ + public NodePinCollectionEntity() + { + } + + public NodePinCollectionEntity(NodePinCollectionEntity nodePinCollectionEntity) + { + Id = nodePinCollectionEntity.Id; + Direction = nodePinCollectionEntity.Direction; + Amount = nodePinCollectionEntity.Amount; + } + + public int Id { get; set; } + public int Direction { set; get; } + public int Amount { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeScriptEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeScriptEntity.cs new file mode 100644 index 000000000..5afeb39df --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/Nodes/NodeScriptEntity.cs @@ -0,0 +1,16 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +public class NodeScriptEntity +{ + public NodeScriptEntity() + { + Nodes = new List(); + Connections = new List(); + } + + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + + public List Nodes { get; set; } + public List Connections { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileCategoryEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileCategoryEntity.cs new file mode 100644 index 000000000..0323db70d --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileCategoryEntity.cs @@ -0,0 +1,78 @@ +using Artemis.Core; +using Artemis.Storage.Entities.Profile; +using LiteDB; +using Serilog; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class ProfileCategoryEntity +{ + public Guid Id { get; set; } + + public string Name { get; set; } = string.Empty; + public bool IsCollapsed { get; set; } + public bool IsSuspended { get; set; } + public int Order { get; set; } + + public List ProfileConfigurations { get; set; } = new(); + + public Storage.Entities.Profile.ProfileCategoryEntity Migrate(ILogger logger, List legacyProfiles, ILiteStorage profileIcons) + { + Storage.Entities.Profile.ProfileCategoryEntity category = new() + { + Id = Id, + Name = Name, + IsCollapsed = IsCollapsed, + IsSuspended = IsSuspended, + Order = Order + }; + + foreach (ProfileConfigurationEntity legacyProfileConfiguration in ProfileConfigurations) + { + // Find the profile + ProfileEntity? legacyProfile = legacyProfiles.FirstOrDefault(p => p.Id == legacyProfileConfiguration.ProfileId); + if (legacyProfile == null) + { + logger.Information("Profile not found for profile configuration {ProfileId}", legacyProfileConfiguration.ProfileId); + continue; + } + + // Clone to the new format via JSON, as both are serializable + string profileJson = CoreJson.Serialize(legacyProfile); + string configJson = CoreJson.Serialize(legacyProfileConfiguration); + Storage.Entities.Profile.ProfileEntity? profile = CoreJson.Deserialize(profileJson); + Storage.Entities.Profile.ProfileConfigurationEntity? config = CoreJson.Deserialize(configJson); + + if (profile == null) + { + logger.Information("Failed to deserialize profile JSON for profile configuration {ProfileId}", legacyProfileConfiguration.ProfileId); + continue; + } + + if (config == null) + { + logger.Information("Failed to deserialize profile configuration JSON for profile configuration {ProfileId}", legacyProfileConfiguration.ProfileId); + continue; + } + + // Add a container + ProfileContainerEntity container = new() + { + Profile = profile, + ProfileConfiguration = config, + }; + + // If available, download the profile icon + if (profileIcons.Exists(legacyProfileConfiguration.FileIconId)) + { + using MemoryStream memoryStream = new(); + profileIcons.Download(legacyProfileConfiguration.FileIconId, memoryStream); + container.Icon = memoryStream.ToArray(); + } + + category.ProfileConfigurations.Add(container); + } + + return category; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationEntity.cs new file mode 100644 index 000000000..89f68fe1d --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationEntity.cs @@ -0,0 +1,29 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class ProfileConfigurationEntity +{ + public string Name { get; set; } = string.Empty; + public string? MaterialIcon { get; set; } + public Guid FileIconId { get; set; } + public int IconType { get; set; } + public bool IconFill { get; set; } + public int Order { get; set; } + + public bool IsSuspended { get; set; } + public int ActivationBehaviour { get; set; } + public NodeScriptEntity? ActivationCondition { get; set; } + + public int HotkeyMode { get; set; } + public ProfileConfigurationHotkeyEntity? EnableHotkey { get; set; } + public ProfileConfigurationHotkeyEntity? DisableHotkey { get; set; } + + public string? ModuleId { get; set; } + + public Guid ProfileCategoryId { get; set; } + public Guid ProfileId { get; set; } + + public bool FadeInAndOut { get; set; } + public int Version { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationHotkeyEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationHotkeyEntity.cs new file mode 100644 index 000000000..a0572cc91 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileConfigurationHotkeyEntity.cs @@ -0,0 +1,7 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class ProfileConfigurationHotkeyEntity +{ + public int? Key { get; set; } + public int? Modifiers { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileEntity.cs new file mode 100644 index 000000000..cbfde4b61 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/ProfileEntity.cs @@ -0,0 +1,32 @@ +using Artemis.Storage.Migrator.Legacy.Entities.General; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class ProfileEntity +{ + public ProfileEntity() + { + Folders = new List(); + Layers = new List(); + ScriptConfigurations = new List(); + } + + public Guid Id { get; set; } + + public string Name { get; set; } = string.Empty; + public bool IsFreshImport { get; set; } + + public List Folders { get; set; } + public List Layers { get; set; } + public List ScriptConfigurations { get; set; } + + public void UpdateGuid(Guid guid) + { + Guid oldGuid = Id; + Id = guid; + + FolderEntity? rootFolder = Folders.FirstOrDefault(f => f.ParentId == oldGuid); + if (rootFolder != null) + rootFolder.ParentId = Id; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyEntity.cs new file mode 100644 index 000000000..1a0a5d4b6 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyEntity.cs @@ -0,0 +1,13 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile.DataBindings; + +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class PropertyEntity +{ + public string Identifier { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public bool KeyframesEnabled { get; set; } + + public DataBindingEntity? DataBinding { get; set; } + public List KeyframeEntities { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyGroupEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyGroupEntity.cs new file mode 100644 index 000000000..8097d36ba --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/PropertyGroupEntity.cs @@ -0,0 +1,8 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class PropertyGroupEntity +{ + public string Identifier { get; set; } = string.Empty; + public List Properties { get; set; } = new(); + public List PropertyGroups { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/TimelineEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/TimelineEntity.cs new file mode 100644 index 000000000..a9401abac --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Profile/TimelineEntity.cs @@ -0,0 +1,8 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Profile; + +public class TimelineEntity +{ + public TimeSpan StartSegmentLength { get; set; } + public TimeSpan MainSegmentLength { get; set; } + public TimeSpan EndSegmentLength { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Surface/DeviceEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Surface/DeviceEntity.cs new file mode 100644 index 000000000..52d86fbcc --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Surface/DeviceEntity.cs @@ -0,0 +1,78 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Surface; + +public class DeviceEntity +{ + public DeviceEntity() + { + InputIdentifiers = new List(); + InputMappings = new List(); + Categories = new List(); + } + + public string Id { get; set; } = string.Empty; + public string DeviceProvider { get; set; } = string.Empty; + public float X { get; set; } + public float Y { get; set; } + public float Rotation { get; set; } + public float Scale { get; set; } + public int ZIndex { get; set; } + public float RedScale { get; set; } + public float GreenScale { get; set; } + public float BlueScale { get; set; } + public bool IsEnabled { get; set; } + + public int PhysicalLayout { get; set; } + public string? LogicalLayout { get; set; } + public string? LayoutType { get; set; } + public string? LayoutParameter { get; set; } + + public List InputIdentifiers { get; set; } + public List InputMappings { get; set; } + public List Categories { get; set; } + + public Storage.Entities.Surface.DeviceEntity Migrate() + { + // All properties match, return a copy + return new Storage.Entities.Surface.DeviceEntity() + { + Id = Id, + DeviceProvider = DeviceProvider, + X = X, + Y = Y, + Rotation = Rotation, + Scale = Scale, + ZIndex = ZIndex, + RedScale = RedScale, + GreenScale = GreenScale, + BlueScale = BlueScale, + IsEnabled = IsEnabled, + PhysicalLayout = PhysicalLayout, + LogicalLayout = LogicalLayout, + LayoutType = LayoutType, + LayoutParameter = LayoutParameter, + InputIdentifiers = InputIdentifiers.Select(i => new Storage.Entities.Surface.DeviceInputIdentifierEntity + { + InputProvider = i.InputProvider, + Identifier = i.Identifier.ToString() ?? string.Empty + }).ToList(), + InputMappings = InputMappings.Select(i => new Storage.Entities.Surface.InputMappingEntity + { + OriginalLedId = i.OriginalLedId, + MappedLedId = i.MappedLedId + }).ToList(), + Categories = Categories + }; + } +} + +public class InputMappingEntity +{ + public int OriginalLedId { get; set; } + public int MappedLedId { get; set; } +} + +public class DeviceInputIdentifierEntity +{ + public string InputProvider { get; set; } = string.Empty; + public object Identifier { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Entities/Workshop/EntryEntity.cs b/src/Artemis.Storage.Migrator/Legacy/Entities/Workshop/EntryEntity.cs new file mode 100644 index 000000000..c5fea30d2 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Entities/Workshop/EntryEntity.cs @@ -0,0 +1,35 @@ +namespace Artemis.Storage.Migrator.Legacy.Entities.Workshop; + +public class EntryEntity +{ + public Guid Id { get; set; } + + public long EntryId { get; set; } + public int EntryType { get; set; } + + public string Author { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + + public long ReleaseId { get; set; } + public string ReleaseVersion { get; set; } = string.Empty; + public DateTimeOffset InstalledAt { get; set; } + + public Dictionary? Metadata { get; set; } + + public Storage.Entities.Workshop.EntryEntity Migrate() + { + // Create a copy + return new Storage.Entities.Workshop.EntryEntity() + { + Id = Id, + EntryId = EntryId, + EntryType = EntryType, + Author = Author, + Name = Name, + ReleaseId = ReleaseId, + ReleaseVersion = ReleaseVersion, + InstalledAt = InstalledAt, + Metadata = Metadata ?? new Dictionary() + }; + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/IProfileMigration.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/IProfileMigration.cs new file mode 100644 index 000000000..f5e9d8092 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/IProfileMigration.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Nodes; + +namespace Artemis.Storage.Migrator.Legacy.Migrations; + +public interface IProfileMigration +{ + int Version { get; } + void Migrate(JsonObject configurationJson, JsonObject profileJson); +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/IStorageMigration.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/IStorageMigration.cs new file mode 100644 index 000000000..4aee9aabd --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/IStorageMigration.cs @@ -0,0 +1,9 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations; + +public interface IStorageMigration +{ + int UserVersion { get; } + void Apply(LiteRepository repository); +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0020AvaloniaReset.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0020AvaloniaReset.cs new file mode 100644 index 000000000..de3478e7b --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0020AvaloniaReset.cs @@ -0,0 +1,17 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0020AvaloniaReset : IStorageMigration +{ + public int UserVersion => 20; + + public void Apply(LiteRepository repository) + { + repository.Database.Commit(); + + List collectionNames = repository.Database.GetCollectionNames().ToList(); + foreach (string collectionName in collectionNames) + repository.Database.DropCollection(collectionName); + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0021GradientNodes.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0021GradientNodes.cs new file mode 100644 index 000000000..354525008 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0021GradientNodes.cs @@ -0,0 +1,87 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile; +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0021GradientNodes : IStorageMigration +{ + private void MigrateDataBinding(PropertyEntity property) + { + NodeScriptEntity? script = property.DataBinding?.NodeScript; + NodeEntity? exitNode = script?.Nodes.FirstOrDefault(s => s.IsExitNode); + if (script == null || exitNode == null) + return; + + // Create a new node at the same position of the exit node + NodeEntity gradientNode = new() + { + Id = Guid.NewGuid(), + Type = "ColorGradientNode", + ProviderId = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78", + Name = "Color Gradient", + Description = "Outputs a color gradient with the given colors", + X = exitNode.X, + Y = exitNode.Y, + Storage = property.Value // Copy the value of the property into the node storage + }; + script.Nodes.Add(gradientNode); + + // Move all connections of the exit node to the new node + foreach (NodeConnectionEntity connection in script.Connections) + { + if (connection.SourceNode == exitNode.Id) + { + connection.SourceNode = gradientNode.Id; + connection.SourcePinId++; + } + } + + // Connect the data binding node to the source node + script.Connections.Add(new NodeConnectionEntity + { + SourceType = "ColorGradient", + SourceNode = exitNode.Id, + SourcePinCollectionId = -1, + SourcePinId = 0, + TargetType = "ColorGradient", + TargetNode = gradientNode.Id, + TargetPinCollectionId = -1, + TargetPinId = 0 + }); + + // Move the exit node to the right + exitNode.X += 300; + exitNode.Y += 30; + } + + private void MigrateDataBinding(PropertyGroupEntity? propertyGroup) + { + if (propertyGroup == null) + return; + + foreach (PropertyGroupEntity propertyGroupPropertyGroup in propertyGroup.PropertyGroups) + MigrateDataBinding(propertyGroupPropertyGroup); + + foreach (PropertyEntity property in propertyGroup.Properties) + { + if (property.Value.StartsWith("[{\"Color\":\"") && property.DataBinding?.NodeScript != null && property.DataBinding.IsEnabled) + MigrateDataBinding(property); + } + } + + public int UserVersion => 21; + + public void Apply(LiteRepository repository) + { + // Find all color gradient data bindings, there's no really good way to do this so infer it from the value + List profiles = repository.Query().ToList(); + foreach (ProfileEntity profileEntity in profiles) + { + foreach (LayerEntity layer in profileEntity.Layers.Where(le => le.LayerBrush != null)) + MigrateDataBinding(layer.LayerBrush?.PropertyGroup); + + repository.Update(profileEntity); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0022TransitionNodes.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0022TransitionNodes.cs new file mode 100644 index 000000000..7556b7840 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0022TransitionNodes.cs @@ -0,0 +1,84 @@ +using Artemis.Storage.Migrator.Legacy.Entities.Profile; +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions; +using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes; +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0022TransitionNodes : IStorageMigration +{ + private void MigrateNodeScript(NodeScriptEntity? nodeScript) + { + if (nodeScript == null) + return; + + foreach (NodeEntity node in nodeScript.Nodes) + { + if (node.Type == "NumericEasingNode") + node.Type = "NumericTransitionNode"; + else if (node.Type == "ColorGradientEasingNode") + node.Type = "ColorGradientTransitionNode"; + else if (node.Type == "SKColorEasingNode") + node.Type = "SKColorTransitionNode"; + else if (node.Type == "EasingTypeNode") + node.Type = "EasingFunctionNode"; + } + } + + private void MigratePropertyGroup(PropertyGroupEntity? propertyGroup) + { + if (propertyGroup == null) + return; + + foreach (PropertyGroupEntity childPropertyGroup in propertyGroup.PropertyGroups) + MigratePropertyGroup(childPropertyGroup); + foreach (PropertyEntity property in propertyGroup.Properties) + MigrateNodeScript(property.DataBinding?.NodeScript); + } + + private void MigrateDisplayCondition(IConditionEntity? conditionEntity) + { + if (conditionEntity is EventConditionEntity eventConditionEntity) + MigrateNodeScript(eventConditionEntity.Script); + else if (conditionEntity is StaticConditionEntity staticConditionEntity) + MigrateNodeScript(staticConditionEntity.Script); + } + + public int UserVersion => 22; + + public void Apply(LiteRepository repository) + { + // Migrate profile configuration display conditions + List categories = repository.Query().ToList(); + foreach (ProfileCategoryEntity profileCategoryEntity in categories) + { + foreach (ProfileConfigurationEntity profileConfigurationEntity in profileCategoryEntity.ProfileConfigurations) + MigrateNodeScript(profileConfigurationEntity.ActivationCondition); + repository.Update(profileCategoryEntity); + } + + // Migrate profile display conditions and data bindings + List profiles = repository.Query().ToList(); + foreach (ProfileEntity profileEntity in profiles) + { + foreach (LayerEntity layer in profileEntity.Layers) + { + MigratePropertyGroup(layer.LayerBrush?.PropertyGroup); + MigratePropertyGroup(layer.GeneralPropertyGroup); + MigratePropertyGroup(layer.TransformPropertyGroup); + foreach (LayerEffectEntity layerEffectEntity in layer.LayerEffects) + MigratePropertyGroup(layerEffectEntity.PropertyGroup); + MigrateDisplayCondition(layer.DisplayCondition); + } + + foreach (FolderEntity folder in profileEntity.Folders) + { + foreach (LayerEffectEntity folderLayerEffect in folder.LayerEffects) + MigratePropertyGroup(folderLayerEffect.PropertyGroup); + MigrateDisplayCondition(folder.DisplayCondition); + } + + repository.Update(profileEntity); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0023LayoutProviders.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0023LayoutProviders.cs new file mode 100644 index 000000000..602ba6f2e --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0023LayoutProviders.cs @@ -0,0 +1,33 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0023LayoutProviders : IStorageMigration +{ + public int UserVersion => 23; + + public void Apply(LiteRepository repository) + { + ILiteCollection deviceEntities = repository.Database.GetCollection("DeviceEntity"); + List toUpdate = new(); + + foreach (BsonDocument bsonDocument in deviceEntities.FindAll()) + { + if (bsonDocument.TryGetValue("CustomLayoutPath", out BsonValue customLayoutPath) && customLayoutPath.IsString && !string.IsNullOrEmpty(customLayoutPath.AsString)) + { + bsonDocument.Add("LayoutType", new BsonValue("CustomPath")); + bsonDocument.Add("LayoutParameter", new BsonValue(customLayoutPath.AsString)); + } + else if (bsonDocument.TryGetValue("DisableDefaultLayout", out BsonValue disableDefaultLayout) && disableDefaultLayout.AsBoolean) + bsonDocument.Add("LayoutType", new BsonValue("None")); + else + bsonDocument.Add("LayoutType", new BsonValue("Default")); + + bsonDocument.Remove("CustomLayoutPath"); + bsonDocument.Remove("DisableDefaultLayout"); + toUpdate.Add(bsonDocument); + } + + deviceEntities.Update(toUpdate); + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0024NodeProviders.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0024NodeProviders.cs new file mode 100644 index 000000000..37d61c5d5 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0024NodeProviders.cs @@ -0,0 +1,105 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0024NodeProviders : IStorageMigration +{ + public int UserVersion => 24; + + public void Apply(LiteRepository repository) + { + ILiteCollection categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity"); + List categoriesToUpdate = new(); + foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll()) + { + BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray; + if (profiles != null) + { + foreach (BsonValue profile in profiles) + profile["Version"] = 1; + categoriesToUpdate.Add(profileCategoryBson); + } + } + categoryCollection.Update(categoriesToUpdate); + + ILiteCollection collection = repository.Database.GetCollection("ProfileEntity"); + List profilesToUpdate = new(); + foreach (BsonDocument profileBson in collection.FindAll()) + { + BsonArray? folders = profileBson["Folders"]?.AsArray; + BsonArray? layers = profileBson["Layers"]?.AsArray; + + if (folders != null) + { + foreach (BsonValue folder in folders) + MigrateProfileElement(folder.AsDocument); + } + + if (layers != null) + { + foreach (BsonValue layer in layers) + { + MigrateProfileElement(layer.AsDocument); + MigratePropertyGroup(layer.AsDocument["GeneralPropertyGroup"].AsDocument); + MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument); + MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument); + } + } + + profilesToUpdate.Add(profileBson); + } + + collection.Update(profilesToUpdate); + } + + private void MigrateProfileElement(BsonDocument profileElement) + { + BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray; + if (layerEffects != null) + { + foreach (BsonValue layerEffect in layerEffects) + MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument); + } + + BsonValue? displayCondition = profileElement["DisplayCondition"]; + if (displayCondition != null) + MigrateNodeScript(displayCondition.AsDocument["Script"].AsDocument); + } + + private void MigratePropertyGroup(BsonDocument? propertyGroup) + { + if (propertyGroup == null || propertyGroup.Keys.Count == 0) + return; + + BsonArray? properties = propertyGroup["Properties"]?.AsArray; + BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray; + + if (properties != null) + { + foreach (BsonValue property in properties) + MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument); + } + + if (propertyGroups != null) + { + foreach (BsonValue childPropertyGroup in propertyGroups) + MigratePropertyGroup(childPropertyGroup.AsDocument); + } + } + + private void MigrateNodeScript(BsonDocument? nodeScript) + { + if (nodeScript == null || nodeScript.Keys.Count == 0) + return; + + BsonArray? nodes = nodeScript["Nodes"]?.AsArray; + if (nodes == null) + return; + + foreach (BsonValue node in nodes) + { + node.AsDocument["Type"] = node.AsDocument["Type"]?.AsString?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes"); + node.AsDocument["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78"; + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0025NodeProvidersProfileConfig.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0025NodeProvidersProfileConfig.cs new file mode 100644 index 000000000..bbb121956 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0025NodeProvidersProfileConfig.cs @@ -0,0 +1,45 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0025NodeProvidersProfileConfig : IStorageMigration +{ + public int UserVersion => 25; + + public void Apply(LiteRepository repository) + { + ILiteCollection categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity"); + List toUpdate = new(); + foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll()) + { + BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray; + if (profiles != null) + { + foreach (BsonValue profile in profiles) + { + profile["Version"] = 2; + MigrateNodeScript(profile["ActivationCondition"]?.AsDocument); + } + toUpdate.Add(profileCategoryBson); + } + } + + categoryCollection.Update(toUpdate); + } + + private void MigrateNodeScript(BsonDocument? nodeScript) + { + if (nodeScript == null || nodeScript.Keys.Count == 0) + return; + + BsonArray? nodes = nodeScript["Nodes"]?.AsArray; + if (nodes == null) + return; + + foreach (BsonValue node in nodes) + { + node.AsDocument["Type"] = node.AsDocument["Type"]?.AsString?.Replace("Artemis.VisualScripting.Nodes", "Artemis.Plugins.Nodes.General.Nodes"); + node.AsDocument["ProviderId"] = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78"; + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0026NodeStorage.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0026NodeStorage.cs new file mode 100644 index 000000000..1212455bb --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0026NodeStorage.cs @@ -0,0 +1,227 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using LiteDB; +using Serilog; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0026NodeStorage : IStorageMigration +{ + private readonly ILogger _logger; + + public M0026NodeStorage(ILogger logger) + { + _logger = logger; + } + + public int UserVersion => 26; + + public void Apply(LiteRepository repository) + { + ILiteCollection categoryCollection = repository.Database.GetCollection("ProfileCategoryEntity"); + List toUpdate = new(); + foreach (BsonDocument profileCategoryBson in categoryCollection.FindAll()) + { + BsonArray? profiles = profileCategoryBson["ProfileConfigurations"]?.AsArray; + if (profiles != null) + { + foreach (BsonValue profile in profiles) + { + profile["Version"] = 4; + MigrateNodeScript(profile["ActivationCondition"]?.AsDocument); + } + + toUpdate.Add(profileCategoryBson); + } + } + + categoryCollection.Update(toUpdate); + + ILiteCollection collection = repository.Database.GetCollection("ProfileEntity"); + List profilesToUpdate = new(); + foreach (BsonDocument profileBson in collection.FindAll()) + { + BsonArray? folders = profileBson["Folders"]?.AsArray; + BsonArray? layers = profileBson["Layers"]?.AsArray; + + if (folders != null) + { + foreach (BsonValue folder in folders) + MigrateProfileElement(folder.AsDocument); + } + + if (layers != null) + { + foreach (BsonValue layer in layers) + { + MigrateProfileElement(layer.AsDocument); + MigratePropertyGroup(layer.AsDocument["GeneralPropertyGroup"].AsDocument); + MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument); + MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument); + } + } + + profilesToUpdate.Add(profileBson); + } + + collection.Update(profilesToUpdate); + } + + private void MigrateProfileElement(BsonDocument profileElement) + { + BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray; + if (layerEffects != null) + { + foreach (BsonValue layerEffect in layerEffects) + MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument); + } + + BsonValue? displayCondition = profileElement["DisplayCondition"]; + if (displayCondition != null) + MigrateNodeScript(displayCondition.AsDocument["Script"].AsDocument); + } + + private void MigratePropertyGroup(BsonDocument? propertyGroup) + { + if (propertyGroup == null || propertyGroup.Keys.Count == 0) + return; + + BsonArray? properties = propertyGroup["Properties"]?.AsArray; + BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray; + + if (properties != null) + { + foreach (BsonValue property in properties) + MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument); + } + + if (propertyGroups != null) + { + foreach (BsonValue childPropertyGroup in propertyGroups) + MigratePropertyGroup(childPropertyGroup.AsDocument); + } + } + + private void MigrateNodeScript(BsonDocument? nodeScript) + { + if (nodeScript == null || nodeScript.Keys.Count == 0) + return; + + BsonArray? nodes = nodeScript["Nodes"]?.AsArray; + if (nodes == null) + return; + + foreach (BsonValue node in nodes) + { + // Migrate the storage of the node + node["Storage"] = MigrateNodeStorageJson(node.AsDocument["Storage"]?.AsString, _logger); + } + } + + private static string? MigrateNodeStorageJson(string? json, ILogger logger) + { + if (string.IsNullOrEmpty(json)) + return json; + + try + { + JsonDocument jsonDocument = JsonDocument.Parse(json); + if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object) + return json; + + JsonObject? jsonObject = JsonObject.Create(jsonDocument.RootElement); + if (jsonObject == null) + return json; + + if (jsonObject["$type"] != null && jsonObject["$values"] != null) + { + JsonArray? values = jsonObject["$values"]?.AsArray(); + if (values != null) + { + foreach (JsonNode? jsonNode in values.ToList()) + { + if (jsonNode is JsonObject childObject) + ConvertToSystemTextJson(childObject); + } + + return values.ToJsonString(); + } + } + else + { + ConvertToSystemTextJson(jsonObject); + } + + return jsonObject.ToJsonString(); + } + catch (Exception e) + { + logger.Error(e, "Failed to migrate node storage JSON"); + return json; + } + } + + private static void ConvertToSystemTextJson(JsonObject jsonObject) + { + FilterType(jsonObject); + + // Recursively convert all JSON arrays from {$type: "xyz", $values: []} to [] + foreach ((string? key, JsonNode? value) in jsonObject.ToDictionary()) + { + if (value is not JsonObject obj) + continue; + + // if there is a $type and a $values, replace the entire node with $values regardless of the value of $type + if (obj["$type"] != null && obj["$values"] != null) + { + JsonArray? values = obj["$values"]?.AsArray(); + if (values != null) + { + obj.Remove("$values"); + jsonObject[key] = values; + foreach (JsonNode? jsonNode in values.ToList()) + { + if (jsonNode is JsonObject childObject) + ConvertToSystemTextJson(childObject); + } + } + + obj.Remove("$type"); + } + else + { + ConvertToSystemTextJson(obj); + } + } + } + + private static void FilterType(JsonObject jsonObject) + { + // Replace or remove $type depending on whether there's a matching JsonDerivedType + // This could be done with reflection but that would mean this migration automatically gains new behaviour over time. + JsonNode? type = jsonObject["$type"]; + if (type != null) + { + // Adaption Hints + if (type.GetValue() == "Artemis.Storage.Entities.Profile.AdaptionHints.CategoryAdaptionHintEntity, Artemis.Storage") + jsonObject["$type"] = "Category"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.AdaptionHints.DeviceAdaptionHintEntity, Artemis.Storage") + jsonObject["$type"] = "Device"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.AdaptionHints.KeyboardSectionAdaptionHintEntity, Artemis.Storage") + jsonObject["$type"] = "KeyboardSection"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.AdaptionHints.SingleLedAdaptionHintEntity, Artemis.Storage") + jsonObject["$type"] = "SingleLed"; + // Conditions + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.Conditions.AlwaysOnConditionEntity, Artemis.Storage") + jsonObject["$type"] = "AlwaysOn"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.Conditions.EventConditionEntity, Artemis.Storage") + jsonObject["$type"] = "Event"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.Conditions.PlayOnceConditionEntity, Artemis.Storage") + jsonObject["$type"] = "PlayOnce"; + else if (type.GetValue() == "Artemis.Storage.Entities.Profile.Conditions.StaticConditionEntity, Artemis.Storage") + jsonObject["$type"] = "Static"; + else + jsonObject.Remove("$type"); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0027Namespace.cs b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0027Namespace.cs new file mode 100644 index 000000000..6a98a25eb --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/Migrations/Storage/M0027Namespace.cs @@ -0,0 +1,45 @@ +using LiteDB; + +namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage; + +public class M0027Namespace : IStorageMigration +{ + public int UserVersion => 27; + + public void Apply(LiteRepository repository) + { + ILiteCollection collection = repository.Database.GetCollection("ProfileEntity"); + List profilesToUpdate = new(); + + foreach (BsonDocument profileBson in collection.FindAll()) + { + MigrateDocument(profileBson); + profilesToUpdate.Add(profileBson); + } + + collection.Update(profilesToUpdate); + } + + private void MigrateDocument(BsonDocument document) + { + foreach ((string? key, BsonValue? value) in document) + { + if (key == "_type") + { + document[key] = document[key].AsString + .Replace("Artemis.Storage.Entities.Profile", "Artemis.Storage.Migrator.Legacy.Entities.Profile") + .Replace(", Artemis.Storage", ", Artemis.Storage.Migrator"); + } + else if (value.IsDocument) + MigrateDocument(value.AsDocument); + else if (value.IsArray) + { + foreach (BsonValue bsonValue in value.AsArray) + { + if (bsonValue.IsDocument) + MigrateDocument(bsonValue.AsDocument); + } + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Legacy/StorageMigrationService.cs b/src/Artemis.Storage.Migrator/Legacy/StorageMigrationService.cs new file mode 100644 index 000000000..86f4f1384 --- /dev/null +++ b/src/Artemis.Storage.Migrator/Legacy/StorageMigrationService.cs @@ -0,0 +1,34 @@ +using Artemis.Storage.Migrator.Legacy.Migrations; +using LiteDB; +using Serilog; + +namespace Artemis.Storage.Migrator.Legacy; + +public static class StorageMigrationService +{ + public static void ApplyPendingMigrations(ILogger logger, LiteRepository repository, IList migrations) + { + foreach (IStorageMigration storageMigration in migrations.OrderBy(m => m.UserVersion)) + { + if (repository.Database.UserVersion >= storageMigration.UserVersion) + continue; + + logger.Information("Applying storage migration {storageMigration} to update DB from v{oldVersion} to v{newVersion}", + storageMigration.GetType().Name, repository.Database.UserVersion, storageMigration.UserVersion); + + repository.Database.BeginTrans(); + try + { + storageMigration.Apply(repository); + } + catch (Exception) + { + repository.Database.Rollback(); + throw; + } + + repository.Database.Commit(); + repository.Database.UserVersion = storageMigration.UserVersion; + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage.Migrator/Program.cs b/src/Artemis.Storage.Migrator/Program.cs index ba637973c..ba15b7bfa 100644 --- a/src/Artemis.Storage.Migrator/Program.cs +++ b/src/Artemis.Storage.Migrator/Program.cs @@ -1,6 +1,16 @@ -using Artemis.Core.DryIoc; +using Artemis.Core; +using Artemis.Core.DryIoc; +using Artemis.Storage.Migrator.Legacy; +using Artemis.Storage.Migrator.Legacy.Entities.General; +using Artemis.Storage.Migrator.Legacy.Entities.Plugins; +using Artemis.Storage.Migrator.Legacy.Entities.Profile; +using Artemis.Storage.Migrator.Legacy.Entities.Surface; +using Artemis.Storage.Migrator.Legacy.Entities.Workshop; +using Artemis.Storage.Migrator.Legacy.Migrations.Storage; using DryIoc; +using LiteDB; using Microsoft.EntityFrameworkCore; +using Serilog; namespace Artemis.Storage.Migrator; @@ -8,12 +18,125 @@ class Program { static void Main(string[] args) { - Container container = new Container(rules => rules + using Container container = new(rules => rules .WithMicrosoftDependencyInjectionRules() .WithConcreteTypeDynamicRegistrations() .WithoutThrowOnRegisteringDisposableTransient()); container.RegisterCore(); - container.Resolve().Database.EnsureCreated(); + + ILogger logger = container.Resolve(); + ArtemisDbContext dbContext = container.Resolve(); + logger.Information("Applying pending migrations..."); + dbContext.Database.Migrate(); + logger.Information("Pending migrations applied"); + + if (!File.Exists(Path.Combine(Constants.DataFolder, "database.db"))) + { + logger.Information("No legacy database found, nothing to migrate"); + return; + } + + logger.Information("Migrating legacy database..."); + + try + { + MigrateLegacyDatabase(logger, dbContext); + // After a successful migration, keep the legacy database around for a while + File.Move(Path.Combine(Constants.DataFolder, "database.db"), Path.Combine(Constants.DataFolder, "legacy.db")); + } + catch (Exception e) + { + logger.Error(e, "Failed to migrate legacy database"); + throw; + } + finally + { + File.Delete(Path.Combine(Constants.DataFolder, "temp.db")); + } + + logger.Information("Legacy database migrated"); + } + + private static void MigrateLegacyDatabase(ILogger logger, ArtemisDbContext dbContext) + { + // Copy the database before using it, we're going to make some modifications to it and we don't want to mess up the original + string databasePath = Path.Combine(Constants.DataFolder, "database.db"); + string tempPath = Path.Combine(Constants.DataFolder, "temp.db"); + File.Copy(databasePath, tempPath, true); + + using LiteRepository repository = new($"FileName={tempPath}"); + + // Apply pending LiteDB migrations, this includes a migration that transforms namespaces to Artemis.Storage.Migrator + StorageMigrationService.ApplyPendingMigrations( + logger, + repository, + [ + new M0020AvaloniaReset(), + new M0021GradientNodes(), + new M0022TransitionNodes(), + new M0023LayoutProviders(), + new M0024NodeProviders(), + new M0025NodeProvidersProfileConfig(), + new M0026NodeStorage(logger), + new M0027Namespace(), + ] + ); + + // Devices + if (!dbContext.Devices.Any()) + { + logger.Information("Migrating devices"); + List legacyDevices = repository.Query().Include(s => s.InputIdentifiers).ToList(); + dbContext.Devices.AddRange(legacyDevices.Select(l => l.Migrate())); + dbContext.SaveChanges(); + } + + // Entries + if (!dbContext.Entries.Any()) + { + logger.Information("Migrating entries"); + List legacyEntries = repository.Query().ToList(); + dbContext.Entries.AddRange(legacyEntries.Select(l => l.Migrate())); + dbContext.SaveChanges(); + } + + // Plugins + if (!dbContext.Plugins.Any()) + { + logger.Information("Migrating plugins"); + List legacyPlugins = repository.Query().ToList(); + dbContext.Plugins.AddRange(legacyPlugins.Select(l => l.Migrate())); + dbContext.SaveChanges(); + } + + // PluginSettings + if (!dbContext.PluginSettings.Any()) + { + logger.Information("Migrating plugin settings"); + List legacyPluginSettings = repository.Query().ToList(); + dbContext.PluginSettings.AddRange(legacyPluginSettings.Select(l => l.Migrate())); + dbContext.SaveChanges(); + } + + // ProfileCategories + if (!dbContext.ProfileCategories.Any()) + { + logger.Information("Migrating profile categories"); + List legacyProfileCategories = repository.Query().ToList(); + ILiteStorage profileIcons = repository.Database.GetStorage("profileIcons"); + List legacyProfiles = repository.Query().ToList(); + dbContext.ProfileCategories.AddRange(legacyProfileCategories.Select(l => l.Migrate(logger, legacyProfiles, profileIcons))); + dbContext.SaveChanges(); + } + + // Releases + if (!dbContext.Releases.Any()) + { + logger.Information("Migrating releases"); + List legacyReleases = repository.Query().ToList(); + dbContext.Releases.AddRange(legacyReleases.Select(l => l.Migrate())); + dbContext.SaveChanges(); + } } } \ No newline at end of file diff --git a/src/Artemis.Storage/Artemis.Storage.csproj b/src/Artemis.Storage/Artemis.Storage.csproj index 83b31c461..fa7b8fe61 100644 --- a/src/Artemis.Storage/Artemis.Storage.csproj +++ b/src/Artemis.Storage/Artemis.Storage.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs index 9157d460c..fb58e8d8a 100644 --- a/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/ProfileConfigurationEntity.cs @@ -1,6 +1,5 @@ using System; using Artemis.Storage.Entities.Profile.Nodes; -using Serilog.Core; namespace Artemis.Storage.Entities.Profile; @@ -8,7 +7,6 @@ public class ProfileConfigurationEntity { public string Name { get; set; } = string.Empty; public string? MaterialIcon { get; set; } - public Guid FileIconId { get; set; } public int IconType { get; set; } public bool IconFill { get; set; } public int Order { get; set; } diff --git a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs index 7f93ea093..2ee9934d6 100644 --- a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs +++ b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs @@ -59,20 +59,27 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable private void LoadFromBitmap(Core.ProfileConfigurationIcon configurationIcon, Stream stream) { - _stream = stream; - if (!configurationIcon.Fill) + try { - Content = new Image {Source = new Bitmap(stream)}; - return; - } + _stream = stream; + if (!configurationIcon.Fill) + { + Content = new Image {Source = new Bitmap(stream)}; + return; + } - Content = new Border + Content = new Border + { + Background = TextElement.GetForeground(this), + VerticalAlignment = VerticalAlignment.Stretch, + HorizontalAlignment = HorizontalAlignment.Stretch, + OpacityMask = new ImageBrush(new Bitmap(stream)) + }; + } + catch (Exception) { - Background = TextElement.GetForeground(this), - VerticalAlignment = VerticalAlignment.Stretch, - HorizontalAlignment = HorizontalAlignment.Stretch, - OpacityMask = new ImageBrush(new Bitmap(stream)) - }; + Content = new MaterialIcon {Kind = MaterialIconKind.QuestionMark}; + } } private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index b88396b71..3c9d036db 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -25,7 +25,6 @@ -