From b788531e4760659bfd128e0e8cefefe5905670af Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Wed, 8 May 2024 02:05:42 +0200 Subject: [PATCH 1/7] Reset all filters after navigation and clear button re-initializing mail list --- Wino.Mail.ViewModels/MailListPageViewModel.cs | 352 +++++++++--------- Wino.Mail/Controls/Advanced/WinoListView.cs | 2 +- Wino.Mail/Views/MailListPage.xaml | 77 +--- Wino.Mail/Views/MailListPage.xaml.cs | 8 + 4 files changed, 213 insertions(+), 226 deletions(-) diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index 6e9d4ae0..8848e11b 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -51,7 +51,7 @@ namespace Wino.Mail.ViewModels * to prevent them from being removed from the list when they are marked as read. */ - private HashSet gmailUnreadFolderMarkedAsReadUniqueIds = new HashSet(); + private readonly HashSet gmailUnreadFolderMarkedAsReadUniqueIds = []; private IObservable> selectionChangedObservable = null; @@ -68,7 +68,6 @@ namespace Wino.Mail.ViewModels public IPreferencesService PreferencesService { get; } private readonly IMailService _mailService; - private readonly INotificationBuilder _notificationBuilder; private readonly IFolderService _folderService; private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; private readonly IThreadingStrategyProvider _threadingStrategyProvider; @@ -100,12 +99,15 @@ namespace Wino.Mail.ViewModels private FilterOption _selectedFilterOption; private SortingOption _selectedSortingOption; + // Indicates state when folder is initializing. It can happen after folder navigation, search or filter change applied. [ObservableProperty] [NotifyPropertyChangedFor(nameof(IsEmpty))] [NotifyPropertyChangedFor(nameof(IsCriteriaFailed))] [NotifyPropertyChangedFor(nameof(IsFolderEmpty))] private bool isInitializingFolder; + private bool isLoadMoreItemsLoading; + [ObservableProperty] private InfoBarMessageType barSeverity; @@ -118,10 +120,21 @@ namespace Wino.Mail.ViewModels [ObservableProperty] private bool isBarOpen; + /// + /// Current folder that is being represented from the menu. + /// + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanSynchronize))] + [NotifyPropertyChangedFor(nameof(IsFolderSynchronizationEnabled))] + private IBaseFolderMenuItem activeFolder; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanSynchronize))] + private bool isAccountSynchronizerInSynchronization; + public MailListPageViewModel(IDialogService dialogService, IWinoNavigationService navigationService, IMailService mailService, - INotificationBuilder notificationBuilder, IStatePersistanceService statePersistanceService, IFolderService folderService, IWinoSynchronizerFactory winoSynchronizerFactory, @@ -136,7 +149,6 @@ namespace Wino.Mail.ViewModels NavigationService = navigationService; _mailService = mailService; - _notificationBuilder = notificationBuilder; _folderService = folderService; _winoSynchronizerFactory = winoSynchronizerFactory; _threadingStrategyProvider = threadingStrategyProvider; @@ -156,53 +168,6 @@ namespace Wino.Mail.ViewModels }); } - /// - /// Executes the requested mail operation for currently selected items. - /// - /// Action to execute for selected items. - [RelayCommand] - private async Task MailOperationAsync(int mailOperationIndex) - { - if (!SelectedItems.Any()) return; - - // Commands don't like enums. So it has to be int. - var operation = (MailOperation)mailOperationIndex; - - var package = new MailOperationPreperationRequest(operation, SelectedItems.Select(a => a.MailCopy)); - - await ExecuteMailOperationAsync(package); - } - - /// - /// Sens a new message to synchronize current folder. - /// - [RelayCommand] - private void SyncFolder() - { - if (!CanSynchronize) return; - - // Only synchronize listed folders. - - // When doing linked inbox sync, we need to save the sync id to report progress back only once. - // Otherwise, we will report progress for each folder and that's what we don't want. - - trackingSynchronizationId = Guid.NewGuid(); - completedTrackingSynchronizationCount = 0; - - foreach (var folder in ActiveFolder.HandlingFolders) - { - var options = new SynchronizationOptions() - { - AccountId = folder.MailAccountId, - Type = SynchronizationType.Custom, - SynchronizationFolderIds = [folder.Id], - GroupedSynchronizationTrackingId = trackingSynchronizationId - }; - - Messenger.Send(new NewSynchronizationRequested(options)); - } - } - private async void ActiveMailItemChanged(MailItemViewModel selectedMailItemViewModel) { if (_activeMailItem == selectedMailItemViewModel) return; @@ -278,34 +243,18 @@ namespace Wino.Mail.ViewModels } } - /// - /// Current folder that is being represented from the menu. - /// - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(CanSynchronize))] - [NotifyPropertyChangedFor(nameof(IsFolderSynchronizationEnabled))] - private IBaseFolderMenuItem activeFolder; - - [ObservableProperty] - [NotifyPropertyChangedFor(nameof(CanSynchronize))] - private bool isAccountSynchronizerInSynchronization; - - public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled; - - public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false; - #region Properties - + public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled; + public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false; public int SelectedItemCount => SelectedItems.Count; public bool HasMultipleItemSelections => SelectedItemCount > 1; public bool HasSelectedItems => SelectedItems.Any(); public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive; - public bool IsEmpty => !IsPerformingSearch && MailCollection.Count == 0; + public bool IsEmpty => !IsInitializingFolder && !IsPerformingSearch && MailCollection.Count == 0; public bool IsCriteriaFailed => IsEmpty && IsInSearchMode; public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !IsInSearchMode; private bool _isPerformingSearch; - public bool IsPerformingSearch { get => _isPerformingSearch; @@ -318,7 +267,7 @@ namespace Wino.Mail.ViewModels } } - public bool IsInSearchMode => !string.IsNullOrEmpty(SearchQuery); + public bool IsInSearchMode { get; set; } #endregion @@ -444,6 +393,55 @@ namespace Wino.Mail.ViewModels SelectedFolderPivot = PivotFolders.FirstOrDefault(); } + #region Commands + + /// + /// Executes the requested mail operation for currently selected items. + /// + /// Action to execute for selected items. + [RelayCommand] + private async Task MailOperationAsync(int mailOperationIndex) + { + if (!SelectedItems.Any()) return; + + // Commands don't like enums. So it has to be int. + var operation = (MailOperation)mailOperationIndex; + + var package = new MailOperationPreperationRequest(operation, SelectedItems.Select(a => a.MailCopy)); + + await ExecuteMailOperationAsync(package); + } + + /// + /// Sens a new message to synchronize current folder. + /// + [RelayCommand] + private void SyncFolder() + { + if (!CanSynchronize) return; + + // Only synchronize listed folders. + + // When doing linked inbox sync, we need to save the sync id to report progress back only once. + // Otherwise, we will report progress for each folder and that's what we don't want. + + trackingSynchronizationId = Guid.NewGuid(); + completedTrackingSynchronizationCount = 0; + + foreach (var folder in ActiveFolder.HandlingFolders) + { + var options = new SynchronizationOptions() + { + AccountId = folder.MailAccountId, + Type = SynchronizationType.Custom, + SynchronizationFolderIds = [folder.Id], + GroupedSynchronizationTrackingId = trackingSynchronizationId + }; + + Messenger.Send(new NewSynchronizationRequested(options)); + } + } + [RelayCommand] private async Task SelectedPivotChanged() { @@ -472,6 +470,74 @@ namespace Wino.Mail.ViewModels await InitializeFolderAsync(); } + [RelayCommand] + public async Task PerformSearchAsync() + { + try + { + if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode) + { + IsInSearchMode = false; + await InitializeFolderAsync(); + } + + if (!string.IsNullOrEmpty(SearchQuery)) + { + IsInSearchMode = true; + IsPerformingSearch = true; + await InitializeFolderAsync(); + } + } + finally + { + IsPerformingSearch = false; + } + } + + [RelayCommand] + private async Task EnableFolderSynchronizationAsync() + { + if (ActiveFolder == null) return; + + foreach (var folder in ActiveFolder.HandlingFolders) + { + await _folderService.ChangeFolderSynchronizationStateAsync(folder.Id, true); + } + + // TODO + //ActiveFolder.IsSynchronizationEnabled = true; + + //OnPropertyChanged(nameof(IsFolderSynchronizationEnabled)); + //OnPropertyChanged(nameof(CanSynchronize)); + + //SyncFolderCommand?.Execute(null); + } + + [RelayCommand] + private async Task LoadMoreItemsAsync() + { + if (IsInitializingFolder) return; + + await ExecuteUIThread(() => { IsInitializingFolder = true; }); + + var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders, + SelectedFilterOption.Type, + SelectedSortingOption.Type, + PreferencesService.IsThreadingEnabled, + SelectedFolderPivot.IsFocused, + IsInSearchMode ? SearchQuery : string.Empty, + MailCollection.MailCopyIdHashSet); + + var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false); + + var viewModels = PrepareMailViewModels(items); + + await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, clearIdCache: false); }); + await ExecuteUIThread(() => { IsInitializingFolder = false; }); + } + + #endregion + public IEnumerable GetTargetMailItemViewModels(IMailItem clickedItem) { // Threat threads as a whole and include everything in the group. Except single selections outside of the thread. @@ -496,7 +562,7 @@ namespace Wino.Mail.ViewModels if (includedInSelectedItems) contextMailItems = SelectedItems; else - contextMailItems = new List() { clickedMailItemViewModel }; + contextMailItems = [clickedMailItemViewModel]; } return contextMailItems; @@ -506,7 +572,7 @@ namespace Wino.Mail.ViewModels => _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems); public void ChangeCustomFocusedState(IEnumerable mailItems, bool isFocused) - => mailItems.Where(a => a is MailItemViewModel).Cast().ForEach(a => a.IsCustomFocused = isFocused); + => mailItems.OfType().ForEach(a => a.IsCustomFocused = isFocused); private bool ShouldPreventItemAdd(IMailItem mailItem) { @@ -650,29 +716,6 @@ namespace Wino.Mail.ViewModels } } - [RelayCommand] - private async Task LoadMoreItemsAsync() - { - if (IsInitializingFolder) return; - - await ExecuteUIThread(() => { IsInitializingFolder = true; }); - - var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders, - SelectedFilterOption.Type, - SelectedSortingOption.Type, - PreferencesService.IsThreadingEnabled, - SelectedFolderPivot.IsFocused, - SearchQuery, - MailCollection.MailCopyIdHashSet); - - var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false); - - var viewModels = PrepareMailViewModels(items); - - await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, clearIdCache: false); }); - await ExecuteUIThread(() => { IsInitializingFolder = false; }); - } - private async Task InitializeFolderAsync() { if (SelectedFilterOption == null || SelectedFolderPivot == null || SelectedSortingOption == null) @@ -680,10 +723,6 @@ namespace Wino.Mail.ViewModels try { - // Clear search query if not performing search. - if (!IsPerformingSearch) - SearchQuery = string.Empty; - MailCollection.Clear(); MailCollection.MailCopyIdHashSet.Clear(); @@ -768,53 +807,12 @@ namespace Wino.Mail.ViewModels } } - [RelayCommand] - private async Task EnableFolderSynchronizationAsync() - { - if (ActiveFolder == null) return; + #region Receivers + void IRecipient.Receive(MailItemSelectedEvent message) + => SelectedItems.Add(message.SelectedMailItem); - foreach (var folder in ActiveFolder.HandlingFolders) - { - await _folderService.ChangeFolderSynchronizationStateAsync(folder.Id, true); - } - - // TODO - //ActiveFolder.IsSynchronizationEnabled = true; - - //OnPropertyChanged(nameof(IsFolderSynchronizationEnabled)); - //OnPropertyChanged(nameof(CanSynchronize)); - - //SyncFolderCommand?.Execute(null); - } - - void IRecipient.Receive(MailItemNavigationRequested message) - { - // Find mail item and add to selected items. - - MailItemViewModel navigatingMailItem = null; - ThreadMailItemViewModel threadMailItemViewModel = null; - - for (int i = 0; i < 3; i++) - { - var mailContainer = MailCollection.GetMailItemContainer(message.UniqueMailId); - - if (mailContainer != null) - { - navigatingMailItem = mailContainer.ItemViewModel; - threadMailItemViewModel = mailContainer.ThreadViewModel; - - break; - } - } - - if (threadMailItemViewModel != null) - threadMailItemViewModel.IsThreadExpanded = true; - - if (navigatingMailItem != null) - WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(navigatingMailItem, message.ScrollToItem)); - else - Debugger.Break(); - } + void IRecipient.Receive(MailItemSelectionRemovedEvent message) + => SelectedItems.Remove(message.RemovedMailItem); async void IRecipient.Receive(ActiveMailFolderChangedEvent message) { @@ -835,6 +833,9 @@ namespace Wino.Mail.ViewModels // Prepare Focused - Other or folder name tabs. UpdateFolderPivots(); + // Reset filters and sorting options. + ResetFilters(); + await InitializeFolderAsync(); // TODO: This should be done in a better way. @@ -847,14 +848,15 @@ namespace Wino.Mail.ViewModels message.FolderInitLoadAwaitTask?.TrySetResult(true); isChangingFolder = false; + + void ResetFilters() + { + SelectedFilterOption = FilterOptions[0]; + SelectedSortingOption = SortingOptions[0]; + SearchQuery = string.Empty; + } } - void IRecipient.Receive(MailItemSelectedEvent message) - => SelectedItems.Add(message.SelectedMailItem); - - void IRecipient.Receive(MailItemSelectionRemovedEvent message) - => SelectedItems.Remove(message.RemovedMailItem); - public void Receive(AccountSynchronizationCompleted message) { if (ActiveFolder == null) return; @@ -889,24 +891,40 @@ namespace Wino.Mail.ViewModels } } + void IRecipient.Receive(MailItemNavigationRequested message) + { + // Find mail item and add to selected items. + + MailItemViewModel navigatingMailItem = null; + ThreadMailItemViewModel threadMailItemViewModel = null; + + for (int i = 0; i < 3; i++) + { + var mailContainer = MailCollection.GetMailItemContainer(message.UniqueMailId); + + if (mailContainer != null) + { + navigatingMailItem = mailContainer.ItemViewModel; + threadMailItemViewModel = mailContainer.ThreadViewModel; + + break; + } + } + + if (threadMailItemViewModel != null) + threadMailItemViewModel.IsThreadExpanded = true; + + if (navigatingMailItem != null) + WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(navigatingMailItem, message.ScrollToItem)); + else + Debugger.Break(); + } + + #endregion + public async void Receive(NewSynchronizationRequested message) => await ExecuteUIThread(() => { OnPropertyChanged(nameof(CanSynchronize)); }); - [RelayCommand] - public async Task PerformSearchAsync() - { - try - { - IsPerformingSearch = !string.IsNullOrEmpty(SearchQuery); - - await InitializeFolderAsync(); - } - finally - { - IsPerformingSearch = false; - } - } - public async void Receive(AccountSynchronizerStateChanged message) => await CheckIfAccountIsSynchronizingAsync(); diff --git a/Wino.Mail/Controls/Advanced/WinoListView.cs b/Wino.Mail/Controls/Advanced/WinoListView.cs index a6382321..a1f7ece3 100644 --- a/Wino.Mail/Controls/Advanced/WinoListView.cs +++ b/Wino.Mail/Controls/Advanced/WinoListView.cs @@ -205,7 +205,7 @@ namespace Wino.Controls.Advanced { bool found = false; - Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + Items.OfType().ForEach(c => { if (!found) { diff --git a/Wino.Mail/Views/MailListPage.xaml b/Wino.Mail/Views/MailListPage.xaml index bab8c1bb..d231ef21 100644 --- a/Wino.Mail/Views/MailListPage.xaml +++ b/Wino.Mail/Views/MailListPage.xaml @@ -391,6 +391,7 @@ PlaceholderText="{x:Bind domain:Translator.SearchBarPlaceholder}" QueryIcon="Find" TabIndex="1000" + TextChanged="SearchBar_TextChanged" Text="{x:Bind ViewModel.SearchQuery, Mode=TwoWay}"> @@ -590,36 +591,6 @@ x:Load="{x:Bind ViewModel.IsInitializingFolder, Mode=OneWay}" /> - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - + Date: Wed, 8 May 2024 19:26:47 +0200 Subject: [PATCH 2/7] Fixed progress ring, width of empty results and message for empty results with filters --- Wino.Mail.ViewModels/MailListPageViewModel.cs | 52 +++++++------------ Wino.Mail/Views/MailListPage.xaml | 26 +++++----- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index 8848e11b..bdd2a9d7 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -104,10 +104,9 @@ namespace Wino.Mail.ViewModels [NotifyPropertyChangedFor(nameof(IsEmpty))] [NotifyPropertyChangedFor(nameof(IsCriteriaFailed))] [NotifyPropertyChangedFor(nameof(IsFolderEmpty))] + [NotifyPropertyChangedFor(nameof(IsProgressRing))] private bool isInitializingFolder; - private bool isLoadMoreItemsLoading; - [ObservableProperty] private InfoBarMessageType barSeverity; @@ -250,22 +249,19 @@ namespace Wino.Mail.ViewModels public bool HasMultipleItemSelections => SelectedItemCount > 1; public bool HasSelectedItems => SelectedItems.Any(); public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive; - public bool IsEmpty => !IsInitializingFolder && !IsPerformingSearch && MailCollection.Count == 0; - public bool IsCriteriaFailed => IsEmpty && IsInSearchMode; - public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !IsInSearchMode; - private bool _isPerformingSearch; - public bool IsPerformingSearch - { - get => _isPerformingSearch; - set - { - if (SetProperty(ref _isPerformingSearch, value)) - { - NotifyItemFoundState(); - } - } - } + /// + /// Indicates current state of the mail list. Doesn't matter it's loading or no. + /// + public bool IsEmpty => MailCollection.Count == 0; + + /// + /// Progress ring only should be visible when the folder is initializing and there are no items. We don't need to show it when there are items. + /// + public bool IsProgressRing => IsInitializingFolder && IsEmpty; + private bool isFilters => IsInSearchMode || SelectedFilterOption.Type != FilterOptionType.All; + public bool IsCriteriaFailed => !IsInitializingFolder && IsEmpty && isFilters; + public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !isFilters; public bool IsInSearchMode { get; set; } @@ -473,24 +469,16 @@ namespace Wino.Mail.ViewModels [RelayCommand] public async Task PerformSearchAsync() { - try + if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode) { - if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode) - { - IsInSearchMode = false; - await InitializeFolderAsync(); - } - - if (!string.IsNullOrEmpty(SearchQuery)) - { - IsInSearchMode = true; - IsPerformingSearch = true; - await InitializeFolderAsync(); - } + IsInSearchMode = false; + await InitializeFolderAsync(); } - finally + + if (!string.IsNullOrEmpty(SearchQuery)) { - IsPerformingSearch = false; + IsInSearchMode = true; + await InitializeFolderAsync(); } } diff --git a/Wino.Mail/Views/MailListPage.xaml b/Wino.Mail/Views/MailListPage.xaml index d231ef21..1cad33f7 100644 --- a/Wino.Mail/Views/MailListPage.xaml +++ b/Wino.Mail/Views/MailListPage.xaml @@ -403,17 +403,16 @@ - + @@ -699,22 +698,21 @@ Foreground="{ThemeResource InformationBrush}" Text="{x:Bind domain:Translator.NoMessageEmptyFolder}" Visibility="{x:Bind ViewModel.IsFolderEmpty, Mode=OneWay}" /> - - - + + + Visibility="{x:Bind ViewModel.IsEmpty, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}"> - + Date: Wed, 8 May 2024 20:01:55 +0200 Subject: [PATCH 3/7] Changed placegolders to start from capital latter --- Wino.Core.Domain/Translations/en_US/resources.json | 10 +++++----- Wino.Core.Domain/Translator.Designer.cs | 12 ++++++------ Wino.Mail.ViewModels/MailListPageViewModel.cs | 2 ++ Wino.Mail/Views/MailListPage.xaml.cs | 3 ++- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index 4d5fc623..f8ef8c9f 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -298,9 +298,9 @@ "NewAccountDialog_AccountNameDefaultValue": "Personal", "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", "NewAccountDialog_Title": "Add New Account", - "NoMailSelected": "no message selected", - "NoMessageCrieteria": "no messages match your search criteria.", - "NoMessageEmptyFolder": "this folder is empty.", + "NoMailSelected": "No message selected", + "NoMessageCrieteria": "Mo messages match your search criteria", + "NoMessageEmptyFolder": "This folder is empty", "Notifications_MultipleNotificationsMessage": "You have {0} new mails", "Notifications_MultipleNotificationsTitle": "New Mails", "Notifications_WinoUpdatedMessage": "Checkout new version {0}", @@ -321,8 +321,8 @@ "Right": "Right", "SynchronizationFolderReport_Success": "up to date", "SynchronizationFolderReport_Failed": "synchronization is failed", - "SearchBarPlaceholder": "search", - "SearchingIn": "searching in", + "SearchBarPlaceholder": "Search", + "SearchingIn": "Searching in", "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", "SettingsAboutGithub_Title": "GitHub", "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs index 6f905ef8..473b8321 100644 --- a/Wino.Core.Domain/Translator.Designer.cs +++ b/Wino.Core.Domain/Translator.Designer.cs @@ -424,7 +424,7 @@ namespace Wino.Core.Domain public static string DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton => Resources.GetTranslatedString(@"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton"); /// - /// Do you want to stop getting messages from {0}? WinoMail will unsubscribe for you by sending an email from your email account to {1}. + /// Do you want to stop getting messages from {0}? Wino will unsubscribe for you by sending an email from your email account to {1}. /// public static string DialogMessage_UnsubscribeConfirmationMailtoMessage => Resources.GetTranslatedString(@"DialogMessage_UnsubscribeConfirmationMailtoMessage"); @@ -1514,17 +1514,17 @@ namespace Wino.Core.Domain public static string NewAccountDialog_Title => Resources.GetTranslatedString(@"NewAccountDialog_Title"); /// - /// no message selected + /// No message selected /// public static string NoMailSelected => Resources.GetTranslatedString(@"NoMailSelected"); /// - /// no messages match your search criteria. + /// Mo messages match your search criteria /// public static string NoMessageCrieteria => Resources.GetTranslatedString(@"NoMessageCrieteria"); /// - /// this folder is empty. + /// This folder is empty /// public static string NoMessageEmptyFolder => Resources.GetTranslatedString(@"NoMessageEmptyFolder"); @@ -1629,12 +1629,12 @@ namespace Wino.Core.Domain public static string SynchronizationFolderReport_Failed => Resources.GetTranslatedString(@"SynchronizationFolderReport_Failed"); /// - /// search + /// Search /// public static string SearchBarPlaceholder => Resources.GetTranslatedString(@"SearchBarPlaceholder"); /// - /// searching in + /// Searching in /// public static string SearchingIn => Resources.GetTranslatedString(@"SearchingIn"); diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index bdd2a9d7..45df9049 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -839,9 +839,11 @@ namespace Wino.Mail.ViewModels void ResetFilters() { + // Expected that FilterOptions and SortingOptions have default value in 0 index. SelectedFilterOption = FilterOptions[0]; SelectedSortingOption = SortingOptions[0]; SearchQuery = string.Empty; + IsInSearchMode = false; } } diff --git a/Wino.Mail/Views/MailListPage.xaml.cs b/Wino.Mail/Views/MailListPage.xaml.cs index d0b038de..33cea63a 100644 --- a/Wino.Mail/Views/MailListPage.xaml.cs +++ b/Wino.Mail/Views/MailListPage.xaml.cs @@ -16,6 +16,7 @@ using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Navigation; using Wino.Controls; using Wino.Controls.Advanced; +using Wino.Core.Domain; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.MailItem; @@ -420,7 +421,7 @@ namespace Wino.Views private void SearchBarUnfocused(object sender, RoutedEventArgs e) { - SearchBar.PlaceholderText = "search"; + SearchBar.PlaceholderText = Translator.SearchBarPlaceholder; } private void MailListPageLoaded(object sender, RoutedEventArgs e) From 5b46c372ab89118c62d1603c61ed8aa7649ca8fc Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Wed, 8 May 2024 23:59:50 +0200 Subject: [PATCH 4/7] Added search pivot --- Wino.Mail.ViewModels/MailListPageViewModel.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index 45df9049..71e0c81c 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -57,8 +57,8 @@ namespace Wino.Mail.ViewModels public WinoMailCollection MailCollection { get; } = new WinoMailCollection(); - public ObservableCollection SelectedItems { get; set; } = new ObservableCollection(); - public ObservableCollection PivotFolders { get; set; } = new ObservableCollection(); + public ObservableCollection SelectedItems { get; set; } = []; + public ObservableCollection PivotFolders { get; set; } = []; private readonly SemaphoreSlim listManipulationSemepahore = new SemaphoreSlim(1); private CancellationTokenSource listManipulationCancellationTokenSource = new CancellationTokenSource(); @@ -99,7 +99,7 @@ namespace Wino.Mail.ViewModels private FilterOption _selectedFilterOption; private SortingOption _selectedSortingOption; - // Indicates state when folder is initializing. It can happen after folder navigation, search or filter change applied. + // Indicates state when folder is initializing. It can happen after folder navigation, search or filter change applied or loading more items. [ObservableProperty] [NotifyPropertyChangedFor(nameof(IsEmpty))] [NotifyPropertyChangedFor(nameof(IsCriteriaFailed))] @@ -471,15 +471,32 @@ namespace Wino.Mail.ViewModels { if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode) { + UpdateFolderPivots(); IsInSearchMode = false; await InitializeFolderAsync(); } if (!string.IsNullOrEmpty(SearchQuery)) { + IsInSearchMode = true; + CreateSearchPivot(); await InitializeFolderAsync(); } + + void CreateSearchPivot() + { + PivotFolders.Clear(); + var isFocused = SelectedFolderPivot?.IsFocused; + SelectedFolderPivot = null; + + if (ActiveFolder == null) return; + + PivotFolders.Add(new FolderPivotViewModel("Results", isFocused)); + + // This will trigger refresh. + SelectedFolderPivot = PivotFolders.FirstOrDefault(); + } } [RelayCommand] From 1f6e1db6956f7694ce4e222add10a3e0905ef17f Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Thu, 9 May 2024 00:51:16 +0200 Subject: [PATCH 5/7] Fyx typo and formating --- .../Translations/en_US/resources.json | 2 +- Wino.Core.Domain/Translator.Designer.cs | 2 +- Wino.Mail.ViewModels/MailListPageViewModel.cs | 127 +++++++++--------- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index f8ef8c9f..f44bc796 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -299,7 +299,7 @@ "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", "NewAccountDialog_Title": "Add New Account", "NoMailSelected": "No message selected", - "NoMessageCrieteria": "Mo messages match your search criteria", + "NoMessageCrieteria": "No messages match your search criteria", "NoMessageEmptyFolder": "This folder is empty", "Notifications_MultipleNotificationsMessage": "You have {0} new mails", "Notifications_MultipleNotificationsTitle": "New Mails", diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs index 473b8321..283898dd 100644 --- a/Wino.Core.Domain/Translator.Designer.cs +++ b/Wino.Core.Domain/Translator.Designer.cs @@ -1519,7 +1519,7 @@ namespace Wino.Core.Domain public static string NoMailSelected => Resources.GetTranslatedString(@"NoMailSelected"); /// - /// Mo messages match your search criteria + /// No messages match your search criteria /// public static string NoMessageCrieteria => Resources.GetTranslatedString(@"NoMessageCrieteria"); diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index 71e0c81c..e9313c1d 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -167,6 +167,65 @@ namespace Wino.Mail.ViewModels }); } + #region Properties + + /// + /// Selected internal folder. This can be either folder's own name or Focused-Other. + /// + public FolderPivotViewModel SelectedFolderPivot + { + get => _selectedFolderPivot; + set + { + if (_selectedFolderPivot != null) + _selectedFolderPivot.SelectedItemCount = 0; + + SetProperty(ref _selectedFolderPivot, value); + } + } + + /// + /// Selected sorting option. + /// + public SortingOption SelectedSortingOption + { + get => _selectedSortingOption; + set + { + if (SetProperty(ref _selectedSortingOption, value)) + { + if (value != null && MailCollection != null) + { + MailCollection.SortingType = value.Type; + } + } + } + } + + public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled; + public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false; + public int SelectedItemCount => SelectedItems.Count; + public bool HasMultipleItemSelections => SelectedItemCount > 1; + public bool HasSelectedItems => SelectedItems.Any(); + public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive; + + /// + /// Indicates current state of the mail list. Doesn't matter it's loading or no. + /// + public bool IsEmpty => MailCollection.Count == 0; + + /// + /// Progress ring only should be visible when the folder is initializing and there are no items. We don't need to show it when there are items. + /// + public bool IsProgressRing => IsInitializingFolder && IsEmpty; + private bool isFilters => IsInSearchMode || SelectedFilterOption.Type != FilterOptionType.All; + public bool IsCriteriaFailed => !IsInitializingFolder && IsEmpty && isFilters; + public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !isFilters; + + public bool IsInSearchMode { get; set; } + + #endregion + private async void ActiveMailItemChanged(MailItemViewModel selectedMailItemViewModel) { if (_activeMailItem == selectedMailItemViewModel) return; @@ -209,64 +268,6 @@ namespace Wino.Mail.ViewModels } } - /// - /// Selected internal folder. This can be either folder's own name or Focused-Other. - /// - public FolderPivotViewModel SelectedFolderPivot - { - get => _selectedFolderPivot; - set - { - if (_selectedFolderPivot != null) - _selectedFolderPivot.SelectedItemCount = 0; - - SetProperty(ref _selectedFolderPivot, value); - } - } - - /// - /// Selected sorting option. - /// - public SortingOption SelectedSortingOption - { - get => _selectedSortingOption; - set - { - if (SetProperty(ref _selectedSortingOption, value)) - { - if (value != null && MailCollection != null) - { - MailCollection.SortingType = value.Type; - } - } - } - } - - #region Properties - public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled; - public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false; - public int SelectedItemCount => SelectedItems.Count; - public bool HasMultipleItemSelections => SelectedItemCount > 1; - public bool HasSelectedItems => SelectedItems.Any(); - public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive; - - /// - /// Indicates current state of the mail list. Doesn't matter it's loading or no. - /// - public bool IsEmpty => MailCollection.Count == 0; - - /// - /// Progress ring only should be visible when the folder is initializing and there are no items. We don't need to show it when there are items. - /// - public bool IsProgressRing => IsInitializingFolder && IsEmpty; - private bool isFilters => IsInSearchMode || SelectedFilterOption.Type != FilterOptionType.All; - public bool IsCriteriaFailed => !IsInitializingFolder && IsEmpty && isFilters; - public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !isFilters; - - public bool IsInSearchMode { get; set; } - - #endregion - public void NotifyItemSelected() { OnPropertyChanged(nameof(HasSelectedItems)); @@ -284,11 +285,6 @@ namespace Wino.Mail.ViewModels OnPropertyChanged(nameof(IsFolderEmpty)); } - [RelayCommand] - public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request); - - public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package); - protected override void OnDispatcherAssigned() { base.OnDispatcherAssigned(); @@ -391,6 +387,9 @@ namespace Wino.Mail.ViewModels #region Commands + [RelayCommand] + public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request); + /// /// Executes the requested mail operation for currently selected items. /// @@ -543,6 +542,8 @@ namespace Wino.Mail.ViewModels #endregion + public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package); + public IEnumerable GetTargetMailItemViewModels(IMailItem clickedItem) { // Threat threads as a whole and include everything in the group. Except single selections outside of the thread. From 1f59d3179c6c67a73976a551a79ce7fec50f4eea Mon Sep 17 00:00:00 2001 From: Aleh Khantsevich Date: Thu, 9 May 2024 01:11:02 +0200 Subject: [PATCH 6/7] Added SearchPivotName trnslation resource --- Wino.Core.Domain/Translations/en_US/resources.json | 1 + Wino.Core.Domain/Translator.Designer.cs | 5 +++++ Wino.Mail.ViewModels/MailListPageViewModel.cs | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json index f44bc796..d7c71364 100644 --- a/Wino.Core.Domain/Translations/en_US/resources.json +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -323,6 +323,7 @@ "SynchronizationFolderReport_Failed": "synchronization is failed", "SearchBarPlaceholder": "Search", "SearchingIn": "Searching in", + "SearchPivotName": "Results", "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", "SettingsAboutGithub_Title": "GitHub", "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs index 283898dd..a07f3c76 100644 --- a/Wino.Core.Domain/Translator.Designer.cs +++ b/Wino.Core.Domain/Translator.Designer.cs @@ -1638,6 +1638,11 @@ namespace Wino.Core.Domain /// public static string SearchingIn => Resources.GetTranslatedString(@"SearchingIn"); + /// + /// Results + /// + public static string SearchPivotName => Resources.GetTranslatedString(@"SearchPivotName"); + /// /// Go to issue tracker GitHub repository. /// diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index e9313c1d..cd0632a3 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -491,7 +491,7 @@ namespace Wino.Mail.ViewModels if (ActiveFolder == null) return; - PivotFolders.Add(new FolderPivotViewModel("Results", isFocused)); + PivotFolders.Add(new FolderPivotViewModel(Translator.SearchPivotName, isFocused)); // This will trigger refresh. SelectedFolderPivot = PivotFolders.FirstOrDefault(); From 5aea223c14c6e14c4d11c0c4af2995f6bd0e7018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Thu, 9 May 2024 03:40:19 +0200 Subject: [PATCH 7/7] Fixed double init issue when performing search. --- Wino.Mail.ViewModels/MailListPageViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index cd0632a3..bc49de02 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -480,7 +480,6 @@ namespace Wino.Mail.ViewModels IsInSearchMode = true; CreateSearchPivot(); - await InitializeFolderAsync(); } void CreateSearchPivot()