file scoped namespaces (#565)
This commit is contained in:
@@ -15,164 +15,163 @@ using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Messaging.Server;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
||||
{
|
||||
public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IWinoServerConnectionManager _serverConnectionManager;
|
||||
private readonly IFolderService _folderService;
|
||||
|
||||
public MailAccount Account { get; set; }
|
||||
public ObservableCollection<IMailItemFolder> CurrentFolders { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isFocusedInboxEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool areNotificationsEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSignatureEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isAppendMessageSettingVisible;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isAppendMessageSettinEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isTaskbarBadgeEnabled;
|
||||
|
||||
public bool IsFocusedInboxSupportedForAccount => Account != null && Account.Preferences.IsFocusedInboxEnabled != null;
|
||||
|
||||
|
||||
public AccountDetailsPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IWinoServerConnectionManager serverConnectionManager,
|
||||
IFolderService folderService)
|
||||
{
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IWinoServerConnectionManager _serverConnectionManager;
|
||||
private readonly IFolderService _folderService;
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_serverConnectionManager = serverConnectionManager;
|
||||
_folderService = folderService;
|
||||
}
|
||||
|
||||
public MailAccount Account { get; set; }
|
||||
public ObservableCollection<IMailItemFolder> CurrentFolders { get; set; } = [];
|
||||
[RelayCommand]
|
||||
private Task SetupSpecialFolders()
|
||||
=> _dialogService.HandleSystemFolderConfigurationDialogAsync(Account.Id, _folderService);
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isFocusedInboxEnabled;
|
||||
[RelayCommand]
|
||||
private void EditSignature()
|
||||
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsSignature_Title, WinoPage.SignatureManagementPage, Account.Id));
|
||||
|
||||
[ObservableProperty]
|
||||
private bool areNotificationsEnabled;
|
||||
[RelayCommand]
|
||||
private void EditAliases()
|
||||
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAliases_Title, WinoPage.AliasManagementPage, Account.Id));
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSignatureEnabled;
|
||||
public Task FolderSyncToggledAsync(IMailItemFolder folderStructure, bool isEnabled)
|
||||
=> _folderService.ChangeFolderSynchronizationStateAsync(folderStructure.Id, isEnabled);
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isAppendMessageSettingVisible;
|
||||
public Task FolderShowUnreadToggled(IMailItemFolder folderStructure, bool isEnabled)
|
||||
=> _folderService.ChangeFolderShowUnreadCountStateAsync(folderStructure.Id, isEnabled);
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isAppendMessageSettinEnabled;
|
||||
[RelayCommand]
|
||||
private async Task RenameAccount()
|
||||
{
|
||||
if (Account == null)
|
||||
return;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isTaskbarBadgeEnabled;
|
||||
var updatedAccount = await _dialogService.ShowEditAccountDialogAsync(Account);
|
||||
|
||||
public bool IsFocusedInboxSupportedForAccount => Account != null && Account.Preferences.IsFocusedInboxEnabled != null;
|
||||
|
||||
|
||||
public AccountDetailsPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IWinoServerConnectionManager serverConnectionManager,
|
||||
IFolderService folderService)
|
||||
if (updatedAccount != null)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_serverConnectionManager = serverConnectionManager;
|
||||
_folderService = folderService;
|
||||
await _accountService.UpdateAccountAsync(updatedAccount);
|
||||
|
||||
ReportUIChange(new AccountUpdatedMessage(updatedAccount));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task SetupSpecialFolders()
|
||||
=> _dialogService.HandleSystemFolderConfigurationDialogAsync(Account.Id, _folderService);
|
||||
[RelayCommand]
|
||||
private async Task DeleteAccount()
|
||||
{
|
||||
if (Account == null)
|
||||
return;
|
||||
|
||||
[RelayCommand]
|
||||
private void EditSignature()
|
||||
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsSignature_Title, WinoPage.SignatureManagementPage, Account.Id));
|
||||
var confirmation = await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DeleteAccountConfirmationTitle,
|
||||
string.Format(Translator.DialogMessage_DeleteAccountConfirmationMessage, Account.Name),
|
||||
Translator.Buttons_Delete);
|
||||
|
||||
[RelayCommand]
|
||||
private void EditAliases()
|
||||
=> Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAliases_Title, WinoPage.AliasManagementPage, Account.Id));
|
||||
if (!confirmation)
|
||||
return;
|
||||
|
||||
public Task FolderSyncToggledAsync(IMailItemFolder folderStructure, bool isEnabled)
|
||||
=> _folderService.ChangeFolderSynchronizationStateAsync(folderStructure.Id, isEnabled);
|
||||
|
||||
public Task FolderShowUnreadToggled(IMailItemFolder folderStructure, bool isEnabled)
|
||||
=> _folderService.ChangeFolderShowUnreadCountStateAsync(folderStructure.Id, isEnabled);
|
||||
var isSynchronizerKilledResponse = await _serverConnectionManager.GetResponseAsync<bool, KillAccountSynchronizerRequested>(new KillAccountSynchronizerRequested(Account.Id));
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RenameAccount()
|
||||
if (isSynchronizerKilledResponse.IsSuccess)
|
||||
{
|
||||
if (Account == null)
|
||||
return;
|
||||
await _accountService.DeleteAccountAsync(Account);
|
||||
|
||||
var updatedAccount = await _dialogService.ShowEditAccountDialogAsync(Account);
|
||||
_dialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success);
|
||||
|
||||
if (updatedAccount != null)
|
||||
{
|
||||
await _accountService.UpdateAccountAsync(updatedAccount);
|
||||
|
||||
ReportUIChange(new AccountUpdatedMessage(updatedAccount));
|
||||
}
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAccount()
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters is Guid accountId)
|
||||
{
|
||||
if (Account == null)
|
||||
return;
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
var confirmation = await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DeleteAccountConfirmationTitle,
|
||||
string.Format(Translator.DialogMessage_DeleteAccountConfirmationMessage, Account.Name),
|
||||
Translator.Buttons_Delete);
|
||||
IsFocusedInboxEnabled = Account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault();
|
||||
AreNotificationsEnabled = Account.Preferences.IsNotificationsEnabled;
|
||||
IsSignatureEnabled = Account.Preferences.IsSignatureEnabled;
|
||||
|
||||
if (!confirmation)
|
||||
return;
|
||||
IsAppendMessageSettingVisible = Account.ProviderType == MailProviderType.IMAP4;
|
||||
IsAppendMessageSettinEnabled = Account.Preferences.ShouldAppendMessagesToSentFolder;
|
||||
IsTaskbarBadgeEnabled = Account.Preferences.IsTaskbarBadgeEnabled;
|
||||
|
||||
OnPropertyChanged(nameof(IsFocusedInboxSupportedForAccount));
|
||||
|
||||
var isSynchronizerKilledResponse = await _serverConnectionManager.GetResponseAsync<bool, KillAccountSynchronizerRequested>(new KillAccountSynchronizerRequested(Account.Id));
|
||||
var folderStructures = (await _folderService.GetFolderStructureForAccountAsync(Account.Id, true)).Folders;
|
||||
|
||||
if (isSynchronizerKilledResponse.IsSuccess)
|
||||
foreach (var folder in folderStructures)
|
||||
{
|
||||
await _accountService.DeleteAccountAsync(Account);
|
||||
|
||||
_dialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success);
|
||||
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters is Guid accountId)
|
||||
{
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
IsFocusedInboxEnabled = Account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault();
|
||||
AreNotificationsEnabled = Account.Preferences.IsNotificationsEnabled;
|
||||
IsSignatureEnabled = Account.Preferences.IsSignatureEnabled;
|
||||
|
||||
IsAppendMessageSettingVisible = Account.ProviderType == MailProviderType.IMAP4;
|
||||
IsAppendMessageSettinEnabled = Account.Preferences.ShouldAppendMessagesToSentFolder;
|
||||
IsTaskbarBadgeEnabled = Account.Preferences.IsTaskbarBadgeEnabled;
|
||||
|
||||
OnPropertyChanged(nameof(IsFocusedInboxSupportedForAccount));
|
||||
|
||||
var folderStructures = (await _folderService.GetFolderStructureForAccountAsync(Account.Id, true)).Folders;
|
||||
|
||||
foreach (var folder in folderStructures)
|
||||
{
|
||||
CurrentFolders.Add(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(IsFocusedInboxEnabled) when IsFocusedInboxSupportedForAccount:
|
||||
Account.Preferences.IsFocusedInboxEnabled = IsFocusedInboxEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(AreNotificationsEnabled):
|
||||
Account.Preferences.IsNotificationsEnabled = AreNotificationsEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsAppendMessageSettinEnabled):
|
||||
Account.Preferences.ShouldAppendMessagesToSentFolder = IsAppendMessageSettinEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsSignatureEnabled):
|
||||
Account.Preferences.IsSignatureEnabled = IsSignatureEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsTaskbarBadgeEnabled):
|
||||
Account.Preferences.IsTaskbarBadgeEnabled = IsTaskbarBadgeEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
CurrentFolders.Add(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(IsFocusedInboxEnabled) when IsFocusedInboxSupportedForAccount:
|
||||
Account.Preferences.IsFocusedInboxEnabled = IsFocusedInboxEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(AreNotificationsEnabled):
|
||||
Account.Preferences.IsNotificationsEnabled = AreNotificationsEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsAppendMessageSettinEnabled):
|
||||
Account.Preferences.ShouldAppendMessagesToSentFolder = IsAppendMessageSettinEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsSignatureEnabled):
|
||||
Account.Preferences.IsSignatureEnabled = IsSignatureEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(IsTaskbarBadgeEnabled):
|
||||
Account.Preferences.IsTaskbarBadgeEnabled = IsTaskbarBadgeEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,368 +22,367 @@ using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Messaging.Server;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
|
||||
{
|
||||
public partial class AccountManagementViewModel : AccountManagementPageViewModelBase
|
||||
private readonly ISpecialImapProviderConfigResolver _specialImapProviderConfigResolver;
|
||||
private readonly IImapTestService _imapTestService;
|
||||
|
||||
public IMailDialogService MailDialogService { get; }
|
||||
|
||||
public AccountManagementViewModel(IMailDialogService dialogService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
INavigationService navigationService,
|
||||
IAccountService accountService,
|
||||
ISpecialImapProviderConfigResolver specialImapProviderConfigResolver,
|
||||
IProviderService providerService,
|
||||
IImapTestService imapTestService,
|
||||
IStoreManagementService storeManagementService,
|
||||
IAuthenticationProvider authenticationProvider,
|
||||
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
|
||||
{
|
||||
private readonly ISpecialImapProviderConfigResolver _specialImapProviderConfigResolver;
|
||||
private readonly IImapTestService _imapTestService;
|
||||
MailDialogService = dialogService;
|
||||
_specialImapProviderConfigResolver = specialImapProviderConfigResolver;
|
||||
_imapTestService = imapTestService;
|
||||
}
|
||||
|
||||
public IMailDialogService MailDialogService { get; }
|
||||
[RelayCommand]
|
||||
private async Task CreateMergedAccountAsync()
|
||||
{
|
||||
var linkName = await DialogService.ShowTextInputDialogAsync(string.Empty, Translator.DialogMessage_CreateLinkedAccountTitle, Translator.DialogMessage_CreateLinkedAccountMessage, Translator.Buttons_Create);
|
||||
|
||||
public AccountManagementViewModel(IMailDialogService dialogService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
INavigationService navigationService,
|
||||
IAccountService accountService,
|
||||
ISpecialImapProviderConfigResolver specialImapProviderConfigResolver,
|
||||
IProviderService providerService,
|
||||
IImapTestService imapTestService,
|
||||
IStoreManagementService storeManagementService,
|
||||
IAuthenticationProvider authenticationProvider,
|
||||
IPreferencesService preferencesService) : base(dialogService, winoServerConnectionManager, navigationService, accountService, providerService, storeManagementService, authenticationProvider, preferencesService)
|
||||
if (string.IsNullOrEmpty(linkName)) return;
|
||||
|
||||
// Create arbitary empty merged inbox with an empty Guid and go to edit page.
|
||||
var mergedInbox = new MergedInbox()
|
||||
{
|
||||
MailDialogService = dialogService;
|
||||
_specialImapProviderConfigResolver = specialImapProviderConfigResolver;
|
||||
_imapTestService = imapTestService;
|
||||
Id = Guid.Empty,
|
||||
Name = linkName
|
||||
};
|
||||
|
||||
var mergedAccountProviderDetailViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, new List<AccountProviderDetailViewModel>());
|
||||
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name,
|
||||
WinoPage.MergedAccountDetailsPage,
|
||||
mergedAccountProviderDetailViewModel));
|
||||
}
|
||||
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddNewAccountAsync()
|
||||
{
|
||||
if (IsAccountCreationBlocked)
|
||||
{
|
||||
var isPurchaseClicked = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_AccountLimitMessage, Translator.DialogMessage_AccountLimitTitle, Translator.Buttons_Purchase);
|
||||
|
||||
if (!isPurchaseClicked) return;
|
||||
|
||||
await PurchaseUnlimitedAccountAsync();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateMergedAccountAsync()
|
||||
MailAccount createdAccount = null;
|
||||
IAccountCreationDialog creationDialog = null;
|
||||
|
||||
try
|
||||
{
|
||||
var linkName = await DialogService.ShowTextInputDialogAsync(string.Empty, Translator.DialogMessage_CreateLinkedAccountTitle, Translator.DialogMessage_CreateLinkedAccountMessage, Translator.Buttons_Create);
|
||||
var providers = ProviderService.GetAvailableProviders();
|
||||
|
||||
if (string.IsNullOrEmpty(linkName)) return;
|
||||
// Select provider.
|
||||
var accountCreationDialogResult = await MailDialogService.ShowAccountProviderSelectionDialogAsync(providers);
|
||||
|
||||
// Create arbitary empty merged inbox with an empty Guid and go to edit page.
|
||||
var mergedInbox = new MergedInbox()
|
||||
var accountCreationCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (accountCreationDialogResult != null)
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
Name = linkName
|
||||
};
|
||||
creationDialog = MailDialogService.GetAccountCreationDialog(accountCreationDialogResult);
|
||||
|
||||
var mergedAccountProviderDetailViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, new List<AccountProviderDetailViewModel>());
|
||||
CustomServerInformation customServerInformation = null;
|
||||
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name,
|
||||
WinoPage.MergedAccountDetailsPage,
|
||||
mergedAccountProviderDetailViewModel));
|
||||
}
|
||||
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddNewAccountAsync()
|
||||
{
|
||||
if (IsAccountCreationBlocked)
|
||||
{
|
||||
var isPurchaseClicked = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_AccountLimitMessage, Translator.DialogMessage_AccountLimitTitle, Translator.Buttons_Purchase);
|
||||
|
||||
if (!isPurchaseClicked) return;
|
||||
|
||||
await PurchaseUnlimitedAccountAsync();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MailAccount createdAccount = null;
|
||||
IAccountCreationDialog creationDialog = null;
|
||||
|
||||
try
|
||||
{
|
||||
var providers = ProviderService.GetAvailableProviders();
|
||||
|
||||
// Select provider.
|
||||
var accountCreationDialogResult = await MailDialogService.ShowAccountProviderSelectionDialogAsync(providers);
|
||||
|
||||
var accountCreationCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (accountCreationDialogResult != null)
|
||||
createdAccount = new MailAccount()
|
||||
{
|
||||
creationDialog = MailDialogService.GetAccountCreationDialog(accountCreationDialogResult);
|
||||
ProviderType = accountCreationDialogResult.ProviderType,
|
||||
Name = accountCreationDialogResult.AccountName,
|
||||
SpecialImapProvider = accountCreationDialogResult.SpecialImapProviderDetails?.SpecialImapProvider ?? SpecialImapProvider.None,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
|
||||
CustomServerInformation customServerInformation = null;
|
||||
await creationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
|
||||
creationDialog.State = AccountCreationDialogState.SigningIn;
|
||||
|
||||
createdAccount = new MailAccount()
|
||||
string tokenInformation = string.Empty;
|
||||
|
||||
// Custom server implementation requires more async waiting.
|
||||
if (creationDialog is IImapAccountCreationDialog customServerDialog)
|
||||
{
|
||||
// Pass along the account properties and perform initial navigation on the imap frame.
|
||||
customServerDialog.StartImapConnectionSetup(createdAccount);
|
||||
|
||||
customServerInformation = await customServerDialog.GetCustomServerInformationAsync()
|
||||
?? throw new AccountSetupCanceledException();
|
||||
|
||||
// At this point connection is successful.
|
||||
// Save the server setup information and later on we'll fetch folders.
|
||||
|
||||
customServerInformation.AccountId = createdAccount.Id;
|
||||
|
||||
createdAccount.Address = customServerInformation.Address;
|
||||
createdAccount.ServerInformation = customServerInformation;
|
||||
createdAccount.SenderName = customServerInformation.DisplayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hanle special imap providers like iCloud and Yahoo.
|
||||
if (accountCreationDialogResult.SpecialImapProviderDetails != null)
|
||||
{
|
||||
ProviderType = accountCreationDialogResult.ProviderType,
|
||||
Name = accountCreationDialogResult.AccountName,
|
||||
SpecialImapProvider = accountCreationDialogResult.SpecialImapProviderDetails?.SpecialImapProvider ?? SpecialImapProvider.None,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
|
||||
await creationDialog.ShowDialogAsync(accountCreationCancellationTokenSource);
|
||||
creationDialog.State = AccountCreationDialogState.SigningIn;
|
||||
|
||||
string tokenInformation = string.Empty;
|
||||
|
||||
// Custom server implementation requires more async waiting.
|
||||
if (creationDialog is IImapAccountCreationDialog customServerDialog)
|
||||
{
|
||||
// Pass along the account properties and perform initial navigation on the imap frame.
|
||||
customServerDialog.StartImapConnectionSetup(createdAccount);
|
||||
|
||||
customServerInformation = await customServerDialog.GetCustomServerInformationAsync()
|
||||
?? throw new AccountSetupCanceledException();
|
||||
|
||||
// At this point connection is successful.
|
||||
// Save the server setup information and later on we'll fetch folders.
|
||||
|
||||
// Special imap provider testing dialog. This is only available for iCloud and Yahoo.
|
||||
customServerInformation = _specialImapProviderConfigResolver.GetServerInformation(createdAccount, accountCreationDialogResult);
|
||||
customServerInformation.Id = Guid.NewGuid();
|
||||
customServerInformation.AccountId = createdAccount.Id;
|
||||
|
||||
createdAccount.SenderName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
|
||||
createdAccount.Address = customServerInformation.Address;
|
||||
createdAccount.ServerInformation = customServerInformation;
|
||||
createdAccount.SenderName = customServerInformation.DisplayName;
|
||||
|
||||
await _imapTestService.TestImapConnectionAsync(customServerInformation, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hanle special imap providers like iCloud and Yahoo.
|
||||
if (accountCreationDialogResult.SpecialImapProviderDetails != null)
|
||||
{
|
||||
// Special imap provider testing dialog. This is only available for iCloud and Yahoo.
|
||||
customServerInformation = _specialImapProviderConfigResolver.GetServerInformation(createdAccount, accountCreationDialogResult);
|
||||
customServerInformation.Id = Guid.NewGuid();
|
||||
customServerInformation.AccountId = createdAccount.Id;
|
||||
// OAuth authentication is handled here.
|
||||
// Server authenticates, returns the token info here.
|
||||
|
||||
createdAccount.SenderName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
|
||||
createdAccount.Address = customServerInformation.Address;
|
||||
var tokenInformationResponse = await WinoServerConnectionManager
|
||||
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
|
||||
createdAccount,
|
||||
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
|
||||
|
||||
await _imapTestService.TestImapConnectionAsync(customServerInformation, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// OAuth authentication is handled here.
|
||||
// Server authenticates, returns the token info here.
|
||||
if (creationDialog.State == AccountCreationDialogState.Canceled)
|
||||
throw new AccountSetupCanceledException();
|
||||
|
||||
var tokenInformationResponse = await WinoServerConnectionManager
|
||||
.GetResponseAsync<TokenInformationEx, AuthorizationRequested>(new AuthorizationRequested(accountCreationDialogResult.ProviderType,
|
||||
createdAccount,
|
||||
createdAccount.ProviderType == MailProviderType.Gmail), accountCreationCancellationTokenSource.Token);
|
||||
if (!tokenInformationResponse.IsSuccess)
|
||||
throw new Exception(tokenInformationResponse.Message);
|
||||
|
||||
if (creationDialog.State == AccountCreationDialogState.Canceled)
|
||||
throw new AccountSetupCanceledException();
|
||||
createdAccount.Address = tokenInformationResponse.Data.AccountAddress;
|
||||
|
||||
if (!tokenInformationResponse.IsSuccess)
|
||||
throw new Exception(tokenInformationResponse.Message);
|
||||
|
||||
createdAccount.Address = tokenInformationResponse.Data.AccountAddress;
|
||||
|
||||
tokenInformationResponse.ThrowIfFailed();
|
||||
}
|
||||
tokenInformationResponse.ThrowIfFailed();
|
||||
}
|
||||
}
|
||||
|
||||
// Address is still doesn't have a value for API synchronizers.
|
||||
// It'll be synchronized with profile information.
|
||||
// Address is still doesn't have a value for API synchronizers.
|
||||
// It'll be synchronized with profile information.
|
||||
|
||||
await AccountService.CreateAccountAsync(createdAccount, customServerInformation);
|
||||
await AccountService.CreateAccountAsync(createdAccount, customServerInformation);
|
||||
|
||||
// Local account has been created.
|
||||
// Local account has been created.
|
||||
|
||||
// Sync profile information if supported.
|
||||
if (createdAccount.IsProfileInfoSyncSupported)
|
||||
{
|
||||
// Start profile information synchronization.
|
||||
// It's only available for Outlook and Gmail synchronizers.
|
||||
// Sync profile information if supported.
|
||||
if (createdAccount.IsProfileInfoSyncSupported)
|
||||
{
|
||||
// Start profile information synchronization.
|
||||
// It's only available for Outlook and Gmail synchronizers.
|
||||
|
||||
var profileSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.UpdateProfile
|
||||
};
|
||||
|
||||
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
var profileSynchronizationResult = profileSynchronizationResponse.Data;
|
||||
|
||||
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
||||
|
||||
createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
|
||||
createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
|
||||
|
||||
if (!string.IsNullOrEmpty(profileSynchronizationResult.ProfileInformation.AccountAddress))
|
||||
{
|
||||
createdAccount.Address = profileSynchronizationResult.ProfileInformation.AccountAddress;
|
||||
}
|
||||
|
||||
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
|
||||
}
|
||||
|
||||
if (creationDialog is IImapAccountCreationDialog customServerAccountCreationDialog)
|
||||
customServerAccountCreationDialog.ShowPreparingFolders();
|
||||
else
|
||||
creationDialog.State = AccountCreationDialogState.PreparingFolders;
|
||||
|
||||
// Start synchronizing folders.
|
||||
var folderSyncOptions = new MailSynchronizationOptions()
|
||||
var profileSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.FoldersOnly
|
||||
Type = MailSynchronizationType.UpdateProfile
|
||||
};
|
||||
|
||||
var folderSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(folderSyncOptions, SynchronizationSource.Client));
|
||||
var profileSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(profileSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
var folderSynchronizationResult = folderSynchronizationResponse.Data;
|
||||
var profileSynchronizationResult = profileSynchronizationResponse.Data;
|
||||
|
||||
if (folderSynchronizationResult == null || folderSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||
if (profileSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
||||
|
||||
// Sync aliases if supported.
|
||||
if (createdAccount.IsAliasSyncSupported)
|
||||
createdAccount.SenderName = profileSynchronizationResult.ProfileInformation.SenderName;
|
||||
createdAccount.Base64ProfilePictureData = profileSynchronizationResult.ProfileInformation.Base64ProfilePictureData;
|
||||
|
||||
if (!string.IsNullOrEmpty(profileSynchronizationResult.ProfileInformation.AccountAddress))
|
||||
{
|
||||
// Try to synchronize aliases for the account.
|
||||
|
||||
var aliasSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.Alias
|
||||
};
|
||||
|
||||
var aliasSyncResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
|
||||
var aliasSynchronizationResult = folderSynchronizationResponse.Data;
|
||||
|
||||
if (aliasSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeAliases);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create root primary alias for the account.
|
||||
// This is only available for accounts that do not support alias synchronization.
|
||||
|
||||
await AccountService.CreateRootAliasAsync(createdAccount.Id, createdAccount.Address);
|
||||
createdAccount.Address = profileSynchronizationResult.ProfileInformation.AccountAddress;
|
||||
}
|
||||
|
||||
// Send changes to listeners.
|
||||
ReportUIChange(new AccountCreatedMessage(createdAccount));
|
||||
|
||||
// Notify success.
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreatedTitle, string.Format(Translator.Info_AccountCreatedMessage, createdAccount.Address), InfoBarMessageType.Success);
|
||||
await AccountService.UpdateProfileInformationAsync(createdAccount.Id, profileSynchronizationResult.ProfileInformation);
|
||||
}
|
||||
}
|
||||
catch (AccountSetupCanceledException)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains(nameof(AccountSetupCanceledException)))
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (ImapClientPoolException clientPoolException)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, clientPoolException.InnerException.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to create account.");
|
||||
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, ex.Message, InfoBarMessageType.Error);
|
||||
if (creationDialog is IImapAccountCreationDialog customServerAccountCreationDialog)
|
||||
customServerAccountCreationDialog.ShowPreparingFolders();
|
||||
else
|
||||
creationDialog.State = AccountCreationDialogState.PreparingFolders;
|
||||
|
||||
// Delete account in case of failure.
|
||||
if (createdAccount != null)
|
||||
// Start synchronizing folders.
|
||||
var folderSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
await AccountService.DeleteAccountAsync(createdAccount);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
creationDialog?.Complete(false);
|
||||
}
|
||||
}
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.FoldersOnly
|
||||
};
|
||||
|
||||
[RelayCommand]
|
||||
private void EditMergedAccounts(MergedAccountProviderDetailViewModel mergedAccountProviderDetailViewModel)
|
||||
{
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name,
|
||||
WinoPage.MergedAccountDetailsPage,
|
||||
mergedAccountProviderDetailViewModel));
|
||||
}
|
||||
var folderSynchronizationResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(folderSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanReorderAccounts))]
|
||||
private Task ReorderAccountsAsync() => MailDialogService.ShowAccountReorderDialogAsync(availableAccounts: Accounts);
|
||||
var folderSynchronizationResult = folderSynchronizationResponse.Data;
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
if (folderSynchronizationResult == null || folderSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||
|
||||
Accounts.CollectionChanged -= AccountCollectionChanged;
|
||||
|
||||
PropertyChanged -= PagePropertyChanged;
|
||||
}
|
||||
|
||||
private void AccountCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(HasAccountsDefined));
|
||||
OnPropertyChanged(nameof(UsedAccountsString));
|
||||
OnPropertyChanged(nameof(IsAccountCreationAlmostOnLimit));
|
||||
|
||||
ReorderAccountsCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void PagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(StartupAccount) && StartupAccount != null)
|
||||
{
|
||||
PreferencesService.StartupEntityId = StartupAccount.StartupEntityId;
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
Accounts.CollectionChanged -= AccountCollectionChanged;
|
||||
Accounts.CollectionChanged += AccountCollectionChanged;
|
||||
|
||||
await InitializeAccountsAsync();
|
||||
|
||||
PropertyChanged -= PagePropertyChanged;
|
||||
PropertyChanged += PagePropertyChanged;
|
||||
}
|
||||
|
||||
public override async Task InitializeAccountsAsync()
|
||||
{
|
||||
StartupAccount = null;
|
||||
|
||||
Accounts.Clear();
|
||||
|
||||
var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
// Group accounts and display merged ones at the top.
|
||||
var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
foreach (var accountGroup in groupedAccounts)
|
||||
// Sync aliases if supported.
|
||||
if (createdAccount.IsAliasSyncSupported)
|
||||
{
|
||||
var mergedInboxId = accountGroup.Key;
|
||||
// Try to synchronize aliases for the account.
|
||||
|
||||
if (mergedInboxId == null)
|
||||
var aliasSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
foreach (var account in accountGroup)
|
||||
{
|
||||
var accountDetails = GetAccountProviderDetails(account);
|
||||
AccountId = createdAccount.Id,
|
||||
Type = MailSynchronizationType.Alias
|
||||
};
|
||||
|
||||
Accounts.Add(accountDetails);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var mergedInbox = accountGroup.First(a => a.MergedInboxId == mergedInboxId).MergedInbox;
|
||||
var aliasSyncResponse = await WinoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
|
||||
var aliasSynchronizationResult = folderSynchronizationResponse.Data;
|
||||
|
||||
var holdingAccountProviderDetails = accountGroup.Select(a => GetAccountProviderDetails(a)).ToList();
|
||||
var mergedAccountViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, holdingAccountProviderDetails);
|
||||
|
||||
Accounts.Add(mergedAccountViewModel);
|
||||
}
|
||||
if (aliasSynchronizationResult.CompletedState != SynchronizationCompletedState.Success)
|
||||
throw new Exception(Translator.Exception_FailedToSynchronizeAliases);
|
||||
}
|
||||
|
||||
// Handle startup entity.
|
||||
if (PreferencesService.StartupEntityId != null)
|
||||
else
|
||||
{
|
||||
StartupAccount = Accounts.FirstOrDefault(a => a.StartupEntityId == PreferencesService.StartupEntityId);
|
||||
// Create root primary alias for the account.
|
||||
// This is only available for accounts that do not support alias synchronization.
|
||||
|
||||
await AccountService.CreateRootAliasAsync(createdAccount.Id, createdAccount.Address);
|
||||
}
|
||||
});
|
||||
|
||||
// Send changes to listeners.
|
||||
ReportUIChange(new AccountCreatedMessage(createdAccount));
|
||||
|
||||
await ManageStorePurchasesAsync().ConfigureAwait(false);
|
||||
// Notify success.
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreatedTitle, string.Format(Translator.Info_AccountCreatedMessage, createdAccount.Address), InfoBarMessageType.Success);
|
||||
}
|
||||
}
|
||||
catch (AccountSetupCanceledException)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains(nameof(AccountSetupCanceledException)))
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (ImapClientPoolException clientPoolException)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, clientPoolException.InnerException.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to create account.");
|
||||
|
||||
DialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, ex.Message, InfoBarMessageType.Error);
|
||||
|
||||
// Delete account in case of failure.
|
||||
if (createdAccount != null)
|
||||
{
|
||||
await AccountService.DeleteAccountAsync(createdAccount);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
creationDialog?.Complete(false);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void EditMergedAccounts(MergedAccountProviderDetailViewModel mergedAccountProviderDetailViewModel)
|
||||
{
|
||||
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name,
|
||||
WinoPage.MergedAccountDetailsPage,
|
||||
mergedAccountProviderDetailViewModel));
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanReorderAccounts))]
|
||||
private Task ReorderAccountsAsync() => MailDialogService.ShowAccountReorderDialogAsync(availableAccounts: Accounts);
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
Accounts.CollectionChanged -= AccountCollectionChanged;
|
||||
|
||||
PropertyChanged -= PagePropertyChanged;
|
||||
}
|
||||
|
||||
private void AccountCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(HasAccountsDefined));
|
||||
OnPropertyChanged(nameof(UsedAccountsString));
|
||||
OnPropertyChanged(nameof(IsAccountCreationAlmostOnLimit));
|
||||
|
||||
ReorderAccountsCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void PagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(StartupAccount) && StartupAccount != null)
|
||||
{
|
||||
PreferencesService.StartupEntityId = StartupAccount.StartupEntityId;
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
Accounts.CollectionChanged -= AccountCollectionChanged;
|
||||
Accounts.CollectionChanged += AccountCollectionChanged;
|
||||
|
||||
await InitializeAccountsAsync();
|
||||
|
||||
PropertyChanged -= PagePropertyChanged;
|
||||
PropertyChanged += PagePropertyChanged;
|
||||
}
|
||||
|
||||
public override async Task InitializeAccountsAsync()
|
||||
{
|
||||
StartupAccount = null;
|
||||
|
||||
Accounts.Clear();
|
||||
|
||||
var accounts = await AccountService.GetAccountsAsync().ConfigureAwait(false);
|
||||
|
||||
// Group accounts and display merged ones at the top.
|
||||
var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
foreach (var accountGroup in groupedAccounts)
|
||||
{
|
||||
var mergedInboxId = accountGroup.Key;
|
||||
|
||||
if (mergedInboxId == null)
|
||||
{
|
||||
foreach (var account in accountGroup)
|
||||
{
|
||||
var accountDetails = GetAccountProviderDetails(account);
|
||||
|
||||
Accounts.Add(accountDetails);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var mergedInbox = accountGroup.First(a => a.MergedInboxId == mergedInboxId).MergedInbox;
|
||||
|
||||
var holdingAccountProviderDetails = accountGroup.Select(a => GetAccountProviderDetails(a)).ToList();
|
||||
var mergedAccountViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, holdingAccountProviderDetails);
|
||||
|
||||
Accounts.Add(mergedAccountViewModel);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle startup entity.
|
||||
if (PreferencesService.StartupEntityId != null)
|
||||
{
|
||||
StartupAccount = Accounts.FirstOrDefault(a => a.StartupEntityId == PreferencesService.StartupEntityId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
await ManageStorePurchasesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,142 +14,141 @@ using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Synchronization;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class AliasManagementPageViewModel : MailBaseViewModel
|
||||
{
|
||||
public partial class AliasManagementPageViewModel : MailBaseViewModel
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(CanSynchronizeAliases))]
|
||||
private MailAccount account;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<MailAccountAlias> accountAliases = [];
|
||||
|
||||
public bool CanSynchronizeAliases => Account?.IsAliasSyncSupported ?? false;
|
||||
|
||||
public AliasManagementPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager)
|
||||
{
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_winoServerConnectionManager = winoServerConnectionManager;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(CanSynchronizeAliases))]
|
||||
private MailAccount account;
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
[ObservableProperty]
|
||||
private List<MailAccountAlias> accountAliases = [];
|
||||
if (parameters is Guid accountId)
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
public bool CanSynchronizeAliases => Account?.IsAliasSyncSupported ?? false;
|
||||
if (Account == null) return;
|
||||
|
||||
public AliasManagementPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager)
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAliasesAsync()
|
||||
{
|
||||
AccountAliases = await _accountService.GetAccountAliasesAsync(Account.Id);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SetAliasPrimaryAsync(MailAccountAlias alias)
|
||||
{
|
||||
if (alias.IsPrimary) return;
|
||||
|
||||
AccountAliases.ForEach(a =>
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_winoServerConnectionManager = winoServerConnectionManager;
|
||||
}
|
||||
a.IsPrimary = a == alias;
|
||||
});
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
await _accountService.UpdateAccountAliasesAsync(Account.Id, AccountAliases);
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SyncAliasesAsync()
|
||||
{
|
||||
if (!CanSynchronizeAliases) return;
|
||||
|
||||
var aliasSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
AccountId = Account.Id,
|
||||
Type = MailSynchronizationType.Alias
|
||||
};
|
||||
|
||||
if (parameters is Guid accountId)
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
if (Account == null) return;
|
||||
var aliasSyncResponse = await _winoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
if (aliasSyncResponse.IsSuccess)
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
else
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, aliasSyncResponse.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
|
||||
private async Task LoadAliasesAsync()
|
||||
[RelayCommand]
|
||||
private async Task AddNewAliasAsync()
|
||||
{
|
||||
var createdAliasDialog = await _dialogService.ShowCreateAccountAliasDialogAsync();
|
||||
|
||||
if (createdAliasDialog.CreatedAccountAlias == null) return;
|
||||
|
||||
var newAlias = createdAliasDialog.CreatedAccountAlias;
|
||||
|
||||
// Check existence.
|
||||
if (AccountAliases.Any(a => a.AliasAddress == newAlias.AliasAddress))
|
||||
{
|
||||
AccountAliases = await _accountService.GetAccountAliasesAsync(Account.Id);
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_AliasExistsTitle,
|
||||
Translator.DialogMessage_AliasExistsMessage,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SetAliasPrimaryAsync(MailAccountAlias alias)
|
||||
// Validate all addresses.
|
||||
if (!EmailValidator.Validate(newAlias.AliasAddress) || (!string.IsNullOrEmpty(newAlias.ReplyToAddress) && !EmailValidator.Validate(newAlias.ReplyToAddress)))
|
||||
{
|
||||
if (alias.IsPrimary) return;
|
||||
|
||||
AccountAliases.ForEach(a =>
|
||||
{
|
||||
a.IsPrimary = a == alias;
|
||||
});
|
||||
|
||||
await _accountService.UpdateAccountAliasesAsync(Account.Id, AccountAliases);
|
||||
await LoadAliasesAsync();
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_InvalidAliasMessage,
|
||||
Translator.DialogMessage_InvalidAliasTitle,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SyncAliasesAsync()
|
||||
newAlias.AccountId = Account.Id;
|
||||
|
||||
AccountAliases.Add(newAlias);
|
||||
|
||||
await _accountService.UpdateAccountAliasesAsync(Account.Id, AccountAliases);
|
||||
_dialogService.InfoBarMessage(Translator.DialogMessage_AliasCreatedTitle, Translator.DialogMessage_AliasCreatedMessage, InfoBarMessageType.Success);
|
||||
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAliasAsync(MailAccountAlias alias)
|
||||
{
|
||||
// Primary aliases can't be deleted.
|
||||
if (alias.IsPrimary)
|
||||
{
|
||||
if (!CanSynchronizeAliases) return;
|
||||
|
||||
var aliasSyncOptions = new MailSynchronizationOptions()
|
||||
{
|
||||
AccountId = Account.Id,
|
||||
Type = MailSynchronizationType.Alias
|
||||
};
|
||||
|
||||
var aliasSyncResponse = await _winoServerConnectionManager.GetResponseAsync<MailSynchronizationResult, NewMailSynchronizationRequested>(new NewMailSynchronizationRequested(aliasSyncOptions, SynchronizationSource.Client));
|
||||
|
||||
if (aliasSyncResponse.IsSuccess)
|
||||
await LoadAliasesAsync();
|
||||
else
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, aliasSyncResponse.Message, InfoBarMessageType.Error);
|
||||
await _dialogService.ShowMessageAsync(Translator.Info_CantDeletePrimaryAliasMessage,
|
||||
Translator.GeneralTitle_Warning,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddNewAliasAsync()
|
||||
// Root aliases can't be deleted.
|
||||
if (alias.IsRootAlias)
|
||||
{
|
||||
var createdAliasDialog = await _dialogService.ShowCreateAccountAliasDialogAsync();
|
||||
|
||||
if (createdAliasDialog.CreatedAccountAlias == null) return;
|
||||
|
||||
var newAlias = createdAliasDialog.CreatedAccountAlias;
|
||||
|
||||
// Check existence.
|
||||
if (AccountAliases.Any(a => a.AliasAddress == newAlias.AliasAddress))
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_AliasExistsTitle,
|
||||
Translator.DialogMessage_AliasExistsMessage,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate all addresses.
|
||||
if (!EmailValidator.Validate(newAlias.AliasAddress) || (!string.IsNullOrEmpty(newAlias.ReplyToAddress) && !EmailValidator.Validate(newAlias.ReplyToAddress)))
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_InvalidAliasMessage,
|
||||
Translator.DialogMessage_InvalidAliasTitle,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
newAlias.AccountId = Account.Id;
|
||||
|
||||
AccountAliases.Add(newAlias);
|
||||
|
||||
await _accountService.UpdateAccountAliasesAsync(Account.Id, AccountAliases);
|
||||
_dialogService.InfoBarMessage(Translator.DialogMessage_AliasCreatedTitle, Translator.DialogMessage_AliasCreatedMessage, InfoBarMessageType.Success);
|
||||
|
||||
await LoadAliasesAsync();
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_CantDeleteRootAliasTitle,
|
||||
Translator.DialogMessage_CantDeleteRootAliasMessage,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAliasAsync(MailAccountAlias alias)
|
||||
{
|
||||
// Primary aliases can't be deleted.
|
||||
if (alias.IsPrimary)
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(Translator.Info_CantDeletePrimaryAliasMessage,
|
||||
Translator.GeneralTitle_Warning,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// Root aliases can't be deleted.
|
||||
if (alias.IsRootAlias)
|
||||
{
|
||||
await _dialogService.ShowMessageAsync(Translator.DialogMessage_CantDeleteRootAliasTitle,
|
||||
Translator.DialogMessage_CantDeleteRootAliasMessage,
|
||||
WinoCustomMessageDialogIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await _accountService.DeleteAccountAliasAsync(alias.Id);
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
await _accountService.DeleteAccountAliasAsync(alias.Id);
|
||||
await LoadAliasesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,134 +9,133 @@ using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Messaging.Server;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
{
|
||||
public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _appTerminationBehavior;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorDisabled))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorEnabled))]
|
||||
private StartupBehaviorResult startupBehaviorResult;
|
||||
|
||||
public bool IsStartupBehaviorDisabled => !IsStartupBehaviorEnabled;
|
||||
public bool IsStartupBehaviorEnabled => StartupBehaviorResult == StartupBehaviorResult.Enabled;
|
||||
|
||||
private string _selectedAppTerminationBehavior;
|
||||
public string SelectedAppTerminationBehavior
|
||||
{
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _appTerminationBehavior;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorDisabled))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorEnabled))]
|
||||
private StartupBehaviorResult startupBehaviorResult;
|
||||
|
||||
public bool IsStartupBehaviorDisabled => !IsStartupBehaviorEnabled;
|
||||
public bool IsStartupBehaviorEnabled => StartupBehaviorResult == StartupBehaviorResult.Enabled;
|
||||
|
||||
private string _selectedAppTerminationBehavior;
|
||||
public string SelectedAppTerminationBehavior
|
||||
get => _selectedAppTerminationBehavior;
|
||||
set
|
||||
{
|
||||
get => _selectedAppTerminationBehavior;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedAppTerminationBehavior, value);
|
||||
SetProperty(ref _selectedAppTerminationBehavior, value);
|
||||
|
||||
PreferencesService.ServerTerminationBehavior = (ServerBackgroundMode)AppTerminationBehavior.IndexOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
|
||||
private readonly IStartupBehaviorService _startupBehaviorService;
|
||||
|
||||
public AppPreferencesPageViewModel(IMailDialogService dialogService,
|
||||
IPreferencesService preferencesService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
IStartupBehaviorService startupBehaviorService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
PreferencesService = preferencesService;
|
||||
_winoServerConnectionManager = winoServerConnectionManager;
|
||||
_startupBehaviorService = startupBehaviorService;
|
||||
|
||||
// Load the app termination behavior options
|
||||
|
||||
_appTerminationBehavior =
|
||||
[
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title, // "Minimize to tray"
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title, // "Invisible"
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title // "Terminate"
|
||||
];
|
||||
|
||||
SelectedAppTerminationBehavior = _appTerminationBehavior[(int)PreferencesService.ServerTerminationBehavior];
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleStartupBehaviorAsync()
|
||||
{
|
||||
if (IsStartupBehaviorEnabled)
|
||||
{
|
||||
await DisableStartupAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await EnableStartupAsync();
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(IsStartupBehaviorEnabled));
|
||||
}
|
||||
|
||||
private async Task EnableStartupAsync()
|
||||
{
|
||||
StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(true);
|
||||
|
||||
NotifyCurrentStartupState();
|
||||
}
|
||||
|
||||
private async Task DisableStartupAsync()
|
||||
{
|
||||
StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(false);
|
||||
|
||||
NotifyCurrentStartupState();
|
||||
}
|
||||
|
||||
private void NotifyCurrentStartupState()
|
||||
{
|
||||
if (StartupBehaviorResult == StartupBehaviorResult.Enabled)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_Enabled, InfoBarMessageType.Success);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.Disabled)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_Disabled, InfoBarMessageType.Warning);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.DisabledByPolicy)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_DisabledByPolicy, InfoBarMessageType.Warning);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.DisabledByUser)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_DisabledByUser, InfoBarMessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, Translator.SettingsAppPreferences_StartupBehavior_FatalError, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
if (e.PropertyName == nameof(SelectedAppTerminationBehavior))
|
||||
{
|
||||
var terminationModeChangedResult = await _winoServerConnectionManager.GetResponseAsync<bool, ServerTerminationModeChanged>(new ServerTerminationModeChanged(PreferencesService.ServerTerminationBehavior));
|
||||
|
||||
if (!terminationModeChangedResult.IsSuccess)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, terminationModeChangedResult.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
StartupBehaviorResult = await _startupBehaviorService.GetCurrentStartupBehaviorAsync();
|
||||
PreferencesService.ServerTerminationBehavior = (ServerBackgroundMode)AppTerminationBehavior.IndexOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
|
||||
private readonly IStartupBehaviorService _startupBehaviorService;
|
||||
|
||||
public AppPreferencesPageViewModel(IMailDialogService dialogService,
|
||||
IPreferencesService preferencesService,
|
||||
IWinoServerConnectionManager winoServerConnectionManager,
|
||||
IStartupBehaviorService startupBehaviorService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
PreferencesService = preferencesService;
|
||||
_winoServerConnectionManager = winoServerConnectionManager;
|
||||
_startupBehaviorService = startupBehaviorService;
|
||||
|
||||
// Load the app termination behavior options
|
||||
|
||||
_appTerminationBehavior =
|
||||
[
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_MinimizeTray_Title, // "Minimize to tray"
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_Invisible_Title, // "Invisible"
|
||||
Translator.SettingsAppPreferences_ServerBackgroundingMode_Terminate_Title // "Terminate"
|
||||
];
|
||||
|
||||
SelectedAppTerminationBehavior = _appTerminationBehavior[(int)PreferencesService.ServerTerminationBehavior];
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleStartupBehaviorAsync()
|
||||
{
|
||||
if (IsStartupBehaviorEnabled)
|
||||
{
|
||||
await DisableStartupAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await EnableStartupAsync();
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(IsStartupBehaviorEnabled));
|
||||
}
|
||||
|
||||
private async Task EnableStartupAsync()
|
||||
{
|
||||
StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(true);
|
||||
|
||||
NotifyCurrentStartupState();
|
||||
}
|
||||
|
||||
private async Task DisableStartupAsync()
|
||||
{
|
||||
StartupBehaviorResult = await _startupBehaviorService.ToggleStartupBehavior(false);
|
||||
|
||||
NotifyCurrentStartupState();
|
||||
}
|
||||
|
||||
private void NotifyCurrentStartupState()
|
||||
{
|
||||
if (StartupBehaviorResult == StartupBehaviorResult.Enabled)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_Enabled, InfoBarMessageType.Success);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.Disabled)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_Disabled, InfoBarMessageType.Warning);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.DisabledByPolicy)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_DisabledByPolicy, InfoBarMessageType.Warning);
|
||||
}
|
||||
else if (StartupBehaviorResult == StartupBehaviorResult.DisabledByUser)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Info, Translator.SettingsAppPreferences_StartupBehavior_DisabledByUser, InfoBarMessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, Translator.SettingsAppPreferences_StartupBehavior_FatalError, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
if (e.PropertyName == nameof(SelectedAppTerminationBehavior))
|
||||
{
|
||||
var terminationModeChangedResult = await _winoServerConnectionManager.GetResponseAsync<bool, ServerTerminationModeChanged>(new ServerTerminationModeChanged(PreferencesService.ServerTerminationBehavior));
|
||||
|
||||
if (!terminationModeChangedResult.IsSuccess)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, terminationModeChangedResult.Message, InfoBarMessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
StartupBehaviorResult = await _startupBehaviorService.GetCurrentStartupBehaviorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,506 +10,505 @@ using Wino.Core.Domain.Models.Comparers;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Collections
|
||||
namespace Wino.Mail.ViewModels.Collections;
|
||||
|
||||
public class WinoMailCollection
|
||||
{
|
||||
public class WinoMailCollection
|
||||
// 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.
|
||||
|
||||
public HashSet<Guid> MailCopyIdHashSet = [];
|
||||
|
||||
public event EventHandler<IMailItem> MailItemRemoved;
|
||||
|
||||
private ListItemComparer listComparer = new ListItemComparer();
|
||||
|
||||
private readonly ObservableGroupedCollection<object, IMailItem> _mailItemSource = new ObservableGroupedCollection<object, IMailItem>();
|
||||
|
||||
public ReadOnlyObservableGroupedCollection<object, IMailItem> MailItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Property that defines how the item sorting should be done in the collection.
|
||||
/// </summary>
|
||||
public SortingOptionType SortingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Threading strategy that will help thread items according to the account type.
|
||||
/// </summary>
|
||||
public IThreadingStrategyProvider ThreadingStrategyProvider { get; set; }
|
||||
|
||||
/// <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; }
|
||||
|
||||
public int Count => _mailItemSource.Count;
|
||||
|
||||
public IDispatcher CoreDispatcher { get; set; }
|
||||
|
||||
public WinoMailCollection()
|
||||
{
|
||||
// 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.
|
||||
MailItems = new ReadOnlyObservableGroupedCollection<object, IMailItem>(_mailItemSource);
|
||||
}
|
||||
|
||||
public HashSet<Guid> MailCopyIdHashSet = [];
|
||||
public void Clear() => _mailItemSource.Clear();
|
||||
|
||||
public event EventHandler<IMailItem> MailItemRemoved;
|
||||
private object GetGroupingKey(IMailItem mailItem)
|
||||
{
|
||||
if (SortingType == SortingOptionType.ReceiveDate)
|
||||
return mailItem.CreationDate.ToLocalTime().Date;
|
||||
else
|
||||
return mailItem.FromName;
|
||||
}
|
||||
|
||||
private ListItemComparer listComparer = new ListItemComparer();
|
||||
|
||||
private readonly ObservableGroupedCollection<object, IMailItem> _mailItemSource = new ObservableGroupedCollection<object, IMailItem>();
|
||||
|
||||
public ReadOnlyObservableGroupedCollection<object, IMailItem> MailItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Property that defines how the item sorting should be done in the collection.
|
||||
/// </summary>
|
||||
public SortingOptionType SortingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Threading strategy that will help thread items according to the account type.
|
||||
/// </summary>
|
||||
public IThreadingStrategyProvider ThreadingStrategyProvider { get; set; }
|
||||
|
||||
/// <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; }
|
||||
|
||||
public int Count => _mailItemSource.Count;
|
||||
|
||||
public IDispatcher CoreDispatcher { get; set; }
|
||||
|
||||
public WinoMailCollection()
|
||||
private void UpdateUniqueIdHashes(IMailHashContainer itemContainer, bool isAdd)
|
||||
{
|
||||
foreach (var item in itemContainer.GetContainingIds())
|
||||
{
|
||||
MailItems = new ReadOnlyObservableGroupedCollection<object, IMailItem>(_mailItemSource);
|
||||
}
|
||||
|
||||
public void Clear() => _mailItemSource.Clear();
|
||||
|
||||
private object GetGroupingKey(IMailItem mailItem)
|
||||
{
|
||||
if (SortingType == SortingOptionType.ReceiveDate)
|
||||
return mailItem.CreationDate.ToLocalTime().Date;
|
||||
else
|
||||
return mailItem.FromName;
|
||||
}
|
||||
|
||||
private void UpdateUniqueIdHashes(IMailHashContainer itemContainer, bool isAdd)
|
||||
{
|
||||
foreach (var item in itemContainer.GetContainingIds())
|
||||
if (isAdd)
|
||||
{
|
||||
if (isAdd)
|
||||
{
|
||||
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());
|
||||
MailCopyIdHashSet.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer());
|
||||
MailCopyIdHashSet.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveItemInternal(ObservableGroup<object, IMailItem> group, IMailItem mailItem)
|
||||
private void InsertItemInternal(object groupKey, IMailItem mailItem)
|
||||
{
|
||||
UpdateUniqueIdHashes(mailItem, true);
|
||||
|
||||
if (mailItem is MailCopy mailCopy)
|
||||
{
|
||||
UpdateUniqueIdHashes(mailItem, false);
|
||||
|
||||
MailItemRemoved?.Invoke(this, mailItem);
|
||||
|
||||
group.Remove(mailItem);
|
||||
|
||||
if (group.Count == 0)
|
||||
{
|
||||
_mailItemSource.RemoveGroup(group.Key);
|
||||
}
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, new MailItemViewModel(mailCopy), listComparer.GetItemComparer());
|
||||
}
|
||||
|
||||
public async Task AddAsync(MailCopy addedItem)
|
||||
else if (mailItem is ThreadMailItem threadMailItem)
|
||||
{
|
||||
// Check all items for whether this item should be threaded with them.
|
||||
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;
|
||||
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false)
|
||||
{
|
||||
shouldExit = true;
|
||||
|
||||
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
// Item belongs to existing thread.
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
var existingGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.AddMailItemViewModel(addedItem); });
|
||||
|
||||
var newGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
if (!existingGroupKey.Equals(newGroupKey))
|
||||
{
|
||||
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));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
||||
}
|
||||
|
||||
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(() =>
|
||||
{
|
||||
threadMailItem.AddThreadItem(item);
|
||||
threadMailItem.AddThreadItem(addedItem);
|
||||
|
||||
if (threadMailItem.ThreadItems.Count == 1) return;
|
||||
|
||||
var newGroupKey = GetGroupingKey(threadMailItem);
|
||||
|
||||
RemoveItemInternal(group, item);
|
||||
InsertItemInternal(newGroupKey, threadMailItem);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update properties.
|
||||
if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
||||
|
||||
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); });
|
||||
}
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, new ThreadMailItemViewModel(threadMailItem), listComparer.GetItemComparer());
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<IMailItem> items, bool clearIdCache)
|
||||
else
|
||||
{
|
||||
if (clearIdCache)
|
||||
{
|
||||
MailCopyIdHashSet.Clear();
|
||||
}
|
||||
|
||||
var groupedByName = items
|
||||
.GroupBy(a => GetGroupingKey(a))
|
||||
.Select(a => new ObservableGroup<object, IMailItem>(a.Key, a));
|
||||
|
||||
foreach (var group in groupedByName)
|
||||
{
|
||||
// Store all mail copy ids for faster access.
|
||||
foreach (var item in group)
|
||||
{
|
||||
if (item is MailItemViewModel mailCopyItem && !MailCopyIdHashSet.Contains(item.UniqueId))
|
||||
{
|
||||
MailCopyIdHashSet.Add(item.UniqueId);
|
||||
}
|
||||
else if (item is ThreadMailItemViewModel threadMailItem)
|
||||
{
|
||||
foreach (var mailItem in threadMailItem.ThreadItems)
|
||||
{
|
||||
if (!MailCopyIdHashSet.Contains(mailItem.UniqueId))
|
||||
{
|
||||
MailCopyIdHashSet.Add(mailItem.UniqueId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingGroup = _mailItemSource.FirstGroupByKeyOrDefault(group.Key);
|
||||
|
||||
if (existingGroup == null)
|
||||
{
|
||||
_mailItemSource.AddGroup(group.Key, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in group)
|
||||
{
|
||||
existingGroup.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
_mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer());
|
||||
}
|
||||
}
|
||||
|
||||
public MailItemContainer GetMailItemContainer(Guid uniqueMailId)
|
||||
private void RemoveItemInternal(ObservableGroup<object, IMailItem> group, IMailItem mailItem)
|
||||
{
|
||||
UpdateUniqueIdHashes(mailItem, false);
|
||||
|
||||
MailItemRemoved?.Invoke(this, mailItem);
|
||||
|
||||
group.Remove(mailItem);
|
||||
|
||||
if (group.Count == 0)
|
||||
{
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == uniqueMailId)
|
||||
return new MailItemContainer(singleMailItemViewModel);
|
||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(uniqueMailId))
|
||||
{
|
||||
var singleItemViewModel = threadMailItemViewModel.GetItemById(uniqueMailId) as MailItemViewModel;
|
||||
|
||||
return new MailItemContainer(singleItemViewModel, threadMailItemViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
_mailItemSource.RemoveGroup(group.Key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
public async Task AddAsync(MailCopy addedItem)
|
||||
{
|
||||
// Check all items for whether this item should be threaded with them.
|
||||
bool shouldExit = false;
|
||||
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
var addedAccountProviderType = addedItem.AssignedAccount.ProviderType;
|
||||
var threadingStrategy = ThreadingStrategyProvider?.GetStrategy(addedAccountProviderType);
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
// This item doesn't exist in the list.
|
||||
if (!MailCopyIdHashSet.Contains(updatedMailCopy.UniqueId))
|
||||
if (shouldExit) break;
|
||||
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var item = group[k];
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId);
|
||||
|
||||
if (itemContainer == null) return;
|
||||
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
|
||||
}
|
||||
shouldExit = true;
|
||||
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
itemContainer.ItemViewModel.MailCopy = updatedMailCopy;
|
||||
}
|
||||
|
||||
UpdateUniqueIdHashes(updatedMailCopy, true);
|
||||
|
||||
// Call thread notifications if possible.
|
||||
itemContainer.ThreadViewModel?.NotifyPropertyChanges();
|
||||
});
|
||||
}
|
||||
|
||||
public MailItemViewModel GetNextItem(MailCopy mailCopy)
|
||||
{
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == mailCopy.UniqueId)
|
||||
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailCopy.UniqueId))
|
||||
{
|
||||
var singleItemViewModel = threadMailItemViewModel.GetItemById(mailCopy.UniqueId) as MailItemViewModel;
|
||||
// Item belongs to existing thread.
|
||||
|
||||
if (singleItemViewModel == null) return null;
|
||||
|
||||
var singleItemIndex = threadMailItemViewModel.ThreadItems.IndexOf(singleItemViewModel);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(MailCopy removeItem)
|
||||
{
|
||||
// This item doesn't exist in the list.
|
||||
if (!MailCopyIdHashSet.Contains(removeItem.UniqueId)) return;
|
||||
|
||||
// Check all items for whether this item should be threaded with them.
|
||||
bool shouldExit = false;
|
||||
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
if (shouldExit) break;
|
||||
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(removeItem.UniqueId))
|
||||
{
|
||||
var removalItem = threadMailItemViewModel.GetItemById(removeItem.UniqueId);
|
||||
|
||||
if (removalItem == null) return;
|
||||
|
||||
// Threads' Id is equal to the last item they hold.
|
||||
// We can't do Id check here because that'd remove the whole thread.
|
||||
|
||||
/* 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.
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
var oldGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
var existingGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.RemoveCopyItem(removalItem); });
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.AddMailItemViewModel(addedItem); });
|
||||
|
||||
if (threadMailItemViewModel.ThreadItems.Count == 1)
|
||||
var newGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
if (!existingGroupKey.Equals(newGroupKey))
|
||||
{
|
||||
// Convert to single item.
|
||||
|
||||
var singleViewModel = threadMailItemViewModel.GetSingleItemViewModel();
|
||||
var groupKey = GetGroupingKey(singleViewModel);
|
||||
var mailThreadItems = threadMailItemViewModel.GetThreadMailItem();
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
// Group must be changed for this thread.
|
||||
// Remove the thread first.
|
||||
|
||||
RemoveItemInternal(group, threadMailItemViewModel);
|
||||
InsertItemInternal(groupKey, singleViewModel);
|
||||
|
||||
// Insert new view model because the previous one might've been deleted with the group.
|
||||
InsertItemInternal(newGroupKey, new ThreadMailItemViewModel(mailThreadItems));
|
||||
});
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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 ExecuteUIThread(() => { RemoveItemInternal(group, threadMailItemViewModel); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item inside the thread is removed.
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.ThreadItems.Remove(removalItem); });
|
||||
|
||||
UpdateUniqueIdHashes(removalItem, false);
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); });
|
||||
}
|
||||
|
||||
shouldExit = true;
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (item.UniqueId == removeItem.UniqueId)
|
||||
else
|
||||
{
|
||||
await ExecuteUIThread(() => { RemoveItemInternal(group, item); });
|
||||
// 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.
|
||||
|
||||
shouldExit = true;
|
||||
/* 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(() =>
|
||||
{
|
||||
threadMailItem.AddThreadItem(item);
|
||||
threadMailItem.AddThreadItem(addedItem);
|
||||
|
||||
if (threadMailItem.ThreadItems.Count == 1) return;
|
||||
|
||||
var newGroupKey = GetGroupingKey(threadMailItem);
|
||||
|
||||
RemoveItemInternal(group, item);
|
||||
InsertItemInternal(newGroupKey, threadMailItem);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update properties.
|
||||
if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
||||
|
||||
shouldExit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
|
||||
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); });
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<IMailItem> items, bool clearIdCache)
|
||||
{
|
||||
if (clearIdCache)
|
||||
{
|
||||
MailCopyIdHashSet.Clear();
|
||||
}
|
||||
|
||||
var groupedByName = items
|
||||
.GroupBy(a => GetGroupingKey(a))
|
||||
.Select(a => new ObservableGroup<object, IMailItem>(a.Key, a));
|
||||
|
||||
foreach (var group in groupedByName)
|
||||
{
|
||||
// Store all mail copy ids for faster access.
|
||||
foreach (var item in group)
|
||||
{
|
||||
if (item is MailItemViewModel mailCopyItem && !MailCopyIdHashSet.Contains(item.UniqueId))
|
||||
{
|
||||
MailCopyIdHashSet.Add(item.UniqueId);
|
||||
}
|
||||
else if (item is ThreadMailItemViewModel threadMailItem)
|
||||
{
|
||||
foreach (var mailItem in threadMailItem.ThreadItems)
|
||||
{
|
||||
if (!MailCopyIdHashSet.Contains(mailItem.UniqueId))
|
||||
{
|
||||
MailCopyIdHashSet.Add(mailItem.UniqueId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingGroup = _mailItemSource.FirstGroupByKeyOrDefault(group.Key);
|
||||
|
||||
if (existingGroup == null)
|
||||
{
|
||||
_mailItemSource.AddGroup(group.Key, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in group)
|
||||
{
|
||||
existingGroup.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MailItemContainer GetMailItemContainer(Guid uniqueMailId)
|
||||
{
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == uniqueMailId)
|
||||
return new MailItemContainer(singleMailItemViewModel);
|
||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(uniqueMailId))
|
||||
{
|
||||
var singleItemViewModel = threadMailItemViewModel.GetItemById(uniqueMailId) as MailItemViewModel;
|
||||
|
||||
return new MailItemContainer(singleItemViewModel, threadMailItemViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId);
|
||||
|
||||
if (itemContainer == null) return;
|
||||
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
|
||||
}
|
||||
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
itemContainer.ItemViewModel.MailCopy = updatedMailCopy;
|
||||
}
|
||||
|
||||
UpdateUniqueIdHashes(updatedMailCopy, true);
|
||||
|
||||
// Call thread notifications if possible.
|
||||
itemContainer.ThreadViewModel?.NotifyPropertyChanges();
|
||||
});
|
||||
}
|
||||
|
||||
public MailItemViewModel GetNextItem(MailCopy mailCopy)
|
||||
{
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == mailCopy.UniqueId)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailCopy.UniqueId))
|
||||
{
|
||||
var singleItemViewModel = threadMailItemViewModel.GetItemById(mailCopy.UniqueId) as MailItemViewModel;
|
||||
|
||||
if (singleItemViewModel == null) return null;
|
||||
|
||||
var singleItemIndex = threadMailItemViewModel.ThreadItems.IndexOf(singleItemViewModel);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(MailCopy removeItem)
|
||||
{
|
||||
// This item doesn't exist in the list.
|
||||
if (!MailCopyIdHashSet.Contains(removeItem.UniqueId)) return;
|
||||
|
||||
// Check all items for whether this item should be threaded with them.
|
||||
bool shouldExit = false;
|
||||
|
||||
var groupCount = _mailItemSource.Count;
|
||||
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
{
|
||||
if (shouldExit) break;
|
||||
|
||||
var group = _mailItemSource[i];
|
||||
|
||||
for (int k = 0; k < group.Count; k++)
|
||||
{
|
||||
var item = group[k];
|
||||
|
||||
if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(removeItem.UniqueId))
|
||||
{
|
||||
var removalItem = threadMailItemViewModel.GetItemById(removeItem.UniqueId);
|
||||
|
||||
if (removalItem == null) return;
|
||||
|
||||
// Threads' Id is equal to the last item they hold.
|
||||
// We can't do Id check here because that'd remove the whole thread.
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
var oldGroupKey = GetGroupingKey(threadMailItemViewModel);
|
||||
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.RemoveCopyItem(removalItem); });
|
||||
|
||||
if (threadMailItemViewModel.ThreadItems.Count == 1)
|
||||
{
|
||||
// Convert to single item.
|
||||
|
||||
var singleViewModel = threadMailItemViewModel.GetSingleItemViewModel();
|
||||
var groupKey = GetGroupingKey(singleViewModel);
|
||||
|
||||
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)
|
||||
{
|
||||
// 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 ExecuteUIThread(() => { RemoveItemInternal(group, threadMailItemViewModel); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item inside the thread is removed.
|
||||
await ExecuteUIThread(() => { threadMailItemViewModel.ThreadItems.Remove(removalItem); });
|
||||
|
||||
UpdateUniqueIdHashes(removalItem, false);
|
||||
}
|
||||
|
||||
shouldExit = true;
|
||||
break;
|
||||
}
|
||||
else if (item.UniqueId == removeItem.UniqueId)
|
||||
{
|
||||
await ExecuteUIThread(() => { RemoveItemInternal(group, item); });
|
||||
|
||||
shouldExit = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,32 +2,31 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Data
|
||||
namespace Wino.Mail.ViewModels.Data;
|
||||
|
||||
[DebuggerDisplay("{FolderTitle}")]
|
||||
public partial class FolderPivotViewModel : ObservableObject
|
||||
{
|
||||
[DebuggerDisplay("{FolderTitle}")]
|
||||
public partial class FolderPivotViewModel : ObservableObject
|
||||
public bool? IsFocused { get; set; }
|
||||
public string FolderTitle { get; }
|
||||
|
||||
public bool ShouldDisplaySelectedItemCount => IsExtendedMode ? SelectedItemCount > 1 : SelectedItemCount > 0;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSelected;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))]
|
||||
private int selectedItemCount;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))]
|
||||
private bool isExtendedMode = true;
|
||||
|
||||
public FolderPivotViewModel(string folderName, bool? isFocused)
|
||||
{
|
||||
public bool? IsFocused { get; set; }
|
||||
public string FolderTitle { get; }
|
||||
IsFocused = isFocused;
|
||||
|
||||
public bool ShouldDisplaySelectedItemCount => IsExtendedMode ? SelectedItemCount > 1 : SelectedItemCount > 0;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSelected;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))]
|
||||
private int selectedItemCount;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))]
|
||||
private bool isExtendedMode = true;
|
||||
|
||||
public FolderPivotViewModel(string folderName, bool? isFocused)
|
||||
{
|
||||
IsFocused = isFocused;
|
||||
|
||||
FolderTitle = IsFocused == null ? folderName : (IsFocused == true ? Translator.Focused : Translator.Other);
|
||||
}
|
||||
FolderTitle = IsFocused == null ? folderName : (IsFocused == true ? Translator.Focused : Translator.Other);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,94 +5,93 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Common;
|
||||
using Wino.Core.Extensions;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Data
|
||||
namespace Wino.Mail.ViewModels.Data;
|
||||
|
||||
public partial class MailAttachmentViewModel : ObservableObject
|
||||
{
|
||||
public partial class MailAttachmentViewModel : ObservableObject
|
||||
private readonly MimePart _mimePart;
|
||||
|
||||
public MailAttachmentType AttachmentType { get; }
|
||||
public string FileName { get; }
|
||||
public string FilePath { get; set; }
|
||||
public string ReadableSize { get; }
|
||||
public byte[] Content { get; set; }
|
||||
|
||||
public IMimeContent MimeContent => _mimePart.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether attachment is busy with opening or saving etc.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool isBusy;
|
||||
|
||||
public MailAttachmentViewModel(MimePart mimePart)
|
||||
{
|
||||
private readonly MimePart _mimePart;
|
||||
_mimePart = mimePart;
|
||||
|
||||
public MailAttachmentType AttachmentType { get; }
|
||||
public string FileName { get; }
|
||||
public string FilePath { get; set; }
|
||||
public string ReadableSize { get; }
|
||||
public byte[] Content { get; set; }
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
public IMimeContent MimeContent => _mimePart.Content;
|
||||
using (memoryStream) mimePart.Content.DecodeTo(memoryStream);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether attachment is busy with opening or saving etc.
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool isBusy;
|
||||
Content = memoryStream.ToArray();
|
||||
|
||||
public MailAttachmentViewModel(MimePart mimePart)
|
||||
FileName = mimePart.FileName;
|
||||
ReadableSize = ((long)Content.Length).GetBytesReadable();
|
||||
|
||||
var extension = Path.GetExtension(FileName);
|
||||
AttachmentType = GetAttachmentType(extension);
|
||||
}
|
||||
|
||||
public MailAttachmentViewModel(SharedFile sharedFile)
|
||||
{
|
||||
Content = sharedFile.Data;
|
||||
|
||||
FileName = sharedFile.FileName;
|
||||
FilePath = sharedFile.FullFilePath;
|
||||
|
||||
ReadableSize = ((long)sharedFile.Data.Length).GetBytesReadable();
|
||||
|
||||
var extension = Path.GetExtension(FileName);
|
||||
AttachmentType = GetAttachmentType(extension);
|
||||
}
|
||||
|
||||
public MailAttachmentType GetAttachmentType(string mediaSubtype)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mediaSubtype))
|
||||
return MailAttachmentType.None;
|
||||
|
||||
switch (mediaSubtype.ToLower())
|
||||
{
|
||||
_mimePart = mimePart;
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
using (memoryStream) mimePart.Content.DecodeTo(memoryStream);
|
||||
|
||||
Content = memoryStream.ToArray();
|
||||
|
||||
FileName = mimePart.FileName;
|
||||
ReadableSize = ((long)Content.Length).GetBytesReadable();
|
||||
|
||||
var extension = Path.GetExtension(FileName);
|
||||
AttachmentType = GetAttachmentType(extension);
|
||||
}
|
||||
|
||||
public MailAttachmentViewModel(SharedFile sharedFile)
|
||||
{
|
||||
Content = sharedFile.Data;
|
||||
|
||||
FileName = sharedFile.FileName;
|
||||
FilePath = sharedFile.FullFilePath;
|
||||
|
||||
ReadableSize = ((long)sharedFile.Data.Length).GetBytesReadable();
|
||||
|
||||
var extension = Path.GetExtension(FileName);
|
||||
AttachmentType = GetAttachmentType(extension);
|
||||
}
|
||||
|
||||
public MailAttachmentType GetAttachmentType(string mediaSubtype)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mediaSubtype))
|
||||
return MailAttachmentType.None;
|
||||
|
||||
switch (mediaSubtype.ToLower())
|
||||
{
|
||||
case ".exe":
|
||||
return MailAttachmentType.Executable;
|
||||
case ".rar":
|
||||
return MailAttachmentType.RarArchive;
|
||||
case ".zip":
|
||||
return MailAttachmentType.Archive;
|
||||
case ".ogg":
|
||||
case ".mp3":
|
||||
case ".wav":
|
||||
case ".aac":
|
||||
case ".alac":
|
||||
return MailAttachmentType.Audio;
|
||||
case ".mp4":
|
||||
case ".wmv":
|
||||
case ".avi":
|
||||
case ".flv":
|
||||
return MailAttachmentType.Video;
|
||||
case ".pdf":
|
||||
return MailAttachmentType.PDF;
|
||||
case ".htm":
|
||||
case ".html":
|
||||
return MailAttachmentType.HTML;
|
||||
case ".png":
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
case ".gif":
|
||||
case ".jiff":
|
||||
return MailAttachmentType.Image;
|
||||
default:
|
||||
return MailAttachmentType.Other;
|
||||
}
|
||||
case ".exe":
|
||||
return MailAttachmentType.Executable;
|
||||
case ".rar":
|
||||
return MailAttachmentType.RarArchive;
|
||||
case ".zip":
|
||||
return MailAttachmentType.Archive;
|
||||
case ".ogg":
|
||||
case ".mp3":
|
||||
case ".wav":
|
||||
case ".aac":
|
||||
case ".alac":
|
||||
return MailAttachmentType.Audio;
|
||||
case ".mp4":
|
||||
case ".wmv":
|
||||
case ".avi":
|
||||
case ".flv":
|
||||
return MailAttachmentType.Video;
|
||||
case ".pdf":
|
||||
return MailAttachmentType.PDF;
|
||||
case ".htm":
|
||||
case ".html":
|
||||
return MailAttachmentType.HTML;
|
||||
case ".png":
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
case ".gif":
|
||||
case ".jiff":
|
||||
return MailAttachmentType.Image;
|
||||
default:
|
||||
return MailAttachmentType.Other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Data
|
||||
namespace Wino.Mail.ViewModels.Data;
|
||||
|
||||
public class MailItemContainer
|
||||
{
|
||||
public class MailItemContainer
|
||||
public MailItemViewModel ItemViewModel { get; set; }
|
||||
public ThreadMailItemViewModel ThreadViewModel { get; set; }
|
||||
|
||||
public MailItemContainer(MailItemViewModel itemViewModel, ThreadMailItemViewModel threadViewModel) : this(itemViewModel)
|
||||
{
|
||||
public MailItemViewModel ItemViewModel { get; set; }
|
||||
public ThreadMailItemViewModel ThreadViewModel { get; set; }
|
||||
ThreadViewModel = threadViewModel ?? throw new ArgumentNullException(nameof(threadViewModel));
|
||||
}
|
||||
|
||||
public MailItemContainer(MailItemViewModel itemViewModel, ThreadMailItemViewModel threadViewModel) : this(itemViewModel)
|
||||
{
|
||||
ThreadViewModel = threadViewModel ?? throw new ArgumentNullException(nameof(threadViewModel));
|
||||
}
|
||||
|
||||
public MailItemContainer(MailItemViewModel itemViewModel)
|
||||
{
|
||||
ItemViewModel = itemViewModel ?? throw new ArgumentNullException(nameof(itemViewModel));
|
||||
}
|
||||
public MailItemContainer(MailItemViewModel itemViewModel)
|
||||
{
|
||||
ItemViewModel = itemViewModel ?? throw new ArgumentNullException(nameof(itemViewModel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,103 +5,102 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Data
|
||||
namespace Wino.Mail.ViewModels.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Single view model for IMailItem representation.
|
||||
/// </summary>
|
||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Single view model for IMailItem representation.
|
||||
/// </summary>
|
||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailItem
|
||||
[ObservableProperty]
|
||||
private MailCopy mailCopy = mailCopy;
|
||||
|
||||
public Guid UniqueId => ((IMailItem)MailCopy).UniqueId;
|
||||
public string ThreadId => ((IMailItem)MailCopy).ThreadId;
|
||||
public string MessageId => ((IMailItem)MailCopy).MessageId;
|
||||
public DateTime CreationDate => ((IMailItem)MailCopy).CreationDate;
|
||||
public string References => ((IMailItem)MailCopy).References;
|
||||
public string InReplyTo => ((IMailItem)MailCopy).InReplyTo;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isCustomFocused;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSelected;
|
||||
|
||||
public bool IsFlagged
|
||||
{
|
||||
[ObservableProperty]
|
||||
private MailCopy mailCopy = mailCopy;
|
||||
|
||||
public Guid UniqueId => ((IMailItem)MailCopy).UniqueId;
|
||||
public string ThreadId => ((IMailItem)MailCopy).ThreadId;
|
||||
public string MessageId => ((IMailItem)MailCopy).MessageId;
|
||||
public DateTime CreationDate => ((IMailItem)MailCopy).CreationDate;
|
||||
public string References => ((IMailItem)MailCopy).References;
|
||||
public string InReplyTo => ((IMailItem)MailCopy).InReplyTo;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isCustomFocused;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSelected;
|
||||
|
||||
public bool IsFlagged
|
||||
{
|
||||
get => MailCopy.IsFlagged;
|
||||
set => SetProperty(MailCopy.IsFlagged, value, MailCopy, (u, n) => u.IsFlagged = n);
|
||||
}
|
||||
|
||||
public string FromName
|
||||
{
|
||||
get => string.IsNullOrEmpty(MailCopy.FromName) ? MailCopy.FromAddress : MailCopy.FromName;
|
||||
set => SetProperty(MailCopy.FromName, value, MailCopy, (u, n) => u.FromName = n);
|
||||
}
|
||||
|
||||
public bool IsFocused
|
||||
{
|
||||
get => MailCopy.IsFocused;
|
||||
set => SetProperty(MailCopy.IsFocused, value, MailCopy, (u, n) => u.IsFocused = n);
|
||||
}
|
||||
|
||||
public bool IsRead
|
||||
{
|
||||
get => MailCopy.IsRead;
|
||||
set => SetProperty(MailCopy.IsRead, value, MailCopy, (u, n) => u.IsRead = n);
|
||||
}
|
||||
|
||||
public bool IsDraft
|
||||
{
|
||||
get => MailCopy.IsDraft;
|
||||
set => SetProperty(MailCopy.IsDraft, value, MailCopy, (u, n) => u.IsDraft = n);
|
||||
}
|
||||
|
||||
public string DraftId
|
||||
{
|
||||
get => MailCopy.DraftId;
|
||||
set => SetProperty(MailCopy.DraftId, value, MailCopy, (u, n) => u.DraftId = n);
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => MailCopy.Id;
|
||||
set => SetProperty(MailCopy.Id, value, MailCopy, (u, n) => u.Id = n);
|
||||
}
|
||||
|
||||
public string Subject
|
||||
{
|
||||
get => MailCopy.Subject;
|
||||
set => SetProperty(MailCopy.Subject, value, MailCopy, (u, n) => u.Subject = n);
|
||||
}
|
||||
|
||||
public string PreviewText
|
||||
{
|
||||
get => MailCopy.PreviewText;
|
||||
set => SetProperty(MailCopy.PreviewText, value, MailCopy, (u, n) => u.PreviewText = n);
|
||||
}
|
||||
|
||||
public string FromAddress
|
||||
{
|
||||
get => MailCopy.FromAddress;
|
||||
set => SetProperty(MailCopy.FromAddress, value, MailCopy, (u, n) => u.FromAddress = n);
|
||||
}
|
||||
|
||||
public bool HasAttachments
|
||||
{
|
||||
get => MailCopy.HasAttachments;
|
||||
set => SetProperty(MailCopy.HasAttachments, value, MailCopy, (u, n) => u.HasAttachments = n);
|
||||
}
|
||||
|
||||
public MailItemFolder AssignedFolder => ((IMailItem)MailCopy).AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => ((IMailItem)MailCopy).AssignedAccount;
|
||||
|
||||
public Guid FileId => ((IMailItem)MailCopy).FileId;
|
||||
|
||||
public AccountContact SenderContact => ((IMailItem)MailCopy).SenderContact;
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => new[] { UniqueId };
|
||||
get => MailCopy.IsFlagged;
|
||||
set => SetProperty(MailCopy.IsFlagged, value, MailCopy, (u, n) => u.IsFlagged = n);
|
||||
}
|
||||
|
||||
public string FromName
|
||||
{
|
||||
get => string.IsNullOrEmpty(MailCopy.FromName) ? MailCopy.FromAddress : MailCopy.FromName;
|
||||
set => SetProperty(MailCopy.FromName, value, MailCopy, (u, n) => u.FromName = n);
|
||||
}
|
||||
|
||||
public bool IsFocused
|
||||
{
|
||||
get => MailCopy.IsFocused;
|
||||
set => SetProperty(MailCopy.IsFocused, value, MailCopy, (u, n) => u.IsFocused = n);
|
||||
}
|
||||
|
||||
public bool IsRead
|
||||
{
|
||||
get => MailCopy.IsRead;
|
||||
set => SetProperty(MailCopy.IsRead, value, MailCopy, (u, n) => u.IsRead = n);
|
||||
}
|
||||
|
||||
public bool IsDraft
|
||||
{
|
||||
get => MailCopy.IsDraft;
|
||||
set => SetProperty(MailCopy.IsDraft, value, MailCopy, (u, n) => u.IsDraft = n);
|
||||
}
|
||||
|
||||
public string DraftId
|
||||
{
|
||||
get => MailCopy.DraftId;
|
||||
set => SetProperty(MailCopy.DraftId, value, MailCopy, (u, n) => u.DraftId = n);
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get => MailCopy.Id;
|
||||
set => SetProperty(MailCopy.Id, value, MailCopy, (u, n) => u.Id = n);
|
||||
}
|
||||
|
||||
public string Subject
|
||||
{
|
||||
get => MailCopy.Subject;
|
||||
set => SetProperty(MailCopy.Subject, value, MailCopy, (u, n) => u.Subject = n);
|
||||
}
|
||||
|
||||
public string PreviewText
|
||||
{
|
||||
get => MailCopy.PreviewText;
|
||||
set => SetProperty(MailCopy.PreviewText, value, MailCopy, (u, n) => u.PreviewText = n);
|
||||
}
|
||||
|
||||
public string FromAddress
|
||||
{
|
||||
get => MailCopy.FromAddress;
|
||||
set => SetProperty(MailCopy.FromAddress, value, MailCopy, (u, n) => u.FromAddress = n);
|
||||
}
|
||||
|
||||
public bool HasAttachments
|
||||
{
|
||||
get => MailCopy.HasAttachments;
|
||||
set => SetProperty(MailCopy.HasAttachments, value, MailCopy, (u, n) => u.HasAttachments = n);
|
||||
}
|
||||
|
||||
public MailItemFolder AssignedFolder => ((IMailItem)MailCopy).AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => ((IMailItem)MailCopy).AssignedAccount;
|
||||
|
||||
public Guid FileId => ((IMailItem)MailCopy).FileId;
|
||||
|
||||
public AccountContact SenderContact => ((IMailItem)MailCopy).SenderContact;
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => new[] { UniqueId };
|
||||
}
|
||||
|
||||
@@ -8,120 +8,119 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Data
|
||||
namespace Wino.Mail.ViewModels.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Thread mail item (multiple IMailItem) view model representation.
|
||||
/// </summary>
|
||||
public partial class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable<string>, IComparable<DateTime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread mail item (multiple IMailItem) view model representation.
|
||||
/// </summary>
|
||||
public partial class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable<string>, IComparable<DateTime>
|
||||
public ObservableCollection<IMailItem> ThreadItems => (MailItem as IMailItemThread)?.ThreadItems ?? [];
|
||||
public AccountContact SenderContact => ((IMailItemThread)MailItem).SenderContact;
|
||||
|
||||
[ObservableProperty]
|
||||
private ThreadMailItem mailItem;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isThreadExpanded;
|
||||
|
||||
public ThreadMailItemViewModel(ThreadMailItem threadMailItem)
|
||||
{
|
||||
public ObservableCollection<IMailItem> ThreadItems => (MailItem as IMailItemThread)?.ThreadItems ?? [];
|
||||
public AccountContact SenderContact => ((IMailItemThread)MailItem).SenderContact;
|
||||
MailItem = new ThreadMailItem();
|
||||
|
||||
[ObservableProperty]
|
||||
private ThreadMailItem mailItem;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isThreadExpanded;
|
||||
|
||||
public ThreadMailItemViewModel(ThreadMailItem threadMailItem)
|
||||
// Local copies
|
||||
foreach (var item in threadMailItem.ThreadItems)
|
||||
{
|
||||
MailItem = new ThreadMailItem();
|
||||
|
||||
// Local copies
|
||||
foreach (var item in threadMailItem.ThreadItems)
|
||||
{
|
||||
AddMailItemViewModel(item);
|
||||
}
|
||||
AddMailItemViewModel(item);
|
||||
}
|
||||
|
||||
public ThreadMailItem GetThreadMailItem() => MailItem;
|
||||
|
||||
public IEnumerable<MailCopy> GetMailCopies()
|
||||
=> ThreadItems.OfType<MailItemViewModel>().Select(a => a.MailCopy);
|
||||
|
||||
public void AddMailItemViewModel(IMailItem mailItem)
|
||||
{
|
||||
if (mailItem == null) return;
|
||||
|
||||
if (mailItem is MailCopy mailCopy)
|
||||
MailItem.AddThreadItem(new MailItemViewModel(mailCopy));
|
||||
else if (mailItem is MailItemViewModel mailItemViewModel)
|
||||
MailItem.AddThreadItem(mailItemViewModel);
|
||||
else
|
||||
Debugger.Break();
|
||||
}
|
||||
|
||||
public bool HasUniqueId(Guid uniqueMailId)
|
||||
=> ThreadItems.Any(a => a.UniqueId == uniqueMailId);
|
||||
|
||||
public IMailItem GetItemById(Guid uniqueMailId)
|
||||
=> ThreadItems.FirstOrDefault(a => a.UniqueId == uniqueMailId);
|
||||
|
||||
public void RemoveCopyItem(IMailItem item)
|
||||
{
|
||||
MailCopy copyToRemove = null;
|
||||
|
||||
if (item is MailItemViewModel mailItemViewModel)
|
||||
copyToRemove = mailItemViewModel.MailCopy;
|
||||
else if (item is MailCopy copyItem)
|
||||
copyToRemove = copyItem;
|
||||
|
||||
var existedItem = ThreadItems.FirstOrDefault(a => a.Id == copyToRemove.Id);
|
||||
|
||||
if (existedItem == null) return;
|
||||
|
||||
ThreadItems.Remove(existedItem);
|
||||
|
||||
NotifyPropertyChanges();
|
||||
}
|
||||
|
||||
public void NotifyPropertyChanges()
|
||||
{
|
||||
// TODO
|
||||
// Stupid temporary fix for not updating UI.
|
||||
// This view model must be reworked with ThreadMailItem together.
|
||||
|
||||
var current = MailItem;
|
||||
|
||||
MailItem = null;
|
||||
MailItem = current;
|
||||
}
|
||||
|
||||
public IMailItem LatestMailItem => ((IMailItemThread)MailItem).LatestMailItem;
|
||||
public IMailItem FirstMailItem => ((IMailItemThread)MailItem).FirstMailItem;
|
||||
|
||||
public string Id => ((IMailItem)MailItem).Id;
|
||||
public string Subject => ((IMailItem)MailItem).Subject;
|
||||
public string ThreadId => ((IMailItem)MailItem).ThreadId;
|
||||
public string MessageId => ((IMailItem)MailItem).MessageId;
|
||||
public string References => ((IMailItem)MailItem).References;
|
||||
public string PreviewText => ((IMailItem)MailItem).PreviewText;
|
||||
public string FromName => ((IMailItem)MailItem).FromName;
|
||||
public DateTime CreationDate => ((IMailItem)MailItem).CreationDate;
|
||||
public string FromAddress => ((IMailItem)MailItem).FromAddress;
|
||||
public bool HasAttachments => ((IMailItem)MailItem).HasAttachments;
|
||||
public bool IsFlagged => ((IMailItem)MailItem).IsFlagged;
|
||||
public bool IsFocused => ((IMailItem)MailItem).IsFocused;
|
||||
public bool IsRead => ((IMailItem)MailItem).IsRead;
|
||||
public bool IsDraft => ((IMailItem)MailItem).IsDraft;
|
||||
public string DraftId => string.Empty;
|
||||
public string InReplyTo => ((IMailItem)MailItem).InReplyTo;
|
||||
|
||||
public MailItemFolder AssignedFolder => ((IMailItem)MailItem).AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => ((IMailItem)MailItem).AssignedAccount;
|
||||
|
||||
public Guid UniqueId => ((IMailItem)MailItem).UniqueId;
|
||||
|
||||
public Guid FileId => ((IMailItem)MailItem).FileId;
|
||||
|
||||
public int CompareTo(DateTime other) => CreationDate.CompareTo(other);
|
||||
public int CompareTo(string other) => FromName.CompareTo(other);
|
||||
|
||||
// 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)MailItem).GetContainingIds();
|
||||
}
|
||||
|
||||
public ThreadMailItem GetThreadMailItem() => MailItem;
|
||||
|
||||
public IEnumerable<MailCopy> GetMailCopies()
|
||||
=> ThreadItems.OfType<MailItemViewModel>().Select(a => a.MailCopy);
|
||||
|
||||
public void AddMailItemViewModel(IMailItem mailItem)
|
||||
{
|
||||
if (mailItem == null) return;
|
||||
|
||||
if (mailItem is MailCopy mailCopy)
|
||||
MailItem.AddThreadItem(new MailItemViewModel(mailCopy));
|
||||
else if (mailItem is MailItemViewModel mailItemViewModel)
|
||||
MailItem.AddThreadItem(mailItemViewModel);
|
||||
else
|
||||
Debugger.Break();
|
||||
}
|
||||
|
||||
public bool HasUniqueId(Guid uniqueMailId)
|
||||
=> ThreadItems.Any(a => a.UniqueId == uniqueMailId);
|
||||
|
||||
public IMailItem GetItemById(Guid uniqueMailId)
|
||||
=> ThreadItems.FirstOrDefault(a => a.UniqueId == uniqueMailId);
|
||||
|
||||
public void RemoveCopyItem(IMailItem item)
|
||||
{
|
||||
MailCopy copyToRemove = null;
|
||||
|
||||
if (item is MailItemViewModel mailItemViewModel)
|
||||
copyToRemove = mailItemViewModel.MailCopy;
|
||||
else if (item is MailCopy copyItem)
|
||||
copyToRemove = copyItem;
|
||||
|
||||
var existedItem = ThreadItems.FirstOrDefault(a => a.Id == copyToRemove.Id);
|
||||
|
||||
if (existedItem == null) return;
|
||||
|
||||
ThreadItems.Remove(existedItem);
|
||||
|
||||
NotifyPropertyChanges();
|
||||
}
|
||||
|
||||
public void NotifyPropertyChanges()
|
||||
{
|
||||
// TODO
|
||||
// Stupid temporary fix for not updating UI.
|
||||
// This view model must be reworked with ThreadMailItem together.
|
||||
|
||||
var current = MailItem;
|
||||
|
||||
MailItem = null;
|
||||
MailItem = current;
|
||||
}
|
||||
|
||||
public IMailItem LatestMailItem => ((IMailItemThread)MailItem).LatestMailItem;
|
||||
public IMailItem FirstMailItem => ((IMailItemThread)MailItem).FirstMailItem;
|
||||
|
||||
public string Id => ((IMailItem)MailItem).Id;
|
||||
public string Subject => ((IMailItem)MailItem).Subject;
|
||||
public string ThreadId => ((IMailItem)MailItem).ThreadId;
|
||||
public string MessageId => ((IMailItem)MailItem).MessageId;
|
||||
public string References => ((IMailItem)MailItem).References;
|
||||
public string PreviewText => ((IMailItem)MailItem).PreviewText;
|
||||
public string FromName => ((IMailItem)MailItem).FromName;
|
||||
public DateTime CreationDate => ((IMailItem)MailItem).CreationDate;
|
||||
public string FromAddress => ((IMailItem)MailItem).FromAddress;
|
||||
public bool HasAttachments => ((IMailItem)MailItem).HasAttachments;
|
||||
public bool IsFlagged => ((IMailItem)MailItem).IsFlagged;
|
||||
public bool IsFocused => ((IMailItem)MailItem).IsFocused;
|
||||
public bool IsRead => ((IMailItem)MailItem).IsRead;
|
||||
public bool IsDraft => ((IMailItem)MailItem).IsDraft;
|
||||
public string DraftId => string.Empty;
|
||||
public string InReplyTo => ((IMailItem)MailItem).InReplyTo;
|
||||
|
||||
public MailItemFolder AssignedFolder => ((IMailItem)MailItem).AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => ((IMailItem)MailItem).AssignedAccount;
|
||||
|
||||
public Guid UniqueId => ((IMailItem)MailItem).UniqueId;
|
||||
|
||||
public Guid FileId => ((IMailItem)MailItem).FileId;
|
||||
|
||||
public int CompareTo(DateTime other) => CreationDate.CompareTo(other);
|
||||
public int CompareTo(string other) => FromName.CompareTo(other);
|
||||
|
||||
// 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)MailItem).GetContainingIds();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class IdlePageViewModel : CoreBaseViewModel
|
||||
{
|
||||
public partial class IdlePageViewModel : CoreBaseViewModel
|
||||
{
|
||||
public IdlePageViewModel(IMailDialogService dialogService) { }
|
||||
}
|
||||
public IdlePageViewModel(IMailDialogService dialogService) { }
|
||||
}
|
||||
|
||||
@@ -6,42 +6,41 @@ using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Translations;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class LanguageTimePageViewModel(IPreferencesService preferencesService, ITranslationService translationService) : MailBaseViewModel
|
||||
{
|
||||
public partial class LanguageTimePageViewModel(IPreferencesService preferencesService, ITranslationService translationService) : MailBaseViewModel
|
||||
public IPreferencesService PreferencesService { get; } = preferencesService;
|
||||
private readonly ITranslationService _translationService = translationService;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<AppLanguageModel> _availableLanguages;
|
||||
|
||||
[ObservableProperty]
|
||||
private AppLanguageModel _selectedLanguage;
|
||||
|
||||
private bool isInitialized = false;
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
public IPreferencesService PreferencesService { get; } = preferencesService;
|
||||
private readonly ITranslationService _translationService = translationService;
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
[ObservableProperty]
|
||||
private List<AppLanguageModel> _availableLanguages;
|
||||
AvailableLanguages = _translationService.GetAvailableLanguages();
|
||||
|
||||
[ObservableProperty]
|
||||
private AppLanguageModel _selectedLanguage;
|
||||
SelectedLanguage = AvailableLanguages.FirstOrDefault(a => a.Language == PreferencesService.CurrentLanguage);
|
||||
|
||||
private bool isInitialized = false;
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
if (!isInitialized) return;
|
||||
|
||||
if (e.PropertyName == nameof(SelectedLanguage))
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
AvailableLanguages = _translationService.GetAvailableLanguages();
|
||||
|
||||
SelectedLanguage = AvailableLanguages.FirstOrDefault(a => a.Language == PreferencesService.CurrentLanguage);
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
if (!isInitialized) return;
|
||||
|
||||
if (e.PropertyName == nameof(SelectedLanguage))
|
||||
{
|
||||
await _translationService.InitializeLanguageAsync(SelectedLanguage.Language);
|
||||
}
|
||||
await _translationService.InitializeLanguageAsync(SelectedLanguage.Language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,39 +5,38 @@ using Wino.Core.Domain.Models.Folders;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public class MailBaseViewModel : CoreBaseViewModel,
|
||||
IRecipient<MailAddedMessage>,
|
||||
IRecipient<MailRemovedMessage>,
|
||||
IRecipient<MailUpdatedMessage>,
|
||||
IRecipient<MailDownloadedMessage>,
|
||||
IRecipient<DraftCreated>,
|
||||
IRecipient<DraftFailed>,
|
||||
IRecipient<DraftMapped>,
|
||||
IRecipient<FolderRenamed>,
|
||||
IRecipient<FolderSynchronizationEnabled>
|
||||
{
|
||||
public class MailBaseViewModel : CoreBaseViewModel,
|
||||
IRecipient<MailAddedMessage>,
|
||||
IRecipient<MailRemovedMessage>,
|
||||
IRecipient<MailUpdatedMessage>,
|
||||
IRecipient<MailDownloadedMessage>,
|
||||
IRecipient<DraftCreated>,
|
||||
IRecipient<DraftFailed>,
|
||||
IRecipient<DraftMapped>,
|
||||
IRecipient<FolderRenamed>,
|
||||
IRecipient<FolderSynchronizationEnabled>
|
||||
{
|
||||
protected virtual void OnMailAdded(MailCopy addedMail) { }
|
||||
protected virtual void OnMailRemoved(MailCopy removedMail) { }
|
||||
protected virtual void OnMailUpdated(MailCopy updatedMail) { }
|
||||
protected virtual void OnMailDownloaded(MailCopy downloadedMail) { }
|
||||
protected virtual void OnDraftCreated(MailCopy draftMail, MailAccount account) { }
|
||||
protected virtual void OnDraftFailed(MailCopy draftMail, MailAccount account) { }
|
||||
protected virtual void OnDraftMapped(string localDraftCopyId, string remoteDraftCopyId) { }
|
||||
protected virtual void OnFolderRenamed(IMailItemFolder mailItemFolder) { }
|
||||
protected virtual void OnFolderSynchronizationEnabled(IMailItemFolder mailItemFolder) { }
|
||||
protected virtual void OnMailAdded(MailCopy addedMail) { }
|
||||
protected virtual void OnMailRemoved(MailCopy removedMail) { }
|
||||
protected virtual void OnMailUpdated(MailCopy updatedMail) { }
|
||||
protected virtual void OnMailDownloaded(MailCopy downloadedMail) { }
|
||||
protected virtual void OnDraftCreated(MailCopy draftMail, MailAccount account) { }
|
||||
protected virtual void OnDraftFailed(MailCopy draftMail, MailAccount account) { }
|
||||
protected virtual void OnDraftMapped(string localDraftCopyId, string remoteDraftCopyId) { }
|
||||
protected virtual void OnFolderRenamed(IMailItemFolder mailItemFolder) { }
|
||||
protected virtual void OnFolderSynchronizationEnabled(IMailItemFolder mailItemFolder) { }
|
||||
|
||||
void IRecipient<MailAddedMessage>.Receive(MailAddedMessage message) => OnMailAdded(message.AddedMail);
|
||||
void IRecipient<MailRemovedMessage>.Receive(MailRemovedMessage message) => OnMailRemoved(message.RemovedMail);
|
||||
void IRecipient<MailUpdatedMessage>.Receive(MailUpdatedMessage message) => OnMailUpdated(message.UpdatedMail);
|
||||
void IRecipient<MailDownloadedMessage>.Receive(MailDownloadedMessage message) => OnMailDownloaded(message.DownloadedMail);
|
||||
void IRecipient<MailAddedMessage>.Receive(MailAddedMessage message) => OnMailAdded(message.AddedMail);
|
||||
void IRecipient<MailRemovedMessage>.Receive(MailRemovedMessage message) => OnMailRemoved(message.RemovedMail);
|
||||
void IRecipient<MailUpdatedMessage>.Receive(MailUpdatedMessage message) => OnMailUpdated(message.UpdatedMail);
|
||||
void IRecipient<MailDownloadedMessage>.Receive(MailDownloadedMessage message) => OnMailDownloaded(message.DownloadedMail);
|
||||
|
||||
void IRecipient<DraftMapped>.Receive(DraftMapped message) => OnDraftMapped(message.LocalDraftCopyId, message.RemoteDraftCopyId);
|
||||
void IRecipient<DraftFailed>.Receive(DraftFailed message) => OnDraftFailed(message.DraftMail, message.Account);
|
||||
void IRecipient<DraftCreated>.Receive(DraftCreated message) => OnDraftCreated(message.DraftMail, message.Account);
|
||||
void IRecipient<DraftMapped>.Receive(DraftMapped message) => OnDraftMapped(message.LocalDraftCopyId, message.RemoteDraftCopyId);
|
||||
void IRecipient<DraftFailed>.Receive(DraftFailed message) => OnDraftFailed(message.DraftMail, message.Account);
|
||||
void IRecipient<DraftCreated>.Receive(DraftCreated message) => OnDraftCreated(message.DraftMail, message.Account);
|
||||
|
||||
void IRecipient<FolderRenamed>.Receive(FolderRenamed message) => OnFolderRenamed(message.MailItemFolder);
|
||||
void IRecipient<FolderSynchronizationEnabled>.Receive(FolderSynchronizationEnabled message) => OnFolderSynchronizationEnabled(message.MailItemFolder);
|
||||
}
|
||||
void IRecipient<FolderRenamed>.Receive(FolderRenamed message) => OnFolderRenamed(message.MailItemFolder);
|
||||
void IRecipient<FolderSynchronizationEnabled>.Receive(FolderSynchronizationEnabled message) => OnFolderSynchronizationEnabled(message.MailItemFolder);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,13 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wino.Core;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public static class MailViewModelsContainerSetup
|
||||
{
|
||||
public static class MailViewModelsContainerSetup
|
||||
public static void RegisterViewModelService(this IServiceCollection services)
|
||||
{
|
||||
public static void RegisterViewModelService(this IServiceCollection services)
|
||||
{
|
||||
// View models use core services.
|
||||
services.RegisterCoreServices();
|
||||
}
|
||||
// View models use core services.
|
||||
services.RegisterCoreServices();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,208 +13,207 @@ using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class MergedAccountDetailsPageViewModel : MailBaseViewModel,
|
||||
IRecipient<MergedInboxRenamed>
|
||||
{
|
||||
public partial class MergedAccountDetailsPageViewModel : MailBaseViewModel,
|
||||
IRecipient<MergedInboxRenamed>
|
||||
[ObservableProperty]
|
||||
private MergedAccountProviderDetailViewModel editingMergedAccount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string mergedAccountName;
|
||||
|
||||
public ObservableCollection<AccountProviderDetailViewModel> LinkedAccounts { get; set; } = [];
|
||||
public ObservableCollection<AccountProviderDetailViewModel> UnlinkedAccounts { get; set; } = [];
|
||||
|
||||
// Empty Guid is passed for new created merged inboxes.
|
||||
public bool IsMergedInboxSaved => EditingMergedAccount != null && EditingMergedAccount.MergedInbox.Id != Guid.Empty;
|
||||
|
||||
public bool CanUnlink => IsMergedInboxSaved;
|
||||
|
||||
// There must be at least 2 accounts linked to a merged account for link to exist.
|
||||
public bool ShouldDeleteMergedAccount => LinkedAccounts.Count < 2;
|
||||
|
||||
public bool CanSaveChanges
|
||||
{
|
||||
[ObservableProperty]
|
||||
private MergedAccountProviderDetailViewModel editingMergedAccount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string mergedAccountName;
|
||||
|
||||
public ObservableCollection<AccountProviderDetailViewModel> LinkedAccounts { get; set; } = [];
|
||||
public ObservableCollection<AccountProviderDetailViewModel> UnlinkedAccounts { get; set; } = [];
|
||||
|
||||
// Empty Guid is passed for new created merged inboxes.
|
||||
public bool IsMergedInboxSaved => EditingMergedAccount != null && EditingMergedAccount.MergedInbox.Id != Guid.Empty;
|
||||
|
||||
public bool CanUnlink => IsMergedInboxSaved;
|
||||
|
||||
// There must be at least 2 accounts linked to a merged account for link to exist.
|
||||
public bool ShouldDeleteMergedAccount => LinkedAccounts.Count < 2;
|
||||
|
||||
public bool CanSaveChanges
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsMergedInboxSaved)
|
||||
{
|
||||
return ShouldDeleteMergedAccount || IsEditingAccountsDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
return LinkedAccounts.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly IProviderService _providerService;
|
||||
|
||||
public MergedAccountDetailsPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IPreferencesService preferencesService,
|
||||
IProviderService providerService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_preferencesService = preferencesService;
|
||||
_providerService = providerService;
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanUnlink))]
|
||||
private async Task UnlinkAccountsAsync()
|
||||
{
|
||||
if (EditingMergedAccount == null) return;
|
||||
|
||||
var isConfirmed = await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_UnlinkAccountsConfirmationMessage, Translator.DialogMessage_UnlinkAccountsConfirmationTitle, Translator.Buttons_Yes);
|
||||
|
||||
if (!isConfirmed) return;
|
||||
|
||||
await _accountService.UnlinkMergedInboxAsync(EditingMergedAccount.MergedInbox.Id);
|
||||
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanSaveChanges))]
|
||||
private async Task SaveChangesAsync()
|
||||
{
|
||||
if (ShouldDeleteMergedAccount)
|
||||
{
|
||||
await UnlinkAccountsAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsMergedInboxSaved)
|
||||
{
|
||||
await _accountService.UpdateMergedInboxAsync(EditingMergedAccount.MergedInbox.Id, LinkedAccounts.Select(a => a.Account.Id).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
await _accountService.CreateMergeAccountsAsync(EditingMergedAccount.MergedInbox, LinkedAccounts.Select(a => a.Account).ToList());
|
||||
}
|
||||
|
||||
// Startup entity is linked now. Change the startup entity.
|
||||
if (_preferencesService.StartupEntityId != null && LinkedAccounts.Any(a => a.StartupEntityId == _preferencesService.StartupEntityId))
|
||||
{
|
||||
_preferencesService.StartupEntityId = EditingMergedAccount.MergedInbox.Id;
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RenameLinkAsync()
|
||||
{
|
||||
if (EditingMergedAccount == null) return;
|
||||
|
||||
var newName = await _dialogService.ShowTextInputDialogAsync(EditingMergedAccount.MergedInbox.Name,
|
||||
Translator.DialogMessage_RenameLinkedAccountsTitle,
|
||||
Translator.DialogMessage_RenameLinkedAccountsMessage,
|
||||
Translator.FolderOperation_Rename);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(newName)) return;
|
||||
|
||||
EditingMergedAccount.MergedInbox.Name = newName;
|
||||
|
||||
// Update database record as well.
|
||||
if (IsMergedInboxSaved)
|
||||
{
|
||||
await _accountService.RenameMergedAccountAsync(EditingMergedAccount.MergedInbox.Id, newName);
|
||||
return ShouldDeleteMergedAccount || IsEditingAccountsDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Publish the message manually since the merged inbox is not saved yet.
|
||||
// This is only for breadcrump item update.
|
||||
|
||||
Messenger.Send(new MergedInboxRenamed(EditingMergedAccount.MergedInbox.Id, newName));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LinkAccount(AccountProviderDetailViewModel account)
|
||||
{
|
||||
LinkedAccounts.Add(account);
|
||||
UnlinkedAccounts.Remove(account);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void UnlinkAccount(AccountProviderDetailViewModel account)
|
||||
{
|
||||
UnlinkedAccounts.Add(account);
|
||||
LinkedAccounts.Remove(account);
|
||||
}
|
||||
|
||||
private bool IsEditingAccountsDirty()
|
||||
{
|
||||
if (EditingMergedAccount == null) return false;
|
||||
|
||||
return EditingMergedAccount.HoldingAccounts.Count != LinkedAccounts.Count ||
|
||||
EditingMergedAccount.HoldingAccounts.Any(a => !LinkedAccounts.Any(la => la.Account.Id == a.Account.Id));
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated;
|
||||
}
|
||||
|
||||
private void LinkedAccountsUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(ShouldDeleteMergedAccount));
|
||||
SaveChangesCommand.NotifyCanExecuteChanged();
|
||||
|
||||
// TODO: Preview common folders for all linked accounts.
|
||||
// Basically showing a preview of how menu items will look.
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated;
|
||||
LinkedAccounts.CollectionChanged += LinkedAccountsUpdated;
|
||||
|
||||
if (parameters is MergedAccountProviderDetailViewModel editingMergedAccount)
|
||||
{
|
||||
MergedAccountName = editingMergedAccount.MergedInbox.Name;
|
||||
EditingMergedAccount = editingMergedAccount;
|
||||
|
||||
foreach (var account in editingMergedAccount.HoldingAccounts)
|
||||
{
|
||||
LinkedAccounts.Add(account);
|
||||
}
|
||||
|
||||
// Load unlinked accounts.
|
||||
|
||||
var allAccounts = await _accountService.GetAccountsAsync();
|
||||
|
||||
foreach (var account in allAccounts)
|
||||
{
|
||||
if (!LinkedAccounts.Any(a => a.Account.Id == account.Id))
|
||||
{
|
||||
var provider = _providerService.GetProviderDetail(account.ProviderType);
|
||||
|
||||
UnlinkedAccounts.Add(new AccountProviderDetailViewModel(provider, account));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnlinkAccountsCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void Receive(MergedInboxRenamed message)
|
||||
{
|
||||
if (EditingMergedAccount?.MergedInbox.Id == message.MergedInboxId)
|
||||
{
|
||||
EditingMergedAccount.MergedInbox.Name = message.NewName;
|
||||
return LinkedAccounts.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IPreferencesService _preferencesService;
|
||||
private readonly IProviderService _providerService;
|
||||
|
||||
public MergedAccountDetailsPageViewModel(IMailDialogService dialogService,
|
||||
IAccountService accountService,
|
||||
IPreferencesService preferencesService,
|
||||
IProviderService providerService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_accountService = accountService;
|
||||
_preferencesService = preferencesService;
|
||||
_providerService = providerService;
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanUnlink))]
|
||||
private async Task UnlinkAccountsAsync()
|
||||
{
|
||||
if (EditingMergedAccount == null) return;
|
||||
|
||||
var isConfirmed = await _dialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_UnlinkAccountsConfirmationMessage, Translator.DialogMessage_UnlinkAccountsConfirmationTitle, Translator.Buttons_Yes);
|
||||
|
||||
if (!isConfirmed) return;
|
||||
|
||||
await _accountService.UnlinkMergedInboxAsync(EditingMergedAccount.MergedInbox.Id);
|
||||
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanSaveChanges))]
|
||||
private async Task SaveChangesAsync()
|
||||
{
|
||||
if (ShouldDeleteMergedAccount)
|
||||
{
|
||||
await UnlinkAccountsAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsMergedInboxSaved)
|
||||
{
|
||||
await _accountService.UpdateMergedInboxAsync(EditingMergedAccount.MergedInbox.Id, LinkedAccounts.Select(a => a.Account.Id).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
await _accountService.CreateMergeAccountsAsync(EditingMergedAccount.MergedInbox, LinkedAccounts.Select(a => a.Account).ToList());
|
||||
}
|
||||
|
||||
// Startup entity is linked now. Change the startup entity.
|
||||
if (_preferencesService.StartupEntityId != null && LinkedAccounts.Any(a => a.StartupEntityId == _preferencesService.StartupEntityId))
|
||||
{
|
||||
_preferencesService.StartupEntityId = EditingMergedAccount.MergedInbox.Id;
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.Send(new BackBreadcrumNavigationRequested());
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RenameLinkAsync()
|
||||
{
|
||||
if (EditingMergedAccount == null) return;
|
||||
|
||||
var newName = await _dialogService.ShowTextInputDialogAsync(EditingMergedAccount.MergedInbox.Name,
|
||||
Translator.DialogMessage_RenameLinkedAccountsTitle,
|
||||
Translator.DialogMessage_RenameLinkedAccountsMessage,
|
||||
Translator.FolderOperation_Rename);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(newName)) return;
|
||||
|
||||
EditingMergedAccount.MergedInbox.Name = newName;
|
||||
|
||||
// Update database record as well.
|
||||
if (IsMergedInboxSaved)
|
||||
{
|
||||
await _accountService.RenameMergedAccountAsync(EditingMergedAccount.MergedInbox.Id, newName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Publish the message manually since the merged inbox is not saved yet.
|
||||
// This is only for breadcrump item update.
|
||||
|
||||
Messenger.Send(new MergedInboxRenamed(EditingMergedAccount.MergedInbox.Id, newName));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LinkAccount(AccountProviderDetailViewModel account)
|
||||
{
|
||||
LinkedAccounts.Add(account);
|
||||
UnlinkedAccounts.Remove(account);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void UnlinkAccount(AccountProviderDetailViewModel account)
|
||||
{
|
||||
UnlinkedAccounts.Add(account);
|
||||
LinkedAccounts.Remove(account);
|
||||
}
|
||||
|
||||
private bool IsEditingAccountsDirty()
|
||||
{
|
||||
if (EditingMergedAccount == null) return false;
|
||||
|
||||
return EditingMergedAccount.HoldingAccounts.Count != LinkedAccounts.Count ||
|
||||
EditingMergedAccount.HoldingAccounts.Any(a => !LinkedAccounts.Any(la => la.Account.Id == a.Account.Id));
|
||||
}
|
||||
|
||||
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedFrom(mode, parameters);
|
||||
|
||||
LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated;
|
||||
}
|
||||
|
||||
private void LinkedAccountsUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(nameof(ShouldDeleteMergedAccount));
|
||||
SaveChangesCommand.NotifyCanExecuteChanged();
|
||||
|
||||
// TODO: Preview common folders for all linked accounts.
|
||||
// Basically showing a preview of how menu items will look.
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated;
|
||||
LinkedAccounts.CollectionChanged += LinkedAccountsUpdated;
|
||||
|
||||
if (parameters is MergedAccountProviderDetailViewModel editingMergedAccount)
|
||||
{
|
||||
MergedAccountName = editingMergedAccount.MergedInbox.Name;
|
||||
EditingMergedAccount = editingMergedAccount;
|
||||
|
||||
foreach (var account in editingMergedAccount.HoldingAccounts)
|
||||
{
|
||||
LinkedAccounts.Add(account);
|
||||
}
|
||||
|
||||
// Load unlinked accounts.
|
||||
|
||||
var allAccounts = await _accountService.GetAccountsAsync();
|
||||
|
||||
foreach (var account in allAccounts)
|
||||
{
|
||||
if (!LinkedAccounts.Any(a => a.Account.Id == account.Id))
|
||||
{
|
||||
var provider = _providerService.GetProviderDetail(account.ProviderType);
|
||||
|
||||
UnlinkedAccounts.Add(new AccountProviderDetailViewModel(provider, account));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnlinkAccountsCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void Receive(MergedInboxRenamed message)
|
||||
{
|
||||
if (EditingMergedAccount?.MergedInbox.Id == message.MergedInboxId)
|
||||
{
|
||||
EditingMergedAccount.MergedInbox.Name = message.NewName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,104 +4,103 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public class MessageListPageViewModel : MailBaseViewModel
|
||||
{
|
||||
public class MessageListPageViewModel : MailBaseViewModel
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
private int selectedMarkAsOptionIndex;
|
||||
|
||||
public int SelectedMarkAsOptionIndex
|
||||
{
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
|
||||
private int selectedMarkAsOptionIndex;
|
||||
|
||||
public int SelectedMarkAsOptionIndex
|
||||
get => selectedMarkAsOptionIndex;
|
||||
set
|
||||
{
|
||||
get => selectedMarkAsOptionIndex;
|
||||
set
|
||||
if (SetProperty(ref selectedMarkAsOptionIndex, value))
|
||||
{
|
||||
if (SetProperty(ref selectedMarkAsOptionIndex, value))
|
||||
if (value >= 0)
|
||||
{
|
||||
if (value >= 0)
|
||||
{
|
||||
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues<MailMarkAsOption>().GetValue(value);
|
||||
}
|
||||
PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues<MailMarkAsOption>().GetValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<MailOperation> availableHoverActions =
|
||||
[
|
||||
MailOperation.Archive,
|
||||
MailOperation.SoftDelete,
|
||||
MailOperation.SetFlag,
|
||||
MailOperation.MarkAsRead,
|
||||
MailOperation.MoveToJunk
|
||||
];
|
||||
|
||||
public List<string> AvailableHoverActionsTranslations { get; set; } =
|
||||
[
|
||||
Translator.HoverActionOption_Archive,
|
||||
Translator.HoverActionOption_Delete,
|
||||
Translator.HoverActionOption_ToggleFlag,
|
||||
Translator.HoverActionOption_ToggleRead,
|
||||
Translator.HoverActionOption_MoveJunk
|
||||
];
|
||||
|
||||
#region Properties
|
||||
|
||||
private int leftHoverActionIndex;
|
||||
|
||||
public int LeftHoverActionIndex
|
||||
{
|
||||
get => leftHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref leftHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.LeftHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int centerHoverActionIndex;
|
||||
|
||||
public int CenterHoverActionIndex
|
||||
{
|
||||
get => centerHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref centerHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.CenterHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int rightHoverActionIndex;
|
||||
|
||||
public int RightHoverActionIndex
|
||||
{
|
||||
get => rightHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref rightHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.RightHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public MessageListPageViewModel(IMailDialogService dialogService,
|
||||
IPreferencesService preferencesService)
|
||||
{
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction);
|
||||
centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction);
|
||||
rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction);
|
||||
|
||||
SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues<MailMarkAsOption>(), PreferencesService.MarkAsPreference);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<MailOperation> availableHoverActions =
|
||||
[
|
||||
MailOperation.Archive,
|
||||
MailOperation.SoftDelete,
|
||||
MailOperation.SetFlag,
|
||||
MailOperation.MarkAsRead,
|
||||
MailOperation.MoveToJunk
|
||||
];
|
||||
|
||||
public List<string> AvailableHoverActionsTranslations { get; set; } =
|
||||
[
|
||||
Translator.HoverActionOption_Archive,
|
||||
Translator.HoverActionOption_Delete,
|
||||
Translator.HoverActionOption_ToggleFlag,
|
||||
Translator.HoverActionOption_ToggleRead,
|
||||
Translator.HoverActionOption_MoveJunk
|
||||
];
|
||||
|
||||
#region Properties
|
||||
|
||||
private int leftHoverActionIndex;
|
||||
|
||||
public int LeftHoverActionIndex
|
||||
{
|
||||
get => leftHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref leftHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.LeftHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int centerHoverActionIndex;
|
||||
|
||||
public int CenterHoverActionIndex
|
||||
{
|
||||
get => centerHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref centerHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.CenterHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int rightHoverActionIndex;
|
||||
|
||||
public int RightHoverActionIndex
|
||||
{
|
||||
get => rightHoverActionIndex;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref rightHoverActionIndex, value))
|
||||
{
|
||||
PreferencesService.RightHoverAction = availableHoverActions[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public MessageListPageViewModel(IMailDialogService dialogService,
|
||||
IPreferencesService preferencesService)
|
||||
{
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction);
|
||||
centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction);
|
||||
rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction);
|
||||
|
||||
SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues<MailMarkAsOption>(), PreferencesService.MarkAsPreference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
public class ActiveMailFolderChangedEvent : NavigateMailFolderEventArgs
|
||||
{
|
||||
public class ActiveMailFolderChangedEvent : NavigateMailFolderEventArgs
|
||||
public ActiveMailFolderChangedEvent(IBaseFolderMenuItem baseFolderMenuItem,
|
||||
TaskCompletionSource<bool> folderInitLoadAwaitTask = null) : base(baseFolderMenuItem, folderInitLoadAwaitTask)
|
||||
{
|
||||
public ActiveMailFolderChangedEvent(IBaseFolderMenuItem baseFolderMenuItem,
|
||||
TaskCompletionSource<bool> folderInitLoadAwaitTask = null) : base(baseFolderMenuItem, folderInitLoadAwaitTask)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// When active mail item in the reader is updated.
|
||||
/// </summary>
|
||||
public class ActiveMailItemChangedEvent
|
||||
{
|
||||
public ActiveMailItemChangedEvent(MailItemViewModel selectedMailItemViewModel)
|
||||
{
|
||||
// SelectedMailItemViewModel can be null.
|
||||
SelectedMailItemViewModel = selectedMailItemViewModel;
|
||||
}
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
public MailItemViewModel SelectedMailItemViewModel { get; set; }
|
||||
/// <summary>
|
||||
/// When active mail item in the reader is updated.
|
||||
/// </summary>
|
||||
public class ActiveMailItemChangedEvent
|
||||
{
|
||||
public ActiveMailItemChangedEvent(MailItemViewModel selectedMailItemViewModel)
|
||||
{
|
||||
// SelectedMailItemViewModel can be null.
|
||||
SelectedMailItemViewModel = selectedMailItemViewModel;
|
||||
}
|
||||
|
||||
public MailItemViewModel SelectedMailItemViewModel { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Wino has complex selected item detection mechanism with nested ListViews that
|
||||
/// supports multi selection with threads. Each list view will raise this for mail list page
|
||||
/// to react.
|
||||
/// </summary>
|
||||
public class MailItemSelectedEvent
|
||||
{
|
||||
public MailItemSelectedEvent(MailItemViewModel selectedMailItem)
|
||||
{
|
||||
SelectedMailItem = selectedMailItem;
|
||||
}
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
public MailItemViewModel SelectedMailItem { get; set; }
|
||||
/// <summary>
|
||||
/// Wino has complex selected item detection mechanism with nested ListViews that
|
||||
/// supports multi selection with threads. Each list view will raise this for mail list page
|
||||
/// to react.
|
||||
/// </summary>
|
||||
public class MailItemSelectedEvent
|
||||
{
|
||||
public MailItemSelectedEvent(MailItemViewModel selectedMailItem)
|
||||
{
|
||||
SelectedMailItem = selectedMailItem;
|
||||
}
|
||||
|
||||
public MailItemViewModel SelectedMailItem { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Selected item removed event.
|
||||
/// </summary>
|
||||
public class MailItemSelectionRemovedEvent
|
||||
{
|
||||
public MailItemSelectionRemovedEvent(MailItemViewModel removedMailItem)
|
||||
{
|
||||
RemovedMailItem = removedMailItem;
|
||||
}
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
public MailItemViewModel RemovedMailItem { get; set; }
|
||||
/// <summary>
|
||||
/// Selected item removed event.
|
||||
/// </summary>
|
||||
public class MailItemSelectionRemovedEvent
|
||||
{
|
||||
public MailItemSelectionRemovedEvent(MailItemViewModel removedMailItem)
|
||||
{
|
||||
RemovedMailItem = removedMailItem;
|
||||
}
|
||||
|
||||
public MailItemViewModel RemovedMailItem { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// When the rendering page is active, but new item is requested to be rendered.
|
||||
/// To not trigger navigation again and re-use existing Chromium.
|
||||
/// </summary>
|
||||
/// <param name="MailItemViewModel"></param>
|
||||
public record NewMailItemRenderingRequestedEvent(MailItemViewModel MailItemViewModel);
|
||||
}
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// When the rendering page is active, but new item is requested to be rendered.
|
||||
/// To not trigger navigation again and re-use existing Chromium.
|
||||
/// </summary>
|
||||
/// <param name="MailItemViewModel"></param>
|
||||
public record NewMailItemRenderingRequestedEvent(MailItemViewModel MailItemViewModel);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Mail.ViewModels.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// When listing view model manipulated the selected mail container in the UI.
|
||||
/// </summary>
|
||||
public record SelectMailItemContainerEvent(MailItemViewModel SelectedMailViewModel, bool ScrollToItem = false);
|
||||
}
|
||||
namespace Wino.Mail.ViewModels.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// When listing view model manipulated the selected mail container in the UI.
|
||||
/// </summary>
|
||||
public record SelectMailItemContainerEvent(MailItemViewModel SelectedMailViewModel, bool ScrollToItem = false);
|
||||
|
||||
@@ -4,70 +4,69 @@ using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class ReadComposePanePageViewModel : MailBaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<int>>
|
||||
{
|
||||
public partial class ReadComposePanePageViewModel : MailBaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<int>>
|
||||
private readonly IFontService _fontService;
|
||||
|
||||
public IPreferencesService PreferencesService { get; set; }
|
||||
public List<string> AvailableFonts => _fontService.GetFonts();
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
string currentReaderFont;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
int currentReaderFontSize;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
string currentComposerFont;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
int currentComposerFontSize;
|
||||
|
||||
public ReadComposePanePageViewModel(IMailDialogService dialogService,
|
||||
IFontService fontService,
|
||||
IPreferencesService preferencesService)
|
||||
{
|
||||
private readonly IFontService _fontService;
|
||||
_fontService = fontService;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
public IPreferencesService PreferencesService { get; set; }
|
||||
public List<string> AvailableFonts => _fontService.GetFonts();
|
||||
CurrentReaderFont = preferencesService.ReaderFont;
|
||||
CurrentReaderFontSize = preferencesService.ReaderFontSize;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
string currentReaderFont;
|
||||
CurrentComposerFont = preferencesService.ComposerFont;
|
||||
CurrentComposerFontSize = preferencesService.ComposerFontSize;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
int currentReaderFontSize;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
string currentComposerFont;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
int currentComposerFontSize;
|
||||
|
||||
public ReadComposePanePageViewModel(IMailDialogService dialogService,
|
||||
IFontService fontService,
|
||||
IPreferencesService preferencesService)
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.PropertyName == nameof(CurrentReaderFont) && message.OldValue != message.NewValue)
|
||||
{
|
||||
_fontService = fontService;
|
||||
PreferencesService = preferencesService;
|
||||
|
||||
CurrentReaderFont = preferencesService.ReaderFont;
|
||||
CurrentReaderFontSize = preferencesService.ReaderFontSize;
|
||||
|
||||
CurrentComposerFont = preferencesService.ComposerFont;
|
||||
CurrentComposerFontSize = preferencesService.ComposerFontSize;
|
||||
PreferencesService.ReaderFont = message.NewValue;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
if (message.PropertyName == nameof(CurrentComposerFont) && message.OldValue != message.NewValue)
|
||||
{
|
||||
if (message.PropertyName == nameof(CurrentReaderFont) && message.OldValue != message.NewValue)
|
||||
{
|
||||
PreferencesService.ReaderFont = message.NewValue;
|
||||
}
|
||||
|
||||
if (message.PropertyName == nameof(CurrentComposerFont) && message.OldValue != message.NewValue)
|
||||
{
|
||||
PreferencesService.ComposerFont = message.NewValue;
|
||||
}
|
||||
PreferencesService.ComposerFont = message.NewValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.PropertyName == nameof(CurrentReaderFontSize))
|
||||
{
|
||||
if (message.PropertyName == nameof(CurrentReaderFontSize))
|
||||
{
|
||||
PreferencesService.ReaderFontSize = CurrentReaderFontSize;
|
||||
}
|
||||
else if (message.PropertyName == nameof(CurrentComposerFontSize))
|
||||
{
|
||||
PreferencesService.ComposerFontSize = CurrentComposerFontSize;
|
||||
}
|
||||
PreferencesService.ReaderFontSize = CurrentReaderFontSize;
|
||||
}
|
||||
else if (message.PropertyName == nameof(CurrentComposerFontSize))
|
||||
{
|
||||
PreferencesService.ComposerFontSize = CurrentComposerFontSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,151 +13,150 @@ using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class SignatureManagementPageViewModel(IMailDialogService dialogService,
|
||||
ISignatureService signatureService,
|
||||
IAccountService accountService) : MailBaseViewModel
|
||||
{
|
||||
public partial class SignatureManagementPageViewModel(IMailDialogService dialogService,
|
||||
ISignatureService signatureService,
|
||||
IAccountService accountService) : MailBaseViewModel
|
||||
public ObservableCollection<AccountSignature> Signatures { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSignatureEnabled;
|
||||
|
||||
private int signatureForNewMessagesIndex;
|
||||
|
||||
public Guid EmptyGuid { get; } = Guid.Empty;
|
||||
|
||||
public int SignatureForNewMessagesIndex
|
||||
{
|
||||
public ObservableCollection<AccountSignature> Signatures { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isSignatureEnabled;
|
||||
|
||||
private int signatureForNewMessagesIndex;
|
||||
|
||||
public Guid EmptyGuid { get; } = Guid.Empty;
|
||||
|
||||
public int SignatureForNewMessagesIndex
|
||||
get => signatureForNewMessagesIndex;
|
||||
set
|
||||
{
|
||||
get => signatureForNewMessagesIndex;
|
||||
set
|
||||
if (value == -1)
|
||||
{
|
||||
if (value == -1)
|
||||
{
|
||||
SetProperty(ref signatureForNewMessagesIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetProperty(ref signatureForNewMessagesIndex, value);
|
||||
}
|
||||
SetProperty(ref signatureForNewMessagesIndex, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private int signatureForFollowingMessagesIndex;
|
||||
|
||||
public int SignatureForFollowingMessagesIndex
|
||||
{
|
||||
get => signatureForFollowingMessagesIndex;
|
||||
set
|
||||
else
|
||||
{
|
||||
if (value == -1)
|
||||
{
|
||||
SetProperty(ref signatureForFollowingMessagesIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetProperty(ref signatureForFollowingMessagesIndex, value);
|
||||
}
|
||||
SetProperty(ref signatureForNewMessagesIndex, value);
|
||||
}
|
||||
}
|
||||
|
||||
private MailAccount Account { get; set; }
|
||||
|
||||
private readonly IMailDialogService _dialogService = dialogService;
|
||||
private readonly ISignatureService _signatureService = signatureService;
|
||||
private readonly IAccountService _accountService = accountService;
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters is Guid accountId)
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
if (Account == null) return;
|
||||
|
||||
var dbSignatures = await _signatureService.GetSignaturesAsync(Account.Id);
|
||||
IsSignatureEnabled = Account.Preferences.IsSignatureEnabled;
|
||||
|
||||
Signatures.Clear();
|
||||
Signatures.Add(new AccountSignature { Id = EmptyGuid, Name = Translator.SettingsSignature_NoneSignatureName });
|
||||
dbSignatures.ForEach(Signatures.Add);
|
||||
|
||||
SignatureForNewMessagesIndex = Signatures.IndexOf(Signatures.FirstOrDefault(x => x.Id == Account.Preferences.SignatureIdForNewMessages));
|
||||
SignatureForFollowingMessagesIndex = Signatures.IndexOf(Signatures.FirstOrDefault(x => x.Id == Account.Preferences.SignatureIdForFollowingMessages));
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(IsSignatureEnabled):
|
||||
Account.Preferences.IsSignatureEnabled = IsSignatureEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(SignatureForNewMessagesIndex):
|
||||
Account.Preferences.SignatureIdForNewMessages = SignatureForNewMessagesIndex > -1
|
||||
&& Signatures[SignatureForNewMessagesIndex].Id != EmptyGuid
|
||||
? Signatures[SignatureForNewMessagesIndex].Id : null;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(SignatureForFollowingMessagesIndex):
|
||||
Account.Preferences.SignatureIdForFollowingMessages = SignatureForFollowingMessagesIndex > -1
|
||||
&& Signatures[SignatureForFollowingMessagesIndex].Id != EmptyGuid
|
||||
? Signatures[SignatureForFollowingMessagesIndex].Id : null;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSignatureEditorCreateAsync()
|
||||
{
|
||||
var dialogResult = await _dialogService.ShowSignatureEditorDialog();
|
||||
|
||||
if (dialogResult == null) return;
|
||||
|
||||
dialogResult.MailAccountId = Account.Id;
|
||||
Signatures.Add(dialogResult);
|
||||
await _signatureService.CreateSignatureAsync(dialogResult);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSignatureEditorEditAsync(AccountSignature signatureModel)
|
||||
{
|
||||
var dialogResult = await _dialogService.ShowSignatureEditorDialog(signatureModel);
|
||||
|
||||
if (dialogResult == null) return;
|
||||
|
||||
var indexOfCurrentSignature = Signatures.IndexOf(signatureModel);
|
||||
var signatureNewMessagesIndex = SignatureForNewMessagesIndex;
|
||||
var signatureFollowingMessagesIndex = SignatureForFollowingMessagesIndex;
|
||||
|
||||
Signatures[indexOfCurrentSignature] = dialogResult;
|
||||
|
||||
// Reset selection to point updated signature.
|
||||
// When Item updated/removed index switches to -1. We save index that was used before and update -1 to it.
|
||||
if (signatureNewMessagesIndex == indexOfCurrentSignature)
|
||||
SignatureForNewMessagesIndex = indexOfCurrentSignature;
|
||||
|
||||
if (signatureFollowingMessagesIndex == indexOfCurrentSignature)
|
||||
SignatureForFollowingMessagesIndex = indexOfCurrentSignature;
|
||||
|
||||
await _signatureService.UpdateSignatureAsync(dialogResult);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteSignatureAsync(AccountSignature signatureModel)
|
||||
{
|
||||
var shouldRemove = await _dialogService.ShowConfirmationDialogAsync(string.Format(Translator.SignatureDeleteDialog_Message, signatureModel.Name), Translator.SignatureDeleteDialog_Title, Translator.Buttons_Delete);
|
||||
|
||||
if (!shouldRemove) return;
|
||||
|
||||
Signatures.Remove(signatureModel);
|
||||
await _signatureService.DeleteSignatureAsync(signatureModel);
|
||||
}
|
||||
}
|
||||
|
||||
private int signatureForFollowingMessagesIndex;
|
||||
|
||||
public int SignatureForFollowingMessagesIndex
|
||||
{
|
||||
get => signatureForFollowingMessagesIndex;
|
||||
set
|
||||
{
|
||||
if (value == -1)
|
||||
{
|
||||
SetProperty(ref signatureForFollowingMessagesIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetProperty(ref signatureForFollowingMessagesIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MailAccount Account { get; set; }
|
||||
|
||||
private readonly IMailDialogService _dialogService = dialogService;
|
||||
private readonly ISignatureService _signatureService = signatureService;
|
||||
private readonly IAccountService _accountService = accountService;
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters is Guid accountId)
|
||||
Account = await _accountService.GetAccountAsync(accountId);
|
||||
|
||||
if (Account == null) return;
|
||||
|
||||
var dbSignatures = await _signatureService.GetSignaturesAsync(Account.Id);
|
||||
IsSignatureEnabled = Account.Preferences.IsSignatureEnabled;
|
||||
|
||||
Signatures.Clear();
|
||||
Signatures.Add(new AccountSignature { Id = EmptyGuid, Name = Translator.SettingsSignature_NoneSignatureName });
|
||||
dbSignatures.ForEach(Signatures.Add);
|
||||
|
||||
SignatureForNewMessagesIndex = Signatures.IndexOf(Signatures.FirstOrDefault(x => x.Id == Account.Preferences.SignatureIdForNewMessages));
|
||||
SignatureForFollowingMessagesIndex = Signatures.IndexOf(Signatures.FirstOrDefault(x => x.Id == Account.Preferences.SignatureIdForFollowingMessages));
|
||||
}
|
||||
|
||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(IsSignatureEnabled):
|
||||
Account.Preferences.IsSignatureEnabled = IsSignatureEnabled;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(SignatureForNewMessagesIndex):
|
||||
Account.Preferences.SignatureIdForNewMessages = SignatureForNewMessagesIndex > -1
|
||||
&& Signatures[SignatureForNewMessagesIndex].Id != EmptyGuid
|
||||
? Signatures[SignatureForNewMessagesIndex].Id : null;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
case nameof(SignatureForFollowingMessagesIndex):
|
||||
Account.Preferences.SignatureIdForFollowingMessages = SignatureForFollowingMessagesIndex > -1
|
||||
&& Signatures[SignatureForFollowingMessagesIndex].Id != EmptyGuid
|
||||
? Signatures[SignatureForFollowingMessagesIndex].Id : null;
|
||||
await _accountService.UpdateAccountAsync(Account);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSignatureEditorCreateAsync()
|
||||
{
|
||||
var dialogResult = await _dialogService.ShowSignatureEditorDialog();
|
||||
|
||||
if (dialogResult == null) return;
|
||||
|
||||
dialogResult.MailAccountId = Account.Id;
|
||||
Signatures.Add(dialogResult);
|
||||
await _signatureService.CreateSignatureAsync(dialogResult);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OpenSignatureEditorEditAsync(AccountSignature signatureModel)
|
||||
{
|
||||
var dialogResult = await _dialogService.ShowSignatureEditorDialog(signatureModel);
|
||||
|
||||
if (dialogResult == null) return;
|
||||
|
||||
var indexOfCurrentSignature = Signatures.IndexOf(signatureModel);
|
||||
var signatureNewMessagesIndex = SignatureForNewMessagesIndex;
|
||||
var signatureFollowingMessagesIndex = SignatureForFollowingMessagesIndex;
|
||||
|
||||
Signatures[indexOfCurrentSignature] = dialogResult;
|
||||
|
||||
// Reset selection to point updated signature.
|
||||
// When Item updated/removed index switches to -1. We save index that was used before and update -1 to it.
|
||||
if (signatureNewMessagesIndex == indexOfCurrentSignature)
|
||||
SignatureForNewMessagesIndex = indexOfCurrentSignature;
|
||||
|
||||
if (signatureFollowingMessagesIndex == indexOfCurrentSignature)
|
||||
SignatureForFollowingMessagesIndex = indexOfCurrentSignature;
|
||||
|
||||
await _signatureService.UpdateSignatureAsync(dialogResult);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteSignatureAsync(AccountSignature signatureModel)
|
||||
{
|
||||
var shouldRemove = await _dialogService.ShowConfirmationDialogAsync(string.Format(Translator.SignatureDeleteDialog_Message, signatureModel.Name), Translator.SignatureDeleteDialog_Title, Translator.Buttons_Delete);
|
||||
|
||||
if (!shouldRemove) return;
|
||||
|
||||
Signatures.Remove(signatureModel);
|
||||
await _signatureService.DeleteSignatureAsync(signatureModel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,35 +4,34 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
namespace Wino.Mail.ViewModels
|
||||
namespace Wino.Mail.ViewModels;
|
||||
|
||||
public partial class WelcomePageViewModel : MailBaseViewModel
|
||||
{
|
||||
public partial class WelcomePageViewModel : MailBaseViewModel
|
||||
public const string VersionFile = "190.md";
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
[ObservableProperty]
|
||||
private string currentVersionNotes;
|
||||
|
||||
public WelcomePageViewModel(IMailDialogService dialogService, IFileService fileService)
|
||||
{
|
||||
public const string VersionFile = "190.md";
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IFileService _fileService;
|
||||
_dialogService = dialogService;
|
||||
_fileService = fileService;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private string currentVersionNotes;
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
public WelcomePageViewModel(IMailDialogService dialogService, IFileService fileService)
|
||||
try
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_fileService = fileService;
|
||||
CurrentVersionNotes = await _fileService.GetFileContentByApplicationUriAsync($"ms-appx:///Assets/ReleaseNotes/{VersionFile}");
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
catch (Exception)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
try
|
||||
{
|
||||
CurrentVersionNotes = await _fileService.GetFileContentByApplicationUriAsync($"ms-appx:///Assets/ReleaseNotes/{VersionFile}");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, "Can't find the patch notes.", Core.Domain.Enums.InfoBarMessageType.Information);
|
||||
}
|
||||
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, "Can't find the patch notes.", Core.Domain.Enums.InfoBarMessageType.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user