using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.Logging; using SharepointToolbox.Core.Messages; using SharepointToolbox.Core.Models; using SharepointToolbox.Localization; namespace SharepointToolbox.ViewModels; public abstract partial class FeatureViewModelBase : ObservableRecipient { private CancellationTokenSource? _cts; private readonly ILogger _logger; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CancelCommand))] private bool _isRunning; [ObservableProperty] private string _statusMessage = string.Empty; [ObservableProperty] private int _progressValue; public IAsyncRelayCommand RunCommand { get; } public RelayCommand CancelCommand { get; } protected FeatureViewModelBase(ILogger logger) { _logger = logger; RunCommand = new AsyncRelayCommand(ExecuteAsync, () => !IsRunning); CancelCommand = new RelayCommand(() => _cts?.Cancel(), () => IsRunning); IsActive = true; // Activates ObservableRecipient for WeakReferenceMessenger } private async Task ExecuteAsync() { _cts = new CancellationTokenSource(); IsRunning = true; StatusMessage = string.Empty; ProgressValue = 0; try { var progress = new Progress(p => { ProgressValue = p.Total > 0 ? (int)(100.0 * p.Current / p.Total) : 0; StatusMessage = p.Message; WeakReferenceMessenger.Default.Send(new ProgressUpdatedMessage(p)); }); await RunOperationAsync(_cts.Token, progress); } catch (OperationCanceledException) { StatusMessage = TranslationSource.Instance["status.cancelled"]; _logger.LogInformation("Operation cancelled by user."); } catch (Exception ex) { StatusMessage = $"{TranslationSource.Instance["err.generic"]} {ex.Message}"; _logger.LogError(ex, "Operation failed."); } finally { IsRunning = false; _cts?.Dispose(); _cts = null; } } protected abstract Task RunOperationAsync(CancellationToken ct, IProgress progress); protected override void OnActivated() { Messenger.Register(this, (r, m) => ((FeatureViewModelBase)r).OnTenantSwitched(m.Value)); } protected virtual void OnTenantSwitched(TenantProfile profile) { // Derived classes override to reset their state } }