1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Artemis/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs
Robert 7e981a61d3 Storage - Fix new profile creation
UI - Fix a crash when dragging and dropping in the sidebar
2024-03-15 17:13:21 +01:00

217 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using DynamicData;
using DynamicData.Binding;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
namespace Artemis.UI.Screens.Sidebar;
public partial class SidebarCategoryViewModel : ActivatableViewModelBase
{
private readonly IProfileService _profileService;
private readonly IRouter _router;
private readonly ISidebarVmFactory _vmFactory;
private readonly IWindowService _windowService;
private ObservableAsPropertyHelper<bool>? _isCollapsed;
private ObservableAsPropertyHelper<bool>? _isSuspended;
[Notify] private SidebarProfileConfigurationViewModel? _selectedProfileConfiguration;
public SidebarCategoryViewModel(ProfileCategory profileCategory, IProfileService profileService, IWindowService windowService, ISidebarVmFactory vmFactory, IRouter router)
{
_profileService = profileService;
_windowService = windowService;
_vmFactory = vmFactory;
_router = router;
ProfileCategory = profileCategory;
SourceList<ProfileConfiguration> profileConfigurations = new();
// Only show items when not collapsed
IObservable<Func<ProfileConfiguration, bool>> profileConfigurationsFilter = this.WhenAnyValue(vm => vm.IsCollapsed).Select(b => new Func<object, bool>(_ => !b));
profileConfigurations.Connect()
.Filter(profileConfigurationsFilter)
.Sort(SortExpressionComparer<ProfileConfiguration>.Ascending(c => c.Order))
.Transform(c => _vmFactory.SidebarProfileConfigurationViewModel(c))
.Bind(out ReadOnlyObservableCollection<SidebarProfileConfigurationViewModel> profileConfigurationViewModels)
.Subscribe();
ProfileConfigurations = profileConfigurationViewModels;
ToggleCollapsed = ReactiveCommand.Create(ExecuteToggleCollapsed);
ToggleSuspended = ReactiveCommand.Create(ExecuteToggleSuspended);
AddProfile = ReactiveCommand.CreateFromTask(ExecuteAddProfile);
ImportProfile = ReactiveCommand.CreateFromTask(ExecuteImportProfile);
MoveUp = ReactiveCommand.Create(ExecuteMoveUp);
MoveDown = ReactiveCommand.Create(ExecuteMoveDown);
RenameCategory = ReactiveCommand.CreateFromTask(ExecuteRenameCategory);
DeleteCategory = ReactiveCommand.CreateFromTask(ExecuteDeleteCategory);
this.WhenActivated(d =>
{
// Navigate on selection change
this.WhenAnyValue(vm => vm.SelectedProfileConfiguration)
.WhereNotNull()
.Subscribe(s => _router.Navigate($"profile-editor/{s.ProfileConfiguration.ProfileId}", new RouterNavigationOptions {IgnoreOnPartialMatch = true, RecycleScreens = false}))
.DisposeWith(d);
_router.CurrentPath.WhereNotNull().Subscribe(r => SelectedProfileConfiguration = ProfileConfigurations.FirstOrDefault(c => c.Matches(r))).DisposeWith(d);
// Update the list of profiles whenever the category fires events
Observable.FromEventPattern<ProfileConfigurationEventArgs>(x => profileCategory.ProfileConfigurationAdded += x, x => profileCategory.ProfileConfigurationAdded -= x)
.Subscribe(e => profileConfigurations.Add(e.EventArgs.ProfileConfiguration))
.DisposeWith(d);
Observable.FromEventPattern<ProfileConfigurationEventArgs>(x => profileCategory.ProfileConfigurationRemoved += x, x => profileCategory.ProfileConfigurationRemoved -= x)
.Subscribe(e => profileConfigurations.RemoveMany(profileConfigurations.Items.Where(c => c == e.EventArgs.ProfileConfiguration)))
.DisposeWith(d);
profileConfigurations.Edit(updater =>
{
updater.Clear();
updater.AddRange(profileCategory.ProfileConfigurations);
});
_isCollapsed = ProfileCategory.WhenAnyValue(vm => vm.IsCollapsed).ToProperty(this, vm => vm.IsCollapsed).DisposeWith(d);
_isSuspended = ProfileCategory.WhenAnyValue(vm => vm.IsSuspended).ToProperty(this, vm => vm.IsSuspended).DisposeWith(d);
});
}
public ReactiveCommand<Unit, Unit> ImportProfile { get; }
public ReactiveCommand<Unit, Unit> ToggleCollapsed { get; }
public ReactiveCommand<Unit, Unit> ToggleSuspended { get; }
public ReactiveCommand<Unit, Unit> AddProfile { get; }
public ReactiveCommand<Unit, Unit> MoveUp { get; }
public ReactiveCommand<Unit, Unit> MoveDown { get; }
public ReactiveCommand<Unit, Unit> RenameCategory { get; }
public ReactiveCommand<Unit, Unit> DeleteCategory { get; }
public ProfileCategory ProfileCategory { get; }
public ReadOnlyObservableCollection<SidebarProfileConfigurationViewModel> ProfileConfigurations { get; }
public bool IsCollapsed => _isCollapsed?.Value ?? false;
public bool IsSuspended => _isSuspended?.Value ?? false;
public void AddProfileConfiguration(ProfileConfiguration profileConfiguration, ProfileConfiguration? target)
{
if (profileConfiguration == target)
return;
ProfileCategory oldCategory = profileConfiguration.Category;
ProfileCategory.AddProfileConfiguration(profileConfiguration, target);
_profileService.SaveProfileCategory(ProfileCategory);
// If the profile moved to a new category, also save the old category
if (oldCategory != ProfileCategory)
_profileService.SaveProfileCategory(oldCategory);
}
private async Task ExecuteRenameCategory()
{
await _windowService.CreateContentDialog()
.WithTitle("Edit category")
.WithViewModel(out SidebarCategoryEditViewModel vm, ProfileCategory)
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
.WithCloseButtonText("Cancel")
.WithDefaultButton(ContentDialogButton.Primary)
.ShowAsync();
}
private async Task ExecuteDeleteCategory()
{
if (await _windowService.ShowConfirmContentDialog($"Delete {ProfileCategory.Name}", "Do you want to delete this category and all its profiles?"))
{
if (ProfileCategory.ProfileConfigurations.Any(c => _profileService.FocusProfile == c))
await _router.Navigate("home");
_profileService.DeleteProfileCategory(ProfileCategory);
}
}
private async Task ExecuteAddProfile()
{
ProfileConfiguration? result = await _windowService.ShowDialogAsync<ProfileConfigurationEditViewModel, ProfileConfiguration?>(ProfileCategory, ProfileConfiguration.Empty);
if (result != null)
{
SidebarProfileConfigurationViewModel viewModel = _vmFactory.SidebarProfileConfigurationViewModel(result);
SelectedProfileConfiguration = viewModel;
}
}
private async Task ExecuteImportProfile()
{
string[]? result = await _windowService.CreateOpenFileDialog()
.HavingFilter(f => f.WithExtension("zip").WithName("Artemis profile"))
.ShowAsync();
if (result == null)
return;
try
{
await using FileStream fileStream = File.OpenRead(result[0]);
await _profileService.ImportProfile(fileStream, ProfileCategory, true, true);
}
catch (Exception e)
{
_windowService.ShowExceptionDialog("Import profile failed", e);
}
}
private void ExecuteToggleCollapsed()
{
ProfileCategory.IsCollapsed = !ProfileCategory.IsCollapsed;
_profileService.SaveProfileCategory(ProfileCategory);
}
private void ExecuteToggleSuspended()
{
ProfileCategory.IsSuspended = !ProfileCategory.IsSuspended;
_profileService.SaveProfileCategory(ProfileCategory);
}
private void ExecuteMoveUp()
{
List<ProfileCategory> categories = _profileService.ProfileCategories.OrderBy(p => p.Order).ToList();
int index = categories.IndexOf(ProfileCategory);
if (index <= 0)
return;
categories.Remove(ProfileCategory);
categories.Insert(index - 1, ProfileCategory);
ApplyCategoryOrder(categories);
}
private void ExecuteMoveDown()
{
List<ProfileCategory> categories = _profileService.ProfileCategories.OrderBy(p => p.Order).ToList();
int index = categories.IndexOf(ProfileCategory);
if (index >= categories.Count - 1)
return;
categories.Remove(ProfileCategory);
categories.Insert(index + 1, ProfileCategory);
ApplyCategoryOrder(categories);
}
private void ApplyCategoryOrder(List<ProfileCategory> categories)
{
for (int i = 0; i < categories.Count; i++)
{
if (categories[i].Order == i + 1)
continue;
categories[i].Order = i + 1;
_profileService.SaveProfileCategory(categories[i]);
}
}
}