diff --git a/src/Artemis.Storage/Migrations/20240706131336_ExpandInstalledEntry.cs b/src/Artemis.Storage/Migrations/20240706131336_ExpandInstalledEntry.cs
index d159d55e7..5e7831dbf 100644
--- a/src/Artemis.Storage/Migrations/20240706131336_ExpandInstalledEntry.cs
+++ b/src/Artemis.Storage/Migrations/20240706131336_ExpandInstalledEntry.cs
@@ -50,6 +50,22 @@ namespace Artemis.Storage.Migrations
type: "TEXT",
nullable: false,
defaultValue: "");
+
+ // Enable auto-update on all entries that are not profiles
+ migrationBuilder.Sql("UPDATE Entries SET AutoUpdate = 1 WHERE EntryType != 2");
+
+ // Enable auto-update on all entries of profiles that are fresh imports
+ migrationBuilder.Sql("""
+ UPDATE Entries
+ SET AutoUpdate = 1
+ WHERE EntryType = 2
+ AND EXISTS (
+ SELECT 1
+ FROM ProfileContainers
+ WHERE json_extract(ProfileContainers.Profile, '$.Id') = json_extract(Entries.Metadata, '$.ProfileId')
+ AND json_extract(ProfileContainers.Profile, '$.IsFreshImport') = 1
+ );
+ """);
}
///
diff --git a/src/Artemis.UI.Shared/Routing/Router/Router.cs b/src/Artemis.UI.Shared/Routing/Router/Router.cs
index 2850b88a3..caa95b3e1 100644
--- a/src/Artemis.UI.Shared/Routing/Router/Router.cs
+++ b/src/Artemis.UI.Shared/Routing/Router/Router.cs
@@ -79,7 +79,7 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
path = NavigateUp(_currentRouteSubject.Value, path);
else
path = path.ToLower().Trim(' ', '/', '\\');
-
+
options ??= new RouterNavigationOptions();
// Routing takes place on the UI thread with processing heavy tasks offloaded by the router itself
@@ -90,7 +90,7 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
public async Task Reload()
{
string path = _currentRouteSubject.Value ?? "blank";
-
+
// Routing takes place on the UI thread with processing heavy tasks offloaded by the router itself
await Dispatcher.UIThread.InvokeAsync(async () =>
{
@@ -128,8 +128,12 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
await navigation.Navigate(args);
// If it was cancelled before completion, don't add it to history or update the current path
+ // Do reload the current path because it may have been partially navigated away from
if (navigation.Cancelled)
+ {
+ await Reload();
return;
+ }
if (options.AddToHistory && previousPath != null)
{
@@ -172,7 +176,7 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
public async Task GoUp(RouterNavigationOptions? options = null)
{
string? currentPath = _currentRouteSubject.Value;
-
+
// Keep removing segments until we find a parent route that resolves
while (currentPath != null && currentPath.Contains('/'))
{
@@ -223,8 +227,8 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
_logger.Debug("Router disposed, should that be? Stacktrace: \r\n{StackTrace}", Environment.StackTrace);
}
-
-
+
+
private string NavigateUp(string current, string path)
{
string[] pathParts = current.Split('/');
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs
index a0c9cea17..dc4ff5e1d 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/IProfileEditorService.cs
@@ -173,11 +173,6 @@ public interface IProfileEditorService : IArtemisSharedUIService
/// The command scope that will group any commands until disposed.
ProfileEditorCommandScope CreateCommandScope(string name);
- ///
- /// Saves the current profile.
- ///
- void SaveProfile();
-
///
/// Asynchronously saves the current profile.
///
diff --git a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
index 6a69bdd3b..a9f17c2ee 100644
--- a/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
+++ b/src/Artemis.UI.Shared/Services/ProfileEditor/ProfileEditorService.cs
@@ -391,19 +391,12 @@ internal class ProfileEditorService : IProfileEditorService
_pixelsPerSecondSubject.OnNext(pixelsPerSecond);
}
-
- ///
- public void SaveProfile()
- {
- Profile? profile = _profileConfigurationSubject.Value?.Profile;
- if (profile != null)
- _profileService.SaveProfile(profile, true);
- }
-
///
public async Task SaveProfileAsync()
{
- await Task.Run(SaveProfile);
+ Profile? profile = _profileConfigurationSubject.Value?.Profile;
+ if (profile != null)
+ await Task.Run(() => _profileService.SaveProfile(profile, true));
}
///
diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
index 6e1da26b5..5ae472de2 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/Properties/DataBinding/DataBindingViewModel.cs
@@ -12,7 +12,6 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.ProfileEditor;
using Artemis.UI.Shared.Services.ProfileEditor.Commands;
-using Avalonia.Threading;
using ReactiveUI;
namespace Artemis.UI.Screens.ProfileEditor.Properties.DataBinding;
@@ -23,9 +22,9 @@ public class DataBindingViewModel : ActivatableViewModelBase
private readonly IProfileEditorService _profileEditorService;
private readonly IWindowService _windowService;
private ObservableAsPropertyHelper? _dataBindingEnabled;
- private bool _editorOpen;
private ObservableAsPropertyHelper? _layerProperty;
private ObservableAsPropertyHelper? _nodeScriptViewModel;
+ private bool _editorOpen;
private bool _playing;
public DataBindingViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService, ISettingsService settingsService)
@@ -106,6 +105,6 @@ public class DataBindingViewModel : ActivatableViewModelBase
private void Save()
{
if (!_editorOpen)
- _profileEditorService.SaveProfile();
+ _profileEditorService.SaveProfileAsync();
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs
index eca2ee350..c92e34dcc 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs
@@ -15,8 +15,11 @@ using Artemis.UI.Screens.ProfileEditor.StatusBar;
using Artemis.UI.Screens.ProfileEditor.VisualEditor;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
+using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.MainWindow;
using Artemis.UI.Shared.Services.ProfileEditor;
+using Artemis.WebClient.Workshop.Models;
+using Artemis.WebClient.Workshop.Services;
using DynamicData;
using DynamicData.Binding;
using PropertyChanged.SourceGenerator;
@@ -30,10 +33,12 @@ public partial class ProfileEditorViewModel : RoutableScreen _tools;
private ObservableAsPropertyHelper? _history;
private ObservableAsPropertyHelper? _suspendedEditing;
-
+
[Notify] private ProfileConfiguration? _profileConfiguration;
///
@@ -48,12 +53,16 @@ public partial class ProfileEditorViewModel : RoutableScreen toolViewModels,
IMainWindowService mainWindowService,
- IInputService inputService)
+ IInputService inputService,
+ IWorkshopService workshopService,
+ IWindowService windowService)
{
_profileService = profileService;
_profileEditorService = profileEditorService;
_settingsService = settingsService;
_mainWindowService = mainWindowService;
+ _workshopService = workshopService;
+ _windowService = windowService;
_tools = new SourceList();
_tools.AddRange(toolViewModels);
@@ -144,7 +153,7 @@ public partial class ProfileEditorViewModel : RoutableScreen
-
-
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
index 712d7fc68..22d723a06 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
@@ -37,11 +37,23 @@
-
-
- by
-
-
+
+
+
+ by
+
+
+
+
+
downloads
-
+
-
+
installed
-
+
update available
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs
index 2c15c9017..3d5cba990 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs
@@ -127,7 +127,9 @@ public partial class EntryReleaseInfoViewModel : ActivatableViewModelBase
await Manage();
}
else if (!_cts.IsCancellationRequested)
+ {
_notificationService.CreateNotification().WithTitle("Installation failed").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show();
+ }
}
catch (Exception e)
{
diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml
index bebac42fd..71ab5983c 100644
--- a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml
@@ -35,11 +35,22 @@
-
-
- by
-
-
+
+
+
+ by
+
+
+
+
_metadata = new();
private long _id;
private string _author;
+ private bool _isOfficial;
private string _name;
private string _summary;
private EntryType _entryType;
@@ -175,6 +176,7 @@ public class InstalledEntry : CorePropertyChanged, IEntrySummary
{
Id = entry.Id;
Author = entry.Author;
+ IsOfficial = entry.IsOfficial;
Name = entry.Name;
Summary = entry.Summary;
EntryType = entry.EntryType;
@@ -200,6 +202,13 @@ public class InstalledEntry : CorePropertyChanged, IEntrySummary
private set => SetAndNotify(ref _author, value);
}
+ ///
+ public bool IsOfficial
+ {
+ get => _isOfficial;
+ private set => SetAndNotify(ref _isOfficial, value);
+ }
+
///
public string Name
{
diff --git a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
index e25faeb5d..cf0c3c779 100644
--- a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
+++ b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
@@ -31,6 +31,7 @@ fragment submittedEntry on Entry {
fragment entrySummary on Entry {
id
author
+ isOfficial
name
summary
entryType
@@ -45,6 +46,7 @@ fragment entrySummary on Entry {
fragment entryDetails on Entry {
id
author
+ isOfficial
name
summary
entryType
diff --git a/src/Artemis.WebClient.Workshop/graphql.config.yml b/src/Artemis.WebClient.Workshop/graphql.config.yml
index a8ba99703..9662a514f 100644
--- a/src/Artemis.WebClient.Workshop/graphql.config.yml
+++ b/src/Artemis.WebClient.Workshop/graphql.config.yml
@@ -2,7 +2,7 @@ schema: schema.graphql
extensions:
endpoints:
Default GraphQL Endpoint:
- url: https://localhost:7281/graphql
+ url: https://workshop.artemis-rgb.com/graphql
headers:
user-agent: JS GraphQL
introspect: true
diff --git a/src/Artemis.WebClient.Workshop/schema.graphql b/src/Artemis.WebClient.Workshop/schema.graphql
index 4df1ff307..12317cc59 100644
--- a/src/Artemis.WebClient.Workshop/schema.graphql
+++ b/src/Artemis.WebClient.Workshop/schema.graphql
@@ -61,6 +61,7 @@ type Entry {
iconId: UUID
id: Long!
images: [Image!]!
+ isOfficial: Boolean!
latestRelease: Release
latestReleaseId: Long
layoutInfo: [LayoutInfo!]!
@@ -124,6 +125,7 @@ type PluginInfo {
entry: Entry!
entryId: Long!
helpPage: String
+ minmumVersion: String
pluginGuid: UUID!
repository: String
requiresAdmin: Boolean!
@@ -310,6 +312,7 @@ input EntryFilterInput {
iconId: UuidOperationFilterInput
id: LongOperationFilterInput
images: ListFilterInputTypeOfImageFilterInput
+ isOfficial: BooleanOperationFilterInput
latestRelease: ReleaseFilterInput
latestReleaseId: LongOperationFilterInput
layoutInfo: ListFilterInputTypeOfLayoutInfoFilterInput
@@ -331,6 +334,7 @@ input EntrySortInput {
icon: ImageSortInput
iconId: SortEnumType
id: SortEnumType
+ isOfficial: SortEnumType
latestRelease: ReleaseSortInput
latestReleaseId: SortEnumType
name: SortEnumType
@@ -479,6 +483,7 @@ input PluginInfoFilterInput {
entry: EntryFilterInput
entryId: LongOperationFilterInput
helpPage: StringOperationFilterInput
+ minmumVersion: StringOperationFilterInput
or: [PluginInfoFilterInput!]
pluginGuid: UuidOperationFilterInput
repository: StringOperationFilterInput
@@ -494,6 +499,7 @@ input PluginInfoSortInput {
entry: EntrySortInput
entryId: SortEnumType
helpPage: SortEnumType
+ minmumVersion: SortEnumType
pluginGuid: SortEnumType
repository: SortEnumType
requiresAdmin: SortEnumType