Cleaning up the solution. Separating Shared.WinRT, Services and Synchronization. Removing synchronization from app. Reducing bundle size by 45mb.
This commit is contained in:
266
Wino.Synchronization/Extensions/GoogleIntegratorExtensions.cs
Normal file
266
Wino.Synchronization/Extensions/GoogleIntegratorExtensions.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Google.Apis.Gmail.v1.Data;
|
||||
using MimeKit;
|
||||
using MimeKit.IO;
|
||||
using MimeKit.IO.Filters;
|
||||
using Wino.Domain.Entities;
|
||||
using Wino.Domain.Enums;
|
||||
using Constants = Wino.Domain.Constants;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class GoogleIntegratorExtensions
|
||||
{
|
||||
public const string INBOX_LABEL_ID = "INBOX";
|
||||
public const string UNREAD_LABEL_ID = "UNREAD";
|
||||
public const string IMPORTANT_LABEL_ID = "IMPORTANT";
|
||||
public const string STARRED_LABEL_ID = "STARRED";
|
||||
public const string DRAFT_LABEL_ID = "DRAFT";
|
||||
public const string SENT_LABEL_ID = "SENT";
|
||||
public const string SPAM_LABEL_ID = "SPAM";
|
||||
public const string CHAT_LABEL_ID = "CHAT";
|
||||
public const string TRASH_LABEL_ID = "TRASH";
|
||||
|
||||
|
||||
|
||||
// Label visibility identifiers.
|
||||
private const string SYSTEM_FOLDER_IDENTIFIER = "system";
|
||||
private const string FOLDER_HIDE_IDENTIFIER = "labelHide";
|
||||
|
||||
private const string CATEGORY_PREFIX = "CATEGORY_";
|
||||
private const string FOLDER_SEPERATOR_STRING = "/";
|
||||
private const char FOLDER_SEPERATOR_CHAR = '/';
|
||||
|
||||
private static Dictionary<string, SpecialFolderType> KnownFolderDictionary = new Dictionary<string, SpecialFolderType>()
|
||||
{
|
||||
{ INBOX_LABEL_ID, SpecialFolderType.Inbox },
|
||||
{ CHAT_LABEL_ID, SpecialFolderType.Chat },
|
||||
{ IMPORTANT_LABEL_ID, SpecialFolderType.Important },
|
||||
{ TRASH_LABEL_ID, SpecialFolderType.Deleted },
|
||||
{ DRAFT_LABEL_ID, SpecialFolderType.Draft },
|
||||
{ SENT_LABEL_ID, SpecialFolderType.Sent },
|
||||
{ SPAM_LABEL_ID, SpecialFolderType.Junk },
|
||||
{ STARRED_LABEL_ID, SpecialFolderType.Starred },
|
||||
{ UNREAD_LABEL_ID, SpecialFolderType.Unread },
|
||||
{ Constants.FORUMS_LABEL_ID, SpecialFolderType.Forums },
|
||||
{ Constants.UPDATES_LABEL_ID, SpecialFolderType.Updates },
|
||||
{ Constants.PROMOTIONS_LABEL_ID, SpecialFolderType.Promotions },
|
||||
{ Constants.SOCIAL_LABEL_ID, SpecialFolderType.Social},
|
||||
{ Constants.PERSONAL_LABEL_ID, SpecialFolderType.Personal},
|
||||
};
|
||||
|
||||
private static string GetNormalizedLabelName(string labelName)
|
||||
{
|
||||
// 1. Remove CATEGORY_ prefix.
|
||||
var normalizedLabelName = labelName.Replace(CATEGORY_PREFIX, string.Empty);
|
||||
|
||||
// 2. Normalize label name by capitalizing first letter.
|
||||
normalizedLabelName = char.ToUpper(normalizedLabelName[0]) + normalizedLabelName.Substring(1).ToLower();
|
||||
|
||||
return normalizedLabelName;
|
||||
}
|
||||
|
||||
public static MailItemFolder GetLocalFolder(this Label label, ListLabelsResponse labelsResponse, Guid accountId)
|
||||
{
|
||||
bool isAllCapital = label.Name.All(a => char.IsUpper(a));
|
||||
|
||||
var normalizedLabelName = GetFolderName(label);
|
||||
|
||||
// Even though we normalize the label name, check is done by capitalizing the label name.
|
||||
var capitalNormalizedLabelName = normalizedLabelName.ToUpper();
|
||||
|
||||
bool isSpecialFolder = KnownFolderDictionary.ContainsKey(capitalNormalizedLabelName);
|
||||
|
||||
var specialFolderType = isSpecialFolder ? KnownFolderDictionary[capitalNormalizedLabelName] : SpecialFolderType.Other;
|
||||
|
||||
// We used to support FOLDER_HIDE_IDENTIFIER to hide invisible folders.
|
||||
// However, a lot of people complained that they don't see their folders after the initial sync
|
||||
// without realizing that they are hidden in Gmail settings. Therefore, it makes more sense to ignore Gmail's configuration
|
||||
// since Wino allows folder visibility configuration separately.
|
||||
|
||||
// Overridden hidden labels are shown in the UI, but they have their synchronization disabled.
|
||||
// This is mainly because 'All Mails' label is hidden by default in Gmail, but there is no point to download all mails.
|
||||
|
||||
bool shouldEnableSynchronization = label.LabelListVisibility != FOLDER_HIDE_IDENTIFIER;
|
||||
bool isHidden = false;
|
||||
|
||||
bool isChildOfCategoryFolder = label.Name.StartsWith(CATEGORY_PREFIX);
|
||||
bool isSticky = isSpecialFolder && specialFolderType != SpecialFolderType.Category && !isChildOfCategoryFolder;
|
||||
|
||||
// By default, all special folders update unread count in the UI except Trash.
|
||||
bool shouldShowUnreadCount = specialFolderType != SpecialFolderType.Deleted || specialFolderType != SpecialFolderType.Other;
|
||||
|
||||
bool isSystemFolder = label.Type == SYSTEM_FOLDER_IDENTIFIER;
|
||||
|
||||
var localFolder = new MailItemFolder()
|
||||
{
|
||||
TextColorHex = label.Color?.TextColor,
|
||||
BackgroundColorHex = label.Color?.BackgroundColor,
|
||||
FolderName = normalizedLabelName,
|
||||
RemoteFolderId = label.Id,
|
||||
Id = Guid.NewGuid(),
|
||||
MailAccountId = accountId,
|
||||
IsSynchronizationEnabled = shouldEnableSynchronization,
|
||||
SpecialFolderType = specialFolderType,
|
||||
IsSystemFolder = isSystemFolder,
|
||||
IsSticky = isSticky,
|
||||
IsHidden = isHidden,
|
||||
ShowUnreadCount = shouldShowUnreadCount,
|
||||
};
|
||||
|
||||
localFolder.ParentRemoteFolderId = isChildOfCategoryFolder ? string.Empty : GetParentFolderRemoteId(label.Name, labelsResponse);
|
||||
|
||||
return localFolder;
|
||||
}
|
||||
|
||||
public static bool GetIsDraft(this Message message)
|
||||
=> message?.LabelIds?.Any(a => a == DRAFT_LABEL_ID) ?? false;
|
||||
|
||||
public static bool GetIsUnread(this Message message)
|
||||
=> message?.LabelIds?.Any(a => a == UNREAD_LABEL_ID) ?? false;
|
||||
|
||||
public static bool GetIsFocused(this Message message)
|
||||
=> message?.LabelIds?.Any(a => a == IMPORTANT_LABEL_ID) ?? false;
|
||||
|
||||
public static bool GetIsFlagged(this Message message)
|
||||
=> message?.LabelIds?.Any(a => a == STARRED_LABEL_ID) ?? false;
|
||||
|
||||
private static string GetParentFolderRemoteId(string fullLabelName, ListLabelsResponse labelsResponse)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fullLabelName)) return string.Empty;
|
||||
|
||||
// Find the last index of '/'
|
||||
int lastIndex = fullLabelName.LastIndexOf('/');
|
||||
|
||||
// If '/' not found or it's at the start, return the empty string.
|
||||
if (lastIndex <= 0) return string.Empty;
|
||||
|
||||
// Extract the parent label
|
||||
var parentLabelName = fullLabelName.Substring(0, lastIndex);
|
||||
|
||||
return labelsResponse.Labels.FirstOrDefault(a => a.Name == parentLabelName)?.Id ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string GetFolderName(Label label)
|
||||
{
|
||||
if (string.IsNullOrEmpty(label.Name)) return string.Empty;
|
||||
|
||||
// Folders with "//" at the end has "/" as the name.
|
||||
if (label.Name.EndsWith(FOLDER_SEPERATOR_STRING)) return FOLDER_SEPERATOR_STRING;
|
||||
|
||||
string[] parts = label.Name.Split(FOLDER_SEPERATOR_CHAR);
|
||||
|
||||
var lastPart = parts[parts.Length - 1];
|
||||
|
||||
return GetNormalizedLabelName(lastPart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MailCopy out of native Gmail message and converted MimeMessage of that native messaage.
|
||||
/// </summary>
|
||||
/// <param name="gmailMessage">Gmail Message</param>
|
||||
/// <param name="mimeMessage">MimeMessage representation of that native message.</param>
|
||||
/// <returns>MailCopy object that is ready to be inserted to database.</returns>
|
||||
public static MailCopy AsMailCopy(this Message gmailMessage, MimeMessage mimeMessage)
|
||||
{
|
||||
bool isUnread = gmailMessage.GetIsUnread();
|
||||
bool isFocused = gmailMessage.GetIsFocused();
|
||||
bool isFlagged = gmailMessage.GetIsFlagged();
|
||||
bool isDraft = gmailMessage.GetIsDraft();
|
||||
|
||||
return new MailCopy()
|
||||
{
|
||||
CreationDate = mimeMessage.Date.UtcDateTime,
|
||||
Subject = HttpUtility.HtmlDecode(mimeMessage.Subject),
|
||||
FromName = MailkitClientExtensions.GetActualSenderName(mimeMessage),
|
||||
FromAddress = MailkitClientExtensions.GetActualSenderAddress(mimeMessage),
|
||||
PreviewText = HttpUtility.HtmlDecode(gmailMessage.Snippet),
|
||||
ThreadId = gmailMessage.ThreadId,
|
||||
Importance = (MailImportance)mimeMessage.Importance,
|
||||
Id = gmailMessage.Id,
|
||||
IsDraft = isDraft,
|
||||
HasAttachments = mimeMessage.Attachments.Any(),
|
||||
IsRead = !isUnread,
|
||||
IsFlagged = isFlagged,
|
||||
IsFocused = isFocused,
|
||||
InReplyTo = mimeMessage.InReplyTo,
|
||||
MessageId = mimeMessage.MessageId,
|
||||
References = mimeMessage.References.GetReferences(),
|
||||
FileId = Guid.NewGuid()
|
||||
};
|
||||
}
|
||||
|
||||
public static Tuple<MailCopy, MimeMessage, IEnumerable<string>> GetMailDetails(this Message message)
|
||||
{
|
||||
MimeMessage mimeMessage = message.GetGmailMimeMessage();
|
||||
|
||||
if (mimeMessage == null)
|
||||
{
|
||||
// This should never happen.
|
||||
Debugger.Break();
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
bool isUnread = message.GetIsUnread();
|
||||
bool isFocused = message.GetIsFocused();
|
||||
bool isFlagged = message.GetIsFlagged();
|
||||
bool isDraft = message.GetIsDraft();
|
||||
|
||||
var mailCopy = new MailCopy()
|
||||
{
|
||||
CreationDate = mimeMessage.Date.UtcDateTime,
|
||||
Subject = HttpUtility.HtmlDecode(mimeMessage.Subject),
|
||||
FromName = MailkitClientExtensions.GetActualSenderName(mimeMessage),
|
||||
FromAddress = MailkitClientExtensions.GetActualSenderAddress(mimeMessage),
|
||||
PreviewText = HttpUtility.HtmlDecode(message.Snippet),
|
||||
ThreadId = message.ThreadId,
|
||||
Importance = (MailImportance)mimeMessage.Importance,
|
||||
Id = message.Id,
|
||||
IsDraft = isDraft,
|
||||
HasAttachments = mimeMessage.Attachments.Any(),
|
||||
IsRead = !isUnread,
|
||||
IsFlagged = isFlagged,
|
||||
IsFocused = isFocused,
|
||||
InReplyTo = mimeMessage.InReplyTo,
|
||||
MessageId = mimeMessage.MessageId,
|
||||
References = mimeMessage.References.GetReferences()
|
||||
};
|
||||
|
||||
return new Tuple<MailCopy, MimeMessage, IEnumerable<string>>(mailCopy, mimeMessage, message.LabelIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MimeKit.MimeMessage instance for this GMail Message's Raw content.
|
||||
/// </summary>
|
||||
/// <param name="message">GMail message.</param>
|
||||
public static MimeMessage GetGmailMimeMessage(this Message message)
|
||||
{
|
||||
if (message == null || message.Raw == null)
|
||||
return null;
|
||||
|
||||
// Gmail raw is not base64 but base64Safe. We need to remove this HTML things.
|
||||
var base64Encoded = message.Raw.Replace(",", "=").Replace("-", "+").Replace("_", "/");
|
||||
|
||||
byte[] bytes = Encoding.ASCII.GetBytes(base64Encoded);
|
||||
|
||||
var stream = new MemoryStream(bytes);
|
||||
|
||||
// This method will dispose outer stream.
|
||||
|
||||
using (stream)
|
||||
{
|
||||
using var filtered = new FilteredStream(stream);
|
||||
filtered.Add(DecoderFilter.Create(ContentEncoding.Base64));
|
||||
|
||||
return MimeMessage.Load(filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Wino.Synchronization/Extensions/ListExtensions.cs
Normal file
58
Wino.Synchronization/Extensions/ListExtensions.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Wino.Domain.Enums;
|
||||
using Wino.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static IEnumerable<T> FlattenBy<T>(this IEnumerable<T> nodes, Func<T, IEnumerable<T>> selector)
|
||||
{
|
||||
if (nodes.Any() == false)
|
||||
return nodes;
|
||||
|
||||
var descendants = nodes
|
||||
.SelectMany(selector)
|
||||
.FlattenBy(selector);
|
||||
|
||||
return nodes.Concat(descendants);
|
||||
}
|
||||
|
||||
public static IEnumerable<IBatchChangeRequest> CreateBatch(this IEnumerable<IGrouping<MailSynchronizerOperation, IRequestBase>> items)
|
||||
{
|
||||
IBatchChangeRequest batch = null;
|
||||
|
||||
foreach (var group in items)
|
||||
{
|
||||
var key = group.Key;
|
||||
}
|
||||
|
||||
yield return batch;
|
||||
}
|
||||
|
||||
public static void AddSorted<T>(this List<T> @this, T item) where T : IComparable<T>
|
||||
{
|
||||
if (@this.Count == 0)
|
||||
{
|
||||
@this.Add(item);
|
||||
return;
|
||||
}
|
||||
if (@this[@this.Count - 1].CompareTo(item) <= 0)
|
||||
{
|
||||
@this.Add(item);
|
||||
return;
|
||||
}
|
||||
if (@this[0].CompareTo(item) >= 0)
|
||||
{
|
||||
@this.Insert(0, item);
|
||||
return;
|
||||
}
|
||||
int index = @this.BinarySearch(item);
|
||||
if (index < 0)
|
||||
index = ~index;
|
||||
@this.Insert(index, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
Wino.Synchronization/Extensions/MailkitClientExtensions.cs
Normal file
178
Wino.Synchronization/Extensions/MailkitClientExtensions.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MailKit;
|
||||
using MimeKit;
|
||||
using Wino.Domain;
|
||||
using Wino.Domain;
|
||||
using Wino.Domain.Entities;
|
||||
using Wino.Domain.Enums;
|
||||
using Wino.Services.Extensions;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class MailkitClientExtensions
|
||||
{
|
||||
public static string CreateUid(Guid folderId, uint messageUid)
|
||||
=> $"{folderId}{Constants.MailCopyUidSeparator}{messageUid}";
|
||||
|
||||
public static MailImportance GetImportance(this MimeMessage messageSummary)
|
||||
{
|
||||
if (messageSummary.Headers != null && messageSummary.Headers.Contains(HeaderId.Importance))
|
||||
{
|
||||
var rawImportance = messageSummary.Headers[HeaderId.Importance];
|
||||
|
||||
return rawImportance switch
|
||||
{
|
||||
"Low" => MailImportance.Low,
|
||||
"High" => MailImportance.High,
|
||||
_ => MailImportance.Normal,
|
||||
};
|
||||
}
|
||||
|
||||
return MailImportance.Normal;
|
||||
}
|
||||
|
||||
public static bool GetIsRead(this MessageFlags? flags)
|
||||
=> flags.GetValueOrDefault().HasFlag(MessageFlags.Seen);
|
||||
|
||||
public static bool GetIsFlagged(this MessageFlags? flags)
|
||||
=> flags.GetValueOrDefault().HasFlag(MessageFlags.Flagged);
|
||||
|
||||
public static string GetThreadId(this IMessageSummary messageSummary)
|
||||
{
|
||||
// First check whether we have the default values.
|
||||
|
||||
if (!string.IsNullOrEmpty(messageSummary.ThreadId))
|
||||
return messageSummary.ThreadId;
|
||||
|
||||
if (messageSummary.GMailThreadId != null)
|
||||
return messageSummary.GMailThreadId.ToString();
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static string GetMessageId(this MimeMessage mimeMessage)
|
||||
=> mimeMessage.MessageId;
|
||||
|
||||
public static string GetReferences(this MessageIdList messageIdList)
|
||||
=> string.Join(";", messageIdList);
|
||||
|
||||
public static string GetInReplyTo(this MimeMessage mimeMessage)
|
||||
{
|
||||
if (mimeMessage.Headers.Contains(HeaderId.InReplyTo))
|
||||
{
|
||||
// Normalize if <> brackets are there.
|
||||
var inReplyTo = mimeMessage.Headers[HeaderId.InReplyTo];
|
||||
|
||||
if (inReplyTo.StartsWith("<") && inReplyTo.EndsWith(">"))
|
||||
return inReplyTo.Substring(1, inReplyTo.Length - 2);
|
||||
|
||||
return inReplyTo;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string GetPreviewText(this MimeMessage message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message.HtmlBody))
|
||||
return message.TextBody;
|
||||
else
|
||||
return HtmlAgilityPackExtensions.GetPreviewText(message.HtmlBody);
|
||||
}
|
||||
|
||||
public static MailCopy GetMailDetails(this IMessageSummary messageSummary, MailItemFolder folder, MimeMessage mime)
|
||||
{
|
||||
// MessageSummary will only have UniqueId, Flags, ThreadId.
|
||||
// Other properties are extracted directly from the MimeMessage.
|
||||
|
||||
// IMAP doesn't have unique id for mails.
|
||||
// All mails are mapped to specific folders with incremental Id.
|
||||
// Uid 1 may belong to different messages in different folders, but can never be
|
||||
// same for different messages in same folders.
|
||||
// Here we create arbitrary Id that maps the Id of the message with Folder UniqueId.
|
||||
// When folder becomes invalid, we'll clear out these MailCopies as well.
|
||||
|
||||
var messageUid = CreateUid(folder.Id, messageSummary.UniqueId.Id);
|
||||
var previewText = mime.GetPreviewText();
|
||||
|
||||
var copy = new MailCopy()
|
||||
{
|
||||
Id = messageUid,
|
||||
CreationDate = mime.Date.UtcDateTime,
|
||||
ThreadId = messageSummary.GetThreadId(),
|
||||
MessageId = mime.GetMessageId(),
|
||||
Subject = mime.Subject,
|
||||
IsRead = messageSummary.Flags.GetIsRead(),
|
||||
IsFlagged = messageSummary.Flags.GetIsFlagged(),
|
||||
PreviewText = previewText,
|
||||
FromAddress = GetActualSenderAddress(mime),
|
||||
FromName = GetActualSenderName(mime),
|
||||
IsFocused = false,
|
||||
Importance = mime.GetImportance(),
|
||||
References = mime.References?.GetReferences(),
|
||||
InReplyTo = mime.GetInReplyTo(),
|
||||
HasAttachments = mime.Attachments.Any(),
|
||||
FileId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
// TODO: Name and Address parsing should be handled better.
|
||||
// At some point Wino needs better contact management.
|
||||
|
||||
public static string GetActualSenderName(MimeMessage message)
|
||||
{
|
||||
if (message == null)
|
||||
return string.Empty;
|
||||
|
||||
// From MimeKit
|
||||
|
||||
// The "From" header specifies the author(s) of the message.
|
||||
// If more than one MimeKit.MailboxAddress is added to the list of "From" addresses,
|
||||
// the MimeKit.MimeMessage.Sender should be set to the single MimeKit.MailboxAddress
|
||||
// of the personal actually sending the message.
|
||||
|
||||
// Also handle: https://stackoverflow.com/questions/46474030/mailkit-from-address
|
||||
|
||||
if (message.Sender != null)
|
||||
return string.IsNullOrEmpty(message.Sender.Name) ? message.Sender.Address : message.Sender.Name;
|
||||
else if (message.From?.Mailboxes != null)
|
||||
{
|
||||
var firstAvailableName = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Name))?.Name;
|
||||
|
||||
if (string.IsNullOrEmpty(firstAvailableName))
|
||||
{
|
||||
var firstAvailableAddress = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Address))?.Address;
|
||||
|
||||
if (!string.IsNullOrEmpty(firstAvailableAddress))
|
||||
{
|
||||
return firstAvailableAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return firstAvailableName;
|
||||
}
|
||||
|
||||
// No sender, no from, I don't know what to do.
|
||||
return Translator.UnknownSender;
|
||||
}
|
||||
|
||||
// TODO: This is wrong.
|
||||
public static string GetActualSenderAddress(MimeMessage mime)
|
||||
{
|
||||
if (mime == null)
|
||||
return string.Empty;
|
||||
|
||||
bool hasSingleFromMailbox = mime.From.Mailboxes.Count() == 1;
|
||||
|
||||
if (hasSingleFromMailbox)
|
||||
return mime.From.Mailboxes.First().GetAddress(idnEncode: true);
|
||||
else if (mime.Sender != null)
|
||||
return mime.Sender.GetAddress(idnEncode: true);
|
||||
else
|
||||
return Translator.UnknownSender;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Wino.Synchronization/Extensions/MailkitExtensions.cs
Normal file
22
Wino.Synchronization/Extensions/MailkitExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using MailKit;
|
||||
using Wino.Domain.Entities;
|
||||
using Wino.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class MailkitExtensions
|
||||
{
|
||||
public static MailItemFolder GetLocalFolder(this IMailFolder mailkitMailFolder)
|
||||
{
|
||||
return new MailItemFolder()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FolderName = mailkitMailFolder.Name,
|
||||
RemoteFolderId = mailkitMailFolder.FullName,
|
||||
ParentRemoteFolderId = mailkitMailFolder.ParentFolder?.FullName,
|
||||
SpecialFolderType = SpecialFolderType.Other
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using Microsoft.Graph.Models;
|
||||
using Wino.Domain.Entities;
|
||||
using Wino.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class OutlookIntegratorExtensions
|
||||
{
|
||||
public static MailItemFolder GetLocalFolder(this MailFolder nativeFolder, Guid accountId)
|
||||
{
|
||||
return new MailItemFolder()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FolderName = nativeFolder.DisplayName,
|
||||
RemoteFolderId = nativeFolder.Id,
|
||||
ParentRemoteFolderId = nativeFolder.ParentFolderId,
|
||||
IsSynchronizationEnabled = true,
|
||||
MailAccountId = accountId,
|
||||
IsHidden = nativeFolder.IsHidden.GetValueOrDefault()
|
||||
};
|
||||
}
|
||||
|
||||
public static bool GetIsDraft(this Message message)
|
||||
=> message != null && message.IsDraft.GetValueOrDefault();
|
||||
|
||||
public static bool GetIsRead(this Message message)
|
||||
=> message != null && message.IsRead.GetValueOrDefault();
|
||||
|
||||
public static bool GetIsFocused(this Message message)
|
||||
=> message?.InferenceClassification != null && message.InferenceClassification.Value == InferenceClassificationType.Focused;
|
||||
|
||||
public static bool GetIsFlagged(this Message message)
|
||||
=> message?.Flag?.FlagStatus != null && message.Flag.FlagStatus == FollowupFlagStatus.Flagged;
|
||||
|
||||
public static MailCopy AsMailCopy(this Message outlookMessage)
|
||||
{
|
||||
bool isDraft = GetIsDraft(outlookMessage);
|
||||
|
||||
var mailCopy = new MailCopy()
|
||||
{
|
||||
MessageId = outlookMessage.InternetMessageId,
|
||||
IsFlagged = GetIsFlagged(outlookMessage),
|
||||
IsFocused = GetIsFocused(outlookMessage),
|
||||
Importance = !outlookMessage.Importance.HasValue ? MailImportance.Normal : (MailImportance)outlookMessage.Importance.Value,
|
||||
IsRead = GetIsRead(outlookMessage),
|
||||
IsDraft = isDraft,
|
||||
CreationDate = outlookMessage.ReceivedDateTime.GetValueOrDefault().DateTime,
|
||||
HasAttachments = outlookMessage.HasAttachments.GetValueOrDefault(),
|
||||
PreviewText = outlookMessage.BodyPreview,
|
||||
Id = outlookMessage.Id,
|
||||
ThreadId = outlookMessage.ConversationId,
|
||||
FromName = outlookMessage.From?.EmailAddress?.Name,
|
||||
FromAddress = outlookMessage.From?.EmailAddress?.Address,
|
||||
Subject = outlookMessage.Subject,
|
||||
FileId = Guid.NewGuid()
|
||||
};
|
||||
|
||||
if (mailCopy.IsDraft)
|
||||
mailCopy.DraftId = mailCopy.ThreadId;
|
||||
|
||||
return mailCopy;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Wino.Synchronization/Extensions/StringExtensions.cs
Normal file
22
Wino.Synchronization/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static bool Contains(this string source, string toCheck, StringComparison comp)
|
||||
{
|
||||
return source?.IndexOf(toCheck, comp) >= 0;
|
||||
}
|
||||
|
||||
public static string ReplaceFirst(this string text, string search, string replace)
|
||||
{
|
||||
int pos = text.IndexOf(search);
|
||||
if (pos < 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user