- Add protected GlobalSites property (IReadOnlyList<SiteInfo>) initialized to Array.Empty - Register GlobalSitesChangedMessage in OnActivated alongside TenantSwitchedMessage - Add private OnGlobalSitesReceived to update GlobalSites and invoke virtual hook - Add protected virtual OnGlobalSitesChanged for derived VMs to override - [Rule 3 - Blocking] Fix MainWindowViewModel missing ExecuteOpenGlobalSitePicker and BroadcastGlobalSites stubs referenced in constructor (pre-existing partial state from earlier TODO commit)
105 lines
3.6 KiB
C#
105 lines
3.6 KiB
C#
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<FeatureViewModelBase> _logger;
|
|
|
|
[ObservableProperty]
|
|
[NotifyCanExecuteChangedFor(nameof(CancelCommand))]
|
|
private bool _isRunning;
|
|
|
|
[ObservableProperty]
|
|
private string _statusMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private int _progressValue;
|
|
|
|
/// <summary>
|
|
/// Sites selected in the global toolbar picker. Updated via GlobalSitesChangedMessage.
|
|
/// Derived VMs check this in RunOperationAsync before falling back to per-tab SiteUrl.
|
|
/// </summary>
|
|
protected IReadOnlyList<SiteInfo> GlobalSites { get; private set; } = Array.Empty<SiteInfo>();
|
|
|
|
public IAsyncRelayCommand RunCommand { get; }
|
|
public RelayCommand CancelCommand { get; }
|
|
|
|
protected FeatureViewModelBase(ILogger<FeatureViewModelBase> 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<OperationProgress>(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<OperationProgress> progress);
|
|
|
|
protected override void OnActivated()
|
|
{
|
|
Messenger.Register<TenantSwitchedMessage>(this, (r, m) => ((FeatureViewModelBase)r).OnTenantSwitched(m.Value));
|
|
Messenger.Register<GlobalSitesChangedMessage>(this, (r, m) => ((FeatureViewModelBase)r).OnGlobalSitesReceived(m.Value));
|
|
}
|
|
|
|
protected virtual void OnTenantSwitched(TenantProfile profile)
|
|
{
|
|
// Derived classes override to reset their state
|
|
}
|
|
|
|
private void OnGlobalSitesReceived(IReadOnlyList<SiteInfo> sites)
|
|
{
|
|
GlobalSites = sites;
|
|
OnGlobalSitesChanged(sites);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the global site selection changes. Override in derived VMs
|
|
/// to update UI state (e.g., pre-fill SiteUrl from first global site).
|
|
/// </summary>
|
|
protected virtual void OnGlobalSitesChanged(IReadOnlyList<SiteInfo> sites)
|
|
{
|
|
// Derived classes override to react to global site changes
|
|
}
|
|
}
|