Merge pull request #331 from bkaankose/feature/ContactPictures
Account contact pictures
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SQLite;
|
||||
|
||||
namespace Wino.Core.Domain.Entities
|
||||
{
|
||||
@@ -9,23 +9,24 @@ namespace Wino.Core.Domain.Entities
|
||||
/// These values will be inserted during MIME fetch.
|
||||
/// </summary>
|
||||
|
||||
|
||||
// TODO: This can easily evolve to Contact store, just like People app in Windows 10/11.
|
||||
// Do it.
|
||||
public class AddressInformation : IEquatable<AddressInformation>
|
||||
public class AccountContact : IEquatable<AccountContact>
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Address { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Base64ContactPicture { get; set; }
|
||||
public bool IsRootContact { get; set; }
|
||||
|
||||
public string DisplayName => Address == Name ? Address : $"{Name} <{Address}>";
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as AddressInformation);
|
||||
return Equals(obj as AccountContact);
|
||||
}
|
||||
|
||||
public bool Equals(AddressInformation other)
|
||||
public bool Equals(AccountContact other)
|
||||
{
|
||||
return !(other is null) &&
|
||||
Address == other.Address &&
|
||||
@@ -40,12 +41,12 @@ namespace Wino.Core.Domain.Entities
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public static bool operator ==(AddressInformation left, AddressInformation right)
|
||||
public static bool operator ==(AccountContact left, AccountContact right)
|
||||
{
|
||||
return EqualityComparer<AddressInformation>.Default.Equals(left, right);
|
||||
return EqualityComparer<AccountContact>.Default.Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(AddressInformation left, AddressInformation right)
|
||||
public static bool operator !=(AccountContact left, AccountContact right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
@@ -141,6 +141,15 @@ namespace Wino.Core.Domain.Entities
|
||||
/// </summary>
|
||||
[Ignore]
|
||||
public MailAccount AssignedAccount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contact information of the sender if exists.
|
||||
/// Warning: This field is not populated by queries.
|
||||
/// Services or View Models are responsible for populating this field.
|
||||
/// </summary>
|
||||
[Ignore]
|
||||
public AccountContact SenderContact { get; set; }
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => [UniqueId];
|
||||
public override string ToString() => $"{Subject} <-> {Id}";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
namespace Wino.Core.Domain.Interfaces
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Core.Domain.Interfaces
|
||||
{
|
||||
public interface IBackgroundTaskService
|
||||
{
|
||||
/// <summary>
|
||||
/// Unregisters all existing background tasks. Useful for migrations.
|
||||
/// Unregisters all background tasks once.
|
||||
/// This is used to clean up the background tasks when the app is updated.
|
||||
/// </summary>
|
||||
void UnregisterAllBackgroundTask();
|
||||
|
||||
/// <summary>
|
||||
/// Registers required background tasks.
|
||||
/// </summary>
|
||||
Task RegisterBackgroundTasksAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,5 +29,6 @@ namespace Wino.Core.Domain.Models.MailItem
|
||||
|
||||
MailItemFolder AssignedFolder { get; }
|
||||
MailAccount AssignedAccount { get; }
|
||||
AccountContact SenderContact { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace Wino.Core.Domain.Models.MailItem
|
||||
|
||||
public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty;
|
||||
|
||||
public AccountContact SenderContact => LatestMailItem?.SenderContact;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
@@ -7,6 +10,7 @@ namespace Wino.Core.UWP.Services
|
||||
public class BackgroundTaskService : IBackgroundTaskService
|
||||
{
|
||||
private const string IsBackgroundTasksUnregisteredKey = nameof(IsBackgroundTasksUnregisteredKey);
|
||||
public const string ToastNotificationActivationHandlerTaskName = "ToastNotificationActivationHandlerTask";
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
@@ -17,7 +21,7 @@ namespace Wino.Core.UWP.Services
|
||||
|
||||
public void UnregisterAllBackgroundTask()
|
||||
{
|
||||
if (!_configurationService.Get(IsBackgroundTasksUnregisteredKey, false))
|
||||
if (_configurationService.Get(IsBackgroundTasksUnregisteredKey, false))
|
||||
{
|
||||
foreach (var task in BackgroundTaskRegistration.AllTasks)
|
||||
{
|
||||
@@ -28,5 +32,32 @@ namespace Wino.Core.UWP.Services
|
||||
_configurationService.Set(IsBackgroundTasksUnregisteredKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Task RegisterBackgroundTasksAsync()
|
||||
{
|
||||
return RegisterToastNotificationHandlerBackgroundTaskAsync();
|
||||
}
|
||||
|
||||
public async Task RegisterToastNotificationHandlerBackgroundTaskAsync()
|
||||
{
|
||||
// If background task is already registered, do nothing.
|
||||
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(ToastNotificationActivationHandlerTaskName)))
|
||||
return;
|
||||
|
||||
// Otherwise request access
|
||||
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
|
||||
|
||||
// Create the background task
|
||||
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
|
||||
{
|
||||
Name = ToastNotificationActivationHandlerTaskName
|
||||
};
|
||||
|
||||
// Assign the toast action trigger
|
||||
builder.SetTrigger(new ToastNotificationActionTrigger());
|
||||
|
||||
// And register the task
|
||||
BackgroundTaskRegistration registration = builder.Register();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ using MimeKit;
|
||||
using MimeKit.IO;
|
||||
using MimeKit.IO.Filters;
|
||||
using MimeKit.Utils;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
@@ -41,16 +39,6 @@ namespace Wino.Core.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public static AddressInformation ToAddressInformation(this MailboxAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
return new AddressInformation() { Name = Translator.UnknownSender, Address = Translator.UnknownAddress };
|
||||
|
||||
if (string.IsNullOrEmpty(address.Name))
|
||||
address.Name = address.Address;
|
||||
|
||||
return new AddressInformation() { Name = address.Name, Address = address.Address };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets html body replacing base64 images with cid linked resources.
|
||||
|
||||
@@ -332,6 +332,18 @@ namespace Wino.Core.Services
|
||||
account.SenderName = profileInformation.SenderName;
|
||||
account.Base64ProfilePictureData = profileInformation.Base64ProfilePictureData;
|
||||
|
||||
// Forcefully add or update a contact data with the provided information.
|
||||
|
||||
var accountContact = new AccountContact()
|
||||
{
|
||||
Address = account.Address,
|
||||
Name = account.SenderName,
|
||||
Base64ContactPicture = account.Base64ProfilePictureData,
|
||||
IsRootContact = true
|
||||
};
|
||||
|
||||
await Connection.InsertOrReplaceAsync(accountContact).ConfigureAwait(false);
|
||||
|
||||
await UpdateAccountAsync(account).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Wino.Core.Services
|
||||
{
|
||||
public interface IContactService
|
||||
{
|
||||
Task<List<AddressInformation>> GetAddressInformationAsync(string queryText);
|
||||
Task<AddressInformation> GetAddressInformationByAddressAsync(string address);
|
||||
Task<List<AccountContact>> GetAddressInformationAsync(string queryText);
|
||||
Task<AccountContact> GetAddressInformationByAddressAsync(string address);
|
||||
Task SaveAddressInformationAsync(MimeMessage message);
|
||||
}
|
||||
|
||||
@@ -19,25 +19,22 @@ namespace Wino.Core.Services
|
||||
{
|
||||
public ContactService(IDatabaseService databaseService) : base(databaseService) { }
|
||||
|
||||
public Task<List<AddressInformation>> GetAddressInformationAsync(string queryText)
|
||||
public Task<List<AccountContact>> GetAddressInformationAsync(string queryText)
|
||||
{
|
||||
if (queryText == null || queryText.Length < 2)
|
||||
return Task.FromResult<List<AddressInformation>>(null);
|
||||
return Task.FromResult<List<AccountContact>>(null);
|
||||
|
||||
var query = new Query(nameof(AddressInformation));
|
||||
var query = new Query(nameof(AccountContact));
|
||||
query.WhereContains("Address", queryText);
|
||||
query.OrWhereContains("Name", queryText);
|
||||
|
||||
var rawLikeQuery = query.GetRawQuery();
|
||||
|
||||
return Connection.QueryAsync<AddressInformation>(rawLikeQuery);
|
||||
return Connection.QueryAsync<AccountContact>(rawLikeQuery);
|
||||
}
|
||||
|
||||
public async Task<AddressInformation> GetAddressInformationByAddressAsync(string address)
|
||||
{
|
||||
return await Connection.Table<AddressInformation>().Where(a => a.Address == address).FirstOrDefaultAsync()
|
||||
?? new AddressInformation() { Name = address, Address = address };
|
||||
}
|
||||
public Task<AccountContact> GetAddressInformationByAddressAsync(string address)
|
||||
=> Connection.Table<AccountContact>().Where(a => a.Address == address).FirstOrDefaultAsync();
|
||||
|
||||
public async Task SaveAddressInformationAsync(MimeMessage message)
|
||||
{
|
||||
@@ -45,10 +42,21 @@ namespace Wino.Core.Services
|
||||
.GetRecipients(true)
|
||||
.Where(a => !string.IsNullOrEmpty(a.Name) && !string.IsNullOrEmpty(a.Address));
|
||||
|
||||
var addressInformations = recipients.Select(a => new AddressInformation() { Name = a.Name, Address = a.Address });
|
||||
var addressInformations = recipients.Select(a => new AccountContact() { Name = a.Name, Address = a.Address });
|
||||
|
||||
foreach (var info in addressInformations)
|
||||
await Connection.InsertOrReplaceAsync(info).ConfigureAwait(false);
|
||||
{
|
||||
var currentContact = await GetAddressInformationByAddressAsync(info.Address).ConfigureAwait(false);
|
||||
|
||||
if (currentContact == null)
|
||||
{
|
||||
await Connection.InsertAsync(info).ConfigureAwait(false);
|
||||
}
|
||||
else if (!currentContact.IsRootContact) // Don't update root contacts. They belong to accounts.
|
||||
{
|
||||
await Connection.InsertOrReplaceAsync(info).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Wino.Core.Services
|
||||
typeof(MailItemFolder),
|
||||
typeof(MailAccount),
|
||||
typeof(TokenInformation),
|
||||
typeof(AddressInformation),
|
||||
typeof(AccountContact),
|
||||
typeof(CustomServerInformation),
|
||||
typeof(AccountSignature),
|
||||
typeof(MergedInbox),
|
||||
|
||||
@@ -201,6 +201,7 @@ namespace Wino.Core.Services
|
||||
|
||||
Dictionary<Guid, MailItemFolder> folderCache = [];
|
||||
Dictionary<Guid, MailAccount> accountCache = [];
|
||||
Dictionary<string, AccountContact> contactCache = [];
|
||||
|
||||
// Populate Folder Assignment for each single mail, to be able later group by "MailAccountId".
|
||||
// This is needed to execute threading strategy by account type.
|
||||
@@ -255,7 +256,9 @@ namespace Wino.Core.Services
|
||||
return threadedItems;
|
||||
|
||||
// Recursive function to populate folder and account assignments for each mail item.
|
||||
async Task LoadAssignedPropertiesWithCacheAsync(IMailItem mail, Dictionary<Guid, MailItemFolder> folderCache, Dictionary<Guid, MailAccount> accountCache)
|
||||
async Task LoadAssignedPropertiesWithCacheAsync(IMailItem mail,
|
||||
Dictionary<Guid, MailItemFolder> folderCache,
|
||||
Dictionary<Guid, MailAccount> accountCache)
|
||||
{
|
||||
if (mail is ThreadMailItem threadMailItem)
|
||||
{
|
||||
@@ -276,6 +279,7 @@ namespace Wino.Core.Services
|
||||
folderAssignment = await _folderService.GetFolderAsync(mailCopy.FolderId).ConfigureAwait(false);
|
||||
_ = folderCache.TryAdd(mailCopy.FolderId, folderAssignment);
|
||||
}
|
||||
|
||||
if (folderAssignment != null)
|
||||
{
|
||||
var isAccountCached = accountCache.TryGetValue(folderAssignment.MailAccountId, out accountAssignment);
|
||||
@@ -283,11 +287,25 @@ namespace Wino.Core.Services
|
||||
{
|
||||
accountAssignment = await _accountService.GetAccountAsync(folderAssignment.MailAccountId).ConfigureAwait(false);
|
||||
_ = accountCache.TryAdd(folderAssignment.MailAccountId, accountAssignment);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool isContactCached = contactCache.TryGetValue(mailCopy.FromAddress, out AccountContact contactAssignment);
|
||||
|
||||
if (!isContactCached && accountAssignment != null)
|
||||
{
|
||||
contactAssignment = await GetSenderContactForAccountAsync(accountAssignment, mailCopy.FromAddress).ConfigureAwait(false);
|
||||
|
||||
if (contactAssignment != null)
|
||||
{
|
||||
_ = contactCache.TryAdd(mailCopy.FromAddress, contactAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
mailCopy.AssignedFolder = folderAssignment;
|
||||
mailCopy.AssignedAccount = accountAssignment;
|
||||
mailCopy.SenderContact = contactAssignment ?? new AccountContact() { Name = mailCopy.FromName, Address = mailCopy.FromAddress };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,11 +322,24 @@ namespace Wino.Core.Services
|
||||
return mailCopies;
|
||||
}
|
||||
|
||||
private Task<AccountContact> GetSenderContactForAccountAsync(MailAccount account, string fromAddress)
|
||||
{
|
||||
// Make sure to return the latest up to date contact information for the original account.
|
||||
if (fromAddress == account.Address)
|
||||
{
|
||||
return Task.FromResult(new AccountContact() { Address = account.Address, Name = account.SenderName, Base64ContactPicture = account.Base64ProfilePictureData });
|
||||
}
|
||||
else
|
||||
{
|
||||
return _contactService.GetAddressInformationByAddressAsync(fromAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAssignedPropertiesAsync(MailCopy mailCopy)
|
||||
{
|
||||
if (mailCopy == null) return;
|
||||
|
||||
// Load AssignedAccount and AssignedFolder.
|
||||
// Load AssignedAccount, AssignedFolder and SenderContact.
|
||||
|
||||
var folder = await _folderService.GetFolderAsync(mailCopy.FolderId);
|
||||
|
||||
@@ -320,6 +351,7 @@ namespace Wino.Core.Services
|
||||
|
||||
mailCopy.AssignedAccount = account;
|
||||
mailCopy.AssignedFolder = folder;
|
||||
mailCopy.SenderContact = await GetSenderContactForAccountAsync(account, mailCopy.FromAddress).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<MailCopy> GetSingleMailItemWithoutFolderAssignmentAsync(string mailCopyId)
|
||||
@@ -579,6 +611,7 @@ namespace Wino.Core.Services
|
||||
mailCopy.UniqueId = Guid.NewGuid();
|
||||
mailCopy.AssignedAccount = account;
|
||||
mailCopy.AssignedFolder = assignedFolder;
|
||||
mailCopy.SenderContact = await GetSenderContactForAccountAsync(account, mailCopy.FromAddress).ConfigureAwait(false);
|
||||
mailCopy.FolderId = assignedFolder.Id;
|
||||
|
||||
// Only save MIME files if they don't exists.
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
await ForceAllAccountSynchronizationsAsync();
|
||||
await MakeSureEnableStartupLaunchAsync();
|
||||
ConfigureBackgroundTasks();
|
||||
await ConfigureBackgroundTasksAsync();
|
||||
}
|
||||
|
||||
private async Task MakeSureEnableStartupLaunchAsync()
|
||||
@@ -289,11 +289,14 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureBackgroundTasks()
|
||||
private async Task ConfigureBackgroundTasksAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// This will only unregister once. Safe to execute multiple times.
|
||||
_backgroundTaskService.UnregisterAllBackgroundTask();
|
||||
|
||||
await _backgroundTaskService.RegisterBackgroundTasksAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -623,7 +626,7 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true)
|
||||
public async Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true)
|
||||
{
|
||||
if (clickedBaseAccountMenuItem == null) return;
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
|
||||
if (item is MailItemViewModel itemViewModel)
|
||||
{
|
||||
await ExecuteUIThread(() => { itemViewModel.Update(addedItem); });
|
||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
||||
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
@@ -230,7 +230,7 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
UpdateUniqueIdHashes(itemViewModel, false);
|
||||
UpdateUniqueIdHashes(addedItem, true);
|
||||
|
||||
await ExecuteUIThread(() => { itemViewModel.Update(addedItem); });
|
||||
await ExecuteUIThread(() => { itemViewModel.MailCopy = addedItem; });
|
||||
|
||||
shouldExit = true;
|
||||
}
|
||||
@@ -349,7 +349,10 @@ namespace Wino.Mail.ViewModels.Collections
|
||||
UpdateUniqueIdHashes(itemContainer.ItemViewModel, false);
|
||||
}
|
||||
|
||||
itemContainer.ItemViewModel?.Update(updatedMailCopy);
|
||||
if (itemContainer.ItemViewModel != null)
|
||||
{
|
||||
itemContainer.ItemViewModel.MailCopy = updatedMailCopy;
|
||||
}
|
||||
|
||||
UpdateUniqueIdHashes(updatedMailCopy, true);
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
public ObservableCollection<MailAttachmentViewModel> IncludedAttachments { get; set; } = [];
|
||||
public ObservableCollection<MailAccount> Accounts { get; set; } = [];
|
||||
public ObservableCollection<AddressInformation> ToItems { get; set; } = [];
|
||||
public ObservableCollection<AddressInformation> CCItems { get; set; } = [];
|
||||
public ObservableCollection<AddressInformation> BCCItems { get; set; } = [];
|
||||
public ObservableCollection<AccountContact> ToItems { get; set; } = [];
|
||||
public ObservableCollection<AccountContact> CCItems { get; set; } = [];
|
||||
public ObservableCollection<AccountContact> BCCItems { get; set; } = [];
|
||||
|
||||
|
||||
public List<EditorToolbarSection> ToolbarSections { get; set; } =
|
||||
@@ -438,7 +438,7 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
var renderModel = _mimeFileService.GetMailRenderModel(replyingMime, mimeFilePath);
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
await ExecuteUIThread(async () =>
|
||||
{
|
||||
// Extract information
|
||||
|
||||
@@ -448,9 +448,9 @@ namespace Wino.Mail.ViewModels
|
||||
CCItems.Clear();
|
||||
BCCItems.Clear();
|
||||
|
||||
LoadAddressInfo(replyingMime.To, ToItems);
|
||||
LoadAddressInfo(replyingMime.Cc, CCItems);
|
||||
LoadAddressInfo(replyingMime.Bcc, BCCItems);
|
||||
await LoadAddressInfoAsync(replyingMime.To, ToItems);
|
||||
await LoadAddressInfoAsync(replyingMime.Cc, CCItems);
|
||||
await LoadAddressInfoAsync(replyingMime.Bcc, BCCItems);
|
||||
|
||||
LoadAttachments();
|
||||
|
||||
@@ -476,14 +476,19 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadAddressInfo(InternetAddressList list, ObservableCollection<AddressInformation> collection)
|
||||
private async Task LoadAddressInfoAsync(InternetAddressList list, ObservableCollection<AccountContact> collection)
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (item is MailboxAddress mailboxAddress)
|
||||
collection.Add(mailboxAddress.ToAddressInformation());
|
||||
{
|
||||
var foundContact = await ContactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false)
|
||||
?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address };
|
||||
|
||||
await ExecuteUIThread(() => { collection.Add(foundContact); });
|
||||
}
|
||||
else if (item is GroupAddress groupAddress)
|
||||
LoadAddressInfo(groupAddress.Members, collection);
|
||||
await LoadAddressInfoAsync(groupAddress.Members, collection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +514,7 @@ namespace Wino.Mail.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAddressInfo(IEnumerable<AddressInformation> addresses, InternetAddressList list)
|
||||
private void SaveAddressInfo(IEnumerable<AccountContact> addresses, InternetAddressList list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
@@ -517,11 +522,12 @@ namespace Wino.Mail.ViewModels
|
||||
list.Add(new MailboxAddress(item.Name, item.Address));
|
||||
}
|
||||
|
||||
public async Task<AddressInformation> GetAddressInformationAsync(string tokenText, ObservableCollection<AddressInformation> collection)
|
||||
public async Task<AccountContact> GetAddressInformationAsync(string tokenText, ObservableCollection<AccountContact> collection)
|
||||
{
|
||||
// Get model from the service. This will make sure the name is properly included if there is any record.
|
||||
|
||||
var info = await ContactService.GetAddressInformationByAddressAsync(tokenText);
|
||||
var info = await ContactService.GetAddressInformationByAddressAsync(tokenText)
|
||||
?? new AccountContact() { Name = tokenText, Address = tokenText };
|
||||
|
||||
// Don't add if there is already that address in the collection.
|
||||
if (collection.Any(a => a.Address == info.Address))
|
||||
@@ -550,7 +556,7 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
CurrentMailDraftItem.Update(updatedMail);
|
||||
CurrentMailDraftItem.MailCopy = updatedMail;
|
||||
|
||||
DiscardCommand.NotifyCanExecuteChanged();
|
||||
SendCommand.NotifyCanExecuteChanged();
|
||||
|
||||
@@ -11,12 +11,12 @@ namespace Wino.Mail.ViewModels.Data
|
||||
/// </summary>
|
||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailItem
|
||||
{
|
||||
public MailCopy MailCopy { get; private set; } = mailCopy;
|
||||
[ObservableProperty]
|
||||
private MailCopy mailCopy = mailCopy;
|
||||
|
||||
public Guid UniqueId => ((IMailItem)MailCopy).UniqueId;
|
||||
public string ThreadId => ((IMailItem)MailCopy).ThreadId;
|
||||
public string MessageId => ((IMailItem)MailCopy).MessageId;
|
||||
public string FromName => ((IMailItem)MailCopy).FromName ?? FromAddress;
|
||||
public DateTime CreationDate => ((IMailItem)MailCopy).CreationDate;
|
||||
public string References => ((IMailItem)MailCopy).References;
|
||||
public string InReplyTo => ((IMailItem)MailCopy).InReplyTo;
|
||||
@@ -33,6 +33,12 @@ namespace Wino.Mail.ViewModels.Data
|
||||
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;
|
||||
@@ -93,20 +99,7 @@ namespace Wino.Mail.ViewModels.Data
|
||||
|
||||
public Guid FileId => ((IMailItem)MailCopy).FileId;
|
||||
|
||||
public void Update(MailCopy updatedMailItem)
|
||||
{
|
||||
MailCopy = updatedMailItem;
|
||||
|
||||
OnPropertyChanged(nameof(IsRead));
|
||||
OnPropertyChanged(nameof(IsFocused));
|
||||
OnPropertyChanged(nameof(IsFlagged));
|
||||
OnPropertyChanged(nameof(IsDraft));
|
||||
OnPropertyChanged(nameof(DraftId));
|
||||
OnPropertyChanged(nameof(Subject));
|
||||
OnPropertyChanged(nameof(PreviewText));
|
||||
OnPropertyChanged(nameof(FromAddress));
|
||||
OnPropertyChanged(nameof(HasAttachments));
|
||||
}
|
||||
public AccountContact SenderContact => ((IMailItem)MailCopy).SenderContact;
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => new[] { UniqueId };
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Wino.Mail.ViewModels.Data
|
||||
public partial class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable<string>, IComparable<DateTime>
|
||||
{
|
||||
public ObservableCollection<IMailItem> ThreadItems => ((IMailItemThread)_threadMailItem).ThreadItems;
|
||||
public AccountContact SenderContact => ((IMailItemThread)_threadMailItem).SenderContact;
|
||||
|
||||
private readonly ThreadMailItem _threadMailItem;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Menus;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Reader;
|
||||
using Wino.Core.Extensions;
|
||||
using Wino.Core.Services;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Messages;
|
||||
@@ -39,6 +38,7 @@ namespace Wino.Mail.ViewModels
|
||||
private readonly Core.Domain.Interfaces.IMailService _mailService;
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IWinoRequestDelegator _requestDelegator;
|
||||
private readonly IContactService _contactService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IUnsubscriptionService _unsubscriptionService;
|
||||
private readonly IWinoServerConnectionManager _winoServerConnectionManager;
|
||||
@@ -104,13 +104,16 @@ namespace Wino.Mail.ViewModels
|
||||
[ObservableProperty]
|
||||
private string fromName;
|
||||
|
||||
[ObservableProperty]
|
||||
private string contactPicture;
|
||||
|
||||
[ObservableProperty]
|
||||
private DateTime creationDate;
|
||||
|
||||
|
||||
public ObservableCollection<AddressInformation> ToItems { get; set; } = new ObservableCollection<AddressInformation>();
|
||||
public ObservableCollection<AddressInformation> CCItemsItems { get; set; } = new ObservableCollection<AddressInformation>();
|
||||
public ObservableCollection<AddressInformation> BCCItems { get; set; } = new ObservableCollection<AddressInformation>();
|
||||
public ObservableCollection<AccountContact> ToItems { get; set; } = new ObservableCollection<AccountContact>();
|
||||
public ObservableCollection<AccountContact> CCItemsItems { get; set; } = new ObservableCollection<AccountContact>();
|
||||
public ObservableCollection<AccountContact> BCCItems { get; set; } = new ObservableCollection<AccountContact>();
|
||||
public ObservableCollection<MailAttachmentViewModel> Attachments { get; set; } = new ObservableCollection<MailAttachmentViewModel>();
|
||||
public ObservableCollection<MailOperationMenuItem> MenuItems { get; set; } = new ObservableCollection<MailOperationMenuItem>();
|
||||
|
||||
@@ -128,6 +131,7 @@ namespace Wino.Mail.ViewModels
|
||||
IFileService fileService,
|
||||
IWinoRequestDelegator requestDelegator,
|
||||
IStatePersistanceService statePersistenceService,
|
||||
IContactService contactService,
|
||||
IClipboardService clipboardService,
|
||||
IUnsubscriptionService unsubscriptionService,
|
||||
IPreferencesService preferencesService,
|
||||
@@ -135,6 +139,7 @@ namespace Wino.Mail.ViewModels
|
||||
{
|
||||
NativeAppService = nativeAppService;
|
||||
StatePersistenceService = statePersistenceService;
|
||||
_contactService = contactService;
|
||||
PreferencesService = preferencesService;
|
||||
_winoServerConnectionManager = winoServerConnectionManager;
|
||||
_clipboardService = clipboardService;
|
||||
@@ -402,9 +407,12 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
var renderingOptions = PreferencesService.GetRenderingOptions();
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
await ExecuteUIThread(async () =>
|
||||
{
|
||||
Attachments.Clear();
|
||||
ToItems.Clear();
|
||||
CCItemsItems.Clear();
|
||||
BCCItems.Clear();
|
||||
|
||||
Subject = message.Subject;
|
||||
|
||||
@@ -412,11 +420,12 @@ namespace Wino.Mail.ViewModels
|
||||
FromAddress = message.From.Mailboxes.FirstOrDefault()?.Address ?? Translator.UnknownAddress;
|
||||
FromName = message.From.Mailboxes.FirstOrDefault()?.Name ?? Translator.UnknownSender;
|
||||
CreationDate = message.Date.DateTime;
|
||||
ContactPicture = initializedMailItemViewModel.SenderContact?.Base64ContactPicture;
|
||||
|
||||
// Extract to,cc and bcc
|
||||
LoadAddressInfo(message.To, ToItems);
|
||||
LoadAddressInfo(message.Cc, CCItemsItems);
|
||||
LoadAddressInfo(message.Bcc, BCCItems);
|
||||
await LoadAddressInfoAsync(message.To, ToItems);
|
||||
await LoadAddressInfoAsync(message.Cc, CCItemsItems);
|
||||
await LoadAddressInfoAsync(message.Bcc, BCCItems);
|
||||
|
||||
// Automatically disable images for Junk folder to prevent pixel tracking.
|
||||
// This can only work for selected mail item rendering, not for EML file rendering.
|
||||
@@ -470,16 +479,19 @@ namespace Wino.Mail.ViewModels
|
||||
StatePersistenceService.IsReadingMail = false;
|
||||
}
|
||||
|
||||
private void LoadAddressInfo(InternetAddressList list, ObservableCollection<AddressInformation> collection)
|
||||
private async Task LoadAddressInfoAsync(InternetAddressList list, ObservableCollection<AccountContact> collection)
|
||||
{
|
||||
collection.Clear();
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (item is MailboxAddress mailboxAddress)
|
||||
collection.Add(mailboxAddress.ToAddressInformation());
|
||||
{
|
||||
var foundContact = await _contactService.GetAddressInformationByAddressAsync(mailboxAddress.Address).ConfigureAwait(false)
|
||||
?? new AccountContact() { Name = mailboxAddress.Name, Address = mailboxAddress.Address };
|
||||
|
||||
await ExecuteUIThread(() => { collection.Add(foundContact); });
|
||||
}
|
||||
else if (item is GroupAddress groupAddress)
|
||||
LoadAddressInfo(groupAddress.Members, collection);
|
||||
await LoadAddressInfoAsync(groupAddress.Members, collection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Personalization;
|
||||
@@ -21,6 +22,14 @@ namespace Wino.Mail.ViewModels
|
||||
|
||||
private bool isPropChangeDisabled = false;
|
||||
|
||||
// Sample mail copy to use in previewing mail display modes.
|
||||
public MailCopy DemoPreviewMailCopy { get; } = new MailCopy()
|
||||
{
|
||||
FromName = "Sender Name",
|
||||
Subject = "Mail Subject",
|
||||
PreviewText = "Thank you for using Wino Mail. We hope you enjoy the experience.",
|
||||
};
|
||||
|
||||
#region Personalization
|
||||
|
||||
public bool IsSelectedWindowsAccentColor => SelectedAppColor == Colors.LastOrDefault();
|
||||
|
||||
@@ -304,7 +304,7 @@ namespace Wino
|
||||
{
|
||||
base.OnWindowCreated(args);
|
||||
|
||||
LogActivation("Window is created.");
|
||||
LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}");
|
||||
|
||||
ConfigureTitleBar();
|
||||
TryRegisterAppCloseChange();
|
||||
@@ -336,7 +336,7 @@ namespace Wino
|
||||
{
|
||||
base.OnFileActivated(args);
|
||||
|
||||
Log.Information($"File activation for {args.Files.Count} item(s).");
|
||||
LogActivation($"OnFileActivated -> ItemCount: {args.Files.Count}, Kind: {args.Kind}, PreviousExecutionState: {args.PreviousExecutionState}");
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
@@ -356,6 +356,8 @@ namespace Wino
|
||||
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails appServiceTriggerDetails)
|
||||
{
|
||||
LogActivation("OnBackgroundActivated -> AppServiceTriggerDetails received.");
|
||||
|
||||
// Only accept connections from callers in the same package
|
||||
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
|
||||
{
|
||||
@@ -373,6 +375,8 @@ namespace Wino
|
||||
{
|
||||
// Notification action is triggered and the app is not running.
|
||||
|
||||
LogActivation("OnBackgroundActivated -> ToastNotificationActionTriggerDetail received.");
|
||||
|
||||
toastActionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnToastActionClickedBackgroundTaskCanceled;
|
||||
|
||||
|
||||
@@ -142,8 +142,6 @@ namespace Wino.Views
|
||||
|
||||
if (ViewModel.MenuItems.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem foundMenuItem))
|
||||
{
|
||||
if (foundMenuItem == null) return;
|
||||
|
||||
foundMenuItem.Expand();
|
||||
|
||||
await ViewModel.NavigateFolderAsync(foundMenuItem);
|
||||
@@ -153,7 +151,27 @@ namespace Wino.Views
|
||||
if (message.NavigateMailItem == null) return;
|
||||
|
||||
// At this point folder is navigated and items are loaded.
|
||||
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId));
|
||||
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
|
||||
}
|
||||
else if (ViewModel.MenuItems.TryGetAccountMenuItem(message.NavigateMailItem.AssignedAccount.Id, out IAccountMenuItem accountMenuItem))
|
||||
{
|
||||
// Loaded account is different. First change the folder items and navigate.
|
||||
|
||||
await ViewModel.ChangeLoadedAccountAsync(accountMenuItem, navigateInbox: false);
|
||||
|
||||
// Find the folder.
|
||||
|
||||
if (ViewModel.MenuItems.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem accountFolderMenuItem))
|
||||
{
|
||||
accountFolderMenuItem.Expand();
|
||||
|
||||
await ViewModel.NavigateFolderAsync(accountFolderMenuItem);
|
||||
|
||||
navigationView.SelectedItem = accountFolderMenuItem;
|
||||
|
||||
// At this point folder is navigated and items are loaded.
|
||||
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Fernandezja.ColorHashSharp;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
@@ -22,7 +26,16 @@ namespace Wino.Controls
|
||||
|
||||
public static readonly DependencyProperty FromNameProperty = DependencyProperty.Register(nameof(FromName), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnAddressInformationChanged));
|
||||
public static readonly DependencyProperty FromAddressProperty = DependencyProperty.Register(nameof(FromAddress), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnAddressInformationChanged));
|
||||
public static readonly DependencyProperty IsKnownProperty = DependencyProperty.Register(nameof(IsKnown), typeof(bool), typeof(ImagePreviewControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty SenderContactPictureProperty = DependencyProperty.Register(nameof(SenderContactPicture), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnAddressInformationChanged)));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets base64 string of the sender contact picture.
|
||||
/// </summary>
|
||||
public string SenderContactPicture
|
||||
{
|
||||
get { return (string)GetValue(SenderContactPictureProperty); }
|
||||
set { SetValue(SenderContactPictureProperty, value); }
|
||||
}
|
||||
|
||||
public string FromName
|
||||
{
|
||||
@@ -36,20 +49,13 @@ namespace Wino.Controls
|
||||
set { SetValue(FromAddressProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsKnown
|
||||
{
|
||||
get { return (bool)GetValue(IsKnownProperty); }
|
||||
set { SetValue(IsKnownProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
private Ellipse Ellipse;
|
||||
private Grid InitialsGrid;
|
||||
private TextBlock InitialsTextblock;
|
||||
private Image KnownHostImage;
|
||||
private CancellationTokenSource contactPictureLoadingCancellationTokenSource;
|
||||
|
||||
public ImagePreviewControl()
|
||||
{
|
||||
@@ -74,22 +80,32 @@ namespace Wino.Controls
|
||||
control.UpdateInformation();
|
||||
}
|
||||
|
||||
private void UpdateInformation()
|
||||
|
||||
|
||||
private async void UpdateInformation()
|
||||
{
|
||||
if (KnownHostImage == null || InitialsGrid == null || InitialsTextblock == null || (string.IsNullOrEmpty(FromName) && string.IsNullOrEmpty(FromAddress)))
|
||||
return;
|
||||
|
||||
// Cancel active image loading if exists.
|
||||
if (!contactPictureLoadingCancellationTokenSource?.IsCancellationRequested ?? false)
|
||||
{
|
||||
contactPictureLoadingCancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
var host = ThumbnailService.GetHost(FromAddress);
|
||||
|
||||
bool isKnownHost = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(host))
|
||||
{
|
||||
var tuple = ThumbnailService.CheckIsKnown(host);
|
||||
|
||||
IsKnown = tuple.Item1;
|
||||
isKnownHost = tuple.Item1;
|
||||
host = tuple.Item2;
|
||||
}
|
||||
|
||||
if (IsKnown)
|
||||
if (isKnownHost)
|
||||
{
|
||||
// Unrealize others.
|
||||
|
||||
@@ -104,15 +120,54 @@ namespace Wino.Controls
|
||||
KnownHostImage.Visibility = Visibility.Collapsed;
|
||||
InitialsGrid.Visibility = Visibility.Visible;
|
||||
|
||||
var colorHash = new ColorHash();
|
||||
var rgb = colorHash.Rgb(FromAddress);
|
||||
if (!string.IsNullOrEmpty(SenderContactPicture))
|
||||
{
|
||||
contactPictureLoadingCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
Ellipse.Fill = new SolidColorBrush(Color.FromArgb(rgb.A, rgb.R, rgb.G, rgb.B));
|
||||
try
|
||||
{
|
||||
var brush = await GetContactImageBrushAsync();
|
||||
|
||||
InitialsTextblock.Text = ExtractInitialsFromName(FromName);
|
||||
if (!contactPictureLoadingCancellationTokenSource?.Token.IsCancellationRequested ?? false)
|
||||
{
|
||||
Ellipse.Fill = brush;
|
||||
InitialsTextblock.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Log exception.
|
||||
Debugger.Break();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var colorHash = new ColorHash();
|
||||
var rgb = colorHash.Rgb(FromAddress);
|
||||
|
||||
Ellipse.Fill = new SolidColorBrush(Color.FromArgb(rgb.A, rgb.R, rgb.G, rgb.B));
|
||||
InitialsTextblock.Text = ExtractInitialsFromName(FromName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ImageBrush> GetContactImageBrushAsync()
|
||||
{
|
||||
// Load the image from base64 string.
|
||||
var bitmapImage = new BitmapImage();
|
||||
|
||||
var imageArray = Convert.FromBase64String(SenderContactPicture);
|
||||
var imageStream = new MemoryStream(imageArray);
|
||||
var randomAccessImageStream = imageStream.AsRandomAccessStream();
|
||||
|
||||
randomAccessImageStream.Seek(0);
|
||||
|
||||
|
||||
await bitmapImage.SetSourceAsync(randomAccessImageStream);
|
||||
|
||||
return new ImageBrush() { ImageSource = bitmapImage };
|
||||
}
|
||||
|
||||
public string ExtractInitialsFromName(string name)
|
||||
{
|
||||
// Change from name to from address in case of name doesn't exists.
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
xmlns:controls="using:Wino.Controls"
|
||||
xmlns:enums="using:Wino.Core.Domain.Enums"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
FocusVisualMargin="8"
|
||||
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
|
||||
FocusVisualPrimaryThickness="2"
|
||||
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="1"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
PointerEntered="ControlPointerEntered"
|
||||
PointerExited="ControlPointerExited">
|
||||
@@ -61,8 +66,9 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FromAddress="{x:Bind FromAddress, Mode=OneWay}"
|
||||
FromName="{x:Bind DisplayName, Mode=OneWay}"
|
||||
SenderContactPicture="{x:Bind MailItem.SenderContact.Base64ContactPicture}"
|
||||
FromAddress="{x:Bind MailItem.FromAddress, Mode=OneWay}"
|
||||
FromName="{x:Bind MailItem.FromName, Mode=OneWay}"
|
||||
Visibility="{x:Bind IsAvatarVisible, Mode=OneWay}" />
|
||||
|
||||
<Grid
|
||||
@@ -90,7 +96,7 @@
|
||||
<TextBlock
|
||||
x:Name="DraftTitle"
|
||||
Margin="0,0,4,0"
|
||||
x:Load="{x:Bind IsDraft, Mode=OneWay}"
|
||||
x:Load="{x:Bind MailItem.IsDraft, Mode=OneWay}"
|
||||
Foreground="{StaticResource DeleteBrush}">
|
||||
|
||||
<Run Text="[" /><Run Text="{x:Bind domain:Translator.Draft}" /><Run Text="]" /> <Run Text=" " />
|
||||
@@ -100,7 +106,7 @@
|
||||
<TextBlock
|
||||
x:Name="SenderText"
|
||||
Grid.Column="1"
|
||||
Text="{x:Bind DisplayName}"
|
||||
Text="{x:Bind MailItem.FromName}"
|
||||
TextTrimming="WordEllipsis" />
|
||||
|
||||
<!-- Hover button -->
|
||||
@@ -148,7 +154,7 @@
|
||||
<TextBlock
|
||||
x:Name="TitleText"
|
||||
MaxLines="1"
|
||||
Text="{x:Bind Subject}"
|
||||
Text="{x:Bind MailItem.Subject}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<TextBlock
|
||||
@@ -157,7 +163,7 @@
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Opacity="0.7"
|
||||
Text="{x:Bind helpers:XamlHelpers.GetMailItemDisplaySummaryForListing(IsDraft, ReceivedDate, Prefer24HourTimeFormat)}" />
|
||||
Text="{x:Bind helpers:XamlHelpers.GetMailItemDisplaySummaryForListing(MailItem.IsDraft, MailItem.CreationDate, Prefer24HourTimeFormat)}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Message -->
|
||||
@@ -173,10 +179,10 @@
|
||||
<Grid x:Name="PreviewTextContainer">
|
||||
<TextBlock
|
||||
x:Name="PreviewTextblock"
|
||||
x:Load="{x:Bind helpers:XamlHelpers.ShouldDisplayPreview(Snippet), Mode=OneWay}"
|
||||
x:Load="{x:Bind helpers:XamlHelpers.ShouldDisplayPreview(MailItem.PreviewText), Mode=OneWay}"
|
||||
MaxLines="1"
|
||||
Opacity="0.7"
|
||||
Text="{x:Bind Snippet}"
|
||||
Text="{x:Bind MailItem.PreviewText}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
|
||||
@@ -190,12 +196,12 @@
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="HasAttachmentContent"
|
||||
x:Load="{x:Bind HasAttachments, Mode=OneWay}"
|
||||
x:Load="{x:Bind MailItem.HasAttachments, Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource AttachmentSymbolControlTemplate}" />
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="IsFlaggedContent"
|
||||
x:Load="{x:Bind IsFlagged, Mode=OneWay}"
|
||||
x:Load="{x:Bind MailItem.IsFlagged, Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource FlaggedSymbolControlTemplate}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -209,7 +215,7 @@
|
||||
<VisualStateGroup x:Name="ReadStates">
|
||||
<VisualState x:Name="Unread">
|
||||
<VisualState.StateTriggers>
|
||||
<StateTrigger IsActive="{x:Bind IsRead, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}" />
|
||||
<StateTrigger IsActive="{x:Bind MailItem.IsRead, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}" />
|
||||
</VisualState.StateTriggers>
|
||||
|
||||
<VisualState.Setters>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Wino.Core.Domain.Entities;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Extensions;
|
||||
@@ -13,22 +12,13 @@ using Wino.Mail.ViewModels.Data;
|
||||
|
||||
namespace Wino.Controls
|
||||
{
|
||||
public sealed partial class MailItemDisplayInformationControl : UserControl, INotifyPropertyChanged
|
||||
public sealed partial class MailItemDisplayInformationControl : UserControl
|
||||
{
|
||||
public ImagePreviewControl GetImagePreviewControl() => ContactImage;
|
||||
|
||||
public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(MailListDisplayMode), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailListDisplayMode.Spacious));
|
||||
public static readonly DependencyProperty ShowPreviewTextProperty = DependencyProperty.Register(nameof(ShowPreviewText), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty SnippetProperty = DependencyProperty.Register(nameof(Snippet), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty));
|
||||
public static readonly DependencyProperty FromNameProperty = DependencyProperty.Register(nameof(FromName), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty));
|
||||
public static readonly DependencyProperty SubjectProperty = DependencyProperty.Register(nameof(Subject), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata("(no-subject)"));
|
||||
public static readonly DependencyProperty IsReadProperty = DependencyProperty.Register(nameof(IsRead), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsFlaggedProperty = DependencyProperty.Register(nameof(IsFlagged), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty FromAddressProperty = DependencyProperty.Register(nameof(FromAddress), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty));
|
||||
public static readonly DependencyProperty HasAttachmentsProperty = DependencyProperty.Register(nameof(HasAttachments), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsCustomFocusedProperty = DependencyProperty.Register(nameof(IsCustomFocused), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty ReceivedDateProperty = DependencyProperty.Register(nameof(ReceivedDate), typeof(DateTime), typeof(MailItemDisplayInformationControl), new PropertyMetadata(default(DateTime)));
|
||||
public static readonly DependencyProperty IsDraftProperty = DependencyProperty.Register(nameof(IsDraft), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsAvatarVisibleProperty = DependencyProperty.Register(nameof(IsAvatarVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsSubjectVisibleProperty = DependencyProperty.Register(nameof(IsSubjectVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty ConnectedExpanderProperty = DependencyProperty.Register(nameof(ConnectedExpander), typeof(Expander), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null));
|
||||
@@ -83,8 +73,6 @@ namespace Wino.Controls
|
||||
}
|
||||
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Expander ConnectedExpander
|
||||
{
|
||||
get { return (Expander)GetValue(ConnectedExpanderProperty); }
|
||||
@@ -103,85 +91,12 @@ namespace Wino.Controls
|
||||
set { SetValue(IsAvatarVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsDraft
|
||||
{
|
||||
get { return (bool)GetValue(IsDraftProperty); }
|
||||
set { SetValue(IsDraftProperty, value); }
|
||||
}
|
||||
|
||||
public DateTime ReceivedDate
|
||||
{
|
||||
get { return (DateTime)GetValue(ReceivedDateProperty); }
|
||||
set { SetValue(ReceivedDateProperty, value); }
|
||||
}
|
||||
public bool IsCustomFocused
|
||||
{
|
||||
get { return (bool)GetValue(IsCustomFocusedProperty); }
|
||||
set { SetValue(IsCustomFocusedProperty, value); }
|
||||
}
|
||||
|
||||
public bool HasAttachments
|
||||
{
|
||||
get { return (bool)GetValue(HasAttachmentsProperty); }
|
||||
set { SetValue(HasAttachmentsProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsRead
|
||||
{
|
||||
get { return (bool)GetValue(IsReadProperty); }
|
||||
set { SetValue(IsReadProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsFlagged
|
||||
{
|
||||
get { return (bool)GetValue(IsFlaggedProperty); }
|
||||
set { SetValue(IsFlaggedProperty, value); }
|
||||
}
|
||||
|
||||
public string FromAddress
|
||||
{
|
||||
get { return (string)GetValue(FromAddressProperty); }
|
||||
set
|
||||
{
|
||||
SetValue(FromAddressProperty, value);
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName)));
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(FromName))
|
||||
return FromAddress;
|
||||
|
||||
return FromName;
|
||||
}
|
||||
}
|
||||
public string FromName
|
||||
{
|
||||
get => (string)GetValue(FromNameProperty);
|
||||
set
|
||||
{
|
||||
SetValue(FromNameProperty, value);
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName)));
|
||||
}
|
||||
}
|
||||
|
||||
public string Subject
|
||||
{
|
||||
get { return (string)GetValue(SubjectProperty); }
|
||||
set { SetValue(SubjectProperty, value); }
|
||||
}
|
||||
|
||||
public string Snippet
|
||||
{
|
||||
get { return (string)GetValue(SnippetProperty); }
|
||||
set { SetValue(SnippetProperty, value); }
|
||||
}
|
||||
|
||||
public bool ShowPreviewText
|
||||
{
|
||||
get { return (bool)GetValue(ShowPreviewTextProperty); }
|
||||
@@ -234,8 +149,8 @@ namespace Wino.Controls
|
||||
{
|
||||
MailOperationPreperationRequest package = null;
|
||||
|
||||
if (MailItem is MailItemViewModel mailItemViewModel)
|
||||
package = new MailOperationPreperationRequest(operation, mailItemViewModel.MailCopy, toggleExecution: true);
|
||||
if (MailItem is MailCopy mailCopy)
|
||||
package = new MailOperationPreperationRequest(operation, mailCopy, toggleExecution: true);
|
||||
else if (MailItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies(), toggleExecution: true);
|
||||
|
||||
|
||||
@@ -20,16 +20,30 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<DataTemplate x:Key="TokenBoxTemplate" x:DataType="entities:AddressInformation">
|
||||
<DataTemplate x:Key="TokenBoxTemplate" x:DataType="entities:AccountContact">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind Address}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Grid.ContextFlyout>
|
||||
|
||||
<!-- TODO: Display contact info. -->
|
||||
<!--<Grid.ContextFlyout>
|
||||
<MenuFlyout Placement="RightEdgeAlignedBottom">
|
||||
<MenuFlyoutItem Text="{x:Bind domain:Translator.ViewContactDetails}" />
|
||||
</MenuFlyout>
|
||||
</Grid.ContextFlyout>
|
||||
</Grid.ContextFlyout>-->
|
||||
|
||||
<Viewbox Width="24">
|
||||
<controls:ImagePreviewControl
|
||||
SenderContactPicture="{x:Bind Base64ContactPicture}"
|
||||
FromAddress="{x:Bind Address}"
|
||||
FromName="{x:Bind Name}" />
|
||||
</Viewbox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="6,0,8,0"
|
||||
@@ -37,7 +51,7 @@
|
||||
Text="{x:Bind Name}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="SuggestionBoxTemplate" x:DataType="entities:AddressInformation">
|
||||
<DataTemplate x:Key="SuggestionBoxTemplate" x:DataType="entities:AccountContact">
|
||||
<Grid Margin="0,12" ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -46,7 +60,7 @@
|
||||
<controls:ImagePreviewControl
|
||||
FromAddress="{x:Bind Address}"
|
||||
FromName="{x:Bind Name}"
|
||||
IsKnown="False" />
|
||||
SenderContactPicture="{x:Bind Base64ContactPicture}" />
|
||||
<TextBlock Grid.Column="1">
|
||||
<Run FontWeight="SemiBold" Text="{x:Bind Name}" /><LineBreak /><Run Text="{x:Bind Address}" />
|
||||
</TextBlock>
|
||||
|
||||
@@ -21,6 +21,7 @@ using Windows.Storage.Pickers;
|
||||
using Windows.UI.ViewManagement.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.Domain;
|
||||
@@ -62,6 +63,19 @@ namespace Wino.Views
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation");
|
||||
}
|
||||
|
||||
private async void GlobalFocusManagerGotFocus(object sender, FocusManagerGotFocusEventArgs e)
|
||||
{
|
||||
// In order to delegate cursor to the inner editor for WebView2.
|
||||
// When the control got focus, we invoke script to focus the editor.
|
||||
// This is not done on the WebView2 handlers, because somehow it is
|
||||
// repeatedly focusing itself, even though when it has the focus already.
|
||||
|
||||
if (e.NewFocusedElement == Chromium)
|
||||
{
|
||||
await FocusEditorAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is ComposePage page)
|
||||
@@ -356,19 +370,26 @@ namespace Wino.Views
|
||||
await InvokeScriptSafeAsync("imageInput.click();");
|
||||
}
|
||||
|
||||
private async Task FocusEditorAsync()
|
||||
/// <summary>
|
||||
/// Places the cursor in the composer.
|
||||
/// </summary>
|
||||
/// <param name="focusControlAsWell">Whether control itself should be focused as well or not.</param>
|
||||
private async Task FocusEditorAsync(bool focusControlAsWell)
|
||||
{
|
||||
await InvokeScriptSafeAsync("editor.selection.focus();");
|
||||
|
||||
Chromium.Focus(FocusState.Keyboard);
|
||||
Chromium.Focus(FocusState.Programmatic);
|
||||
if (focusControlAsWell)
|
||||
{
|
||||
Chromium.Focus(FocusState.Keyboard);
|
||||
Chromium.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
private async void EmojiButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji);
|
||||
|
||||
await FocusEditorAsync();
|
||||
await FocusEditorAsync(focusControlAsWell: true);
|
||||
}
|
||||
|
||||
public async Task UpdateEditorThemeAsync()
|
||||
@@ -440,6 +461,8 @@ namespace Wino.Views
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
FocusManager.GotFocus += GlobalFocusManagerGotFocus;
|
||||
|
||||
var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation");
|
||||
anim?.TryStart(Chromium);
|
||||
|
||||
@@ -574,7 +597,7 @@ namespace Wino.Views
|
||||
|
||||
var deferal = args.GetDeferral();
|
||||
|
||||
AddressInformation addedItem = null;
|
||||
AccountContact addedItem = null;
|
||||
|
||||
var boxTag = sender.Tag?.ToString();
|
||||
|
||||
@@ -644,8 +667,8 @@ namespace Wino.Views
|
||||
{
|
||||
var boxTag = tokenizingTextBox.Tag?.ToString();
|
||||
|
||||
AddressInformation addedItem = null;
|
||||
ObservableCollection<AddressInformation> addressCollection = null;
|
||||
AccountContact addedItem = null;
|
||||
ObservableCollection<AccountContact> addressCollection = null;
|
||||
|
||||
if (boxTag == "ToBox")
|
||||
addressCollection = ViewModel.ToItems;
|
||||
@@ -694,6 +717,7 @@ namespace Wino.Views
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
|
||||
FocusManager.GotFocus -= GlobalFocusManagerGotFocus;
|
||||
await ViewModel.UpdateMimeChangesAsync();
|
||||
|
||||
DisposeDisposables();
|
||||
|
||||
@@ -118,29 +118,15 @@
|
||||
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
|
||||
ContextRequested="MailItemContextRequested"
|
||||
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
|
||||
FocusVisualMargin="8"
|
||||
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
|
||||
FocusVisualPrimaryThickness="2"
|
||||
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="1"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft, Mode=OneWay}"
|
||||
IsFlagged="{x:Bind IsFlagged, Mode=OneWay}"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
IsRead="{x:Bind IsRead, Mode=OneWay}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
MailItem="{Binding}"
|
||||
MailItem="{x:Bind MailCopy, Mode=OneWay}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Single Mail Item Template for Threads -->
|
||||
@@ -155,24 +141,15 @@
|
||||
FocusVisualPrimaryThickness="2"
|
||||
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="1"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft, Mode=OneWay}"
|
||||
IsFlagged="{x:Bind IsFlagged, Mode=OneWay}"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
IsRead="{x:Bind IsRead, Mode=OneWay}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
MailItem="{Binding}"
|
||||
MailItem="{x:Bind MailCopy, Mode=OneWay}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Mail Item Content Selector -->
|
||||
@@ -199,24 +176,15 @@
|
||||
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
|
||||
DragStarting="ThreadHeaderDragStart"
|
||||
DropCompleted="ThreadHeaderDragFinished"
|
||||
FromAddress="{x:Bind FromAddress}"
|
||||
FromName="{x:Bind FromName}"
|
||||
HasAttachments="{x:Bind HasAttachments}"
|
||||
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
|
||||
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
|
||||
IsDraft="{x:Bind IsDraft}"
|
||||
IsFlagged="{x:Bind IsFlagged}"
|
||||
IsHitTestVisible="True"
|
||||
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
|
||||
IsRead="{x:Bind IsRead}"
|
||||
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
|
||||
MailItem="{Binding}"
|
||||
MailItem="{Binding Mode=OneWay}"
|
||||
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
|
||||
ReceivedDate="{x:Bind CreationDate}"
|
||||
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}"
|
||||
Snippet="{x:Bind PreviewText}"
|
||||
Subject="{x:Bind Subject}" />
|
||||
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<listview:WinoListView
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,6 +7,7 @@
|
||||
xmlns:controls1="using:Wino.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
x:Name="root"
|
||||
xmlns:enums="using:Wino.Core.Domain.Enums"
|
||||
xmlns:helpers="using:Wino.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -141,28 +142,22 @@
|
||||
<DataTemplate x:Key="CompactDisplayModePreviewTemplate" x:DataType="enums:MailListDisplayMode">
|
||||
<controls1:MailItemDisplayInformationControl
|
||||
DisplayMode="Compact"
|
||||
FromName="{x:Bind domain:Translator.SettingsPersonalizationMailDisplayCompactMode}"
|
||||
ShowPreviewText="False"
|
||||
Snippet="Thank you for using Wino Mail."
|
||||
Subject="Welcome to Wino Mail" />
|
||||
MailItem="{Binding ElementName=root, Path=ViewModel.DemoPreviewMailCopy}"
|
||||
ShowPreviewText="False" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="MediumDisplayModePreviewTemplate" x:DataType="enums:MailListDisplayMode">
|
||||
<controls1:MailItemDisplayInformationControl
|
||||
DisplayMode="Medium"
|
||||
FromName="{x:Bind domain:Translator.SettingsPersonalizationMailDisplayMediumMode}"
|
||||
ShowPreviewText="True"
|
||||
Snippet="Thank you for using Wino Mail."
|
||||
Subject="Welcome to Wino Mail" />
|
||||
MailItem="{Binding ElementName=root, Path=ViewModel.DemoPreviewMailCopy}" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="SpaciousDisplayModePreviewTemplate" x:DataType="enums:MailListDisplayMode">
|
||||
<controls1:MailItemDisplayInformationControl
|
||||
DisplayMode="Spacious"
|
||||
FromName="{x:Bind domain:Translator.SettingsPersonalizationMailDisplaySpaciousMode}"
|
||||
ShowPreviewText="True"
|
||||
Snippet="Thank you for using Wino Mail."
|
||||
Subject="Welcome to Wino Mail" />
|
||||
MailItem="{Binding ElementName=root, Path=ViewModel.DemoPreviewMailCopy}"
|
||||
ShowPreviewText="True" />
|
||||
</DataTemplate>
|
||||
|
||||
<selectors:MailItemDisplayModePreviewTemplateSelector
|
||||
|
||||
Reference in New Issue
Block a user