chore: release v2.4
- Add theme system (Dark/Light palettes, ModernTheme, ThemeManager) - Add InputDialog, Spinner common view - Add DuplicatesCsvExportService - Refresh views, dialogs, and view models across tabs - Update localization strings (en/fr) - Tweak services (transfer, permissions, search, user access, ownership elevation, bulk operations) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using SharepointToolbox.Core.Models;
|
||||
using SharepointToolbox.Services;
|
||||
using SharepointToolbox.Views.Dialogs;
|
||||
|
||||
namespace SharepointToolbox.ViewModels.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Headless logic for <see cref="SitePickerDialog"/>. Loads sites via
|
||||
/// <see cref="ISiteListService"/> and applies filter/sort in memory so the
|
||||
/// dialog's code-behind stays a thin shim and the logic is unit-testable
|
||||
/// without a WPF host.
|
||||
/// </summary>
|
||||
public class SitePickerDialogLogic
|
||||
{
|
||||
private readonly ISiteListService _siteListService;
|
||||
private readonly TenantProfile _profile;
|
||||
|
||||
public SitePickerDialogLogic(ISiteListService siteListService, TenantProfile profile)
|
||||
{
|
||||
_siteListService = siteListService;
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all accessible sites for the tenant profile and wraps them in
|
||||
/// <see cref="SitePickerItem"/> so the dialog can bind checkboxes.
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<SitePickerItem>> LoadAsync(
|
||||
IProgress<OperationProgress> progress,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var sites = await _siteListService.GetSitesAsync(_profile, progress, ct);
|
||||
return sites
|
||||
.Select(s => new SitePickerItem(s.Url, s.Title, s.StorageUsedMb, s.StorageQuotaMb, s.Template))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters items by free-text (title/url substring), storage-size range,
|
||||
/// and site kind. Empty or zero-range parameters become no-ops.
|
||||
/// </summary>
|
||||
public static IEnumerable<SitePickerItem> ApplyFilter(
|
||||
IEnumerable<SitePickerItem> items,
|
||||
string text,
|
||||
long minMb,
|
||||
long maxMb,
|
||||
string kindFilter)
|
||||
{
|
||||
var result = items;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
result = result.Where(i =>
|
||||
i.Url.Contains(text, StringComparison.OrdinalIgnoreCase) ||
|
||||
i.Title.Contains(text, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
result = result.Where(i => i.StorageUsedMb >= minMb && i.StorageUsedMb <= maxMb);
|
||||
|
||||
if (!string.IsNullOrEmpty(kindFilter) && kindFilter != "All")
|
||||
result = result.Where(i => i.Kind.ToString() == kindFilter);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable sort by a named column and direction. Unknown column names
|
||||
/// return the input sequence unchanged.
|
||||
/// </summary>
|
||||
public static IEnumerable<SitePickerItem> ApplySort(
|
||||
IEnumerable<SitePickerItem> items,
|
||||
string column,
|
||||
ListSortDirection direction)
|
||||
{
|
||||
var asc = direction == ListSortDirection.Ascending;
|
||||
return column switch
|
||||
{
|
||||
"Title" => asc ? items.OrderBy(i => i.Title) : items.OrderByDescending(i => i.Title),
|
||||
"Url" => asc ? items.OrderBy(i => i.Url) : items.OrderByDescending(i => i.Url),
|
||||
"Kind" => asc ? items.OrderBy(i => i.KindDisplay) : items.OrderByDescending(i => i.KindDisplay),
|
||||
"StorageUsedMb" => asc
|
||||
? items.OrderBy(i => i.StorageUsedMb)
|
||||
: items.OrderByDescending(i => i.StorageUsedMb),
|
||||
"IsSelected" => asc
|
||||
? items.OrderBy(i => i.IsSelected)
|
||||
: items.OrderByDescending(i => i.IsSelected),
|
||||
_ => items
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lenient long parse: whitespace-only or unparseable input yields
|
||||
/// <paramref name="fallback"/> instead of throwing.
|
||||
/// </summary>
|
||||
public static long ParseLongOrDefault(string text, long fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return fallback;
|
||||
return long.TryParse(text.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var v)
|
||||
? v
|
||||
: fallback;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user