Files
Sharepoint-Toolbox/SharepointToolbox/ViewModels/MainWindowViewModel.cs
Dev 467a940c6f feat(06-03): localize GlobalSitesSelectedLabel in MainWindowViewModel
- Replace hardcoded EN strings with TranslationSource.Instance lookups
- Uses toolbar.globalSites.count (formatted) and toolbar.globalSites.none keys
- Follows same pattern as PermissionsViewModel.SitesSelectedLabel
2026-04-07 10:06:57 +02:00

184 lines
6.2 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" (localized).
/// </summary>
public string GlobalSitesSelectedLabel =>
GlobalSelectedSites.Count > 0
? string.Format(Localization.TranslationSource.Instance["toolbar.globalSites.count"], GlobalSelectedSites.Count)
: Localization.TranslationSource.Instance["toolbar.globalSites.none"];
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()));
}
}