Remove old shells, some UI improvements for settings.

This commit is contained in:
Burak Kaan Köse
2026-03-21 18:25:54 +01:00
parent e6a38a3e77
commit 8d143e3b08
8 changed files with 30 additions and 1570 deletions
-479
View File
@@ -1,479 +0,0 @@
<abstract:MailAppShellAbstract
x:Class="Wino.Views.MailAppShell"
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: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.Mail.WinUI.Controls"
xmlns:coreConverters="using:Wino.Mail.WinUI.Converters"
xmlns:coreSelectors="using:Wino.Mail.WinUI.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"
Visibility="{x:Bind IsSynchronizationProgressVisible, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}" />
<TextBlock
x:Name="SyncStatusText"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
MaxLines="1"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind SynchronizationStatus, Mode=OneWay}"
TextTrimming="CharacterEllipsis"
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
</StackPanel>
<PathIcon
x:Name="AttentionIcon"
Grid.Column="1"
Width="16"
Height="16"
Margin="8,0"
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.Column="2"
Width="16"
Height="16"
Margin="8,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsActive="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}"
IsIndeterminate="{x:Bind IsProgressIndeterminate, Mode=OneWay}"
Visibility="{x:Bind IsSynchronizationProgressVisible, 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
Margin="0,-2,0,0"
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">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<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"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
MaxLines="1"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind SynchronizationStatus, Mode=OneWay}"
TextTrimming="CharacterEllipsis"
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
<TextBlock
FontSize="12"
MaxLines="1"
Style="{StaticResource CaptionTextBlockStyle}"
TextTrimming="CharacterEllipsis"
Visibility="{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(IsSynchronizationProgressVisible), Mode=OneWay}">
<Run Text="{x:Bind MergedAccountCount, Mode=OneWay}" /><Run Text="{x:Bind domain:Translator.MenuMergedAccountItemAccountsSuffix}" />
</TextBlock>
</StackPanel>
<muxc:ProgressRing
x:Name="SynchronizationProgressBar"
Grid.Column="1"
Width="16"
Height="16"
Margin="8,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsActive="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}"
IsIndeterminate="{x:Bind IsProgressIndeterminate, Mode=OneWay}"
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
</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"
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}"
StoreUpdateItemTemplate="{StaticResource StoreUpdateItemTemplate}" />
</Page.Resources>
<Grid
x:Name="RootGrid"
Padding="0"
ColumnSpacing="0"
RowSpacing="0">
<muxc:NavigationView
x:Name="navigationView"
Grid.Row="1"
Grid.ColumnSpan="3"
Margin="-1,-1,0,0"
Style="{StaticResource CalendarShellNavigationViewStyle}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
AlwaysShowHeader="True"
DisplayModeChanged="NavigationViewDisplayModeChanged"
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="InnerShellFrame"
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>
</Grid>
</abstract:MailAppShellAbstract>
-432
View File
@@ -1,432 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
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;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.WinUI;
using Wino.Mail.WinUI.Controls;
using Wino.MenuFlyouts;
using Wino.MenuFlyouts.Context;
using Wino.Messaging.Client.Accounts;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
using Wino.Messaging.UI;
using Wino.Views.Abstract;
namespace Wino.Views;
public sealed partial class MailAppShell : MailAppShellAbstract,
IRecipient<AccountMenuItemExtended>,
IRecipient<NavigateMailFolderEvent>,
IRecipient<CreateNewMailWithMultipleAccountsRequested>
{
public Frame GetShellFrame() => InnerShellFrame;
[GeneratedDependencyProperty]
public partial UIElement? TopShellContent { get; set; }
public MailAppShell() : base()
{
InitializeComponent();
PreviewKeyDown += OnPreviewKeyDown;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
Bindings.StopTracking();
}
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 dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
if (dragPackage == null) return;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
var mailCopies = ExtractMailCopies(dragPackage).ToList();
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 == null || !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);
var draggedMails = ExtractMailCopies(dragPackage);
if (!draggedMails.Any()) return false;
if (!draggedMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false;
return true;
}
private static IEnumerable<MailCopy> ExtractMailCopies(MailDragPackage dragPackage)
{
foreach (var item in dragPackage.DraggingMails)
{
if (item is MailCopy mailCopy)
{
yield return mailCopy;
}
else if (item is MailItemViewModel singleMailItemViewModel)
{
yield return singleMailItemViewModel.MailCopy;
}
else if (item is ThreadMailItemViewModel threadViewModel)
{
foreach (var threadMail in threadViewModel.ThreadEmails)
{
yield return threadMail.MailCopy;
}
}
}
}
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;
if (draggingFolder == null) return;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName);
}
}
public async void Receive(AccountMenuItemExtended message)
{
await DispatcherQueue.EnqueueAsync(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.InnerShellFrame);
// 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, Microsoft.UI.Xaml.Navigation.NavigationEventArgs e) => TopShellContent = ((BasePage)e.Content).ShellContent;
partial void OnTopShellContentChanged(UIElement? newValue) => WeakReferenceMessenger.Default.Send(new TitleBarShellContentUpdated());
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();
}
}
private void NavigationViewDisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == NavigationViewDisplayMode.Minimal)
{
InnerShellFrame.Margin = new Thickness(7, 0, 0, 0);
}
else
{
InnerShellFrame.Margin = new Thickness(0);
}
}
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
return;
var key = NormalizeKey(e.Key);
if (string.IsNullOrEmpty(key))
return;
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Mail, key, GetCurrentModifierKeys());
if (shortcut == null)
return;
var details = new KeyboardShortcutTriggerDetails
{
ShortcutId = shortcut.Id,
Mode = shortcut.Mode,
Action = shortcut.Action,
Key = shortcut.Key,
ModifierKeys = shortcut.ModifierKeys,
Sender = sender,
Origin = FocusManager.GetFocusedElement(XamlRoot)
};
await ViewModel.KeyboardShortcutHook(details);
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
{
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
}
if (details.Handled)
{
e.Handled = true;
}
}
private bool ShouldIgnoreShortcut()
{
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
return true;
if (focusedElement is FrameworkElement frameworkElement)
{
var typeName = frameworkElement.GetType().Name;
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private static ModifierKeys GetCurrentModifierKeys()
{
var modifiers = ModifierKeys.None;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Control;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Alt;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Shift;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
{
modifiers |= ModifierKeys.Windows;
}
return modifiers;
}
private static string NormalizeKey(Windows.System.VirtualKey key)
{
return key switch
{
Windows.System.VirtualKey.Control or
Windows.System.VirtualKey.LeftControl or
Windows.System.VirtualKey.RightControl or
Windows.System.VirtualKey.Menu or
Windows.System.VirtualKey.LeftMenu or
Windows.System.VirtualKey.RightMenu or
Windows.System.VirtualKey.Shift or
Windows.System.VirtualKey.LeftShift or
Windows.System.VirtualKey.RightShift or
Windows.System.VirtualKey.LeftWindows or
Windows.System.VirtualKey.RightWindows => string.Empty,
_ => key.ToString()
};
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
WeakReferenceMessenger.Default.Register<AccountMenuItemExtended>(this);
WeakReferenceMessenger.Default.Register<CreateNewMailWithMultipleAccountsRequested>(this);
WeakReferenceMessenger.Default.Register<NavigateMailFolderEvent>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
WeakReferenceMessenger.Default.Unregister<AccountMenuItemExtended>(this);
WeakReferenceMessenger.Default.Unregister<CreateNewMailWithMultipleAccountsRequested>(this);
WeakReferenceMessenger.Default.Unregister<NavigateMailFolderEvent>(this);
}
}
@@ -1,12 +0,0 @@
using Wino.Calendar.ViewModels;
using Wino.Mail.WinUI;
namespace Wino.Mail.Views.Abstract;
public abstract class CalendarAppShellAbstract : BasePage<CalendarAppShellViewModel>
{
protected CalendarAppShellAbstract()
{
NavigationCacheMode = Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Disabled;
}
}
@@ -1,12 +0,0 @@
using Wino.Mail.ViewModels;
using Wino.Mail.WinUI;
namespace Wino.Views.Abstract;
public abstract class MailAppShellAbstract : BasePage<MailAppShellViewModel>
{
protected MailAppShellAbstract()
{
NavigationCacheMode = Microsoft.UI.Xaml.Navigation.NavigationCacheMode.Disabled;
}
}
@@ -1,308 +0,0 @@
<abstract:CalendarAppShellAbstract
x:Class="Wino.Mail.WinUI.Views.Calendar.CalendarAppShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Mail.Views.Abstract"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:calendarControls="using:Wino.Calendar.Controls"
xmlns:communityControls="using:CommunityToolkit.WinUI.Controls"
xmlns:coreControls="using:Wino.Mail.WinUI.Controls"
xmlns:coreSelectors="using:Wino.Mail.WinUI.Selectors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Wino.Calendar.ViewModels.Data"
xmlns:domain="using:Wino.Core.Domain"
xmlns:helpers="using:Wino.Helpers"
xmlns:local="using:Wino.Calendar.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:menu="using:Wino.Core.Domain.MenuItems"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Loaded="OnLoaded"
PreviewKeyDown="OnPreviewKeyDown"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="CalendarSettingsItemTemplate" x:DataType="menu:SettingsItem">
<coreControls:WinoNavigationViewItem DataContext="{x:Bind}" SelectsOnInvoked="False">
<coreControls:WinoNavigationViewItem.Icon>
<muxc:AnimatedIcon>
<muxc:AnimatedIcon.Source>
<animatedvisuals:AnimatedSettingsVisualSource />
</muxc:AnimatedIcon.Source>
<muxc:AnimatedIcon.FallbackIconSource>
<muxc:SymbolIconSource Symbol="Setting" />
</muxc:AnimatedIcon.FallbackIconSource>
</muxc:AnimatedIcon>
</coreControls:WinoNavigationViewItem.Icon>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.MenuSettings}" />
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<DataTemplate x:Key="CalendarStoreUpdateItemTemplate" x:DataType="menu:StoreUpdateMenuItem">
<coreControls:WinoNavigationViewItem DataContext="{x:Bind}" SelectsOnInvoked="False">
<muxc:NavigationViewItem.Icon>
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE896;" />
</muxc:NavigationViewItem.Icon>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.MenuUpdateAvailable}" />
</coreControls:WinoNavigationViewItem>
</DataTemplate>
<coreSelectors:NavigationMenuTemplateSelector
x:Key="NavigationMenuTemplateSelector"
RatingItemTemplate="{StaticResource RatingItemTemplate}"
SeperatorTemplate="{StaticResource SeperatorTemplate}" />
<Style
x:Key="CalendarNavigationButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="Padding" Value="8,4,8,6" />
<Setter Property="CornerRadius" Value="6" />
<Setter Property="Width" Value="40" />
</Style>
</Page.Resources>
<Grid
x:Name="RootGrid"
Padding="0"
ColumnSpacing="0"
RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
Grid.ColumnSpan="2"
Background="{ThemeResource WinoApplicationBackgroundColor}"
IsHitTestVisible="False">
<Grid.BackgroundTransition>
<BrushTransition />
</Grid.BackgroundTransition>
</Grid>
<muxc:NavigationView
x:Name="navigationView"
Grid.Row="1"
Grid.ColumnSpan="3"
Background="Transparent"
DisplayModeChanged="NavigationViewDisplayModeChanged"
IsBackButtonVisible="Collapsed"
IsPaneOpen="{x:Bind ViewModel.PreferencesService.IsNavigationPaneOpened, Mode=TwoWay}"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
IsTitleBarAutoPaddingEnabled="False"
ItemInvoked="NavigationViewItemInvoked"
MenuItemTemplateSelector="{StaticResource NavigationMenuTemplateSelector}"
MenuItemsSource="{x:Bind ViewModel.MenuItems, Mode=OneWay}"
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=TwoWay}"
PaneClosed="NavigationPaneClosed"
PaneDisplayMode="Auto"
PaneOpened="NavigationPaneOpened"
Style="{StaticResource CalendarShellNavigationViewStyle}">
<muxc:NavigationView.PaneCustomContent>
<Grid x:Name="PaneCustomContent" Padding="0,0,0,6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<coreControls:WinoNavigationViewItem
x:Name="NewCalendarEventNavigationItem"
Grid.Row="0"
Height="50"
Margin="0,0,0,12"
AutomationProperties.Name="{x:Bind domain:Translator.CalendarEventCompose_NewEventButton, Mode=OneTime}"
IsTabStop="True"
KeyDown="NewCalendarEventNavigationItemKeyDown"
SelectsOnInvoked="False"
Tapped="NewCalendarEventNavigationItemTapped">
<muxc:NavigationViewItem.Icon>
<coreControls:WinoFontIcon Icon="NewMail" />
</muxc:NavigationViewItem.Icon>
<TextBlock
Margin="0,-2,0,0"
VerticalAlignment="Center"
FontSize="16"
Style="{StaticResource FlyoutPickerTitleTextBlockStyle}"
Text="{x:Bind domain:Translator.CalendarEventCompose_NewEventButton, Mode=OneTime}" />
</coreControls:WinoNavigationViewItem>
<Border
x:Name="RangeSummaryCard"
Grid.Row="1"
Margin="16,0,16,12"
Padding="12"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="8">
<StackPanel Spacing="12">
<TextBlock
FontWeight="SemiBold"
Text="{x:Bind ViewModel.VisibleDateRangeText, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
<muxc:CalendarView
x:Name="VisibleDateRangeCalendarView"
HorizontalAlignment="Stretch"
SelectionMode="Multiple"
SelectedDatesChanged="VisibleDateRangeCalendarViewSelectedDatesChanged" />
</StackPanel>
</Border>
<!-- Account Calendars Host -->
<ListView
x:Name="CalendarHostListView"
Grid.Row="2"
ItemsSource="{x:Bind ViewModel.AccountCalendarStateService.GroupedAccountCalendars}"
SelectionMode="None">
<ListView.Header>
<TextBlock
Margin="20,12,12,12"
FontSize="16"
Text="Calendars" />
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:GroupedAccountCalendarViewModel">
<muxc:Expander
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}">
<muxc:Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Width="26"
MinWidth="0"
IsChecked="{x:Bind IsCheckedState, Mode=TwoWay}"
IsThreeState="True" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
TextWrapping="Wrap">
<Run FontWeight="SemiBold" Text="{x:Bind Account.Name}" />
<Run FontSize="12" Text="(" /><Run FontSize="12" Text="{x:Bind Account.Address}" /><Run FontSize="12" Text=")" />
</TextBlock>
</Grid>
</muxc:Expander.Header>
<muxc:Expander.Content>
<ItemsControl ItemsSource="{x:Bind AccountCalendars}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="data:AccountCalendarViewModel">
<CheckBox
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsChecked="{x:Bind IsChecked, Mode=TwoWay}">
<Grid Margin="0,0,0,5" ColumnSpacing="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse
Width="20"
Height="20"
Fill="{x:Bind helpers:XamlHelpers.GetSolidColorBrushFromHex(BackgroundColorHex), Mode=OneWay}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
FontSize="14"
Text="{x:Bind Name, Mode=OneWay}"
TextWrapping="Wrap" />
</Grid>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</muxc:Expander.Content>
</muxc:Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</muxc:NavigationView.PaneCustomContent>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Main Content -->
<Frame
x:Name="InnerShellFrame"
Padding="0,0,7,7"
CacheSize="0"
IsNavigationStackEnabled="True">
<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" />
</Grid>
</muxc:NavigationView>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LowResolutionStates">
<VisualState x:Name="BigScreen">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1200" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="navigationView.IsPaneOpen" Value="True" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="SmallScreen">
<VisualState.Setters>
<Setter Target="navigationView.IsPaneOpen" Value="False" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CalendarOrientationStates">
<VisualState x:Name="HorizontalCalendar" />
<VisualState x:Name="VerticalCalendar" />
</VisualStateGroup>
<VisualStateGroup x:Name="ShellStateContentGroup">
<VisualState x:Name="DefaultShellContentState" />
<VisualState x:Name="EventDetailsContentState">
<VisualState.Setters>
<Setter Target="RangeSummaryCard.IsEnabled" Value="False" />
<Setter Target="CalendarHostListView.IsEnabled" Value="False" />
</VisualState.Setters>
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.StatePersistenceService.IsEventDetailsVisible, Mode=OneWay}" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</abstract:CalendarAppShellAbstract>
@@ -1,307 +0,0 @@
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.MenuItems;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Calendar;
using Wino.Mail.Views.Abstract;
using Wino.Messaging.Client.Calendar;
using Windows.System;
namespace Wino.Mail.WinUI.Views.Calendar;
public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
IRecipient<CalendarDisplayTypeChangedMessage>
{
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar";
private bool _isSynchronizingVisibleDateRangeCalendar;
public Frame GetShellFrame() => InnerShellFrame;
public CalendarAppShell()
{
InitializeComponent();
ViewModel.PropertyChanged += ViewModelPropertyChanged;
ManageCalendarDisplayType(ViewModel.StatePersistenceService.CalendarDisplayType);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
UpdateNavigationPaneLayout(navigationView.DisplayMode);
SynchronizeVisibleDateRangeCalendar();
}
private void ManageCalendarDisplayType(Core.Domain.Enums.CalendarDisplayType displayType)
{
if (displayType == Core.Domain.Enums.CalendarDisplayType.Month)
{
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
}
else
{
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
}
}
private void PreviousDateClicked(object sender, RoutedEventArgs e)
=> ViewModel.PreviousDateRangeCommand.Execute(null);
private void NextDateClicked(object sender, RoutedEventArgs e)
=> ViewModel.NextDateRangeCommand.Execute(null);
private async void NewCalendarEventNavigationItemTapped(object sender, TappedRoutedEventArgs e)
{
e.Handled = true;
await InvokeNewCalendarEventAsync();
}
private void VisibleDateRangeCalendarViewSelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
{
if (_isSynchronizingVisibleDateRangeCalendar)
return;
DateTimeOffset? interactedDate = null;
if (args.AddedDates.Count > 0)
{
interactedDate = args.AddedDates[0];
}
else if (args.RemovedDates.Count > 0)
{
interactedDate = args.RemovedDates[0];
}
if (interactedDate is null)
return;
var clickedArgs = new CalendarViewDayClickedEventArgs(interactedDate.Value.DateTime);
if (ViewModel.DateClickedCommand.CanExecute(clickedArgs))
{
ViewModel.DateClickedCommand.Execute(clickedArgs);
}
}
private async void NewCalendarEventNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
return;
e.Handled = true;
await InvokeNewCalendarEventAsync();
}
private async void NavigationViewItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.InvokedItemContainer is FrameworkElement { DataContext: IMenuItem menuItem })
{
await ViewModel.HandleNavigationItemInvokedAsync(menuItem);
}
}
private void NavigationViewDisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
=> UpdateNavigationPaneLayout(args.DisplayMode);
private void NavigationPaneOpened(NavigationView sender, object args)
=> UpdateNavigationPaneLayout(sender.DisplayMode);
private void NavigationPaneClosed(NavigationView sender, object args)
=> UpdateNavigationPaneLayout(sender.DisplayMode);
private Task InvokeNewCalendarEventAsync()
=> ViewModel.HandleNavigationItemInvokedAsync(new NewCalendarEventMenuItem());
private void UpdateNavigationPaneLayout(NavigationViewDisplayMode displayMode)
{
var paneContentVisibility = displayMode == NavigationViewDisplayMode.Expanded && navigationView.IsPaneOpen
? Visibility.Visible
: Visibility.Collapsed;
PaneCustomContent.Visibility = paneContentVisibility;
Debug.WriteLine($"NavigationView display mode changed to {displayMode}. Pane custom content visibility set to {paneContentVisibility}.");
}
public void Receive(CalendarDisplayTypeChangedMessage message)
{
ManageCalendarDisplayType(message.NewDisplayType);
SynchronizeVisibleDateRangeCalendar();
}
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(ViewModel.CurrentVisibleRange) or nameof(ViewModel.VisibleDateRangeText))
{
SynchronizeVisibleDateRangeCalendar();
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
InnerShellFrame.BackStack.Clear();
InnerShellFrame.ForwardStack.Clear();
if (InnerShellFrame.Content is IDisposable disposableContent)
{
disposableContent.Dispose();
}
Bindings.StopTracking();
}
protected override void RegisterRecipients()
{
base.RegisterRecipients();
WeakReferenceMessenger.Default.Register<CalendarDisplayTypeChangedMessage>(this);
}
protected override void UnregisterRecipients()
{
base.UnregisterRecipients();
WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
}
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
return;
var key = NormalizeKey(e.Key);
if (string.IsNullOrEmpty(key))
return;
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Calendar, key, GetCurrentModifierKeys());
if (shortcut == null)
return;
var details = new KeyboardShortcutTriggerDetails
{
ShortcutId = shortcut.Id,
Mode = shortcut.Mode,
Action = shortcut.Action,
Key = shortcut.Key,
ModifierKeys = shortcut.ModifierKeys,
Sender = sender,
Origin = FocusManager.GetFocusedElement(XamlRoot)
};
await ViewModel.KeyboardShortcutHook(details);
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
{
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
}
if (details.Handled)
{
e.Handled = true;
}
}
private bool ShouldIgnoreShortcut()
{
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
return true;
if (focusedElement is FrameworkElement frameworkElement)
{
var typeName = frameworkElement.GetType().Name;
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private static ModifierKeys GetCurrentModifierKeys()
{
var modifiers = ModifierKeys.None;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Control;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Alt;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
modifiers |= ModifierKeys.Shift;
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
{
modifiers |= ModifierKeys.Windows;
}
return modifiers;
}
private static string NormalizeKey(Windows.System.VirtualKey key)
{
return key switch
{
Windows.System.VirtualKey.Control or
Windows.System.VirtualKey.LeftControl or
Windows.System.VirtualKey.RightControl or
Windows.System.VirtualKey.Menu or
Windows.System.VirtualKey.LeftMenu or
Windows.System.VirtualKey.RightMenu or
Windows.System.VirtualKey.Shift or
Windows.System.VirtualKey.LeftShift or
Windows.System.VirtualKey.RightShift or
Windows.System.VirtualKey.LeftWindows or
Windows.System.VirtualKey.RightWindows => string.Empty,
_ => key.ToString()
};
}
private void SynchronizeVisibleDateRangeCalendar()
{
if (!DispatcherQueue.HasThreadAccess)
{
var enqueued = DispatcherQueue.TryEnqueue(SynchronizeVisibleDateRangeCalendar);
if (!enqueued)
throw new InvalidOperationException("Could not marshal visible date range calendar synchronization onto the UI thread.");
return;
}
_isSynchronizingVisibleDateRangeCalendar = true;
try
{
VisibleDateRangeCalendarView.SelectedDates.Clear();
var currentRange = ViewModel.CurrentVisibleRange;
if (currentRange == null)
return;
foreach (var date in currentRange.Dates)
{
VisibleDateRangeCalendarView.SelectedDates.Add(new DateTimeOffset(date.ToDateTime(TimeOnly.MinValue)));
}
VisibleDateRangeCalendarView.SetDisplayDate(new DateTimeOffset(currentRange.AnchorDate.ToDateTime(TimeOnly.MinValue)));
}
finally
{
_isSynchronizingVisibleDateRangeCalendar = false;
}
}
}
+2 -15
View File
@@ -568,27 +568,14 @@
Text="{x:Bind domain:Translator.CalendarEventCompose_NewEventButton, Mode=OneTime}" />
</coreControls:WinoNavigationViewItem>
<Border
x:Name="RangeSummaryCard"
Grid.Row="1"
Margin="16,0,16,12"
Padding="12"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="8">
<StackPanel Spacing="12">
<TextBlock
FontWeight="SemiBold"
Text="{x:Bind ViewModel.CalendarClient.VisibleDateRangeText, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
<muxc:CalendarView
x:Name="VisibleDateRangeCalendarView"
Grid.Row="1"
Margin="12,0"
HorizontalAlignment="Stretch"
SelectedDatesChanged="VisibleDateRangeCalendarViewSelectedDatesChanged"
SelectionMode="Multiple"
Style="{StaticResource WinoCalendarViewStyle}" />
</StackPanel>
</Border>
<ListView
x:Name="CalendarHostListView"
@@ -66,6 +66,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
ViewModel.MailClient.PropertyChanged += MailClientPropertyChanged;
ViewModel.CalendarClient.PropertyChanged += CalendarClientPropertyChanged;
ViewModel.PropertyChanged += ViewModelPropertyChanged;
ViewModel.PreferencesService.PreferenceChanged += PreferencesServiceChanged;
ViewModel.StatePersistenceService.StatePropertyChanged += StatePersistenceServiceChanged;
CalendarTypeSelector.RegisterPropertyChangedCallback(WinoCalendarTypeSelectorControl.SelectedTypeProperty, CalendarTypeSelectorSelectedTypeChanged);
@@ -561,6 +562,7 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
try
{
VisibleDateRangeCalendarView.FirstDayOfWeek = MapFirstDayOfWeek(ViewModel.PreferencesService.FirstDayOfWeek);
VisibleDateRangeCalendarView.SelectedDates.Clear();
var currentRange = ViewModel.CalendarClient.CurrentVisibleRange;
@@ -580,6 +582,27 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
}
}
private void PreferencesServiceChanged(object? sender, string propertyName)
{
if (propertyName == nameof(IPreferencesService.FirstDayOfWeek))
{
SynchronizeVisibleDateRangeCalendar();
}
}
private static Windows.Globalization.DayOfWeek MapFirstDayOfWeek(DayOfWeek dayOfWeek)
=> dayOfWeek switch
{
DayOfWeek.Sunday => Windows.Globalization.DayOfWeek.Sunday,
DayOfWeek.Monday => Windows.Globalization.DayOfWeek.Monday,
DayOfWeek.Tuesday => Windows.Globalization.DayOfWeek.Tuesday,
DayOfWeek.Wednesday => Windows.Globalization.DayOfWeek.Wednesday,
DayOfWeek.Thursday => Windows.Globalization.DayOfWeek.Thursday,
DayOfWeek.Friday => Windows.Globalization.DayOfWeek.Friday,
DayOfWeek.Saturday => Windows.Globalization.DayOfWeek.Saturday,
_ => Windows.Globalization.DayOfWeek.Monday
};
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(ViewModel.SelectedMenuItem) || !ViewModel.CurrentClient.HandlesNavigationSelection)