file scoped namespaces (#565)
This commit is contained in:
@@ -5,251 +5,250 @@ using MimeKit;
|
||||
using MimeKit.Text;
|
||||
using MimeKit.Tnef;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
|
||||
/// </summary>
|
||||
public class HtmlPreviewVisitor : MimeVisitor
|
||||
{
|
||||
List<MultipartRelated> stack = new List<MultipartRelated>();
|
||||
List<MimeEntity> attachments = new List<MimeEntity>();
|
||||
|
||||
readonly string tempDir;
|
||||
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
|
||||
/// Creates a new HtmlPreviewVisitor.
|
||||
/// </summary>
|
||||
public class HtmlPreviewVisitor : MimeVisitor
|
||||
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
|
||||
public HtmlPreviewVisitor(string tempDirectory)
|
||||
{
|
||||
List<MultipartRelated> stack = new List<MultipartRelated>();
|
||||
List<MimeEntity> attachments = new List<MimeEntity>();
|
||||
tempDir = tempDirectory;
|
||||
}
|
||||
|
||||
readonly string tempDir;
|
||||
/// <summary>
|
||||
/// The list of attachments that were in the MimeMessage.
|
||||
/// </summary>
|
||||
public IList<MimeEntity> Attachments
|
||||
{
|
||||
get { return attachments; }
|
||||
}
|
||||
|
||||
public string Body { get; set; }
|
||||
/// <summary>
|
||||
/// The HTML string that can be set on the BrowserControl.
|
||||
/// </summary>
|
||||
public string HtmlBody
|
||||
{
|
||||
get { return Body ?? string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HtmlPreviewVisitor.
|
||||
/// </summary>
|
||||
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
|
||||
public HtmlPreviewVisitor(string tempDirectory)
|
||||
protected override void VisitMultipartAlternative(MultipartAlternative alternative)
|
||||
{
|
||||
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
|
||||
for (int i = alternative.Count - 1; i >= 0 && Body == null; i--)
|
||||
alternative[i].Accept(this);
|
||||
}
|
||||
|
||||
protected override void VisitMultipartRelated(MultipartRelated related)
|
||||
{
|
||||
var root = related.Root;
|
||||
|
||||
// push this multipart/related onto our stack
|
||||
stack.Add(related);
|
||||
|
||||
// visit the root document
|
||||
root.Accept(this);
|
||||
|
||||
// pop this multipart/related off our stack
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
}
|
||||
|
||||
// look up the image based on the img src url within our multipart/related stack
|
||||
bool TryGetImage(string url, out MimePart image)
|
||||
{
|
||||
UriKind kind;
|
||||
int index;
|
||||
Uri uri;
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
kind = UriKind.Absolute;
|
||||
else if (Uri.IsWellFormedUriString(url, UriKind.Relative))
|
||||
kind = UriKind.Relative;
|
||||
else
|
||||
kind = UriKind.RelativeOrAbsolute;
|
||||
|
||||
try
|
||||
{
|
||||
tempDir = tempDirectory;
|
||||
uri = new Uri(url, kind);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of attachments that were in the MimeMessage.
|
||||
/// </summary>
|
||||
public IList<MimeEntity> Attachments
|
||||
catch
|
||||
{
|
||||
get { return attachments; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML string that can be set on the BrowserControl.
|
||||
/// </summary>
|
||||
public string HtmlBody
|
||||
{
|
||||
get { return Body ?? string.Empty; }
|
||||
}
|
||||
|
||||
protected override void VisitMultipartAlternative(MultipartAlternative alternative)
|
||||
{
|
||||
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
|
||||
for (int i = alternative.Count - 1; i >= 0 && Body == null; i--)
|
||||
alternative[i].Accept(this);
|
||||
}
|
||||
|
||||
protected override void VisitMultipartRelated(MultipartRelated related)
|
||||
{
|
||||
var root = related.Root;
|
||||
|
||||
// push this multipart/related onto our stack
|
||||
stack.Add(related);
|
||||
|
||||
// visit the root document
|
||||
root.Accept(this);
|
||||
|
||||
// pop this multipart/related off our stack
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
}
|
||||
|
||||
// look up the image based on the img src url within our multipart/related stack
|
||||
bool TryGetImage(string url, out MimePart image)
|
||||
{
|
||||
UriKind kind;
|
||||
int index;
|
||||
Uri uri;
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
kind = UriKind.Absolute;
|
||||
else if (Uri.IsWellFormedUriString(url, UriKind.Relative))
|
||||
kind = UriKind.Relative;
|
||||
else
|
||||
kind = UriKind.RelativeOrAbsolute;
|
||||
|
||||
try
|
||||
{
|
||||
uri = new Uri(url, kind);
|
||||
}
|
||||
catch
|
||||
{
|
||||
image = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = stack.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if ((index = stack[i].IndexOf(uri)) == -1)
|
||||
continue;
|
||||
|
||||
image = stack[i][index] as MimePart;
|
||||
return image != null;
|
||||
}
|
||||
|
||||
image = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the image to our temp directory and return a "file://" url suitable for
|
||||
// the browser control to load.
|
||||
// Note: if you'd rather embed the image data into the HTML, you can construct a
|
||||
// "data:" url instead.
|
||||
string SaveImage(MimePart image)
|
||||
for (int i = stack.Count - 1; i >= 0; i--)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
image.Content.DecodeTo(memory);
|
||||
var buffer = memory.GetBuffer();
|
||||
var length = (int)memory.Length;
|
||||
var base64 = Convert.ToBase64String(buffer, 0, length);
|
||||
if ((index = stack[i].IndexOf(uri)) == -1)
|
||||
continue;
|
||||
|
||||
return string.Format("data:{0};base64,{1}", image.ContentType.MimeType, base64);
|
||||
}
|
||||
|
||||
//string fileName = url
|
||||
// .Replace(':', '_')
|
||||
// .Replace('\\', '_')
|
||||
// .Replace('/', '_');
|
||||
|
||||
//string path = Path.Combine(tempDir, fileName);
|
||||
|
||||
//if (!File.Exists(path))
|
||||
//{
|
||||
// using (var output = File.Create(path))
|
||||
// image.Content.DecodeTo(output);
|
||||
//}
|
||||
|
||||
//return "file://" + path.Replace('\\', '/');
|
||||
image = stack[i][index] as MimePart;
|
||||
return image != null;
|
||||
}
|
||||
|
||||
// Replaces <img src=...> urls that refer to images embedded within the message with
|
||||
// "file://" urls that the browser control will actually be able to load.
|
||||
void HtmlTagCallback(HtmlTagContext ctx, HtmlWriter htmlWriter)
|
||||
image = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the image to our temp directory and return a "file://" url suitable for
|
||||
// the browser control to load.
|
||||
// Note: if you'd rather embed the image data into the HTML, you can construct a
|
||||
// "data:" url instead.
|
||||
string SaveImage(MimePart image)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0)
|
||||
image.Content.DecodeTo(memory);
|
||||
var buffer = memory.GetBuffer();
|
||||
var length = (int)memory.Length;
|
||||
var base64 = Convert.ToBase64String(buffer, 0, length);
|
||||
|
||||
return string.Format("data:{0};base64,{1}", image.ContentType.MimeType, base64);
|
||||
}
|
||||
|
||||
//string fileName = url
|
||||
// .Replace(':', '_')
|
||||
// .Replace('\\', '_')
|
||||
// .Replace('/', '_');
|
||||
|
||||
//string path = Path.Combine(tempDir, fileName);
|
||||
|
||||
//if (!File.Exists(path))
|
||||
//{
|
||||
// using (var output = File.Create(path))
|
||||
// image.Content.DecodeTo(output);
|
||||
//}
|
||||
|
||||
//return "file://" + path.Replace('\\', '/');
|
||||
}
|
||||
|
||||
// Replaces <img src=...> urls that refer to images embedded within the message with
|
||||
// "file://" urls that the browser control will actually be able to load.
|
||||
void HtmlTagCallback(HtmlTagContext ctx, HtmlWriter htmlWriter)
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// replace the src attribute with a file:// URL
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// replace the src attribute with a file:// URL
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
if (attribute.Id == HtmlAttributeId.Src)
|
||||
{
|
||||
if (attribute.Id == HtmlAttributeId.Src)
|
||||
{
|
||||
MimePart image;
|
||||
string url;
|
||||
MimePart image;
|
||||
string url;
|
||||
|
||||
if (!TryGetImage(attribute.Value, out image))
|
||||
{
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
continue;
|
||||
}
|
||||
|
||||
url = SaveImage(image);
|
||||
|
||||
htmlWriter.WriteAttributeName(attribute.Name);
|
||||
htmlWriter.WriteAttributeValue(url);
|
||||
}
|
||||
else
|
||||
if (!TryGetImage(attribute.Value, out image))
|
||||
{
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// add and/or replace oncontextmenu="return false;"
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
if (attribute.Name.ToLowerInvariant() == "oncontextmenu")
|
||||
continue;
|
||||
}
|
||||
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
url = SaveImage(image);
|
||||
|
||||
htmlWriter.WriteAttribute("oncontextmenu", "return false;");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Unknown)
|
||||
{
|
||||
ctx.DeleteTag = true;
|
||||
ctx.DeleteEndTag = true;
|
||||
htmlWriter.WriteAttributeName(attribute.Name);
|
||||
htmlWriter.WriteAttributeValue(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, true);
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VisitTextPart(TextPart entity)
|
||||
else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag)
|
||||
{
|
||||
TextConverter converter;
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
if (Body != null)
|
||||
// add and/or replace oncontextmenu="return false;"
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
// since we've already found the body, treat this as an attachment
|
||||
attachments.Add(entity);
|
||||
return;
|
||||
if (attribute.Name.ToLowerInvariant() == "oncontextmenu")
|
||||
continue;
|
||||
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
|
||||
if (entity.IsHtml)
|
||||
htmlWriter.WriteAttribute("oncontextmenu", "return false;");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Unknown)
|
||||
{
|
||||
converter = new HtmlToHtml
|
||||
{
|
||||
HtmlTagCallback = HtmlTagCallback
|
||||
};
|
||||
}
|
||||
else if (entity.IsFlowed)
|
||||
{
|
||||
var flowed = new FlowedToHtml();
|
||||
string delsp;
|
||||
|
||||
if (entity.ContentType.Parameters.TryGetValue("delsp", out delsp))
|
||||
flowed.DeleteSpace = delsp.ToLowerInvariant() == "yes";
|
||||
|
||||
converter = flowed;
|
||||
ctx.DeleteTag = true;
|
||||
ctx.DeleteEndTag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
converter = new TextToHtml();
|
||||
ctx.WriteTag(htmlWriter, true);
|
||||
}
|
||||
|
||||
Body = converter.Convert(entity.Text);
|
||||
}
|
||||
|
||||
protected override void VisitTnefPart(TnefPart entity)
|
||||
{
|
||||
// extract any attachments in the MS-TNEF part
|
||||
attachments.AddRange(entity.ExtractAttachments());
|
||||
}
|
||||
|
||||
protected override void VisitMessagePart(MessagePart entity)
|
||||
{
|
||||
// treat message/rfc822 parts as attachments
|
||||
attachments.Add(entity);
|
||||
}
|
||||
|
||||
protected override void VisitMimePart(MimePart entity)
|
||||
{
|
||||
// realistically, if we've gotten this far, then we can treat this as an attachment
|
||||
// even if the IsAttachment property is false.
|
||||
attachments.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VisitTextPart(TextPart entity)
|
||||
{
|
||||
TextConverter converter;
|
||||
|
||||
if (Body != null)
|
||||
{
|
||||
// since we've already found the body, treat this as an attachment
|
||||
attachments.Add(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.IsHtml)
|
||||
{
|
||||
converter = new HtmlToHtml
|
||||
{
|
||||
HtmlTagCallback = HtmlTagCallback
|
||||
};
|
||||
}
|
||||
else if (entity.IsFlowed)
|
||||
{
|
||||
var flowed = new FlowedToHtml();
|
||||
string delsp;
|
||||
|
||||
if (entity.ContentType.Parameters.TryGetValue("delsp", out delsp))
|
||||
flowed.DeleteSpace = delsp.ToLowerInvariant() == "yes";
|
||||
|
||||
converter = flowed;
|
||||
}
|
||||
else
|
||||
{
|
||||
converter = new TextToHtml();
|
||||
}
|
||||
|
||||
Body = converter.Convert(entity.Text);
|
||||
}
|
||||
|
||||
protected override void VisitTnefPart(TnefPart entity)
|
||||
{
|
||||
// extract any attachments in the MS-TNEF part
|
||||
attachments.AddRange(entity.ExtractAttachments());
|
||||
}
|
||||
|
||||
protected override void VisitMessagePart(MessagePart entity)
|
||||
{
|
||||
// treat message/rfc822 parts as attachments
|
||||
attachments.Add(entity);
|
||||
}
|
||||
|
||||
protected override void VisitMimePart(MimePart entity)
|
||||
{
|
||||
// realistically, if we've gotten this far, then we can treat this as an attachment
|
||||
// even if the IsAttachment property is false.
|
||||
attachments.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// An interface that returns the UniqueId store for IMailItem.
|
||||
/// For threads, it may be multiple items.
|
||||
/// For single mails, it'll always be one item.
|
||||
/// </summary>
|
||||
public interface IMailHashContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that returns the UniqueId store for IMailItem.
|
||||
/// For threads, it may be multiple items.
|
||||
/// For single mails, it'll always be one item.
|
||||
/// </summary>
|
||||
public interface IMailHashContainer
|
||||
{
|
||||
IEnumerable<Guid> GetContainingIds();
|
||||
}
|
||||
IEnumerable<Guid> GetContainingIds();
|
||||
}
|
||||
|
||||
@@ -2,34 +2,33 @@
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface of simplest representation of a MailCopy.
|
||||
/// </summary>
|
||||
public interface IMailItem : IMailHashContainer
|
||||
{
|
||||
Guid UniqueId { get; }
|
||||
string Id { get; }
|
||||
string Subject { get; }
|
||||
string ThreadId { get; }
|
||||
string MessageId { get; }
|
||||
string References { get; }
|
||||
string InReplyTo { get; }
|
||||
string PreviewText { get; }
|
||||
string FromName { get; }
|
||||
DateTime CreationDate { get; }
|
||||
string FromAddress { get; }
|
||||
bool HasAttachments { get; }
|
||||
bool IsFlagged { get; }
|
||||
bool IsFocused { get; }
|
||||
bool IsRead { get; }
|
||||
string DraftId { get; }
|
||||
bool IsDraft { get; }
|
||||
Guid FileId { get; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
MailItemFolder AssignedFolder { get; }
|
||||
MailAccount AssignedAccount { get; }
|
||||
AccountContact SenderContact { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Interface of simplest representation of a MailCopy.
|
||||
/// </summary>
|
||||
public interface IMailItem : IMailHashContainer
|
||||
{
|
||||
Guid UniqueId { get; }
|
||||
string Id { get; }
|
||||
string Subject { get; }
|
||||
string ThreadId { get; }
|
||||
string MessageId { get; }
|
||||
string References { get; }
|
||||
string InReplyTo { get; }
|
||||
string PreviewText { get; }
|
||||
string FromName { get; }
|
||||
DateTime CreationDate { get; }
|
||||
string FromAddress { get; }
|
||||
bool HasAttachments { get; }
|
||||
bool IsFlagged { get; }
|
||||
bool IsFocused { get; }
|
||||
bool IsRead { get; }
|
||||
string DraftId { get; }
|
||||
bool IsDraft { get; }
|
||||
Guid FileId { get; }
|
||||
|
||||
MailItemFolder AssignedFolder { get; }
|
||||
MailAccount AssignedAccount { get; }
|
||||
AccountContact SenderContact { get; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Interface that represents conversation threads.
|
||||
/// Even though this type has 1 single UI representation most of the time,
|
||||
/// it can contain multiple IMailItem.
|
||||
/// </summary>
|
||||
public interface IMailItemThread : IMailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that represents conversation threads.
|
||||
/// Even though this type has 1 single UI representation most of the time,
|
||||
/// it can contain multiple IMailItem.
|
||||
/// </summary>
|
||||
public interface IMailItemThread : IMailItem
|
||||
{
|
||||
ObservableCollection<IMailItem> ThreadItems { get; }
|
||||
IMailItem LatestMailItem { get; }
|
||||
IMailItem FirstMailItem { get; }
|
||||
}
|
||||
ObservableCollection<IMailItem> ThreadItems { get; }
|
||||
IMailItem LatestMailItem { get; }
|
||||
IMailItem FirstMailItem { get; }
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using MailKit;
|
||||
using MimeKit;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates all required information to create a MimeMessage for IMAP synchronizer.
|
||||
/// </summary>
|
||||
public class ImapMessageCreationPackage
|
||||
{
|
||||
public IMessageSummary MessageSummary { get; }
|
||||
public MimeMessage MimeMessage { get; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public ImapMessageCreationPackage(IMessageSummary messageSummary, MimeMessage mimeMessage)
|
||||
{
|
||||
MessageSummary = messageSummary;
|
||||
MimeMessage = mimeMessage;
|
||||
}
|
||||
/// <summary>
|
||||
/// Encapsulates all required information to create a MimeMessage for IMAP synchronizer.
|
||||
/// </summary>
|
||||
public class ImapMessageCreationPackage
|
||||
{
|
||||
public IMessageSummary MessageSummary { get; }
|
||||
public MimeMessage MimeMessage { get; }
|
||||
|
||||
public ImapMessageCreationPackage(IMessageSummary messageSummary, MimeMessage mimeMessage)
|
||||
{
|
||||
MessageSummary = messageSummary;
|
||||
MimeMessage = mimeMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public class MailDetailInformation
|
||||
{
|
||||
public string Id { get; set; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public bool IsRead { get; set; }
|
||||
public bool IsFlagged { get; set; }
|
||||
public bool IsDraft { get; set; }
|
||||
}
|
||||
public class MailDetailInformation
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public bool IsRead { get; set; }
|
||||
public bool IsFlagged { get; set; }
|
||||
public bool IsDraft { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class that holds information when the drag/drop of mails are performed.
|
||||
/// </summary>
|
||||
public class MailDragPackage
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that holds information when the drag/drop of mails are performed.
|
||||
/// </summary>
|
||||
public class MailDragPackage
|
||||
public MailDragPackage(IEnumerable<IMailItem> draggingMails)
|
||||
{
|
||||
public MailDragPackage(IEnumerable<IMailItem> draggingMails)
|
||||
{
|
||||
DraggingMails = draggingMails;
|
||||
}
|
||||
|
||||
public MailDragPackage(IMailItem draggingMail)
|
||||
{
|
||||
DraggingMails =
|
||||
[
|
||||
draggingMail
|
||||
];
|
||||
}
|
||||
|
||||
public IEnumerable<IMailItem> DraggingMails { get; set; }
|
||||
DraggingMails = draggingMails;
|
||||
}
|
||||
|
||||
public MailDragPackage(IMailItem draggingMail)
|
||||
{
|
||||
DraggingMails =
|
||||
[
|
||||
draggingMail
|
||||
];
|
||||
}
|
||||
|
||||
public IEnumerable<IMailItem> DraggingMails { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public class MailFolderPairMetadata
|
||||
{
|
||||
public class MailFolderPairMetadata
|
||||
{
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public string MailCopyId { get; set; }
|
||||
}
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public string MailCopyId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using MimeKit;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record NewMailItemPackage(MailCopy Copy, MimeMessage Mime, string AssignedRemoteFolderId);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public record NewMailItemPackage(MailCopy Copy, MimeMessage Mime, string AssignedRemoteFolderId);
|
||||
|
||||
@@ -3,13 +3,12 @@ using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders,
|
||||
FilterOptionType FilterType,
|
||||
SortingOptionType SortingOptionType,
|
||||
bool CreateThreads,
|
||||
bool? IsFocusedOnly,
|
||||
string SearchQuery,
|
||||
IEnumerable<Guid> ExistingUniqueIds);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders,
|
||||
FilterOptionType FilterType,
|
||||
SortingOptionType SortingOptionType,
|
||||
bool CreateThreads,
|
||||
bool? IsFocusedOnly,
|
||||
string SearchQuery,
|
||||
IEnumerable<Guid> ExistingUniqueIds);
|
||||
|
||||
@@ -4,36 +4,35 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates the options for preparing requests to execute mail operations for mail items like Move, Delete, MarkAsRead, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action"> Action to execute. </param>
|
||||
/// <param name="MailItems"> Mail copies execute the action on. </param>
|
||||
/// <param name="ToggleExecution"> Whether the operation can be reverted if needed.
|
||||
/// eg. MarkAsRead on already read item will set the action to MarkAsUnread.
|
||||
/// This is used in hover actions for example. </param>
|
||||
/// <param name="IgnoreHardDeleteProtection"> Whether hard delete protection should be ignored.
|
||||
/// Discard draft requests for example should ignore hard delete protection. </param>
|
||||
/// <param name="MoveTargetFolder"> Moving folder for the Move operation.
|
||||
/// If null and the action is Move, the user will be prompted to select a folder. </param>
|
||||
public record MailOperationPreperationRequest(MailOperation Action, IEnumerable<MailCopy> MailItems, bool ToggleExecution, bool IgnoreHardDeleteProtection, IMailItemFolder MoveTargetFolder)
|
||||
{
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
IEnumerable<MailCopy> mailItems,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, mailItems ?? throw new ArgumentNullException(nameof(mailItems)), toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
MailCopy singleMailItem,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, new List<MailCopy>() { singleMailItem }, toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Encapsulates the options for preparing requests to execute mail operations for mail items like Move, Delete, MarkAsRead, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action"> Action to execute. </param>
|
||||
/// <param name="MailItems"> Mail copies execute the action on. </param>
|
||||
/// <param name="ToggleExecution"> Whether the operation can be reverted if needed.
|
||||
/// eg. MarkAsRead on already read item will set the action to MarkAsUnread.
|
||||
/// This is used in hover actions for example. </param>
|
||||
/// <param name="IgnoreHardDeleteProtection"> Whether hard delete protection should be ignored.
|
||||
/// Discard draft requests for example should ignore hard delete protection. </param>
|
||||
/// <param name="MoveTargetFolder"> Moving folder for the Move operation.
|
||||
/// If null and the action is Move, the user will be prompted to select a folder. </param>
|
||||
public record MailOperationPreperationRequest(MailOperation Action, IEnumerable<MailCopy> MailItems, bool ToggleExecution, bool IgnoreHardDeleteProtection, IMailItemFolder MoveTargetFolder)
|
||||
{
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
IEnumerable<MailCopy> mailItems,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, mailItems ?? throw new ArgumentNullException(nameof(mailItems)), toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
MailCopy singleMailItem,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, new List<MailCopy>() { singleMailItem }, toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using MimeKit;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates MimeMessage and the path to the file.
|
||||
/// </summary>
|
||||
public record MimeMessageInformation(MimeMessage MimeMessage, string Path);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates MimeMessage and the path to the file.
|
||||
/// </summary>
|
||||
public record MimeMessageInformation(MimeMessage MimeMessage, string Path);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that holds immutable information about special folders in Outlook.
|
||||
/// </summary>
|
||||
/// <param name="InboxId"></param>
|
||||
/// <param name="TrashId"></param>
|
||||
/// <param name="JunkId"></param>
|
||||
/// <param name="DraftId"></param>
|
||||
/// <param name="SentId"></param>
|
||||
/// <param name="ArchiveId"></param>
|
||||
public record OutlookSpecialFolderIdInformation(string InboxId, string TrashId, string JunkId, string DraftId, string SentId, string ArchiveId);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class that holds immutable information about special folders in Outlook.
|
||||
/// </summary>
|
||||
/// <param name="InboxId"></param>
|
||||
/// <param name="TrashId"></param>
|
||||
/// <param name="JunkId"></param>
|
||||
/// <param name="DraftId"></param>
|
||||
/// <param name="SentId"></param>
|
||||
/// <param name="ArchiveId"></param>
|
||||
public record OutlookSpecialFolderIdInformation(string InboxId, string TrashId, string JunkId, string DraftId, string SentId, string ArchiveId);
|
||||
|
||||
@@ -4,19 +4,18 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record SendDraftPreparationRequest(MailCopy MailItem,
|
||||
MailAccountAlias SendingAlias,
|
||||
MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailAccountPreferences AccountPreferences,
|
||||
string Base64MimeMessage)
|
||||
{
|
||||
[JsonIgnore]
|
||||
private MimeMessage mime;
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
[JsonIgnore]
|
||||
public MimeMessage Mime => mime ??= Base64MimeMessage.GetMimeMessageFromBase64();
|
||||
}
|
||||
public record SendDraftPreparationRequest(MailCopy MailItem,
|
||||
MailAccountAlias SendingAlias,
|
||||
MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailAccountPreferences AccountPreferences,
|
||||
string Base64MimeMessage)
|
||||
{
|
||||
[JsonIgnore]
|
||||
private MimeMessage mime;
|
||||
|
||||
[JsonIgnore]
|
||||
public MimeMessage Mime => mime ??= Base64MimeMessage.GetMimeMessageFromBase64();
|
||||
}
|
||||
|
||||
@@ -5,91 +5,90 @@ using System.Linq;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public class ThreadMailItem : IMailItemThread
|
||||
{
|
||||
public class ThreadMailItem : IMailItemThread
|
||||
// TODO: Ideally this should be SortedList.
|
||||
public ObservableCollection<IMailItem> ThreadItems { get; } = new ObservableCollection<IMailItem>();
|
||||
|
||||
public IMailItem LatestMailItem => ThreadItems.LastOrDefault();
|
||||
public IMailItem FirstMailItem => ThreadItems.FirstOrDefault();
|
||||
|
||||
public bool AddThreadItem(IMailItem item)
|
||||
{
|
||||
// TODO: Ideally this should be SortedList.
|
||||
public ObservableCollection<IMailItem> ThreadItems { get; } = new ObservableCollection<IMailItem>();
|
||||
if (item == null) return false;
|
||||
|
||||
public IMailItem LatestMailItem => ThreadItems.LastOrDefault();
|
||||
public IMailItem FirstMailItem => ThreadItems.FirstOrDefault();
|
||||
|
||||
public bool AddThreadItem(IMailItem item)
|
||||
if (ThreadItems.Any(a => a.Id == item.Id))
|
||||
{
|
||||
if (item == null) return false;
|
||||
|
||||
if (ThreadItems.Any(a => a.Id == item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item != null && item.IsDraft)
|
||||
{
|
||||
ThreadItems.Insert(0, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
var insertItem = ThreadItems.FirstOrDefault(a => !a.IsDraft && a.CreationDate < item.CreationDate);
|
||||
|
||||
if (insertItem == null)
|
||||
ThreadItems.Insert(ThreadItems.Count, item);
|
||||
else
|
||||
{
|
||||
var index = ThreadItems.IndexOf(insertItem);
|
||||
|
||||
ThreadItems.Insert(index, item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item != null && item.IsDraft)
|
||||
{
|
||||
ThreadItems.Insert(0, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => ThreadItems?.Select(a => a.UniqueId) ?? default;
|
||||
var insertItem = ThreadItems.FirstOrDefault(a => !a.IsDraft && a.CreationDate < item.CreationDate);
|
||||
|
||||
#region IMailItem
|
||||
if (insertItem == null)
|
||||
ThreadItems.Insert(ThreadItems.Count, item);
|
||||
else
|
||||
{
|
||||
var index = ThreadItems.IndexOf(insertItem);
|
||||
|
||||
public Guid UniqueId => LatestMailItem?.UniqueId ?? Guid.Empty;
|
||||
public string Id => LatestMailItem?.Id ?? string.Empty;
|
||||
ThreadItems.Insert(index, item);
|
||||
}
|
||||
|
||||
// Show subject from last item.
|
||||
public string Subject => LatestMailItem?.Subject ?? string.Empty;
|
||||
|
||||
public string ThreadId => LatestMailItem?.ThreadId ?? string.Empty;
|
||||
|
||||
public string PreviewText => FirstMailItem?.PreviewText ?? string.Empty;
|
||||
|
||||
public string FromName => LatestMailItem?.FromName ?? string.Empty;
|
||||
|
||||
public string FromAddress => LatestMailItem?.FromAddress ?? string.Empty;
|
||||
|
||||
public bool HasAttachments => ThreadItems.Any(a => a.HasAttachments);
|
||||
|
||||
public bool IsFlagged => ThreadItems.Any(a => a.IsFlagged);
|
||||
|
||||
public bool IsFocused => LatestMailItem?.IsFocused ?? false;
|
||||
|
||||
public bool IsRead => ThreadItems.All(a => a.IsRead);
|
||||
|
||||
public DateTime CreationDate => FirstMailItem?.CreationDate ?? DateTime.MinValue;
|
||||
|
||||
public bool IsDraft => ThreadItems.Any(a => a.IsDraft);
|
||||
|
||||
public string DraftId => string.Empty;
|
||||
|
||||
public string MessageId => LatestMailItem?.MessageId;
|
||||
|
||||
public string References => LatestMailItem?.References ?? string.Empty;
|
||||
|
||||
public string InReplyTo => LatestMailItem?.InReplyTo ?? string.Empty;
|
||||
|
||||
public MailItemFolder AssignedFolder => LatestMailItem?.AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => LatestMailItem?.AssignedAccount;
|
||||
|
||||
public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty;
|
||||
|
||||
public AccountContact SenderContact => LatestMailItem?.SenderContact;
|
||||
|
||||
#endregion
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => ThreadItems?.Select(a => a.UniqueId) ?? default;
|
||||
|
||||
#region IMailItem
|
||||
|
||||
public Guid UniqueId => LatestMailItem?.UniqueId ?? Guid.Empty;
|
||||
public string Id => LatestMailItem?.Id ?? string.Empty;
|
||||
|
||||
// Show subject from last item.
|
||||
public string Subject => LatestMailItem?.Subject ?? string.Empty;
|
||||
|
||||
public string ThreadId => LatestMailItem?.ThreadId ?? string.Empty;
|
||||
|
||||
public string PreviewText => FirstMailItem?.PreviewText ?? string.Empty;
|
||||
|
||||
public string FromName => LatestMailItem?.FromName ?? string.Empty;
|
||||
|
||||
public string FromAddress => LatestMailItem?.FromAddress ?? string.Empty;
|
||||
|
||||
public bool HasAttachments => ThreadItems.Any(a => a.HasAttachments);
|
||||
|
||||
public bool IsFlagged => ThreadItems.Any(a => a.IsFlagged);
|
||||
|
||||
public bool IsFocused => LatestMailItem?.IsFocused ?? false;
|
||||
|
||||
public bool IsRead => ThreadItems.All(a => a.IsRead);
|
||||
|
||||
public DateTime CreationDate => FirstMailItem?.CreationDate ?? DateTime.MinValue;
|
||||
|
||||
public bool IsDraft => ThreadItems.Any(a => a.IsDraft);
|
||||
|
||||
public string DraftId => string.Empty;
|
||||
|
||||
public string MessageId => LatestMailItem?.MessageId;
|
||||
|
||||
public string References => LatestMailItem?.References ?? string.Empty;
|
||||
|
||||
public string InReplyTo => LatestMailItem?.InReplyTo ?? string.Empty;
|
||||
|
||||
public MailItemFolder AssignedFolder => LatestMailItem?.AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => LatestMailItem?.AssignedAccount;
|
||||
|
||||
public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty;
|
||||
|
||||
public AccountContact SenderContact => LatestMailItem?.SenderContact;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a single rule for toggling user actions if needed.
|
||||
/// For example: If user wants to mark a mail as read, but it's already read, then it should be marked as unread.
|
||||
/// </summary>
|
||||
/// <param name="SourceAction"></param>
|
||||
/// <param name="TargetAction"></param>
|
||||
/// <param name="Condition"></param>
|
||||
public record ToggleRequestRule(MailOperation SourceAction, MailOperation TargetAction, Func<IMailItem, bool> Condition);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a single rule for toggling user actions if needed.
|
||||
/// For example: If user wants to mark a mail as read, but it's already read, then it should be marked as unread.
|
||||
/// </summary>
|
||||
/// <param name="SourceAction"></param>
|
||||
/// <param name="TargetAction"></param>
|
||||
/// <param name="Condition"></param>
|
||||
public record ToggleRequestRule(MailOperation SourceAction, MailOperation TargetAction, Func<IMailItem, bool> Condition);
|
||||
|
||||
Reference in New Issue
Block a user