This commit is contained in:
Burak Kaan Köse
2025-10-03 15:46:38 +02:00
parent 2bec513d2c
commit 7b41f558d4
62 changed files with 1811 additions and 2514 deletions
+216 -221
View File
@@ -3,23 +3,26 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Views.Abstract"
xmlns:advanced="using:Wino.Mail.WinUI.Controls.Advanced"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:collections="using:CommunityToolkit.Mvvm.Collections"
xmlns:controls="using:Wino.Controls"
xmlns:converters="using:Wino.Converters"
xmlns:coreControls="using:Wino.Core.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Wino.Mail.ViewModels.Collections"
xmlns:domain="using:Wino.Core.Domain"
xmlns:enums="using:Wino.Core.Domain.Enums"
xmlns:helpers="using:Wino.Helpers"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:listview="using:Wino.Controls.Advanced"
xmlns:local="using:Wino.Behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:menuflyouts="using:Wino.MenuFlyouts"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:selectors="using:Wino.Selectors"
xmlns:selectors1="using:Wino.Mail.WinUI.Selectors"
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
xmlns:toolkitExt="using:CommunityToolkit.WinUI"
xmlns:viewModelData="using:Wino.Mail.ViewModels.Data"
@@ -30,17 +33,11 @@
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="MailCollectionViewSource"
IsSourceGrouped="True"
Source="{x:Bind ViewModel.MailCollection.MailItems, Mode=OneWay}" />
<Thickness x:Key="ExpanderHeaderPadding">0,0,0,0</Thickness>
<Thickness x:Key="ExpanderChevronMargin">0,0,12,0</Thickness>
<Thickness x:Key="ExpanderHeaderBorderThickness">0,0,0,0</Thickness>
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="CardBackgroundFillColorSecondaryBrush">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="ExpanderContentBorderBrush">Transparent</SolidColorBrush>
<StaticResource x:Key="ExpanderContentBackground" ResourceKey="CardBackgroundFillColorSecondaryBrush" />
@@ -49,155 +46,210 @@
<SolidColorBrush x:Key="SystemControlRevealFocusVisualBrush" Color="#FF4C4A48" />
<SolidColorBrush x:Key="SystemControlFocusVisualSecondaryBrush" Color="#FFFFFFFF" />
<!-- Header Templates -->
<DataTemplate x:Key="MailGroupHeaderDefaultTemplate" x:DataType="collections:IReadOnlyObservableGroup">
<Grid
Margin="4,2"
AllowFocusOnInteraction="False"
Background="{ThemeResource MailListHeaderBackgroundColor}"
CornerRadius="6">
<TextBlock
Padding="12"
VerticalAlignment="Center"
AllowFocusOnInteraction="False"
FontSize="13"
FontWeight="SemiBold"
Text="{x:Bind helpers:XamlHelpers.GetMailGroupDateString(Key)}" />
</Grid>
</DataTemplate>
<!-- Swipe Items -->
<!--
TODO: Temporarily disabled. WinUI2 - SwipeControl crash.
https://github.com/microsoft/microsoft-ui-xaml/issues/2527
-->
<!--<SwipeItems x:Key="LeftSwipeItems" Mode="Execute">
<SwipeItem BehaviorOnInvoked="Close"
Invoked="LeftSwipeItemInvoked"
Text="{x:Bind domain:Translator.MailOperation_Delete}">
<SwipeItem.IconSource>
<coreControls:WinoFontIconSource Icon="Delete" />
</SwipeItem.IconSource>
<SwipeItem.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Offset="0.0" Color="#FFF5A6A6" />
<GradientStop Offset="0.5" Color="#FFEF4444" />
<GradientStop Offset="1.0" Color="#FFF32525" />
</LinearGradientBrush>
</SwipeItem.Background>
</SwipeItem>
</SwipeItems>
-->
<!--
<SwipeItems x:Key="RightSwipeItems" Mode="Execute">
<SwipeItem BehaviorOnInvoked="Close"
Invoked="RightSwipeItemInvoked"
Text="{x:Bind domain:Translator.MarkReadUnread}">
<SwipeItem.IconSource>
<coreControls:WinoFontIconSource Icon="MarkRead" />
</SwipeItem.IconSource>
</SwipeItem>
</SwipeItems>-->
<!-- Single Mail Item Template -->
<DataTemplate x:Key="SingleMailItemTemplate" x:DataType="viewModelData:MailItemViewModel">
<controls:MailItemDisplayInformationControl
x:DefaultBindMode="OneWay"
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
ContextRequested="MailItemContextRequested"
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
IsThumbnailUpdated="{x:Bind ThumbnailUpdatedEvent, Mode=OneWay}"
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
MailItem="{x:Bind MailCopy, Mode=OneWay}"
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
</DataTemplate>
<!-- Single Mail Item Template for Threads -->
<DataTemplate x:Key="ThreadSingleMailItemTemplate" x:DataType="viewModelData:MailItemViewModel">
<controls:MailItemDisplayInformationControl
x:DefaultBindMode="OneWay"
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
ContextRequested="MailItemContextRequested"
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
FocusVisualMargin="8"
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
FocusVisualPrimaryThickness="2"
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
FocusVisualSecondaryThickness="1"
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
IsCustomFocused="{x:Bind IsCustomFocused, Mode=OneWay}"
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
IsThumbnailUpdated="{x:Bind ThumbnailUpdatedEvent, Mode=OneWay}"
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
MailItem="{x:Bind MailCopy, Mode=OneWay}"
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
</DataTemplate>
<!-- Mail Item Content Selector -->
<selectors:MailItemDisplaySelector x:Key="MailItemDisplaySelector" SingleMailItemTemplate="{StaticResource SingleMailItemTemplate}">
<selectors:MailItemDisplaySelector.ThreadMailItemTemplate>
<DataTemplate x:DataType="viewModelData:ThreadMailItemViewModel">
<controls:WinoExpander
x:Name="ThreadExpander"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
<controls:WinoExpander.Header>
<controls:MailItemDisplayInformationControl
x:DefaultBindMode="OneWay"
Background="Transparent"
BorderThickness="0"
CenterHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.CenterHoverAction, Mode=OneWay}"
ConnectedExpander="{Binding ElementName=ThreadExpander}"
ContextRequested="MailItemContextRequested"
DisplayMode="{Binding ElementName=root, Path=ViewModel.PreferencesService.MailItemDisplayMode, Mode=OneWay}"
DragStarting="ThreadHeaderDragStart"
DropCompleted="ThreadHeaderDragFinished"
HoverActionExecutedCommand="{Binding ElementName=root, Path=ViewModel.ExecuteHoverActionCommand}"
IsAvatarVisible="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowSenderPicturesEnabled, Mode=OneWay}"
IsHitTestVisible="True"
IsHoverActionsEnabled="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsHoverActionsEnabled, Mode=OneWay}"
IsThreadExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}"
IsThreadExpanderVisible="True"
LeftHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.LeftHoverAction, Mode=OneWay}"
MailItem="{x:Bind MailItem, Mode=OneWay}"
Prefer24HourTimeFormat="{Binding ElementName=root, Path=ViewModel.PreferencesService.Prefer24HourTimeFormat, Mode=OneWay}"
RightHoverAction="{Binding ElementName=root, Path=ViewModel.PreferencesService.RightHoverAction, Mode=OneWay}"
ShowPreviewText="{Binding ElementName=root, Path=ViewModel.PreferencesService.IsShowPreviewEnabled, Mode=OneWay}" />
</controls:WinoExpander.Header>
<controls:WinoExpander.Content>
<listview:WinoListView
x:Name="ThreadItemsList"
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
IsThreadListView="True"
ItemTemplate="{StaticResource ThreadSingleMailItemTemplate}"
ItemsSource="{x:Bind ThreadItems}"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemContainerTransitions>
<TransitionCollection>
<EdgeUIThemeTransition />
</TransitionCollection>
</ListView.ItemContainerTransitions>
</listview:WinoListView>
</controls:WinoExpander.Content>
</controls:WinoExpander>
</DataTemplate>
</selectors:MailItemDisplaySelector.ThreadMailItemTemplate>
</selectors:MailItemDisplaySelector>
<SolidColorBrush x:Key="ButtonBackgroundDisabled">Transparent</SolidColorBrush>
<StackLayout
x:Key="DefaultItemsViewLayout"
Orientation="Vertical"
Spacing="6" />
<!-- Templates -->
<DataTemplate x:Key="EmailTemplate" x:DataType="viewModelData:MailItemViewModel">
<ItemContainer
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsSelected="{x:Bind IsSelected, Mode=TwoWay}">
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation To="1.0" Duration="0:0:1" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:OpacityAnimation To="0.0" Duration="0:0:1" />
</animations:Implicit.HideAnimations>
<Grid Height="80">
<!-- Thread background -->
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource CardStrokeColorDefaultBrush}"
Visibility="{x:Bind IsDisplayedInThread, Mode=OneWay}" />
<Grid Padding="8,4,12,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- 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>
</DataTemplate>
<DataTemplate x:Key="ThreadExpanderTemplate" x:DataType="viewModelData:ThreadMailItemViewModel">
<ItemContainer Tag="{x:Bind}" Tapped="ThreadContainerTapped">
<Grid
Padding="4,12,0,12"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Expansion indicator -->
<Viewbox Width="12" Height="12">
<FontIcon
x:Name="ExpanderIcon"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE76C;" />
</Viewbox>
<!-- Thread content -->
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock
FontWeight="SemiBold"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Text="{x:Bind Subject, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind LatestEmailDate, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
<!-- Email count badge -->
<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>
</ItemContainer>
</DataTemplate>
<DataTemplate x:Key="DateGroupHeaderTemplate" x:DataType="data:DateGroupHeader">
<ItemContainer IsHitTestVisible="False">
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<TextBlock
FontSize="14"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind DisplayName, Mode=OneWay}" />
</Grid>
</ItemContainer>
</DataTemplate>
<DataTemplate x:Key="SenderGroupHeaderTemplate" x:DataType="data:SenderGroupHeader">
<ItemContainer IsHitTestVisible="False">
<Grid Padding="12,8" Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Style="{ThemeResource SubtitleTextBlockStyle}"
Text="{x:Bind DisplayName, Mode=OneWay}" />
<StackPanel
Grid.Column="1"
Orientation="Horizontal"
Spacing="8">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="{x:Bind ItemCount, Mode=OneWay}" />
</StackPanel>
</Grid>
</ItemContainer>
</DataTemplate>
<selectors1:MailItemContainerSelector
x:Name="MailItemContainerSelector"
DateGroupHeaderTemplate="{StaticResource DateGroupHeaderTemplate}"
EmailTemplate="{StaticResource EmailTemplate}"
SenderGroupHeaderTemplate="{StaticResource SenderGroupHeaderTemplate}"
ThreadExpanderTemplate="{StaticResource ThreadExpanderTemplate}" />
</Page.Resources>
<Page.KeyboardAccelerators>
@@ -299,8 +351,7 @@
VerticalAlignment="Center"
Canvas.ZIndex="100"
Checked="SelectAllCheckboxChecked"
Unchecked="SelectAllCheckboxUnchecked"
Visibility="{x:Bind helpers:XamlHelpers.IsSelectionModeMultiple(MailListView.SelectionMode), Mode=OneWay}" />
Unchecked="SelectAllCheckboxUnchecked" />
<!-- Folders -->
@@ -448,70 +499,14 @@
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<RefreshContainer RefreshRequested="PullToRefreshRequested" Visibility="{x:Bind ViewModel.IsEmpty, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}">
<SemanticZoom x:Name="SemanticZoomContainer" CanChangeViews="{x:Bind ViewModel.PreferencesService.IsSemanticZoomEnabled, Mode=OneWay}">
<SemanticZoom.ZoomedInView>
<listview:WinoListView
x:Name="MailListView"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
ItemDeletedCommand="{x:Bind ViewModel.ExecuteMailOperationCommand}"
ItemTemplateSelector="{StaticResource MailItemDisplaySelector}"
ItemsSource="{x:Bind MailCollectionViewSource.View, Mode=OneWay}"
LoadMoreCommand="{x:Bind ViewModel.LoadMoreItemsCommand}"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.ItemContainerTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</ListView.ItemContainerTransitions>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Margin="8,0,12,0" AreStickyGroupHeadersEnabled="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Resources>
<ResourceDictionary>
<Style BasedOn="{StaticResource MailListHeaderStyle}" TargetType="ListViewHeaderItem" />
</ResourceDictionary>
</ListView.Resources>
<ListView.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource MailGroupHeaderDefaultTemplate}" HidesIfEmpty="True" />
</ListView.GroupStyle>
</listview:WinoListView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView
x:Name="ZoomOutList"
x:Load="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(SemanticZoomContainer.IsZoomedInViewActive), Mode=OneWay}"
ItemsSource="{x:Bind MailCollectionViewSource.View.CollectionGroups}">
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="12" />
<Setter Property="Padding" Value="0" />
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate x:DataType="ICollectionViewGroup">
<Grid Background="{ThemeResource MailListHeaderBackgroundColor}" CornerRadius="6">
<TextBlock
Margin="12,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind helpers:XamlHelpers.GetMailGroupDateString(Group)}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</RefreshContainer>
<advanced:WinoItemsView
x:Name="MailListView"
Margin="4"
ItemTemplate="{StaticResource MailItemContainerSelector}"
ItemsSource="{x:Bind ViewModel.MailCollection.Items, Mode=OneTime}"
Layout="{StaticResource DefaultItemsViewLayout}"
SelectionChanged="ListSelectionChanged"
SelectionMode="Extended" />
<!-- Try online search panel. -->
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}">
+136 -87
View File
@@ -7,19 +7,19 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using MoreLinq;
using Windows.Foundation;
using Wino.Controls;
using Wino.Controls.Advanced;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.Domain.Models.Navigation;
using Wino.Helpers;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
using Wino.Mail.WinUI;
@@ -62,10 +62,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
{
base.OnNavigatedFrom(e);
// Dispose all WinoListView items.
MailListView.Dispose();
this.Bindings.StopTracking();
RenderingFrame.Navigate(typeof(IdlePage));
@@ -81,7 +77,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
SelectAllCheckbox.Checked -= SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked -= SelectAllCheckboxUnchecked;
SelectAllCheckbox.IsChecked = MailListView.Items.Count > 0 && MailListView.SelectedItems.Count == MailListView.Items.Count;
SelectAllCheckbox.IsChecked = MailListView.CastedItemsSource?.Count() > 0 && MailListView.SelectedItems.Count == MailListView.CastedItemsSource.Count();
SelectAllCheckbox.Checked += SelectAllCheckboxChecked;
SelectAllCheckbox.Unchecked += SelectAllCheckboxUnchecked;
@@ -110,10 +106,10 @@ public sealed partial class MailListPage : MailListPageAbstract,
}
}
SelectAllCheckbox.IsChecked = false;
// SelectAllCheckbox.IsChecked = false;
SelectionModeToggle.IsChecked = false;
MailListView.ClearSelections();
// MailListView.ClearSelections();
UpdateSelectAllButtonStatus();
ViewModel.SelectedPivotChangedCommand.Execute(null);
@@ -121,7 +117,8 @@ public sealed partial class MailListPage : MailListPageAbstract,
private void ChangeSelectionMode(ListViewSelectionMode mode)
{
MailListView.ChangeSelectionMode(mode);
// ItemsView doesn't have a ChangeSelectionMode method like ListView
// The selection mode is set in XAML and doesn't need to change dynamically for ItemsView
if (ViewModel?.PivotFolders != null)
{
@@ -136,44 +133,46 @@ public sealed partial class MailListPage : MailListPageAbstract,
private void SelectAllCheckboxChecked(object sender, RoutedEventArgs e)
{
MailListView.SelectAllWino();
// MailListView.SelectAllWino();
}
private void SelectAllCheckboxUnchecked(object sender, RoutedEventArgs e)
{
MailListView.ClearSelections();
// MailListView.ClearSelections();
}
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
// TODO: New ItemsView implementation.
// 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.
if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
{
await FocusManager.TryFocusAsync(control, FocusState.Keyboard);
//if (sender is MailItemDisplayInformationControl control && args.TryGetPosition(sender, out Point p))
//{
// await FocusManager.TryFocusAsync(control, FocusState.Keyboard);
if (control.DataContext is IMailItem clickedMailItemContext)
{
var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
var availableActions = ViewModel.GetAvailableMailActions(targetItems);
// if (control.DataContext is IMailItem clickedMailItemContext)
// {
// var targetItems = ViewModel.GetTargetMailItemViewModels(clickedMailItemContext);
// var availableActions = ViewModel.GetAvailableMailActions(targetItems);
if (!availableActions?.Any() ?? false) return;
var t = targetItems.ElementAt(0);
// if (!availableActions?.Any() ?? false) return;
// var t = targetItems.ElementAt(0);
ViewModel.ChangeCustomFocusedState(targetItems, true);
// ViewModel.ChangeCustomFocusedState(targetItems, true);
var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
// var clickedOperation = await GetMailOperationFromFlyoutAsync(availableActions, control, p.X, p.Y);
ViewModel.ChangeCustomFocusedState(targetItems, false);
// ViewModel.ChangeCustomFocusedState(targetItems, false);
if (clickedOperation == null) return;
// if (clickedOperation == null) return;
var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
// var prepRequest = new MailOperationPreperationRequest(clickedOperation.Operation, targetItems.Select(a => a.MailCopy));
await ViewModel.ExecuteMailOperationAsync(prepRequest);
}
}
// await ViewModel.ExecuteMailOperationAsync(prepRequest);
// }
//}
}
private async Task<MailOperationMenuItem> GetMailOperationFromFlyoutAsync(IEnumerable<MailOperationMenuItem> availableActions,
@@ -196,7 +195,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
void IRecipient<ClearMailSelectionsRequested>.Receive(ClearMailSelectionsRequested message)
{
MailListView.ClearSelections(null, preserveThreadExpanding: true);
// MailListView.ClearSelections(null, preserveThreadExpanding: true);
}
void IRecipient<ActiveMailItemChangedEvent>.Receive(ActiveMailItemChangedEvent message)
@@ -299,46 +298,46 @@ public sealed partial class MailListPage : MailListPageAbstract,
{
if (message.SelectedMailViewModel == null) return;
await ViewModel.ExecuteUIThread(async () =>
{
MailListView.ClearSelections(message.SelectedMailViewModel, true);
//await ViewModel.ExecuteUIThread(async () =>
//{
// MailListView.ClearSelections(message.SelectedMailViewModel, true);
int retriedSelectionCount = 0;
trySelection:
// int retriedSelectionCount = 0;
//trySelection:
bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
// bool isSelected = MailListView.SelectMailItemContainer(message.SelectedMailViewModel);
if (!isSelected)
{
for (int i = retriedSelectionCount; i < 5;)
{
// Retry with delay until the container is realized. Max 1 second.
await Task.Delay(200);
// if (!isSelected)
// {
// for (int i = retriedSelectionCount; i < 5;)
// {
// // Retry with delay until the container is realized. Max 1 second.
// await Task.Delay(200);
retriedSelectionCount++;
// retriedSelectionCount++;
goto trySelection;
}
}
// goto trySelection;
// }
// }
// Automatically scroll to the selected item.
// This is useful when creating draft.
if (isSelected && message.ScrollToItem)
{
var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
// // Automatically scroll to the selected item.
// // This is useful when creating draft.
// if (isSelected && message.ScrollToItem)
// {
// var collectionContainer = ViewModel.MailCollection.GetMailItemContainer(message.SelectedMailViewModel.UniqueId);
// Scroll to thread if available.
if (collectionContainer.ThreadViewModel != null)
{
MailListView.ScrollIntoView(collectionContainer.ThreadViewModel, ScrollIntoViewAlignment.Default);
}
else if (collectionContainer.ItemViewModel != null)
{
MailListView.ScrollIntoView(collectionContainer.ItemViewModel, ScrollIntoViewAlignment.Default);
}
// // Scroll to thread if available.
// if (collectionContainer.ThreadViewModel != null)
// {
// MailListView.StartBringItemIntoView(collectionContainer.ThreadViewModel, new BringIntoViewOptions());
// }
// else if (collectionContainer.ItemViewModel != null)
// {
// MailListView.StartBringItemIntoView(collectionContainer.ItemViewModel, new BringIntoViewOptions());
// }
}
});
// }
//});
}
private void SearchBoxFocused(object sender, RoutedEventArgs e)
@@ -357,32 +356,29 @@ public sealed partial class MailListPage : MailListPageAbstract,
/// </summary>
private void ThreadHeaderDragStart(UIElement sender, DragStartingEventArgs args)
{
if (sender is MailItemDisplayInformationControl control
&& control.ConnectedExpander?.Content is WinoListView contentListView)
{
var allItems = contentListView.Items.Where(a => a is IMailItem);
//if (sender is MailItemDisplayInformationControl control
// && control.ConnectedExpander?.Content is WinoListView contentListView)
//{
// var allItems = contentListView.Items.Where(a => a is MailCopy);
// Highlight all items.
allItems.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = true);
// // Highlight all items.
// allItems.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = true);
// Set native drag arg properties.
args.AllowedOperations = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
// // Set native drag arg properties.
// args.AllowedOperations = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
// var dragPackage = new MailDragPackage(allItems.Cast<MailCopy>());
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
args.DragUI.SetContentFromDataPackage();
// args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
// args.DragUI.SetContentFromDataPackage();
control.ConnectedExpander.IsExpanded = true;
}
// control.ConnectedExpander.IsExpanded = true;
//}
}
private void ThreadHeaderDragFinished(UIElement sender, DropCompletedEventArgs args)
{
if (sender is MailItemDisplayInformationControl control && control.ConnectedExpander != null && control.ConnectedExpander.Content is WinoListView contentListView)
{
contentListView.Items.Where(a => a is MailItemViewModel).Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
}
}
private async void LeftSwipeItemInvoked(Microsoft.UI.Xaml.Controls.SwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemInvokedEventArgs args)
@@ -400,7 +396,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, threadMailItemViewModel.GetMailCopies());
var package = new MailOperationPreperationRequest(MailOperation.SoftDelete, threadMailItemViewModel.ThreadEmails.Select(a => a.MailCopy));
await ViewModel.ExecuteMailOperationAsync(package);
}
}
@@ -422,19 +418,15 @@ public sealed partial class MailListPage : MailListPageAbstract,
}
else if (swipeControl.Tag is ThreadMailItemViewModel threadMailItemViewModel)
{
bool isAllRead = threadMailItemViewModel.ThreadItems.All(a => a.IsRead);
bool isAllRead = threadMailItemViewModel.ThreadEmails.All(a => a.IsRead);
var operation = isAllRead ? MailOperation.MarkAsUnread : MailOperation.MarkAsRead;
var package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies());
var package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.ThreadEmails.Select(a => a.MailCopy));
await ViewModel.ExecuteMailOperationAsync(package);
}
}
private void PullToRefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args)
{
ViewModel.SyncFolderCommand?.Execute(null);
}
private async void SearchBar_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
@@ -494,8 +486,65 @@ public sealed partial class MailListPage : MailListPageAbstract,
}
private void SelectAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
=> MailListView.SelectAllWino();
{
// MailListView.SelectAllWino();
}
private void DeleteAllInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
=> 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>
/// Animates the rotation using high-performance Composition APIs
/// </summary>
private void AnimateRotationWithComposition(FrameworkElement element, float targetAngleInDegrees)
{
// Get the element's visual from the composition layer
var visual = ElementCompositionPreview.GetElementVisual(element);
var compositor = visual.Compositor;
// Set the center point for rotation (center of the element)
visual.CenterPoint = new System.Numerics.Vector3(
(float)element.ActualWidth / 2f,
(float)element.ActualHeight / 2f,
0f);
// Create a rotation animation
var rotationAnimation = compositor.CreateScalarKeyFrameAnimation();
rotationAnimation.Target = "RotationAngleInDegrees";
rotationAnimation.Duration = TimeSpan.FromMilliseconds(200);
// Add easing function for smooth animation
var easingFunction = compositor.CreateCubicBezierEasingFunction(
new System.Numerics.Vector2(0.25f, 0.1f), // Control point 1
new System.Numerics.Vector2(0.25f, 1f)); // Control point 2 (similar to CircleEase)
// Insert keyframe with the target angle and easing
rotationAnimation.InsertKeyFrame(1.0f, targetAngleInDegrees, easingFunction);
// Start the animation
visual.StartAnimation("RotationAngleInDegrees", rotationAnimation);
}
private void ListSelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args)
{
UpdateSelectAllButtonStatus();
UpdateAdaptiveness();
}
}