diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 2b73c5c5..030217b0 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -1586,5 +1586,13 @@ "Exception_FailedToSynchronizeCategories": "Failed to synchronize categories", "ImapCalDavSettings_AutoDiscoveryFailed": "Auto-discovery failed. Please enter settings manually in the Advanced tab.", "AccountContextMenu_ManageAccountSettings": "Manage account settings", - "AccountContextMenu_CreateFolder": "Create folder" + "AccountContextMenu_CreateFolder": "Create folder", + "WinoAccount_Management_SummarySectionTitle": "Account Summary", + "WinoAccount_Management_SummarySectionDescription": "Overview of connected account usage", + "WinoAccount_Management_SummaryAccountsLabel": "Accounts", + "WinoAccount_Management_SummaryEmailsLabel": "Emails", + "WinoAccount_Management_SummaryStorageLabel": "Storage", + "WinoAccount_Management_SummaryCalendarsLabel": "Calendars", + "WinoAccount_Management_SummaryDefaultAccountLabel": "Default account: {0}", + "WinoAccount_Management_SummaryUnavailable": "—" } diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs index 531ae4c4..19e535a3 100644 --- a/Wino.Mail.ViewModels/AccountManagementViewModel.cs +++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -20,27 +21,47 @@ using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; using Wino.Core.Services; using Wino.Core.ViewModels; using Wino.Core.ViewModels.Data; using Wino.Mail.ViewModels.Data; using Wino.Messaging.Client.Navigation; using Wino.Messaging.UI; +using Wino.Services; namespace Wino.Mail.ViewModels; public partial class AccountManagementViewModel : AccountManagementPageViewModelBase { private const string LocalExportFileName = "wino-data-export.json"; + private const string SummaryUnavailable = "—"; private static readonly UTF8Encoding Utf8WithoutBom = new(false); private readonly IWinoAccountDataSyncService _syncService; private readonly IWinoLogger _winoLogger; private readonly ISpecialImapProviderConfigResolver _specialImapProviderConfigResolver; private readonly ICalDavClient _calDavClient; + private readonly IMimeStorageService _mimeStorageService; + private readonly ICalendarService _calendarService; public IMailDialogService MailDialogService { get; } + [ObservableProperty] + public partial string SummaryAccountCountText { get; set; } = SummaryUnavailable; + + [ObservableProperty] + public partial string SummaryEmailCountText { get; set; } = SummaryUnavailable; + + [ObservableProperty] + public partial string SummaryStorageText { get; set; } = SummaryUnavailable; + + [ObservableProperty] + public partial string SummaryCalendarCountText { get; set; } = SummaryUnavailable; + + [ObservableProperty] + public partial string SummaryDefaultAccountText { get; set; } = string.Format(Translator.WinoAccount_Management_SummaryDefaultAccountLabel, SummaryUnavailable); + public AccountManagementViewModel(IMailDialogService dialogService, INavigationService navigationService, IAccountService accountService, @@ -50,6 +71,8 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel IWinoLogger winoLogger, ISpecialImapProviderConfigResolver specialImapProviderConfigResolver, ICalDavClient calDavClient, + IMimeStorageService mimeStorageService, + ICalendarService calendarService, IAuthenticationProvider authenticationProvider, IPreferencesService preferencesService) : base(dialogService, navigationService, accountService, providerService, winoAccountProfileService, authenticationProvider, preferencesService) { @@ -58,6 +81,8 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel _winoLogger = winoLogger; _specialImapProviderConfigResolver = specialImapProviderConfigResolver; _calDavClient = calDavClient; + _mimeStorageService = mimeStorageService; + _calendarService = calendarService; } [ObservableProperty] @@ -398,7 +423,90 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel } }); + await RefreshAccountSummaryAsync(accounts).ConfigureAwait(false); + } + private async Task RefreshAccountSummaryAsync(List accounts) + { + await ExecuteUIThread(() => + { + SummaryAccountCountText = accounts.Count.ToString(CultureInfo.CurrentCulture); + SummaryEmailCountText = SummaryUnavailable; + SummaryStorageText = SummaryUnavailable; + SummaryCalendarCountText = SummaryUnavailable; + SummaryDefaultAccountText = string.Format( + Translator.WinoAccount_Management_SummaryDefaultAccountLabel, + GetDefaultAccountName(accounts)); + }); + + try + { + var accountIds = accounts.Select(a => a.Id).ToList(); + + if (accountIds.Count > 0) + { + var storageSizes = await _mimeStorageService.GetAccountsMimeStorageSizesAsync(accountIds).ConfigureAwait(false); + + var totalStorageBytes = storageSizes.Values.DefaultIfEmpty(0L).Sum(); + var totalCalendarCount = 0; + + foreach (var account in accounts) + { + try + { + var calendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false); + totalCalendarCount += calendars?.Count ?? 0; + } + catch + { + // Keep partial summary data if a specific account calendar lookup fails. + } + } + + await ExecuteUIThread(() => + { + SummaryEmailCountText = SummaryUnavailable; + SummaryStorageText = totalStorageBytes > 0 + ? totalStorageBytes.GetBytesReadable() + : "0 B"; + SummaryCalendarCountText = totalCalendarCount.ToString(CultureInfo.CurrentCulture); + }); + } + } + catch + { + // Leave graceful placeholders if summary data can't be gathered. + } + } + + private string GetDefaultAccountName(List accounts) + { + var startupEntityId = PreferencesService.StartupEntityId; + + if (startupEntityId != null) + { + var startupAccount = accounts.FirstOrDefault(a => a.Id == startupEntityId) + ?? accounts.FirstOrDefault(a => a.MergedInboxId == startupEntityId) + ?? accounts.FirstOrDefault(a => a.MergedInbox?.Id == startupEntityId); + + if (startupAccount?.MergedInbox != null) + return startupAccount.MergedInbox.Name; + + if (!string.IsNullOrWhiteSpace(startupAccount?.Name)) + return startupAccount.Name; + + if (!string.IsNullOrWhiteSpace(startupAccount?.Address)) + return startupAccount.Address; + } + + var firstAccount = accounts.FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(firstAccount?.Name)) + return firstAccount.Name; + + if (!string.IsNullOrWhiteSpace(firstAccount?.Address)) + return firstAccount.Address; + + return SummaryUnavailable; } private static string BuildExportSuccessMessage(Wino.Core.Domain.Models.Accounts.WinoAccountSyncExportResult result) diff --git a/Wino.Mail.WinUI/Views/ManageAccountsPage.xaml b/Wino.Mail.WinUI/Views/ManageAccountsPage.xaml index 8b9394b3..b76a59b9 100644 --- a/Wino.Mail.WinUI/Views/ManageAccountsPage.xaml +++ b/Wino.Mail.WinUI/Views/ManageAccountsPage.xaml @@ -15,21 +15,99 @@ MaxWidth="900" Padding="20" HorizontalAlignment="Stretch"> - - -