2026-02-10 01:03:03 +01:00
|
|
|
using System;
|
2024-04-18 01:44:37 +02:00
|
|
|
using System.Collections.Generic;
|
2025-10-26 23:35:09 +01:00
|
|
|
using System.Collections.ObjectModel;
|
2026-02-10 01:03:03 +01:00
|
|
|
using System.ComponentModel;
|
2024-04-18 01:44:37 +02:00
|
|
|
using System.Linq;
|
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
2025-10-29 17:02:58 +01:00
|
|
|
using Wino.Core.Domain;
|
2026-02-09 22:39:30 +01:00
|
|
|
using Wino.Core.Domain.Entities.Shared;
|
2025-10-28 14:43:22 +01:00
|
|
|
using Wino.Core.Domain.Enums;
|
2026-02-09 22:39:30 +01:00
|
|
|
using Wino.Core.Domain.Interfaces;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
namespace Wino.Mail.ViewModels.Data;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Thread mail item (multiple IMailItem) view model representation.
|
|
|
|
|
/// </summary>
|
2026-02-09 22:39:30 +01:00
|
|
|
public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListItem, IMailItemDisplayInformation
|
2024-04-18 01:44:37 +02:00
|
|
|
{
|
2025-10-03 15:46:38 +02:00
|
|
|
private readonly string _threadId;
|
2026-02-08 01:41:32 +01:00
|
|
|
private readonly HashSet<Guid> _uniqueIdSet = [];
|
|
|
|
|
private MailItemViewModel _cachedLatestMailViewModel;
|
2026-03-01 12:07:15 +01:00
|
|
|
private int _suspendChildPropertyNotificationsCount;
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2025-02-16 11:54:23 +01:00
|
|
|
[ObservableProperty]
|
2025-10-03 15:46:38 +02:00
|
|
|
[NotifyPropertyChangedRecipients]
|
2025-10-27 22:52:26 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
2025-10-03 15:46:38 +02:00
|
|
|
public partial bool IsThreadExpanded { get; set; }
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
[ObservableProperty]
|
2025-10-27 22:52:26 +01:00
|
|
|
[NotifyPropertyChangedRecipients]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
2025-10-03 15:46:38 +02:00
|
|
|
public partial bool IsSelected { get; set; }
|
|
|
|
|
|
2026-02-08 01:41:32 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Direct callback invoked when <see cref="IsSelected"/> changes.
|
|
|
|
|
/// Used by the ListViewItem container to update its IsCustomSelected DP
|
|
|
|
|
/// without subscribing to INotifyPropertyChanged (faster, AOT-safe).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Action<bool> OnSelectionChanged { get; set; }
|
|
|
|
|
|
|
|
|
|
partial void OnIsSelectedChanged(bool value) => OnSelectionChanged?.Invoke(value);
|
|
|
|
|
|
2026-02-05 12:48:38 +01:00
|
|
|
[ObservableProperty]
|
|
|
|
|
public partial bool IsBusy { get; set; }
|
|
|
|
|
|
2025-10-27 22:52:26 +01:00
|
|
|
public bool IsSelectedOrExpanded => IsSelected || IsThreadExpanded;
|
|
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the number of emails in this thread
|
|
|
|
|
/// </summary>
|
2025-10-26 23:35:09 +01:00
|
|
|
public int EmailCount => ThreadEmails.Count;
|
2025-10-03 15:46:38 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the latest email's subject for display
|
|
|
|
|
/// </summary>
|
2025-10-28 14:43:22 +01:00
|
|
|
public string Subject => latestMailViewModel?.MailCopy?.Subject;
|
2025-10-03 15:46:38 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the latest email's sender name for display
|
|
|
|
|
/// </summary>
|
2025-11-12 00:39:37 +01:00
|
|
|
public string FromName => latestMailViewModel?.MailCopy?.FromName ?? Translator.UnknownSender;
|
2025-10-03 15:46:38 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the latest email's creation date for sorting
|
|
|
|
|
/// </summary>
|
2025-10-28 14:43:22 +01:00
|
|
|
public DateTime CreationDate => latestMailViewModel?.MailCopy?.CreationDate ?? DateTime.MinValue;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the latest email's sender address for display
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string FromAddress => latestMailViewModel?.FromAddress ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the preview text from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string PreviewText => latestMailViewModel?.PreviewText ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether any email in this thread has attachments
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool HasAttachments => ThreadEmails.Any(e => e.HasAttachments);
|
|
|
|
|
|
2026-02-11 01:49:29 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether any email in this thread is a calendar invitation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsCalendarEvent => ThreadEmails.Any(e => e.IsCalendarEvent);
|
|
|
|
|
|
2025-10-28 14:43:22 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether any email in this thread is flagged
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsFlagged => ThreadEmails.Any(e => e.IsFlagged);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether the latest email is focused
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsFocused => latestMailViewModel?.IsFocused ?? false;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether all emails in this thread are read
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsRead => ThreadEmails.All(e => e.IsRead);
|
|
|
|
|
|
2026-04-11 21:02:51 +02:00
|
|
|
public bool HasReadReceiptTracking => latestMailViewModel?.HasReadReceiptTracking ?? false;
|
|
|
|
|
|
|
|
|
|
public bool IsReadReceiptAcknowledged => latestMailViewModel?.IsReadReceiptAcknowledged ?? false;
|
|
|
|
|
|
|
|
|
|
public string ReadReceiptDisplayText => latestMailViewModel?.ReadReceiptDisplayText ?? string.Empty;
|
|
|
|
|
|
2025-10-28 14:43:22 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets whether any email in this thread is a draft
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsDraft => ThreadEmails.Any(e => e.IsDraft);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the draft ID from the latest email if it's a draft
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string DraftId => latestMailViewModel?.DraftId ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Id => latestMailViewModel?.Id ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the importance of the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public MailImportance Importance => latestMailViewModel?.Importance ?? MailImportance.Normal;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the thread ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string ThreadId => latestMailViewModel?.ThreadId ?? _threadId;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the message ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string MessageId => latestMailViewModel?.MessageId ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the references from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string References => latestMailViewModel?.References ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the in-reply-to from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string InReplyTo => latestMailViewModel?.InReplyTo ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the file ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Guid FileId => latestMailViewModel?.FileId ?? Guid.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the folder ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Guid FolderId => latestMailViewModel?.FolderId ?? Guid.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the unique ID from the latest email
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Guid UniqueId => latestMailViewModel?.UniqueId ?? Guid.Empty;
|
|
|
|
|
|
2026-03-07 11:43:56 +01:00
|
|
|
public Guid? ContactPictureFileId => latestMailViewModel?.MailCopy?.SenderContact?.ContactPictureFileId;
|
2025-10-28 14:43:22 +01:00
|
|
|
|
|
|
|
|
public bool ThumbnailUpdatedEvent => latestMailViewModel?.ThumbnailUpdatedEvent ?? false;
|
2025-10-03 15:46:38 +02:00
|
|
|
|
2026-02-09 22:39:30 +01:00
|
|
|
public AccountContact SenderContact => latestMailViewModel?.MailCopy?.SenderContact;
|
|
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
/// <summary>
|
2025-10-26 23:35:09 +01:00
|
|
|
/// Gets all emails in this thread (observable)
|
2025-10-03 15:46:38 +02:00
|
|
|
/// </summary>
|
2025-10-26 23:35:09 +01:00
|
|
|
///
|
|
|
|
|
[ObservableProperty]
|
2025-11-01 12:11:05 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(EmailCount))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(Subject))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(FromName))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(CreationDate))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(FromAddress))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(PreviewText))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(HasAttachments))]
|
2026-02-11 01:49:29 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsCalendarEvent))]
|
2025-11-01 12:11:05 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsFlagged))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsFocused))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsRead))]
|
2026-04-11 21:02:51 +02:00
|
|
|
[NotifyPropertyChangedFor(nameof(HasReadReceiptTracking))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsReadReceiptAcknowledged))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(ReadReceiptDisplayText))]
|
2025-11-01 12:11:05 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsDraft))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(DraftId))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(Id))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(Importance))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(ThreadId))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(MessageId))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(References))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(InReplyTo))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(FileId))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(FolderId))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(UniqueId))]
|
2026-03-07 11:43:56 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(ContactPictureFileId))]
|
2026-02-09 22:39:30 +01:00
|
|
|
[NotifyPropertyChangedFor(nameof(SenderContact))]
|
2025-10-26 23:35:09 +01:00
|
|
|
public partial ObservableCollection<MailItemViewModel> ThreadEmails { get; set; } = [];
|
2025-10-03 15:46:38 +02:00
|
|
|
|
2026-02-08 01:41:32 +01:00
|
|
|
private MailItemViewModel latestMailViewModel => _cachedLatestMailViewModel;
|
2025-10-18 11:45:10 +02:00
|
|
|
|
2025-11-01 12:35:47 +01:00
|
|
|
public DateTime SortingDate => CreationDate;
|
|
|
|
|
|
|
|
|
|
public string SortingName => FromName;
|
|
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
public ThreadMailItemViewModel(string threadId)
|
2025-02-16 11:54:23 +01:00
|
|
|
{
|
2025-10-03 15:46:38 +02:00
|
|
|
_threadId = threadId;
|
2025-02-16 11:54:23 +01:00
|
|
|
}
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2026-03-01 12:07:15 +01:00
|
|
|
internal void SuspendChildPropertyNotifications() => _suspendChildPropertyNotificationsCount++;
|
|
|
|
|
|
|
|
|
|
internal void ResumeChildPropertyNotifications()
|
|
|
|
|
{
|
|
|
|
|
if (_suspendChildPropertyNotificationsCount > 0)
|
|
|
|
|
{
|
|
|
|
|
_suspendChildPropertyNotificationsCount--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshLatestMailCache()
|
|
|
|
|
{
|
|
|
|
|
_cachedLatestMailViewModel = ThreadEmails
|
|
|
|
|
.OrderByDescending(static item => item.MailCopy.CreationDate)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Adds an email to this thread
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void AddEmail(MailItemViewModel email)
|
|
|
|
|
{
|
|
|
|
|
if (email.MailCopy.ThreadId != _threadId)
|
|
|
|
|
throw new ArgumentException($"Email ThreadId '{email.MailCopy.ThreadId}' does not match expander ThreadId '{_threadId}'");
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2025-11-12 15:44:43 +01:00
|
|
|
// Insert email in sorted order by CreationDate (newest first, oldest last)
|
|
|
|
|
var insertIndex = 0;
|
|
|
|
|
for (int i = 0; i < ThreadEmails.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (ThreadEmails[i].MailCopy.CreationDate < email.MailCopy.CreationDate)
|
|
|
|
|
{
|
|
|
|
|
insertIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
insertIndex = i + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ThreadEmails.Insert(insertIndex, email);
|
2026-02-10 01:03:03 +01:00
|
|
|
email.PropertyChanged += ThreadEmailPropertyChanged;
|
2026-02-08 01:41:32 +01:00
|
|
|
_uniqueIdSet.Add(email.MailCopy.UniqueId);
|
2026-03-01 12:07:15 +01:00
|
|
|
RefreshLatestMailCache();
|
2026-02-10 01:03:03 +01:00
|
|
|
OnPropertyChanged(nameof(EmailCount));
|
2026-03-01 12:07:15 +01:00
|
|
|
NotifyMailItemUpdated(email, MailCopyChangeFlags.All);
|
2025-02-16 11:54:23 +01:00
|
|
|
}
|
2024-04-18 01:44:37 +02:00
|
|
|
|
2025-10-03 15:46:38 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Removes an email from this thread
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void RemoveEmail(MailItemViewModel email)
|
2025-02-16 11:54:23 +01:00
|
|
|
{
|
2025-10-26 23:35:09 +01:00
|
|
|
if (ThreadEmails.Remove(email))
|
2025-10-03 15:46:38 +02:00
|
|
|
{
|
2026-02-10 01:03:03 +01:00
|
|
|
email.PropertyChanged -= ThreadEmailPropertyChanged;
|
2026-02-08 01:41:32 +01:00
|
|
|
_uniqueIdSet.Remove(email.MailCopy.UniqueId);
|
2026-03-01 12:07:15 +01:00
|
|
|
RefreshLatestMailCache();
|
2026-02-10 01:03:03 +01:00
|
|
|
OnPropertyChanged(nameof(EmailCount));
|
2026-03-01 12:07:15 +01:00
|
|
|
NotifyMailItemUpdated(email, MailCopyChangeFlags.All);
|
2025-10-03 15:46:38 +02:00
|
|
|
}
|
2025-02-16 11:54:23 +01:00
|
|
|
}
|
2025-10-18 22:16:28 +02:00
|
|
|
|
2026-02-10 01:03:03 +01:00
|
|
|
public void UnregisterThreadEmailPropertyChangedHandlers()
|
|
|
|
|
{
|
|
|
|
|
foreach (var email in ThreadEmails)
|
|
|
|
|
{
|
|
|
|
|
email.PropertyChanged -= ThreadEmailPropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void ThreadEmailPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
|
{
|
2026-03-01 12:07:15 +01:00
|
|
|
if (_suspendChildPropertyNotificationsCount > 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (sender is not MailItemViewModel updatedMailItem)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (e.PropertyName == nameof(MailItemViewModel.IsSelected) ||
|
|
|
|
|
e.PropertyName == nameof(MailItemViewModel.IsDisplayedInThread) ||
|
|
|
|
|
e.PropertyName == nameof(MailItemViewModel.IsBusy))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e.PropertyName == nameof(MailItemViewModel.ThumbnailUpdatedEvent))
|
|
|
|
|
{
|
|
|
|
|
if (ReferenceEquals(updatedMailItem, latestMailViewModel))
|
|
|
|
|
{
|
|
|
|
|
OnPropertyChanged(nameof(ThumbnailUpdatedEvent));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 01:03:03 +01:00
|
|
|
return;
|
2026-03-01 12:07:15 +01:00
|
|
|
}
|
2026-02-10 01:03:03 +01:00
|
|
|
|
2026-03-01 12:07:15 +01:00
|
|
|
var changedFlags = string.IsNullOrEmpty(e.PropertyName)
|
|
|
|
|
? MailCopyChangeFlags.All
|
|
|
|
|
: MailItemViewModel.GetChangeFlagsForProperty(e.PropertyName);
|
|
|
|
|
|
|
|
|
|
if (changedFlags == MailCopyChangeFlags.None)
|
2026-02-10 01:03:03 +01:00
|
|
|
{
|
2026-03-01 12:07:15 +01:00
|
|
|
NotifyMailItemUpdated(updatedMailItem, MailCopyChangeFlags.All);
|
2026-02-10 01:03:03 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 12:07:15 +01:00
|
|
|
NotifyMailItemUpdated(updatedMailItem, changedFlags);
|
2026-02-10 01:03:03 +01:00
|
|
|
}
|
2026-03-01 12:07:15 +01:00
|
|
|
|
2026-01-27 20:37:18 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Notifies that a mail item within this thread has been updated.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="updatedMailItem">The mail item that was updated (can be null to refresh all).</param>
|
2026-03-01 12:07:15 +01:00
|
|
|
/// <param name="changedFlags">Set of changed child fields.</param>
|
|
|
|
|
public void NotifyMailItemUpdated(MailItemViewModel updatedMailItem, MailCopyChangeFlags changedFlags = MailCopyChangeFlags.All)
|
2026-01-27 20:37:18 +01:00
|
|
|
{
|
2026-03-01 12:07:15 +01:00
|
|
|
if (changedFlags == MailCopyChangeFlags.None)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var previousLatest = latestMailViewModel;
|
|
|
|
|
|
|
|
|
|
if (changedFlags == MailCopyChangeFlags.All ||
|
|
|
|
|
(changedFlags & MailCopyChangeFlags.CreationDate) != 0 ||
|
|
|
|
|
previousLatest == null ||
|
|
|
|
|
!ThreadEmails.Contains(previousLatest))
|
|
|
|
|
{
|
|
|
|
|
RefreshLatestMailCache();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var currentLatest = latestMailViewModel;
|
|
|
|
|
var latestChanged = !ReferenceEquals(previousLatest, currentLatest);
|
|
|
|
|
|
|
|
|
|
var updatesDisplayedLatest = changedFlags == MailCopyChangeFlags.All ||
|
|
|
|
|
updatedMailItem == null ||
|
|
|
|
|
latestChanged ||
|
|
|
|
|
ReferenceEquals(updatedMailItem, previousLatest) ||
|
|
|
|
|
ReferenceEquals(updatedMailItem, currentLatest);
|
|
|
|
|
|
|
|
|
|
var changedProperties = new List<string>(10);
|
|
|
|
|
|
|
|
|
|
void Queue(string propertyName)
|
|
|
|
|
{
|
|
|
|
|
if (!changedProperties.Contains(propertyName))
|
|
|
|
|
{
|
|
|
|
|
changedProperties.Add(propertyName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (updatesDisplayedLatest)
|
|
|
|
|
{
|
|
|
|
|
if (changedFlags == MailCopyChangeFlags.All || latestChanged)
|
|
|
|
|
{
|
|
|
|
|
Queue(nameof(Subject));
|
|
|
|
|
Queue(nameof(FromName));
|
|
|
|
|
Queue(nameof(CreationDate));
|
|
|
|
|
Queue(nameof(FromAddress));
|
|
|
|
|
Queue(nameof(PreviewText));
|
|
|
|
|
Queue(nameof(IsFocused));
|
|
|
|
|
Queue(nameof(DraftId));
|
|
|
|
|
Queue(nameof(Id));
|
|
|
|
|
Queue(nameof(Importance));
|
|
|
|
|
Queue(nameof(ThreadId));
|
|
|
|
|
Queue(nameof(MessageId));
|
|
|
|
|
Queue(nameof(References));
|
|
|
|
|
Queue(nameof(InReplyTo));
|
|
|
|
|
Queue(nameof(FileId));
|
|
|
|
|
Queue(nameof(FolderId));
|
|
|
|
|
Queue(nameof(UniqueId));
|
2026-03-07 11:43:56 +01:00
|
|
|
Queue(nameof(ContactPictureFileId));
|
2026-03-01 12:07:15 +01:00
|
|
|
Queue(nameof(SenderContact));
|
|
|
|
|
Queue(nameof(ThumbnailUpdatedEvent));
|
|
|
|
|
Queue(nameof(SortingDate));
|
|
|
|
|
Queue(nameof(SortingName));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.Subject) != 0)
|
|
|
|
|
Queue(nameof(Subject));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.FromName) != 0)
|
|
|
|
|
{
|
|
|
|
|
Queue(nameof(FromName));
|
|
|
|
|
Queue(nameof(SortingName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.CreationDate) != 0)
|
|
|
|
|
{
|
|
|
|
|
Queue(nameof(CreationDate));
|
|
|
|
|
Queue(nameof(SortingDate));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.FromAddress) != 0)
|
|
|
|
|
Queue(nameof(FromAddress));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.PreviewText) != 0)
|
|
|
|
|
Queue(nameof(PreviewText));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.IsFocused) != 0)
|
|
|
|
|
Queue(nameof(IsFocused));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.DraftId) != 0)
|
|
|
|
|
Queue(nameof(DraftId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.Id) != 0)
|
|
|
|
|
Queue(nameof(Id));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.Importance) != 0)
|
|
|
|
|
Queue(nameof(Importance));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.ThreadId) != 0)
|
|
|
|
|
Queue(nameof(ThreadId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.MessageId) != 0)
|
|
|
|
|
Queue(nameof(MessageId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.References) != 0)
|
|
|
|
|
Queue(nameof(References));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.InReplyTo) != 0)
|
|
|
|
|
Queue(nameof(InReplyTo));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.FileId) != 0)
|
|
|
|
|
Queue(nameof(FileId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.FolderId) != 0)
|
|
|
|
|
Queue(nameof(FolderId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.UniqueId) != 0)
|
|
|
|
|
Queue(nameof(UniqueId));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.SenderContact) != 0)
|
|
|
|
|
{
|
2026-03-07 11:43:56 +01:00
|
|
|
Queue(nameof(ContactPictureFileId));
|
2026-03-01 12:07:15 +01:00
|
|
|
Queue(nameof(SenderContact));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.HasAttachments) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
Queue(nameof(HasAttachments));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.ItemType) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
Queue(nameof(IsCalendarEvent));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.IsFlagged) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
Queue(nameof(IsFlagged));
|
|
|
|
|
|
|
|
|
|
if ((changedFlags & MailCopyChangeFlags.IsRead) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
Queue(nameof(IsRead));
|
|
|
|
|
|
2026-04-11 21:02:51 +02:00
|
|
|
if ((changedFlags & MailCopyChangeFlags.ReadReceiptState) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
{
|
|
|
|
|
Queue(nameof(HasReadReceiptTracking));
|
|
|
|
|
Queue(nameof(IsReadReceiptAcknowledged));
|
|
|
|
|
Queue(nameof(ReadReceiptDisplayText));
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 12:07:15 +01:00
|
|
|
if ((changedFlags & MailCopyChangeFlags.IsDraft) != 0 || changedFlags == MailCopyChangeFlags.All)
|
|
|
|
|
Queue(nameof(IsDraft));
|
|
|
|
|
|
|
|
|
|
foreach (var changedProperty in changedProperties)
|
|
|
|
|
{
|
|
|
|
|
OnPropertyChanged(changedProperty);
|
|
|
|
|
}
|
2026-01-27 20:37:18 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-18 22:16:28 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Checks if this thread contains an email with the specified unique ID
|
|
|
|
|
/// </summary>
|
2026-02-08 01:41:32 +01:00
|
|
|
public bool HasUniqueId(Guid uniqueId) => _uniqueIdSet.Contains(uniqueId);
|
2025-10-25 10:54:38 +02:00
|
|
|
|
|
|
|
|
public IEnumerable<Guid> GetContainingIds() => ThreadEmails.Select(a => a.MailCopy.UniqueId);
|
2025-10-26 14:53:22 +01:00
|
|
|
|
|
|
|
|
public IEnumerable<MailItemViewModel> GetSelectedMailItems()
|
|
|
|
|
{
|
|
|
|
|
if (IsSelected)
|
|
|
|
|
{
|
|
|
|
|
// If the thread itself is selected, return all emails in the thread
|
|
|
|
|
return ThreadEmails;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Otherwise, return only individually selected emails within the thread
|
|
|
|
|
return ThreadEmails.Where(e => e.IsSelected);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-18 01:44:37 +02:00
|
|
|
}
|
2026-02-10 01:03:03 +01:00
|
|
|
|