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?.SenderContact?.Name ?? 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] public partial ObservableCollection ThreadEmails { get; set; } = []; private MailItemViewModel latestMailViewModel => ThreadEmails.OrderByDescending(e => e.MailCopy?.CreationDate).FirstOrDefault()!; public ThreadMailItemViewModel(string threadId) { _threadId = threadId; } public void NotifyPropertyChanges() { OnPropertyChanged(nameof(Subject)); OnPropertyChanged(nameof(FromName)); OnPropertyChanged(nameof(CreationDate)); OnPropertyChanged(nameof(FromAddress)); OnPropertyChanged(nameof(PreviewText)); OnPropertyChanged(nameof(HasAttachments)); OnPropertyChanged(nameof(IsFlagged)); OnPropertyChanged(nameof(IsFocused)); OnPropertyChanged(nameof(IsRead)); OnPropertyChanged(nameof(IsDraft)); OnPropertyChanged(nameof(DraftId)); OnPropertyChanged(nameof(Id)); OnPropertyChanged(nameof(Importance)); OnPropertyChanged(nameof(ThreadId)); OnPropertyChanged(nameof(MessageId)); OnPropertyChanged(nameof(References)); OnPropertyChanged(nameof(InReplyTo)); OnPropertyChanged(nameof(FileId)); OnPropertyChanged(nameof(FolderId)); OnPropertyChanged(nameof(UniqueId)); OnPropertyChanged(nameof(ThreadEmails)); OnPropertyChanged(nameof(EmailCount)); OnPropertyChanged(nameof(Base64ContactPicture)); } /// /// 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); NotifyPropertyChanges(); } /// /// Removes an email from this thread /// public void RemoveEmail(MailItemViewModel email) { if (ThreadEmails.Remove(email)) { NotifyPropertyChanges(); } } /// /// 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); } } }