Merged feature/vNext. Initial commit for Wino Mail 2.0
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
@@ -21,12 +21,10 @@ public record HttpRequestBundle<TRequest>(TRequest NativeRequest, IUIChangeReque
|
||||
/// <param name="BatchRequest">Batch request that is generated by base synchronizer.</param>
|
||||
public record HttpRequestBundle<TRequest, TResponse>(TRequest NativeRequest, IRequestBase Request) : HttpRequestBundle<TRequest>(NativeRequest, Request)
|
||||
{
|
||||
[RequiresDynamicCode("AOT")]
|
||||
[RequiresUnreferencedCode("AOT")]
|
||||
public async Task<TResponse> DeserializeBundleAsync(HttpResponseMessage httpResponse, CancellationToken cancellationToken = default)
|
||||
public async Task<TResponse> DeserializeBundleAsync(HttpResponseMessage httpResponse, JsonTypeInfo<TResponse> typeInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
return JsonSerializer.Deserialize<TResponse>(content) ?? throw new InvalidOperationException("Invalid Http Response Deserialization");
|
||||
return JsonSerializer.Deserialize(content, typeInfo) ?? throw new InvalidOperationException("Invalid Http Response Deserialization");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,20 @@ public class ImapRequest
|
||||
{
|
||||
public Func<IImapClient, IRequestBase, Task> IntegratorTask { get; }
|
||||
public IRequestBase Request { get; }
|
||||
public bool RequiresConnectedClient { get; }
|
||||
|
||||
public ImapRequest(Func<IImapClient, IRequestBase, Task> integratorTask, IRequestBase request)
|
||||
public ImapRequest(Func<IImapClient, IRequestBase, Task> integratorTask, IRequestBase request, bool requiresConnectedClient = true)
|
||||
{
|
||||
IntegratorTask = integratorTask;
|
||||
Request = request;
|
||||
RequiresConnectedClient = requiresConnectedClient;
|
||||
}
|
||||
}
|
||||
|
||||
public class ImapRequest<TRequestBaseType> : ImapRequest where TRequestBaseType : IRequestBase
|
||||
{
|
||||
public ImapRequest(Func<IImapClient, TRequestBaseType, Task> integratorTask, TRequestBaseType request)
|
||||
: base((client, request) => integratorTask(client, (TRequestBaseType)request), request)
|
||||
public ImapRequest(Func<IImapClient, TRequestBaseType, Task> integratorTask, TRequestBaseType request, bool requiresConnectedClient = true)
|
||||
: base((client, request) => integratorTask(client, (TRequestBaseType)request), request, requiresConnectedClient)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to accept a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record AcceptEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.AcceptEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful acceptance, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Accepted;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientUpdated));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If acceptance fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientReverted));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Core.Helpers;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to create a new calendar event on the server.
|
||||
/// Non-recurring events create an optimistic in-memory item for immediate UI feedback.
|
||||
/// Recurring events skip optimistic rendering and rely on provider synchronization to materialize instances.
|
||||
/// </summary>
|
||||
public record CreateCalendarEventRequest : CalendarRequestBase
|
||||
{
|
||||
public CalendarEventComposeResult ComposeResult { get; }
|
||||
public AccountCalendar AssignedCalendar { get; }
|
||||
public PreparedCalendarEventCreateModel PreparedEvent { get; }
|
||||
public CalendarItem PreparedItem => PreparedEvent.CalendarItem;
|
||||
public bool IsRecurring => !string.IsNullOrWhiteSpace(ComposeResult?.Recurrence);
|
||||
|
||||
public CreateCalendarEventRequest(CalendarEventComposeResult composeResult, AccountCalendar assignedCalendar)
|
||||
: this(composeResult, assignedCalendar, CalendarEventComposeMapper.Prepare(composeResult, assignedCalendar))
|
||||
{
|
||||
}
|
||||
|
||||
private CreateCalendarEventRequest(
|
||||
CalendarEventComposeResult composeResult,
|
||||
AccountCalendar assignedCalendar,
|
||||
PreparedCalendarEventCreateModel preparedEvent)
|
||||
: base(ShouldCreateOptimisticItem(composeResult) ? preparedEvent.CalendarItem : null)
|
||||
{
|
||||
ComposeResult = composeResult ?? throw new ArgumentNullException(nameof(composeResult));
|
||||
AssignedCalendar = assignedCalendar ?? throw new ArgumentNullException(nameof(assignedCalendar));
|
||||
PreparedEvent = preparedEvent ?? throw new ArgumentNullException(nameof(preparedEvent));
|
||||
}
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.CreateEvent;
|
||||
|
||||
public override int ResynchronizationDelay => 5000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
if (Item == null)
|
||||
return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
if (Item == null)
|
||||
return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
|
||||
}
|
||||
|
||||
private static bool ShouldCreateOptimisticItem(CalendarEventComposeResult composeResult)
|
||||
=> string.IsNullOrWhiteSpace(composeResult?.Recurrence);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to decline a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record DeclineEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeclineEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful decline, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Cancelled;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientUpdated));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If decline fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientReverted));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to delete a calendar event on the server.
|
||||
/// </summary>
|
||||
public record DeleteCalendarEventRequest(CalendarItem Item) : CalendarRequestBase(Item)
|
||||
{
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeleteEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful deletion, resync to confirm the event was removed.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Notify UI that the event was deleted
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If deletion fails, we should notify the UI to add it back
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Outlook-specific request to decline a calendar event invitation.
|
||||
/// In Outlook, declined events are removed from the calendar by the API after synchronization,
|
||||
/// so this request sends a delete notification to remove the event from the UI.
|
||||
/// </summary>
|
||||
public record OutlookDeclineEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.DeclineEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful decline, we need to resync to confirm the event is removed.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// In Outlook, declined events are deleted from the calendar after sync
|
||||
// Send deleted message to remove from UI immediately
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemDeleted(Item));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If decline fails, restore the previous status and re-add the event
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemAdded(Item));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to tentatively accept a calendar event invitation on the server.
|
||||
/// The calendar item status should be updated locally before queuing this request.
|
||||
/// </summary>
|
||||
public record TentativeEventRequest(CalendarItem Item, string ResponseMessage = null) : CalendarRequestBase(Item)
|
||||
{
|
||||
private readonly CalendarItemStatus _previousStatus = Item.Status;
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.TentativeEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful tentative acceptance, we need to resync to get updated status.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Update the item status locally
|
||||
Item.Status = CalendarItemStatus.Tentative;
|
||||
|
||||
// Notify UI that the event status was updated
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientUpdated));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If tentative acceptance fails, revert to the previous status
|
||||
Item.Status = _previousStatus;
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientReverted));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.Client.Calendar;
|
||||
|
||||
namespace Wino.Core.Requests.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Request to update an existing calendar event on the server.
|
||||
/// The calendar item should be already updated in the local database before queuing this request.
|
||||
/// </summary>
|
||||
public record UpdateCalendarEventRequest(CalendarItem Item, List<CalendarEventAttendee> Attendees) : CalendarRequestBase(Item)
|
||||
{
|
||||
/// <summary>
|
||||
/// Original attendees before the update, used for reverting changes if the update fails.
|
||||
/// </summary>
|
||||
public List<CalendarEventAttendee> OriginalAttendees { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Original calendar item state before the update, used for reverting changes if the update fails.
|
||||
/// </summary>
|
||||
public CalendarItem OriginalItem { get; init; }
|
||||
|
||||
public override CalendarSynchronizerOperation Operation => CalendarSynchronizerOperation.UpdateEvent;
|
||||
|
||||
/// <summary>
|
||||
/// After successful update, we need to resync to ensure changes are properly reflected.
|
||||
/// </summary>
|
||||
public override int ResynchronizationDelay => 2000;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Notify UI that the event was updated locally
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientUpdated));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
// If update fails, restore the original state
|
||||
if (OriginalItem != null && OriginalAttendees != null)
|
||||
{
|
||||
// Send the original item back to restore UI state
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(OriginalItem, CalendarItemUpdateSource.ClientReverted));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: just notify with current item to trigger refresh
|
||||
WeakReferenceMessenger.Default.Send(new CalendarItemUpdated(Item, CalendarItemUpdateSource.ClientReverted));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
|
||||
namespace Wino.Core.Requests.Folder;
|
||||
|
||||
public record CreateSubFolderRequest(MailItemFolder Folder, string NewFolderName) : FolderRequestBase(Folder, FolderSynchronizerOperation.CreateSubFolder)
|
||||
{
|
||||
public override void ApplyUIChanges() { }
|
||||
public override void RevertUIChanges() { }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Core.Requests.Folder;
|
||||
|
||||
public record DeleteFolderRequest(MailItemFolder Folder) : FolderRequestBase(Folder, FolderSynchronizerOperation.DeleteFolder)
|
||||
{
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new FolderDeleted(Folder));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges() { }
|
||||
}
|
||||
@@ -17,10 +17,14 @@ public record MarkFolderAsReadRequest(MailItemFolder Folder, List<MailCopy> Mail
|
||||
|
||||
foreach (var item in MailsToMarkRead)
|
||||
{
|
||||
// Skip if already read
|
||||
if (item.IsRead) continue;
|
||||
|
||||
item.IsRead = true;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new BulkMailUpdatedMessage(MailsToMarkRead));
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item, MailUpdateSource.ClientUpdated, MailCopyChangeFlags.IsRead));
|
||||
}
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
@@ -29,10 +33,14 @@ public record MarkFolderAsReadRequest(MailItemFolder Folder, List<MailCopy> Mail
|
||||
|
||||
foreach (var item in MailsToMarkRead)
|
||||
{
|
||||
// Skip if already unread (wasn't changed by ApplyUIChanges)
|
||||
if (!item.IsRead) continue;
|
||||
|
||||
item.IsRead = false;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new BulkMailUpdatedMessage(MailsToMarkRead));
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item, MailUpdateSource.ClientReverted, MailCopyChangeFlags.IsRead));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Guid> SynchronizationFolderIds => [Folder.Id];
|
||||
|
||||
@@ -12,24 +12,38 @@ namespace Wino.Core.Requests.Mail;
|
||||
public record ChangeFlagRequest(MailCopy Item, bool IsFlagged) : MailRequestBase(Item),
|
||||
ICustomFolderSynchronizationRequest
|
||||
{
|
||||
private readonly bool _originalIsFlagged = Item.IsFlagged;
|
||||
|
||||
public List<Guid> SynchronizationFolderIds => [Item.FolderId];
|
||||
|
||||
public bool ExcludeMustHaveFolders => true;
|
||||
|
||||
public override MailSynchronizerOperation Operation => MailSynchronizerOperation.ChangeFlag;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this request represents an actual state change.
|
||||
/// If the mail is already in the desired flagged state, no change is needed.
|
||||
/// </summary>
|
||||
public bool IsNoOp { get; } = Item.IsFlagged == IsFlagged;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Skip UI update if the mail is already in the desired state
|
||||
if (IsNoOp) return;
|
||||
|
||||
Item.IsFlagged = IsFlagged;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item));
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item, MailUpdateSource.ClientUpdated, MailCopyChangeFlags.IsFlagged));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
Item.IsFlagged = !IsFlagged;
|
||||
// Skip UI revert if this was a no-op request
|
||||
if (IsNoOp) return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item));
|
||||
Item.IsFlagged = _originalIsFlagged;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item, MailUpdateSource.ClientReverted, MailCopyChangeFlags.IsFlagged));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Domain.Models.Requests;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Core.Requests.Mail;
|
||||
|
||||
@@ -24,6 +22,7 @@ public record CreateDraftRequest(DraftPreparationRequest DraftPreperationRequest
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item));
|
||||
// Keep local draft intact when create-draft synchronization fails.
|
||||
// This allows users to retry sending the local draft to the server.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,24 +11,38 @@ namespace Wino.Core.Requests.Mail;
|
||||
|
||||
public record MarkReadRequest(MailCopy Item, bool IsRead) : MailRequestBase(Item), ICustomFolderSynchronizationRequest
|
||||
{
|
||||
private readonly bool _originalIsRead = Item.IsRead;
|
||||
|
||||
public List<Guid> SynchronizationFolderIds => [Item.FolderId];
|
||||
|
||||
public override MailSynchronizerOperation Operation => MailSynchronizerOperation.MarkRead;
|
||||
|
||||
public bool ExcludeMustHaveFolders => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this request represents an actual state change.
|
||||
/// If the mail is already in the desired read state, no change is needed.
|
||||
/// </summary>
|
||||
public bool IsNoOp { get; } = Item.IsRead == IsRead;
|
||||
|
||||
public override void ApplyUIChanges()
|
||||
{
|
||||
// Skip UI update if the mail is already in the desired state
|
||||
if (IsNoOp) return;
|
||||
|
||||
Item.IsRead = IsRead;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item));
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item, MailUpdateSource.ClientUpdated, MailCopyChangeFlags.IsRead));
|
||||
}
|
||||
|
||||
public override void RevertUIChanges()
|
||||
{
|
||||
Item.IsRead = !IsRead;
|
||||
// Skip UI revert if this was a no-op request
|
||||
if (IsNoOp) return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item));
|
||||
Item.IsRead = _originalIsRead;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item, MailUpdateSource.ClientReverted, MailCopyChangeFlags.IsRead));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user