- 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:
Burak Kaan Köse
2026-04-08 15:31:14 +02:00
parent a855d8c8a8
commit 76f6ae0a1e
18 changed files with 233 additions and 24 deletions
+55 -4
View File
@@ -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;
}
}
+58 -4
View File
@@ -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()));