Bunch of changes for ItemsView and threads.

This commit is contained in:
Burak Kaan Köse
2025-10-18 11:45:10 +02:00
parent 522a2da114
commit ad135c5e32
18 changed files with 229 additions and 252 deletions
+27 -27
View File
@@ -15,56 +15,56 @@
<PackageVersion Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.2.250402" /> <PackageVersion Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" /> <PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250926-build.2293" /> <PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250926-build.2293" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" /> <PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageVersion Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" Version="0.1.250926-build.2293" /> <PackageVersion Include="CommunityToolkit.Labs.WinUI.DependencyPropertyGenerator" Version="0.1.250926-build.2293" />
<PackageVersion Include="EmailValidation" Version="1.3.0" /> <PackageVersion Include="EmailValidation" Version="1.3.0" />
<PackageVersion Include="gravatar-dotnet" Version="0.1.3" /> <PackageVersion Include="gravatar-dotnet" Version="0.1.3" />
<PackageVersion Include="HtmlAgilityPack" Version="1.12.0" /> <PackageVersion Include="HtmlAgilityPack" Version="1.12.4" />
<PackageVersion Include="Ical.Net" Version="4.3.1" /> <PackageVersion Include="Ical.Net" Version="4.3.1" />
<PackageVersion Include="IsExternalInit" Version="1.0.3" /> <PackageVersion Include="IsExternalInit" Version="1.0.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Graph" Version="5.75.0" /> <PackageVersion Include="Microsoft.Graph" Version="5.94.0" />
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.3.2" /> <PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
<PackageVersion Include="Microsoft.Identity.Client" Version="4.77.1" /> <PackageVersion Include="Microsoft.Identity.Client" Version="4.77.1" />
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.70.1" /> <PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.77.1" />
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.70.1" /> <PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.77.1" />
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" /> <PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" /> <PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
<PackageVersion Include="MimeKit" Version="4.11.0" /> <PackageVersion Include="MimeKit" Version="4.14.0" />
<PackageVersion Include="morelinq" Version="4.4.0" /> <PackageVersion Include="morelinq" Version="4.4.0" />
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" /> <PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
<PackageVersion Include="Nito.AsyncEx.Tasks" Version="5.1.2" /> <PackageVersion Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
<PackageVersion Include="NodaTime" Version="3.2.2" /> <PackageVersion Include="NodaTime" Version="3.2.2" />
<PackageVersion Include="Sentry.Serilog" Version="5.15.1" /> <PackageVersion Include="Sentry.Serilog" Version="5.16.1" />
<PackageVersion Include="Serilog" Version="4.2.0" /> <PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.Exceptions" Version="8.4.0" /> <PackageVersion Include="Serilog.Exceptions" Version="8.4.0" />
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" /> <PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" /> <PackageVersion Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
<PackageVersion Include="SkiaSharp" Version="3.116.1" /> <PackageVersion Include="SkiaSharp" Version="3.119.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="SqlKata" Version="4.0.1" /> <PackageVersion Include="SqlKata" Version="4.0.1" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" /> <PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" /> <PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
<PackageVersion Include="System.Text.Json" Version="9.0.4" /> <PackageVersion Include="System.Text.Json" Version="9.0.10" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="H.NotifyIcon.WinUI" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.WinUI" Version="2.3.1" />
<PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" /> <PackageVersion Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageVersion Include="Google.Apis.Auth" Version="1.69.0" /> <PackageVersion Include="Google.Apis.Auth" Version="1.72.0" />
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3667" /> <PackageVersion Include="Google.Apis.Calendar.v3" Version="1.69.0.3746" />
<PackageVersion Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" /> <PackageVersion Include="Google.Apis.Gmail.v1" Version="1.70.0.3833" />
<PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.68.0.3359" /> <PackageVersion Include="Google.Apis.PeopleService.v1" Version="1.69.0.3785" />
<PackageVersion Include="HtmlKit" Version="1.2.0" /> <PackageVersion Include="HtmlKit" Version="1.2.0" />
<PackageVersion Include="MailKit" Version="4.11.0" /> <PackageVersion Include="MailKit" Version="4.14.1" />
<PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" /> <PackageVersion Include="TimePeriodLibrary.NET" Version="2.1.6" />
<PackageVersion Include="System.Reactive" Version="6.0.1" /> <PackageVersion Include="System.Reactive" Version="6.1.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" /> <PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.4" /> <PackageVersion Include="System.Text.Encodings.Web" Version="9.0.10" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" /> <PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250916003" /> <PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.250930001-experimental1" />
<PackageVersion Include="WinUIEx" Version="2.8.0" /> <PackageVersion Include="WinUIEx" Version="2.9.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -12,5 +12,7 @@ public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders
bool CreateThreads, bool CreateThreads,
bool? IsFocusedOnly, bool? IsFocusedOnly,
string SearchQuery, string SearchQuery,
IEnumerable<Guid> ExistingUniqueIds, HashSet<Guid> ExistingUniqueIds = null,
List<MailCopy> PreFetchMailCopies = null); List<MailCopy> PreFetchMailCopies = null,
int Skip = 0,
int Take = 0);
+1 -2
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly> <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<Platforms>x86;x64;arm64</Platforms> <Platforms>x86;x64;arm64</Platforms>
@@ -57,7 +57,6 @@
<PackageReference Include="MimeKit" /> <PackageReference Include="MimeKit" />
<PackageReference Include="MailKit" /> <PackageReference Include="MailKit" />
<PackageReference Include="sqlite-net-pcl" /> <PackageReference Include="sqlite-net-pcl" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="TimePeriodLibrary.NET" /> <PackageReference Include="TimePeriodLibrary.NET" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+1
View File
@@ -26,6 +26,7 @@ public static class XamlHelpers
#region Converters #region Converters
public static Thickness GetMailItemControlMargin(bool isDisplayedInThread) => isDisplayedInThread ? new Thickness(40, 0, 6, 0) : new Thickness(6, 0, 6, 0);
public static bool IsMultiple(int count) => count > 1; public static bool IsMultiple(int count) => count > 1;
public static bool ReverseIsMultiple(int count) => count < 1; public static bool ReverseIsMultiple(int count) => count < 1;
public static PopupPlacementMode GetPlaccementModeForCalendarType(CalendarDisplayType type) public static PopupPlacementMode GetPlaccementModeForCalendarType(CalendarDisplayType type)
@@ -39,6 +39,7 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
private readonly Dictionary<string, int> _groupHeaderIndexCache = []; private readonly Dictionary<string, int> _groupHeaderIndexCache = [];
private readonly Dictionary<string, List<object>> _groupItems = []; private readonly Dictionary<string, List<object>> _groupItems = [];
private readonly Dictionary<string, ThreadMailItemViewModel> _threadExpanders = []; private readonly Dictionary<string, ThreadMailItemViewModel> _threadExpanders = [];
private readonly HashSet<Guid> _mailCopyIdHashSet = [];
private bool _disposed; private bool _disposed;
private bool _isUpdating; private bool _isUpdating;
@@ -78,6 +79,11 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
/// </summary> /// </summary>
public int TotalUnreadCount => _sourceItems.Count(e => e.MailCopy?.IsRead == false); public int TotalUnreadCount => _sourceItems.Count(e => e.MailCopy?.IsRead == false);
/// <summary>
/// HashSet containing unique IDs of all mail copies in the collection for pagination tracking
/// </summary>
public HashSet<Guid> MailCopyIdHashSet => _mailCopyIdHashSet;
/// <summary> /// <summary>
/// Gets all email items across all groups as a flat collection /// Gets all email items across all groups as a flat collection
/// </summary> /// </summary>
@@ -227,6 +233,9 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
if (email?.MailCopy == null) if (email?.MailCopy == null)
return; return;
// Add to unique ID tracking
_mailCopyIdHashSet.Add(email.MailCopy.UniqueId);
_isUpdating = true; _isUpdating = true;
try try
{ {
@@ -306,6 +315,9 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
if (email?.MailCopy == null) if (email?.MailCopy == null)
return; return;
// Remove from unique ID tracking
_mailCopyIdHashSet.Remove(email.MailCopy.UniqueId);
_isUpdating = true; _isUpdating = true;
try try
{ {
@@ -386,6 +398,12 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
_isUpdating = true; _isUpdating = true;
try try
{ {
// Add to unique ID tracking
foreach (var email in emailList)
{
_mailCopyIdHashSet.Add(email.MailCopy.UniqueId);
}
// For bulk loading, add to source and refresh // For bulk loading, add to source and refresh
foreach (var email in emailList) foreach (var email in emailList)
{ {
@@ -430,6 +448,7 @@ public partial class GroupedEmailCollection : ObservableObject, IRecipient<Prope
_groupHeaderIndexCache.Clear(); _groupHeaderIndexCache.Clear();
_groupItems.Clear(); _groupItems.Clear();
_threadExpanders.Clear(); _threadExpanders.Clear();
_mailCopyIdHashSet.Clear();
OnPropertyChanged(nameof(TotalCount)); OnPropertyChanged(nameof(TotalCount));
OnPropertyChanged(nameof(TotalUnreadCount)); OnPropertyChanged(nameof(TotalUnreadCount));
+3 -4
View File
@@ -13,14 +13,13 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions; using Wino.Core.Domain.Exceptions;
using Wino.Core.Services;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Extensions; using Wino.Core.Extensions;
using Wino.Core.Services;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
namespace Wino.Mail.ViewModels; namespace Wino.Mail.ViewModels;
@@ -411,8 +410,8 @@ public partial class ComposePageViewModel : MailBaseViewModel
// Download missing MIME message using SynchronizationManager // Download missing MIME message using SynchronizationManager
await SynchronizationManager.Instance.DownloadMimeMessageAsync( await SynchronizationManager.Instance.DownloadMimeMessageAsync(
CurrentMailDraftItem.MailCopy, CurrentMailDraftItem.MailCopy,
CurrentMailDraftItem.AssignedAccount.Id); CurrentMailDraftItem.MailCopy.AssignedAccount.Id);
goto retry; goto retry;
} }
@@ -53,16 +53,13 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IDisposable
/// </summary> /// </summary>
public IReadOnlyList<MailItemViewModel> ThreadEmails => _threadEmails.AsReadOnly(); public IReadOnlyList<MailItemViewModel> ThreadEmails => _threadEmails.AsReadOnly();
public MailItemViewModel LatestMailViewModel => _threadEmails.OrderByDescending(e => e.MailCopy?.CreationDate).FirstOrDefault()!;
public ThreadMailItemViewModel(string threadId) public ThreadMailItemViewModel(string threadId)
{ {
_threadId = threadId; _threadId = threadId;
} }
partial void OnIsSelectedChanged(bool value)
{
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (_disposed) if (_disposed)
@@ -86,6 +83,8 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IDisposable
{ {
OnPropertyChanged(nameof(Subject)); OnPropertyChanged(nameof(Subject));
OnPropertyChanged(nameof(FromName)); OnPropertyChanged(nameof(FromName));
OnPropertyChanged(nameof(LatestEmailDate));
OnPropertyChanged(nameof(LatestMailViewModel));
} }
+18 -37
View File
@@ -60,14 +60,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null; private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null;
public GroupedEmailCollection MailCollection { get; set; } = new GroupedEmailCollection(); public GroupedEmailCollection MailCollection { get; set; } = new GroupedEmailCollection();
//public IEnumerable<MailItemViewModel> SelectedItems
//{
// get
// {
// }
//}
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = []; public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = [];
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = []; public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = [];
public ObservableCollection<MailOperationMenuItem> ActionItems { get; set; } = []; public ObservableCollection<MailOperationMenuItem> ActionItems { get; set; } = [];
@@ -246,11 +238,10 @@ public partial class MailListPageViewModel : MailBaseViewModel,
{ {
if (SetProperty(ref _selectedSortingOption, value)) if (SetProperty(ref _selectedSortingOption, value))
{ {
// TODO: Update sorting in mail collection. if (value != null && MailCollection != null)
//if (value != null && MailCollection != null) {
//{ MailCollection.GroupingType = value.Type == SortingOptionType.ReceiveDate ? EmailGroupingType.ByDate : EmailGroupingType.ByFromName;
// MailCollection.SortingType = value.Type; }
//}
} }
} }
} }
@@ -562,24 +553,24 @@ public partial class MailListPageViewModel : MailBaseViewModel,
[RelayCommand] [RelayCommand]
private async Task LoadMoreItemsAsync() private async Task LoadMoreItemsAsync()
{ {
//if (IsInitializingFolder || IsOnlineSearchEnabled) return; if (IsInitializingFolder || IsOnlineSearchEnabled) return;
//await ExecuteUIThread(() => { IsInitializingFolder = true; }); await ExecuteUIThread(() => { IsInitializingFolder = true; });
//var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders, var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders,
// SelectedFilterOption.Type, SelectedFilterOption.Type,
// SelectedSortingOption.Type, SelectedSortingOption.Type,
// PreferencesService.IsThreadingEnabled, PreferencesService.IsThreadingEnabled,
// SelectedFolderPivot.IsFocused, SelectedFolderPivot.IsFocused,
// IsInSearchMode ? SearchQuery : string.Empty, IsInSearchMode ? SearchQuery : string.Empty,
// MailCollection.MailCopyIdHashSet); MailCollection.MailCopyIdHashSet);
//var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false); var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false);
//var viewModels = PrepareMailViewModels(items); var viewModels = PrepareMailViewModels(items);
//await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, clearIdCache: false); }); await ExecuteUIThread(() => { MailCollection.AddEmails(viewModels); });
//await ExecuteUIThread(() => { IsInitializingFolder = false; }); await ExecuteUIThread(() => { IsInitializingFolder = false; });
} }
#endregion #endregion
@@ -589,7 +580,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
public IEnumerable<MailOperationMenuItem> GetAvailableMailActions(IEnumerable<MailItemViewModel> contextMailItems) public IEnumerable<MailOperationMenuItem> GetAvailableMailActions(IEnumerable<MailItemViewModel> contextMailItems)
=> _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems.Select(a => a.MailCopy)); => _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems.Select(a => a.MailCopy));
private bool ShouldPreventItemAdd(MailCopy mailItem) private bool ShouldPreventItemAdd(MailCopy mailItem)
{ {
bool condition = mailItem.IsRead bool condition = mailItem.IsRead
@@ -733,13 +723,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
private IEnumerable<MailItemViewModel> PrepareMailViewModels(IEnumerable<MailCopy> mailItems) private IEnumerable<MailItemViewModel> PrepareMailViewModels(IEnumerable<MailCopy> mailItems)
{ {
return mailItems.Select(a => new MailItemViewModel(a)); return mailItems.Select(a => new MailItemViewModel(a));
//foreach (var item in mailItems)
//{
// if (item is MailCopy singleMailItem)
// yield return new MailItemViewModel(singleMailItem);
// else if (item is ThreadMailItem threadMailItem)
// yield return new ThreadMailItemViewModel(threadMailItem);
//}
} }
[RelayCommand] [RelayCommand]
@@ -784,7 +767,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
// Here items are sorted and filtered. // Here items are sorted and filtered.
List<MailCopy> items = null; List<MailCopy> items = null;
List<MailCopy> onlineSearchItems = null;
bool isDoingSearch = !string.IsNullOrEmpty(SearchQuery); bool isDoingSearch = !string.IsNullOrEmpty(SearchQuery);
bool isDoingOnlineSearch = false; bool isDoingOnlineSearch = false;
@@ -851,8 +833,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
PreferencesService.IsThreadingEnabled, PreferencesService.IsThreadingEnabled,
SelectedFolderPivot.IsFocused, SelectedFolderPivot.IsFocused,
SearchQuery, SearchQuery,
default, MailCollection.MailCopyIdHashSet);
onlineSearchItems);
items = await _mailService.FetchMailsAsync(initializationOptions, cancellationToken).ConfigureAwait(false); items = await _mailService.FetchMailsAsync(initializationOptions, cancellationToken).ConfigureAwait(false);
@@ -25,7 +25,6 @@ using Wino.Core.Services;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages; using Wino.Mail.ViewModels.Messages;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Messaging.Server;
using Wino.Messaging.UI; using Wino.Messaging.UI;
using IMailService = Wino.Core.Domain.Interfaces.IMailService; using IMailService = Wino.Core.Domain.Interfaces.IMailService;
@@ -355,8 +354,8 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel,
// Download missing MIME message using SynchronizationManager // Download missing MIME message using SynchronizationManager
await SynchronizationManager.Instance.DownloadMimeMessageAsync( await SynchronizationManager.Instance.DownloadMimeMessageAsync(
mailItemViewModel.MailCopy, mailItemViewModel.MailCopy,
mailItemViewModel.AssignedAccount.Id); mailItemViewModel.MailCopy.AssignedAccount.Id);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
-2
View File
@@ -3,7 +3,6 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Views.Abstract" xmlns:abstract="using:Wino.Views.Abstract"
xmlns:advanced="using:Wino.Controls.Advanced"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals" xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:animations="using:CommunityToolkit.WinUI.Animations" xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:controls="using:Wino.Controls" xmlns:controls="using:Wino.Controls"
@@ -356,7 +355,6 @@
<Grid <Grid
x:Name="RootGrid" x:Name="RootGrid"
Padding="0" Padding="0"
Background="{ThemeResource WinoApplicationBackgroundColor}"
ColumnSpacing="0" ColumnSpacing="0"
RowSpacing="0"> RowSpacing="0">
@@ -1,14 +1,46 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows.Input;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
namespace Wino.Mail.WinUI.Controls.Advanced; namespace Wino.Mail.WinUI.Controls.Advanced;
public partial class WinoItemsView : ItemsView public partial class WinoItemsView : ItemsView
{ {
private const string PART_ScrollView = nameof(PART_ScrollView);
private ScrollView? _internalScrollView;
[GeneratedDependencyProperty]
public partial ICommand LoadMoreCommand { get; set; }
public IEnumerable<object>? CastedItemsSource => ItemsSource as IEnumerable<object>; public IEnumerable<object>? CastedItemsSource => ItemsSource as IEnumerable<object>;
public WinoItemsView() public WinoItemsView()
{ {
DefaultStyleKey = typeof(ItemsView); DefaultStyleKey = typeof(ItemsView);
} }
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_internalScrollView = GetTemplateChild("PART_ScrollView") as ScrollView ?? throw new System.Exception("Can't find the ScrollView in WinoItemsView.");
_internalScrollView.ViewChanged -= InternalScrollViewPositionChanged;
_internalScrollView.ViewChanged += InternalScrollViewPositionChanged;
}
private void InternalScrollViewPositionChanged(ScrollView sender, object args)
{
if (_internalScrollView == null) return;
// No need to raise init request if there are no items in the list.
if (ItemsSource == null) return;
double progress = sender.VerticalOffset / sender.ScrollableHeight;
// Trigger when scrolled past 90% of total height
if (progress >= 0.9) LoadMoreCommand?.Execute(null);
}
} }
@@ -12,7 +12,6 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models; using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Reader; using Wino.Core.Domain.Models.Reader;
using Wino.Core.WinUI;
using Wino.Core.WinUI.Extensions; using Wino.Core.WinUI.Extensions;
using Wino.Mail.WinUI; using Wino.Mail.WinUI;
@@ -140,7 +139,7 @@ public sealed partial class WebViewEditorControl : Control, IDisposable
{ {
this.DefaultStyleKey = typeof(WebViewEditorControl); this.DefaultStyleKey = typeof(WebViewEditorControl);
IsEditorDarkMode = WinoApplication.Current.UnderlyingThemeService.IsUnderlyingThemeDark(); IsEditorDarkMode = Core.WinUI.WinoApplication.Current.UnderlyingThemeService.IsUnderlyingThemeDark();
} }
protected override async void OnApplyTemplate() protected override async void OnApplyTemplate()
+2 -2
View File
@@ -12,7 +12,7 @@
<!-- SystemBackdrop will be set by NewThemeService --> <!-- SystemBackdrop will be set by NewThemeService -->
<Grid> <Grid Background="{ThemeResource WinoApplicationBackgroundColor}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -25,10 +25,10 @@
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
BackRequested="BackButtonClicked" BackRequested="BackButtonClicked"
Background="Transparent"
IsBackButtonVisible="{x:Bind StatePersistanceService.IsBackButtonVisible, Mode=OneWay}" IsBackButtonVisible="{x:Bind StatePersistanceService.IsBackButtonVisible, Mode=OneWay}"
IsPaneToggleButtonVisible="True" IsPaneToggleButtonVisible="True"
PaneToggleRequested="PaneButtonClicked" /> PaneToggleRequested="PaneButtonClicked" />
<Frame <Frame
x:Name="MainShellFrame" x:Name="MainShellFrame"
Grid.Row="1" Grid.Row="1"
+48 -120
View File
@@ -67,104 +67,34 @@
<animations:OpacityAnimation To="0.0" Duration="0:0:1" /> <animations:OpacityAnimation To="0.0" Duration="0:0:1" />
</animations:Implicit.HideAnimations> </animations:Implicit.HideAnimations>
<Grid Height="80"> <controls:MailItemDisplayInformationControl
<!-- Thread background --> Margin="{x:Bind helpers:XamlHelpers.GetMailItemControlMargin(IsDisplayedInThread), Mode=OneWay}"
<Grid x:DefaultBindMode="OneWay"
HorizontalAlignment="Stretch" CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
VerticalAlignment="Stretch" ContextRequested="MailItemContextRequested"
Background="{ThemeResource CardStrokeColorDefaultBrush}" DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
Visibility="{x:Bind IsDisplayedInThread, Mode=OneWay}" /> HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
<Grid Padding="8,4,12,4"> IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
<Grid.ColumnDefinitions> IsThumbnailUpdated="{x:Bind ThumbnailUpdatedEvent, Mode=OneWay}"
<ColumnDefinition Width="Auto" /> LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
<ColumnDefinition Width="*" /> MailItem="{x:Bind MailCopy, Mode=OneWay}"
<ColumnDefinition Width="Auto" /> Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
</Grid.ColumnDefinitions> RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
<!-- Left: PersonPicture -->
<PersonPicture
Grid.Column="0"
Width="40"
Height="40"
Margin="0,0,12,0"
VerticalAlignment="Center"
DisplayName="{x:Bind FromName, Mode=OneWay}" />
<!-- Center: Content -->
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
Spacing="2">
<!-- Subject -->
<TextBlock
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByReadState(IsRead), Mode=OneWay}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
MaxLines="1"
Text="{x:Bind Subject, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<!-- Sender Name -->
<TextBlock
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByReadState(IsRead), Mode=OneWay}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
MaxLines="1"
Text="{x:Bind FromName, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<!-- Preview Text -->
<TextBlock
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
MaxLines="1"
Text="{x:Bind PreviewText, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
<!-- Right: Indicators -->
<StackPanel
Grid.Column="2"
Margin="12,0,0,0"
VerticalAlignment="Center"
Spacing="4">
<!-- Unread Indicator (show when IsRead is false) -->
<Ellipse
Width="8"
Height="8"
Fill="{ThemeResource AccentTextFillColorPrimaryBrush}"
Visibility="{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(IsRead), Mode=OneWay}" />
<!-- Flagged Indicator -->
<FontIcon
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Glyph="&#xE129;"
Visibility="{x:Bind IsFlagged, Mode=OneWay}" />
<!-- Attachment Indicator -->
<FontIcon
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE723;"
Visibility="{x:Bind HasAttachments, Mode=OneWay}" />
</StackPanel>
</Grid>
</Grid>
</ItemContainer> </ItemContainer>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="ThreadExpanderTemplate" x:DataType="viewModelData:ThreadMailItemViewModel"> <DataTemplate x:Key="ThreadExpanderTemplate" x:DataType="viewModelData:ThreadMailItemViewModel">
<ItemContainer Tag="{x:Bind}" Tapped="ThreadContainerTapped"> <ItemContainer
<Grid CanUserSelect="UserCannotSelect"
Padding="4,12,0,12" RightTapped="ThreadContainerRightTapped"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}" Tag="{x:Bind}"
ColumnSpacing="8"> Tapped="ThreadContainerTapped">
<Grid Padding="4,0,0,0" ColumnSpacing="8">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Expansion indicator --> <!-- Expansion indicator -->
@@ -177,42 +107,34 @@
Glyph="&#xE76C;" /> Glyph="&#xE76C;" />
</Viewbox> </Viewbox>
<!-- Thread content --> <controls:MailItemDisplayInformationControl
<StackPanel Grid.Column="1" VerticalAlignment="Center"> Grid.Column="1"
<TextBlock x:DefaultBindMode="OneWay"
FontWeight="SemiBold" CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}" ContextRequested="MailItemContextRequested"
Text="{x:Bind Subject, Mode=OneWay}" DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
TextTrimming="CharacterEllipsis" /> HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
<TextBlock IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
Style="{ThemeResource CaptionTextBlockStyle}" IsThumbnailUpdated="{x:Bind LatestMailViewModel.ThumbnailUpdatedEvent, Mode=OneWay}"
Text="{x:Bind LatestEmailDate, Mode=OneWay}" LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
TextTrimming="CharacterEllipsis" /> MailItem="{x:Bind LatestMailViewModel.MailCopy, Mode=OneWay}"
</StackPanel> Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
<!-- Email count badge --> ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
<Border
Grid.Column="2"
Margin="8,0,12,0"
Padding="6,2"
VerticalAlignment="Center"
Background="{ThemeResource AccentTextFillColorPrimaryBrush}"
CornerRadius="8">
<TextBlock
Foreground="White"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind EmailCount, Mode=OneWay}" />
</Border>
</Grid> </Grid>
</ItemContainer> </ItemContainer>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="DateGroupHeaderTemplate" x:DataType="data:DateGroupHeader"> <DataTemplate x:Key="DateGroupHeaderTemplate" x:DataType="data:DateGroupHeader">
<ItemContainer IsHitTestVisible="False"> <ItemContainer
CanUserSelect="UserCannotSelect"
IsEnabled="False"
IsHitTestVisible="False">
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"> <Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<TextBlock <TextBlock
FontSize="14" FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextFillColorPrimaryBrush}" Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Style="{ThemeResource CaptionTextBlockStyle}" Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind DisplayName, Mode=OneWay}" /> Text="{x:Bind DisplayName, Mode=OneWay}" />
@@ -221,7 +143,10 @@
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="SenderGroupHeaderTemplate" x:DataType="data:SenderGroupHeader"> <DataTemplate x:Key="SenderGroupHeaderTemplate" x:DataType="data:SenderGroupHeader">
<ItemContainer IsHitTestVisible="False"> <ItemContainer
CanUserSelect="UserCannotSelect"
IsEnabled="False"
IsHitTestVisible="False">
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"> <Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -503,15 +428,18 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<advanced:WinoItemsView <advanced:WinoItemsView
x:Name="MailListView" x:Name="MailListView"
Margin="4" Margin="4"
ItemTemplate="{StaticResource MailItemContainerSelector}" ItemTemplate="{StaticResource MailItemContainerSelector}"
ItemsSource="{x:Bind ViewModel.MailCollection.Items, Mode=OneTime}" ItemsSource="{x:Bind ViewModel.MailCollection.Items, Mode=OneTime}"
Layout="{StaticResource DefaultItemsViewLayout}" Layout="{StaticResource DefaultItemsViewLayout}"
LoadMoreCommand="{x:Bind ViewModel.LoadMoreItemsCommand}"
SelectionChanged="ListSelectionChanged" SelectionChanged="ListSelectionChanged"
SelectionMode="Extended" /> SelectionMode="Extended" />
<!-- Try online search panel. --> <!-- Try online search panel. -->
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}"> <Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}">
<Button <Button
+43 -36
View File
@@ -13,6 +13,7 @@ using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
using MoreLinq; using MoreLinq;
using Windows.Foundation; using Windows.Foundation;
using Wino.Controls;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
@@ -74,7 +75,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
// Dispose all WinoListView items. // Dispose all WinoListView items.
MailListView.Dispose(); // MailListView.Dispose();
this.Bindings.StopTracking(); this.Bindings.StopTracking();
@@ -162,31 +163,24 @@ public sealed partial class MailListPage : MailListPageAbstract,
// Context is requested from a single mail point, but we might have multiple selected items. // Context is requested from a single mail point, but we might have multiple selected items.
// This menu should be calculated based on all selected items by providers. // This menu should be calculated based on all selected items by providers.
//if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p)) if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
//{ {
// await FocusManager.TryFocusAsync(control, FocusState.Keyboard); if (control.DataContext is MailItemViewModel clickedMailItemContext)
{
var targetItems = ViewModel.MailCollection.AllItems.Where(a => a.IsSelected);
var availableActions = ViewModel.GetAvailableMailActions(targetItems);
// if (control.DataContext is IMailItem clickedMailItemContext) if (!availableActions?.Any() ?? false) return;
// {
// var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
// var availableActions = ViewModel.GetAvailableMailActions(targetItems);
// if (!availableActions?.Any() ?? false) return; var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
// var t = targetItems.ElementAt(0);
// ViewModel.ChangeCustomFocusedState(targetItems, true); if (clickedOperation == null) return;
// var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y); var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
// ViewModel.ChangeCustomFocusedState(targetItems, false); await ViewModel.ExecuteMailOperationAsync(prepRequest);
}
// if (clickedOperation == null) return; }
// var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
// await ViewModel.ExecuteMailOperationAsync(prepRequest);
// }
//}
} }
private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions, private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions,
@@ -507,22 +501,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
=> ViewModel.ExecuteMailOperationCommand.Execute(MailOperation.SoftDelete); => ViewModel.ExecuteMailOperationCommand.Execute(MailOperation.SoftDelete);
private void ThreadContainerTapped(object sender, TappedRoutedEventArgs e)
{
if (sender is ItemContainer container && container.Tag is ThreadMailItemViewModel expander)
{
// Toggle expansion state
expander.IsThreadExpanded = !expander.IsThreadExpanded;
// Find the expander icon and animate its rotation using Composition APIs
var expanderIcon = WinoVisualTreeHelper.GetChildObject<FontIcon>(container, "ExpanderIcon");
if (expanderIcon != null)
{
var targetAngle = expander.IsThreadExpanded ? 90f : 0f;
AnimateRotationWithComposition(expanderIcon, targetAngle);
}
}
}
/// <summary> /// <summary>
/// Animates the rotation using high-performance Composition APIs /// Animates the rotation using high-performance Composition APIs
@@ -561,4 +540,32 @@ public sealed partial class MailListPage : MailListPageAbstract,
UpdateSelectAllButtonStatus(); UpdateSelectAllButtonStatus();
UpdateAdaptiveness(); UpdateAdaptiveness();
} }
private void ThreadContainerRightTapped(object sender, RightTappedRoutedEventArgs e)
{
if (sender is ItemContainer container && container.Tag is ThreadMailItemViewModel expander)
{
expander.IsThreadExpanded = !expander.IsThreadExpanded;
// Select all.
ViewModel.MailCollection.AllItems.Where(a => expander.ThreadEmails.Contains(a)).ForEach(a => a.IsSelected = true);
}
}
private void ThreadContainerTapped(object sender, TappedRoutedEventArgs e)
{
if (sender is ItemContainer container && container.Tag is ThreadMailItemViewModel expander)
{
// Toggle expansion state
expander.IsThreadExpanded = !expander.IsThreadExpanded;
// Find the expander icon and animate its rotation using Composition APIs
var expanderIcon = WinoVisualTreeHelper.GetChildObject<FontIcon>(container, "ExpanderIcon");
if (expanderIcon != null)
{
var targetAngle = expander.IsThreadExpanded ? 90f : 0f;
AnimateRotationWithComposition(expanderIcon, targetAngle);
}
}
}
} }
@@ -108,6 +108,7 @@
<TextBlock <TextBlock
x:Name="ElementThemeSelectionDisabledTextBlock" x:Name="ElementThemeSelectionDisabledTextBlock"
Margin="4,0,0,0"
x:Load="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay, Converter={StaticResource ReverseBooleanConverter}}" x:Load="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay, Converter={StaticResource ReverseBooleanConverter}}"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
Text="{x:Bind domain:Translator.SettingsElementThemeSelectionDisabled}" /> Text="{x:Bind domain:Translator.SettingsElementThemeSelectionDisabled}" />
@@ -149,7 +150,10 @@
</controls:SettingsExpander> </controls:SettingsExpander>
<!-- Backdrop Selection --> <!-- Backdrop Selection -->
<controls:SettingsCard Description="Choose the backdrop effect for your app window" Header="Window Backdrop"> <controls:SettingsCard
Description="Choose the backdrop effect for your app window"
Header="Window Backdrop"
IsEnabled="{x:Bind ViewModel.CanSelectElementTheme, Mode=OneWay}">
<controls:SettingsCard.HeaderIcon> <controls:SettingsCard.HeaderIcon>
<PathIcon Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" /> <PathIcon Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
</controls:SettingsCard.HeaderIcon> </controls:SettingsCard.HeaderIcon>
+14 -9
View File
@@ -28,6 +28,7 @@ public class MailService : BaseDatabaseService, IMailService
private readonly IContactService _contactService; private readonly IContactService _contactService;
private readonly IAccountService _accountService; private readonly IAccountService _accountService;
private readonly ISignatureService _signatureService; private readonly ISignatureService _signatureService;
private readonly IMimeFileService _mimeFileService; private readonly IMimeFileService _mimeFileService;
private readonly IPreferencesService _preferencesService; private readonly IPreferencesService _preferencesService;
@@ -192,15 +193,23 @@ public class MailService : BaseDatabaseService, IMailService
.OrWhereContains("MailCopy.FromName", options.SearchQuery) .OrWhereContains("MailCopy.FromName", options.SearchQuery)
.OrWhereContains("MailCopy.FromAddress", options.SearchQuery)); .OrWhereContains("MailCopy.FromAddress", options.SearchQuery));
// Support pagination by excluding already fetched items
if (options.ExistingUniqueIds?.Any() ?? false) if (options.ExistingUniqueIds?.Any() ?? false)
{ {
query.WhereNotIn("MailCopy.UniqueId", options.ExistingUniqueIds); query.WhereNotIn("MailCopy.UniqueId", options.ExistingUniqueIds);
} }
//if (options.Skip > 0) // Support skip for pagination
//{ if (options.Skip > 0)
// query.Skip(options.Skip); {
//} query.Skip(options.Skip);
}
// Support custom take count for pagination
if (options.Take > 0)
{
query.Take(options.Take);
}
return query.GetRawQuery(); return query.GetRawQuery();
} }
@@ -218,7 +227,6 @@ public class MailService : BaseDatabaseService, IMailService
{ {
// If not just do the query. // If not just do the query.
var query = BuildMailFetchQuery(options); var query = BuildMailFetchQuery(options);
mails = await Connection.QueryAsync<MailCopy>(query); mails = await Connection.QueryAsync<MailCopy>(query);
} }
@@ -239,9 +247,6 @@ public class MailService : BaseDatabaseService, IMailService
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// Threading is disabled. Just return everything as it is.
// mails.Sort(options.SortingOptionType == SortingOptionType.ReceiveDate ? new DateComparer() : new NameComparer());
return [.. mails]; return [.. mails];
} }
@@ -1005,7 +1010,7 @@ public class MailService : BaseDatabaseService, IMailService
public async Task<List<MailCopy>> GetExistingMailsAsync(Guid folderId, IEnumerable<MailKit.UniqueId> uniqueIds) public async Task<List<MailCopy>> GetExistingMailsAsync(Guid folderId, IEnumerable<MailKit.UniqueId> uniqueIds)
{ {
var localMailIds = uniqueIds.Where(a => a != null).Select(a => MailkitClientExtensions.CreateUid(folderId, a.Id)).ToArray(); var localMailIds = uniqueIds.Select(a => MailkitClientExtensions.CreateUid(folderId, a.Id)).ToArray();
var query = new Query(nameof(MailCopy)) var query = new Query(nameof(MailCopy))
.WhereIn("Id", localMailIds) .WhereIn("Id", localMailIds)
+5
View File
@@ -14,9 +14,14 @@
<!-- key value for <packageSource> should match key values from <packageSources> element --> <!-- key value for <packageSource> should match key values from <packageSources> element -->
<packageSource key="nuget"> <packageSource key="nuget">
<package pattern="*" /> <package pattern="*" />
<package pattern="CommunityToolkit.Common" />
<package pattern="Microsoft.*" />
<package pattern="Newtonsoft.Json" />
<package pattern="CommunityToolkit.WinUI.Extensions" />
</packageSource> </packageSource>
<packageSource key="labsFeed"> <packageSource key="labsFeed">
<package pattern="CommunityToolkit.Labs.*" /> <package pattern="CommunityToolkit.Labs.*" />
<package pattern="CommunityToolkit.WinUI.Extensions" />
</packageSource> </packageSource>
</packageSourceMapping> </packageSourceMapping>
</configuration> </configuration>