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;
|
2025-02-23 17:05:46 +01:00
|
|
|
|
using Serilog;
|
2024-11-10 23:28:25 +01:00
|
|
|
|
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 = [];
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
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-16 11:54:23 +01:00
|
|
|
|
public WinoMailCollection()
|
|
|
|
|
|
{
|
|
|
|
|
|
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:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
private void UpdateUniqueIdHashes(IMailHashContainer itemContainer, bool isAdd)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var item in itemContainer.GetContainingIds())
|
2024-07-15 00:00:38 +02:00
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (isAdd)
|
2024-07-15 00:00:38 +02:00
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
MailCopyIdHashSet.Add(item);
|
2024-07-15 00:00:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
MailCopyIdHashSet.Remove(item);
|
2024-07-15 00:00:38 +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
|
|
|
|
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);
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
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);
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
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-16 11:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
public async Task AddAsync(MailCopy addedItem)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check all items for whether this item should be threaded with them.
|
|
|
|
|
|
bool shouldExit = false;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var groupCount = _mailItemSource.Count;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var addedAccountProviderType = addedItem.AssignedAccount.ProviderType;
|
|
|
|
|
|
var threadingStrategy = ThreadingStrategyProvider?.GetStrategy(addedAccountProviderType);
|
2025-02-16 11:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
for (int i = 0; i < groupCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (shouldExit) break;
|
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++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var item = group[k];
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false)
|
|
|
|
|
|
{
|
|
|
|
|
|
shouldExit = true;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Item belongs to existing thread.
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
/* Add original item to the thread.
|
|
|
|
|
|
* If new group key is not the same as existing thread:
|
|
|
|
|
|
* -> Remove the whole thread from list
|
|
|
|
|
|
* -> Add the thread to the list again for sorting.
|
|
|
|
|
|
* Update thread properties.
|
|
|
|
|
|
*/
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var existingGroupKey = GetGroupingKey(threadMailItemViewModel);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() => { threadMailItemViewModel.AddMailItemViewModel(addedItem); });
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var newGroupKey = GetGroupingKey(threadMailItemViewModel);
|
2024-05-21 23:48:44 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (!existingGroupKey.Equals(newGroupKey))
|
|
|
|
|
|
{
|
|
|
|
|
|
var mailThreadItems = threadMailItemViewModel.GetThreadMailItem();
|
2025-02-16 11:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() =>
|
2025-02-16 11:43:30 +01:00
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
// Group must be changed for this thread.
|
|
|
|
|
|
// Remove the thread first.
|
2025-02-16 11:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
RemoveItemInternal(group, threadMailItemViewModel);
|
2025-02-16 11:43:30 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
// Insert new view model because the previous one might've been deleted with the group.
|
|
|
|
|
|
InsertItemInternal(newGroupKey, new ThreadMailItemViewModel(mailThreadItems));
|
|
|
|
|
|
});
|
2024-04-18 01:44:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
|
|
|
|
|
}
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
UpdateUniqueIdHashes(addedItem, true);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Item belongs to a single mail item that is not threaded yet.
|
|
|
|
|
|
// Same item might've been tried to added as well.
|
|
|
|
|
|
// In that case we must just update the item but not thread it.
|
2024-05-21 23:48:44 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
/* Remove target item.
|
|
|
|
|
|
* Create a new thread with both items.
|
|
|
|
|
|
* Add new thread to the list.
|
|
|
|
|
|
*/
|
2025-02-16 11:35:43 +01:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (item.Id == addedItem.Id)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Item is already added to the list.
|
|
|
|
|
|
// We need to update the copy it holds.
|
|
|
|
|
|
|
|
|
|
|
|
if (item is MailItemViewModel itemViewModel)
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
UpdateUniqueIdHashes(itemViewModel, false);
|
|
|
|
|
|
UpdateUniqueIdHashes(addedItem, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Single item that must be threaded together with added item.
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var threadMailItem = new ThreadMailItem();
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
threadMailItem.AddThreadItem(item);
|
|
|
|
|
|
threadMailItem.AddThreadItem(addedItem);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
if (threadMailItem.ThreadItems.Count == 1) return;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var newGroupKey = GetGroupingKey(threadMailItem);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
RemoveItemInternal(group, item);
|
|
|
|
|
|
InsertItemInternal(newGroupKey, threadMailItem);
|
|
|
|
|
|
});
|
2024-04-18 01:44:37 +02:00
|
|
|
|
}
|
2025-02-16 11:54:23 +01:00
|
|
|
|
|
|
|
|
|
|
break;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
}
|
2025-02-16 11:54:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Update properties.
|
|
|
|
|
|
if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-16 11:54:23 +01:00
|
|
|
|
UpdateUniqueIdHashes(itemViewModel, false);
|
|
|
|
|
|
UpdateUniqueIdHashes(addedItem, true);
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
shouldExit = true;
|
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
|
|
|
|
if (!shouldExit)
|
|
|
|
|
|
{
|
|
|
|
|
|
// At this point all items are already checked and not suitable option was available.
|
|
|
|
|
|
// Item doesn't belong to any thread.
|
|
|
|
|
|
// Just add it to the collection.
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
var groupKey = GetGroupingKey(addedItem);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
|
await ExecuteUIThread(() => { InsertItemInternal(groupKey, addedItem); });
|
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:43:30 +01:00
|
|
|
|
{
|
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
|
|
|
|
|
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:43:30 +01:00
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-02-23 17:05:46 +01:00
|
|
|
|
try
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-23 17:05:46 +01:00
|
|
|
|
var groupCount = _mailItemSource.Count;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-23 17:05:46 +01:00
|
|
|
|
for (int i = 0; i < groupCount; i++)
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-23 17:05:46 +01:00
|
|
|
|
var group = _mailItemSource[i];
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-23 17:05:46 +01:00
|
|
|
|
for (int k = 0; k < group.Count; k++)
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-23 17:05:46 +01:00
|
|
|
|
var item = group[k];
|
|
|
|
|
|
|
|
|
|
|
|
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == mailCopy.UniqueId)
|
2025-02-16 11:54:23 +01:00
|
|
|
|
{
|
2025-02-23 17:05:46 +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
|
|
|
|
}
|
2025-02-23 17:05:46 +01:00
|
|
|
|
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailCopy.UniqueId))
|
2024-04-18 01:44:37 +02:00
|
|
|
|
{
|
2025-02-23 17:05:46 +01:00
|
|
|
|
var singleItemViewModel = threadMailItemViewModel.GetItemById(mailCopy.UniqueId) as MailItemViewModel;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-23 17:05:46 +01:00
|
|
|
|
if (singleItemViewModel == null) return null;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-23 17:05:46 +01:00
|
|
|
|
var singleItemIndex = threadMailItemViewModel.ThreadItems.IndexOf(singleItemViewModel);
|
2024-04-18 01:44:37 +02:00
|
|
|
|
|
2025-02-23 17:05:46 +01: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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-02-23 17:05:46 +01:00
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log.Warning(ex, "Failed to find the next item to select.");
|
2025-02-16 11:43:30 +01:00
|
|
|
|
}
|
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:43:30 +01:00
|
|
|
|
|
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:43:30 +01:00
|
|
|
|
{
|
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.
|
2024-07-15 00:00:38 +02:00
|
|
|
|
|
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:43:30 +01:00
|
|
|
|
|
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:43:30 +01:00
|
|
|
|
}
|
2025-02-16 11:54:23 +01:00
|
|
|
|
else if (threadMailItemViewModel.ThreadItems.Count == 0)
|
2025-02-16 11:43:30 +01:00
|
|
|
|
{
|
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:43:30 +01:00
|
|
|
|
}
|
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:43:30 +01: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
|
|
|
|
}
|