- Fix for gmail calendar event creation.
- Proper junk API calls for gmail and outlook, not just moving the item. - Add ability to hide ai actions panel.
This commit is contained in:
@@ -85,6 +85,7 @@ public static class SynchronizationActionHelper
|
||||
{
|
||||
MarkReadRequest r => r.IsRead ? "MarkRead" : "MarkUnread",
|
||||
ChangeFlagRequest r => r.IsFlagged ? "SetFlag" : "ClearFlag",
|
||||
ChangeJunkStateRequest r => r.IsJunk ? "MarkJunk" : "MarkNotJunk",
|
||||
ArchiveRequest r => r.IsArchiving ? "Archive" : "Unarchive",
|
||||
_ => request.Operation.ToString()
|
||||
};
|
||||
@@ -100,6 +101,8 @@ public static class SynchronizationActionHelper
|
||||
"MarkUnread" => string.Format(Translator.SyncAction_MarkingAsUnread, count),
|
||||
"Delete" => string.Format(Translator.SyncAction_Deleting, count),
|
||||
"Move" => string.Format(Translator.SyncAction_Moving, count),
|
||||
"MarkJunk" => string.Format(Translator.SyncAction_Moving, count),
|
||||
"MarkNotJunk" => string.Format(Translator.SyncAction_Moving, count),
|
||||
"Archive" => string.Format(Translator.SyncAction_Archiving, count),
|
||||
"Unarchive" => string.Format(Translator.SyncAction_Unarchiving, count),
|
||||
"SetFlag" => string.Format(Translator.SyncAction_SettingFlag, count),
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Core.Requests.Mail;
|
||||
|
||||
public record ChangeJunkStateRequest(bool IsJunk, MailCopy Item, MailItemFolder FromFolder, MailItemFolder TargetFolder)
|
||||
: MailRequestBase(Item), ICustomFolderSynchronizationRequest
|
||||
{
|
||||
public List<Guid> SynchronizationFolderIds
|
||||
{
|
||||
get
|
||||
{
|
||||
var folderIds = new List<Guid> { FromFolder.Id };
|
||||
|
||||
if (TargetFolder != null)
|
||||
{
|
||||
folderIds.Add(TargetFolder.Id);
|
||||
}
|
||||
|
||||
return folderIds;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ExcludeMustHaveFolders => false;
|
||||
|
||||
public override MailSynchronizerOperation Operation => MailSynchronizerOperation.ChangeJunkState;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item, EntityUpdateSource.ClientUpdated));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new MailAddedMessage(Item, EntityUpdateSource.ClientReverted));
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchChangeJunkStateRequest : BatchCollection<ChangeJunkStateRequest>
|
||||
{
|
||||
public BatchChangeJunkStateRequest(IEnumerable<ChangeJunkStateRequest> collection) : base(collection)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,10 @@ public class WinoRequestProcessor : IWinoRequestProcessor
|
||||
var inboxFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Inbox)
|
||||
?? throw new UnavailableSpecialFolderException(SpecialFolderType.Inbox, mailItem.AssignedAccount.Id);
|
||||
|
||||
return new MoveRequest(mailItem, mailItem.AssignedFolder, inboxFolder);
|
||||
if (mailItem.AssignedAccount.ProviderType == MailProviderType.IMAP4)
|
||||
return new MoveRequest(mailItem, mailItem.AssignedFolder, inboxFolder);
|
||||
|
||||
return new ChangeJunkStateRequest(false, mailItem, mailItem.AssignedFolder, inboxFolder);
|
||||
}
|
||||
else if (action == MailOperation.UnArchive)
|
||||
{
|
||||
@@ -204,7 +207,10 @@ public class WinoRequestProcessor : IWinoRequestProcessor
|
||||
var junkFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Junk)
|
||||
?? throw new UnavailableSpecialFolderException(SpecialFolderType.Junk, mailItem.AssignedAccount.Id);
|
||||
|
||||
return new MoveRequest(mailItem, mailItem.AssignedFolder, junkFolder);
|
||||
if (mailItem.AssignedAccount.ProviderType == MailProviderType.IMAP4)
|
||||
return new MoveRequest(mailItem, mailItem.AssignedFolder, junkFolder);
|
||||
|
||||
return new ChangeJunkStateRequest(true, mailItem, mailItem.AssignedFolder, junkFolder);
|
||||
}
|
||||
else if (action == MailOperation.AlwaysMoveToFocused || action == MailOperation.AlwaysMoveToOther)
|
||||
return new AlwaysMoveToRequest(mailItem, action == MailOperation.AlwaysMoveToFocused);
|
||||
|
||||
@@ -1219,6 +1219,36 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(networkCall, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<IClientServiceRequest>> ChangeJunkState(BatchChangeJunkStateRequest request)
|
||||
{
|
||||
bool isJunk = request[0].IsJunk;
|
||||
|
||||
var addLabelIds = new HashSet<string>();
|
||||
var removeLabelIds = new HashSet<string>();
|
||||
|
||||
if (isJunk)
|
||||
{
|
||||
addLabelIds.Add(ServiceConstants.SPAM_LABEL_ID);
|
||||
removeLabelIds.Add(ServiceConstants.INBOX_LABEL_ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
addLabelIds.Add(ServiceConstants.INBOX_LABEL_ID);
|
||||
removeLabelIds.Add(ServiceConstants.SPAM_LABEL_ID);
|
||||
}
|
||||
|
||||
var batchModifyRequest = new BatchModifyMessagesRequest
|
||||
{
|
||||
Ids = request.Select(a => a.Item.Id.ToString()).ToList(),
|
||||
AddLabelIds = addLabelIds.ToList(),
|
||||
RemoveLabelIds = removeLabelIds.ToList()
|
||||
};
|
||||
|
||||
var networkCall = _gmailService.Users.Messages.BatchModify(batchModifyRequest, "me");
|
||||
|
||||
return [new HttpRequestBundle<IClientServiceRequest>(networkCall, request)];
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<IClientServiceRequest>> MarkRead(BatchMarkReadRequest request)
|
||||
{
|
||||
bool readStatus = request[0].IsRead;
|
||||
@@ -1783,11 +1813,13 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
private static bool IsExistingEntityOperation(IUIChangeRequest request)
|
||||
=> request is BatchDeleteRequest
|
||||
|| request is BatchMoveRequest
|
||||
|| request is BatchChangeJunkStateRequest
|
||||
|| request is BatchChangeFlagRequest
|
||||
|| request is BatchMarkReadRequest
|
||||
|| request is BatchArchiveRequest
|
||||
|| request is DeleteRequest
|
||||
|| request is MoveRequest
|
||||
|| request is ChangeJunkStateRequest
|
||||
|| request is ChangeFlagRequest
|
||||
|| request is MarkReadRequest
|
||||
|| request is ArchiveRequest
|
||||
@@ -1803,6 +1835,8 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
private static bool ShouldRevertOptimisticMailStateChange(IUIChangeRequest request)
|
||||
=> request is BatchMarkReadRequest
|
||||
|| request is MarkReadRequest
|
||||
|| request is BatchChangeJunkStateRequest
|
||||
|| request is ChangeJunkStateRequest
|
||||
|| request is BatchChangeFlagRequest
|
||||
|| request is ChangeFlagRequest;
|
||||
|
||||
@@ -2504,25 +2538,28 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
googleEvent.Start = new EventDateTime
|
||||
{
|
||||
Date = calendarItem.StartDate.ToString("yyyy-MM-dd"),
|
||||
TimeZone = calendarItem.StartTimeZone
|
||||
TimeZone = NormalizeGoogleTimeZoneId(calendarItem.StartTimeZone)
|
||||
};
|
||||
googleEvent.End = new EventDateTime
|
||||
{
|
||||
Date = calendarItem.EndDate.ToString("yyyy-MM-dd"),
|
||||
TimeZone = calendarItem.EndTimeZone
|
||||
TimeZone = NormalizeGoogleTimeZoneId(calendarItem.EndTimeZone)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var startTimeZone = NormalizeGoogleTimeZoneId(calendarItem.StartTimeZone);
|
||||
var endTimeZone = NormalizeGoogleTimeZoneId(calendarItem.EndTimeZone ?? calendarItem.StartTimeZone);
|
||||
|
||||
googleEvent.Start = new EventDateTime
|
||||
{
|
||||
DateTimeDateTimeOffset = new DateTimeOffset(calendarItem.StartDate, ResolveOffset(calendarItem.StartDate, calendarItem.StartTimeZone)),
|
||||
TimeZone = calendarItem.StartTimeZone
|
||||
TimeZone = startTimeZone
|
||||
};
|
||||
googleEvent.End = new EventDateTime
|
||||
{
|
||||
DateTimeDateTimeOffset = new DateTimeOffset(calendarItem.EndDate, ResolveOffset(calendarItem.EndDate, calendarItem.EndTimeZone ?? calendarItem.StartTimeZone)),
|
||||
TimeZone = calendarItem.EndTimeZone
|
||||
TimeZone = endTimeZone
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2896,4 +2933,18 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeGoogleTimeZoneId(string timeZoneId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(timeZoneId))
|
||||
return timeZoneId;
|
||||
|
||||
if (timeZoneId.Contains('/'))
|
||||
return timeZoneId;
|
||||
|
||||
if (TimeZoneInfo.TryConvertWindowsIdToIanaId(timeZoneId, out var ianaTimeZoneId))
|
||||
return ianaTimeZoneId;
|
||||
|
||||
return timeZoneId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1358,12 +1358,10 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
/// <param name="requestInformation">Post request information.</param>
|
||||
/// <param name="content">Content object to serialize.</param>
|
||||
/// <returns>Updated post request information.</returns>
|
||||
private RequestInformation PreparePostRequestInformation(RequestInformation requestInformation, Microsoft.Graph.Me.Messages.Item.Move.MovePostRequestBody content = null)
|
||||
private RequestInformation PreparePostRequestInformation(RequestInformation requestInformation, string contentJson = "{}")
|
||||
{
|
||||
requestInformation.Headers.Clear();
|
||||
|
||||
string contentJson = content == null ? "{}" : JsonSerializer.Serialize(content, OutlookSynchronizerJsonContext.Default.MovePostRequestBody);
|
||||
|
||||
requestInformation.Content = new MemoryStream(Encoding.UTF8.GetBytes(contentJson));
|
||||
requestInformation.HttpMethod = Method.POST;
|
||||
requestInformation.Headers.Add("Content-Type", "application/json");
|
||||
@@ -1371,6 +1369,26 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
return requestInformation;
|
||||
}
|
||||
|
||||
private RequestInformation PreparePostRequestInformation(RequestInformation requestInformation, Microsoft.Graph.Me.Messages.Item.Move.MovePostRequestBody content)
|
||||
=> PreparePostRequestInformation(requestInformation, JsonSerializer.Serialize(content, OutlookSynchronizerJsonContext.Default.MovePostRequestBody));
|
||||
|
||||
private RequestInformation PrepareReportMessageRequestInformation(ChangeJunkStateRequest request)
|
||||
{
|
||||
var reportAction = request.IsJunk ? "junk" : "notJunk";
|
||||
var body = $$"""
|
||||
{
|
||||
"IsMessageMoveRequested": true,
|
||||
"ReportAction": "{{reportAction}}"
|
||||
}
|
||||
""";
|
||||
|
||||
return PreparePostRequestInformation(new RequestInformation
|
||||
{
|
||||
URI = new Uri($"https://graph.microsoft.com/beta/me/messages/{Uri.EscapeDataString(request.Item.Id)}/reportMessage"),
|
||||
HttpMethod = Method.POST
|
||||
}, body);
|
||||
}
|
||||
|
||||
#region Mail Integration
|
||||
|
||||
public override bool DelaySendOperationSynchronization() => true;
|
||||
@@ -1402,6 +1420,16 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
});
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<RequestInformation>> ChangeJunkState(BatchChangeJunkStateRequest request)
|
||||
{
|
||||
return request
|
||||
.Select(item => (IRequestBundle<RequestInformation>)new HttpRequestBundle<RequestInformation>(
|
||||
PrepareReportMessageRequestInformation(item),
|
||||
item,
|
||||
item))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override List<IRequestBundle<RequestInformation>> MarkRead(BatchMarkReadRequest request)
|
||||
{
|
||||
return ForEachRequest(request, (item) =>
|
||||
@@ -1747,8 +1775,32 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
}
|
||||
}
|
||||
|
||||
var directRequests = batchedRequests
|
||||
.Where(bundle => bundle.Request is ChangeJunkStateRequest)
|
||||
.ToList();
|
||||
|
||||
foreach (var directRequest in directRequests)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _graphClient.RequestAdapter.SendAsync(
|
||||
directRequest.NativeRequest,
|
||||
Message.CreateFromDiscriminatorValue,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
directRequest.UIChangeRequest?.RevertUIChanges();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Now batch and execute the network requests.
|
||||
var batchedGroups = batchedRequests.Batch((int)MaximumAllowedBatchRequestSize);
|
||||
var batchEligibleRequests = batchedRequests
|
||||
.Except(directRequests)
|
||||
.ToList();
|
||||
|
||||
var batchedGroups = batchEligibleRequests.Batch((int)MaximumAllowedBatchRequestSize);
|
||||
|
||||
foreach (var batch in batchedGroups)
|
||||
{
|
||||
@@ -1910,11 +1962,13 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
|
||||
private static bool IsExistingEntityOperation(IUIChangeRequest request)
|
||||
=> request is BatchDeleteRequest
|
||||
|| request is BatchMoveRequest
|
||||
|| request is BatchChangeJunkStateRequest
|
||||
|| request is BatchChangeFlagRequest
|
||||
|| request is BatchMarkReadRequest
|
||||
|| request is BatchArchiveRequest
|
||||
|| request is DeleteRequest
|
||||
|| request is MoveRequest
|
||||
|| request is ChangeJunkStateRequest
|
||||
|| request is ChangeFlagRequest
|
||||
|| request is MarkReadRequest
|
||||
|| request is ArchiveRequest
|
||||
|
||||
@@ -181,6 +181,9 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
||||
case MailSynchronizerOperation.ChangeFlag:
|
||||
nativeRequests.AddRange(ChangeFlag(new BatchChangeFlagRequest(group.Cast<ChangeFlagRequest>())));
|
||||
break;
|
||||
case MailSynchronizerOperation.ChangeJunkState:
|
||||
nativeRequests.AddRange(ChangeJunkState(new BatchChangeJunkStateRequest(group.Cast<ChangeJunkStateRequest>())));
|
||||
break;
|
||||
case MailSynchronizerOperation.AlwaysMoveTo:
|
||||
nativeRequests.AddRange(AlwaysMoveTo(new BatchAlwaysMoveToRequest(group.Cast<AlwaysMoveToRequest>())));
|
||||
break;
|
||||
@@ -577,6 +580,7 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
|
||||
public virtual bool DelaySendOperationSynchronization() => false;
|
||||
public virtual List<IRequestBundle<TBaseRequest>> Move(BatchMoveRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> ChangeFlag(BatchChangeFlagRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> ChangeJunkState(BatchChangeJunkStateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> MarkRead(BatchMarkReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> Delete(BatchDeleteRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
public virtual List<IRequestBundle<TBaseRequest>> AlwaysMoveTo(BatchAlwaysMoveToRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
|
||||
|
||||
Reference in New Issue
Block a user