Removal of Bindings

This commit is contained in:
Burak Kaan Köse
2025-11-16 00:23:23 +01:00
parent a2c7e5f29a
commit 07aeaf8c8f
217 changed files with 234 additions and 22608 deletions
@@ -5,15 +5,21 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:shared="using:Wino.Core.Domain.Entities.Shared"
Title="{x:Bind domain:Translator.AccountPickerDialog_Title}"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ListView
DisplayMemberPath="Address"
IsItemClickEnabled="True"
ItemClick="AccountClicked"
ItemsSource="{x:Bind AvailableAccounts}"
SelectionMode="None" />
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate x:DataType="shared:MailAccount">
<TextBlock Text="{x:Bind Address}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentDialog>
+1 -14
View File
@@ -20,20 +20,7 @@
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
<!-- Root Account Template -->
<DataTemplate x:Key="RootAccountTemplate" x:DataType="viewModelData:AccountProviderDetailViewModel">
<winuiControls:SettingsCard
Margin="0,2,0,0"
Command="{Binding ViewModel.NavigateAccountDetailsCommand, Mode=OneWay, ElementName=root}"
CommandParameter="{x:Bind}"
Description="{x:Bind Account.Address}"
Header="{x:Bind Account.Name}"
IsClickEnabled="True">
<winuiControls:SettingsCard.HeaderIcon>
<coreControls:WinoFontIcon FontSize="64" Icon="{x:Bind helpers:XamlHelpers.GetProviderIcon(Account)}" />
</winuiControls:SettingsCard.HeaderIcon>
</winuiControls:SettingsCard>
</DataTemplate>
<!--#region Navigation Menu Templates-->
@@ -19,7 +19,20 @@
mc:Ignorable="d">
<Page.Resources>
<!-- Root Account Template -->
<DataTemplate x:Key="RootAccountTemplate" x:DataType="viewModelData:AccountProviderDetailViewModel">
<winuiControls:SettingsCard
Margin="0,2,0,0"
Click="RootAccountTemplate_Click"
CommandParameter="{x:Bind}"
Description="{x:Bind Account.Address}"
Header="{x:Bind Account.Name}"
IsClickEnabled="True">
<winuiControls:SettingsCard.HeaderIcon>
<coreControls:WinoFontIcon FontSize="64" Icon="{x:Bind helpers:XamlHelpers.GetProviderIcon(Account)}" />
</winuiControls:SettingsCard.HeaderIcon>
</winuiControls:SettingsCard>
</DataTemplate>
<!-- Merged Account Template -->
<DataTemplate x:Key="MergedAccountTemplate" x:DataType="data:MergedAccountProviderDetailViewModel">
@@ -29,7 +42,7 @@
<winuiControls:SettingsCard
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Command="{Binding ViewModel.EditMergedAccountsCommand, Mode=OneWay, ElementName=root}"
Click="EditMergedAccounts_Click"
CommandParameter="{x:Bind}"
Description="{x:Bind domain:Translator.SettingsEditLinkedInbox_Description}"
Header="{x:Bind domain:Translator.SettingsEditLinkedInbox_Title}"
@@ -1,5 +1,8 @@
using System;
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Navigation;
using Wino.Core.ViewModels.Data;
using Wino.Mail.ViewModels.Data;
using Wino.Views.Abstract;
namespace Wino.Views;
@@ -12,4 +15,21 @@ public sealed partial class AccountManagementPage : AccountManagementPageAbstrac
NavigationCacheMode = NavigationCacheMode.Enabled;
}
private void EditMergedAccounts_Click(object sender, RoutedEventArgs e)
{
if (sender is SettingsCard card && card.CommandParameter is MergedAccountProviderDetailViewModel mergedAccount)
{
ViewModel.EditMergedAccountsCommand.Execute(mergedAccount);
}
}
private void RootAccountTemplate_Click(object sender, RoutedEventArgs e)
{
if (sender is SettingsCard card && card.CommandParameter is AccountProviderDetailViewModel accountDetails)
{
ViewModel.NavigateAccountDetailsCommand.Execute(accountDetails);
}
}
}
@@ -18,7 +18,7 @@
<DataTemplate x:Key="LinkedAccountTemplate" x:DataType="data:AccountProviderDetailViewModel">
<controls:SettingsCard
Margin="0,2,0,0"
Command="{Binding ViewModel.UnlinkAccountCommand, Mode=OneWay, ElementName=MainPage}"
Click="UnlinkAccount_Click"
CommandParameter="{x:Bind}"
Description="{x:Bind Account.Address}"
Header="{x:Bind Account.Name}"
@@ -38,7 +38,7 @@
<DataTemplate x:Key="UnlinkedAccountTemplate" x:DataType="data:AccountProviderDetailViewModel">
<controls:SettingsCard
Margin="0,2,0,0"
Command="{Binding ViewModel.LinkAccountCommand, Mode=OneWay, ElementName=MainPage}"
Click="LinkAccount_Click"
CommandParameter="{x:Bind}"
Description="{x:Bind Account.Address}"
FlowDirection="RightToLeft"
@@ -1,3 +1,6 @@
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Wino.Mail.ViewModels.Data;
using Wino.Views.Abstract;
@@ -9,4 +12,20 @@ public sealed partial class MergedAccountDetailsPage : MergedAccountDetailsPageA
{
InitializeComponent();
}
private void UnlinkAccount_Click(object sender, RoutedEventArgs e)
{
if (sender is SettingsCard card && card.CommandParameter is AccountProviderDetailViewModel account)
{
ViewModel.UnlinkAccountCommand.Execute(account);
}
}
private void LinkAccount_Click(object sender, RoutedEventArgs e)
{
if (sender is SettingsCard card && card.CommandParameter is AccountProviderDetailViewModel account)
{
ViewModel.LinkAccountCommand.Execute(account);
}
}
}
+6 -6
View File
@@ -84,12 +84,12 @@
<Grid.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem
Command="{Binding ElementName=root, Path=ViewModel.OpenAttachmentCommand}"
CommandParameter="{Binding}"
Click="OpenAttachment_Click"
CommandParameter="{x:Bind}"
Text="{x:Bind domain:Translator.Buttons_Open}" />
<MenuFlyoutItem
Command="{Binding ElementName=root, Path=ViewModel.SaveAttachmentCommand}"
CommandParameter="{Binding}"
Click="SaveAttachment_Click"
CommandParameter="{x:Bind}"
Text="{x:Bind domain:Translator.Buttons_Save}" />
</MenuFlyout>
</Grid.ContextFlyout>
@@ -139,8 +139,8 @@
VerticalAlignment="Stretch"
Background="Transparent"
BorderThickness="0"
Command="{Binding ElementName=root, Path=ViewModel.RemoveAttachmentCommand}"
CommandParameter="{Binding}">
Click="RemoveAttachment_Click"
CommandParameter="{x:Bind}">
<SymbolIcon
Grid.Column="2"
VerticalAlignment="Center"
+24
View File
@@ -385,6 +385,30 @@ public sealed partial class ComposePage : ComposePageAbstract,
DisposeDisposables();
}
private void OpenAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
{
ViewModel.OpenAttachmentCommand.Execute(attachment);
}
}
private void SaveAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
{
ViewModel.SaveAttachmentCommand.Execute(attachment);
}
}
private void RemoveAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is MailAttachmentViewModel attachment)
{
ViewModel.RemoveAttachmentCommand.Execute(attachment);
}
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
+3 -3
View File
@@ -54,7 +54,7 @@
Grid.Row="1"
Grid.Column="1"
Padding="0"
Command="{Binding ElementName=root, Path=ViewModel.CopyClipboardCommand}"
Click="CopyAddress_Click"
CommandParameter="{x:Bind Address}"
Content="{x:Bind Address}" />
</Grid>
@@ -83,7 +83,7 @@
<Grid.ContextFlyout>
<MenuFlyout Placement="Right">
<MenuFlyoutItem
Command="{Binding ElementName=root, Path=ViewModel.OpenAttachmentCommand}"
Click="OpenAttachment_Click"
CommandParameter="{x:Bind}"
Text="{x:Bind domain:Translator.Buttons_Open}">
<MenuFlyoutItem.Icon>
@@ -91,7 +91,7 @@
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem
Command="{Binding ElementName=root, Path=ViewModel.SaveAttachmentCommand}"
Click="SaveAttachment_Click"
CommandParameter="{x:Bind}"
Text="{x:Bind domain:Translator.Buttons_Save}">
<MenuFlyoutItem.Icon>
@@ -298,6 +298,30 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
}
}
private void CopyAddress_Click(object sender, RoutedEventArgs e)
{
if (sender is HyperlinkButton button && button.CommandParameter is string address)
{
ViewModel.CopyClipboardCommand.Execute(address);
}
}
private void OpenAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
{
ViewModel.OpenAttachmentCommand.Execute(attachment);
}
}
private void SaveAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
{
ViewModel.SaveAttachmentCommand.Execute(attachment);
}
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
@@ -20,7 +20,7 @@
<DataTemplate x:DataType="settings:SettingOption">
<controls:SettingsCard
Margin="0,2"
Command="{Binding ElementName=root, Path=ViewModel.NavigateSubDetailCommand}"
Click="SettingOptionClicked"
CommandParameter="{x:Bind NavigationPage}"
Description="{x:Bind Description}"
Header="{x:Bind Title}"
@@ -1,3 +1,5 @@
using CommunityToolkit.WinUI.Controls;
using Wino.Core.Domain.Enums;
using Wino.Views.Abstract;
namespace Wino.Views.Settings;
@@ -8,4 +10,9 @@ public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
{
InitializeComponent();
}
private void SettingOptionClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is SettingsCard card && card.CommandParameter is WinoPage page) ViewModel.NavigateSubDetailCommand.Execute(page);
}
}
@@ -3,7 +3,6 @@
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:behaviors="using:CommunityToolkit.WinUI.Behaviors"
xmlns:controls="using:Wino.Controls"
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -62,15 +61,15 @@
<RadioButton
Grid.Column="2"
Command="{Binding ElementName=root, Path=ViewModel.SetAliasPrimaryCommand}"
CommandParameter="{Binding}"
Click="SetAliasPrimary_Click"
CommandParameter="{x:Bind}"
IsChecked="{x:Bind IsPrimary, Mode=OneWay}" />
<Button
Grid.Column="3"
HorizontalAlignment="Right"
Command="{Binding ElementName=root, Path=ViewModel.DeleteAliasCommand}"
CommandParameter="{Binding}"
Click="DeleteAlias_Click"
CommandParameter="{x:Bind}"
IsEnabled="{x:Bind CanDelete}">
<Button.Content>
<PathIcon Data="F1 M 19.375 3.125 C 19.375 3.26823 19.34733 3.382162 19.291992 3.466797 C 19.236652 3.551434 19.16341 3.61491 19.072266 3.657227 C 18.981119 3.699545 18.880207 3.727215 18.769531 3.740234 C 18.658854 3.753256 18.544922 3.759766 18.427734 3.759766 C 18.362629 3.759766 18.297525 3.758139 18.232422 3.754883 C 18.167316 3.751629 18.108723 3.75 18.056641 3.75 L 16.435547 17.783203 C 16.396484 18.095703 16.305338 18.387045 16.162109 18.657227 C 16.018879 18.927408 15.836588 19.161783 15.615234 19.360352 C 15.39388 19.55892 15.139974 19.71517 14.853516 19.829102 C 14.567057 19.943033 14.267578 20 13.955078 20 L 6.044922 20 C 5.732422 20 5.432942 19.943033 5.146484 19.829102 C 4.860026 19.71517 4.60612 19.55892 4.384766 19.360352 C 4.163411 19.161783 3.98112 18.927408 3.837891 18.657227 C 3.694661 18.387045 3.603516 18.095703 3.564453 17.783203 L 1.943359 3.75 C 1.878255 3.75 1.813151 3.751629 1.748047 3.754883 C 1.682943 3.758139 1.617839 3.759766 1.552734 3.759766 C 1.442057 3.759766 1.33138 3.753256 1.220703 3.740234 C 1.110026 3.727215 1.010742 3.697918 0.922852 3.652344 C 0.834961 3.606771 0.763346 3.541668 0.708008 3.457031 C 0.652669 3.372396 0.625 3.261719 0.625 3.125 C 0.625 2.95573 0.686849 2.809246 0.810547 2.685547 C 0.934245 2.56185 1.080729 2.5 1.25 2.5 L 6.933594 2.5 C 6.998697 2.141928 7.127278 1.809896 7.319336 1.503906 C 7.511393 1.197918 7.745768 0.932617 8.022461 0.708008 C 8.299153 0.483398 8.606771 0.309246 8.945312 0.185547 C 9.283854 0.06185 9.635416 0 10 0 C 10.364583 0 10.716146 0.06185 11.054688 0.185547 C 11.393229 0.309246 11.700846 0.483398 11.977539 0.708008 C 12.25423 0.932617 12.488605 1.197918 12.680664 1.503906 C 12.872721 1.809896 13.001302 2.141928 13.066406 2.5 L 18.75 2.5 C 18.91927 2.5 19.065754 2.56185 19.189453 2.685547 C 19.31315 2.809246 19.375 2.95573 19.375 3.125 Z M 8.232422 2.5 L 11.767578 2.5 C 11.702474 2.311199 11.611328 2.140301 11.494141 1.987305 C 11.376953 1.834311 11.240234 1.702475 11.083984 1.591797 C 10.927734 1.481121 10.758463 1.396484 10.576172 1.337891 C 10.39388 1.279297 10.201822 1.25 10 1.25 C 9.798177 1.25 9.606119 1.279297 9.423828 1.337891 C 9.241536 1.396484 9.072266 1.481121 8.916016 1.591797 C 8.759766 1.702475 8.623047 1.834311 8.505859 1.987305 C 8.388672 2.140301 8.297525 2.311199 8.232422 2.5 Z M 16.796875 3.75 L 3.203125 3.75 L 4.804688 17.646484 C 4.84375 17.972006 4.978841 18.237305 5.209961 18.442383 C 5.441081 18.647461 5.719401 18.75 6.044922 18.75 L 13.955078 18.75 C 14.111328 18.75 14.261067 18.72233 14.404297 18.666992 C 14.547525 18.611654 14.674479 18.535156 14.785156 18.4375 C 14.895833 18.339844 14.986979 18.222656 15.058594 18.085938 C 15.130208 17.949219 15.175781 17.802734 15.195312 17.646484 Z M 7.5 14.375 L 7.5 8.125 C 7.5 7.95573 7.561849 7.809246 7.685547 7.685547 C 7.809245 7.56185 7.955729 7.5 8.125 7.5 C 8.294271 7.5 8.440755 7.56185 8.564453 7.685547 C 8.68815 7.809246 8.75 7.95573 8.75 8.125 L 8.75 14.375 C 8.75 14.544271 8.68815 14.690756 8.564453 14.814453 C 8.440755 14.938151 8.294271 15 8.125 15 C 7.955729 15 7.809245 14.938151 7.685547 14.814453 C 7.561849 14.690756 7.5 14.544271 7.5 14.375 Z M 11.25 14.375 L 11.25 8.125 C 11.25 7.95573 11.311849 7.809246 11.435547 7.685547 C 11.559244 7.56185 11.705729 7.5 11.875 7.5 C 12.04427 7.5 12.190754 7.56185 12.314453 7.685547 C 12.43815 7.809246 12.5 7.95573 12.5 8.125 L 12.5 14.375 C 12.5 14.544271 12.43815 14.690756 12.314453 14.814453 C 12.190754 14.938151 12.04427 15 11.875 15 C 11.705729 15 11.559244 14.938151 11.435547 14.814453 C 11.311849 14.690756 11.25 14.544271 11.25 14.375 Z " />
@@ -1,3 +1,5 @@
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Entities.Mail;
using Wino.Views.Abstract;
namespace Wino.Views.Settings;
@@ -6,6 +8,22 @@ public sealed partial class AliasManagementPage : AliasManagementPageAbstract
{
public AliasManagementPage()
{
this.InitializeComponent();
InitializeComponent();
}
private void SetAliasPrimary_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is RadioButton button && button.CommandParameter is MailAccountAlias alias)
{
ViewModel.SetAliasPrimaryCommand.Execute(alias);
}
}
private void DeleteAlias_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is MailAccountAlias alias)
{
ViewModel.DeleteAliasCommand.Execute(alias);
}
}
}
@@ -30,7 +30,7 @@
Height="48"
Margin="0,0,16,0"
DisplayName="{x:Bind Name}"
ProfilePicture="{x:Bind helpers:XamlHelpers.Base64ToBitmapImage(Base64ContactPicture), Mode=OneWay}" />
ProfilePicture="{x:Bind helpers:XamlHelpers.Base64ToBitmapImage(Base64ContactPicture)}" />
<!-- Contact Info -->
<StackPanel Grid.Column="1" VerticalAlignment="Center">
@@ -48,7 +48,7 @@
Padding="4,2"
Background="{ThemeResource AccentFillColorDefaultBrush}"
CornerRadius="2"
Visibility="{x:Bind IsRootContact, Mode=OneWay}">
Visibility="{x:Bind IsRootContact}">
<TextBlock
FontSize="10"
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
@@ -57,7 +57,7 @@
<Border
Padding="4,2"
CornerRadius="2"
Visibility="{x:Bind IsOverridden, Mode=OneWay}">
Visibility="{x:Bind IsOverridden}">
<TextBlock FontSize="10" Text="{x:Bind domain:Translator.ContactStatus_Modified, Mode=OneTime}" />
</Border>
</StackPanel>
@@ -69,23 +69,23 @@
Orientation="Horizontal"
Spacing="8">
<Button
Command="{Binding ViewModel.EditContactCommand, ElementName=root}"
CommandParameter="{Binding}"
Click="EditContact_Click"
CommandParameter="{x:Bind}"
Style="{StaticResource SubtleButtonStyle}"
ToolTipService.ToolTip="{x:Bind domain:Translator.ContactAction_Edit, Mode=OneTime}">
<FontIcon FontSize="16" Glyph="&#xE70F;" />
</Button>
<Button
Command="{Binding ViewModel.PickContactPhotoCommand, ElementName=root}"
CommandParameter="{Binding}"
Click="PickContactPhoto_Click"
CommandParameter="{x:Bind}"
Style="{StaticResource SubtleButtonStyle}"
ToolTipService.ToolTip="{x:Bind domain:Translator.ContactAction_ChangePhoto, Mode=OneTime}">
<FontIcon FontSize="16" Glyph="&#xE91B;" />
</Button>
<Button
Command="{Binding ViewModel.DeleteContactCommand, ElementName=root}"
CommandParameter="{Binding}"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(IsRootContact), Mode=OneWay}"
Click="DeleteContact_Click"
CommandParameter="{x:Bind}"
IsEnabled="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(IsRootContact)}"
Style="{StaticResource SubtleButtonStyle}"
ToolTipService.ToolTip="{x:Bind domain:Translator.ContactAction_Delete, Mode=OneTime}">
<FontIcon FontSize="16" Glyph="&#xE74D;" />
@@ -1,3 +1,5 @@
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Entities.Shared;
using Wino.Views.Abstract;
namespace Wino.Views.Settings;
@@ -6,6 +8,30 @@ public sealed partial class ContactsPage : ContactsPageAbstract
{
public ContactsPage()
{
this.InitializeComponent();
InitializeComponent();
}
private void EditContact_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is AccountContact contact)
{
ViewModel.EditContactCommand.Execute(contact);
}
}
private void PickContactPhoto_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is AccountContact contact)
{
ViewModel.PickContactPhotoCommand.Execute(contact);
}
}
private void DeleteContact_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is AccountContact contact)
{
ViewModel.DeleteContactCommand.Execute(contact);
}
}
}
@@ -68,7 +68,7 @@
<Button
Grid.Column="2"
Margin="4,0"
Command="{Binding ViewModel.StartEditingShortcutCommand, ElementName=root}"
Click="EditShortcut_Click"
CommandParameter="{x:Bind}"
Content="&#xE70F;"
FontFamily="Segoe MDL2 Assets"
@@ -78,7 +78,7 @@
<Button
Grid.Column="3"
Margin="4,0"
Command="{Binding ViewModel.DeleteShortcutCommand, ElementName=root}"
Click="DeleteShortcut_Click"
CommandParameter="{x:Bind}"
Content="&#xE74D;"
FontFamily="Segoe MDL2 Assets"
@@ -1,3 +1,5 @@
using Microsoft.UI.Xaml.Controls;
using Wino.Core.ViewModels.Data;
using Wino.Mail.WinUI.Views.Abstract;
namespace Wino.Views.Settings;
@@ -8,4 +10,20 @@ public sealed partial class KeyboardShortcutsPage : KeyboardShortcutsPageAbstrac
{
this.InitializeComponent();
}
private void EditShortcut_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is KeyboardShortcutViewModel shortcut)
{
ViewModel.StartEditingShortcutCommand.Execute(shortcut);
}
}
private void DeleteShortcut_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is KeyboardShortcutViewModel shortcut)
{
ViewModel.DeleteShortcutCommand.Execute(shortcut);
}
}
}
@@ -12,6 +12,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:reader="using:Wino.Core.Domain.Models.Reader"
xmlns:system="using:System"
x:Name="root"
mc:Ignorable="d">
@@ -19,7 +20,7 @@
<DataTemplate x:Key="SignatureDialogTemplate" x:DataType="entities:AccountSignature">
<controls1:SwitchPresenter Value="{x:Bind Id}">
<!-- Case for "None" signature -->
<controls1:Case Value="{Binding ViewModel.EmptyGuid, Mode=OneTime, ElementName=root}" />
<controls1:Case Value="{x:Bind system:Guid.Empty}" />
<!-- Default case for regular signatures -->
<controls1:Case IsDefault="True">
@@ -34,14 +35,14 @@
<controls1:SettingsCard Header="{x:Bind domain:Translator.SettingsSignature_EditSignature_Title}">
<Button
Width="65"
Command="{Binding ViewModel.OpenSignatureEditorEditCommand, Mode=OneWay, ElementName=root}"
Click="EditSignature_Click"
CommandParameter="{x:Bind}"
Content="{x:Bind domain:Translator.Buttons_Edit}" />
</controls1:SettingsCard>
<controls1:SettingsCard Header="{x:Bind domain:Translator.SettingsSignature_DeleteSignature_Title}">
<Button
Background="Red"
Command="{Binding ViewModel.DeleteSignatureCommand, Mode=OneWay, ElementName=root}"
Click="DeleteSignature_Click"
CommandParameter="{x:Bind}"
Content="{x:Bind domain:Translator.Buttons_Delete}" />
</controls1:SettingsCard>
@@ -1,3 +1,5 @@
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Entities.Mail;
using Wino.Views.Abstract;
namespace Wino.Views.Settings;
@@ -6,6 +8,22 @@ public sealed partial class SignatureManagementPage : SignatureManagementPageAbs
{
public SignatureManagementPage()
{
this.InitializeComponent();
InitializeComponent();
}
private void EditSignature_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is AccountSignature signature)
{
ViewModel.OpenSignatureEditorEditCommand.Execute(signature);
}
}
private void DeleteSignature_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.CommandParameter is AccountSignature signature)
{
ViewModel.DeleteSignatureCommand.Execute(signature);
}
}
}
@@ -1,22 +0,0 @@
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Views;
namespace Wino.Activation;
internal class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
{
protected override Task HandleInternalAsync(IActivatedEventArgs args)
{
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
return Task.CompletedTask;
}
// Only navigate if Frame content doesn't exist.
protected override bool CanHandleInternal(IActivatedEventArgs args)
=> (Window.Current?.Content as Frame)?.Content == null;
}
@@ -1,67 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Extensions;
using Wino.Views;
namespace Wino.Activation;
internal class FileActivationHandler : ActivationHandler<FileActivatedEventArgs>
{
private readonly INativeAppService _nativeAppService;
private readonly IMimeFileService _mimeFileService;
private readonly IStatePersistanceService _statePersistanceService;
private readonly INavigationService _winoNavigationService;
public FileActivationHandler(INativeAppService nativeAppService,
IMimeFileService mimeFileService,
IStatePersistanceService statePersistanceService,
INavigationService winoNavigationService)
{
_nativeAppService = nativeAppService;
_mimeFileService = mimeFileService;
_statePersistanceService = statePersistanceService;
_winoNavigationService = winoNavigationService;
}
protected override async Task HandleInternalAsync(FileActivatedEventArgs args)
{
// Always handle the last item passed.
// Multiple files are not supported.
var file = args.Files.Last() as StorageFile;
// Only EML files are supported now.
var fileExtension = Path.GetExtension(file.Path);
if (string.Equals(fileExtension, ".eml", StringComparison.OrdinalIgnoreCase))
{
var fileBytes = await file.ToByteArrayAsync();
var directoryName = Path.GetDirectoryName(file.Path);
var messageInformation = await _mimeFileService.GetMimeMessageInformationAsync(fileBytes, directoryName).ConfigureAwait(false);
if (_nativeAppService.IsAppRunning())
{
// TODO: Activate another Window and go to mail rendering page.
_winoNavigationService.Navigate(WinoPage.MailRenderingPage, messageInformation, NavigationReferenceFrame.RenderingFrame);
}
else
{
_statePersistanceService.ShouldShiftMailRenderingDesign = true;
(Window.Current.Content as Frame).Navigate(typeof(MailRenderingPage), messageInformation, new DrillInNavigationTransitionInfo());
}
}
}
protected override bool CanHandleInternal(FileActivatedEventArgs args) => args.Files.Any();
}
@@ -1,59 +0,0 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Windows.ApplicationModel.Activation;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Launch;
using Wino.Messaging.Client.Shell;
namespace Wino.Activation;
internal class ProtocolActivationHandler : ActivationHandler<ProtocolActivatedEventArgs>
{
private const string MailtoProtocolTag = "mailto:";
private readonly INativeAppService _nativeAppService;
private readonly ILaunchProtocolService _launchProtocolService;
public ProtocolActivationHandler(INativeAppService nativeAppService, ILaunchProtocolService launchProtocolService)
{
_nativeAppService = nativeAppService;
_launchProtocolService = launchProtocolService;
}
protected override Task HandleInternalAsync(ProtocolActivatedEventArgs args)
{
// Check URI prefix.
var protocolString = args.Uri.AbsoluteUri;
if (protocolString.StartsWith(MailtoProtocolTag))
{
// mailto activation. Try to parse params.
_launchProtocolService.MailToUri = new MailToUri(protocolString);
if (_nativeAppService.IsAppRunning())
{
// Just send publish a message. Shell will continue.
WeakReferenceMessenger.Default.Send(new MailtoProtocolMessageRequested());
}
}
return Task.CompletedTask;
}
protected override bool CanHandleInternal(ProtocolActivatedEventArgs args)
{
// Validate the URI scheme.
try
{
var uriGet = args.Uri;
}
catch (UriFormatException)
{
return false;
}
return base.CanHandleInternal(args);
}
}
@@ -1,75 +0,0 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Windows.ApplicationModel.Activation;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
using Wino.Messaging.Client.Accounts;
namespace Wino.Activation;
/// <summary>
/// This handler will only handle the toasts that runs on foreground.
/// Background executions are not handled here like mark as read or delete.
/// </summary>
internal class ToastNotificationActivationHandler : ActivationHandler<ToastNotificationActivatedEventArgs>
{
private readonly IMailService _mailService;
private readonly IFolderService _folderService;
private ToastArguments _toastArguments;
public ToastNotificationActivationHandler(IMailService mailService,
IFolderService folderService)
{
_mailService = mailService;
_folderService = folderService;
}
protected override async Task HandleInternalAsync(ToastNotificationActivatedEventArgs args)
{
// Create the mail item navigation event.
// If the app is running, it'll be picked up by the Messenger.
// Otherwise we'll save it and handle it when the shell loads all accounts.
// Parse the mail unique id and perform above actions.
if (Guid.TryParse(_toastArguments[Constants.ToastMailUniqueIdKey], out Guid mailItemUniqueId))
{
var account = await _mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false);
if (account == null) return;
var mailItem = await _mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false);
if (mailItem == null) return;
var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem);
// Delegate this event to LaunchProtocolService so app shell can pick it up on launch if app doesn't work.
var launchProtocolService = App.Current.Services.GetService<ILaunchProtocolService>();
launchProtocolService.LaunchParameter = message;
// Send the messsage anyways. Launch protocol service will be ignored if the message is picked up by subscriber shell.
WeakReferenceMessenger.Default.Send(message);
}
}
protected override bool CanHandleInternal(ToastNotificationActivatedEventArgs args)
{
try
{
_toastArguments = ToastArguments.Parse(args.Argument);
return
_toastArguments.Contains(Constants.ToastMailUniqueIdKey) &&
_toastArguments.Contains(Constants.ToastActionKey);
}
catch (Exception ex)
{
Log.Error(ex, "Couldn't handle parsing toast notification arguments for foreground navigate.");
}
return false;
}
}
-557
View File
@@ -1,557 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// LottieGen version:
// 8.1.240821.1+077322fa26
//
// Command:
// LottieGen -Language CSharp -Public -WinUIVersion 2.6 -InputFile synchronization.json
//
// Input file:
// synchronization.json (2404 bytes created 18:54+01:00 Dec 21 2024)
//
// LottieGen source:
// http://aka.ms/Lottie
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
// ___________________________________________________________
// | Object stats | UAP v15 count | UAP v7 count |
// |__________________________|_______________|______________|
// | All CompositionObjects | 26 | 26 |
// |--------------------------+---------------+--------------|
// | Expression animators | 1 | 1 |
// | KeyFrame animators | 1 | 1 |
// | Reference parameters | 1 | 1 |
// | Expression operations | 0 | 0 |
// |--------------------------+---------------+--------------|
// | Animated brushes | - | - |
// | Animated gradient stops | - | - |
// | ExpressionAnimations | 1 | 1 |
// | PathKeyFrameAnimations | - | - |
// |--------------------------+---------------+--------------|
// | ContainerVisuals | 1 | 1 |
// | ShapeVisuals | 1 | 1 |
// |--------------------------+---------------+--------------|
// | ContainerShapes | 1 | 1 |
// | CompositionSpriteShapes | 2 | 2 |
// |--------------------------+---------------+--------------|
// | Brushes | 1 | 1 |
// | Gradient stops | - | - |
// | CompositionVisualSurface | - | - |
// -----------------------------------------------------------
using Microsoft.Graphics.Canvas.Geometry;
using System;
using System.Collections.Generic;
using System.Numerics;
using Windows.Graphics;
using Windows.UI;
using Windows.UI.Composition;
namespace AnimatedVisuals
{
// Name: main_libary_shelf_icon_sync
// Frame rate: 60 fps
// Frame count: 61
// Duration: 1016.7 mS
sealed class Synchronization
: Microsoft.UI.Xaml.Controls.IAnimatedVisualSource
, Microsoft.UI.Xaml.Controls.IAnimatedVisualSource2
{
// Animation duration: 1.017 seconds.
internal const long c_durationTicks = 10166666;
public Microsoft.UI.Xaml.Controls.IAnimatedVisual TryCreateAnimatedVisual(Compositor compositor)
{
object ignored = null;
return TryCreateAnimatedVisual(compositor, out ignored);
}
public Microsoft.UI.Xaml.Controls.IAnimatedVisual TryCreateAnimatedVisual(Compositor compositor, out object diagnostics)
{
diagnostics = null;
if (Synchronization_AnimatedVisual_UAPv15.IsRuntimeCompatible())
{
var res =
new Synchronization_AnimatedVisual_UAPv15(
compositor
);
return res;
}
if (Synchronization_AnimatedVisual_UAPv7.IsRuntimeCompatible())
{
var res =
new Synchronization_AnimatedVisual_UAPv7(
compositor
);
return res;
}
return null;
}
/// <summary>
/// Gets the number of frames in the animation.
/// </summary>
public double FrameCount => 61d;
/// <summary>
/// Gets the frame rate of the animation.
/// </summary>
public double Framerate => 60d;
/// <summary>
/// Gets the duration of the animation.
/// </summary>
public TimeSpan Duration => TimeSpan.FromTicks(10166666);
/// <summary>
/// Converts a zero-based frame number to the corresponding progress value denoting the
/// start of the frame.
/// </summary>
public double FrameToProgress(double frameNumber)
{
return frameNumber / 61d;
}
/// <summary>
/// Returns a map from marker names to corresponding progress values.
/// </summary>
public IReadOnlyDictionary<string, double> Markers =>
new Dictionary<string, double>
{
};
/// <summary>
/// Sets the color property with the given name, or does nothing if no such property
/// exists.
/// </summary>
public void SetColorProperty(string propertyName, Color value)
{
}
/// <summary>
/// Sets the scalar property with the given name, or does nothing if no such property
/// exists.
/// </summary>
public void SetScalarProperty(string propertyName, double value)
{
}
sealed class Synchronization_AnimatedVisual_UAPv15
: Microsoft.UI.Xaml.Controls.IAnimatedVisual
{
const long c_durationTicks = 10166666;
readonly Compositor _c;
readonly ExpressionAnimation _reusableExpressionAnimation;
AnimationController _animationController_0;
CompositionColorBrush _colorBrush_AlmostDarkSlateGray_FF2D3846;
ContainerVisual _root;
void BindProperty(
CompositionObject target,
string animatedPropertyName,
string expression,
string referenceParameterName,
CompositionObject referencedObject)
{
_reusableExpressionAnimation.ClearAllParameters();
_reusableExpressionAnimation.Expression = expression;
_reusableExpressionAnimation.SetReferenceParameter(referenceParameterName, referencedObject);
target.StartAnimation(animatedPropertyName, _reusableExpressionAnimation);
}
ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(float initialProgress, float initialValue, CompositionEasingFunction initialEasingFunction)
{
var result = _c.CreateScalarKeyFrameAnimation();
result.Duration = TimeSpan.FromTicks(c_durationTicks);
result.InsertKeyFrame(initialProgress, initialValue, initialEasingFunction);
return result;
}
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix, CompositionBrush fillBrush)
{
var result = _c.CreateSpriteShape(geometry);
result.TransformMatrix = transformMatrix;
result.FillBrush = fillBrush;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
AnimationController AnimationController_0()
{
var result = _animationController_0 = _c.CreateAnimationController();
result.Pause();
BindProperty(result, "Progress", "_.Progress", "_", _root);
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 2 Offset:<28.255, 18.903>
CanvasGeometry Geometry_0()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(11.7449999F, 5.09700012F));
builder.AddCubicBezier(new Vector2(11.7449999F, -3.66000009F), new Vector2(4.56699991F, -10.9189997F), new Vector2(-4.25500011F, -10.9189997F));
builder.AddCubicBezier(new Vector2(-8.6239996F, -10.9189997F), new Vector2(-12.7040005F, -9.20300007F), new Vector2(-15.7449999F, -6.08900023F));
builder.AddLine(new Vector2(-12.8739996F, -3.32599998F));
builder.AddCubicBezier(new Vector2(-10.5930004F, -5.66200018F), new Vector2(-7.53200006F, -6.94799995F), new Vector2(-4.25500011F, -6.94799995F));
builder.AddCubicBezier(new Vector2(2.36199999F, -6.94799995F), new Vector2(7.74499989F, -1.47099996F), new Vector2(7.74499989F, 5.09700012F));
builder.AddLine(new Vector2(3.74499989F, 5.09700012F));
builder.AddLine(new Vector2(9.74499989F, 10.9189997F));
builder.AddLine(new Vector2(15.7449999F, 5.09700012F));
builder.AddLine(new Vector2(11.7449999F, 5.09700012F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 1 Offset:<19.745, 29.096>
CanvasGeometry Geometry_1()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(4.25500011F, 6.94799995F));
builder.AddCubicBezier(new Vector2(-2.36199999F, 6.94799995F), new Vector2(-7.74499989F, 1.472F), new Vector2(-7.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-3.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-9.74499989F, -10.9189997F));
builder.AddLine(new Vector2(-15.7449999F, -5.09499979F));
builder.AddLine(new Vector2(-11.7449999F, -5.09499979F));
builder.AddCubicBezier(new Vector2(-11.7449999F, 3.66199994F), new Vector2(-4.56699991F, 10.9189997F), new Vector2(4.25500011F, 10.9189997F));
builder.AddCubicBezier(new Vector2(8.6260004F, 10.9189997F), new Vector2(12.7060003F, 9.20300007F), new Vector2(15.7449999F, 6.08900023F));
builder.AddLine(new Vector2(12.8739996F, 3.32500005F));
builder.AddCubicBezier(new Vector2(10.5930004F, 5.66099977F), new Vector2(7.53200006F, 6.94799995F), new Vector2(4.25500011F, 6.94799995F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
CompositionColorBrush ColorBrush_AlmostDarkSlateGray_FF2D3846()
{
return _colorBrush_AlmostDarkSlateGray_FF2D3846 = _c.CreateColorBrush(Color.FromArgb(0xFF, 0x2D, 0x38, 0x46));
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
CompositionContainerShape ContainerShape()
{
var result = _c.CreateContainerShape();
result.CenterPoint = new Vector2(24F, 24F);
var shapes = result.Shapes;
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
shapes.Add(SpriteShape_0());
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
shapes.Add(SpriteShape_1());
result.StartAnimation("RotationAngleInDegrees", RotationAngleInDegreesScalarAnimation_0_to_360(), AnimationController_0());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
CompositionPathGeometry PathGeometry_0()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_0()));
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
CompositionPathGeometry PathGeometry_1()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_1()));
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_0()
{
// Offset:<28.255, 18.903>
var geometry = PathGeometry_0();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 28.2549992F, 18.9029999F), ColorBrush_AlmostDarkSlateGray_FF2D3846());;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_1()
{
// Offset:<19.745, 29.096>
var geometry = PathGeometry_1();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 19.7450008F, 29.0960007F), _colorBrush_AlmostDarkSlateGray_FF2D3846);;
return result;
}
// The root of the composition.
ContainerVisual Root()
{
var result = _root = _c.CreateContainerVisual();
var propertySet = result.Properties;
propertySet.InsertScalar("Progress", 0F);
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
result.Children.InsertAtTop(ShapeVisual_0());
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Rotation
ScalarKeyFrameAnimation RotationAngleInDegreesScalarAnimation_0_to_360()
{
// Frame 0.
var result = CreateScalarKeyFrameAnimation(0F, 0F, HoldThenStepEasingFunction());
// Frame 61.
result.InsertKeyFrame(1F, 360F, _c.CreateCubicBezierEasingFunction(new Vector2(0.314999998F, 0F), new Vector2(0.465000004F, 0.861999989F)));
return result;
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
ShapeVisual ShapeVisual_0()
{
var result = _c.CreateShapeVisual();
result.Size = new Vector2(48F, 48F);
result.Shapes.Add(ContainerShape());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// RotationAngleInDegrees
StepEasingFunction HoldThenStepEasingFunction()
{
var result = _c.CreateStepEasingFunction();
result.IsFinalStepSingleFrame = true;
return result;
}
internal Synchronization_AnimatedVisual_UAPv15(
Compositor compositor
)
{
_c = compositor;
_reusableExpressionAnimation = compositor.CreateExpressionAnimation();
Root();
}
public Visual RootVisual => _root;
public TimeSpan Duration => TimeSpan.FromTicks(c_durationTicks);
public Vector2 Size => new Vector2(48F, 48F);
void IDisposable.Dispose() => _root?.Dispose();
internal static bool IsRuntimeCompatible()
{
return Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 15);
}
}
sealed class Synchronization_AnimatedVisual_UAPv7
: Microsoft.UI.Xaml.Controls.IAnimatedVisual
{
const long c_durationTicks = 10166666;
readonly Compositor _c;
readonly ExpressionAnimation _reusableExpressionAnimation;
CompositionColorBrush _colorBrush_AlmostDarkSlateGray_FF2D3846;
ContainerVisual _root;
void BindProperty(
CompositionObject target,
string animatedPropertyName,
string expression,
string referenceParameterName,
CompositionObject referencedObject)
{
_reusableExpressionAnimation.ClearAllParameters();
_reusableExpressionAnimation.Expression = expression;
_reusableExpressionAnimation.SetReferenceParameter(referenceParameterName, referencedObject);
target.StartAnimation(animatedPropertyName, _reusableExpressionAnimation);
}
ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(float initialProgress, float initialValue, CompositionEasingFunction initialEasingFunction)
{
var result = _c.CreateScalarKeyFrameAnimation();
result.Duration = TimeSpan.FromTicks(c_durationTicks);
result.InsertKeyFrame(initialProgress, initialValue, initialEasingFunction);
return result;
}
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix, CompositionBrush fillBrush)
{
var result = _c.CreateSpriteShape(geometry);
result.TransformMatrix = transformMatrix;
result.FillBrush = fillBrush;
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 2 Offset:<28.255, 18.903>
CanvasGeometry Geometry_0()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(11.7449999F, 5.09700012F));
builder.AddCubicBezier(new Vector2(11.7449999F, -3.66000009F), new Vector2(4.56699991F, -10.9189997F), new Vector2(-4.25500011F, -10.9189997F));
builder.AddCubicBezier(new Vector2(-8.6239996F, -10.9189997F), new Vector2(-12.7040005F, -9.20300007F), new Vector2(-15.7449999F, -6.08900023F));
builder.AddLine(new Vector2(-12.8739996F, -3.32599998F));
builder.AddCubicBezier(new Vector2(-10.5930004F, -5.66200018F), new Vector2(-7.53200006F, -6.94799995F), new Vector2(-4.25500011F, -6.94799995F));
builder.AddCubicBezier(new Vector2(2.36199999F, -6.94799995F), new Vector2(7.74499989F, -1.47099996F), new Vector2(7.74499989F, 5.09700012F));
builder.AddLine(new Vector2(3.74499989F, 5.09700012F));
builder.AddLine(new Vector2(9.74499989F, 10.9189997F));
builder.AddLine(new Vector2(15.7449999F, 5.09700012F));
builder.AddLine(new Vector2(11.7449999F, 5.09700012F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 1 Offset:<19.745, 29.096>
CanvasGeometry Geometry_1()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(4.25500011F, 6.94799995F));
builder.AddCubicBezier(new Vector2(-2.36199999F, 6.94799995F), new Vector2(-7.74499989F, 1.472F), new Vector2(-7.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-3.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-9.74499989F, -10.9189997F));
builder.AddLine(new Vector2(-15.7449999F, -5.09499979F));
builder.AddLine(new Vector2(-11.7449999F, -5.09499979F));
builder.AddCubicBezier(new Vector2(-11.7449999F, 3.66199994F), new Vector2(-4.56699991F, 10.9189997F), new Vector2(4.25500011F, 10.9189997F));
builder.AddCubicBezier(new Vector2(8.6260004F, 10.9189997F), new Vector2(12.7060003F, 9.20300007F), new Vector2(15.7449999F, 6.08900023F));
builder.AddLine(new Vector2(12.8739996F, 3.32500005F));
builder.AddCubicBezier(new Vector2(10.5930004F, 5.66099977F), new Vector2(7.53200006F, 6.94799995F), new Vector2(4.25500011F, 6.94799995F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
CompositionColorBrush ColorBrush_AlmostDarkSlateGray_FF2D3846()
{
return _colorBrush_AlmostDarkSlateGray_FF2D3846 = _c.CreateColorBrush(Color.FromArgb(0xFF, 0x2D, 0x38, 0x46));
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
CompositionContainerShape ContainerShape()
{
var result = _c.CreateContainerShape();
result.CenterPoint = new Vector2(24F, 24F);
var shapes = result.Shapes;
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
shapes.Add(SpriteShape_0());
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
shapes.Add(SpriteShape_1());
result.StartAnimation("RotationAngleInDegrees", RotationAngleInDegreesScalarAnimation_0_to_360());
var controller = result.TryGetAnimationController("RotationAngleInDegrees");
controller.Pause();
BindProperty(controller, "Progress", "_.Progress", "_", _root);
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
CompositionPathGeometry PathGeometry_0()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_0()));
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
CompositionPathGeometry PathGeometry_1()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_1()));
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_0()
{
// Offset:<28.255, 18.903>
var geometry = PathGeometry_0();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 28.2549992F, 18.9029999F), ColorBrush_AlmostDarkSlateGray_FF2D3846());;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_1()
{
// Offset:<19.745, 29.096>
var geometry = PathGeometry_1();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 19.7450008F, 29.0960007F), _colorBrush_AlmostDarkSlateGray_FF2D3846);;
return result;
}
// The root of the composition.
ContainerVisual Root()
{
var result = _root = _c.CreateContainerVisual();
var propertySet = result.Properties;
propertySet.InsertScalar("Progress", 0F);
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
result.Children.InsertAtTop(ShapeVisual_0());
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Rotation
ScalarKeyFrameAnimation RotationAngleInDegreesScalarAnimation_0_to_360()
{
// Frame 0.
var result = CreateScalarKeyFrameAnimation(0F, 0F, HoldThenStepEasingFunction());
// Frame 61.
result.InsertKeyFrame(1F, 360F, _c.CreateCubicBezierEasingFunction(new Vector2(0.314999998F, 0F), new Vector2(0.465000004F, 0.861999989F)));
return result;
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
ShapeVisual ShapeVisual_0()
{
var result = _c.CreateShapeVisual();
result.Size = new Vector2(48F, 48F);
result.Shapes.Add(ContainerShape());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// RotationAngleInDegrees
StepEasingFunction HoldThenStepEasingFunction()
{
var result = _c.CreateStepEasingFunction();
result.IsFinalStepSingleFrame = true;
return result;
}
internal Synchronization_AnimatedVisual_UAPv7(
Compositor compositor
)
{
_c = compositor;
_reusableExpressionAnimation = compositor.CreateExpressionAnimation();
Root();
}
public Visual RootVisual => _root;
public TimeSpan Duration => TimeSpan.FromTicks(c_durationTicks);
public Vector2 Size => new Vector2(48F, 48F);
void IDisposable.Dispose() => _root?.Dispose();
internal static bool IsRuntimeCompatible()
{
return Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7);
}
}
}
}
-27
View File
@@ -1,27 +0,0 @@
<uwp:WinoApplication
x:Class="Wino.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Controls"
xmlns:selectors="using:Wino.Selectors"
xmlns:styles="using:Wino.Styles"
xmlns:uwp="using:Wino.Core.UWP"
xmlns:wino="using:Wino">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<uwp:CoreGeneric />
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
<ResourceDictionary Source="Styles/ItemContainerStyles.xaml" />
<ResourceDictionary Source="Styles/ImagePreviewControl.xaml" />
<ResourceDictionary Source="Styles/WebViewEditorControl.xaml" />
<styles:WinoExpanderStyle />
<!-- Last item must always be the default theme. -->
<ResourceDictionary Source="ms-appx:///Wino.Core.UWP/AppThemes/Mica.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</uwp:WinoApplication>
-264
View File
@@ -1,264 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Notifications;
using Microsoft.Extensions.DependencyInjection;
using Nito.AsyncEx;
using Serilog;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.UI.Core.Preview;
using Windows.UI.Notifications;
using Wino.Activation;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.UWP;
using Wino.Mail.Services;
using Wino.Mail.ViewModels;
using Wino.Messaging.Client.Connection;
using Wino.Messaging.Client.Navigation;
using Wino.Messaging.Client.Shell;
using Wino.Messaging.Server;
using Wino.Services;
namespace Wino;
public sealed partial class App : WinoApplication,
IRecipient<NewMailSynchronizationRequested>
{
private BackgroundTaskDeferral connectionBackgroundTaskDeferral;
private BackgroundTaskDeferral toastActionBackgroundTaskDeferral;
public App()
{
InitializeComponent();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
WeakReferenceMessenger.Default.Register<LanguageChanged>(this);
WeakReferenceMessenger.Default.Register<NewMailSynchronizationRequested>(this);
}
public override async void OnResuming(object sender, object e)
{
// App Service connection was lost on suspension.
// We must restore it.
// Server might be running already, but re-launching it will trigger a new connection attempt.
// Server connection is now handled by the empty implementation
// No need to reconnect after resuming
}
public override IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.RegisterViewModelService();
services.RegisterSharedServices();
services.RegisterCoreUWPServices();
services.RegisterCoreViewModels();
RegisterUWPServices(services);
RegisterViewModels(services);
RegisterActivationHandlers(services);
return services.BuildServiceProvider();
}
#region Dependency Injection
private void RegisterActivationHandlers(IServiceCollection services)
{
services.AddTransient<ProtocolActivationHandler>();
services.AddTransient<ToastNotificationActivationHandler>();
services.AddTransient<FileActivationHandler>();
}
private void RegisterUWPServices(IServiceCollection services)
{
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IMailDialogService, DialogService>();
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
services.AddTransient<IProviderService, ProviderService>();
services.AddSingleton<IAuthenticatorConfig, MailAuthenticatorConfiguration>();
}
private void RegisterViewModels(IServiceCollection services)
{
services.AddSingleton(typeof(AppShellViewModel));
services.AddTransient(typeof(MailListPageViewModel));
services.AddTransient(typeof(MailRenderingPageViewModel));
services.AddTransient(typeof(AccountManagementViewModel));
services.AddTransient(typeof(WelcomePageViewModel));
services.AddTransient(typeof(ComposePageViewModel));
services.AddTransient(typeof(IdlePageViewModel));
services.AddTransient(typeof(EditAccountDetailsPageViewModel));
services.AddTransient(typeof(AccountDetailsPageViewModel));
services.AddTransient(typeof(SignatureManagementPageViewModel));
services.AddTransient(typeof(MessageListPageViewModel));
services.AddTransient(typeof(ReadComposePanePageViewModel));
services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
services.AddTransient(typeof(LanguageTimePageViewModel));
services.AddTransient(typeof(AppPreferencesPageViewModel));
services.AddTransient(typeof(AliasManagementPageViewModel));
}
#endregion
protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails appServiceTriggerDetails)
{
LogActivation("OnBackgroundActivated -> AppServiceTriggerDetails received.");
// Only accept connections from callers in the same package
if (appServiceTriggerDetails.CallerPackageFamilyName == Package.Current.Id.FamilyName)
{
// Connection established from the fulltrust process
// This is no longer needed with the empty connection manager implementation
connectionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnConnectionBackgroundTaskCanceled;
}
}
else if (args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
{
// Notification action is triggered and the app is not running.
LogActivation("OnBackgroundActivated -> ToastNotificationActionTriggerDetail received.");
toastActionBackgroundTaskDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnToastActionClickedBackgroundTaskCanceled;
await InitializeServicesAsync();
var toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
// All toast activation mail actions are handled here like mark as read or delete.
// This should not launch the application on the foreground.
// Get the action and mail item id.
// Prepare package and send to delegator.
if (toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation action) &&
toastArguments.TryGetValue(Constants.ToastMailUniqueIdKey, out string mailUniqueIdString) &&
Guid.TryParse(mailUniqueIdString, out Guid mailUniqueId))
{
// At this point server should've already been connected.
var processor = base.Services.GetService<IWinoRequestProcessor>();
var delegator = base.Services.GetService<IWinoRequestDelegator>();
var mailService = base.Services.GetService<IMailService>();
var mailItem = await mailService.GetSingleMailItemAsync(mailUniqueId);
if (mailItem != null)
{
var package = new MailOperationPreperationRequest(action, mailItem);
await delegator.ExecuteAsync(package);
}
}
toastActionBackgroundTaskDeferral.Complete();
}
else
{
// Other background activations might have handlers.
// AppServiceTrigger is handled here because delegating it to handlers somehow make it not work...
await ActivateWinoAsync(args);
}
}
private void OnToastActionClickedBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
sender.Canceled -= OnToastActionClickedBackgroundTaskCanceled;
Log.Information($"Toast action background task was canceled. Reason: {reason}");
toastActionBackgroundTaskDeferral?.Complete();
toastActionBackgroundTaskDeferral = null;
}
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
{
yield return Services.GetService<ProtocolActivationHandler>();
yield return Services.GetService<ToastNotificationActivationHandler>();
yield return Services.GetService<FileActivationHandler>();
}
public void OnConnectionBackgroundTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
sender.Canceled -= OnConnectionBackgroundTaskCanceled;
Log.Information($"Server connection background task was canceled. Reason: {reason}");
connectionBackgroundTaskDeferral?.Complete();
connectionBackgroundTaskDeferral = null;
}
public async void Receive(NewMailSynchronizationRequested message)
{
// Synchronization is now handled elsewhere
// The empty connection manager doesn't perform actual sync operations
await Task.CompletedTask;
}
protected override async void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
Log.Information("App close requested.");
var deferral = e.GetDeferral();
// Wino should notify user on app close if:
// 1. Startup behavior is not Enabled.
// 2. Server terminate behavior is set to Terminate.
// User has some accounts. Check if Wino Server runs on system startup.
var dialogService = base.Services.GetService<IMailDialogService>();
var startupBehaviorService = base.Services.GetService<IStartupBehaviorService>();
var preferencesService = base.Services.GetService<IPreferencesService>();
var currentStartupBehavior = await startupBehaviorService.GetCurrentStartupBehaviorAsync();
bool? isGoToAppPreferencesRequested = null;
if (currentStartupBehavior != StartupBehaviorResult.Enabled)
{
// Startup behavior is not enabled.
isGoToAppPreferencesRequested = await dialogService.ShowWinoCustomMessageDialogAsync(Translator.AppCloseBackgroundSynchronizationWarningTitle,
$"{Translator.AppCloseStartupLaunchDisabledWarningMessageFirstLine}\n{Translator.AppCloseStartupLaunchDisabledWarningMessageSecondLine}\n\n{Translator.AppCloseStartupLaunchDisabledWarningMessageThirdLine}",
Translator.Buttons_Yes,
WinoCustomMessageDialogIcon.Warning,
Translator.Buttons_No,
"DontAskDisabledStartup");
}
if (isGoToAppPreferencesRequested == true)
{
WeakReferenceMessenger.Default.Send(new NavigateAppPreferencesRequested());
e.Handled = true;
}
deferral.Complete();
}
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
=> new DefaultActivationHandler();
}
-470
View File
@@ -1,470 +0,0 @@
<abstract:AppShellAbstract
x:Class="Wino.Views.AppShell"
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.Controls.Advanced"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:controls="using:Wino.Controls"
xmlns:controls1="using:CommunityToolkit.WinUI.Controls"
xmlns:coreControls="using:Wino.Core.UWP.Controls"
xmlns:coreConverters="using:Wino.Core.UWP.Converters"
xmlns:coreSelectors="using:Wino.Core.UWP.Selectors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:enums="using:Wino.Core.Domain.Enums"
xmlns:helpers="using:Wino.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:menu="using:Wino.Core.Domain.MenuItems"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
x:Name="Root"
mc:Ignorable="d">
<Page.Resources>
<coreConverters:HexToColorBrushConverter x:Key="HexToColorBrushConverter" />
<!-- Clickable New Style Account Template -->
<DataTemplate x:Key="ClickableAccountMenuTemplate" x:DataType="menu:AccountMenuItem">
<controls:AccountNavigationItem
x:Name="AccountItem"
Height="50"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
BindingData="{x:Bind}"
DataContext="{x:Bind}"
IsActiveAccount="{x:Bind IsSelected, Mode=OneWay}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}"
SelectsOnInvoked="False"
Style="{StaticResource SingleAccountNavigationViewItemTemplate}">
<coreControls:WinoNavigationViewItem.ContentTransitions>
<TransitionCollection>
<EdgeUIThemeTransition Edge="Top" />
</TransitionCollection>
</coreControls:WinoNavigationViewItem.ContentTransitions>
<muxc:NavigationViewItem.Icon>
<coreControls:WinoFontIcon
FontSize="12"
Foreground="{x:Bind AccountColorHex, Converter={StaticResource HexToColorBrushConverter}, Mode=OneWay}"
Icon="{x:Bind helpers:XamlHelpers.GetProviderIcon(Parameter)}" />
</muxc:NavigationViewItem.Icon>
<muxc:NavigationViewItem.InfoBadge>
<muxc:InfoBadge
Background="{ThemeResource SystemAccentColor}"
Foreground="White"
Visibility="{x:Bind helpers:XamlHelpers.CountToVisibilityConverter(UnreadItemCount), Mode=OneWay}"
Value="{x:Bind UnreadItemCount, Mode=OneWay}" />
</muxc:NavigationViewItem.InfoBadge>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<TextBlock
x:Name="AccountNameTextblock"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByChildSelectedState(IsSelected), Mode=OneWay}"
MaxLines="1"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind AccountName, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
MaxLines="1"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Parameter.Address, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
<PathIcon
x:Name="AttentionIcon"
Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="{x:Bind IsAttentionRequired, Mode=OneWay}"
Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z "
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" />
<muxc:ProgressRing
x:Name="SynchronizationProgressBar"
Grid.ColumnSpan="3"
Width="10"
Height="10"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Background="{ThemeResource AppBarItemBackgroundThemeBrush}"
Foreground="{ThemeResource AppBarItemForegroundThemeBrush}"
IsActive="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}"
IsIndeterminate="{x:Bind IsProgressIndeterminate}"
Maximum="100"
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}"
Value="{x:Bind SynchronizationProgress, Mode=OneWay}" />
</Grid>
</controls:AccountNavigationItem>
</DataTemplate>
<!-- Fix account issues. -->
<!-- Authentication -->
<DataTemplate x:Key="FixAuthenticationIssueTemplate" x:DataType="menu:FixAccountIssuesMenuItem">
<coreControls:WinoNavigationViewItem SelectsOnInvoked="False">
<coreControls:WinoNavigationViewItem.Content>
<TextBlock
Margin="0,10"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
HorizontalTextAlignment="Center"
TextWrapping="WrapWholeWords">
<Run Text="Account credentials can not be verified." /><LineBreak /><Run Text="Click here to fix it." />
</TextBlock>
</coreControls:WinoNavigationViewItem.Content>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- Missing system folder config. -->
<DataTemplate x:Key="FixMissingFolderConfig" x:DataType="menu:FixAccountIssuesMenuItem">
<coreControls:WinoNavigationViewItem SelectsOnInvoked="False">
<coreControls:WinoNavigationViewItem.Content>
<TextBlock
Margin="0,10"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
HorizontalTextAlignment="Center"
TextWrapping="WrapWholeWords">
<Run Text="Account is missing system folder configuration." /><LineBreak /><LineBreak /><Run Text="Click here to fix it." />
</TextBlock>
</coreControls:WinoNavigationViewItem.Content>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- New Mail -->
<DataTemplate x:Key="CreateNewMailTemplate" x:DataType="menu:NewMailMenuItem">
<coreControls:WinoNavigationViewItem
Height="50"
DataContext="{x:Bind}"
SelectsOnInvoked="False">
<muxc:NavigationViewItem.Icon>
<coreControls:WinoFontIcon Icon="NewMail" />
</muxc:NavigationViewItem.Icon>
<TextBlock
VerticalAlignment="Center"
FontSize="16"
Style="{StaticResource FlyoutPickerTitleTextBlockStyle}"
Text="{x:Bind domain:Translator.MenuNewMail}" />
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- Inbox or any other folders. -->
<DataTemplate x:Key="FolderMenuTemplate" x:DataType="menu:FolderMenuItem">
<coreControls:WinoNavigationViewItem
MinHeight="40"
AllowDrop="True"
ContextRequested="MenuItemContextRequested"
DataContext="{x:Bind}"
DragEnter="ItemDragEnterOnFolder"
DragLeave="ItemDragLeaveFromFolder"
Drop="ItemDroppedOnFolder"
FontSize="50"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByChildSelectedState(IsSelected), Mode=OneWay}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}"
IsSelected="{x:Bind IsSelected, Mode=TwoWay}"
MenuItemsSource="{x:Bind SubMenuItems, Mode=OneWay}"
SelectsOnInvoked="{x:Bind IsMoveTarget, Mode=OneWay}"
ToolTipService.ToolTip="{x:Bind FolderName, Mode=OneWay}">
<animations:Implicit.Animations>
<animations:ScaleAnimation Duration="0:0:0.5" />
</animations:Implicit.Animations>
<coreControls:WinoNavigationViewItem.Icon>
<coreControls:WinoFontIcon FontSize="64" Icon="{x:Bind helpers:XamlHelpers.GetSpecialFolderPathIconGeometry(Parameter.SpecialFolderType)}" />
</coreControls:WinoNavigationViewItem.Icon>
<muxc:NavigationViewItem.InfoBadge>
<muxc:InfoBadge
x:Name="FolderInfoBadge"
Background="{StaticResource SystemAccentColor}"
Foreground="White"
Visibility="{x:Bind helpers:XamlHelpers.CountToVisibilityConverter(UnreadItemCount), Mode=OneWay}"
Value="{x:Bind UnreadItemCount, Mode=OneWay}" />
</muxc:NavigationViewItem.InfoBadge>
<muxc:NavigationViewItem.Content>
<Grid
x:Name="FolderBackgroundGrid"
MaxHeight="36"
Padding="2"
VerticalAlignment="Center">
<Grid
x:Name="BackgroundColorGrid"
x:Load="{x:Bind HasTextColor, Mode=OneWay}"
Background="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(Parameter.BackgroundColorHex), Mode=OneWay}"
CornerRadius="3">
<TextBlock
x:Name="CustomColorTitle"
Margin="4,0,0,0"
VerticalAlignment="Center"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightBySyncState(IsSelected), Mode=OneWay}"
Foreground="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(Parameter.TextColorHex), Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind FolderName, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</Grid>
<TextBlock
x:Name="NormalTitle"
VerticalAlignment="Center"
x:Load="{x:Bind HasTextColor, Converter={StaticResource ReverseBooleanConverter}}"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightBySyncState(IsSelected), Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind FolderName, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</Grid>
</muxc:NavigationViewItem.Content>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- Merged Inbox -->
<DataTemplate x:Key="MergedAccountTemplate" x:DataType="menu:MergedAccountMenuItem">
<controls:AccountNavigationItem
x:Name="AccountItem"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
BindingData="{x:Bind}"
DataContext="{x:Bind}"
IsActiveAccount="{x:Bind IsSelected, Mode=TwoWay}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}"
MenuItemsSource="{x:Bind SubMenuItems}"
SelectsOnInvoked="False"
Style="{StaticResource SingleAccountNavigationViewItemTemplate}">
<muxc:NavigationViewItem.InfoBadge>
<muxc:InfoBadge
x:Name="FolderInfoBadge"
Background="{StaticResource SystemAccentColor}"
Foreground="White"
Visibility="{x:Bind helpers:XamlHelpers.CountToVisibilityConverter(UnreadItemCount), Mode=OneWay}"
Value="{x:Bind UnreadItemCount, Mode=OneWay}" />
</muxc:NavigationViewItem.InfoBadge>
<coreControls:WinoNavigationViewItem.ContentTransitions>
<TransitionCollection>
<EdgeUIThemeTransition Edge="Top" />
</TransitionCollection>
</coreControls:WinoNavigationViewItem.ContentTransitions>
<coreControls:WinoNavigationViewItem.Icon>
<PathIcon Data="F1 M 8.613281 17.5 C 8.75 17.942709 8.945312 18.359375 9.199219 18.75 L 4.921875 18.75 C 4.433594 18.75 3.966471 18.650717 3.520508 18.452148 C 3.074544 18.25358 2.683919 17.986654 2.348633 17.651367 C 2.013346 17.31608 1.746419 16.925455 1.547852 16.479492 C 1.349284 16.033529 1.25 15.566406 1.25 15.078125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 15.078125 1.25 C 15.566406 1.25 16.033527 1.349285 16.479492 1.547852 C 16.925455 1.74642 17.31608 2.013348 17.651367 2.348633 C 17.986652 2.68392 18.25358 3.074545 18.452148 3.520508 C 18.650715 3.966473 18.75 4.433594 18.75 4.921875 L 18.75 6.572266 C 18.580729 6.344402 18.390299 6.132813 18.178711 5.9375 C 17.967121 5.742188 17.740885 5.566407 17.5 5.410156 L 17.5 4.951172 C 17.5 4.625651 17.433268 4.314779 17.299805 4.018555 C 17.16634 3.722332 16.987305 3.461914 16.762695 3.237305 C 16.538086 3.012695 16.277668 2.83366 15.981445 2.700195 C 15.685221 2.566732 15.374349 2.5 15.048828 2.5 L 4.951172 2.5 C 4.619141 2.5 4.303385 2.568359 4.003906 2.705078 C 3.704427 2.841797 3.44401 3.02409 3.222656 3.251953 C 3.001302 3.479818 2.825521 3.745117 2.695312 4.047852 C 2.565104 4.350587 2.5 4.66797 2.5 5 L 13.310547 5 C 12.60091 5.266928 11.998697 5.683594 11.503906 6.25 L 2.5 6.25 L 2.5 15.048828 C 2.5 15.38737 2.568359 15.704753 2.705078 16.000977 C 2.841797 16.297201 3.024088 16.55599 3.251953 16.777344 C 3.479818 16.998697 3.745117 17.174479 4.047852 17.304688 C 4.350586 17.434896 4.667969 17.5 5 17.5 Z" />
</coreControls:WinoNavigationViewItem.Icon>
<Grid Height="50">
<StackPanel VerticalAlignment="Center" Spacing="0">
<TextBlock
x:Name="AccountNameTextblock"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByChildSelectedState(IsChildSelected), Mode=OneWay}"
MaxLines="1"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind MergedAccountName, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<TextBlock
FontSize="12"
MaxLines="1"
Style="{StaticResource CaptionTextBlockStyle}"
TextTrimming="CharacterEllipsis">
<Run Text="{x:Bind MergedAccountCount}" /><Run Text="{x:Bind domain:Translator.MenuMergedAccountItemAccountsSuffix}" />
</TextBlock>
</StackPanel>
</Grid>
</controls:AccountNavigationItem>
</DataTemplate>
<!-- Merged Account Common Folder Item -->
<DataTemplate x:Key="MergedAccountFolderMenuItemTemplate" x:DataType="menu:MergedAccountFolderMenuItem">
<coreControls:WinoNavigationViewItem
MinHeight="30"
AllowDrop="True"
ContextRequested="MenuItemContextRequested"
DataContext="{x:Bind}"
DragEnter="ItemDragEnterOnFolder"
DragLeave="ItemDragLeaveFromFolder"
Drop="ItemDroppedOnFolder"
FontSize="50"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByChildSelectedState(IsSelected), Mode=OneWay}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}"
IsSelected="{x:Bind IsSelected, Mode=TwoWay}"
SelectsOnInvoked="True"
ToolTipService.ToolTip="{x:Bind FolderName, Mode=OneWay}">
<animations:Implicit.Animations>
<animations:ScaleAnimation Duration="0:0:0.5" />
</animations:Implicit.Animations>
<coreControls:WinoNavigationViewItem.Icon>
<coreControls:WinoFontIcon FontSize="64" Icon="{x:Bind helpers:XamlHelpers.GetSpecialFolderPathIconGeometry(FolderType)}" />
</coreControls:WinoNavigationViewItem.Icon>
<muxc:NavigationViewItem.InfoBadge>
<muxc:InfoBadge
x:Name="FolderInfoBadge"
Background="{StaticResource SystemAccentColor}"
Foreground="White"
Visibility="{x:Bind helpers:XamlHelpers.CountToVisibilityConverter(UnreadItemCount), Mode=OneWay}"
Value="{x:Bind UnreadItemCount, Mode=OneWay}" />
</muxc:NavigationViewItem.InfoBadge>
<Grid
x:Name="FolderBackgroundGrid"
Padding="2"
VerticalAlignment="Center">
<TextBlock
x:Name="NormalTitle"
VerticalAlignment="Center"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightBySyncState(IsSelected), Mode=OneWay}"
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind FolderName, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</Grid>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<!-- Merged Account More Expansion Item -->
<DataTemplate x:Key="MergedAccountMoreFolderItemTemplate" x:DataType="menu:MergedAccountMoreFolderMenuItem">
<coreControls:WinoNavigationViewItem
MinHeight="30"
Content="{x:Bind domain:Translator.More}"
FontWeight="{x:Bind helpers:XamlHelpers.GetFontWeightByChildSelectedState(IsSelected), Mode=OneWay}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}"
IsSelected="{x:Bind IsSelected, Mode=TwoWay}"
MenuItemsSource="{x:Bind SubMenuItems, Mode=OneWay}"
SelectsOnInvoked="True">
<animations:Implicit.Animations>
<animations:ScaleAnimation Duration="0:0:0.5" />
</animations:Implicit.Animations>
<coreControls:WinoNavigationViewItem.Icon>
<coreControls:WinoFontIcon FontSize="64" Icon="{x:Bind helpers:XamlHelpers.GetSpecialFolderPathIconGeometry(enums:SpecialFolderType.More)}" />
</coreControls:WinoNavigationViewItem.Icon>
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<coreSelectors:NavigationMenuTemplateSelector
x:Key="NavigationMenuTemplateSelector"
AccountManagementTemplate="{StaticResource ManageAccountsTemplate}"
ClickableAccountMenuTemplate="{StaticResource ClickableAccountMenuTemplate}"
FixAuthenticationIssueTemplate="{StaticResource FixAuthenticationIssueTemplate}"
FixMissingFolderConfigTemplate="{StaticResource FixMissingFolderConfig}"
FolderMenuTemplate="{StaticResource FolderMenuTemplate}"
MergedAccountFolderTemplate="{StaticResource MergedAccountFolderMenuItemTemplate}"
MergedAccountMoreExpansionItemTemplate="{StaticResource MergedAccountMoreFolderItemTemplate}"
MergedAccountTemplate="{StaticResource MergedAccountTemplate}"
NewMailTemplate="{StaticResource CreateNewMailTemplate}"
RatingItemTemplate="{StaticResource RatingItemTemplate}"
SeperatorTemplate="{StaticResource SeperatorTemplate}"
SettingsItemTemplate="{StaticResource SettingsItemTemplate}" />
</Page.Resources>
<Grid
x:Name="RootGrid"
Padding="0"
ColumnSpacing="0"
RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Background="{ThemeResource WinoApplicationBackgroundColor}"
IsHitTestVisible="False" />
<muxc:NavigationView
x:Name="navigationView"
Grid.Row="1"
Grid.ColumnSpan="3"
Margin="-1,-1,0,0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
AlwaysShowHeader="True"
DisplayModeChanged="NavigationViewDisplayModeChanged"
FooterMenuItemsSource="{x:Bind ViewModel.FooterItems, Mode=OneWay}"
IsBackButtonVisible="Collapsed"
IsPaneOpen="{x:Bind ViewModel.PreferencesService.IsNavigationPaneOpened, Mode=TwoWay}"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
IsTabStop="True"
IsTitleBarAutoPaddingEnabled="False"
ItemInvoked="NavigationViewItemInvoked"
MenuItemTemplateSelector="{StaticResource NavigationMenuTemplateSelector}"
MenuItemsSource="{x:Bind ViewModel.MenuItems, Mode=OneWay}"
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=TwoWay}"
PaneDisplayMode="Auto"
PaneOpening="NavigationPaneOpening"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
SelectedItem="{x:Bind ViewModel.SelectedMenuItem, Mode=TwoWay}"
SelectionChanged="MenuSelectionChanged">
<muxc:NavigationView.ContentTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</muxc:NavigationView.ContentTransitions>
<Grid ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls1:PropertySizer
Width="1"
HorizontalAlignment="Left"
Background="Transparent"
Binding="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=TwoWay}"
Canvas.ZIndex="20"
Foreground="Transparent"
IsHitTestVisible="{x:Bind navigationView.IsPaneOpen, Mode=OneWay}"
IsTabStop="False"
Maximum="1000"
Minimum="116" />
<!-- Main Content -->
<Frame
x:Name="ShellFrame"
Padding="0,0,7,7"
IsNavigationStackEnabled="False"
Navigated="ShellFrameContentNavigated">
<Frame.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
<!-- InfoBar -->
<coreControls:WinoInfoBar
x:Name="ShellInfoBar"
MaxWidth="700"
Margin="0,60,25,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
IsClosable="False"
IsOpen="False" />
<!-- Teaching Tip -->
<muxc:TeachingTip
x:Name="ShellTip"
IsOpen="False"
PreferredPlacement="Bottom"
Target="{x:Bind ShellInfoBar}" />
</Grid>
</muxc:NavigationView>
<coreControls:WinoAppTitleBar
x:Name="RealAppBar"
Grid.ColumnSpan="2"
BackButtonClicked="BackButtonClicked"
Canvas.ZIndex="150"
CoreWindowText="{x:Bind ViewModel.StatePersistenceService.CoreWindowTitle, Mode=OneWay}"
IsBackButtonVisible="{x:Bind ViewModel.StatePersistenceService.IsBackButtonVisible, Mode=OneWay}"
IsDragArea="True"
IsNavigationPaneOpen="{x:Bind navigationView.IsPaneOpen, Mode=TwoWay}"
IsReaderNarrowed="{x:Bind ViewModel.StatePersistenceService.IsReaderNarrowed, Mode=OneWay}"
NavigationViewDisplayMode="{x:Bind navigationView.DisplayMode, Mode=OneWay}"
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=OneWay}"
ReadingPaneLength="{x:Bind ViewModel.StatePersistenceService.MailListPaneLength, Mode=OneWay}"
SystemReserved="180" />
</Grid>
</abstract:AppShellAbstract>
-355
View File
@@ -1,355 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.UWP;
using Wino.Core.UWP.Controls;
using Wino.Extensions;
using Wino.Mail.ViewModels.Data;
using Wino.MenuFlyouts;
using Wino.MenuFlyouts.Context;
using Wino.Messaging.Client.Accounts;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
using Wino.Views.Abstract;
namespace Wino.Views;
public sealed partial class AppShell : AppShellAbstract,
IRecipient<AccountMenuItemExtended>,
IRecipient<NavigateMailFolderEvent>,
IRecipient<CreateNewMailWithMultipleAccountsRequested>,
IRecipient<InfoBarMessageRequested>
{
public AppShell() : base()
{
InitializeComponent();
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.LayoutMetricsChanged += TitleBarLayoutUpdated;
}
private void TitleBarLayoutUpdated(CoreApplicationViewTitleBar sender, object args) => UpdateTitleBarLayout(sender);
private void UpdateTitleBarLayout(CoreApplicationViewTitleBar coreTitleBar) => RealAppBar.SystemReserved = coreTitleBar.SystemOverlayRightInset;
private async void ItemDroppedOnFolder(object sender, DragEventArgs e)
{
// Validate package content.
if (sender is WinoNavigationViewItem droppedContainer)
{
droppedContainer.IsDraggingItemOver = false;
if (CanContinueDragDrop(droppedContainer, e))
{
if (droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
{
var mailCopies = new List<MailCopy>();
var dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
// Extract mail copies from IMailItem.
// ThreadViewModels will be divided into pieces.
foreach (var item in dragPackage.DraggingMails)
{
if (item is MailItemViewModel singleMailItemViewModel)
{
mailCopies.Add(singleMailItemViewModel.MailCopy);
}
else if (item is ThreadMailItemViewModel threadViewModel)
{
mailCopies.AddRange(threadViewModel.GetMailCopies());
}
}
await ViewModel.PerformMoveOperationAsync(mailCopies, draggingFolder);
}
}
}
}
private void ItemDragLeaveFromFolder(object sender, DragEventArgs e)
{
if (sender is WinoNavigationViewItem leavingContainer)
{
leavingContainer.IsDraggingItemOver = false;
}
}
private bool CanContinueDragDrop(WinoNavigationViewItem interactingContainer, DragEventArgs args)
{
// TODO: Maybe override caption with some information why the validation failed?
// Note: Caption has a max length. It may be trimmed in some languages.
if (interactingContainer == null || !args.DataView.Properties.ContainsKey(nameof(MailDragPackage))) return false;
var dragPackage = args.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
// Invalid package.
if (!dragPackage.DraggingMails.Any()) return false;
// Check whether source and target folder are the same.
if (interactingContainer.IsSelected) return false;
// Check if the interacting container is a folder.
if (!(interactingContainer.DataContext is IBaseFolderMenuItem folderMenuItem)) return false;
// Check if the folder is a move target.
if (!folderMenuItem.IsMoveTarget) return false;
// Check whether the moving item's account has at least one same as the target folder's account.
var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId);
if (!dragPackage.DraggingMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false;
return true;
}
private void ItemDragEnterOnFolder(object sender, DragEventArgs e)
{
// Validate package content.
if (sender is WinoNavigationViewItem droppedContainer && CanContinueDragDrop(droppedContainer, e))
{
droppedContainer.IsDraggingItemOver = true;
var draggingFolder = droppedContainer.DataContext as IBaseFolderMenuItem;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName);
}
}
public async void Receive(AccountMenuItemExtended message)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () =>
{
if (message.FolderId == default) return;
if (ViewModel.MenuItems.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem foundMenuItem))
{
foundMenuItem.Expand();
await ViewModel.NavigateFolderAsync(foundMenuItem);
navigationView.SelectedItem = foundMenuItem;
if (message.NavigateMailItem == null) return;
// At this point folder is navigated and items are loaded.
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
}
else if (ViewModel.MenuItems.TryGetAccountMenuItem(message.NavigateMailItem.AssignedAccount.Id, out IAccountMenuItem accountMenuItem))
{
// Loaded account is different. First change the folder items and navigate.
await ViewModel.ChangeLoadedAccountAsync(accountMenuItem, navigateInbox: false);
// Find the folder.
if (ViewModel.MenuItems.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem accountFolderMenuItem))
{
accountFolderMenuItem.Expand();
await ViewModel.NavigateFolderAsync(accountFolderMenuItem);
navigationView.SelectedItem = accountFolderMenuItem;
// At this point folder is navigated and items are loaded.
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
}
}
});
}
private async void MenuSelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is IMenuItem invokedMenuItem)
{
await ViewModel.MenuItemInvokedOrSelectedAsync(invokedMenuItem);
}
}
private async void NavigationViewItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args)
{
// SelectsOnInvoked is handled in MenuSelectionChanged.
// This part is only for the items that are not selectable.
if (args.InvokedItemContainer is WinoNavigationViewItem winoNavigationViewItem)
{
if (winoNavigationViewItem.SelectsOnInvoked) return;
await ViewModel.MenuItemInvokedOrSelectedAsync(winoNavigationViewItem.DataContext as IMenuItem);
}
}
public void Receive(NavigateMailFolderEvent message)
{
if (message.BaseFolderMenuItem == null) return;
if (navigationView.SelectedItem != message.BaseFolderMenuItem)
{
var navigateFolderArgs = new NavigateMailFolderEventArgs(message.BaseFolderMenuItem, message.FolderInitLoadAwaitTask);
ViewModel.NavigationService.Navigate(WinoPage.MailListPage, navigateFolderArgs, NavigationReferenceFrame.ShellFrame);
// Prevent double navigation.
navigationView.SelectionChanged -= MenuSelectionChanged;
navigationView.SelectedItem = message.BaseFolderMenuItem;
navigationView.SelectionChanged += MenuSelectionChanged;
}
else
{
// Complete the init task since we are already on the right page.
message.FolderInitLoadAwaitTask?.TrySetResult(true);
}
}
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
=> RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
private void BackButtonClicked(WinoAppTitleBar sender, RoutedEventArgs args)
{
WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested());
WeakReferenceMessenger.Default.Send(new DisposeRenderingFrameRequested());
}
private async void MenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
// Delegate this request to ViewModel.
// VM will prepare available actions for this folder and show Menu Flyout.
if (sender is WinoNavigationViewItem menuItem &&
menuItem.DataContext is IBaseFolderMenuItem baseFolderMenuItem &&
baseFolderMenuItem.IsMoveTarget &&
args.TryGetPosition(sender, out Point p))
{
args.Handled = true;
var source = new TaskCompletionSource<FolderOperationMenuItem>();
var actions = ViewModel.GetFolderContextMenuActions(baseFolderMenuItem);
var flyout = new FolderOperationFlyout(actions, source);
flyout.ShowAt(menuItem, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Standard,
Position = new Point(p.X + 30, p.Y - 20)
});
var operation = await source.Task;
flyout.Dispose();
// No action selected.
if (operation == null) return;
await ViewModel.PerformFolderOperationAsync(operation.Operation, baseFolderMenuItem);
}
}
public void Receive(CreateNewMailWithMultipleAccountsRequested message)
{
// Find the NewMail menu item container.
var container = navigationView.ContainerFromMenuItem(ViewModel.CreateMailMenuItem);
var flyout = new AccountSelectorFlyout(message.AllAccounts, ViewModel.CreateNewMailForAsync);
flyout.ShowAt(container, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Auto,
Placement = FlyoutPlacementMode.Right
});
}
private void NavigationPaneOpening(Microsoft.UI.Xaml.Controls.NavigationView sender, object args)
{
// It's annoying that NavigationView doesn't respect expansion state of the items in Minimal display mode.
// Expanded items are collaped, and users need to expand them again.
// Regardless of the reason, we will expand the selected item if it's a folder with parent account for visibility.
if (sender.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal && sender.SelectedItem is IFolderMenuItem selectedFolderMenuItem)
{
selectedFolderMenuItem.Expand();
}
}
/// <summary>
/// InfoBar message is requested.
/// </summary>
public async void Receive(InfoBarMessageRequested message)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (string.IsNullOrEmpty(message.ActionButtonTitle) || message.Action == null)
{
ShellInfoBar.ActionButton = null;
}
else
{
ShellInfoBar.ActionButton = new Button()
{
Content = message.ActionButtonTitle,
Command = new RelayCommand(message.Action)
};
}
ShellInfoBar.Message = message.Message;
ShellInfoBar.Title = message.Title;
ShellInfoBar.Severity = message.Severity.AsMUXCInfoBarSeverity();
ShellInfoBar.IsOpen = true;
});
}
private void NavigationViewDisplayModeChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal)
{
ShellFrame.Margin = new Thickness(7, 0, 0, 0);
}
else
{
ShellFrame.Margin = new Thickness(0);
}
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
WeakReferenceMessenger.Default.Register<InfoBarMessageRequested>(this);
WeakReferenceMessenger.Default.Register<AccountMenuItemExtended>(this);
WeakReferenceMessenger.Default.Register<CreateNewMailWithMultipleAccountsRequested>(this);
WeakReferenceMessenger.Default.Register<NavigateMailFolderEvent>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
WeakReferenceMessenger.Default.Unregister<InfoBarMessageRequested>(this);
WeakReferenceMessenger.Default.Unregister<AccountMenuItemExtended>(this);
WeakReferenceMessenger.Default.Unregister<CreateNewMailWithMultipleAccountsRequested>(this);
WeakReferenceMessenger.Default.Unregister<NavigateMailFolderEvent>(this);
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

