using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using Wino.Core.Domain; using Wino.Core.Domain.Enums; namespace Wino.Mail.ViewModels.Data; /// /// Thread mail item (multiple IMailItem) view model representation. /// public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListItem { private readonly string _threadId; [ObservableProperty] [NotifyPropertyChangedRecipients] [NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))] public partial bool IsThreadExpanded { get; set; } [ObservableProperty] [NotifyPropertyChangedRecipients] [NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))] public partial bool IsSelected { get; set; } public bool IsSelectedOrExpanded => IsSelected || IsThreadExpanded; /// /// Gets the number of emails in this thread /// public int EmailCount => ThreadEmails.Count; /// /// Gets the latest email's subject for display /// public string Subject => latestMailViewModel?.MailCopy?.Subject; /// /// Gets the latest email's sender name for display /// public string FromName => latestMailViewModel?.MailCopy?.FromName ?? Translator.UnknownSender; /// /// Gets the latest email's creation date for sorting /// public DateTime CreationDate => latestMailViewModel?.MailCopy?.CreationDate ?? DateTime.MinValue; /// /// Gets the latest email's sender address for display /// public string FromAddress => latestMailViewModel?.FromAddress ?? string.Empty; /// /// Gets the preview text from the latest email /// public string PreviewText => latestMailViewModel?.PreviewText ?? string.Empty; /// /// Gets whether any email in this thread has attachments /// public bool HasAttachments => ThreadEmails.Any(e => e.HasAttachments); /// /// Gets whether any email in this thread is flagged /// public bool IsFlagged => ThreadEmails.Any(e => e.IsFlagged); /// /// Gets whether the latest email is focused /// public bool IsFocused => latestMailViewModel?.IsFocused ?? false; /// /// Gets whether all emails in this thread are read /// public bool IsRead => ThreadEmails.All(e => e.IsRead); /// /// Gets whether any email in this thread is a draft /// public bool IsDraft => ThreadEmails.Any(e => e.IsDraft); /// /// Gets the draft ID from the latest email if it's a draft /// public string DraftId => latestMailViewModel?.DraftId ?? string.Empty; /// /// Gets the ID from the latest email /// public string Id => latestMailViewModel?.Id ?? string.Empty; /// /// Gets the importance of the latest email /// public MailImportance Importance => latestMailViewModel?.Importance ?? MailImportance.Normal; /// /// Gets the thread ID from the latest email /// public string ThreadId => latestMailViewModel?.ThreadId ?? _threadId; /// /// Gets the message ID from the latest email /// public string MessageId => latestMailViewModel?.MessageId ?? string.Empty; /// /// Gets the references from the latest email /// public string References => latestMailViewModel?.References ?? string.Empty; /// /// Gets the in-reply-to from the latest email /// public string InReplyTo => latestMailViewModel?.InReplyTo ?? string.Empty; /// /// Gets the file ID from the latest email /// public Guid FileId => latestMailViewModel?.FileId ?? Guid.Empty; /// /// Gets the folder ID from the latest email /// public Guid FolderId => latestMailViewModel?.FolderId ?? Guid.Empty; /// /// Gets the unique ID from the latest email /// public Guid UniqueId => latestMailViewModel?.UniqueId ?? Guid.Empty; public string Base64ContactPicture => latestMailViewModel?.MailCopy?.SenderContact?.Base64ContactPicture ?? string.Empty; public bool ThumbnailUpdatedEvent => latestMailViewModel?.ThumbnailUpdatedEvent ?? false; /// /// Gets all emails in this thread (observable) /// /// [ObservableProperty] [NotifyPropertyChangedFor(nameof(EmailCount))] [NotifyPropertyChangedFor(nameof(Subject))] [NotifyPropertyChangedFor(nameof(FromName))] [NotifyPropertyChangedFor(nameof(CreationDate))] [NotifyPropertyChangedFor(nameof(FromAddress))] [NotifyPropertyChangedFor(nameof(PreviewText))] [NotifyPropertyChangedFor(nameof(HasAttachments))] [NotifyPropertyChangedFor(nameof(IsFlagged))] [NotifyPropertyChangedFor(nameof(IsFocused))] [NotifyPropertyChangedFor(nameof(IsRead))] [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))] [NotifyPropertyChangedFor(nameof(Base64ContactPicture))] public partial ObservableCollection ThreadEmails { get; set; } = []; private MailItemViewModel latestMailViewModel => ThreadEmails.OrderByDescending(e => e.MailCopy?.CreationDate).FirstOrDefault()!; public DateTime SortingDate => CreationDate; public string SortingName => FromName; public ThreadMailItemViewModel(string threadId) { _threadId = threadId; } /// /// Adds an email to this thread /// public void AddEmail(MailItemViewModel email) { if (email.MailCopy.ThreadId != _threadId) throw new ArgumentException($"Email ThreadId '{email.MailCopy.ThreadId}' does not match expander ThreadId '{_threadId}'"); ThreadEmails.Add(email); // Reassign to trigger property change notifications ThreadEmails = ThreadEmails; } /// /// Removes an email from this thread /// public void RemoveEmail(MailItemViewModel email) { if (ThreadEmails.Remove(email)) { // Reassign to trigger property change notifications ThreadEmails = ThreadEmails; } } /// /// Checks if this thread contains an email with the specified unique ID /// public bool HasUniqueId(Guid uniqueId) => ThreadEmails.Any(email => email.MailCopy.UniqueId == uniqueId); public IEnumerable GetContainingIds() => ThreadEmails.Select(a => a.MailCopy.UniqueId); public IEnumerable 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); } } }