Add mail categories support
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Core.Requests.Category;
|
||||
using Wino.Core.Services;
|
||||
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class MailCategoryManagementPageViewModel : MailBaseViewModel
|
||||
{
|
||||
private readonly IMailCategoryService _mailCategoryService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(CanRefresh))]
|
||||
public partial MailAccount Account { get; set; }
|
||||
|
||||
public ObservableCollection<MailCategory> Categories { get; } = [];
|
||||
|
||||
public bool CanRefresh => Account?.ProviderType == MailProviderType.Outlook;
|
||||
public bool HasCategories => Categories.Count > 0;
|
||||
|
||||
public MailCategoryManagementPageViewModel(
|
||||
IMailCategoryService mailCategoryService,
|
||||
IAccountService accountService,
|
||||
IMailDialogService dialogService,
|
||||
IWinoRequestDelegator winoRequestDelegator)
|
||||
{
|
||||
_mailCategoryService = mailCategoryService;
|
||||
_accountService = accountService;
|
||||
_dialogService = dialogService;
|
||||
_winoRequestDelegator = winoRequestDelegator;
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters is not Guid accountId)
|
||||
return;
|
||||
|
||||
Account = await _accountService.GetAccountAsync(accountId).ConfigureAwait(false);
|
||||
|
||||
if (Account != null)
|
||||
{
|
||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task AddCategoryAsync()
|
||||
=> CreateOrUpdateCategoryAsync();
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RefreshCategoriesAsync()
|
||||
{
|
||||
if (!CanRefresh)
|
||||
return;
|
||||
|
||||
var shouldContinue = await _dialogService.ShowConfirmationDialogAsync(
|
||||
Translator.MailCategoryManagementPage_RefreshConfirmationMessage,
|
||||
Translator.Buttons_Refresh,
|
||||
Translator.Buttons_Refresh).ConfigureAwait(false);
|
||||
|
||||
if (!shouldContinue)
|
||||
return;
|
||||
|
||||
await _mailCategoryService.DeleteCategoriesAsync(Account.Id).ConfigureAwait(false);
|
||||
await SynchronizationManager.Instance.SynchronizeCategoriesAsync(Account.Id).ConfigureAwait(false);
|
||||
|
||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task EditCategoryAsync(MailCategory category)
|
||||
=> CreateOrUpdateCategoryAsync(category);
|
||||
|
||||
public async Task DeleteCategoryAsync(MailCategory category)
|
||||
{
|
||||
if (category == null)
|
||||
return;
|
||||
|
||||
var shouldDelete = await _dialogService.ShowConfirmationDialogAsync(
|
||||
string.Format(Translator.MailCategoryManagementPage_DeleteConfirmationMessage, category.Name),
|
||||
Translator.MailCategoryManagementPage_DeleteConfirmationTitle,
|
||||
Translator.Buttons_Delete).ConfigureAwait(false);
|
||||
|
||||
if (!shouldDelete)
|
||||
return;
|
||||
|
||||
var deleteRequest = await BuildDeleteCategoryRequestAsync(category).ConfigureAwait(false);
|
||||
await _mailCategoryService.DeleteCategoryAsync(category.Id).ConfigureAwait(false);
|
||||
await QueueOutlookCategoryRequestsAsync(deleteRequest).ConfigureAwait(false);
|
||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task SetFavoriteAsync(MailCategory category, bool isFavorite)
|
||||
{
|
||||
if (category == null)
|
||||
return;
|
||||
|
||||
await _mailCategoryService.ToggleFavoriteAsync(category.Id, isFavorite).ConfigureAwait(false);
|
||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task CreateOrUpdateCategoryAsync(MailCategory existingCategory = null)
|
||||
{
|
||||
var dialogResult = await _dialogService.ShowEditMailCategoryDialogAsync(existingCategory).ConfigureAwait(false);
|
||||
if (dialogResult == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dialogResult.Name))
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(
|
||||
Translator.MailCategoryDialog_InvalidNameMessage,
|
||||
Translator.MailCategoryDialog_InvalidNameTitle,
|
||||
WinoCustomMessageDialogIcon.Warning).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedName = dialogResult.Name.Trim();
|
||||
var categoryIdToExclude = existingCategory?.Id;
|
||||
var alreadyExists = await _mailCategoryService.CategoryNameExistsAsync(Account.Id, normalizedName, categoryIdToExclude).ConfigureAwait(false);
|
||||
|
||||
if (alreadyExists)
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(
|
||||
Translator.MailCategoryDialog_DuplicateMessage,
|
||||
Translator.MailCategoryDialog_DuplicateTitle,
|
||||
WinoCustomMessageDialogIcon.Warning).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingCategory == null)
|
||||
{
|
||||
var newCategory = new MailCategory
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
MailAccountId = Account.Id,
|
||||
Name = normalizedName,
|
||||
BackgroundColorHex = dialogResult.BackgroundColorHex,
|
||||
TextColorHex = dialogResult.TextColorHex,
|
||||
Source = Account.ProviderType == MailProviderType.Outlook ? MailCategorySource.Outlook : MailCategorySource.Local
|
||||
};
|
||||
|
||||
await _mailCategoryService.CreateCategoryAsync(newCategory).ConfigureAwait(false);
|
||||
|
||||
if (Account.ProviderType == MailProviderType.Outlook)
|
||||
{
|
||||
await _winoRequestDelegator.ExecuteAsync(Account.Id, [new MailCategoryCreateRequest(newCategory)]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousName = existingCategory.Name;
|
||||
var previousRemoteId = existingCategory.RemoteId;
|
||||
|
||||
existingCategory.Name = normalizedName;
|
||||
existingCategory.BackgroundColorHex = dialogResult.BackgroundColorHex;
|
||||
existingCategory.TextColorHex = dialogResult.TextColorHex;
|
||||
|
||||
await _mailCategoryService.UpdateCategoryAsync(existingCategory).ConfigureAwait(false);
|
||||
|
||||
if (Account.ProviderType == MailProviderType.Outlook)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(previousRemoteId))
|
||||
{
|
||||
await _winoRequestDelegator.ExecuteAsync(Account.Id, [new MailCategoryCreateRequest(existingCategory)]).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var affectedMessages = await BuildAffectedMessageTargetsAsync(existingCategory.Id).ConfigureAwait(false);
|
||||
var updateRequest = new MailCategoryUpdateRequest(existingCategory, previousName, previousRemoteId, affectedMessages);
|
||||
await _winoRequestDelegator.ExecuteAsync(Account.Id, [updateRequest]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<MailCategoryDeleteRequest> BuildDeleteCategoryRequestAsync(MailCategory category)
|
||||
{
|
||||
if (category == null || Account?.ProviderType != MailProviderType.Outlook)
|
||||
return null;
|
||||
|
||||
var mailCopies = await _mailCategoryService.GetMailCopiesForCategoryAsync(category.Id).ConfigureAwait(false);
|
||||
var affectedMessages = new List<MailCategoryMessageUpdateTarget>();
|
||||
|
||||
foreach (var mailCopy in mailCopies.Where(a => !string.IsNullOrWhiteSpace(a.Id)))
|
||||
{
|
||||
var remainingNames = await _mailCategoryService.GetCategoryNamesForMailAsync(mailCopy.UniqueId).ConfigureAwait(false);
|
||||
var categoryNames = remainingNames
|
||||
.Where(a => !string.Equals(a, category.Name, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
affectedMessages.Add(new MailCategoryMessageUpdateTarget(mailCopy.Id, categoryNames));
|
||||
}
|
||||
|
||||
return new MailCategoryDeleteRequest(category, category.RemoteId, affectedMessages);
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<MailCategoryMessageUpdateTarget>> BuildAffectedMessageTargetsAsync(Guid categoryId)
|
||||
{
|
||||
var mailCopies = await _mailCategoryService.GetMailCopiesForCategoryAsync(categoryId).ConfigureAwait(false);
|
||||
var affectedMessages = new List<MailCategoryMessageUpdateTarget>();
|
||||
|
||||
foreach (var mailCopy in mailCopies.Where(a => !string.IsNullOrWhiteSpace(a.Id)))
|
||||
{
|
||||
var categoryNames = await _mailCategoryService.GetCategoryNamesForMailAsync(mailCopy.UniqueId).ConfigureAwait(false);
|
||||
affectedMessages.Add(new MailCategoryMessageUpdateTarget(mailCopy.Id, categoryNames));
|
||||
}
|
||||
|
||||
return affectedMessages;
|
||||
}
|
||||
|
||||
private Task QueueOutlookCategoryRequestsAsync(params IRequestBase[] requests)
|
||||
=> Account?.ProviderType == MailProviderType.Outlook && requests.Any(a => a != null)
|
||||
? _winoRequestDelegator.ExecuteAsync(Account.Id, requests.Where(a => a != null))
|
||||
: Task.CompletedTask;
|
||||
|
||||
private async Task LoadCategoriesAsync()
|
||||
{
|
||||
var categories = await _mailCategoryService.GetCategoriesAsync(Account.Id).ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
Categories.Clear();
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
Categories.Add(category);
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(HasCategories));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user