Fix merge conflicts

This commit is contained in:
Aleh Khantsevich
2024-06-13 01:42:19 +02:00
31 changed files with 484 additions and 33 deletions

View File

@@ -58,6 +58,16 @@ namespace Wino.Core.MenuItems
Parameter = account;
AccountName = account.Name;
AttentionReason = account.AttentionReason;
if (SubMenuItems == null) return;
foreach (var item in SubMenuItems)
{
if (item is IFolderMenuItem folderMenuItem)
{
folderMenuItem.UpdateParentAccounnt(account);
}
}
}
private void UpdateFixAccountIssueMenuItem()

View File

@@ -46,7 +46,7 @@ namespace Wino.Core.MenuItems
public IEnumerable<IMailItemFolder> HandlingFolders => new List<IMailItemFolder>() { Parameter };
public MailAccount ParentAccount { get; }
public MailAccount ParentAccount { get; private set; }
public string AssignedAccountName => ParentAccount?.Name;
@@ -71,5 +71,7 @@ namespace Wino.Core.MenuItems
}
public override string ToString() => FolderName;
public void UpdateParentAccounnt(MailAccount account) => ParentAccount = account;
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
namespace Wino.Core.Messages.Accounts
{
/// <summary>
/// Emitted when account menu items are reordered.
/// </summary>
/// <param name="newOrderDictionary">New order info.</param>
public record AccountMenuItemsReordered(Dictionary<Guid, int> newOrderDictionary);
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using MoreLinq;
@@ -9,8 +10,25 @@ using Wino.Core.Domain.Models.Requests;
namespace Wino.Core.Requests
{
public record SendDraftRequest(SendDraftPreparationRequest Request) : RequestBase<BatchMarkReadRequest>(Request.MailItem, MailSynchronizerOperation.Send)
public record SendDraftRequest(SendDraftPreparationRequest Request)
: RequestBase<BatchMarkReadRequest>(Request.MailItem, MailSynchronizerOperation.Send),
ICustomFolderSynchronizationRequest
{
public List<Guid> SynchronizationFolderIds
{
get
{
var folderIds = new List<Guid> { Request.DraftFolder.Id };
if (Request.SentFolder != null)
{
folderIds.Add(Request.SentFolder.Id);
}
return folderIds;
}
}
public override IBatchChangeRequest CreateBatch(IEnumerable<IRequest> matchingItems)
=> new BatchSendDraftRequestRequest(matchingItems, Request);

View File

@@ -215,7 +215,7 @@ namespace Wino.Core.Services
public async Task<List<MailAccount>> GetAccountsAsync()
{
var accounts = await Connection.Table<MailAccount>().ToListAsync();
var accounts = await Connection.Table<MailAccount>().OrderBy(a => a.Order).ToListAsync();
foreach (var account in accounts)
{
@@ -299,12 +299,21 @@ namespace Wino.Core.Services
{
var account = await Connection.Table<MailAccount>().FirstOrDefaultAsync(a => a.Id == accountId);
if (account?.ProviderType == MailProviderType.IMAP4)
account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id);
if (account == null)
{
_logger.Error("Could not find account with id {AccountId}", accountId);
}
else
{
if (account.ProviderType == MailProviderType.IMAP4)
account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id);
account.Preferences = await GetAccountPreferencesAsync(account.Id);
account.Preferences = await GetAccountPreferencesAsync(account.Id);
return account;
return account;
}
return null;
}
public Task<CustomServerInformation> GetAccountCustomServerInformationAsync(Guid accountId)
@@ -334,6 +343,12 @@ namespace Wino.Core.Services
{
_preferencesService.StartupEntityId = account.Id;
}
else
{
// Set the order of the account.
// This can be changed by the user later in manage accounts page.
account.Order = accountCount;
}
await Connection.InsertAsync(account);
@@ -350,6 +365,8 @@ namespace Wino.Core.Services
// Outlook & Office 365 supports Focused inbox. Enabled by default.
bool isMicrosoftProvider = account.ProviderType == MailProviderType.Outlook || account.ProviderType == MailProviderType.Office365;
// TODO: This should come from account settings API.
// Wino doesn't have MailboxSettings yet.
if (isMicrosoftProvider)
account.Preferences.IsFocusedInboxEnabled = true;
@@ -397,5 +414,25 @@ namespace Wino.Core.Services
return account.SynchronizationDeltaIdentifier;
}
public async Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair)
{
foreach (var pair in accountIdOrderPair)
{
var account = await GetAccountAsync(pair.Key);
if (account == null)
{
_logger.Information("Could not find account with id {Key} for reordering. It may be a linked account.", pair.Key);
continue;
}
account.Order = pair.Value;
await Connection.UpdateAsync(account);
}
Messenger.Send(new AccountMenuItemsReordered(accountIdOrderPair));
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Wino.Core.Services
{
public class MailService : BaseDatabaseService, IMailService
{
private const int ItemLoadCount = 20;
private const int ItemLoadCount = 100;
private readonly IFolderService _folderService;
private readonly IContactService _contactService;
@@ -415,6 +415,14 @@ namespace Wino.Core.Services
await Connection.DeleteAsync(mailCopy).ConfigureAwait(false);
// If there are no more copies exists of the same mail, delete the MIME file as well.
var isMailExists = await IsMailExistsAsync(mailCopy.Id).ConfigureAwait(false);
if (!isMailExists)
{
await _mimeFileService.DeleteMimeMessageAsync(mailCopy.AssignedAccount.Id, mailCopy.FileId).ConfigureAwait(false);
}
ReportUIChange(new MailRemovedMessage(mailCopy));
}

View File

@@ -148,6 +148,16 @@ namespace Wino.Core.Synchronizers
await synchronizationSemaphore.WaitAsync(activeSynchronizationCancellationToken);
// Let servers to finish their job. Sometimes the servers doesn't respond immediately.
// TODO: Outlook sends back the deleted Draft. Might be a bug in the graph API or in Wino.
var hasSendDraftRequest = batches.Any(a => a is BatchSendDraftRequestRequest);
if (hasSendDraftRequest && DelaySendOperationSynchronization())
{
await Task.Delay(2000);
}
// Start the internal synchronization.
var synchronizationResult = await SynchronizeInternalAsync(options, activeSynchronizationCancellationToken).ConfigureAwait(false);
@@ -305,6 +315,7 @@ namespace Wino.Core.Synchronizers
return options;
}
public virtual bool DelaySendOperationSynchronization() => false;
public virtual IEnumerable<IRequestBundle<TBaseRequest>> Move(BatchMoveRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual IEnumerable<IRequestBundle<TBaseRequest>> ChangeFlag(BatchChangeFlagRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual IEnumerable<IRequestBundle<TBaseRequest>> MarkRead(BatchMarkReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));

View File

@@ -664,9 +664,18 @@ namespace Wino.Core.Synchronizers
ITransferProgress transferProgress = null,
CancellationToken cancellationToken = default)
{
var gmailMessage = await _gmailService.Users.Messages.Get("me", mailItem.Id).ExecuteAsync(cancellationToken).ConfigureAwait(false);
var request = _gmailService.Users.Messages.Get("me", mailItem.Id);
request.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
var gmailMessage = await request.ExecuteAsync(cancellationToken).ConfigureAwait(false);
var mimeMessage = gmailMessage.GetGmailMimeMessage();
if (mimeMessage == null)
{
_logger.Warning("Tried to download Gmail Raw Mime with {Id} id and server responded without a data.", mailItem.Id);
return;
}
await _gmailChangeProcessor.SaveMimeFileAsync(mailItem.FileId, mimeMessage, Account.Id).ConfigureAwait(false);
}
@@ -891,7 +900,8 @@ namespace Wino.Core.Synchronizers
// This seem to be a worse approach. Now both Outlook and Gmail use X-Wino-Draft-Id header to map drafts.
// This is a better approach since we don't need to fetch the draft resource to get the draft id.
if (mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader)
if (mailCopy.IsDraft
&& mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader)
&& Guid.TryParse(mimeMessage.Headers[Domain.Constants.WinoLocalDraftHeader], out Guid localDraftCopyUniqueId))
{
// This message belongs to existing local draft copy.

View File

@@ -406,6 +406,8 @@ namespace Wino.Core.Synchronizers
#region Mail Integration
public override bool DelaySendOperationSynchronization() => true;
public override IEnumerable<IRequestBundle<RequestInformation>> Move(BatchMoveRequest request)
{
var requestBody = new Microsoft.Graph.Me.Messages.Item.Move.MovePostRequestBody()
@@ -520,6 +522,56 @@ namespace Wino.Core.Synchronizers
});
}
public override IEnumerable<IRequestBundle<RequestInformation>> SendDraft(BatchSendDraftRequestRequest request)
{
var sendDraftPreparationRequest = request.Request;
// 1. Delete draft
// 2. Create new Message with new MIME.
// 3. Make sure that conversation id is tagged correctly for replies.
var mailCopyId = sendDraftPreparationRequest.MailItem.Id;
var mimeMessage = sendDraftPreparationRequest.Mime;
var batchDeleteRequest = new BatchDeleteRequest(new List<IRequest>()
{
new DeleteRequest(sendDraftPreparationRequest.MailItem)
});
var deleteBundle = Delete(batchDeleteRequest).ElementAt(0);
mimeMessage.Prepare(EncodingConstraint.None);
var plainTextBytes = Encoding.UTF8.GetBytes(mimeMessage.ToString());
var base64Encoded = Convert.ToBase64String(plainTextBytes);
var outlookMessage = new Message()
{
ConversationId = sendDraftPreparationRequest.MailItem.ThreadId
};
// Apply importance here as well just in case.
if (mimeMessage.Importance != MessageImportance.Normal)
outlookMessage.Importance = mimeMessage.Importance == MessageImportance.High ? Importance.High : Importance.Low;
var body = new Microsoft.Graph.Me.SendMail.SendMailPostRequestBody()
{
Message = outlookMessage
};
var sendRequest = _graphClient.Me.SendMail.ToPostRequestInformation(body);
sendRequest.Headers.Clear();
sendRequest.Headers.Add("Content-Type", "text/plain");
var stream = new MemoryStream(Encoding.UTF8.GetBytes(base64Encoded));
sendRequest.SetStreamContent(stream, "text/plain");
var sendMailRequest = new HttpRequestBundle<RequestInformation>(sendRequest, request);
return [deleteBundle, sendMailRequest];
}
public override async Task DownloadMissingMimeMessageAsync(IMailItem mailItem,
MailKit.ITransferProgress transferProgress = null,
CancellationToken cancellationToken = default)
@@ -636,7 +688,8 @@ namespace Wino.Core.Synchronizers
var mimeMessage = await DownloadMimeMessageAsync(message.Id, cancellationToken).ConfigureAwait(false);
var mailCopy = message.AsMailCopy();
if (mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader)
if (message.IsDraft.GetValueOrDefault()
&& mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader)
&& Guid.TryParse(mimeMessage.Headers[Domain.Constants.WinoLocalDraftHeader], out Guid localDraftCopyUniqueId))
{
// This message belongs to existing local draft copy.