Ability to select alias in composer page.
This commit is contained in:
@@ -141,7 +141,7 @@ namespace Wino.Core.Domain.Entities
|
||||
/// </summary>
|
||||
[Ignore]
|
||||
public MailAccount AssignedAccount { get; set; }
|
||||
public IEnumerable<Guid> GetContainingIds() => new[] { UniqueId };
|
||||
public IEnumerable<Guid> GetContainingIds() => [UniqueId];
|
||||
public override string ToString() => $"{Subject} <-> {Id}";
|
||||
}
|
||||
}
|
||||
|
||||
7
Wino.Core.Domain/Exceptions/MissingAliasException.cs
Normal file
7
Wino.Core.Domain/Exceptions/MissingAliasException.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Wino.Core.Domain.Exceptions
|
||||
{
|
||||
public class MissingAliasException : System.Exception
|
||||
{
|
||||
public MissingAliasException() : base(Translator.Exception_MissingAlias) { }
|
||||
}
|
||||
}
|
||||
@@ -146,5 +146,14 @@ namespace Wino.Core.Domain.Interfaces
|
||||
/// <param name="remoteAccountAliases">Remotely fetched basic alias info from synchronizer.</param>
|
||||
/// <param name="account">Account to update remote aliases for..</param>
|
||||
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the primary account alias for the given account id.
|
||||
/// Used when creating draft messages.
|
||||
/// </summary>
|
||||
/// <param name="accountId">Account id.</param>
|
||||
/// <returns>Primary alias for the account.</returns>
|
||||
Task<MailAccountAlias> GetPrimaryAccountAliasAsync(Guid accountId);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,22 @@ namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public class SendDraftPreparationRequest
|
||||
{
|
||||
public MailCopy MailItem { get; set; }
|
||||
public string Base64MimeMessage { get; set; }
|
||||
public MailItemFolder SentFolder { get; set; }
|
||||
public MailItemFolder DraftFolder { get; set; }
|
||||
public MailAccountPreferences AccountPreferences { get; set; }
|
||||
public MailCopy MailItem { get; }
|
||||
public string Base64MimeMessage { get; }
|
||||
public MailItemFolder SentFolder { get; }
|
||||
public MailItemFolder DraftFolder { get; }
|
||||
public MailAccountPreferences AccountPreferences { get; }
|
||||
public MailAccountAlias SendingAlias { get; set; }
|
||||
|
||||
public SendDraftPreparationRequest(MailCopy mailItem,
|
||||
MailAccountAlias sendingAlias,
|
||||
MailItemFolder sentFolder,
|
||||
MailItemFolder draftFolder,
|
||||
MailAccountPreferences accountPreferences,
|
||||
string base64MimeMessage)
|
||||
{
|
||||
MailItem = mailItem;
|
||||
SendingAlias = sendingAlias;
|
||||
SentFolder = sentFolder;
|
||||
DraftFolder = draftFolder;
|
||||
AccountPreferences = accountPreferences;
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
"CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper",
|
||||
"DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?",
|
||||
"DialogMessage_AccountLimitTitle": "Account Limit Reached",
|
||||
"DialogMessage_AliasNotSelectedTitle": "Missing Alias",
|
||||
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
|
||||
"DialogMessage_AliasExistsTitle": "Existing Alias",
|
||||
"DialogMessage_AliasExistsMessage": "This alias is already in use.",
|
||||
"DialogMessage_InvalidAliasTitle": "Invalid Alias",
|
||||
@@ -133,6 +135,7 @@
|
||||
"Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.",
|
||||
"Exception_FailedToSynchronizeFolders": "Failed to synchronize folders",
|
||||
"Exception_FailedToSynchronizeAliases": "Failed to synchronize aliases",
|
||||
"Exception_MissingAlias": "Primary alias does not exist for this account. Creating draft failed.",
|
||||
"Exception_FailedToSynchronizeProfileInformation": "Failed to synchronize profile information",
|
||||
"Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.",
|
||||
"Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.",
|
||||
|
||||
15
Wino.Core.Domain/Translator.Designer.cs
generated
15
Wino.Core.Domain/Translator.Designer.cs
generated
@@ -358,6 +358,16 @@ namespace Wino.Core.Domain
|
||||
/// </summary>
|
||||
public static string DialogMessage_AccountLimitTitle => Resources.GetTranslatedString(@"DialogMessage_AccountLimitTitle");
|
||||
|
||||
/// <summary>
|
||||
/// Missing Alias
|
||||
/// </summary>
|
||||
public static string DialogMessage_AliasNotSelectedTitle => Resources.GetTranslatedString(@"DialogMessage_AliasNotSelectedTitle");
|
||||
|
||||
/// <summary>
|
||||
/// You must select an alias before sending a message.
|
||||
/// </summary>
|
||||
public static string DialogMessage_AliasNotSelectedMessage => Resources.GetTranslatedString(@"DialogMessage_AliasNotSelectedMessage");
|
||||
|
||||
/// <summary>
|
||||
/// Existing Alias
|
||||
/// </summary>
|
||||
@@ -688,6 +698,11 @@ namespace Wino.Core.Domain
|
||||
/// </summary>
|
||||
public static string Exception_FailedToSynchronizeAliases => Resources.GetTranslatedString(@"Exception_FailedToSynchronizeAliases");
|
||||
|
||||
/// <summary>
|
||||
/// Primary alias does not exist for this account. Creating draft failed.
|
||||
/// </summary>
|
||||
public static string Exception_MissingAlias => Resources.GetTranslatedString(@"Exception_MissingAlias");
|
||||
|
||||
/// <summary>
|
||||
/// Failed to synchronize profile information
|
||||
/// </summary>
|
||||
|
||||
@@ -559,5 +559,14 @@ namespace Wino.Core.Services
|
||||
|
||||
Messenger.Send(new AccountMenuItemsReordered(accountIdOrderPair));
|
||||
}
|
||||
|
||||
public async Task<MailAccountAlias> GetPrimaryAccountAliasAsync(Guid accountId)
|
||||
{
|
||||
var aliases = await GetAccountAliasesAsync(accountId);
|
||||
|
||||
if (aliases == null || aliases.Count == 0) return null;
|
||||
|
||||
return aliases.FirstOrDefault(a => a.IsPrimary) ?? aliases.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using SqlKata;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Comparers;
|
||||
@@ -62,12 +63,14 @@ namespace Wino.Core.Services
|
||||
// This header will be used to map the local draft copy with the remote draft copy.
|
||||
var mimeUniqueId = createdDraftMimeMessage.Headers[Constants.WinoLocalDraftHeader];
|
||||
|
||||
var primaryAlias = await _accountService.GetPrimaryAccountAliasAsync(accountId).ConfigureAwait(false);
|
||||
|
||||
var copy = new MailCopy
|
||||
{
|
||||
UniqueId = Guid.Parse(mimeUniqueId),
|
||||
Id = Guid.NewGuid().ToString(), // This will be replaced after network call with the remote draft id.
|
||||
CreationDate = DateTime.UtcNow,
|
||||
FromAddress = composerAccount.Address,
|
||||
FromAddress = primaryAlias?.AliasAddress ?? composerAccount.Address,
|
||||
FromName = composerAccount.SenderName,
|
||||
HasAttachments = false,
|
||||
Importance = MailImportance.Normal,
|
||||
@@ -621,12 +624,17 @@ namespace Wino.Core.Services
|
||||
// This unique id is stored in mime headers for Wino to identify remote message with local copy.
|
||||
// Same unique id will be used for the local copy as well.
|
||||
// Synchronizer will map this unique id to the local draft copy after synchronization.
|
||||
|
||||
var message = new MimeMessage()
|
||||
{
|
||||
Headers = { { Constants.WinoLocalDraftHeader, Guid.NewGuid().ToString() } },
|
||||
From = { new MailboxAddress(account.SenderName, account.Address) }
|
||||
};
|
||||
|
||||
var primaryAlias = await _accountService.GetPrimaryAccountAliasAsync(account.Id) ?? throw new MissingAliasException();
|
||||
|
||||
// Set FromName and FromAddress by alias.
|
||||
message.From.Add(new MailboxAddress(account.SenderName, primaryAlias.AliasAddress));
|
||||
|
||||
var builder = new BodyBuilder();
|
||||
|
||||
var signature = await GetSignature(account, draftCreationOptions.Reason);
|
||||
|
||||
@@ -729,17 +729,26 @@ namespace Wino.Mail.ViewModels
|
||||
operationAccount = accounts.FirstOrDefault();
|
||||
else
|
||||
{
|
||||
// There are multiple accounts and there is no selection.
|
||||
// Don't list all accounts, but only accounts that belong to Merged Inbox.
|
||||
|
||||
if (latestSelectedAccountMenuItem is MergedAccountMenuItem selectedMergedAccountMenuItem)
|
||||
{
|
||||
// There are multiple accounts and there is no selection.
|
||||
// Don't list all accounts, but only accounts that belong to Merged Inbox.
|
||||
|
||||
var mergedAccounts = accounts.Where(a => a.MergedInboxId == selectedMergedAccountMenuItem.EntityId);
|
||||
|
||||
if (!mergedAccounts.Any()) return;
|
||||
|
||||
Messenger.Send(new CreateNewMailWithMultipleAccountsRequested(mergedAccounts.ToList()));
|
||||
}
|
||||
else if (latestSelectedAccountMenuItem is AccountMenuItem selectedAccountMenuItem)
|
||||
{
|
||||
operationAccount = selectedAccountMenuItem.HoldingAccounts.ElementAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// User is at some other page. List all accounts.
|
||||
Messenger.Send(new CreateNewMailWithMultipleAccountsRequested(accounts));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,12 @@ namespace Wino.Mail.ViewModels
|
||||
[NotifyCanExecuteChangedFor(nameof(SendCommand))]
|
||||
private MailAccount composingAccount;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<MailAccountAlias> availableAliases;
|
||||
|
||||
[ObservableProperty]
|
||||
private MailAccountAlias selectedAlias;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isDraggingOverComposerGrid;
|
||||
|
||||
@@ -166,6 +172,12 @@ namespace Wino.Mail.ViewModels
|
||||
if (!isConfirmed) return;
|
||||
}
|
||||
|
||||
if (SelectedAlias == null)
|
||||
{
|
||||
DialogService.InfoBarMessage(Translator.DialogMessage_AliasNotSelectedTitle, Translator.DialogMessage_AliasNotSelectedMessage, InfoBarMessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save mime changes before sending.
|
||||
await UpdateMimeChangesAsync().ConfigureAwait(false);
|
||||
|
||||
@@ -180,7 +192,12 @@ namespace Wino.Mail.ViewModels
|
||||
int count = (int)memoryStream.Length;
|
||||
|
||||
var base64EncodedMessage = Convert.ToBase64String(buffer);
|
||||
var draftSendPreparationRequest = new SendDraftPreparationRequest(CurrentMailDraftItem.MailCopy, sentFolder, CurrentMailDraftItem.AssignedFolder, CurrentMailDraftItem.AssignedAccount.Preferences, base64EncodedMessage);
|
||||
var draftSendPreparationRequest = new SendDraftPreparationRequest(CurrentMailDraftItem.MailCopy,
|
||||
SelectedAlias,
|
||||
sentFolder,
|
||||
CurrentMailDraftItem.AssignedFolder,
|
||||
CurrentMailDraftItem.AssignedAccount.Preferences,
|
||||
base64EncodedMessage);
|
||||
|
||||
await _worker.ExecuteAsync(draftSendPreparationRequest);
|
||||
}
|
||||
@@ -197,6 +214,8 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
SaveImportance();
|
||||
SaveSubject();
|
||||
SaveFromAddress();
|
||||
SaveReplyToAddress();
|
||||
|
||||
await SaveAttachmentsAsync();
|
||||
await SaveBodyAsync();
|
||||
@@ -210,6 +229,7 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
CurrentMailDraftItem.Subject = CurrentMimeMessage.Subject;
|
||||
CurrentMailDraftItem.PreviewText = CurrentMimeMessage.TextBody;
|
||||
CurrentMailDraftItem.FromAddress = SelectedAlias.AliasAddress;
|
||||
|
||||
// Update database.
|
||||
await _mailService.UpdateMailAsync(CurrentMailDraftItem.MailCopy);
|
||||
@@ -227,7 +247,10 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveImportance() { CurrentMimeMessage.Importance = IsImportanceSelected ? SelectedMessageImportance : MessageImportance.Normal; }
|
||||
private void SaveImportance()
|
||||
{
|
||||
CurrentMimeMessage.Importance = IsImportanceSelected ? SelectedMessageImportance : MessageImportance.Normal;
|
||||
}
|
||||
|
||||
private void SaveSubject()
|
||||
{
|
||||
@@ -285,14 +308,12 @@ namespace Wino.Mail.ViewModels
|
||||
await UpdateMimeChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
if (parameters != null && parameters is MailItemViewModel mailItem)
|
||||
{
|
||||
await LoadAccountsAsync();
|
||||
|
||||
CurrentMailDraftItem = mailItem;
|
||||
|
||||
_ = TryPrepareComposeAsync(true);
|
||||
@@ -321,46 +342,52 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAccountsAsync()
|
||||
{
|
||||
// Load accounts
|
||||
|
||||
var accounts = await _accountService.GetAccountsAsync();
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
Accounts.Add(account);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> InitializeComposerAccountAsync()
|
||||
{
|
||||
if (CurrentMailDraftItem == null) return false;
|
||||
|
||||
if (ComposingAccount != null) return true;
|
||||
|
||||
if (CurrentMailDraftItem == null)
|
||||
return false;
|
||||
var composingAccount = await _accountService.GetAccountAsync(CurrentMailDraftItem.AssignedAccount.Id).ConfigureAwait(false);
|
||||
if (composingAccount == null) return false;
|
||||
|
||||
var aliases = await _accountService.GetAccountAliasesAsync(composingAccount.Id).ConfigureAwait(false);
|
||||
|
||||
if (aliases == null || !aliases.Any()) return false;
|
||||
|
||||
// MailAccountAlias primaryAlias = aliases.Find(a => a.IsPrimary) ?? aliases.First();
|
||||
|
||||
// Auto-select the correct alias from the message itself.
|
||||
// If can't, fallback to primary alias.
|
||||
|
||||
MailAccountAlias primaryAlias = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(CurrentMailDraftItem.FromAddress))
|
||||
{
|
||||
primaryAlias = aliases.Find(a => a.AliasAddress == CurrentMailDraftItem.FromAddress);
|
||||
}
|
||||
|
||||
primaryAlias ??= await _accountService.GetPrimaryAccountAliasAsync(ComposingAccount.Id).ConfigureAwait(false);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
ComposingAccount = Accounts.FirstOrDefault(a => a.Id == CurrentMailDraftItem.AssignedAccount.Id);
|
||||
ComposingAccount = composingAccount;
|
||||
AvailableAliases = aliases;
|
||||
SelectedAlias = primaryAlias;
|
||||
});
|
||||
|
||||
return ComposingAccount != null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task TryPrepareComposeAsync(bool downloadIfNeeded)
|
||||
{
|
||||
if (CurrentMailDraftItem == null)
|
||||
return;
|
||||
if (CurrentMailDraftItem == null) return;
|
||||
|
||||
bool isComposerInitialized = await InitializeComposerAccountAsync();
|
||||
|
||||
if (!isComposerInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!isComposerInitialized) return;
|
||||
|
||||
retry:
|
||||
retry:
|
||||
|
||||
// Replying existing message.
|
||||
MimeMessageInformation mimeMessageInformation = null;
|
||||
@@ -452,6 +479,31 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveFromAddress()
|
||||
{
|
||||
if (SelectedAlias == null || CurrentMimeMessage == null) return;
|
||||
|
||||
|
||||
CurrentMimeMessage.From.Clear();
|
||||
CurrentMimeMessage.From.Add(new MailboxAddress(ComposingAccount.SenderName, SelectedAlias.AliasAddress));
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void SaveReplyToAddress()
|
||||
{
|
||||
if (SelectedAlias == null || CurrentMimeMessage == null) return;
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedAlias.ReplyToAddress))
|
||||
{
|
||||
if (!CurrentMimeMessage.ReplyTo.Any(a => a is MailboxAddress mailboxAddress && mailboxAddress.Address == SelectedAlias.ReplyToAddress))
|
||||
{
|
||||
CurrentMimeMessage.ReplyTo.Clear();
|
||||
CurrentMimeMessage.ReplyTo.Add(new MailboxAddress(SelectedAlias.ReplyToAddress, SelectedAlias.ReplyToAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAddressInfo(IEnumerable<AddressInformation> addresses, InternetAddressList list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Wino.Mail.ViewModels.Data
|
||||
public string MessageId => ((IMailItem)MailCopy).MessageId;
|
||||
public string FromName => ((IMailItem)MailCopy).FromName ?? FromAddress;
|
||||
public DateTime CreationDate => ((IMailItem)MailCopy).CreationDate;
|
||||
public string FromAddress => ((IMailItem)MailCopy).FromAddress;
|
||||
public bool HasAttachments => ((IMailItem)MailCopy).HasAttachments;
|
||||
public string References => ((IMailItem)MailCopy).References;
|
||||
public string InReplyTo => ((IMailItem)MailCopy).InReplyTo;
|
||||
@@ -77,6 +76,12 @@ namespace Wino.Mail.ViewModels.Data
|
||||
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 MailItemFolder AssignedFolder => ((IMailItem)MailCopy).AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => ((IMailItem)MailCopy).AssignedAccount;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user