using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; using Artemis.Core; using Artemis.Core.ScriptingProviders; using Artemis.Core.Services; using Artemis.UI.DryIoc.Factories; using Artemis.UI.Screens.Scripting.Dialogs; using Artemis.UI.Shared; 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.Scripting; public partial class ScriptsDialogViewModel : DialogViewModelBase { private readonly Dictionary _providerViewModels = new(); private readonly IScriptingService _scriptingService; private readonly IWindowService _windowService; private ObservableAsPropertyHelper? _hasScripts; [Notify] private ReadOnlyObservableCollection _scriptConfigurations; [Notify] private IScriptEditorViewModel? _scriptEditorViewModel; [Notify] private ScriptConfigurationViewModel? _selectedScript; public ScriptsDialogViewModel(IScriptingService scriptingService, IWindowService windowService, IProfileService profileService, IScriptVmFactory scriptVmFactory) { _scriptingService = scriptingService; _windowService = windowService; ScriptType = ScriptType.Global; ScriptingProviders = new List(scriptingService.ScriptingProviders); AddScriptConfiguration = ReactiveCommand.CreateFromTask(ExecuteAddScriptConfiguration, Observable.Return(ScriptingProviders.Any())); this.WhenAnyValue(vm => vm.SelectedScript).Subscribe(s => SetupScriptEditor(s?.ScriptConfiguration)); _scriptConfigurations = new ReadOnlyObservableCollection(new ObservableCollection()); // TODO: When not bound to a profile, base the contents of the UI on the ScriptingService } public ScriptsDialogViewModel(Profile profile, IScriptingService scriptingService, IWindowService windowService, IProfileService profileService, IScriptVmFactory scriptVmFactory) : this(scriptingService, windowService, profileService, scriptVmFactory) { ScriptType = ScriptType.Profile; Profile = profile; this.WhenActivated(d => { _hasScripts = Profile.ScriptConfigurations.ToObservableChangeSet() .Count() .Select(c => c > 0) .ToProperty(this, vm => vm.HasScripts) .DisposeWith(d); Profile.ScriptConfigurations.ToObservableChangeSet() .Transform(c => scriptVmFactory.ScriptConfigurationViewModel(Profile, c)) .Bind(out ReadOnlyObservableCollection scriptConfigurationViewModels) .Subscribe() .DisposeWith(d); ScriptConfigurations = scriptConfigurationViewModels; SelectedScript = ScriptConfigurations.FirstOrDefault(); Disposable.Create(() => profileService.SaveProfile(Profile, false)).DisposeWith(d); }); } public ScriptType ScriptType { get; } public List ScriptingProviders { get; } public Profile? Profile { get; } public bool HasScripts => _hasScripts?.Value ?? false; public ReactiveCommand AddScriptConfiguration { get; } public async Task CanClose() { if (!ScriptConfigurations.Any(s => s.ScriptConfiguration.HasChanges)) return true; bool result = await _windowService.ShowConfirmContentDialog("Discard changes", "One or more scripts still have pending changes, do you want to discard them?"); if (!result) return false; foreach (ScriptConfigurationViewModel scriptConfigurationViewModel in ScriptConfigurations) scriptConfigurationViewModel.ScriptConfiguration.DiscardPendingChanges(); return true; } private void SetupScriptEditor(ScriptConfiguration? scriptConfiguration) { if (scriptConfiguration == null) { ScriptEditorViewModel = null; return; } // The script is null if the provider is missing if (scriptConfiguration.Script == null) { ScriptEditorViewModel = null; return; } if (!_providerViewModels.TryGetValue(scriptConfiguration.Script.ScriptingProvider, out IScriptEditorViewModel? viewModel)) { viewModel = scriptConfiguration.Script.ScriptingProvider.CreateScriptEditor(ScriptType); _providerViewModels.Add(scriptConfiguration.Script.ScriptingProvider, viewModel); } ScriptEditorViewModel = viewModel; ScriptEditorViewModel.ChangeScript(scriptConfiguration.Script); } private async Task ExecuteAddScriptConfiguration() { await _windowService.CreateContentDialog() .WithTitle("Add script") .WithViewModel(out ScriptConfigurationCreateViewModel vm) .WithCloseButtonText("Cancel") .HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Submit)) .WithDefaultButton(ContentDialogButton.Primary) .ShowAsync(); if (vm.ScriptConfiguration == null) return; // Add the script to the profile and instantiate it if (Profile != null) _scriptingService.AddScript(vm.ScriptConfiguration, Profile); else _scriptingService.AddScript(vm.ScriptConfiguration); // Select the new script SelectedScript = ScriptConfigurations.LastOrDefault(); } }