diff --git a/Wino.Core.Domain/Interfaces/IPreferencesService.cs b/Wino.Core.Domain/Interfaces/IPreferencesService.cs index 505b309c..c5c377ea 100644 --- a/Wino.Core.Domain/Interfaces/IPreferencesService.cs +++ b/Wino.Core.Domain/Interfaces/IPreferencesService.cs @@ -155,5 +155,10 @@ namespace Wino.Core.Domain.Interfaces /// Setting: Gets or sets what should happen to server app when the client is terminated. /// ServerBackgroundMode ServerTerminationBehavior { get; set; } + + /// + /// Setting: Whether the mail list action bar is enabled or not. + /// + bool IsMailListActionBarEnabled { get; set; } } } diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index c0739152..807d8b08 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -519,7 +519,7 @@ "SettingsRenameMergeAccount_Title": "Rename", "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", - "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Description": "Hide/show the preview text.", "SettingsShowPreviewText_Title": "Show Preview Text", "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", "SettingsShowSenderPictures_Title": "Show Sender Avatars", @@ -533,7 +533,9 @@ "SettingsStore_Title": "Rate in Store", "SettingsThreads_Description": "Organize messages into conversation threads.", "SettingsThreads_Title": "Conversation Threading", - "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsMailListActionBar_Description": "Hide/show action bar at top of message list.", + "SettingsMailListActionBar_Title": "Show mail list actions", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. his will not delete your accounts.", "SettingsUnlinkAccounts_Title": "Unlink Accounts", "SortingOption_Date": "by date", "SortingOption_Name": "by name", diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs index 4f086782..ca031f72 100644 --- a/Wino.Core.Domain/Translator.Designer.cs +++ b/Wino.Core.Domain/Translator.Designer.cs @@ -2619,7 +2619,7 @@ namespace Wino.Core.Domain public static string SettingsSemanticZoom_Title => Resources.GetTranslatedString(@"SettingsSemanticZoom_Title"); /// - /// Hide/show thepreview text. + /// Hide/show the preview text. /// public static string SettingsShowPreviewText_Description => Resources.GetTranslatedString(@"SettingsShowPreviewText_Description"); @@ -2689,7 +2689,17 @@ namespace Wino.Core.Domain public static string SettingsThreads_Title => Resources.GetTranslatedString(@"SettingsThreads_Title"); /// - /// Remove the link between accounts. This will not delete your accounts. + /// Hide/show action bar at top of message list. + /// + public static string SettingsMailListActionBar_Description => Resources.GetTranslatedString(@"SettingsMailListActionBar_Description"); + + /// + /// Show mail list actions + /// + public static string SettingsMailListActionBar_Title => Resources.GetTranslatedString(@"SettingsMailListActionBar_Title"); + + /// + /// Remove the link between accounts. his will not delete your accounts. /// public static string SettingsUnlinkAccounts_Description => Resources.GetTranslatedString(@"SettingsUnlinkAccounts_Description"); diff --git a/Wino.Core.UWP/Services/PreferencesService.cs b/Wino.Core.UWP/Services/PreferencesService.cs index 7d77aef5..d7826f0e 100644 --- a/Wino.Core.UWP/Services/PreferencesService.cs +++ b/Wino.Core.UWP/Services/PreferencesService.cs @@ -64,6 +64,12 @@ namespace Wino.Core.UWP.Services set => SetPropertyAndSave(nameof(IsThreadingEnabled), value); } + public bool IsMailListActionBarEnabled + { + get => _configurationService.Get(nameof(IsMailListActionBarEnabled), false); + set => SetPropertyAndSave(nameof(IsMailListActionBarEnabled), value); + } + public bool IsShowSenderPicturesEnabled { get => _configurationService.Get(nameof(IsShowSenderPicturesEnabled), true); diff --git a/Wino.Core/Services/ContextMenuItemService.cs b/Wino.Core/Services/ContextMenuItemService.cs index 3c1040fe..e6238e8f 100644 --- a/Wino.Core/Services/ContextMenuItemService.cs +++ b/Wino.Core/Services/ContextMenuItemService.cs @@ -80,27 +80,26 @@ namespace Wino.Core.Services } else { - bool isAllFlagged = selectedMailItems.All(a => a.IsFlagged); bool isAllRead = selectedMailItems.All(a => a.IsRead); bool isAllUnread = selectedMailItems.All(a => !a.IsRead); + bool isAllFlagged = selectedMailItems.All(a => a.IsFlagged); + bool isAllNotFlagged = selectedMailItems.All(a => !a.IsFlagged); - if (isAllRead) - operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread)); - else + List readOperations = (isAllRead, isAllUnread) switch { - if (!isAllUnread) - operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread)); + (true, false) => [MailOperationMenuItem.Create(MailOperation.MarkAsUnread)], + (false, true) => [MailOperationMenuItem.Create(MailOperation.MarkAsRead)], + _ => [MailOperationMenuItem.Create(MailOperation.MarkAsRead), MailOperationMenuItem.Create(MailOperation.MarkAsUnread)] + }; + operationList.AddRange(readOperations); - operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead)); - } - - if (isAllFlagged) - operationList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); - else + List flagsOperations = (isAllFlagged, isAllNotFlagged) switch { - operationList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); - operationList.Add(MailOperationMenuItem.Create(MailOperation.SetFlag)); - } + (true, false) => [MailOperationMenuItem.Create(MailOperation.ClearFlag)], + (false, true) => [MailOperationMenuItem.Create(MailOperation.SetFlag)], + _ => [MailOperationMenuItem.Create(MailOperation.SetFlag), MailOperationMenuItem.Create(MailOperation.ClearFlag)] + }; + operationList.AddRange(flagsOperations); } // Ignore diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index a09c3d67..47dedbd2 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -41,8 +41,7 @@ namespace Wino.Mail.ViewModels IRecipient, IRecipient, IRecipient, - IRecipient, - IRecipient + IRecipient { private bool isChangingFolder = false; @@ -63,6 +62,7 @@ namespace Wino.Mail.ViewModels public ObservableCollection SelectedItems { get; set; } = []; public ObservableCollection PivotFolders { get; set; } = []; + public ObservableCollection ActionItems { get; set; } = []; private readonly SemaphoreSlim listManipulationSemepahore = new SemaphoreSlim(1); private CancellationTokenSource listManipulationCancellationTokenSource = new CancellationTokenSource(); @@ -200,6 +200,13 @@ namespace Wino.Mail.ViewModels }; } + private void SetupTopBarActions() + { + ActionItems.Clear(); + var actions = GetAvailableMailActions(SelectedItems); + actions.ForEach(a => ActionItems.Add(a)); + } + #region Properties /// @@ -365,7 +372,7 @@ namespace Wino.Mail.ViewModels NotifyItemSelected(); - Messenger.Send(new SelectedMailItemsChanged(SelectedItems.Count)); + SetupTopBarActions(); } private void UpdateFolderPivots() @@ -415,19 +422,31 @@ namespace Wino.Mail.ViewModels [RelayCommand] public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request); + [RelayCommand] + private async Task ExecuteTopBarAction(MailOperationMenuItem menuItem) + { + if (menuItem == null || !SelectedItems.Any()) return; + + await HandleMailOperation(menuItem.Operation, SelectedItems); + } + /// /// Executes the requested mail operation for currently selected items. /// /// Action to execute for selected items. [RelayCommand] - private async Task MailOperationAsync(int mailOperationIndex) + private async Task ExecuteMailOperation(MailOperation mailOperation) { if (!SelectedItems.Any()) return; - // Commands don't like enums. So it has to be int. - var operation = (MailOperation)mailOperationIndex; + await HandleMailOperation(mailOperation, SelectedItems); + } - var package = new MailOperationPreperationRequest(operation, SelectedItems.Select(a => a.MailCopy)); + private async Task HandleMailOperation(MailOperation mailOperation, IEnumerable mailItems) + { + if (!mailItems.Any()) return; + + var package = new MailOperationPreperationRequest(mailOperation, mailItems.Select(a => a.MailCopy)); await ExecuteMailOperationAsync(package); } @@ -649,6 +668,8 @@ namespace Wino.Mail.ViewModels Debug.WriteLine($"Updating {updatedMail.Id}-> {updatedMail.UniqueId}"); await MailCollection.UpdateMailCopy(updatedMail); + + await ExecuteUIThread(() => { SetupTopBarActions(); }); } protected override async void OnMailRemoved(MailCopy removedMail) @@ -1012,7 +1033,5 @@ namespace Wino.Mail.ViewModels await ExecuteUIThread(() => { IsAccountSynchronizerInSynchronization = isAnyAccountSynchronizing; }); } - - public void Receive(SelectedMailItemsChanged message) => NotifyItemSelected(); } } diff --git a/Wino.Mail/App.xaml b/Wino.Mail/App.xaml index bf1fe890..91b2d0b2 100644 --- a/Wino.Mail/App.xaml +++ b/Wino.Mail/App.xaml @@ -11,6 +11,8 @@ + + diff --git a/Wino.Mail/Controls/Advanced/WinoListView.cs b/Wino.Mail/Controls/Advanced/WinoListView.cs index 10f215da..764084e2 100644 --- a/Wino.Mail/Controls/Advanced/WinoListView.cs +++ b/Wino.Mail/Controls/Advanced/WinoListView.cs @@ -120,7 +120,7 @@ namespace Wino.Controls.Advanced { args.Handled = true; - ItemDeletedCommand?.Execute((int)MailOperation.SoftDelete); + ItemDeletedCommand?.Execute(MailOperation.SoftDelete); } } diff --git a/Wino.Mail/Controls/WinoPivotControl.xaml b/Wino.Mail/Controls/WinoPivotControl.xaml deleted file mode 100644 index a524e6d3..00000000 --- a/Wino.Mail/Controls/WinoPivotControl.xaml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Wino.Mail/Controls/WinoPivotControl.xaml.cs b/Wino.Mail/Controls/WinoPivotControl.xaml.cs deleted file mode 100644 index 975871bc..00000000 --- a/Wino.Mail/Controls/WinoPivotControl.xaml.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Runtime.InteropServices.WindowsRuntime; -using System.Threading.Tasks; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; -using Wino.Extensions; - -namespace Wino.Controls -{ - // TODO: Memory leak with FolderPivot bindings. - public sealed partial class WinoPivotControl : UserControl - { - private Compositor _compositor; - private ShapeVisual _shapeVisual; - private CompositionSpriteShape _spriteShape; - private CompositionRoundedRectangleGeometry _roundedRectangle; - - public event EventHandler SelectionChanged; - - public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(WinoPivotControl), new PropertyMetadata(null)); - public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(WinoPivotControl), new PropertyMetadata(null)); - public static readonly DependencyProperty SelectorPipeColorProperty = DependencyProperty.Register(nameof(SelectorPipeColor), typeof(SolidColorBrush), typeof(WinoPivotControl), new PropertyMetadata(Colors.Transparent, OnSelectorPipeColorChanged)); - public static readonly DependencyProperty DataTemplateProperty = DependencyProperty.Register(nameof(DataTemplate), typeof(DataTemplate), typeof(WinoPivotControl), new PropertyMetadata(null)); - - public DataTemplate DataTemplate - { - get { return (DataTemplate)GetValue(DataTemplateProperty); } - set { SetValue(DataTemplateProperty, value); } - } - - public SolidColorBrush SelectorPipeColor - { - get { return (SolidColorBrush)GetValue(SelectorPipeColorProperty); } - set { SetValue(SelectorPipeColorProperty, value); } - } - - public object SelectedItem - { - get { return (object)GetValue(SelectedItemProperty); } - set { SetValue(SelectedItemProperty, value); } - } - - public object ItemsSource - { - get { return (object)GetValue(ItemsSourceProperty); } - set { SetValue(ItemsSourceProperty, value); } - } - - private static void OnSelectorPipeColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) - { - if (obj is WinoPivotControl control) - { - control.UpdateSelectorPipeColor(); - } - } - - private void UpdateSelectorPipeColor() - { - if (_spriteShape != null && _compositor != null) - { - _spriteShape.FillBrush = _compositor.CreateColorBrush(SelectorPipeColor.Color); - } - } - - private void CreateSelectorVisuals() - { - _compositor = this.Visual().Compositor; - - _roundedRectangle = _compositor.CreateRoundedRectangleGeometry(); - _roundedRectangle.CornerRadius = new Vector2(3, 3); - - _spriteShape = _compositor.CreateSpriteShape(_roundedRectangle); - _spriteShape.CenterPoint = new Vector2(100, 100); - - _shapeVisual = _compositor.CreateShapeVisual(); - - _shapeVisual.Shapes.Clear(); - _shapeVisual.Shapes.Add(_spriteShape); - - SelectorPipe.SetChildVisual(_shapeVisual); - - _shapeVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400); - } - - public WinoPivotControl() - { - this.InitializeComponent(); - - CreateSelectorVisuals(); - } - - private bool IsContainerPresent() - { - return SelectedItem != null && PivotHeaders.ContainerFromItem(SelectedItem) != null; - } - - private void PivotHeaders_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - UpdateVisuals(); - - SelectionChanged?.Invoke(sender, e); - } - - private void UpdateVisuals() - { - MoveSelector(); - } - - private void UpdateSelectorVisibility() - { - SelectorPipe.Visibility = IsContainerPresent() ? Visibility.Visible : Visibility.Collapsed; - } - - private async void MoveSelector() - { - if (PivotHeaders.SelectedItem != null) - { - // Get selected item container position - // TODO: It's bad... - while(PivotHeaders.ContainerFromItem(PivotHeaders.SelectedItem) == null) - { - await Task.Delay(100); - } - - UpdateSelectorVisibility(); - - var container = PivotHeaders.ContainerFromItem(PivotHeaders.SelectedItem) as FrameworkElement; - - if (container != null) - { - var transformToVisual = container.TransformToVisual(this); - Point screenCoords = transformToVisual.TransformPoint(new Point(0, 0)); - - float actualWidth = 0, leftMargin = 0, translateX = 0; - - leftMargin = (float)(screenCoords.X); - - if (PivotHeaders.Items.Count > 1) - { - // Multiple items, pipe is centered. - - actualWidth = (float)(container.ActualWidth + 12) / 2; - translateX = leftMargin - 10 + (actualWidth / 2); - } - else - { - actualWidth = (float)(container.ActualWidth) - 12; - translateX = leftMargin + 4; - } - - SelectorPipe.Width = actualWidth; - SelectorPipe.Translation = new Vector3(translateX, 0, 0); - } - else - { - Debug.WriteLine("Container null"); - } - } - } - - private void SelectorPipeSizeChanged(object sender, SizeChangedEventArgs e) - { - _roundedRectangle.Size = e.NewSize.ToVector2(); - _shapeVisual.Size = e.NewSize.ToVector2(); - } - - private void ControlUnloaded(object sender, RoutedEventArgs e) - { - //PivotHeaders.SelectionChanged -= PivotHeaders_SelectionChanged; - //PivotHeaders.SelectedItem = null; - - //SelectedItem = null; - //ItemsSource = null; - } - - private void ControlLoaded(object sender, RoutedEventArgs e) - { - // Bindings.Update(); - } - } -} diff --git a/Wino.Mail/Extensions/EnumerableExtensions.cs b/Wino.Mail/Extensions/EnumerableExtensions.cs deleted file mode 100644 index 4d94a9be..00000000 --- a/Wino.Mail/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections; - -namespace Wino.Extensions -{ - public static class EnumerableExtensions - { - public static IEnumerable OfType(this IEnumerable source) - { - foreach (object item in source) - { - if (item is T1 || item is T2) - { - yield return item; - } - } - } - } -} diff --git a/Wino.Mail/Views/MailListPage.xaml b/Wino.Mail/Views/MailListPage.xaml index 61328647..e7ec8e49 100644 --- a/Wino.Mail/Views/MailListPage.xaml +++ b/Wino.Mail/Views/MailListPage.xaml @@ -5,7 +5,6 @@ xmlns:abstract="using:Wino.Views.Abstract" xmlns:collections="using:CommunityToolkit.Mvvm.Collections" xmlns:controls="using:Wino.Controls" - xmlns:controls1="using:CommunityToolkit.WinUI.Controls" xmlns:converters="using:Wino.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:domain="using:Wino.Core.Domain" @@ -13,11 +12,14 @@ xmlns:helpers="using:Wino.Helpers" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:ic="using:Microsoft.Xaml.Interactions.Core" + xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:listview="using:Wino.Controls.Advanced" + xmlns:local="using:Wino.Behaviors" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:menuflyouts="using:Wino.MenuFlyouts" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:selectors="using:Wino.Selectors" + xmlns:toolkit="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" xmlns:viewModelData="using:Wino.Mail.ViewModels.Data" xmlns:wino="using:Wino" @@ -31,6 +33,28 @@ IsSourceGrouped="True" Source="{x:Bind ViewModel.MailCollection.MailItems, Mode=OneWay}" /> + + 0,0,0,0 0,0,12,0 0,0,0,0 @@ -45,23 +69,10 @@ - - - - - - - - - @@ -208,28 +219,6 @@ Transparent - - - - @@ -272,7 +261,7 @@ - + + Visibility="{x:Bind ViewModel.PreferencesService.IsMailListActionBarEnabled}"> + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - + MinWidth="0" + Margin="8,0,0,0" + VerticalAlignment="Center" + Canvas.ZIndex="100" + Checked="SelectAllCheckboxChecked" + Unchecked="SelectAllCheckboxUnchecked" + Visibility="{x:Bind helpers:XamlHelpers.IsSelectionModeMultiple(MailListView.SelectionMode), Mode=OneWay}" /> - - + + + + + + + + + + + + + - - - - + + + + + + + + @@ -502,20 +395,20 @@ SortingOptions="{x:Bind ViewModel.SortingOptions, Mode=OneTime}" /> + - - -