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