merge communication branch
This commit is contained in:
@@ -12,7 +12,7 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
private readonly IStoreRatingService _storeRatingService;
|
||||
private readonly INativeAppService _nativeAppService;
|
||||
private readonly IAppInitializerService _appInitializerService;
|
||||
private readonly IApplicationConfiguration _appInitializerService;
|
||||
private readonly IFileService _fileService;
|
||||
private readonly ILogInitializer _logInitializer;
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Wino.Mail.ViewModels
|
||||
IDialogService dialogService,
|
||||
INativeAppService nativeAppService,
|
||||
IPreferencesService preferencesService,
|
||||
IAppInitializerService appInitializerService,
|
||||
IApplicationConfiguration appInitializerService,
|
||||
IFileService fileService,
|
||||
ILogInitializer logInitializer) : base(dialogService)
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
private async Task SaveLogInternalAsync(string sourceFileName)
|
||||
{
|
||||
var appDataFolder = _appInitializerService.GetApplicationDataFolder();
|
||||
var appDataFolder = _appInitializerService.ApplicationDataFolderPath;
|
||||
|
||||
var logFile = Path.Combine(appDataFolder, sourceFileName);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
@@ -13,13 +12,12 @@ using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
{
|
||||
public partial class AccountDetailsPageViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IWinoSynchronizerFactory _synchronizerFactory;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IFolderService _folderService;
|
||||
|
||||
@@ -45,11 +43,9 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
|
||||
public AccountDetailsPageViewModel(IDialogService dialogService,
|
||||
IWinoSynchronizerFactory synchronizerFactory,
|
||||
IAccountService accountService,
|
||||
IFolderService folderService) : base(dialogService)
|
||||
{
|
||||
_synchronizerFactory = synchronizerFactory;
|
||||
_accountService = accountService;
|
||||
_folderService = folderService;
|
||||
}
|
||||
@@ -99,10 +95,7 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
await _accountService.DeleteAccountAsync(Account);
|
||||
|
||||
_synchronizerFactory.DeleteSynchronizer(Account);
|
||||
|
||||
// TODO: Clear existing requests.
|
||||
// _synchronizationWorker.ClearRequests(Account.Id);
|
||||
// TODO: Server: Cancel ongoing calls from server for this account.
|
||||
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success);
|
||||
|
||||
|
||||
@@ -16,11 +16,10 @@ using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Store;
|
||||
using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Core.Messages.Authorization;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
{
|
||||
@@ -35,7 +34,6 @@ namespace Wino.Mail.ViewModels
|
||||
private readonly IStoreManagementService _storeManagementService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly IAuthenticationProvider _authenticationProvider;
|
||||
private readonly IWinoSynchronizerFactory _synchronizerFactory;
|
||||
|
||||
public ObservableCollection<IAccountProviderDetailViewModel> Accounts { get; set; } = [];
|
||||
|
||||
@@ -60,7 +58,6 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
public AccountManagementViewModel(IDialogService dialogService,
|
||||
IWinoNavigationService navigationService,
|
||||
IWinoSynchronizerFactory synchronizerFactory,
|
||||
IAccountService accountService,
|
||||
IProviderService providerService,
|
||||
IFolderService folderService,
|
||||
@@ -69,7 +66,6 @@ namespace Wino.Mail.ViewModels
|
||||
IAuthenticationProvider authenticationProvider) : base(dialogService)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_synchronizerFactory = synchronizerFactory;
|
||||
_dialogService = dialogService;
|
||||
_providerService = providerService;
|
||||
_folderService = folderService;
|
||||
@@ -205,29 +201,31 @@ namespace Wino.Mail.ViewModels
|
||||
// Local account has been created.
|
||||
// Create new synchronizer and start synchronization.
|
||||
|
||||
var synchronizer = _synchronizerFactory.CreateNewSynchronizer(createdAccount);
|
||||
// TODO: Server: Make sure that server synchronizes folders and sends back the result.
|
||||
|
||||
if (creationDialog is ICustomServerAccountCreationDialog customServerAccountCreationDialog)
|
||||
customServerAccountCreationDialog.ShowPreparingFolders();
|
||||
else
|
||||
creationDialog.State = AccountCreationDialogState.PreparingFolders;
|
||||
//var synchronizer = _synchronizerFactory.CreateNewSynchronizer(createdAccount);
|
||||
|
||||
var options = new SynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = SynchronizationType.FoldersOnly
|
||||
};
|
||||
//if (creationDialog is ICustomServerAccountCreationDialog customServerAccountCreationDialog)
|
||||
// customServerAccountCreationDialog.ShowPreparingFolders();
|
||||
//else
|
||||
// creationDialog.State = AccountCreationDialogState.PreparingFolders;
|
||||
|
||||
var synchronizationResult = await synchronizer.SynchronizeAsync(options);
|
||||
//var options = new SynchronizationOptions()
|
||||
//{
|
||||
// AccountId = createdAccount.Id,
|
||||
// Type = SynchronizationType.FoldersOnly
|
||||
//};
|
||||
|
||||
if (synchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||
//var synchronizationResult = await synchronizer.SynchronizeAsync(options);
|
||||
|
||||
// Check if Inbox folder is available for the account after synchronization.
|
||||
var isInboxAvailable = await _folderService.IsInboxAvailableForAccountAsync(createdAccount.Id);
|
||||
//if (synchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
// throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||
|
||||
if (!isInboxAvailable)
|
||||
throw new Exception(Translator.Exception_InboxNotAvailable);
|
||||
//// Check if Inbox folder is available for the account after synchronization.
|
||||
//var isInboxAvailable = await _folderService.IsInboxAvailableForAccountAsync(createdAccount.Id);
|
||||
|
||||
//if (!isInboxAvailable)
|
||||
// throw new Exception(Translator.Exception_InboxNotAvailable);
|
||||
|
||||
// Send changes to listeners.
|
||||
ReportUIChange(new AccountCreatedMessage(createdAccount));
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using MoreLinq;
|
||||
@@ -25,8 +26,8 @@ using Wino.Core.Messages.Mails;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Core.Messages.Shell;
|
||||
using Wino.Core.Messages.Synchronization;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Core.Services;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
{
|
||||
@@ -62,6 +63,7 @@ namespace Wino.Mail.ViewModels
|
||||
#endregion
|
||||
|
||||
public IStatePersistanceService StatePersistenceService { get; }
|
||||
public IWinoServerConnectionManager ServerConnectionManager { get; }
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
public IWinoNavigationService NavigationService { get; }
|
||||
|
||||
@@ -73,7 +75,6 @@ namespace Wino.Mail.ViewModels
|
||||
private readonly INotificationBuilder _notificationBuilder;
|
||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||
|
||||
private readonly IWinoSynchronizerFactory _synchronizerFactory;
|
||||
private readonly IBackgroundTaskService _backgroundTaskService;
|
||||
private readonly IMimeFileService _mimeFileService;
|
||||
|
||||
@@ -82,9 +83,11 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
private readonly SemaphoreSlim accountInitFolderUpdateSlim = new SemaphoreSlim(1);
|
||||
|
||||
[ObservableProperty]
|
||||
private string _activeConnectionStatus = WinoServerConnectionStatus.None.ToString();
|
||||
|
||||
public AppShellViewModel(IDialogService dialogService,
|
||||
IWinoNavigationService navigationService,
|
||||
IWinoSynchronizerFactory synchronizerFactory,
|
||||
IBackgroundTaskService backgroundTaskService,
|
||||
IMimeFileService mimeFileService,
|
||||
INativeAppService nativeAppService,
|
||||
@@ -97,13 +100,23 @@ namespace Wino.Mail.ViewModels
|
||||
INotificationBuilder notificationBuilder,
|
||||
IWinoRequestDelegator winoRequestDelegator,
|
||||
IFolderService folderService,
|
||||
IStatePersistanceService statePersistanceService) : base(dialogService)
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IWinoServerConnectionManager serverConnectionManager) : base(dialogService)
|
||||
{
|
||||
StatePersistenceService = statePersistanceService;
|
||||
ServerConnectionManager = serverConnectionManager;
|
||||
|
||||
ServerConnectionManager.StatusChanged += async (sender, status) =>
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
ActiveConnectionStatus = status.ToString();
|
||||
});
|
||||
};
|
||||
|
||||
PreferencesService = preferencesService;
|
||||
NavigationService = navigationService;
|
||||
|
||||
_synchronizerFactory = synchronizerFactory;
|
||||
_backgroundTaskService = backgroundTaskService;
|
||||
_mimeFileService = mimeFileService;
|
||||
_nativeAppService = nativeAppService;
|
||||
@@ -117,6 +130,9 @@ namespace Wino.Mail.ViewModels
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task ReconnectServerAsync() => ServerConnectionManager.ConnectAsync();
|
||||
|
||||
protected override void OnDispatcherAssigned()
|
||||
{
|
||||
base.OnDispatcherAssigned();
|
||||
@@ -776,65 +792,65 @@ namespace Wino.Mail.ViewModels
|
||||
await _winoRequestDelegator.ExecuteAsync(draftPreperationRequest);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async void Receive(NewSynchronizationRequested message)
|
||||
{
|
||||
// TODO: Queue new synchronization for an account.
|
||||
|
||||
// Don't send message for sync completion when we execute requests.
|
||||
// People are usually interested in seeing the notification after they trigger the synchronization.
|
||||
|
||||
bool shouldReportSynchronizationResult = message.Options.Type != SynchronizationType.ExecuteRequests;
|
||||
//bool shouldReportSynchronizationResult = message.Options.Type != SynchronizationType.ExecuteRequests;
|
||||
|
||||
var synchronizer = _synchronizerFactory.GetAccountSynchronizer(message.Options.AccountId);
|
||||
//var synchronizer = _synchronizerFactory.GetAccountSynchronizer(message.Options.AccountId);
|
||||
|
||||
if (synchronizer == null) return;
|
||||
//if (synchronizer == null) return;
|
||||
|
||||
var accountId = message.Options.AccountId;
|
||||
//var accountId = message.Options.AccountId;
|
||||
|
||||
message.Options.ProgressListener = this;
|
||||
//message.Options.ProgressListener = this;
|
||||
|
||||
bool isSynchronizationSucceeded = false;
|
||||
//bool isSynchronizationSucceeded = false;
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Cancellation Token
|
||||
var synchronizationResult = await synchronizer.SynchronizeAsync(message.Options);
|
||||
//try
|
||||
//{
|
||||
// // TODO: Cancellation Token
|
||||
// var synchronizationResult = await synchronizer.SynchronizeAsync(message.Options);
|
||||
|
||||
isSynchronizationSucceeded = synchronizationResult.CompletedState == SynchronizationCompletedState.Success;
|
||||
// isSynchronizationSucceeded = synchronizationResult.CompletedState == SynchronizationCompletedState.Success;
|
||||
|
||||
// Create notification for synchronization result.
|
||||
if (synchronizationResult.DownloadedMessages.Any())
|
||||
{
|
||||
var accountInboxFolder = await _folderService.GetSpecialFolderByAccountIdAsync(message.Options.AccountId, SpecialFolderType.Inbox);
|
||||
// // Create notification for synchronization result.
|
||||
// if (synchronizationResult.DownloadedMessages.Any())
|
||||
// {
|
||||
// var accountInboxFolder = await _folderService.GetSpecialFolderByAccountIdAsync(message.Options.AccountId, SpecialFolderType.Inbox);
|
||||
|
||||
if (accountInboxFolder == null) return;
|
||||
// if (accountInboxFolder == null) return;
|
||||
|
||||
await _notificationBuilder.CreateNotificationsAsync(accountInboxFolder.Id, synchronizationResult.DownloadedMessages);
|
||||
}
|
||||
}
|
||||
catch (AuthenticationAttentionException)
|
||||
{
|
||||
await SetAccountAttentionAsync(accountId, AccountAttentionReason.InvalidCredentials);
|
||||
}
|
||||
catch (SystemFolderConfigurationMissingException)
|
||||
{
|
||||
await SetAccountAttentionAsync(accountId, AccountAttentionReason.MissingSystemFolderConfiguration);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.Info_SyncCanceledMessage, Translator.Info_SyncCanceledMessage, InfoBarMessageType.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, ex.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (shouldReportSynchronizationResult)
|
||||
Messenger.Send(new AccountSynchronizationCompleted(accountId,
|
||||
isSynchronizationSucceeded ? SynchronizationCompletedState.Success : SynchronizationCompletedState.Failed,
|
||||
message.Options.GroupedSynchronizationTrackingId));
|
||||
}
|
||||
// await _notificationBuilder.CreateNotificationsAsync(accountInboxFolder.Id, synchronizationResult.DownloadedMessages);
|
||||
// }
|
||||
//}
|
||||
//catch (AuthenticationAttentionException)
|
||||
//{
|
||||
// await SetAccountAttentionAsync(accountId, AccountAttentionReason.InvalidCredentials);
|
||||
//}
|
||||
//catch (SystemFolderConfigurationMissingException)
|
||||
//{
|
||||
// await SetAccountAttentionAsync(accountId, AccountAttentionReason.MissingSystemFolderConfiguration);
|
||||
//}
|
||||
//catch (OperationCanceledException)
|
||||
//{
|
||||
// DialogService.InfoBarMessage(Translator.Info_SyncCanceledMessage, Translator.Info_SyncCanceledMessage, InfoBarMessageType.Warning);
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// DialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, ex.Message, InfoBarMessageType.Error);
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// if (shouldReportSynchronizationResult)
|
||||
// Messenger.Send(new AccountSynchronizationCompleted(accountId,
|
||||
// isSynchronizationSucceeded ? SynchronizationCompletedState.Success : SynchronizationCompletedState.Failed,
|
||||
// message.Options.GroupedSynchronizationTrackingId));
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
{
|
||||
@@ -72,9 +71,7 @@ namespace Wino.Mail.ViewModels
|
||||
protected virtual void OnFolderRenamed(IMailItemFolder mailItemFolder) { }
|
||||
protected virtual void OnFolderSynchronizationEnabled(IMailItemFolder mailItemFolder) { }
|
||||
|
||||
public void ReportUIChange<TMessage>(TMessage message) where TMessage : class, IUIMessage
|
||||
=> Messenger.Send(message);
|
||||
|
||||
public void ReportUIChange<TMessage>(TMessage message) where TMessage : class, IServerMessage => Messenger.Send(message);
|
||||
void IRecipient<AccountCreatedMessage>.Receive(AccountCreatedMessage message) => OnAccountCreated(message.Account);
|
||||
void IRecipient<AccountRemovedMessage>.Receive(AccountRemovedMessage message) => OnAccountRemoved(message.Account);
|
||||
void IRecipient<AccountUpdatedMessage>.Receive(AccountUpdatedMessage message) => OnAccountUpdated(message.Account);
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
// If the item provider here for update or removal doesn't exist here
|
||||
// we can ignore the operation.
|
||||
|
||||
public HashSet<Guid> MailCopyIdHashSet = new HashSet<Guid>();
|
||||
public HashSet<Guid> MailCopyIdHashSet = [];
|
||||
|
||||
public event EventHandler<IMailItem> MailItemRemoved;
|
||||
|
||||
private ListItemComparer listComparer = new ListItemComparer();
|
||||
|
||||
@@ -61,45 +63,51 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
return mailItem.FromName;
|
||||
}
|
||||
|
||||
private async Task InsertItemInternalAsync(object groupKey, IMailItem mailItem)
|
||||
=> await ExecuteUIThread(() =>
|
||||
{
|
||||
if (mailItem is MailCopy mailCopy)
|
||||
{
|
||||
MailCopyIdHashSet.Add(mailCopy.UniqueId);
|
||||
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, new MailItemViewModel(mailCopy), listComparer.GetItemComparer());
|
||||
}
|
||||
else if (mailItem is ThreadMailItem threadMailItem)
|
||||
{
|
||||
foreach (var item in threadMailItem.ThreadItems)
|
||||
{
|
||||
MailCopyIdHashSet.Add(item.UniqueId);
|
||||
}
|
||||
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, new ThreadMailItemViewModel(threadMailItem), listComparer.GetItemComparer());
|
||||
}
|
||||
else if (mailItem is MailItemViewModel)
|
||||
{
|
||||
MailCopyIdHashSet.Add(mailItem.UniqueId);
|
||||
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer());
|
||||
}
|
||||
});
|
||||
|
||||
private async Task RemoveItemInternalAsync(ObservableGroup<object, IMailItem> group, IMailItem mailItem)
|
||||
private void UpdateUniqueIdHashes(IMailHashContainer itemContainer, bool isAdd)
|
||||
{
|
||||
MailCopyIdHashSet.Remove(mailItem.UniqueId);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
foreach (var item in itemContainer.GetContainingIds())
|
||||
{
|
||||
group.Remove(mailItem);
|
||||
|
||||
if (group.Count == 0)
|
||||
if (isAdd)
|
||||
{
|
||||
_mailItemSource.RemoveGroup(group.Key);
|
||||
MailCopyIdHashSet.Add(item);
|
||||
}
|
||||
});
|
||||
else
|
||||
{
|
||||
MailCopyIdHashSet.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, new ThreadMailItemViewModel(threadMailItem), listComparer.GetItemComparer());
|
||||
}
|
||||
else
|
||||
{
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer());
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveItemInternal(ObservableGroup<object, IMailItem> group, IMailItem mailItem)
|
||||
{
|
||||
UpdateUniqueIdHashes(mailItem, false);
|
||||
|
||||
MailItemRemoved?.Invoke(this, mailItem);
|
||||
|
||||
group.Remove(mailItem);
|
||||
|
||||
if (group.Count == 0)
|
||||
{
|
||||
_mailItemSource.RemoveGroup(group.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddAsync(MailCopy addedItem)
|
||||
@@ -109,6 +117,9 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
var addedAccountProviderType = addedItem.AssignedAccount.ProviderType;
|
||||
var threadingStrategy = ThreadingStrategyProvider?.GetStrategy(addedAccountProviderType);
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
if (shouldExit) break;
|
||||
@@ -119,10 +130,6 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
var addedAccountProviderType = addedItem.AssignedAccount.ProviderType;
|
||||
|
||||
var threadingStrategy = ThreadingStrategyProvider?.GetStrategy(addedAccountProviderType);
|
||||
|
||||
if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false)
|
||||
{
|
||||
shouldExit = true;
|
||||
@@ -140,23 +147,32 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
var existingGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
threadMailItemViewModel.AddMailItemViewModel(addedItem);
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.AddMailItemViewModel(addedItem); });
|
||||
|
||||
var newGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
if (!existingGroupKey.Equals(newGroupKey))
|
||||
{
|
||||
await RemoveItemInternalAsync(group, threadMailItemViewModel);
|
||||
await InsertItemInternalAsync(newGroupKey, threadMailItemViewModel);
|
||||
var mailThreadItems = threadMailItemViewModel.GetThreadMailItem();
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
// Group must be changed for this thread.
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
||||
|
||||
if (!MailCopyIdHashSet.Contains(addedItem.UniqueId))
|
||||
else
|
||||
{
|
||||
MailCopyIdHashSet.Add(addedItem.UniqueId);
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
||||
}
|
||||
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -177,10 +193,10 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
if (item is MailItemViewModel itemViewModel)
|
||||
{
|
||||
itemViewModel.Update(addedItem);
|
||||
await ExecuteUIThread(() => { itemViewModel.Update(addedItem); });
|
||||
|
||||
MailCopyIdHashSet.Remove(itemViewModel.UniqueId);
|
||||
MailCopyIdHashSet.Add(addedItem.UniqueId);
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -189,15 +205,18 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
var threadMailItem = new ThreadMailItem();
|
||||
|
||||
threadMailItem.AddThreadItem(item);
|
||||
threadMailItem.AddThreadItem(addedItem);
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
threadMailItem.AddThreadItem(item);
|
||||
threadMailItem.AddThreadItem(addedItem);
|
||||
|
||||
if (threadMailItem.ThreadItems.Count == 1) return;
|
||||
if (threadMailItem.ThreadItems.Count == 1) return;
|
||||
|
||||
var newGroupKey = GetGroupingKey(threadMailItem);
|
||||
var newGroupKey = GetGroupingKey(threadMailItem);
|
||||
|
||||
await RemoveItemInternalAsync(group, item);
|
||||
await InsertItemInternalAsync(newGroupKey, threadMailItem);
|
||||
RemoveItemInternal(group, item);
|
||||
InsertItemInternal(newGroupKey, threadMailItem);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -208,6 +227,9 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
// Update properties.
|
||||
if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
await ExecuteUIThread(() => { itemViewModel.Update(addedItem); });
|
||||
|
||||
shouldExit = true;
|
||||
@@ -224,7 +246,7 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
var groupKey = GetGroupingKey(addedItem);
|
||||
|
||||
await InsertItemInternalAsync(groupKey, addedItem);
|
||||
await ExecuteUIThread(() => { InsertItemInternal(groupKey, addedItem); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,12 +290,9 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
foreach (var item in group)
|
||||
{
|
||||
existingGroup.Add(item);
|
||||
|
||||
// _mailItemSource.InsertItem(existingGroup, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,11 +344,14 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
if (itemContainer == null) return;
|
||||
|
||||
// mailCopyIdHashSet.Remove(itemContainer.ItemViewModel.UniqueId);
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
|
||||
}
|
||||
|
||||
itemContainer.ItemViewModel?.Update(updatedMailCopy);
|
||||
|
||||
// mailCopyIdHashSet.Add(updatedMailCopy.UniqueId);
|
||||
UpdateUniqueIdHashes(updatedMailCopy, true);
|
||||
|
||||
// Call thread notifications if possible.
|
||||
itemContainer.ThreadViewModel?.NotifyPropertyChanges();
|
||||
@@ -426,6 +448,8 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
* -> Remove the thread.
|
||||
*/
|
||||
|
||||
var oldGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.RemoveCopyItem(removalItem); });
|
||||
|
||||
if (threadMailItemViewModel.ThreadItems.Count == 1)
|
||||
@@ -435,25 +459,37 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
var singleViewModel = threadMailItemViewModel.GetSingleItemViewModel();
|
||||
var groupKey = GetGroupingKey(singleViewModel);
|
||||
|
||||
await RemoveItemInternalAsync(group, threadMailItemViewModel);
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
RemoveItemInternal(group, threadMailItemViewModel);
|
||||
InsertItemInternal(groupKey, singleViewModel);
|
||||
});
|
||||
|
||||
// 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.
|
||||
|
||||
if (!PruneSingleNonDraftItems || singleViewModel.IsDraft)
|
||||
if (PruneSingleNonDraftItems && !singleViewModel.IsDraft)
|
||||
{
|
||||
await InsertItemInternalAsync(groupKey, singleViewModel);
|
||||
// This item should not be here anymore.
|
||||
// It's basically a reply mail in Draft folder.
|
||||
var newGroup = _mailItemSource.FirstGroupByKeyOrDefault(groupKey);
|
||||
|
||||
if (newGroup != null)
|
||||
{
|
||||
await ExecuteUIThread(() => { RemoveItemInternal(newGroup, singleViewModel); });
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (threadMailItemViewModel.ThreadItems.Count == 0)
|
||||
{
|
||||
await RemoveItemInternalAsync(group, threadMailItemViewModel);
|
||||
await ExecuteUIThread(() => { RemoveItemInternal(group, threadMailItemViewModel); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item inside the thread is removed.
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.ThreadItems.Remove(removalItem); });
|
||||
|
||||
threadMailItemViewModel.ThreadItems.Remove(removalItem);
|
||||
UpdateUniqueIdHashes(removalItem, false);
|
||||
}
|
||||
|
||||
shouldExit = true;
|
||||
@@ -461,7 +497,8 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
}
|
||||
else if (item.UniqueId == removeItem.UniqueId)
|
||||
{
|
||||
await RemoveItemInternalAsync(group, item);
|
||||
await ExecuteUIThread(() => { RemoveItemInternal(group, item); });
|
||||
|
||||
shouldExit = true;
|
||||
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
@@ -94,5 +95,7 @@ namespace Wino.Mail.ViewModels.Data
|
||||
OnPropertyChanged(nameof(Subject));
|
||||
OnPropertyChanged(nameof(PreviewText));
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => new[] { UniqueId };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,18 +12,14 @@ namespace Wino.Mail.ViewModels.Data
|
||||
/// <summary>
|
||||
/// Thread mail item (multiple IMailItem) view model representation.
|
||||
/// </summary>
|
||||
public class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable<string>, IComparable<DateTime>
|
||||
public partial class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable<string>, IComparable<DateTime>
|
||||
{
|
||||
public ObservableCollection<IMailItem> ThreadItems => ((IMailItemThread)_threadMailItem).ThreadItems;
|
||||
|
||||
private readonly ThreadMailItem _threadMailItem;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isThreadExpanded;
|
||||
public bool IsThreadExpanded
|
||||
{
|
||||
get => isThreadExpanded;
|
||||
set => SetProperty(ref isThreadExpanded, value);
|
||||
}
|
||||
|
||||
public ThreadMailItemViewModel(ThreadMailItem threadMailItem)
|
||||
{
|
||||
@@ -36,6 +32,8 @@ namespace Wino.Mail.ViewModels.Data
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadMailItem GetThreadMailItem() => _threadMailItem;
|
||||
|
||||
public IEnumerable<MailCopy> GetMailCopies()
|
||||
=> ThreadItems.OfType<MailItemViewModel>().Select(a => a.MailCopy);
|
||||
|
||||
@@ -123,5 +121,7 @@ namespace Wino.Mail.ViewModels.Data
|
||||
|
||||
// Get single mail item view model out of the only item in thread items.
|
||||
public MailItemViewModel GetSingleItemViewModel() => ThreadItems.First() as MailItemViewModel;
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => ((IMailItemThread)_threadMailItem).GetContainingIds();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
private readonly IMailService _mailService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly IWinoSynchronizerFactory _winoSynchronizerFactory;
|
||||
private readonly IThreadingStrategyProvider _threadingStrategyProvider;
|
||||
private readonly IContextMenuItemService _contextMenuItemService;
|
||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||
@@ -143,7 +142,6 @@ namespace Wino.Mail.ViewModels
|
||||
IMailService mailService,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IFolderService folderService,
|
||||
IWinoSynchronizerFactory winoSynchronizerFactory,
|
||||
IThreadingStrategyProvider threadingStrategyProvider,
|
||||
IContextMenuItemService contextMenuItemService,
|
||||
IWinoRequestDelegator winoRequestDelegator,
|
||||
@@ -156,7 +154,6 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
_mailService = mailService;
|
||||
_folderService = folderService;
|
||||
_winoSynchronizerFactory = winoSynchronizerFactory;
|
||||
_threadingStrategyProvider = threadingStrategyProvider;
|
||||
_contextMenuItemService = contextMenuItemService;
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
@@ -172,6 +169,24 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
await ExecuteUIThread(() => { SelectedItemCollectionUpdated(a.EventArgs); });
|
||||
});
|
||||
|
||||
MailCollection.MailItemRemoved += (c, removedItem) =>
|
||||
{
|
||||
if (removedItem is ThreadMailItemViewModel removedThreadViewModelItem)
|
||||
{
|
||||
foreach (var viewModel in removedThreadViewModelItem.ThreadItems.Cast<MailItemViewModel>())
|
||||
{
|
||||
if (SelectedItems.Contains(viewModel))
|
||||
{
|
||||
SelectedItems.Remove(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (removedItem is MailItemViewModel removedMailItemViewModel && SelectedItems.Contains(removedMailItemViewModel))
|
||||
{
|
||||
SelectedItems.Remove(removedMailItemViewModel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#region Properties
|
||||
@@ -310,25 +325,6 @@ namespace Wino.Mail.ViewModels
|
||||
MailCollection.CoreDispatcher = Dispatcher;
|
||||
}
|
||||
|
||||
//protected override async void OnFolderUpdated(MailItemFolder updatedFolder, MailAccount account)
|
||||
//{
|
||||
// base.OnFolderUpdated(updatedFolder, account);
|
||||
|
||||
// // Don't need to update if the folder update does not belong to the current folder menu item.
|
||||
// if (ActiveFolder == null || updatedFolder == null || !ActiveFolder.HandlingFolders.Any(a => a.Id == updatedFolder.Id)) return;
|
||||
|
||||
// await ExecuteUIThread(() =>
|
||||
// {
|
||||
// ActiveFolder.UpdateFolder(updatedFolder);
|
||||
|
||||
// OnPropertyChanged(nameof(CanSynchronize));
|
||||
// OnPropertyChanged(nameof(IsFolderSynchronizationEnabled));
|
||||
// });
|
||||
|
||||
// // Force synchronization after enabling the folder.
|
||||
// SyncFolder();
|
||||
//}
|
||||
|
||||
private async void UpdateBarMessage(InfoBarMessageType severity, string title, string message)
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
@@ -603,6 +599,8 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
base.OnMailAdded(addedMail);
|
||||
|
||||
if (addedMail.AssignedAccount == null || addedMail.AssignedFolder == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await listManipulationSemepahore.WaitAsync();
|
||||
@@ -618,12 +616,9 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
if (!shouldPreventIgnoringFilter && ShouldPreventItemAdd(addedMail)) return;
|
||||
|
||||
await ExecuteUIThread(async () =>
|
||||
{
|
||||
await MailCollection.AddAsync(addedMail);
|
||||
await MailCollection.AddAsync(addedMail);
|
||||
|
||||
NotifyItemFoundState();
|
||||
});
|
||||
await ExecuteUIThread(() => { NotifyItemFoundState(); });
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
@@ -693,6 +688,7 @@ namespace Wino.Mail.ViewModels
|
||||
gmailUnreadFolderMarkedAsReadUniqueIds.Remove(removedMail.UniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnDraftCreated(MailCopy draftMail, MailAccount account)
|
||||
{
|
||||
base.OnDraftCreated(draftMail, account);
|
||||
@@ -704,10 +700,10 @@ namespace Wino.Mail.ViewModels
|
||||
await listManipulationSemepahore.WaitAsync();
|
||||
|
||||
// Create the item. Draft folder navigation is already done at this point.
|
||||
await ExecuteUIThread(async () =>
|
||||
{
|
||||
await MailCollection.AddAsync(draftMail);
|
||||
await MailCollection.AddAsync(draftMail);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
// New draft is created by user. Select the item.
|
||||
Messenger.Send(new MailItemNavigationRequested(draftMail.UniqueId, ScrollToItem: true));
|
||||
|
||||
@@ -940,8 +936,6 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
if (navigatingMailItem != null)
|
||||
WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(navigatingMailItem, message.ScrollToItem));
|
||||
else
|
||||
Debugger.Break();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -980,17 +974,19 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
foreach (var accountId in accountIds)
|
||||
{
|
||||
var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(accountId);
|
||||
// TODO: Server: Check whether account is already synchronizing from the server.
|
||||
|
||||
if (synchronizer == null) continue;
|
||||
//var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(accountId);
|
||||
|
||||
bool isAccountSynchronizing = synchronizer.State != AccountSynchronizerState.Idle;
|
||||
//if (synchronizer == null) continue;
|
||||
|
||||
if (isAccountSynchronizing)
|
||||
{
|
||||
isAnyAccountSynchronizing = true;
|
||||
break;
|
||||
}
|
||||
//bool isAccountSynchronizing = synchronizer.State != AccountSynchronizerState.Idle;
|
||||
|
||||
//if (isAccountSynchronizing)
|
||||
//{
|
||||
// isAnyAccountSynchronizing = true;
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Wino.Mail.ViewModels
|
||||
private readonly IMimeFileService _mimeFileService;
|
||||
private readonly Core.Domain.Interfaces.IMailService _mailService;
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IWinoSynchronizerFactory _winoSynchronizerFactory;
|
||||
private readonly IWinoRequestDelegator _requestDelegator;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IUnsubscriptionService _unsubscriptionService;
|
||||
@@ -124,7 +123,6 @@ namespace Wino.Mail.ViewModels
|
||||
IMimeFileService mimeFileService,
|
||||
Core.Domain.Interfaces.IMailService mailService,
|
||||
IFileService fileService,
|
||||
IWinoSynchronizerFactory winoSynchronizerFactory,
|
||||
IWinoRequestDelegator requestDelegator,
|
||||
IStatePersistanceService statePersistanceService,
|
||||
IClipboardService clipboardService,
|
||||
@@ -141,7 +139,6 @@ namespace Wino.Mail.ViewModels
|
||||
_mimeFileService = mimeFileService;
|
||||
_mailService = mailService;
|
||||
_fileService = fileService;
|
||||
_winoSynchronizerFactory = winoSynchronizerFactory;
|
||||
_requestDelegator = requestDelegator;
|
||||
}
|
||||
|
||||
@@ -344,30 +341,31 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task HandleSingleItemDownloadAsync(MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItemViewModel.AssignedAccount.Id);
|
||||
// TODO: Server: Download single mime and report back the item here.
|
||||
|
||||
try
|
||||
{
|
||||
// To show the progress on the UI.
|
||||
CurrentDownloadPercentage = 1;
|
||||
//var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItemViewModel.AssignedAccount.Id);
|
||||
|
||||
await synchronizer.DownloadMissingMimeMessageAsync(mailItemViewModel.MailCopy, this, renderCancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Log.Information("MIME download is canceled.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetProgress();
|
||||
}
|
||||
//try
|
||||
//{
|
||||
// // To show the progress on the UI.
|
||||
// CurrentDownloadPercentage = 1;
|
||||
|
||||
// await synchronizer.DownloadMissingMimeMessageAsync(mailItemViewModel.MailCopy, this, renderCancellationTokenSource.Token);
|
||||
//}
|
||||
//catch (OperationCanceledException)
|
||||
//{
|
||||
// Log.Information("MIME download is canceled.");
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// DialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error);
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// ResetProgress();
|
||||
//}
|
||||
}
|
||||
|
||||
private async Task RenderAsync(MailItemViewModel mailItemViewModel, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -9,8 +9,8 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Messages.Navigation;
|
||||
using Wino.Core.Requests;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\Wino.Core\Wino.Core.csproj" />
|
||||
<ProjectReference Include="..\Wino.Messages\Wino.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user