Files

513 lines
18 KiB
C#
Raw Permalink Normal View History

2024-04-18 01:44:37 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Collections;
using Serilog;
using Wino.Core.Domain.Entities.Mail;
2024-04-18 01:44:37 +02:00
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Comparers;
using Wino.Core.Domain.Models.MailItem;
using Wino.Mail.ViewModels.Data;
2025-02-16 11:54:23 +01:00
namespace Wino.Mail.ViewModels.Collections;
public class WinoMailCollection
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
// We cache each mail copy id for faster access on updates.
// If the item provider here for update or removal doesn't exist here
// we can ignore the operation.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public HashSet<Guid> MailCopyIdHashSet = [];
2025-02-16 11:54:23 +01:00
public event EventHandler<IMailItem> MailItemRemoved;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private ListItemComparer listComparer = new ListItemComparer();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private readonly ObservableGroupedCollection<object, IMailItem> _mailItemSource = new ObservableGroupedCollection<object, IMailItem>();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public ReadOnlyObservableGroupedCollection<object, IMailItem> MailItems { get; }
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Property that defines how the item sorting should be done in the collection.
/// </summary>
public SortingOptionType SortingType { get; set; }
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Threading strategy that will help thread items according to the account type.
/// </summary>
public IThreadingStrategyProvider ThreadingStrategyProvider { get; set; }
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Automatically deletes single mail items after the delete operation or thread->single transition.
/// This is useful when reply draft is discarded in the thread. Only enabled for Draft folder for now.
/// </summary>
public bool PruneSingleNonDraftItems { get; set; }
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public int Count => _mailItemSource.Count;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public IDispatcher CoreDispatcher { get; set; }
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
private readonly ThreadingManager _threadingManager;
public WinoMailCollection(IThreadingStrategyProvider threadingStrategyProvider)
2025-02-16 11:54:23 +01:00
{
2025-02-26 19:59:20 +01:00
_threadingManager = new ThreadingManager(threadingStrategyProvider);
2025-02-16 11:54:23 +01:00
MailItems = new ReadOnlyObservableGroupedCollection<object, IMailItem>(_mailItemSource);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void Clear() => _mailItemSource.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private object GetGroupingKey(IMailItem mailItem)
{
if (SortingType == SortingOptionType.ReceiveDate)
return mailItem.CreationDate.ToLocalTime().Date;
else
return mailItem.FromName;
}
2025-02-16 11:54:23 +01:00
private void UpdateUniqueIdHashes(IMailHashContainer itemContainer, bool isAdd)
{
foreach (var item in itemContainer.GetContainingIds())
{
2025-02-16 11:54:23 +01:00
if (isAdd)
{
2025-02-16 11:54:23 +01:00
MailCopyIdHashSet.Add(item);
}
else
{
2025-02-16 11:54:23 +01:00
MailCopyIdHashSet.Remove(item);
}
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void InsertItemInternal(object groupKey, IMailItem mailItem)
{
UpdateUniqueIdHashes(mailItem, true);
if (mailItem is MailCopy mailCopy)
{
_mailItemSource.InsertItem(groupKey, listComparer, new MailItemViewModel(mailCopy), listComparer.GetItemComparer());
}
else if (mailItem is ThreadMailItem threadMailItem)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
_mailItemSource.InsertItem(groupKey, listComparer, new ThreadMailItemViewModel(threadMailItem), listComparer.GetItemComparer());
}
else
{
_mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer());
}
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void RemoveItemInternal(ObservableGroup<object, IMailItem> group, IMailItem mailItem)
{
UpdateUniqueIdHashes(mailItem, false);
2025-02-16 11:54:23 +01:00
MailItemRemoved?.Invoke(this, mailItem);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
group.Remove(mailItem);
2025-02-16 11:54:23 +01:00
if (group.Count == 0)
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
_mailItemSource.RemoveGroup(group.Key);
}
}
2025-02-26 19:59:20 +01:00
private async Task HandleThreadingAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
2025-02-16 11:54:23 +01:00
{
2025-02-26 19:59:20 +01:00
if (item is ThreadMailItemViewModel threadViewModel)
{
await HandleExistingThreadAsync(group, threadViewModel, addedItem);
}
else
{
await HandleNewThreadAsync(group, item, addedItem);
}
}
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
private async Task HandleExistingThreadAsync(ObservableGroup<object, IMailItem> group, ThreadMailItemViewModel threadViewModel, MailCopy addedItem)
{
var existingGroupKey = GetGroupingKey(threadViewModel);
feat: Enhanced sender avatars with gravatar and favicons integration (#685) * feat: Enhanced sender avatars with gravatar and favicons integration * chore: Remove unused known companies thumbnails * feat(thumbnail): add IThumbnailService and refactor usage - Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities. - Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container. - Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods. - Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables. - Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance. - Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`. * style: Show favicons as squares - Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display. - Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling. - Updated `UpdateInformation` method to manage favicon visibility. - Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images. - Enhanced XAML to include `FaviconSquircle` for improved UI appearance. * refactor thumbnail service * Removed old code and added clear method * added prefetch function * Change key from host to email * Remove redundant code * Test event * Fixed an issue with the thumbnail updated event. * Fix cutted favicons * exclude some domain from favicons * add yandex.ru * fix buttons in settings * remove prefetch method * Added thumbnails propagation to mailRenderingPage * Revert MailItemViewModel to object * Remove redundant code * spaces * await load parameter added * fix spaces * fix case sensativity for mail list thumbnails * change duckdns to google * Some cleanup. --------- Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com> Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00
2025-02-26 19:59:20 +01:00
await ExecuteUIThread(() => { threadViewModel.AddMailItemViewModel(addedItem); });
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
var newGroupKey = GetGroupingKey(threadViewModel);
feat: Enhanced sender avatars with gravatar and favicons integration (#685) * feat: Enhanced sender avatars with gravatar and favicons integration * chore: Remove unused known companies thumbnails * feat(thumbnail): add IThumbnailService and refactor usage - Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities. - Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container. - Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods. - Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables. - Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance. - Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`. * style: Show favicons as squares - Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display. - Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling. - Updated `UpdateInformation` method to manage favicon visibility. - Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images. - Enhanced XAML to include `FaviconSquircle` for improved UI appearance. * refactor thumbnail service * Removed old code and added clear method * added prefetch function * Change key from host to email * Remove redundant code * Test event * Fixed an issue with the thumbnail updated event. * Fix cutted favicons * exclude some domain from favicons * add yandex.ru * fix buttons in settings * remove prefetch method * Added thumbnails propagation to mailRenderingPage * Revert MailItemViewModel to object * Remove redundant code * spaces * await load parameter added * fix spaces * fix case sensativity for mail list thumbnails * change duckdns to google * Some cleanup. --------- Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com> Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00
2025-02-26 19:59:20 +01:00
if (!existingGroupKey.Equals(newGroupKey))
{
await MoveThreadToNewGroupAsync(group, threadViewModel, newGroupKey);
}
else
{
await ExecuteUIThread(() => { threadViewModel.NotifyPropertyChanges(); });
}
UpdateUniqueIdHashes(addedItem, true);
}
2025-02-26 19:59:20 +01:00
private async Task HandleNewThreadAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
{
if (item.Id == addedItem.Id)
2025-02-16 11:54:23 +01:00
{
2025-02-26 19:59:20 +01:00
await UpdateExistingItemAsync(item, addedItem);
}
else
{
await CreateNewThreadAsync(group, item, addedItem);
}
}
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
private async Task MoveThreadToNewGroupAsync(ObservableGroup<object, IMailItem> currentGroup, ThreadMailItemViewModel threadViewModel, object newGroupKey)
{
var mailThreadItems = threadViewModel.GetThreadMailItem();
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
await ExecuteUIThread(() =>
{
RemoveItemInternal(currentGroup, threadViewModel);
InsertItemInternal(newGroupKey, new ThreadMailItemViewModel(mailThreadItems));
});
}
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
private async Task CreateNewThreadAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
{
var threadMailItem = _threadingManager.CreateNewThread(item, addedItem);
var newGroupKey = GetGroupingKey(threadMailItem);
await ExecuteUIThread(() =>
{
RemoveItemInternal(group, item);
InsertItemInternal(newGroupKey, threadMailItem);
});
}
public async Task AddAsync(MailCopy addedItem)
{
foreach (var group in _mailItemSource)
{
foreach (var item in group)
{
if (_threadingManager.ShouldThread(addedItem, item))
2025-02-16 11:54:23 +01:00
{
2025-02-26 19:59:20 +01:00
await HandleThreadingAsync(group, item, addedItem);
return;
2025-02-16 11:54:23 +01:00
}
2025-02-26 19:59:20 +01:00
else if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
2025-02-16 11:54:23 +01:00
{
2025-02-26 19:59:20 +01:00
await UpdateExistingItemAsync(itemViewModel, addedItem);
return;
2024-04-18 01:44:37 +02:00
}
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
await AddNewItemAsync(addedItem);
}
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
private async Task AddNewItemAsync(MailCopy addedItem)
{
var groupKey = GetGroupingKey(addedItem);
await ExecuteUIThread(() => { InsertItemInternal(groupKey, addedItem); });
}
private async Task UpdateExistingItemAsync(IMailItem existingItem, MailCopy updatedItem)
{
if (existingItem is MailItemViewModel itemViewModel)
{
UpdateUniqueIdHashes(itemViewModel, false);
UpdateUniqueIdHashes(updatedItem, true);
2024-04-18 01:44:37 +02:00
2025-02-26 19:59:20 +01:00
await ExecuteUIThread(() => { itemViewModel.MailCopy = updatedItem; });
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void AddRange(IEnumerable<IMailItem> items, bool clearIdCache)
{
if (clearIdCache)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
MailCopyIdHashSet.Clear();
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var groupedByName = items
.GroupBy(a => GetGroupingKey(a))
.Select(a => new ObservableGroup<object, IMailItem>(a.Key, a));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
foreach (var group in groupedByName)
{
// Store all mail copy ids for faster access.
foreach (var item in group)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (item is MailItemViewModel mailCopyItem && !MailCopyIdHashSet.Contains(item.UniqueId))
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
MailCopyIdHashSet.Add(item.UniqueId);
}
else if (item is ThreadMailItemViewModel threadMailItem)
{
foreach (var mailItem in threadMailItem.ThreadItems)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
if (!MailCopyIdHashSet.Contains(mailItem.UniqueId))
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
MailCopyIdHashSet.Add(mailItem.UniqueId);
2024-04-18 01:44:37 +02:00
}
}
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var existingGroup = _mailItemSource.FirstGroupByKeyOrDefault(group.Key);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (existingGroup == null)
{
_mailItemSource.AddGroup(group.Key, group);
}
else
{
foreach (var item in group)
{
2025-02-16 11:54:23 +01:00
existingGroup.Add(item);
2024-04-18 01:44:37 +02:00
}
}
}
2025-02-16 11:54:23 +01:00
}
public MailItemContainer GetMailItemContainer(Guid uniqueMailId)
{
var groupCount = _mailItemSource.Count;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
for (int i = 0; i < groupCount; i++)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var group = _mailItemSource[i];
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
for (int k = 0; k < group.Count; k++)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var item = group[k];
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == uniqueMailId)
return new MailItemContainer(singleMailItemViewModel);
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(uniqueMailId))
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var singleItemViewModel = threadMailItemViewModel.GetItemById(uniqueMailId) as MailItemViewModel;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return new MailItemContainer(singleItemViewModel, threadMailItemViewModel);
2024-04-18 01:44:37 +02:00
}
}
2025-02-16 11:54:23 +01:00
}
return null;
}
2024-04-18 01:44:37 +02:00
feat: Enhanced sender avatars with gravatar and favicons integration (#685) * feat: Enhanced sender avatars with gravatar and favicons integration * chore: Remove unused known companies thumbnails * feat(thumbnail): add IThumbnailService and refactor usage - Introduced a new interface `IThumbnailService` for handling thumbnail-related functionalities. - Registered `IThumbnailService` with its implementation `ThumbnailService` in the service container. - Updated `NotificationBuilder` to use an instance of `IThumbnailService` instead of static methods. - Refactored `ThumbnailService` from a static class to a regular class with instance methods and variables. - Modified `ImagePreviewControl` to utilize the new `IThumbnailService` instance. - Completed integration of `IThumbnailService` in the application by registering it in `App.xaml.cs`. * style: Show favicons as squares - Changed `hintCrop` in `NotificationBuilder` to `None` for app logo display. - Added `FaviconSquircle`, `FaviconImage`, and `isFavicon` to `ImagePreviewControl` for favicon handling. - Updated `UpdateInformation` method to manage favicon visibility. - Introduced `GetBitmapImageAsync` for converting Base64 to Bitmap images. - Enhanced XAML to include `FaviconSquircle` for improved UI appearance. * refactor thumbnail service * Removed old code and added clear method * added prefetch function * Change key from host to email * Remove redundant code * Test event * Fixed an issue with the thumbnail updated event. * Fix cutted favicons * exclude some domain from favicons * add yandex.ru * fix buttons in settings * remove prefetch method * Added thumbnails propagation to mailRenderingPage * Revert MailItemViewModel to object * Remove redundant code * spaces * await load parameter added * fix spaces * fix case sensativity for mail list thumbnails * change duckdns to google * Some cleanup. --------- Co-authored-by: Aleh Khantsevich <aleh.khantsevich@gmail.com> Co-authored-by: Burak Kaan Köse <bkaankose@outlook.com>
2025-06-21 01:40:25 +02:00
public void UpdateThumbnails(string address)
{
if (CoreDispatcher == null) return;
CoreDispatcher.ExecuteOnUIThread(() =>
{
foreach (var group in _mailItemSource)
{
foreach (var item in group)
{
if (item is MailItemViewModel mailItemViewModel && mailItemViewModel.MailCopy.FromAddress.Equals(address, StringComparison.OrdinalIgnoreCase))
{
mailItemViewModel.ThumbnailUpdatedEvent = !mailItemViewModel.ThumbnailUpdatedEvent;
}
}
}
});
}
2025-02-16 11:54:23 +01:00
/// <summary>
/// Fins the item container that updated mail copy belongs to and updates it.
/// </summary>
/// <param name="updatedMailCopy">Updated mail copy.</param>
/// <returns></returns>
public async Task UpdateMailCopy(MailCopy updatedMailCopy)
{
// This item doesn't exist in the list.
if (!MailCopyIdHashSet.Contains(updatedMailCopy.UniqueId))
{
return;
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() =>
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (itemContainer == null) return;
if (itemContainer.ItemViewModel != null)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
if (itemContainer.ItemViewModel != null)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
itemContainer.ItemViewModel.MailCopy = updatedMailCopy;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
UpdateUniqueIdHashes(updatedMailCopy, true);
2025-02-16 11:54:23 +01:00
// Call thread notifications if possible.
itemContainer.ThreadViewModel?.NotifyPropertyChanges();
});
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public MailItemViewModel GetNextItem(MailCopy mailCopy)
{
try
2024-04-18 01:44:37 +02:00
{
var groupCount = _mailItemSource.Count;
2024-04-18 01:44:37 +02:00
for (int i = 0; i < groupCount; i++)
2024-04-18 01:44:37 +02:00
{
var group = _mailItemSource[i];
2024-04-18 01:44:37 +02:00
for (int k = 0; k < group.Count; k++)
2024-04-18 01:44:37 +02:00
{
var item = group[k];
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == mailCopy.UniqueId)
2025-02-16 11:54:23 +01:00
{
if (k + 1 < group.Count)
{
return group[k + 1] as MailItemViewModel;
}
else if (i + 1 < groupCount)
{
return _mailItemSource[i + 1][0] as MailItemViewModel;
}
else
{
return null;
}
2025-02-16 11:54:23 +01:00
}
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailCopy.UniqueId))
2024-04-18 01:44:37 +02:00
{
var singleItemViewModel = threadMailItemViewModel.GetItemById(mailCopy.UniqueId) as MailItemViewModel;
2024-04-18 01:44:37 +02:00
if (singleItemViewModel == null) return null;
2024-04-18 01:44:37 +02:00
var singleItemIndex = threadMailItemViewModel.ThreadItems.IndexOf(singleItemViewModel);
2024-04-18 01:44:37 +02:00
if (singleItemIndex + 1 < threadMailItemViewModel.ThreadItems.Count)
{
return threadMailItemViewModel.ThreadItems[singleItemIndex + 1] as MailItemViewModel;
}
else if (i + 1 < groupCount)
{
return _mailItemSource[i + 1][0] as MailItemViewModel;
}
else
{
return null;
}
2024-04-18 01:44:37 +02:00
}
}
}
return null;
}
catch (Exception ex)
{
Log.Warning(ex, "Failed to find the next item to select.");
}
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
return null;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public async Task RemoveAsync(MailCopy removeItem)
{
// This item doesn't exist in the list.
if (!MailCopyIdHashSet.Contains(removeItem.UniqueId)) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Check all items for whether this item should be threaded with them.
bool shouldExit = false;
2025-02-16 11:54:23 +01:00
var groupCount = _mailItemSource.Count;
for (int i = 0; i < groupCount; i++)
{
if (shouldExit) break;
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
var group = _mailItemSource[i];
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
for (int k = 0; k < group.Count; k++)
{
var item = group[k];
if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(removeItem.UniqueId))
{
2025-02-16 11:54:23 +01:00
var removalItem = threadMailItemViewModel.GetItemById(removeItem.UniqueId);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (removalItem == null) return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// Threads' Id is equal to the last item they hold.
// We can't do Id check here because that'd remove the whole thread.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/* Remove item from the thread.
* If thread had 1 item inside:
* -> Remove the thread and insert item as single item.
* If thread had 0 item inside:
* -> Remove the thread.
*/
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
var oldGroupKey = GetGroupingKey(threadMailItemViewModel);
await ExecuteUIThread(() => { threadMailItemViewModel.RemoveCopyItem(removalItem); });
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (threadMailItemViewModel.ThreadItems.Count == 1)
{
// Convert to single item.
2025-02-16 11:54:23 +01:00
var singleViewModel = threadMailItemViewModel.GetSingleItemViewModel();
var groupKey = GetGroupingKey(singleViewModel);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() =>
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
RemoveItemInternal(group, threadMailItemViewModel);
InsertItemInternal(groupKey, singleViewModel);
});
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
// If thread->single conversion is being done, we should ignore it for non-draft items.
// eg. Deleting a reply message from draft folder. Single non-draft item should not be re-added.
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (PruneSingleNonDraftItems && !singleViewModel.IsDraft)
{
// This item should not be here anymore.
// It's basically a reply mail in Draft folder.
var newGroup = _mailItemSource.FirstGroupByKeyOrDefault(groupKey);
2025-02-16 11:54:23 +01:00
if (newGroup != null)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { RemoveItemInternal(newGroup, singleViewModel); });
2024-04-18 01:44:37 +02:00
}
}
}
2025-02-16 11:54:23 +01:00
else if (threadMailItemViewModel.ThreadItems.Count == 0)
{
2025-02-16 11:54:23 +01:00
await ExecuteUIThread(() => { RemoveItemInternal(group, threadMailItemViewModel); });
}
else
{
// Item inside the thread is removed.
await ExecuteUIThread(() => { threadMailItemViewModel.ThreadItems.Remove(removalItem); });
2025-02-16 11:35:43 +01:00
2025-02-16 11:54:23 +01:00
UpdateUniqueIdHashes(removalItem, false);
}
2025-02-16 11:54:23 +01:00
shouldExit = true;
break;
}
else if (item.UniqueId == removeItem.UniqueId)
{
await ExecuteUIThread(() => { RemoveItemInternal(group, item); });
shouldExit = true;
break;
2024-04-18 01:44:37 +02:00
}
}
}
}
2025-02-16 11:54:23 +01:00
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
2024-04-18 01:44:37 +02:00
}