mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Enhances setup wizard workflow
Refactors the setup wizard to streamline the initial user experience. Includes the following changes: - Adds nyan cat animation during feature installations for user enjoyment - Skips essential feature selection if workshop is unreachable - Moves directly to settings after device/surface selection - Adds option to configure settings, after device configuration
This commit is contained in:
parent
6988255dce
commit
13c497f41e
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
|
||||
@ -8,6 +8,7 @@ using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using DryIoc;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using ContentDialogButton = Artemis.UI.Shared.Services.Builders.ContentDialogButton;
|
||||
|
||||
namespace Artemis.UI.Shared.Services;
|
||||
|
||||
@ -94,6 +95,7 @@ internal class WindowService : IWindowService
|
||||
.WithTitle(title)
|
||||
.WithContent(message)
|
||||
.HavingPrimaryButton(b => b.WithText(confirm))
|
||||
.WithDefaultButton(ContentDialogButton.Primary)
|
||||
.WithCloseButtonText(cancel)
|
||||
.ShowAsync();
|
||||
|
||||
|
||||
@ -34,10 +34,7 @@ internal class Program
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
{
|
||||
return AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
return AppBuilder.Configure<App>().UsePlatformDetect().LogToTrace().UseReactiveUI();
|
||||
}
|
||||
|
||||
public static void CreateLogger(IContainer container)
|
||||
|
||||
1
src/Artemis.UI/Assets/Animations/nyan.json
Normal file
1
src/Artemis.UI/Assets/Animations/nyan.json
Normal file
File diff suppressed because one or more lines are too long
@ -54,8 +54,8 @@
|
||||
<DockPanel Grid.Row="2" LastChildFill="False" IsVisible="{CompiledBinding !Screen.HideAllButtons}" HorizontalSpacing="10">
|
||||
<Button Command="{CompiledBinding SkipOrFinishWizard}" IsVisible="{CompiledBinding !Screen.ShowFinish}" DockPanel.Dock="Left">Skip & close</Button>
|
||||
<Button Command="{CompiledBinding Screen.Continue}" IsVisible="{CompiledBinding !Screen.ShowFinish}" Width="80" DockPanel.Dock="Right">Continue</Button>
|
||||
<Button Command="{CompiledBinding Screen.GoBack}" IsEnabled="{CompiledBinding Screen.ShowGoBack}" DockPanel.Dock="Right">Back</Button>
|
||||
<Button Command="{CompiledBinding SkipOrFinishWizard}" IsVisible="{CompiledBinding Screen.ShowFinish}" Width="80" DockPanel.Dock="Right">Finish</Button>
|
||||
<Button Command="{CompiledBinding Screen.GoBack}" IsEnabled="{CompiledBinding Screen.ShowGoBack}" DockPanel.Dock="Right">Back</Button>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
<Panel>
|
||||
<!-- Selection stage -->
|
||||
<Grid RowDefinitions="Auto,*,Auto" IsVisible="{CompiledBinding CurrentEntry, Converter={x:Static ObjectConverters.IsNull}}">
|
||||
<TextBlock Grid.Row="0" TextWrapping="Wrap">
|
||||
<TextBlock Grid.Row="0" TextWrapping="Wrap" IsVisible="{CompiledBinding !FetchingDefaultEntries}">
|
||||
Below is a list of default features that can be installed to get you started with Artemis. You can always install or uninstall features later via the Workshop.
|
||||
</TextBlock>
|
||||
|
||||
|
||||
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding FetchingDefaultEntries}" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="10">
|
||||
<ProgressBar IsIndeterminate="True" Width="400" />
|
||||
<TextBlock TextAlignment="Center">Fetching default features from the Artemis workshop...</TextBlock>
|
||||
@ -29,7 +29,7 @@
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
|
||||
|
||||
<TextBlock Classes="card-title" IsVisible="{CompiledBinding EssentialEntryViewModels.Count}">
|
||||
Essentials
|
||||
</TextBlock>
|
||||
@ -40,7 +40,7 @@
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
|
||||
|
||||
<TextBlock Classes="card-title" IsVisible="{CompiledBinding OtherEntryViewModels.Count}">
|
||||
Other features
|
||||
</TextBlock>
|
||||
@ -56,14 +56,23 @@
|
||||
</Grid>
|
||||
|
||||
<!-- Installation stage -->
|
||||
<StackPanel IsVisible="{CompiledBinding CurrentEntry, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<TextBlock Classes="card-title">
|
||||
Currently installing
|
||||
</TextBlock>
|
||||
<ContentControl Content="{CompiledBinding CurrentEntry}" />
|
||||
<StackPanel Width="400" Margin="100 0 0 0" IsVisible="{CompiledBinding CurrentEntry, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<Lottie Path="/Assets/Animations/nyan.json" RepeatCount="-1" Width="300" Height="300"></Lottie>
|
||||
|
||||
<ProgressBar ShowProgressText="True" ProgressTextFormat="{}{0}/{3} Features Installed ({1:0}%)" Minimum="0" Maximum="{CompiledBinding TotalEntries}"
|
||||
Value="{CompiledBinding InstalledEntries}" />
|
||||
<ProgressBar Minimum="0"
|
||||
Maximum="{CompiledBinding TotalEntries}"
|
||||
Value="{CompiledBinding CurrentEntry.InstallProgress, FallbackValue=0}"
|
||||
IsIndeterminate="{CompiledBinding CurrentEntry.Enabling, FallbackValue=true}"
|
||||
Margin="0 0 0 5" />
|
||||
<TextBlock>
|
||||
<Run>Currently installing: </Run>
|
||||
<Run Text="{CompiledBinding CurrentEntry.Entry.Name}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
<ProgressBar Minimum="0" Maximum="{CompiledBinding TotalEntries}" Value="{CompiledBinding InstalledEntries}" Margin="0 20 0 5" />
|
||||
<TextBlock>
|
||||
<Run Text="{CompiledBinding RemainingEntries}" FontWeight="Bold" />
|
||||
<Run>feature(s) remaining, hold on tight!</Run>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
@ -27,17 +27,22 @@ public partial class DefaultEntriesStepViewModel : WizardStepViewModel
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly Func<IEntrySummary, DefaultEntryItemViewModel> _getDefaultEntryItemViewModel;
|
||||
private readonly ObservableAsPropertyHelper<int> _remainingEntries;
|
||||
|
||||
public ObservableCollection<DefaultEntryItemViewModel> DeviceProviderEntryViewModels { get; } = [];
|
||||
public ObservableCollection<DefaultEntryItemViewModel> EssentialEntryViewModels { get; } = [];
|
||||
public ObservableCollection<DefaultEntryItemViewModel> OtherEntryViewModels { get; } = [];
|
||||
|
||||
public int RemainingEntries => _remainingEntries.Value;
|
||||
|
||||
public DefaultEntriesStepViewModel(IWorkshopService workshopService, IDeviceService deviceService, IWorkshopClient client,
|
||||
Func<IEntrySummary, DefaultEntryItemViewModel> getDefaultEntryItemViewModel)
|
||||
{
|
||||
_deviceService = deviceService;
|
||||
_client = client;
|
||||
_getDefaultEntryItemViewModel = getDefaultEntryItemViewModel;
|
||||
_remainingEntries = this.WhenAnyValue(vm => vm.InstalledEntries, vm => vm.TotalEntries)
|
||||
.Select(t => t.Item2 - t.Item1)
|
||||
.ToProperty(this, vm => vm.RemainingEntries);
|
||||
|
||||
ContinueText = "Install selected entries";
|
||||
Continue = ReactiveCommand.CreateFromTask(async ct =>
|
||||
|
||||
@ -16,12 +16,13 @@ using Artemis.WebClient.Workshop.Models;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using StrawberryShake;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.UI.Screens.StartupWizard.Steps;
|
||||
|
||||
public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IWorkshopService _workshopService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
@ -30,15 +31,20 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
|
||||
[Notify] private bool _isInstalled;
|
||||
[Notify] private bool _shouldInstall;
|
||||
|
||||
public DefaultEntryItemViewModel(IEntrySummary entry, IWorkshopService workshopService, IWindowService windowService, IPluginManagementService pluginManagementService, ISettingsVmFactory settingsVmFactory)
|
||||
[Notify] private bool _enabling;
|
||||
[Notify] private float _installProgress;
|
||||
|
||||
public DefaultEntryItemViewModel(ILogger logger, IEntrySummary entry, IWorkshopService workshopService, IWindowService windowService, IPluginManagementService pluginManagementService,
|
||||
ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_workshopService = workshopService;
|
||||
_windowService = windowService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
Entry = entry;
|
||||
|
||||
_progress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
|
||||
this.WhenActivated((CompositeDisposable _) => { IsInstalled = workshopService.GetInstalledEntry(entry.Id) != null; });
|
||||
}
|
||||
|
||||
@ -61,26 +67,44 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
.WithCloseButtonText("Skip and continue")
|
||||
.ShowAsync();
|
||||
}
|
||||
// If the entry is a plugin, enable the plugin
|
||||
// If the entry is a plugin, enable the plugin and all features
|
||||
else if (result.Entry?.EntryType == EntryType.Plugin)
|
||||
{
|
||||
await EnablePlugin(result.Entry);
|
||||
Enabling = true;
|
||||
await EnablePluginAndFeatures(result.Entry);
|
||||
Enabling = false;
|
||||
}
|
||||
|
||||
return result.IsSuccess;
|
||||
}
|
||||
|
||||
private async Task EnablePlugin(InstalledEntry entry)
|
||||
private async Task EnablePluginAndFeatures(InstalledEntry entry)
|
||||
{
|
||||
if (!entry.TryGetMetadata("PluginId", out Guid pluginId))
|
||||
throw new InvalidOperationException("Plugin entry does not contain a PluginId metadata value.");
|
||||
|
||||
|
||||
Plugin? plugin = _pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == pluginId);
|
||||
if (plugin == null)
|
||||
throw new InvalidOperationException($"Plugin with id '{pluginId}' does not exist.");
|
||||
|
||||
|
||||
// There's quite a bit of UI involved in enabling a plugin, borrowing the PluginSettingsViewModel for this
|
||||
PluginViewModel pluginViewModel = _settingsVmFactory.PluginViewModel(plugin, ReactiveCommand.Create(() => { }));
|
||||
await pluginViewModel.UpdateEnabled(true);
|
||||
|
||||
// Find features without prerequisites to enable
|
||||
foreach (PluginFeatureInfo pluginFeatureInfo in plugin.Features)
|
||||
{
|
||||
if (pluginFeatureInfo.Instance == null || pluginFeatureInfo.Instance.IsEnabled || pluginFeatureInfo.Prerequisites.Count != 0)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
_pluginManagementService.EnablePluginFeature(pluginFeatureInfo.Instance, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warning(e, "Failed to enable plugin feature '{FeatureName}', skipping", pluginFeatureInfo.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ public class SurfaceStepViewModel : WizardStepViewModel
|
||||
_deviceService = deviceService;
|
||||
SelectLayout = ReactiveCommand.Create<string>(ExecuteSelectLayout);
|
||||
|
||||
Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen<DefaultEntriesStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen<SettingsStepViewModel>());
|
||||
GoBack = ReactiveCommand.Create(() => Wizard.ChangeScreen<LayoutsStepViewModel>());
|
||||
}
|
||||
|
||||
@ -30,6 +30,6 @@ public class SurfaceStepViewModel : WizardStepViewModel
|
||||
// TODO: Implement the layout
|
||||
_deviceService.AutoArrangeDevices();
|
||||
|
||||
Wizard.ChangeScreen<DefaultEntriesStepViewModel>();
|
||||
Wizard.ChangeScreen<SettingsStepViewModel>();
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,15 @@
|
||||
namespace Artemis.UI.Screens.StartupWizard.Steps;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
|
||||
namespace Artemis.UI.Screens.StartupWizard.Steps;
|
||||
|
||||
public class WorkshopUnreachableStepViewModel : WizardStepViewModel
|
||||
{
|
||||
public WorkshopUnreachableStepViewModel()
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
public WorkshopUnreachableStepViewModel(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
HideAllButtons = true;
|
||||
}
|
||||
|
||||
@ -14,6 +20,10 @@ public class WorkshopUnreachableStepViewModel : WizardStepViewModel
|
||||
|
||||
public void Skip()
|
||||
{
|
||||
PluginSetting<bool> setting = _settingsService.GetSetting("UI.SetupWizardCompleted", false);
|
||||
setting.Value = false;
|
||||
setting.Save();
|
||||
|
||||
Wizard.Close(false);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user