Folder operations, Gmail folder sync improvements and rework of menu items. (#273)

* New rename folder dialog keys.

* Insfra work for folder operations and rename folder code.

* RenameFolder for Gmail.

* Fixed input dialog to take custom take for primary button.

* Missing rename for DS call.

* Outlook to throw exception in case of error.

* Implemented rename folder functionality for Outlook.

* Remove default primary text from input dialog.

* Fixed an issue where outlook folder rename does not work.

* Disable vertical scroll for composing page editor items.

* Fixing some issues with imap folder sync.

* fix copy pasta

* TODO folder update/removed overrides for shell.

* New rename folder dialog keys.

* Insfra work for folder operations and rename folder code.

* RenameFolder for Gmail.

* Fixed input dialog to take custom take for primary button.

* Missing rename for DS call.

* Outlook to throw exception in case of error.

* Implemented rename folder functionality for Outlook.

* Remove default primary text from input dialog.

* Fixed an issue where outlook folder rename does not work.

* Disable vertical scroll for composing page editor items.

* Fixing some issues with imap folder sync.

* fix copy pasta

* TODO folder update/removed overrides for shell.

* New rename folder dialog keys.

* Insfra work for folder operations and rename folder code.

* RenameFolder for Gmail.

* Fixed input dialog to take custom take for primary button.

* Missing rename for DS call.

* Outlook to throw exception in case of error.

* Implemented rename folder functionality for Outlook.

* Remove default primary text from input dialog.

* Fixed an issue where outlook folder rename does not work.

* Disable vertical scroll for composing page editor items.

* Fixing some issues with imap folder sync.

* fix copy pasta

* TODO folder update/removed overrides for shell.

* New rename folder dialog keys.

* Fixed an issue where redundant older updates causing pivots to be re-created.

* New empty folder request

* New rename folder dialog keys.

* Insfra work for folder operations and rename folder code.

* RenameFolder for Gmail.

* Fixed input dialog to take custom take for primary button.

* Missing rename for DS call.

* Outlook to throw exception in case of error.

* Implemented rename folder functionality for Outlook.

* Remove default primary text from input dialog.

* Fixed an issue where outlook folder rename does not work.

* Fixing some issues with imap folder sync.

* fix copy pasta

* TODO folder update/removed overrides for shell.

* New rename folder dialog keys.

* New rename folder dialog keys.

* New rename folder dialog keys.

* Fixed an issue where redundant older updates causing pivots to be re-created.

* New empty folder request

* Enable empty folder on base sync.

* Move updates on event listeners.

* Remove folder UI messages.

* Reworked folder synchronization for gmail.

* Loading folders on the fly as the selected account changed instead of relying on cached menu items.

* Merged account folder items, re-navigating to existing rendering page.

* - Reworked merged account menu system.
- Reworked unread item count loadings.
- Fixed back button visibility.
- Instant rendering of mails if renderer is active.
- Animation fixes.
- Menu item re-load crash/hang fixes.

* Handle folder renaming on the UI.

* Empty folder for all synchronizers.

* New execution delay mechanism and handling folder mark as read for all synchronizers.

* Revert UI changes on failure for IMAP.

* Remove duplicate translation keys.

* Cleanup.
This commit is contained in:
Burak Kaan Köse
2024-07-09 01:05:16 +02:00
committed by GitHub
parent ac01006398
commit 536fbb23a1
67 changed files with 1396 additions and 1585 deletions

View File

@@ -19,7 +19,6 @@ using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.Extensions;
using Wino.Core.MenuItems;
using Wino.Core.Messages.Accounts;
using Wino.Core.Messages.Mails;
@@ -49,8 +48,8 @@ namespace Wino.Mail.ViewModels
private IAccountMenuItem latestSelectedAccountMenuItem;
public MenuItemCollection FooterItems { get; set; } = [];
public MenuItemCollection MenuItems { get; set; } = [];
public MenuItemCollection FooterItems { get; set; }
public MenuItemCollection MenuItems { get; set; }
private readonly SettingsItem SettingsItem = new SettingsItem();
@@ -118,6 +117,14 @@ namespace Wino.Mail.ViewModels
_winoRequestDelegator = winoRequestDelegator;
}
protected override void OnDispatcherAssigned()
{
base.OnDispatcherAssigned();
MenuItems = new MenuItemCollection(Dispatcher);
FooterItems = new MenuItemCollection(Dispatcher);
}
public IEnumerable<FolderOperationMenuItem> GetFolderContextMenuActions(IBaseFolderMenuItem folder)
{
if (folder == null || folder.SpecialFolderType == SpecialFolderType.Category || folder.SpecialFolderType == SpecialFolderType.More)
@@ -148,183 +155,63 @@ namespace Wino.Mail.ViewModels
private async Task LoadAccountsAsync()
{
var accounts = await _accountService.GetAccountsAsync();
// First clear all account menu items.
MenuItems.RemoveRange(MenuItems.Where(a => a is IAccountMenuItem));
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
// Group accounts by merged account.
var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId);
var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId).OrderBy(a => a.Key != null);
foreach (var accountGroup in groupedAccounts)
foreach (var group in groupedAccounts)
{
var mergedInbox = accountGroup.Key;
var mergedInboxId = group.Key;
if (mergedInbox == null)
if (mergedInboxId == null)
{
// This account is not merged. Create menu item for each account.
foreach (var account in accountGroup)
// Single accounts.
// Preserve the order while listing.
var orderedGroup = group.OrderBy(a => a.Order);
foreach (var account in orderedGroup)
{
await CreateNestedAccountMenuItem(account);
await ExecuteUIThread(() =>
{
MenuItems.Add(new AccountMenuItem(account, null));
});
}
}
else
{
// Accounts are merged. Create menu item for merged inbox.
await CreateMergedInboxMenuItemAsync(accountGroup);
// Merged accounts.
var mergedInbox = group.First().MergedInbox;
var mergedAccountMenuItem = new MergedAccountMenuItem(mergedInbox, group, null);
foreach (var accountItem in group)
{
mergedAccountMenuItem.SubMenuItems.Add(new AccountMenuItem(accountItem, mergedAccountMenuItem));
}
await ExecuteUIThread(() =>
{
MenuItems.Add(mergedAccountMenuItem);
});
}
}
// Re-assign latest selected account menu item for containers to reflect changes better.
// Also , this will ensure that the latest selected account is still selected after re-creation.
if (latestSelectedAccountMenuItem != null)
{
latestSelectedAccountMenuItem = MenuItems.GetAccountMenuItem(latestSelectedAccountMenuItem.EntityId.GetValueOrDefault());
if (latestSelectedAccountMenuItem != null)
{
latestSelectedAccountMenuItem.IsSelected = true;
}
}
}
protected override async void OnFolderUpdated(MailItemFolder updatedFolder, MailAccount account)
{
base.OnFolderUpdated(updatedFolder, account);
if (updatedFolder == null) return;
var folderMenuItemsToUpdate = MenuItems.GetFolderItems(updatedFolder.Id);
foreach (var item in folderMenuItemsToUpdate)
if (latestSelectedAccountMenuItem != null && MenuItems.TryGetAccountMenuItem(latestSelectedAccountMenuItem.EntityId.GetValueOrDefault(), out IAccountMenuItem foundLatestSelectedAccountMenuItem))
{
await ExecuteUIThread(() =>
{
item.UpdateFolder(updatedFolder);
foundLatestSelectedAccountMenuItem.IsSelected = true;
});
}
}
private async Task CreateMergedInboxMenuItemAsync(IEnumerable<MailAccount> accounts)
{
var mergedInbox = accounts.First().MergedInbox;
var mergedInboxMenuItem = new MergedAccountMenuItem(mergedInbox, null); // Merged accounts are parentless.
// Store common special type folders.
var commonFolderList = new Dictionary<MailAccount, IMailItemFolder>();
// Map special folder types for each account.
var accountTreeList = new List<AccountFolderTree>();
foreach (var account in accounts)
{
var accountStructure = await _folderService.GetFolderStructureForAccountAsync(account.Id, includeHiddenFolders: true);
accountTreeList.Add(accountStructure);
}
var allFolders = accountTreeList.SelectMany(a => a.Folders);
// 1. Group sticky folders by special folder type.
// 2. Merge all folders that are sticky and have the same special folder type.
// 3. Add merged folder menu items to the merged inbox menu item.
// 4. Add remaining sticky folders that doesn't exist in all accounts as plain folder menu items.
var stickyFolders = allFolders.Where(a => a.IsSticky);
var grouped = stickyFolders
.GroupBy(a => a.SpecialFolderType)
.Where(a => accountTreeList.All(b => b.HasSpecialTypeFolder(a.Key)));
var mergedInboxItems = grouped.Select(a => new MergedAccountFolderMenuItem(a.ToList(), mergedInboxMenuItem, mergedInbox));
// Shared common folders.
foreach (var mergedInboxFolder in mergedInboxItems)
{
mergedInboxMenuItem.SubMenuItems.Add(mergedInboxFolder);
}
var usedFolderIds = mergedInboxItems.SelectMany(a => a.Parameter.Select(a => a.Id));
var remainingStickyFolders = stickyFolders.Where(a => !usedFolderIds.Contains(a.Id));
// Marked as sticky, but doesn't exist in all accounts. Add as plain folder menu item.
foreach (var remainingStickyFolder in remainingStickyFolders)
{
var account = accounts.FirstOrDefault(a => a.Id == remainingStickyFolder.MailAccountId);
mergedInboxMenuItem.SubMenuItems.Add(new FolderMenuItem(remainingStickyFolder, account, mergedInboxMenuItem));
}
var mergedMoreItem = new MergedAccountMoreFolderMenuItem(null, null, mergedInboxMenuItem);
// 2. Sticky folder preparation is done. Continue with regular account menu items.
foreach (var accountTree in accountTreeList)
{
var tree = accountTree.GetAccountMenuTree(mergedInboxMenuItem);
mergedMoreItem.SubMenuItems.Add(tree);
}
mergedInboxMenuItem.SubMenuItems.Add(mergedMoreItem);
MenuItems.Add(mergedInboxMenuItem);
// Instead of refreshing all accounts, refresh the merged account only.
// Receiver will handle it.
Messenger.Send(new RefreshUnreadCountsMessage(mergedInbox.Id));
}
private async Task<IAccountMenuItem> CreateNestedAccountMenuItem(MailAccount account)
{
try
{
await accountInitFolderUpdateSlim.WaitAsync();
// Don't remove but replace existing record.
int existingIndex = -1;
var existingAccountMenuItem = MenuItems.FirstOrDefault(a => a is AccountMenuItem accountMenuItem && accountMenuItem.Parameter.Id == account.Id);
if (existingAccountMenuItem != null)
{
existingIndex = MenuItems.IndexOf(existingAccountMenuItem);
}
// Create account structure with integrator for this menu item.
var accountStructure = await _folderService.GetFolderStructureForAccountAsync(account.Id, includeHiddenFolders: false);
var createdMenuItem = accountStructure.GetAccountMenuTree();
await ExecuteUIThread(() =>
{
if (existingIndex >= 0)
{
createdMenuItem.IsExpanded = existingAccountMenuItem.IsExpanded;
MenuItems.RemoveAt(existingIndex);
MenuItems.Insert(existingIndex, createdMenuItem);
}
else
{
MenuItems.AddAccountMenuItem(createdMenuItem);
}
});
Messenger.Send(new RefreshUnreadCountsMessage(account.Id));
return createdMenuItem;
}
catch (Exception ex)
{
Log.Error(ex, WinoErrors.AccountStructureRender);
}
finally
{
accountInitFolderUpdateSlim.Release();
}
return null;
}
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
@@ -371,7 +258,6 @@ namespace Wino.Mail.ViewModels
Type = SynchronizationType.Inbox
};
Messenger.Send(new NewSynchronizationRequested(options));
}
}
@@ -392,9 +278,9 @@ namespace Wino.Mail.ViewModels
// Find the account that this folder and mail belongs to.
var account = await _mailService.GetMailAccountByUniqueIdAsync(accountExtendedMessage.NavigateMailItem.UniqueId).ConfigureAwait(false);
if (account != null && MenuItems.GetAccountMenuItem(account.Id) is IAccountMenuItem accountMenuItem)
if (account != null && MenuItems.TryGetAccountMenuItem(account.Id, out IAccountMenuItem accountMenuItem))
{
ChangeLoadedAccount(accountMenuItem);
await ChangeLoadedAccountAsync(accountMenuItem);
WeakReferenceMessenger.Default.Send(accountExtendedMessage);
@@ -402,7 +288,7 @@ namespace Wino.Mail.ViewModels
}
else
{
ProcessLaunchDefault();
await ProcessLaunchDefaultAsync();
}
}
}
@@ -419,7 +305,7 @@ namespace Wino.Mail.ViewModels
else
{
// Use default startup extending.
ProcessLaunchDefault();
await ProcessLaunchDefaultAsync();
}
}
}
@@ -429,7 +315,7 @@ namespace Wino.Mail.ViewModels
}
}
private void ProcessLaunchDefault()
private async Task ProcessLaunchDefaultAsync()
{
if (PreferencesService.StartupEntityId == null)
{
@@ -452,7 +338,7 @@ namespace Wino.Mail.ViewModels
if (startupEntityMenuItem is IAccountMenuItem startupAccountMenuItem)
{
ChangeLoadedAccount(startupAccountMenuItem);
await ChangeLoadedAccountAsync(startupAccountMenuItem);
}
}
}
@@ -470,30 +356,49 @@ namespace Wino.Mail.ViewModels
var args = new NavigateMailFolderEventArgs(baseFolderMenuItem, mailInitCompletionSource);
NavigationService.NavigateFolder(args);
StatePersistenceService.CoreWindowTitle = $"{baseFolderMenuItem.AssignedAccountName} - {baseFolderMenuItem.FolderName}";
UpdateWindowTitleForFolder(baseFolderMenuItem);
// Wait until mail list page picks up the event and finish initialization of the mails.
await mailInitCompletionSource.Task;
}
private void UpdateWindowTitleForFolder(IBaseFolderMenuItem folder)
{
StatePersistenceService.CoreWindowTitle = $"{folder.AssignedAccountName} - {folder.FolderName}";
}
private async Task NavigateSpecialFolderAsync(MailAccount account, SpecialFolderType specialFolderType, bool extendAccountMenu)
{
try
{
if (account == null) return;
// If the account is inside a merged account, expand the merged account and navigate to shared folder.
if (MenuItems.TryGetMergedAccountRootFolderMenuItemByAccountId(account.Id, specialFolderType, out MergedAccountFolderMenuItem mergedFolderItem))
{
mergedFolderItem.Expand();
await NavigateFolderAsync(mergedFolderItem);
}
else if (MenuItems.TryGetRootSpecialFolderMenuItem(account.Id, specialFolderType, out FolderMenuItem rootFolderMenuItem))
{
// Account is not in merged account. Navigate to root folder.
if (!MenuItems.TryGetAccountMenuItem(account.Id, out IAccountMenuItem accountMenuItem)) return;
rootFolderMenuItem.Expand();
await NavigateFolderAsync(rootFolderMenuItem);
// First make sure to navigate to the given accounnt.
if (latestSelectedAccountMenuItem != accountMenuItem)
{
await ChangeLoadedAccountAsync(accountMenuItem, false);
}
// Account folders are already initialized.
// Try to find the special folder menu item and navigate to it.
if (latestSelectedAccountMenuItem is IMergedAccountMenuItem latestMergedAccountMenuItem)
{
if (MenuItems.TryGetMergedAccountSpecialFolderMenuItem(latestSelectedAccountMenuItem.EntityId.Value, specialFolderType, out IBaseFolderMenuItem mergedFolderMenuItem))
{
await NavigateFolderAsync(mergedFolderMenuItem);
}
}
else if (latestSelectedAccountMenuItem is IAccountMenuItem latestAccountMenuItem)
{
if (MenuItems.TryGetSpecialFolderMenuItem(account.Id, specialFolderType, out FolderMenuItem rootFolderMenuItem))
{
await NavigateFolderAsync(rootFolderMenuItem);
}
}
}
catch (Exception ex)
@@ -566,7 +471,12 @@ namespace Wino.Mail.ViewModels
foreach (var folder in folderMenuItem.HandlingFolders)
{
await _winoRequestDelegator.ExecuteAsync(operation, folder);
if (folder is MailItemFolder realFolder)
{
var folderPrepRequest = new FolderOperationPreperationRequest(operation, realFolder);
await _winoRequestDelegator.ExecuteAsync(folderPrepRequest);
}
}
// Refresh the pins.
@@ -632,6 +542,11 @@ namespace Wino.Mail.ViewModels
await NavigateFolderAsync(baseFolderMenuItem);
}
else if (clickedMenuItem is MergedAccountMenuItem clickedMergedAccountMenuItem && latestSelectedAccountMenuItem != clickedMenuItem)
{
// Don't navigate to merged account if it's already selected. Preserve user's already selected folder.
await ChangeLoadedAccountAsync(clickedMergedAccountMenuItem, true);
}
else if (clickedMenuItem is SettingsItem)
{
NavigationService.Navigate(WinoPage.SettingsPage);
@@ -642,19 +557,23 @@ namespace Wino.Mail.ViewModels
}
else if (clickedMenuItem is IAccountMenuItem clickedAccountMenuItem && latestSelectedAccountMenuItem != clickedAccountMenuItem)
{
ChangeLoadedAccount(clickedAccountMenuItem);
await ChangeLoadedAccountAsync(clickedAccountMenuItem);
}
}
private async void ChangeLoadedAccount(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true)
private async Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true)
{
if (clickedBaseAccountMenuItem == null) return;
// User clicked an account in Windows Mail style menu.
// List folders for this account and select Inbox.
await MenuItems.SetAccountMenuItemEnabledStatusAsync(false);
await ExecuteUIThread(() =>
{
clickedBaseAccountMenuItem.IsEnabled = false;
if (latestSelectedAccountMenuItem != null)
{
latestSelectedAccountMenuItem.IsSelected = false;
@@ -662,20 +581,18 @@ namespace Wino.Mail.ViewModels
clickedBaseAccountMenuItem.IsSelected = true;
latestSelectedAccountMenuItem = clickedBaseAccountMenuItem;
if (clickedBaseAccountMenuItem is AccountMenuItem accountMenuItem)
{
MenuItems.ReplaceFolders(accountMenuItem.SubMenuItems);
}
else if (clickedBaseAccountMenuItem is MergedAccountMenuItem mergedAccountMenuItem)
{
MenuItems.ReplaceFolders(mergedAccountMenuItem.SubMenuItems);
}
});
// Load account folder structure and replace the visible folders.
var folders = await _folderService.GetAccountFoldersForDisplayAsync(clickedBaseAccountMenuItem);
// var unreadCountResult = await _folderService.GetUnreadItemCountResultsAsync(clickedBaseAccountMenuItem.HoldingAccounts.Select(a => a.Id)).ConfigureAwait(false);
await MenuItems.ReplaceFoldersAsync(folders);
await UpdateUnreadItemCountAsync();
await MenuItems.SetAccountMenuItemEnabledStatusAsync(true);
if (navigateInbox)
{
await Task.Yield();
@@ -687,6 +604,67 @@ namespace Wino.Mail.ViewModels
}
}
private async Task UpdateUnreadItemCountAsync()
{
// Get visible account menu items, ordered by merged accounts at the last.
// We will update the unread counts for all single accounts and trigger UI refresh for merged menu items.
var accountMenuItems = MenuItems.GetAllAccountMenuItems().OrderBy(a => a.HoldingAccounts.Count());
// Individually get all single accounts' unread counts.
var accountIds = accountMenuItems.OfType<AccountMenuItem>().Select(a => a.AccountId);
var unreadCountResult = await _folderService.GetUnreadItemCountResultsAsync(accountIds).ConfigureAwait(false);
// Recursively update all folders' unread counts to 0.
// Query above only returns unread counts that exists. We need to reset the rest to 0 first.
await ExecuteUIThread(() =>
{
MenuItems.UpdateUnreadItemCountsToZero();
});
foreach (var accountMenuItem in accountMenuItems)
{
if (accountMenuItem is MergedAccountMenuItem mergedAccountMenuItem)
{
await ExecuteUIThread(() =>
{
mergedAccountMenuItem.RefreshFolderItemCount();
});
}
else
{
await ExecuteUIThread(() =>
{
accountMenuItem.UnreadItemCount = unreadCountResult
.Where(a => a.AccountId == accountMenuItem.HoldingAccounts.First().Id && a.SpecialFolderType == SpecialFolderType.Inbox)
.Sum(a => a.UnreadItemCount);
});
}
}
// Try to update unread counts for all folders.
foreach (var unreadCount in unreadCountResult)
{
if (MenuItems.TryGetFolderMenuItem(unreadCount.FolderId, out IBaseFolderMenuItem folderMenuItem))
{
if (folderMenuItem is IMergedAccountFolderMenuItem mergedAccountFolderMenuItem)
{
await ExecuteUIThread(() =>
{
folderMenuItem.UnreadItemCount = unreadCountResult.Where(a => a.SpecialFolderType == unreadCount.SpecialFolderType && mergedAccountFolderMenuItem.HandlingFolders.Select(b => b.Id).Contains(a.FolderId)).Sum(a => a.UnreadItemCount);
});
}
else
{
await ExecuteUIThread(() =>
{
folderMenuItem.UnreadItemCount = unreadCount.UnreadItemCount;
});
}
}
}
}
private async void NavigateInbox(IAccountMenuItem clickedBaseAccountMenuItem)
{
if (clickedBaseAccountMenuItem is AccountMenuItem accountMenuItem)
@@ -741,7 +719,16 @@ namespace Wino.Mail.ViewModels
else
{
// There are multiple accounts and there is no selection.
Messenger.Send(new CreateNewMailWithMultipleAccountsRequested(accounts));
// Don't list all accounts, but only accounts that belong to Merged Inbox.
if (latestSelectedAccountMenuItem is MergedAccountMenuItem selectedMergedAccountMenuItem)
{
var mergedAccounts = accounts.Where(a => a.MergedInboxId == selectedMergedAccountMenuItem.EntityId);
if (!mergedAccounts.Any()) return;
Messenger.Send(new CreateNewMailWithMultipleAccountsRequested(mergedAccounts.ToList()));
}
}
}
@@ -851,18 +838,26 @@ namespace Wino.Mail.ViewModels
protected override async void OnAccountUpdated(MailAccount updatedAccount)
=> await ExecuteUIThread(() => { MenuItems.GetAccountMenuItem(updatedAccount.Id)?.UpdateAccount(updatedAccount); });
{
await ExecuteUIThread(() =>
{
if (MenuItems.TryGetAccountMenuItem(updatedAccount.Id, out IAccountMenuItem foundAccountMenuItem))
{
foundAccountMenuItem.UpdateAccount(updatedAccount);
}
});
}
protected override void OnAccountRemoved(MailAccount removedAccount)
=> Messenger.Send(new AccountsMenuRefreshRequested(true));
protected override async void OnAccountCreated(MailAccount createdAccount)
{
var createdMenuItem = await CreateNestedAccountMenuItem(createdAccount);
await RecreateMenuItemsAsync();
if (createdMenuItem == null) return;
if (!MenuItems.TryGetAccountMenuItem(createdAccount.Id, out IAccountMenuItem createdMenuItem)) return;
ChangeLoadedAccount(createdMenuItem);
await ChangeLoadedAccountAsync(createdMenuItem);
// Each created account should start a new synchronization automatically.
var options = new SynchronizationOptions()
@@ -876,96 +871,9 @@ namespace Wino.Mail.ViewModels
await _nativeAppService.PinAppToTaskbarAsync();
}
/// <summary>
/// Updates given single account menu item's unread count for all folders.
/// </summary>
/// <param name="accountMenuItem">Menu item to update unread count for.</param>
/// <returns>Unread item count for Inbox only.</returns>
private async Task<int> UpdateSingleAccountMenuItemUnreadCountAsync(AccountMenuItem accountMenuItem)
{
var accountId = accountMenuItem.AccountId;
int inboxItemCount = 0;
// Get the folders needed to be refreshed.
var allFolders = await _folderService.GetUnreadUpdateFoldersAsync(accountId);
foreach (var folder in allFolders)
{
var unreadItemCount = await UpdateAccountFolderUnreadItemCountAsync(accountMenuItem, folder.Id);
if (folder.SpecialFolderType == SpecialFolderType.Inbox)
{
inboxItemCount = unreadItemCount;
await ExecuteUIThread(() => { accountMenuItem.UnreadItemCount = unreadItemCount; });
}
}
return inboxItemCount;
}
private async Task RefreshUnreadCountsForAccountAsync(Guid accountId)
{
// TODO: Merged accounts unread item count.
var accountMenuItem = MenuItems.GetAccountMenuItem(accountId);
if (accountMenuItem == null) return;
if (accountMenuItem is AccountMenuItem singleAccountMenuItem)
{
await UpdateSingleAccountMenuItemUnreadCountAsync(singleAccountMenuItem);
}
else if (accountMenuItem is MergedAccountMenuItem mergedAccountMenuItem)
{
// Merged account.
// Root account should include all parent accounts' unread item count.
int totalUnreadCount = 0;
var individualAccountMenuItems = mergedAccountMenuItem.GetAccountMenuItems();
foreach (var singleMenuItem in individualAccountMenuItems)
{
totalUnreadCount += await UpdateSingleAccountMenuItemUnreadCountAsync(singleMenuItem);
}
// At this point all single accounts are calculated.
// Merge account folder's menu items can be calculated from those values for precision.
await ExecuteUIThread(() =>
{
mergedAccountMenuItem.RefreshFolderItemCount();
mergedAccountMenuItem.UnreadItemCount = totalUnreadCount;
});
}
await ExecuteUIThread(async () => { await _notificationBuilder.UpdateTaskbarIconBadgeAsync(); });
}
private async Task<int> UpdateAccountFolderUnreadItemCountAsync(AccountMenuItem accountMenuItem, Guid folderId)
{
if (accountMenuItem == null) return 0;
var folder = accountMenuItem.FlattenedFolderHierarchy.Find(a => a.Parameter?.Id == folderId);
if (folder == null) return 0;
int folderUnreadItemCount = 0;
folderUnreadItemCount = await _folderService.GetFolderNotificationBadgeAsync(folder.Parameter.Id).ConfigureAwait(false);
await ExecuteUIThread(() => { folder.UnreadItemCount = folderUnreadItemCount; });
return folderUnreadItemCount;
}
private async Task SetAccountAttentionAsync(Guid accountId, AccountAttentionReason reason)
{
var accountMenuItem = MenuItems.GetAccountMenuItem(accountId);
if (accountMenuItem == null) return;
if (!MenuItems.TryGetAccountMenuItem(accountId, out IAccountMenuItem accountMenuItem)) return;
var accountModel = accountMenuItem.HoldingAccounts.First(a => a.Id == accountId);
@@ -1025,7 +933,7 @@ namespace Wino.Mail.ViewModels
}
public async void Receive(RefreshUnreadCountsMessage message)
=> await RefreshUnreadCountsForAccountAsync(message.AccountId);
=> await UpdateUnreadItemCountAsync();
public async void Receive(AccountsMenuRefreshRequested message)
{
@@ -1035,7 +943,7 @@ namespace Wino.Mail.ViewModels
{
if (MenuItems.FirstOrDefault(a => a is IAccountMenuItem) is IAccountMenuItem firstAccount)
{
ChangeLoadedAccount(firstAccount);
await ChangeLoadedAccountAsync(firstAccount);
}
}
}
@@ -1057,21 +965,48 @@ namespace Wino.Mail.ViewModels
await CreateFooterItemsAsync();
await RecreateMenuItemsAsync();
ChangeLoadedAccount(latestSelectedAccountMenuItem, navigateInbox: false);
await ChangeLoadedAccountAsync(latestSelectedAccountMenuItem, navigateInbox: false);
}
private void ReorderAccountMenuItems(Dictionary<Guid, int> newAccountOrder)
{
foreach (var item in newAccountOrder)
{
var menuItem = MenuItems.GetAccountMenuItem(item.Key);
if (menuItem == null) continue;
if (!MenuItems.TryGetAccountMenuItem(item.Key, out IAccountMenuItem menuItem)) return;
MenuItems.Move(MenuItems.IndexOf(menuItem), item.Value);
}
}
public void Receive(AccountMenuItemsReordered message) => ReorderAccountMenuItems(message.newOrderDictionary);
private async void UpdateFolderCollection(IMailItemFolder updatedMailItemFolder)
{
var menuItem = MenuItems.GetAllFolderMenuItems(updatedMailItemFolder.Id);
if (!menuItem.Any()) return;
foreach (var item in menuItem)
{
await ExecuteUIThread(() =>
{
item.UpdateFolder(updatedMailItemFolder);
});
}
}
protected override void OnFolderRenamed(IMailItemFolder mailItemFolder)
{
base.OnFolderRenamed(mailItemFolder);
UpdateFolderCollection(mailItemFolder);
}
protected override void OnFolderSynchronizationEnabled(IMailItemFolder mailItemFolder)
{
base.OnFolderSynchronizationEnabled(mailItemFolder);
UpdateFolderCollection(mailItemFolder);
}
}
}