Refactoring mail collection class.
This commit is contained in:
31
Wino.Mail.ViewModels/Collections/ThreadingManager.cs
Normal file
31
Wino.Mail.ViewModels/Collections/ThreadingManager.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
|
|
||||||
|
namespace Wino.Mail.ViewModels.Collections;
|
||||||
|
|
||||||
|
internal class ThreadingManager
|
||||||
|
{
|
||||||
|
private readonly IThreadingStrategyProvider _threadingStrategyProvider;
|
||||||
|
|
||||||
|
public ThreadingManager(IThreadingStrategyProvider threadingStrategyProvider)
|
||||||
|
{
|
||||||
|
_threadingStrategyProvider = threadingStrategyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldThread(MailCopy newItem, IMailItem existingItem)
|
||||||
|
{
|
||||||
|
if (_threadingStrategyProvider == null) return false;
|
||||||
|
|
||||||
|
var strategy = _threadingStrategyProvider.GetStrategy(newItem.AssignedAccount.ProviderType);
|
||||||
|
return strategy?.ShouldThreadWithItem(newItem, existingItem) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThreadMailItem CreateNewThread(IMailItem existingItem, MailCopy newItem)
|
||||||
|
{
|
||||||
|
var thread = new ThreadMailItem();
|
||||||
|
thread.AddThreadItem(existingItem);
|
||||||
|
thread.AddThreadItem(newItem);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,8 +49,11 @@ public class WinoMailCollection
|
|||||||
|
|
||||||
public IDispatcher CoreDispatcher { get; set; }
|
public IDispatcher CoreDispatcher { get; set; }
|
||||||
|
|
||||||
public WinoMailCollection()
|
private readonly ThreadingManager _threadingManager;
|
||||||
|
|
||||||
|
public WinoMailCollection(IThreadingStrategyProvider threadingStrategyProvider)
|
||||||
{
|
{
|
||||||
|
_threadingManager = new ThreadingManager(threadingStrategyProvider);
|
||||||
MailItems = new ReadOnlyObservableGroupedCollection<object, IMailItem>(_mailItemSource);
|
MailItems = new ReadOnlyObservableGroupedCollection<object, IMailItem>(_mailItemSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,143 +114,109 @@ public class WinoMailCollection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddAsync(MailCopy addedItem)
|
private async Task HandleThreadingAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
|
||||||
{
|
{
|
||||||
// Check all items for whether this item should be threaded with them.
|
if (item is ThreadMailItemViewModel threadViewModel)
|
||||||
bool shouldExit = false;
|
|
||||||
|
|
||||||
var groupCount = _mailItemSource.Count;
|
|
||||||
|
|
||||||
var addedAccountProviderType = addedItem.AssignedAccount.ProviderType;
|
|
||||||
var threadingStrategy = ThreadingStrategyProvider?.GetStrategy(addedAccountProviderType);
|
|
||||||
|
|
||||||
for (int i = 0; i < groupCount; i++)
|
|
||||||
{
|
{
|
||||||
if (shouldExit) break;
|
await HandleExistingThreadAsync(group, threadViewModel, addedItem);
|
||||||
|
}
|
||||||
var group = _mailItemSource[i];
|
else
|
||||||
|
|
||||||
for (int k = 0; k < group.Count; k++)
|
|
||||||
{
|
{
|
||||||
var item = group[k];
|
await HandleNewThreadAsync(group, item, addedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false)
|
private async Task HandleExistingThreadAsync(ObservableGroup<object, IMailItem> group, ThreadMailItemViewModel threadViewModel, MailCopy addedItem)
|
||||||
{
|
{
|
||||||
shouldExit = true;
|
var existingGroupKey = GetGroupingKey(threadViewModel);
|
||||||
|
|
||||||
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
await ExecuteUIThread(() => { threadViewModel.AddMailItemViewModel(addedItem); });
|
||||||
{
|
|
||||||
// Item belongs to existing thread.
|
|
||||||
|
|
||||||
/* Add original item to the thread.
|
var newGroupKey = GetGroupingKey(threadViewModel);
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var existingGroupKey = GetGroupingKey(threadMailItemViewModel);
|
|
||||||
|
|
||||||
await ExecuteUIThread(() => { threadMailItemViewModel.AddMailItemViewModel(addedItem); });
|
|
||||||
|
|
||||||
var newGroupKey = GetGroupingKey(threadMailItemViewModel);
|
|
||||||
|
|
||||||
if (!existingGroupKey.Equals(newGroupKey))
|
if (!existingGroupKey.Equals(newGroupKey))
|
||||||
{
|
{
|
||||||
var mailThreadItems = threadMailItemViewModel.GetThreadMailItem();
|
await MoveThreadToNewGroupAsync(group, threadViewModel, newGroupKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ExecuteUIThread(() => { threadViewModel.NotifyPropertyChanges(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateUniqueIdHashes(addedItem, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleNewThreadAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
|
||||||
|
{
|
||||||
|
if (item.Id == addedItem.Id)
|
||||||
|
{
|
||||||
|
await UpdateExistingItemAsync(item, addedItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await CreateNewThreadAsync(group, item, addedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MoveThreadToNewGroupAsync(ObservableGroup<object, IMailItem> currentGroup, ThreadMailItemViewModel threadViewModel, object newGroupKey)
|
||||||
|
{
|
||||||
|
var mailThreadItems = threadViewModel.GetThreadMailItem();
|
||||||
|
|
||||||
await ExecuteUIThread(() =>
|
await ExecuteUIThread(() =>
|
||||||
{
|
{
|
||||||
// Group must be changed for this thread.
|
RemoveItemInternal(currentGroup, threadViewModel);
|
||||||
// Remove the thread first.
|
|
||||||
|
|
||||||
RemoveItemInternal(group, threadMailItemViewModel);
|
|
||||||
|
|
||||||
// Insert new view model because the previous one might've been deleted with the group.
|
|
||||||
InsertItemInternal(newGroupKey, new ThreadMailItemViewModel(mailThreadItems));
|
InsertItemInternal(newGroupKey, new ThreadMailItemViewModel(mailThreadItems));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
private async Task CreateNewThreadAsync(ObservableGroup<object, IMailItem> group, IMailItem item, MailCopy addedItem)
|
||||||
{
|
{
|
||||||
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
var threadMailItem = _threadingManager.CreateNewThread(item, addedItem);
|
||||||
}
|
var newGroupKey = GetGroupingKey(threadMailItem);
|
||||||
|
|
||||||
UpdateUniqueIdHashes(addedItem, true);
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
/* Remove target item.
|
|
||||||
* Create a new thread with both items.
|
|
||||||
* Add new thread to the list.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
|
||||||
|
|
||||||
UpdateUniqueIdHashes(itemViewModel, false);
|
|
||||||
UpdateUniqueIdHashes(addedItem, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Single item that must be threaded together with added item.
|
|
||||||
|
|
||||||
var threadMailItem = new ThreadMailItem();
|
|
||||||
|
|
||||||
await ExecuteUIThread(() =>
|
await ExecuteUIThread(() =>
|
||||||
{
|
{
|
||||||
threadMailItem.AddThreadItem(item);
|
|
||||||
threadMailItem.AddThreadItem(addedItem);
|
|
||||||
|
|
||||||
if (threadMailItem.ThreadItems.Count == 1) return;
|
|
||||||
|
|
||||||
var newGroupKey = GetGroupingKey(threadMailItem);
|
|
||||||
|
|
||||||
RemoveItemInternal(group, item);
|
RemoveItemInternal(group, item);
|
||||||
InsertItemInternal(newGroupKey, threadMailItem);
|
InsertItemInternal(newGroupKey, threadMailItem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
public async Task AddAsync(MailCopy addedItem)
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Update properties.
|
foreach (var group in _mailItemSource)
|
||||||
if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
{
|
||||||
|
foreach (var item in group)
|
||||||
|
{
|
||||||
|
if (_threadingManager.ShouldThread(addedItem, item))
|
||||||
|
{
|
||||||
|
await HandleThreadingAsync(group, item, addedItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
||||||
|
{
|
||||||
|
await UpdateExistingItemAsync(itemViewModel, addedItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await AddNewItemAsync(addedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(itemViewModel, false);
|
||||||
UpdateUniqueIdHashes(addedItem, true);
|
UpdateUniqueIdHashes(updatedItem, true);
|
||||||
|
|
||||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
await ExecuteUIThread(() => { itemViewModel.MailCopy = updatedItem; });
|
||||||
|
|
||||||
shouldExit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
var groupKey = GetGroupingKey(addedItem);
|
|
||||||
|
|
||||||
await ExecuteUIThread(() => { InsertItemInternal(groupKey, addedItem); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null;
|
private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null;
|
||||||
|
|
||||||
public WinoMailCollection MailCollection { get; } = new WinoMailCollection();
|
public WinoMailCollection MailCollection { get; }
|
||||||
|
|
||||||
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = [];
|
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = [];
|
||||||
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = [];
|
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = [];
|
||||||
@@ -163,6 +163,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
IWinoLogger winoLogger,
|
IWinoLogger winoLogger,
|
||||||
IWinoServerConnectionManager winoServerConnectionManager)
|
IWinoServerConnectionManager winoServerConnectionManager)
|
||||||
{
|
{
|
||||||
|
MailCollection = new WinoMailCollection(threadingStrategyProvider);
|
||||||
PreferencesService = preferencesService;
|
PreferencesService = preferencesService;
|
||||||
ThemeService = themeService;
|
ThemeService = themeService;
|
||||||
_winoLogger = winoLogger;
|
_winoLogger = winoLogger;
|
||||||
|
|||||||
Reference in New Issue
Block a user