diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml new file mode 100644 index 000000000..2317f1da8 --- /dev/null +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml @@ -0,0 +1,32 @@ + + + + + + Installing default plugins and profiles, these will provide you with a basic setup to get started with Artemis. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml.cs new file mode 100644 index 000000000..1050a6b30 --- /dev/null +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.StartupWizard.Steps; + +public partial class DefaultEntriesStepView : ReactiveUserControl +{ + public DefaultEntriesStepView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepViewModel.cs new file mode 100644 index 000000000..d9226057a --- /dev/null +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntriesStepViewModel.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.Core.Services; +using Artemis.UI.Extensions; +using Artemis.UI.Shared.Services; +using Artemis.UI.Shared.Utilities; +using Artemis.WebClient.Workshop; +using Artemis.WebClient.Workshop.Handlers.InstallationHandlers; +using Artemis.WebClient.Workshop.Services; +using PropertyChanged.SourceGenerator; +using ReactiveUI; +using StrawberryShake; + +namespace Artemis.UI.Screens.StartupWizard.Steps; + +public partial class DefaultEntriesStepViewModel : WizardStepViewModel +{ + [Notify] private bool _workshopReachable; + [Notify] private bool _fetchingDefaultEntries; + [Notify] private int _totalEntries; + [Notify] private int _installedEntries; + [Notify] private int _installProgress; + [Notify] private IEntrySummary? _currentEntry; + + private readonly IWorkshopService _workshopService; + private readonly IWorkshopClient _client; + private readonly IWindowService _windowService; + private readonly Progress _currentEntryProgress = new(); + + + public DefaultEntriesStepViewModel(IWorkshopService workshopService, IDeviceService deviceService, IWorkshopClient client, IWindowService windowService) + { + _workshopService = workshopService; + _client = client; + _windowService = windowService; + _currentEntryProgress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage; + + Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen()); + GoBack = ReactiveCommand.Create(() => + { + if (deviceService.EnabledDevices.Count == 0) + Wizard.ChangeScreen(); + else + Wizard.ChangeScreen(); + }); + + this.WhenActivatedAsync(async d => + { + WorkshopReachable = await _workshopService.ValidateWorkshopStatus(d.AsCancellationToken()); + if (WorkshopReachable) + { + await InstallDefaultEntries(d.AsCancellationToken()); + } + }); + } + + public async Task InstallDefaultEntries(CancellationToken cancellationToken) + { + FetchingDefaultEntries = true; + TotalEntries = 0; + InstalledEntries = 0; + + if (!WorkshopReachable) + return false; + + IOperationResult result = await _client.GetDefaultEntries.ExecuteAsync(100, null, cancellationToken); + List entries = result.Data?.EntriesV2?.Edges?.Select(e => e.Node).Cast().ToList() ?? []; + while (result.Data?.EntriesV2?.PageInfo is {HasNextPage: true}) + { + result = await _client.GetDefaultEntries.ExecuteAsync(100, result.Data.EntriesV2.PageInfo.EndCursor, cancellationToken); + if (result.Data?.EntriesV2?.Edges != null) + entries.AddRange(result.Data.EntriesV2.Edges.Select(e => e.Node)); + } + + await Task.Delay(1000); + FetchingDefaultEntries = false; + TotalEntries = entries.Count; + + if (entries.Count == 0) + return false; + + foreach (IEntrySummary entry in entries) + { + if (cancellationToken.IsCancellationRequested) + return false; + + CurrentEntry = entry; + + // Skip entries without a release and entries that are already installed + if (entry.LatestRelease == null || _workshopService.GetInstalledEntry(entry.Id) != null) + { + InstalledEntries++; + continue; + } + + EntryInstallResult installResult = await _workshopService.InstallEntry(entry, entry.LatestRelease, _currentEntryProgress, cancellationToken); + + // Unlikely as default entries do nothing fancy + if (!installResult.IsSuccess) + { + await _windowService.CreateContentDialog().WithTitle("Failed to install entry") + .WithContent($"Failed to install entry '{entry.Name}' ({entry.Id}): {installResult.Message}") + .WithCloseButtonText("Skip and continue") + .ShowAsync(); + } + + InstalledEntries++; + } + + return true; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepViewModel.cs index ed2f364d9..1a3e15768 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DevicesStepViewModel.cs @@ -22,7 +22,7 @@ public class DevicesStepViewModel : WizardStepViewModel Continue = ReactiveCommand.Create(() => { if (deviceService.EnabledDevices.Count == 0) - Wizard.ChangeScreen(); + Wizard.ChangeScreen(); else Wizard.ChangeScreen(); }); diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs index 04989be43..3ddc71021 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/SurfaceStepViewModel.cs @@ -19,7 +19,7 @@ public class SurfaceStepViewModel : WizardStepViewModel _deviceService = deviceService; SelectLayout = ReactiveCommand.Create(ExecuteSelectLayout); - Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen()); + Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen()); GoBack = ReactiveCommand.Create(() => Wizard.ChangeScreen()); } @@ -30,6 +30,6 @@ public class SurfaceStepViewModel : WizardStepViewModel // TODO: Implement the layout _deviceService.AutoArrangeDevices(); - Wizard.ChangeScreen(); + Wizard.ChangeScreen(); } } \ No newline at end of file diff --git a/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql b/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql index ce6eedddc..3827be7ea 100644 --- a/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql +++ b/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql @@ -18,4 +18,20 @@ query GetPopularEntries { popularEntries { ...entrySummary } +} + +query GetDefaultEntries($first: Int $after: String) { + entriesV2(where: {isDefault: {eq: true}} first: $first after: $after) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + cursor + node { + ...entrySummary + } + } + } } \ No newline at end of file