- Add OpenGlobalSitePickerDialog factory property (dialog factory pattern) - Add GlobalSelectedSites ObservableCollection<SiteInfo> - Add GlobalSitesSelectedLabel computed property for toolbar display - Add OpenGlobalSitePickerCommand (disabled when no profile selected) - Broadcast GlobalSitesChangedMessage via WeakReferenceMessenger on collection change - Clear GlobalSelectedSites on tenant switch (OnSelectedProfileChanged) - Clear GlobalSelectedSites on session clear (ClearSessionAsync) - Add using SharepointToolbox.Views.Dialogs for SitePickerDialog cast
184 lines
6.1 KiB
C#
184 lines
6.1 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Windows;
|
|
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.Services;
|
|
using SharepointToolbox.Views.Dialogs;
|
|
|
|
namespace SharepointToolbox.ViewModels;
|
|
|
|
public partial class MainWindowViewModel : ObservableRecipient
|
|
{
|
|
private readonly ProfileService _profileService;
|
|
private readonly SessionManager _sessionManager;
|
|
private readonly ILogger<MainWindowViewModel> _logger;
|
|
|
|
// Set by the view layer (MainWindow.xaml.cs) to open the dialog using DI
|
|
public Func<Window>? OpenProfileManagementDialog { get; set; }
|
|
|
|
/// <summary>
|
|
/// Factory set by MainWindow.xaml.cs to open the SitePickerDialog for global site selection.
|
|
/// Returns the opened Window; ViewModel calls ShowDialog() on it.
|
|
/// </summary>
|
|
public Func<Window>? OpenGlobalSitePickerDialog { get; set; }
|
|
|
|
[ObservableProperty]
|
|
private TenantProfile? _selectedProfile;
|
|
|
|
[ObservableProperty]
|
|
private string _connectionStatus = "Not connected";
|
|
|
|
[ObservableProperty]
|
|
private string _progressStatus = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private int _progressPercentage;
|
|
|
|
public ObservableCollection<TenantProfile> TenantProfiles { get; } = new();
|
|
|
|
public ObservableCollection<SiteInfo> GlobalSelectedSites { get; } = new();
|
|
|
|
/// <summary>
|
|
/// Label for toolbar display: "3 site(s) selected" or "No sites selected".
|
|
/// </summary>
|
|
public string GlobalSitesSelectedLabel =>
|
|
GlobalSelectedSites.Count > 0
|
|
? $"{GlobalSelectedSites.Count} site(s) selected"
|
|
: "No sites selected";
|
|
|
|
public IAsyncRelayCommand ConnectCommand { get; }
|
|
public IAsyncRelayCommand ClearSessionCommand { get; }
|
|
public RelayCommand ManageProfilesCommand { get; }
|
|
public RelayCommand OpenGlobalSitePickerCommand { get; }
|
|
|
|
public MainWindowViewModel(
|
|
ProfileService profileService,
|
|
SessionManager sessionManager,
|
|
ILogger<MainWindowViewModel> logger)
|
|
{
|
|
_profileService = profileService;
|
|
_sessionManager = sessionManager;
|
|
_logger = logger;
|
|
|
|
ConnectCommand = new AsyncRelayCommand(ConnectAsync, () => SelectedProfile != null);
|
|
ClearSessionCommand = new AsyncRelayCommand(ClearSessionAsync, () => SelectedProfile != null);
|
|
ManageProfilesCommand = new RelayCommand(OpenProfileManagement);
|
|
OpenGlobalSitePickerCommand = new RelayCommand(ExecuteOpenGlobalSitePicker, () => SelectedProfile != null);
|
|
GlobalSelectedSites.CollectionChanged += (_, _) =>
|
|
{
|
|
OnPropertyChanged(nameof(GlobalSitesSelectedLabel));
|
|
BroadcastGlobalSites();
|
|
};
|
|
|
|
IsActive = true;
|
|
}
|
|
|
|
protected override void OnActivated()
|
|
{
|
|
base.OnActivated();
|
|
Messenger.Register<ProgressUpdatedMessage>(this, (r, m) =>
|
|
{
|
|
var vm = (MainWindowViewModel)r;
|
|
vm.ProgressStatus = m.Value.Message;
|
|
vm.ProgressPercentage = m.Value.Total > 0
|
|
? (int)(100.0 * m.Value.Current / m.Value.Total)
|
|
: 0;
|
|
});
|
|
}
|
|
|
|
partial void OnSelectedProfileChanged(TenantProfile? value)
|
|
{
|
|
if (value != null)
|
|
{
|
|
WeakReferenceMessenger.Default.Send(new TenantSwitchedMessage(value));
|
|
}
|
|
ConnectCommand.NotifyCanExecuteChanged();
|
|
ClearSessionCommand.NotifyCanExecuteChanged();
|
|
// Clear global site selection on tenant switch (sites belong to a tenant)
|
|
GlobalSelectedSites.Clear();
|
|
OpenGlobalSitePickerCommand.NotifyCanExecuteChanged();
|
|
}
|
|
|
|
public async Task LoadProfilesAsync()
|
|
{
|
|
try
|
|
{
|
|
var profiles = await _profileService.GetProfilesAsync();
|
|
TenantProfiles.Clear();
|
|
foreach (var profile in profiles)
|
|
TenantProfiles.Add(profile);
|
|
|
|
if (TenantProfiles.Count > 0)
|
|
SelectedProfile = TenantProfiles[0];
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to load tenant profiles.");
|
|
}
|
|
}
|
|
|
|
private async Task ConnectAsync()
|
|
{
|
|
if (SelectedProfile == null) return;
|
|
try
|
|
{
|
|
ConnectionStatus = "Connecting...";
|
|
await _sessionManager.GetOrCreateContextAsync(SelectedProfile, CancellationToken.None);
|
|
ConnectionStatus = SelectedProfile.Name;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ConnectionStatus = "Connection failed";
|
|
_logger.LogError(ex, "Failed to connect to tenant {TenantUrl}.", SelectedProfile.TenantUrl);
|
|
}
|
|
}
|
|
|
|
private async Task ClearSessionAsync()
|
|
{
|
|
if (SelectedProfile == null) return;
|
|
try
|
|
{
|
|
await _sessionManager.ClearSessionAsync(SelectedProfile.TenantUrl);
|
|
GlobalSelectedSites.Clear();
|
|
ConnectionStatus = "Not connected";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Failed to clear session for {TenantUrl}.", SelectedProfile.TenantUrl);
|
|
}
|
|
}
|
|
|
|
private void OpenProfileManagement()
|
|
{
|
|
if (OpenProfileManagementDialog == null) return;
|
|
var dialog = OpenProfileManagementDialog();
|
|
dialog.Owner = Application.Current.MainWindow;
|
|
dialog.ShowDialog();
|
|
|
|
// Reload profiles after dialog closes (modal — ShowDialog blocks until closed)
|
|
_ = LoadProfilesAsync();
|
|
}
|
|
|
|
private void ExecuteOpenGlobalSitePicker()
|
|
{
|
|
if (OpenGlobalSitePickerDialog == null) return;
|
|
var dialog = OpenGlobalSitePickerDialog.Invoke();
|
|
if (dialog?.ShowDialog() == true && dialog is SitePickerDialog picker)
|
|
{
|
|
GlobalSelectedSites.Clear();
|
|
foreach (var site in picker.SelectedUrls)
|
|
GlobalSelectedSites.Add(site);
|
|
}
|
|
}
|
|
|
|
private void BroadcastGlobalSites()
|
|
{
|
|
WeakReferenceMessenger.Default.Send(
|
|
new GlobalSitesChangedMessage(GlobalSelectedSites.ToList().AsReadOnly()));
|
|
}
|
|
}
|