-71
View File
@@ -1,71 +0,0 @@
# 🚀 Welcome to Wino Mail v1.10.2
It is time for another pack of improvements for Wino Mail! Checkout the latest updates we packaged for this version:
## .NET 9 Upgrade
For most of you, this may not bring significant improvements, but our underlying (and outdated) technology stack moves a step further with this change. We got rid of the outdated technologies for the codebase to bring significant updates easier later on.
If you are a developer and want to learn about how .NET9 affects the UWP applications, checkout [this blog post](https://devblogs.microsoft.com/ifdef-windows/preview-uwp-support-for-dotnet-9-native-aot/) to learn more about our upgrade journey. Native AOT is still in progress and will be available in later updates.
## Account Colors and Edit Account Details
In older versions, you were only able to rename your account name in account details. Now there is a new page where you can edit account name, sender name and assign a new account color to highlight account icon on the side bar menu.
![Account Colors](https://www.winomail.app/blog-resources/release-1-10-2/account_colors2.webp)
For IMAP accounts, you can change your configuration as well.
![Account Details Edit](https://www.winomail.app/blog-resources/release-1-10-2/account_colors1.webp)
## Online Search
One of the common issues users report as a feedback is that they can't find the mail they are looking for. Main problem for this issue is that Wino Mail does not synchronize all your mails (at least for some mail providers like Outlook) for performance reasons. Search was performed for the downloaded mails locally in offline mode.
Just like how old Windows Mail used to work, Wino Mail can now perform provider specific queries to perform an online search and download the missing mails. If you can't find what you are looking for you can always do an online search using this button under the search results.
![Online Search Button](https://www.winomail.app/blog-resources/release-1-10-2/online_search_1.webp)
Gmail has it's own query language to perform the filter such as "label:UNREAD". This is also supported. You can use the search just like in Gmail Web UI and get the mails you need.
Default search mode is still Local, meaning that all searches will be performed in offline mode and the button will be visible. If you want to switch the default search mode to Online; go to Settings -> App Preferences -> Default Search Mode to change it
![Default Search Mode](https://www.winomail.app/blog-resources/release-1-10-2/online_search_2.webp)
## Live changes for IMAP and stability improvements.
For IMAP servers that support [IDLE command](https://datatracker.ietf.org/doc/html/rfc2177.html) live changes to Inbox folder will be listened with minimum effort. This means that whenever you recieve a mail or some flag (read/unread etc.) changes in your Inbox folder, changes are immidiately reflected to Wino without requiring a synchronization.
On top of that; this update brings significant stability and performance improvements to all IMAP servers. We have reworked our IMAP synchronizers to be more resource efficient and performant.
## iCloud and Yahoo on setup dialog
Account setup dialog is more streamlined in this version. Now it supports iCloud and Yahoo; with an additional helping links to create app-specific password to login with Wino. This is the first effort to make setting up accounts easier for users and there will be more in the future.
![New IMAP Providers](https://www.winomail.app/blog-resources/release-1-10-2/imap_providers.webp)
## Gmail Archive Functionality
In reality, Archive folder is a virtual folder in Gmail that doesn't exist. For Gmail, archiving means 'it doesn't belong to Inbox' or putting in words as Google "something that does not have Inbox label". Due to limitations in Wino Mail's architecture and lack of support in Gmail API; Archive functionality used to work by moving the mails you marked as 'archived' to your special archive folder you have configured in account settings. This behavior is no longer exists and archiving/unarchiving will work as it is in Gmail web UI.
Starting from this version you will be able to see "Archive" folder for your Gmail accounts and all your archived mails will be synchronized on your next sync. (If you don't see the folder, relaunch the app after doing a synchronization.)
![Archive folder for Gmail](https://www.winomail.app/blog-resources/release-1-10-2/gmail_archive.webp)
## Additional Bugfixes and Improvements
As always, this major release has a lot of overall bugfixes for the application.
* Fixed 410 GONE error for Outlook.
* Fixed 404 NOT FOUND error for Gmail. If your local cache is expired and Wino can't store the state of your mails, it will re-synchronize everything to keep state healthy.
* AppCenter SDK is removed. Logging and analytics are migrated to Azure App Insights.
* Diagnostic ID is implemented to track user errors better. If you had an error in the app and share the Diagnostic Id under Settings -> About -> Diagnostics, we can easily track down the issue you had to provide better help.
* Fixed the issue with dates and times are not translated properly to selected application language.
* Prevented moving mails in linked accounts when users try to move multiple mails that belong to different accounts.
* Implemented IMAP setup dialog validations to notify users during IMAP account creation for missing fields.
* Displaying "You" for received mails instead of your full mail address by @Tiktack in https://github.com/bkaankose/Wino-Mail/pull/566
* Automatically saving drafts on app close to prevent data loss by @Tiktack in https://github.com/bkaankose/Wino-Mail/pull/546
* Displaying full message (MIME) source by @Tiktack in https://github.com/bkaankose/Wino-Mail/pull/541
* Clearing rendered text selection when changing mails by @Tiktack in https://github.com/bkaankose/Wino-Mail/pull/543
* Creating a shared web editor component by @Tiktack in https://github.com/bkaankose/Wino-Mail/pull/578
* Clickable plaintext links and fixes to dark mode by @KamilDev in https://github.com/bkaankose/Wino-Mail/pull/488
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

@@ -1,198 +0,0 @@
using System.Collections;
using System.Collections.Specialized;
using System.Windows.Input;
using CommunityToolkit.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Menus;
using Wino.Core.UWP.Controls;
using Wino.Helpers;
namespace Wino.Behaviors;
public partial class BindableCommandBarBehavior : Behavior<CommandBar>
{
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>();
public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
"PrimaryCommands", typeof(object), typeof(BindableCommandBarBehavior),
new PropertyMetadata(null, UpdateCommands));
[GeneratedDependencyProperty]
public partial ICommand ItemClickedCommand { get; set; }
public object PrimaryCommands
{
get { return GetValue(PrimaryCommandsProperty); }
set { SetValue(PrimaryCommandsProperty, value); }
}
protected override void OnDetaching()
{
base.OnDetaching();
AttachChanges(false);
if (PrimaryCommands is IEnumerable enumerable)
{
foreach (var item in enumerable)
{
if (item is ButtonBase button)
{
button.Click -= Button_Click;
}
}
}
}
private void UpdatePrimaryCommands()
{
if (AssociatedObject == null)
return;
if (PrimaryCommands == null)
return;
if (AssociatedObject.PrimaryCommands is IEnumerable enumerableObjects)
{
foreach (var item in enumerableObjects)
{
if (item is ButtonBase button)
{
button.Click -= Button_Click;
}
}
}
if (AssociatedObject.SecondaryCommands is IEnumerable secondaryObject)
{
foreach (var item in secondaryObject)
{
if (item is ButtonBase button)
{
button.Click -= Button_Click;
}
}
}
AssociatedObject.PrimaryCommands.Clear();
AssociatedObject.SecondaryCommands.Clear();
if (PrimaryCommands is not IEnumerable enumerable) return;
foreach (var command in enumerable)
{
if (command is MailOperationMenuItem mailOperationMenuItem)
{
ICommandBarElement menuItem = null;
if (mailOperationMenuItem.Operation == Core.Domain.Enums.MailOperation.Seperator)
{
menuItem = new AppBarSeparator();
}
else
{
var label = XamlHelpers.GetOperationString(mailOperationMenuItem.Operation);
var labelPosition = string.IsNullOrWhiteSpace(label) || !_preferencesService.IsShowActionLabelsEnabled ?
CommandBarLabelPosition.Collapsed : CommandBarLabelPosition.Default;
menuItem = new AppBarButton
{
Width = double.NaN,
MinWidth = 40,
Icon = new WinoFontIcon() { Glyph = ControlConstants.WinoIconFontDictionary[XamlHelpers.GetWinoIconGlyph(mailOperationMenuItem.Operation)] },
Label = label,
LabelPosition = labelPosition,
DataContext = mailOperationMenuItem,
};
if (!string.IsNullOrWhiteSpace(label))
{
var toolTip = new ToolTip
{
Content = label
};
ToolTipService.SetToolTip((DependencyObject)menuItem, toolTip);
}
((AppBarButton)menuItem).Click -= Button_Click;
((AppBarButton)menuItem).Click += Button_Click;
}
if (mailOperationMenuItem.IsSecondaryMenuPreferred)
{
AssociatedObject.SecondaryCommands.Add(menuItem);
}
else
{
AssociatedObject.PrimaryCommands.Add(menuItem);
}
}
//if (dependencyObject is ICommandBarElement icommandBarElement)
//{
// if (dependencyObject is ButtonBase button)
// {
// button.Click -= Button_Click;
// button.Click += Button_Click;
// }
// if (command is MailOperationMenuItem mailOperationMenuItem)
// {
// }
//}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ItemClickedCommand?.Execute(((ButtonBase)sender).DataContext);
}
protected override void OnAttached()
{
base.OnAttached();
AttachChanges(true);
UpdatePrimaryCommands();
}
private void PrimaryCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdatePrimaryCommands();
}
private static void UpdateCommands(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (dependencyObject is not BindableCommandBarBehavior behavior) return;
if (dependencyPropertyChangedEventArgs.OldValue is INotifyCollectionChanged oldList)
{
oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
}
behavior.AttachChanges(true);
behavior.UpdatePrimaryCommands();
}
private void AttachChanges(bool register)
{
if (PrimaryCommands is null) return;
if (PrimaryCommands is INotifyCollectionChanged collectionChanged)
{
if (register)
{
collectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged;
collectionChanged.CollectionChanged += PrimaryCommandsCollectionChanged;
}
else
collectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged;
}
}
}
@@ -1,85 +0,0 @@
using System.Collections.ObjectModel;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Controls;
namespace Wino.Behaviors;
public class CreateMailNavigationItemBehavior : Behavior<WinoNavigationViewItem>
{
public IMenuItem SelectedMenuItem
{
get { return (IMenuItem)GetValue(SelectedMenuItemProperty); }
set { SetValue(SelectedMenuItemProperty, value); }
}
public ObservableCollection<IMenuItem> MenuItems
{
get { return (ObservableCollection<IMenuItem>)GetValue(MenuItemsProperty); }
set { SetValue(MenuItemsProperty, value); }
}
public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(nameof(MenuItems), typeof(ObservableCollection<IMenuItem>), typeof(CreateMailNavigationItemBehavior), new PropertyMetadata(null, OnMenuItemsChanged));
public static readonly DependencyProperty SelectedMenuItemProperty = DependencyProperty.Register(nameof(SelectedMenuItem), typeof(IMenuItem), typeof(CreateMailNavigationItemBehavior), new PropertyMetadata(null, OnSelectedMenuItemChanged));
public CreateMailNavigationItemBehavior()
{
}
protected override void OnAttached()
{
base.OnAttached();
}
protected override void OnDetaching()
{
base.OnDetaching();
}
private static void OnMenuItemsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (dependencyObject is CreateMailNavigationItemBehavior behavior)
{
if (dependencyPropertyChangedEventArgs.NewValue != null)
behavior.RegisterMenuItemChanges();
behavior.ManageAccounts();
}
}
private void RegisterMenuItemChanges()
{
if (MenuItems != null)
{
MenuItems.CollectionChanged -= MenuCollectionUpdated;
MenuItems.CollectionChanged += MenuCollectionUpdated;
}
}
private void MenuCollectionUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
ManageAccounts();
}
private static void OnSelectedMenuItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (dependencyObject is CreateMailNavigationItemBehavior behavior)
{
behavior.ManageAccounts();
}
}
private void ManageAccounts()
{
if (MenuItems == null) return;
AssociatedObject.MenuItems.Clear();
if (SelectedMenuItem == null)
{
// ??
}
}
}
@@ -1,62 +0,0 @@
using System.Numerics;
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Xaml;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Controls;
namespace Wino.Controls;
public partial class AccountNavigationItem : WinoNavigationViewItem
{
public static readonly DependencyProperty IsActiveAccountProperty = DependencyProperty.Register(nameof(IsActiveAccount), typeof(bool), typeof(AccountNavigationItem), new PropertyMetadata(false, new PropertyChangedCallback(OnIsActiveAccountChanged)));
public static readonly DependencyProperty BindingDataProperty = DependencyProperty.Register(nameof(BindingData), typeof(IAccountMenuItem), typeof(AccountNavigationItem), new PropertyMetadata(null));
public bool IsActiveAccount
{
get { return (bool)GetValue(IsActiveAccountProperty); }
set { SetValue(IsActiveAccountProperty, value); }
}
public IAccountMenuItem BindingData
{
get { return (IAccountMenuItem)GetValue(BindingDataProperty); }
set { SetValue(BindingDataProperty, value); }
}
private const string PART_NavigationViewItemMenuItemsHost = "NavigationViewItemMenuItemsHost";
private const string PART_SelectionIndicator = "CustomSelectionIndicator";
private ItemsRepeater _itemsRepeater;
private Windows.UI.Xaml.Shapes.Rectangle _selectionIndicator;
public AccountNavigationItem()
{
DefaultStyleKey = typeof(AccountNavigationItem);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_itemsRepeater = GetTemplateChild(PART_NavigationViewItemMenuItemsHost) as ItemsRepeater;
_selectionIndicator = GetTemplateChild(PART_SelectionIndicator) as Windows.UI.Xaml.Shapes.Rectangle;
UpdateSelectionBorder();
}
private static void OnIsActiveAccountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is AccountNavigationItem control)
control.UpdateSelectionBorder();
}
private void UpdateSelectionBorder()
{
if (_selectionIndicator == null) return;
_selectionIndicator.Scale = IsActiveAccount ? new Vector3(1, 1, 1) : new Vector3(0, 0, 0);
_selectionIndicator.Visibility = IsActiveAccount ? Visibility.Visible : Visibility.Collapsed;
}
}
-418
View File
@@ -1,418 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using CommunityToolkit.Mvvm.Messaging;
using MoreLinq;
using Serilog;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
using Wino.Extensions;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
namespace Wino.Controls.Advanced;
/// <summary>
/// Custom ListView control that handles multiple selection with Extended/Multiple selection mode
/// and supports threads.
/// </summary>
public partial class WinoListView : ListView, IDisposable
{
private ILogger logger = Log.ForContext<WinoListView>();
private const string PART_ScrollViewer = "ScrollViewer";
private ScrollViewer internalScrollviewer;
/// <summary>
/// Gets or sets whether this ListView belongs to thread items.
/// This is important for detecting selected items etc.
/// </summary>
public bool IsThreadListView
{
get { return (bool)GetValue(IsThreadListViewProperty); }
set { SetValue(IsThreadListViewProperty, value); }
}
public ICommand ItemDeletedCommand
{
get { return (ICommand)GetValue(ItemDeletedCommandProperty); }
set { SetValue(ItemDeletedCommandProperty, value); }
}
public ICommand LoadMoreCommand
{
get { return (ICommand)GetValue(LoadMoreCommandProperty); }
set { SetValue(LoadMoreCommandProperty, value); }
}
public bool IsThreadScrollingEnabled
{
get { return (bool)GetValue(IsThreadScrollingEnabledProperty); }
set { SetValue(IsThreadScrollingEnabledProperty, value); }
}
public static readonly DependencyProperty IsThreadScrollingEnabledProperty = DependencyProperty.Register(nameof(IsThreadScrollingEnabled), typeof(bool), typeof(WinoListView), new PropertyMetadata(false));
public static readonly DependencyProperty LoadMoreCommandProperty = DependencyProperty.Register(nameof(LoadMoreCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null));
public static readonly DependencyProperty IsThreadListViewProperty = DependencyProperty.Register(nameof(IsThreadListView), typeof(bool), typeof(WinoListView), new PropertyMetadata(false, new PropertyChangedCallback(OnIsThreadViewChanged)));
public static readonly DependencyProperty ItemDeletedCommandProperty = DependencyProperty.Register(nameof(ItemDeletedCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null));
public WinoListView()
{
CanDragItems = true;
IsItemClickEnabled = true;
IsMultiSelectCheckBoxEnabled = true;
IsRightTapEnabled = true;
SelectionMode = ListViewSelectionMode.Extended;
ShowsScrollingPlaceholders = false;
SingleSelectionFollowsFocus = true;
DragItemsCompleted += ItemDragCompleted;
DragItemsStarting += ItemDragStarting;
SelectionChanged += SelectedItemsChanged;
ProcessKeyboardAccelerators += ProcessDelKey;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
internalScrollviewer = GetTemplateChild(PART_ScrollViewer) as ScrollViewer;
if (internalScrollviewer == null)
{
logger.Warning("WinoListView does not have an internal ScrollViewer. Infinite scrolling behavior might be effected.");
return;
}
internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged;
internalScrollviewer.ViewChanged += InternalScrollVeiwerViewChanged;
}
private static void OnIsThreadViewChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is WinoListView winoListView)
{
winoListView.AdjustThreadViewContainerVisuals();
}
}
private void AdjustThreadViewContainerVisuals()
{
if (IsThreadListView)
{
ItemContainerTransitions.Clear();
}
}
private double lastestRaisedOffset = 0;
private int lastItemSize = 0;
// TODO: This is buggy. Does not work all the time. Debug.
private void InternalScrollVeiwerViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (internalScrollviewer == null) return;
// No need to raise init request if there are no items in the list.
if (Items.Count == 0) return;
// If the scrolling is finished, check the current viewport height.
if (e.IsIntermediate)
{
var currentOffset = internalScrollviewer.VerticalOffset;
var maxOffset = internalScrollviewer.ScrollableHeight;
if (currentOffset + 10 >= maxOffset && lastestRaisedOffset != maxOffset && Items.Count != lastItemSize)
{
// We must load more.
lastestRaisedOffset = maxOffset;
lastItemSize = Items.Count;
LoadMoreCommand?.Execute(null);
}
}
}
private void ProcessDelKey(UIElement sender, Windows.UI.Xaml.Input.ProcessKeyboardAcceleratorEventArgs args)
{
if (args.Key == Windows.System.VirtualKey.Delete)
{
args.Handled = true;
ItemDeletedCommand?.Execute(MailOperation.SoftDelete);
}
}
private void ItemDragCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
{
if (args.Items.Any(a => a is MailItemViewModel))
{
args.Items.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
}
}
private void ItemDragStarting(object sender, DragItemsStartingEventArgs args)
{
// Dragging multiple mails from different accounts/folders are supported with the condition below:
// All mails belongs to the drag will be matched on the dropped folder's account.
// Meaning that if users drag 1 mail from Account A/Inbox and 1 mail from Account B/Inbox,
// and drop to Account A/Inbox, the mail from Account B/Inbox will NOT be moved.
if (IsThreadListView)
{
var allItems = args.Items.Cast<MailItemViewModel>();
// Highlight all items
allItems.ForEach(a => a.IsCustomFocused = true);
// Set native drag arg properties.
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
}
else
{
var dragPackage = new MailDragPackage(args.Items.Cast<IMailItem>());
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
}
}
public void ChangeSelectionMode(ListViewSelectionMode selectionMode)
{
SelectionMode = selectionMode;
if (!IsThreadListView)
{
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
{
var threadListView = GetThreadInternalListView(c);
if (threadListView != null)
{
threadListView.SelectionMode = selectionMode;
}
});
}
}
/// <summary>
/// Finds the container for given mail item and adds it to selected items.
/// </summary>
/// <param name="mailItemViewModel">Mail to be added to selected items.</param>
/// <returns>Whether selection was successful or not.</returns>
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
{
var itemContainer = ContainerFromItem(mailItemViewModel);
// This item might be in thread container.
if (itemContainer == null)
{
bool found = false;
Items.OfType<ThreadMailItemViewModel>().ForEach(c =>
{
if (!found)
{
var threadListView = GetThreadInternalListView(c);
if (threadListView != null)
found = threadListView.SelectMailItemContainer(mailItemViewModel);
}
});
return found;
}
SelectedItems.Add(mailItemViewModel);
return true;
}
/// <summary>
/// Recursively clears all selections except the given mail.
/// </summary>
/// <param name="exceptViewModel">Exceptional mail item to be not unselected.</param>
/// <param name="preserveThreadExpanding">Whether expansion states of thread containers should stay as it is or not.</param>
public void ClearSelections(MailItemViewModel exceptViewModel = null, bool preserveThreadExpanding = false)
{
SelectedItems.Clear();
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
{
var threadListView = GetThreadInternalListView(c);
if (threadListView == null)
return;
if (exceptViewModel != null)
{
if (!threadListView.SelectedItems.Contains(exceptViewModel))
{
if (!preserveThreadExpanding)
{
c.IsThreadExpanded = false;
}
threadListView.SelectedItems.Clear();
}
}
else
{
if (!preserveThreadExpanding)
{
c.IsThreadExpanded = false;
}
threadListView.SelectedItems.Clear();
}
});
}
/// <summary>
/// Recursively selects all mails, including thread items.
/// </summary>
public void SelectAllWino()
{
SelectAll();
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
{
c.IsThreadExpanded = true;
var threadListView = GetThreadInternalListView(c);
threadListView?.SelectAll();
});
}
// SelectedItems changed.
private void SelectedItemsChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems != null)
{
foreach (var removedItem in e.RemovedItems)
{
if (removedItem is MailItemViewModel removedMailItemViewModel)
{
// Mail item un-selected.
removedMailItemViewModel.IsSelected = false;
WeakReferenceMessenger.Default.Send(new MailItemSelectionRemovedEvent(removedMailItemViewModel));
}
else if (removedItem is ThreadMailItemViewModel removedThreadItemViewModel)
{
removedThreadItemViewModel.IsThreadExpanded = false;
}
}
}
if (e.AddedItems != null)
{
foreach (var addedItem in e.AddedItems)
{
if (addedItem is MailItemViewModel addedMailItemViewModel)
{
// Mail item selected.
addedMailItemViewModel.IsSelected = true;
WeakReferenceMessenger.Default.Send(new MailItemSelectedEvent(addedMailItemViewModel));
}
else if (addedItem is ThreadMailItemViewModel threadMailItemViewModel)
{
if (IsThreadScrollingEnabled)
{
if (internalScrollviewer != null && ContainerFromItem(threadMailItemViewModel) is FrameworkElement threadFrameworkElement)
{
internalScrollviewer.ScrollToElement(threadFrameworkElement, true, true, bringToTopOrLeft: true);
}
}
// Try to select first item.
if (GetThreadInternalListView(threadMailItemViewModel) is WinoListView internalListView)
{
internalListView.SelectFirstItem();
}
}
}
}
if (!IsThreadListView)
{
if (SelectionMode == ListViewSelectionMode.Extended && SelectedItems.Count == 1)
{
// Only 1 single item is selected in extended mode for main list view.
// We should un-select all thread items.
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
{
// c.IsThreadExpanded = false;
var threadListView = GetThreadInternalListView(c);
threadListView?.SelectedItems.Clear();
});
}
}
}
public async void SelectFirstItem()
{
if (Items.Count > 0)
{
if (Items[0] is MailItemViewModel firstMailItemViewModel)
{
// Make sure the invisible container is realized.
await Task.Delay(250);
if (ContainerFromItem(firstMailItemViewModel) is ListViewItem firstItemContainer)
{
firstItemContainer.IsSelected = true;
}
firstMailItemViewModel.IsSelected = true;
}
}
}
private WinoListView GetThreadInternalListView(ThreadMailItemViewModel threadMailItemViewModel)
{
var itemContainer = ContainerFromItem(threadMailItemViewModel);
if (itemContainer is ListViewItem listItem)
{
var expander = listItem.GetChildByName<WinoExpander>("ThreadExpander");
if (expander != null)
return expander.Content as WinoListView;
}
return null;
}
public void Dispose()
{
DragItemsCompleted -= ItemDragCompleted;
DragItemsStarting -= ItemDragStarting;
SelectionChanged -= SelectedItemsChanged;
ProcessKeyboardAccelerators -= ProcessDelKey;
if (internalScrollviewer != null)
{
internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged;
}
foreach (var item in Items)
{
if (item is ThreadMailItemViewModel threadMailItemViewModel)
{
var threadListView = GetThreadInternalListView(threadMailItemViewModel);
threadListView?.Dispose();
}
}
}
}
-230
View File
@@ -1,230 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Fernandezja.ColorHashSharp;
using Microsoft.Extensions.DependencyInjection;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;
using Wino.Core.Domain.Interfaces;
namespace Wino.Controls;
public partial class ImagePreviewControl : Control
{
private const string PART_EllipseInitialsGrid = "EllipseInitialsGrid";
private const string PART_InitialsTextBlock = "InitialsTextBlock";
private const string PART_KnownHostImage = "KnownHostImage";
private const string PART_Ellipse = "Ellipse";
private const string PART_FaviconSquircle = "FaviconSquircle";
private const string PART_FaviconImage = "FaviconImage";
#region Dependency Properties
public static readonly DependencyProperty FromNameProperty = DependencyProperty.Register(nameof(FromName), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnInformationChanged));
public static readonly DependencyProperty FromAddressProperty = DependencyProperty.Register(nameof(FromAddress), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnInformationChanged));
public static readonly DependencyProperty SenderContactPictureProperty = DependencyProperty.Register(nameof(SenderContactPicture), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, new PropertyChangedCallback(OnInformationChanged)));
public static readonly DependencyProperty ThumbnailUpdatedEventProperty = DependencyProperty.Register(nameof(ThumbnailUpdatedEvent), typeof(bool), typeof(ImagePreviewControl), new PropertyMetadata(false, new PropertyChangedCallback(OnInformationChanged)));
public bool ThumbnailUpdatedEvent
{
get { return (bool)GetValue(ThumbnailUpdatedEventProperty); }
set { SetValue(ThumbnailUpdatedEventProperty, value); }
}
/// <summary>
/// Gets or sets base64 string of the sender contact picture.
/// </summary>
public string SenderContactPicture
{
get { return (string)GetValue(SenderContactPictureProperty); }
set { SetValue(SenderContactPictureProperty, value); }
}
public string FromName
{
get { return (string)GetValue(FromNameProperty); }
set { SetValue(FromNameProperty, value); }
}
public string FromAddress
{
get { return (string)GetValue(FromAddressProperty); }
set { SetValue(FromAddressProperty, value); }
}
#endregion
private Ellipse Ellipse;
private Grid InitialsGrid;
private TextBlock InitialsTextblock;
private Image KnownHostImage;
private Border FaviconSquircle;
private Image FaviconImage;
private CancellationTokenSource contactPictureLoadingCancellationTokenSource;
public ImagePreviewControl()
{
DefaultStyleKey = nameof(ImagePreviewControl);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
InitialsGrid = GetTemplateChild(PART_EllipseInitialsGrid) as Grid;
InitialsTextblock = GetTemplateChild(PART_InitialsTextBlock) as TextBlock;
KnownHostImage = GetTemplateChild(PART_KnownHostImage) as Image;
Ellipse = GetTemplateChild(PART_Ellipse) as Ellipse;
FaviconSquircle = GetTemplateChild(PART_FaviconSquircle) as Border;
FaviconImage = GetTemplateChild(PART_FaviconImage) as Image;
UpdateInformation();
}
private static void OnInformationChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is ImagePreviewControl control)
control.UpdateInformation();
}
private async void UpdateInformation()
{
if ((KnownHostImage == null && FaviconSquircle == null) || InitialsGrid == null || InitialsTextblock == null || (string.IsNullOrEmpty(FromName) && string.IsNullOrEmpty(FromAddress)))
return;
// Cancel active image loading if exists.
if (!contactPictureLoadingCancellationTokenSource?.IsCancellationRequested ?? false)
{
contactPictureLoadingCancellationTokenSource.Cancel();
}
string contactPicture = SenderContactPicture;
var isAvatarThumbnail = false;
if (string.IsNullOrEmpty(contactPicture) && !string.IsNullOrEmpty(FromAddress))
{
contactPicture = await App.Current.ThumbnailService.GetThumbnailAsync(FromAddress);
isAvatarThumbnail = true;
}
if (!string.IsNullOrEmpty(contactPicture))
{
if (isAvatarThumbnail && FaviconSquircle != null && FaviconImage != null)
{
// Show favicon in squircle
FaviconSquircle.Visibility = Visibility.Visible;
InitialsGrid.Visibility = Visibility.Collapsed;
KnownHostImage.Visibility = Visibility.Collapsed;
var bitmapImage = await GetBitmapImageAsync(contactPicture);
if (bitmapImage != null)
{
FaviconImage.Source = bitmapImage;
}
}
else
{
// Show normal avatar (tondo)
FaviconSquircle.Visibility = Visibility.Collapsed;
KnownHostImage.Visibility = Visibility.Collapsed;
InitialsGrid.Visibility = Visibility.Visible;
contactPictureLoadingCancellationTokenSource = new CancellationTokenSource();
try
{
var brush = await GetContactImageBrushAsync(contactPicture);
if (brush != null)
{
if (!contactPictureLoadingCancellationTokenSource?.Token.IsCancellationRequested ?? false)
{
Ellipse.Fill = brush;
InitialsTextblock.Text = string.Empty;
}
}
}
catch (Exception)
{
Debugger.Break();
}
}
}
else
{
FaviconSquircle.Visibility = Visibility.Collapsed;
KnownHostImage.Visibility = Visibility.Collapsed;
InitialsGrid.Visibility = Visibility.Visible;
var colorHash = new ColorHash();
var rgb = colorHash.Rgb(FromAddress);
Ellipse.Fill = new SolidColorBrush(Color.FromArgb(rgb.A, rgb.R, rgb.G, rgb.B));
InitialsTextblock.Text = ExtractInitialsFromName(FromName);
}
}
private static async Task<ImageBrush> GetContactImageBrushAsync(string base64)
{
// Load the image from base64 string.
var bitmapImage = await GetBitmapImageAsync(base64);
if (bitmapImage == null) return null;
return new ImageBrush() { ImageSource = bitmapImage };
}
private static async Task<BitmapImage> GetBitmapImageAsync(string base64)
{
try
{
var bitmapImage = new BitmapImage();
var imageArray = Convert.FromBase64String(base64);
var imageStream = new MemoryStream(imageArray);
var randomAccessImageStream = imageStream.AsRandomAccessStream();
randomAccessImageStream.Seek(0);
await bitmapImage.SetSourceAsync(randomAccessImageStream);
return bitmapImage;
}
catch (Exception) { }
return null;
}
public string ExtractInitialsFromName(string name)
{
// Change from name to from address in case of name doesn't exists.
if (string.IsNullOrEmpty(name))
{
name = FromAddress;
}
// first remove all: punctuation, separator chars, control chars, and numbers (unicode style regexes)
string initials = Regex.Replace(name, @"[\p{P}\p{S}\p{C}\p{N}]+", "");
// Replacing all possible whitespace/separator characters (unicode style), with a single, regular ascii space.
initials = Regex.Replace(initials, @"\p{Z}+", " ");
// Remove all Sr, Jr, I, II, III, IV, V, VI, VII, VIII, IX at the end of names
initials = Regex.Replace(initials.Trim(), @"\s+(?:[JS]R|I{1,3}|I[VX]|VI{0,3})$", "", RegexOptions.IgnoreCase);
// Extract up to 2 initials from the remaining cleaned name.
initials = Regex.Replace(initials, @"^(\p{L})[^\s]*(?:\s+(?:\p{L}+\s+(?=\p{L}))?(?:(\p{L})\p{L}*)?)?$", "$1$2").Trim();
if (initials.Length > 2)
{
// Worst case scenario, everything failed, just grab the first two letters of what we have left.
initials = initials.Substring(0, 2);
}
return initials.ToUpperInvariant();
}
}
@@ -1,336 +0,0 @@
<UserControl
x:Class="Wino.Controls.MailItemDisplayInformationControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:controls="using:Wino.Controls"
xmlns:coreControls="using:Wino.Core.UWP.Controls"
xmlns:domain="using:Wino.Core.Domain"
xmlns:enums="using:Wino.Core.Domain.Enums"
xmlns:helpers="using:Wino.Helpers"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
FocusVisualMargin="8"
FocusVisualPrimaryBrush="{StaticResource SystemControlRevealFocusVisualBrush}"
FocusVisualPrimaryThickness="2"
FocusVisualSecondaryBrush="{StaticResource SystemControlFocusVisualSecondaryBrush}"
FocusVisualSecondaryThickness="1"
PointerEntered="ControlPointerEntered"
PointerExited="ControlPointerExited">
<UserControl.Resources>
<Style
x:Key="HoverActionButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</UserControl.Resources>
<Grid>
<Grid
x:Name="RootContainer"
Padding="0,1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:DefaultBindMode="OneWay">
<Grid x:Name="UnreadContainer">
<Ellipse
Width="8"
Height="8"
Margin="0,12,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Canvas.ZIndex="0"
Fill="{ThemeResource SystemAccentColor}"
Visibility="{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(MailItem.IsRead), Mode=OneWay}" />
</Grid>
<Border
x:Name="RootContainerVisualWrapper"
Margin="0,4"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0.5"
CornerRadius="4" />
<Grid x:Name="MainContentContainer">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:ImagePreviewControl
x:Name="ContactImage"
Width="35"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="14"
FromAddress="{x:Bind MailItem.FromAddress, Mode=OneWay}"
FromName="{x:Bind MailItem.FromName, Mode=OneWay}"
SenderContactPicture="{x:Bind MailItem.SenderContact.Base64ContactPicture}"
ThumbnailUpdatedEvent="{x:Bind IsThumbnailUpdated, Mode=OneWay}"
Visibility="{x:Bind IsAvatarVisible, Mode=OneWay}" />
<Grid
x:Name="ContentGrid"
Grid.Column="1"
Canvas.ZIndex="2">
<!-- Sender + Title -->
<Grid x:Name="ContentStackpanel" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Sender + IsDraft + Hover Buttons -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- IsDraft Tag -->
<TextBlock
x:Name="DraftTitle"
Margin="0,0,4,0"
x:Load="{x:Bind MailItem.IsDraft, Mode=OneWay}"
Foreground="{StaticResource DeleteBrush}">
<Run Text="[" /><Run Text="{x:Bind domain:Translator.Draft}" /><Run Text="]" /> <Run Text=" " />
</TextBlock>
<!-- Sender -->
<TextBlock
x:Name="SenderTextFromName"
Grid.Column="1"
Text="{x:Bind MailItem.FromName}"
TextTrimming="WordEllipsis"
Visibility="{x:Bind helpers:XamlHelpers.StringToVisibilityConverter(MailItem.FromName)}" />
<!-- Sender -->
<TextBlock
x:Name="SenderTextFromAddress"
Grid.Column="1"
Text="{x:Bind MailItem.FromAddress}"
TextTrimming="CharacterEllipsis"
Visibility="{x:Bind helpers:XamlHelpers.StringToVisibilityReversedConverter(MailItem.FromName)}" />
<!-- Hover button -->
<StackPanel
x:Name="HoverActionButtons"
Grid.Column="2"
HorizontalAlignment="Right"
Background="Transparent"
Canvas.ZIndex="999"
ChildrenTransitions="{x:Null}"
Orientation="Horizontal"
Spacing="12"
Visibility="Collapsed">
<Button Click="FirstActionClicked" Style="{StaticResource HoverActionButtonStyle}">
<Button.Content>
<coreControls:WinoFontIcon FontSize="16" Icon="{x:Bind helpers:XamlHelpers.GetWinoIconGlyph(LeftHoverAction), Mode=OneWay}" />
</Button.Content>
</Button>
<Button Click="SecondActionClicked" Style="{StaticResource HoverActionButtonStyle}">
<Button.Content>
<coreControls:WinoFontIcon FontSize="16" Icon="{x:Bind helpers:XamlHelpers.GetWinoIconGlyph(CenterHoverAction), Mode=OneWay}" />
</Button.Content>
</Button>
<Button Click="ThirdActionClicked" Style="{StaticResource HoverActionButtonStyle}">
<Button.Content>
<coreControls:WinoFontIcon FontSize="16" Icon="{x:Bind helpers:XamlHelpers.GetWinoIconGlyph(RightHoverAction), Mode=OneWay}" />
</Button.Content>
</Button>
</StackPanel>
</Grid>
<!-- Subject + IsDraft -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<local:AnimatedIcon
xmlns:local="using:Microsoft.UI.Xaml.Controls"
x:Name="ExpandCollapseChevron"
Width="14"
Height="14"
Margin="-4,0,2,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
local:AnimatedIcon.State="NormalOff"
AutomationProperties.AccessibilityView="Raw"
Foreground="{ThemeResource ApplicationForegroundThemeBrush}"
RenderTransformOrigin="0.5, 0.5"
Visibility="{x:Bind IsThreadExpanderVisible, Mode=OneWay}">
<animatedvisuals:AnimatedChevronRightDownSmallVisualSource />
<local:AnimatedIcon.FallbackIconSource>
<local:FontIconSource
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xE76C;"
IsTextScaleFactorEnabled="False" />
</local:AnimatedIcon.FallbackIconSource>
<local:AnimatedIcon.RenderTransform />
</local:AnimatedIcon>
<!-- Subject is bound in the code behind. -->
<TextBlock
x:Name="TitleText"
Grid.Column="1"
MaxLines="1"
TextTrimming="CharacterEllipsis" />
<TextBlock
Grid.Column="2"
Margin="4,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="11"
Opacity="0.7"
Text="{x:Bind helpers:XamlHelpers.GetMailItemDisplaySummaryForListing(MailItem.IsDraft, MailItem.CreationDate, Prefer24HourTimeFormat)}" />
</Grid>
<!-- Message -->
<Grid
x:Name="PreviewTextContainerRoot"
Grid.Row="2"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid x:Name="PreviewTextContainer">
<TextBlock
x:Name="PreviewTextblock"
x:Load="{x:Bind helpers:XamlHelpers.ShouldDisplayPreview(MailItem.PreviewText), Mode=OneWay}"
MaxLines="1"
Opacity="0.7"
Text="{x:Bind MailItem.PreviewText}"
TextTrimming="CharacterEllipsis" />
</Grid>
<!-- Right Icons Container -->
<StackPanel
x:Name="IconsContainer"
Grid.Column="1"
Margin="4,4,1,4"
Orientation="Horizontal"
Spacing="2">
<ContentPresenter
x:Name="HasAttachmentContent"
x:Load="{x:Bind MailItem.HasAttachments, Mode=OneWay}"
ContentTemplate="{StaticResource AttachmentSymbolControlTemplate}" />
<ContentPresenter
x:Name="IsFlaggedContent"
x:Load="{x:Bind MailItem.IsFlagged, Mode=OneWay}"
ContentTemplate="{StaticResource FlaggedSymbolControlTemplate}" />
</StackPanel>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<VisualStateManager.VisualStateGroups>
<!-- Read States -->
<VisualStateGroup x:Name="ReadStates">
<VisualState x:Name="Unread">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind MailItem.IsRead, Converter={StaticResource ReverseBooleanConverter}, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="TitleText.Foreground" Value="{ThemeResource SystemAccentColor}" />
<Setter Target="TitleText.FontWeight" Value="Semibold" />
<Setter Target="SenderTextFromName.FontWeight" Value="Bold" />
<Setter Target="SenderTextFromAddress.FontWeight" Value="Bold" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Read" />
</VisualStateGroup>
<!-- Sizing States -->
<VisualStateGroup x:Name="SizingStates">
<VisualState x:Name="Compact">
<VisualState.Setters>
<Setter Target="RootContainer.Height" Value="60" />
<Setter Target="ContentGrid.Padding" Value="8,0" />
<Setter Target="PreviewTextContainer.Visibility" Value="Collapsed" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind helpers:XamlHelpers.ObjectEquals(DisplayMode, enums:MailListDisplayMode.Compact), Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
<!-- Medium -->
<VisualState x:Name="Medium">
<VisualState.Setters>
<Setter Target="RootContainer.Height" Value="80" />
<Setter Target="ContentGrid.Padding" Value="6,0" />
<Setter Target="PreviewTextContainer.Visibility" Value="Visible" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind helpers:XamlHelpers.ObjectEquals(DisplayMode, enums:MailListDisplayMode.Medium), Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
<!-- Spacious -->
<VisualState x:Name="Spacious">
<VisualState.Setters>
<Setter Target="RootContainer.Height" Value="Auto" />
<Setter Target="ContentGrid.Padding" Value="12,12,6,12" />
<Setter Target="PreviewTextContainer.Visibility" Value="Visible" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind helpers:XamlHelpers.ObjectEquals(DisplayMode, enums:MailListDisplayMode.Spacious), Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
<!-- Preview Text States -->
<VisualStateGroup x:Name="PreviewTextStates">
<VisualState x:Name="ShowText" />
<VisualState x:Name="HideText">
<VisualState.Setters>
<Setter Target="PreviewTextContainerRoot.Visibility" Value="Collapsed" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ShowPreviewText, Mode=OneWay, Converter={StaticResource ReverseBooleanConverter}}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
<!-- Thread Expanding States -->
<VisualStateGroup x:Name="ExpanderStates">
<VisualState x:Name="NotExpanded" />
<VisualState x:Name="ExpandedState">
<VisualState.Setters>
<Setter Target="ExpandCollapseChevron.(controls:AnimatedIcon.State)" Value="NormalOn" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind IsThreadExpanded, Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>
@@ -1,218 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Windows.Input;
using CommunityToolkit.WinUI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem;
using Wino.Extensions;
using Wino.Mail.ViewModels.Data;
namespace Wino.Controls;
public sealed partial class MailItemDisplayInformationControl : UserControl
{
public ImagePreviewControl GetImagePreviewControl() => ContactImage;
public bool IsRunningHoverAction { get; set; }
public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(MailListDisplayMode), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailListDisplayMode.Spacious));
public static readonly DependencyProperty ShowPreviewTextProperty = DependencyProperty.Register(nameof(ShowPreviewText), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
public static readonly DependencyProperty IsCustomFocusedProperty = DependencyProperty.Register(nameof(IsCustomFocused), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
public static readonly DependencyProperty IsAvatarVisibleProperty = DependencyProperty.Register(nameof(IsAvatarVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
public static readonly DependencyProperty IsSubjectVisibleProperty = DependencyProperty.Register(nameof(IsSubjectVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
public static readonly DependencyProperty ConnectedExpanderProperty = DependencyProperty.Register(nameof(ConnectedExpander), typeof(WinoExpander), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null));
public static readonly DependencyProperty LeftHoverActionProperty = DependencyProperty.Register(nameof(LeftHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None));
public static readonly DependencyProperty CenterHoverActionProperty = DependencyProperty.Register(nameof(CenterHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None));
public static readonly DependencyProperty RightHoverActionProperty = DependencyProperty.Register(nameof(RightHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None));
public static readonly DependencyProperty HoverActionExecutedCommandProperty = DependencyProperty.Register(nameof(HoverActionExecutedCommand), typeof(ICommand), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null));
public static readonly DependencyProperty MailItemProperty = DependencyProperty.Register(nameof(MailItem), typeof(IMailItem), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null, new PropertyChangedCallback(OnMailItemChanged)));
public static readonly DependencyProperty IsHoverActionsEnabledProperty = DependencyProperty.Register(nameof(IsHoverActionsEnabled), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true));
public static readonly DependencyProperty Prefer24HourTimeFormatProperty = DependencyProperty.Register(nameof(Prefer24HourTimeFormat), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
public static readonly DependencyProperty IsThreadExpanderVisibleProperty = DependencyProperty.Register(nameof(IsThreadExpanderVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
public static readonly DependencyProperty IsThreadExpandedProperty = DependencyProperty.Register(nameof(IsThreadExpanded), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
public static readonly DependencyProperty IsThumbnailUpdatedProperty = DependencyProperty.Register(nameof(IsThumbnailUpdated), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false));
public bool IsThumbnailUpdated
{
get { return (bool)GetValue(IsThumbnailUpdatedProperty); }
set { SetValue(IsThumbnailUpdatedProperty, value); }
}
public bool IsThreadExpanded
{
get { return (bool)GetValue(IsThreadExpandedProperty); }
set { SetValue(IsThreadExpandedProperty, value); }
}
public bool IsThreadExpanderVisible
{
get { return (bool)GetValue(IsThreadExpanderVisibleProperty); }
set { SetValue(IsThreadExpanderVisibleProperty, value); }
}
public bool Prefer24HourTimeFormat
{
get { return (bool)GetValue(Prefer24HourTimeFormatProperty); }
set { SetValue(Prefer24HourTimeFormatProperty, value); }
}
public bool IsHoverActionsEnabled
{
get { return (bool)GetValue(IsHoverActionsEnabledProperty); }
set { SetValue(IsHoverActionsEnabledProperty, value); }
}
public IMailItem MailItem
{
get { return (IMailItem)GetValue(MailItemProperty); }
set { SetValue(MailItemProperty, value); }
}
public ICommand HoverActionExecutedCommand
{
get { return (ICommand)GetValue(HoverActionExecutedCommandProperty); }
set { SetValue(HoverActionExecutedCommandProperty, value); }
}
public MailOperation LeftHoverAction
{
get { return (MailOperation)GetValue(LeftHoverActionProperty); }
set { SetValue(LeftHoverActionProperty, value); }
}
public MailOperation CenterHoverAction
{
get { return (MailOperation)GetValue(CenterHoverActionProperty); }
set { SetValue(CenterHoverActionProperty, value); }
}
public MailOperation RightHoverAction
{
get { return (MailOperation)GetValue(RightHoverActionProperty); }
set { SetValue(RightHoverActionProperty, value); }
}
public WinoExpander ConnectedExpander
{
get { return (WinoExpander)GetValue(ConnectedExpanderProperty); }
set { SetValue(ConnectedExpanderProperty, value); }
}
public bool IsSubjectVisible
{
get { return (bool)GetValue(IsSubjectVisibleProperty); }
set { SetValue(IsSubjectVisibleProperty, value); }
}
public bool IsAvatarVisible
{
get { return (bool)GetValue(IsAvatarVisibleProperty); }
set { SetValue(IsAvatarVisibleProperty, value); }
}
public bool IsCustomFocused
{
get { return (bool)GetValue(IsCustomFocusedProperty); }
set { SetValue(IsCustomFocusedProperty, value); }
}
public bool ShowPreviewText
{
get { return (bool)GetValue(ShowPreviewTextProperty); }
set { SetValue(ShowPreviewTextProperty, value); }
}
public MailListDisplayMode DisplayMode
{
get { return (MailListDisplayMode)GetValue(DisplayModeProperty); }
set { SetValue(DisplayModeProperty, value); }
}
public MailItemDisplayInformationControl()
{
this.InitializeComponent();
var compositor = this.Visual().Compositor;
var leftBackgroundVisual = compositor.CreateSpriteVisual();
RootContainerVisualWrapper.SetChildVisual(leftBackgroundVisual);
MainContentContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RootContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
ContentGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
ContentStackpanel.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
IconsContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RootContainerVisualWrapper.SizeChanged += (s, e) => leftBackgroundVisual.Size = e.NewSize.ToVector2();
}
private static void OnMailItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (obj is MailItemDisplayInformationControl control)
{
control.UpdateInformation();
}
}
private void UpdateInformation()
{
if (MailItem == null) return;
TitleText.Text = string.IsNullOrWhiteSpace(MailItem.Subject) ? Translator.MailItemNoSubject : MailItem.Subject;
}
private void ControlPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (IsHoverActionsEnabled)
{
HoverActionButtons.Visibility = Visibility.Visible;
UnreadContainer.Visibility = Visibility.Collapsed;
}
}
private void ControlPointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (IsHoverActionsEnabled)
{
HoverActionButtons.Visibility = Visibility.Collapsed;
UnreadContainer.Visibility = Visibility.Visible;
}
}
private void ExecuteHoverAction(MailOperation operation)
{
IsRunningHoverAction = true;
MailOperationPreperationRequest package = null;
if (MailItem is MailCopy mailCopy)
package = new MailOperationPreperationRequest(operation, mailCopy, toggleExecution: true);
else if (MailItem is ThreadMailItemViewModel threadMailItemViewModel)
package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies(), toggleExecution: true);
else if (MailItem is ThreadMailItem threadMailItem)
package = new MailOperationPreperationRequest(operation, threadMailItem.ThreadItems.Cast<MailItemViewModel>().Select(a => a.MailCopy), toggleExecution: true);
if (package == null) return;
HoverActionExecutedCommand?.Execute(package);
}
private void FirstActionClicked(object sender, RoutedEventArgs e)
{
ExecuteHoverAction(LeftHoverAction);
}
private void SecondActionClicked(object sender, RoutedEventArgs e)
{
ExecuteHoverAction(CenterHoverAction);
}
private void ThirdActionClicked(object sender, RoutedEventArgs e)
{
ExecuteHoverAction(RightHoverAction);
}
}

Some files were not shown because too many files have changed in this diff Show More