Merge pull request #185 from Tiktack/183-ui-improvements

Search improvements
This commit is contained in:
Burak Kaan Köse
2024-05-09 03:58:50 +02:00
committed by GitHub
6 changed files with 295 additions and 296 deletions

View File

@@ -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": "No 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,9 @@
"Right": "Right",
"SynchronizationFolderReport_Success": "up to date",
"SynchronizationFolderReport_Failed": "synchronization is failed",
"SearchBarPlaceholder": "search",
"SearchingIn": "searching in",
"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",

View File

@@ -424,7 +424,7 @@ namespace Wino.Core.Domain
public static string DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton => Resources.GetTranslatedString(@"DialogMessage_UnsubscribeConfirmationGoToWebsiteConfirmButton");
/// <summary>
/// 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}.
/// </summary>
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");
/// <summary>
/// no message selected
/// No message selected
/// </summary>
public static string NoMailSelected => Resources.GetTranslatedString(@"NoMailSelected");
/// <summary>
/// no messages match your search criteria.
/// No messages match your search criteria
/// </summary>
public static string NoMessageCrieteria => Resources.GetTranslatedString(@"NoMessageCrieteria");
/// <summary>
/// this folder is empty.
/// This folder is empty
/// </summary>
public static string NoMessageEmptyFolder => Resources.GetTranslatedString(@"NoMessageEmptyFolder");
@@ -1629,15 +1629,20 @@ namespace Wino.Core.Domain
public static string SynchronizationFolderReport_Failed => Resources.GetTranslatedString(@"SynchronizationFolderReport_Failed");
/// <summary>
/// search
/// Search
/// </summary>
public static string SearchBarPlaceholder => Resources.GetTranslatedString(@"SearchBarPlaceholder");
/// <summary>
/// searching in
/// Searching in
/// </summary>
public static string SearchingIn => Resources.GetTranslatedString(@"SearchingIn");
/// <summary>
/// Results
/// </summary>
public static string SearchPivotName => Resources.GetTranslatedString(@"SearchPivotName");
/// <summary>
/// Go to issue tracker GitHub repository.
/// </summary>

View File

@@ -51,14 +51,14 @@ namespace Wino.Mail.ViewModels
* to prevent them from being removed from the list when they are marked as read.
*/
private HashSet<Guid> gmailUnreadFolderMarkedAsReadUniqueIds = new HashSet<Guid>();
private readonly HashSet<Guid> gmailUnreadFolderMarkedAsReadUniqueIds = [];
private IObservable<System.Reactive.EventPattern<NotifyCollectionChangedEventArgs>> selectionChangedObservable = null;
public WinoMailCollection MailCollection { get; } = new WinoMailCollection();
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = new ObservableCollection<MailItemViewModel>();
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = new ObservableCollection<FolderPivotViewModel>();
public ObservableCollection<MailItemViewModel> SelectedItems { get; set; } = [];
public ObservableCollection<FolderPivotViewModel> PivotFolders { get; set; } = [];
private readonly SemaphoreSlim listManipulationSemepahore = new SemaphoreSlim(1);
private CancellationTokenSource listManipulationCancellationTokenSource = new CancellationTokenSource();
@@ -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,10 +99,12 @@ 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 or loading more items.
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsEmpty))]
[NotifyPropertyChangedFor(nameof(IsCriteriaFailed))]
[NotifyPropertyChangedFor(nameof(IsFolderEmpty))]
[NotifyPropertyChangedFor(nameof(IsProgressRing))]
private bool isInitializingFolder;
[ObservableProperty]
@@ -118,10 +119,21 @@ namespace Wino.Mail.ViewModels
[ObservableProperty]
private bool isBarOpen;
/// <summary>
/// Current folder that is being represented from the menu.
/// </summary>
[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 +148,6 @@ namespace Wino.Mail.ViewModels
NavigationService = navigationService;
_mailService = mailService;
_notificationBuilder = notificationBuilder;
_folderService = folderService;
_winoSynchronizerFactory = winoSynchronizerFactory;
_threadingStrategyProvider = threadingStrategyProvider;
@@ -156,53 +167,65 @@ namespace Wino.Mail.ViewModels
});
}
/// <summary>
/// Executes the requested mail operation for currently selected items.
/// </summary>
/// <param name="operation">Action to execute for selected items.</param>
[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);
}
#region Properties
/// <summary>
/// Sens a new message to synchronize current folder.
/// Selected internal folder. This can be either folder's own name or Focused-Other.
/// </summary>
[RelayCommand]
private void SyncFolder()
public FolderPivotViewModel SelectedFolderPivot
{
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)
get => _selectedFolderPivot;
set
{
var options = new SynchronizationOptions()
{
AccountId = folder.MailAccountId,
Type = SynchronizationType.Custom,
SynchronizationFolderIds = [folder.Id],
GroupedSynchronizationTrackingId = trackingSynchronizationId
};
if (_selectedFolderPivot != null)
_selectedFolderPivot.SelectedItemCount = 0;
Messenger.Send(new NewSynchronizationRequested(options));
SetProperty(ref _selectedFolderPivot, value);
}
}
/// <summary>
/// Selected sorting option.
/// </summary>
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;
/// <summary>
/// Indicates current state of the mail list. Doesn't matter it's loading or no.
/// </summary>
public bool IsEmpty => MailCollection.Count == 0;
/// <summary>
/// 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.
/// </summary>
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;
@@ -245,83 +268,6 @@ namespace Wino.Mail.ViewModels
}
}
/// <summary>
/// Selected internal folder. This can be either folder's own name or Focused-Other.
/// </summary>
public FolderPivotViewModel SelectedFolderPivot
{
get => _selectedFolderPivot;
set
{
if (_selectedFolderPivot != null)
_selectedFolderPivot.SelectedItemCount = 0;
SetProperty(ref _selectedFolderPivot, value);
}
}
/// <summary>
/// Selected sorting option.
/// </summary>
public SortingOption SelectedSortingOption
{
get => _selectedSortingOption;
set
{
if (SetProperty(ref _selectedSortingOption, value))
{
if (value != null && MailCollection != null)
{
MailCollection.SortingType = value.Type;
}
}
}
}
/// <summary>
/// Current folder that is being represented from the menu.
/// </summary>
[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 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 IsCriteriaFailed => IsEmpty && IsInSearchMode;
public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !IsInSearchMode;
private bool _isPerformingSearch;
public bool IsPerformingSearch
{
get => _isPerformingSearch;
set
{
if (SetProperty(ref _isPerformingSearch, value))
{
NotifyItemFoundState();
}
}
}
public bool IsInSearchMode => !string.IsNullOrEmpty(SearchQuery);
#endregion
public void NotifyItemSelected()
{
OnPropertyChanged(nameof(HasSelectedItems));
@@ -339,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();
@@ -444,6 +385,58 @@ namespace Wino.Mail.ViewModels
SelectedFolderPivot = PivotFolders.FirstOrDefault();
}
#region Commands
[RelayCommand]
public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request);
/// <summary>
/// Executes the requested mail operation for currently selected items.
/// </summary>
/// <param name="operation">Action to execute for selected items.</param>
[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);
}
/// <summary>
/// Sens a new message to synchronize current folder.
/// </summary>
[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 +465,84 @@ namespace Wino.Mail.ViewModels
await InitializeFolderAsync();
}
[RelayCommand]
public async Task PerformSearchAsync()
{
if (string.IsNullOrEmpty(SearchQuery) && IsInSearchMode)
{
UpdateFolderPivots();
IsInSearchMode = false;
await InitializeFolderAsync();
}
if (!string.IsNullOrEmpty(SearchQuery))
{
IsInSearchMode = true;
CreateSearchPivot();
}
void CreateSearchPivot()
{
PivotFolders.Clear();
var isFocused = SelectedFolderPivot?.IsFocused;
SelectedFolderPivot = null;
if (ActiveFolder == null) return;
PivotFolders.Add(new FolderPivotViewModel(Translator.SearchPivotName, isFocused));
// This will trigger refresh.
SelectedFolderPivot = PivotFolders.FirstOrDefault();
}
}
[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 Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package);
public IEnumerable<MailItemViewModel> GetTargetMailItemViewModels(IMailItem clickedItem)
{
// Threat threads as a whole and include everything in the group. Except single selections outside of the thread.
@@ -496,7 +567,7 @@ namespace Wino.Mail.ViewModels
if (includedInSelectedItems)
contextMailItems = SelectedItems;
else
contextMailItems = new List<MailItemViewModel>() { clickedMailItemViewModel };
contextMailItems = [clickedMailItemViewModel];
}
return contextMailItems;
@@ -506,7 +577,7 @@ namespace Wino.Mail.ViewModels
=> _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems);
public void ChangeCustomFocusedState(IEnumerable<IMailItem> mailItems, bool isFocused)
=> mailItems.Where(a => a is MailItemViewModel).Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = isFocused);
=> mailItems.OfType<MailItemViewModel>().ForEach(a => a.IsCustomFocused = isFocused);
private bool ShouldPreventItemAdd(IMailItem mailItem)
{
@@ -650,29 +721,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 +728,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 +812,12 @@ namespace Wino.Mail.ViewModels
}
}
[RelayCommand]
private async Task EnableFolderSynchronizationAsync()
{
if (ActiveFolder == null) return;
#region Receivers
void IRecipient<MailItemSelectedEvent>.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<MailItemNavigationRequested>.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<MailItemSelectionRemovedEvent>.Receive(MailItemSelectionRemovedEvent message)
=> SelectedItems.Remove(message.RemovedMailItem);
async void IRecipient<ActiveMailFolderChangedEvent>.Receive(ActiveMailFolderChangedEvent message)
{
@@ -835,6 +838,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 +853,17 @@ namespace Wino.Mail.ViewModels
message.FolderInitLoadAwaitTask?.TrySetResult(true);
isChangingFolder = false;
void ResetFilters()
{
// Expected that FilterOptions and SortingOptions have default value in 0 index.
SelectedFilterOption = FilterOptions[0];
SelectedSortingOption = SortingOptions[0];
SearchQuery = string.Empty;
IsInSearchMode = false;
}
}
void IRecipient<MailItemSelectedEvent>.Receive(MailItemSelectedEvent message)
=> SelectedItems.Add(message.SelectedMailItem);
void IRecipient<MailItemSelectionRemovedEvent>.Receive(MailItemSelectionRemovedEvent message)
=> SelectedItems.Remove(message.RemovedMailItem);
public void Receive(AccountSynchronizationCompleted message)
{
if (ActiveFolder == null) return;
@@ -889,24 +898,40 @@ namespace Wino.Mail.ViewModels
}
}
void IRecipient<MailItemNavigationRequested>.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();

View File

@@ -205,7 +205,7 @@ namespace Wino.Controls.Advanced
{
bool found = false;
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
Items.OfType<ThreadMailItemViewModel>().ForEach(c =>
{
if (!found)
{

View File

@@ -391,6 +391,7 @@
PlaceholderText="{x:Bind domain:Translator.SearchBarPlaceholder}"
QueryIcon="Find"
TabIndex="1000"
TextChanged="SearchBar_TextChanged"
Text="{x:Bind ViewModel.SearchQuery, Mode=TwoWay}">
<i:Interaction.Behaviors>
<ic:EventTriggerBehavior EventName="QuerySubmitted">
@@ -402,17 +403,16 @@
<Grid x:Name="RootGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ReaderColumn" Width="Auto" />
<ColumnDefinition x:Name="ReaderColumn" Width="*" MaxWidth="{x:Bind ViewModel.StatePersistanceService.MailListPaneLength, Mode=OneWay}"/>
<ColumnDefinition x:Name="RendererColumn" Width="*" />
</Grid.ColumnDefinitions>
<!-- Mail Items -->
<Grid
x:Name="ReaderGrid"
MaxWidth="{x:Bind ViewModel.StatePersistanceService.MailListPaneLength, Mode=OneWay}"
Margin="-1,0,0,0"
Padding="4,0,0,0"
HorizontalAlignment="Left"
HorizontalAlignment="Stretch"
Background="{ThemeResource ReadingPaneBackgroundColorBrush}"
CornerRadius="{ThemeResource MailListGridCornerRadius}">
<Grid.RowDefinitions>
@@ -590,36 +590,6 @@
x:Load="{x:Bind ViewModel.IsInitializingFolder, Mode=OneWay}" />
</Grid>
<!-- Search Mode Panel -->
<Grid
x:Name="SearchModePanel"
Grid.Row="1"
Padding="12"
x:Load="{x:Bind ViewModel.IsInSearchMode, Mode=OneWay}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
CornerRadius="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
VerticalAlignment="Center"
FontSize="16"
FontWeight="SemiBold"
Text="{x:Bind domain:Translator.Results}" />
<Button
x:Name="BackToFolderButton"
Grid.Column="1"
HorizontalAlignment="Right"
x:Load="{x:Bind helpers:XamlHelpers.ReverseBoolConverter(ViewModel.IsPerformingSearch), Mode=OneWay}">
<Viewbox Width="16">
<PathIcon Data="M512,584.5L87.5,1009C77.5,1019 65.5,1024 51.5,1024C36.8333,1024 24.5833,1019.08 14.75,1009.25C4.91667,999.417 0,987.167 0,972.5C0,958.5 5,946.5 15,936.5L439.5,512L15,87.5C5,77.5 0,65.3334 0,51C0,44 1.33333,37.3334 4,31C6.66667,24.6667 10.3333,19.25 15,14.75C19.6667,10.25 25.1667,6.66669 31.5,4C37.8333,1.33337 44.5,0 51.5,0C65.5,0 77.5,5 87.5,15L512,439.5L936.5,15C946.5,5 958.667,0 973,0C980,0 986.583,1.33337 992.75,4C998.917,6.66669 1004.33,10.3334 1009,15C1013.67,19.6667 1017.33,25.0834 1020,31.25C1022.67,37.4167 1024,44 1024,51C1024,65.3334 1019,77.5 1009,87.5L584.5,512L1009,936.5C1019,946.5 1024,958.5 1024,972.5C1024,979.5 1022.67,986.167 1020,992.5C1017.33,998.833 1013.75,1004.33 1009.25,1009C1004.75,1013.67 999.333,1017.33 993,1020C986.667,1022.67 980,1024 973,1024C958.667,1024 946.5,1019 936.5,1009Z" />
</Viewbox>
</Button>
</Grid>
<!-- Pivot + Sync + Multi Select -->
<Grid
Grid.Row="1"
@@ -698,18 +668,22 @@
<!-- No items createria -->
<StackPanel
x:Name="NoItemsPanel"
Grid.Row="0"
Grid.Row="2"
Grid.RowSpan="3"
VerticalAlignment="Center"
Opacity="0.4"
HorizontalAlignment="Stretch"
Spacing="10">
<Viewbox Width="48" Visibility="{x:Bind ViewModel.IsEmpty, Mode=OneWay}">
<Viewbox
Width="48"
Visibility="{x:Bind ViewModel.IsEmpty, Mode=OneWay}"
Opacity="0.4">
<Path Data="M252,960C227,960 203.083,954.917 180.25,944.75C157.417,934.583 137.417,920.917 120.25,903.75C103.083,886.583 89.4167,866.583 79.25,843.75C69.0833,820.917 64,797 64,772L64,252C64,227 69.0833,203.083 79.25,180.25C89.4167,157.417 103.083,137.417 120.25,120.25C137.417,103.083 157.417,89.4167 180.25,79.25C203.083,69.0834 227,64.0001 252,64L772,64C797,64.0001 820.917,69.0834 843.75,79.25C866.583,89.4167 886.583,103.083 903.75,120.25C920.917,137.417 934.583,157.417 944.75,180.25C954.917,203.083 960,227 960,252L960,772C960,797 954.917,820.917 944.75,843.75C934.583,866.583 920.917,886.583 903.75,903.75C886.583,920.917 866.583,934.583 843.75,944.75C820.917,954.917 797,960 772,960ZM770.5,896C787.167,896 803.083,892.583 818.25,885.75C833.417,878.917 846.75,869.75 858.25,858.25C869.75,846.75 878.917,833.417 885.75,818.25C892.583,803.083 896,787.167 896,770.5L896,253.5C896,236.833 892.583,220.917 885.75,205.75C878.917,190.583 869.75,177.25 858.25,165.75C846.75,154.25 833.417,145.083 818.25,138.25C803.083,131.417 787.167,128 770.5,128L253.5,128C236.833,128 220.917,131.417 205.75,138.25C190.583,145.083 177.25,154.25 165.75,165.75C154.25,177.25 145.083,190.583 138.25,205.75C131.417,220.917 128,236.833 128,253.5L128,770.5C128,787.167 131.417,803.083 138.25,818.25C145.083,833.417 154.25,846.75 165.75,858.25C177.25,869.75 190.583,878.917 205.75,885.75C220.917,892.583 236.833,896 253.5,896ZM559,604.5C541.667,616.833 523.417,625.833 504.25,631.5C485.083,637.167 465,640 444,640C418,640 393.583,634.833 370.75,624.5C347.917,614.167 328,600.167 311,582.5C294,564.833 280.583,544.417 270.75,521.25C260.917,498.083 256,473.667 256,448C256,421.333 261,396.333 271,373C281,349.667 294.667,329.333 312,312C329.333,294.667 349.667,281 373,271C396.333,261 421.333,256 448,256C473.667,256 498.083,260.917 521.25,270.75C544.417,280.583 564.833,294 582.5,311C600.167,328 614.167,347.917 624.5,370.75C634.833,393.583 640,418 640,444C640,465 637.167,485.083 631.5,504.25C625.833,523.417 616.833,541.667 604.5,559L758.5,713.5C764.833,719.833 768,727.333 768,736C768,744.667 764.833,752.167 758.5,758.5C752.167,764.833 744.667,768 736,768C727.333,768 719.833,764.833 713.5,758.5ZM576,448L576,445.5C576,428.167 572.5,411.917 565.5,396.75C558.5,381.583 549.167,368.333 537.5,357C525.833,345.667 512.25,336.667 496.75,330C481.25,323.333 465,320 448,320C430.333,320 413.75,323.417 398.25,330.25C382.75,337.083 369.25,346.25 357.75,357.75C346.25,369.25 337.083,382.833 330.25,398.5C323.417,414.167 320,430.667 320,448C320,465.667 323.417,482.25 330.25,497.75C337.083,513.25 346.25,526.75 357.75,538.25C369.25,549.75 382.75,558.917 398.25,565.75C413.75,572.583 430.333,576 448,576C465.333,576 481.833,572.583 497.5,565.75C513.167,558.917 526.75,549.75 538.25,538.25C549.75,526.75 558.917,513.25 565.75,497.75C572.583,482.25 576,465.667 576,448Z" Fill="{ThemeResource InformationBrush}" />
</Viewbox>
<TextBlock
HorizontalAlignment="Center"
Opacity="0.4"
FontSize="20"
FontWeight="ExtraLight"
Foreground="{ThemeResource InformationBrush}"
@@ -718,18 +692,27 @@
<TextBlock
HorizontalAlignment="Center"
Opacity="0.4"
FontSize="20"
FontWeight="ExtraLight"
Foreground="{ThemeResource InformationBrush}"
Text="{x:Bind domain:Translator.NoMessageEmptyFolder}"
Visibility="{x:Bind ViewModel.IsFolderEmpty, Mode=OneWay}" />
<muxc:ProgressRing
Grid.Row="2"
x:Name="LoadingProgressIndicator"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="{x:Bind ViewModel.IsProgressRing, Mode=OneWay}"
Canvas.ZIndex="999" />
</StackPanel>
<!-- Mail Items -->
<muxc:RefreshContainer
Grid.Row="2"
RefreshRequested="PullToRefreshRequested"
Visibility="{x:Bind ViewModel.IsPerformingSearch, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}">
Visibility="{x:Bind ViewModel.IsEmpty, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}">
<SemanticZoom x:Name="SemanticZoomContainer" CanChangeViews="{x:Bind ViewModel.PreferencesService.IsSemanticZoomEnabled, Mode=OneWay}">
<SemanticZoom.ZoomedInView>
<listview:WinoListView
@@ -796,30 +779,6 @@
</SemanticZoom>
</muxc:RefreshContainer>
<muxc:ProgressRing
x:Name="LoadingProgressIndicator"
Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="{x:Bind ViewModel.IsInitializingFolder, Mode=OneWay}"
Canvas.ZIndex="999" />
<!-- Search Mode Panel -->
<Grid
x:Name="SearchProgressPanel"
Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="{x:Bind ViewModel.IsInSearchMode, Mode=OneWay}">
<!-- Searching -->
<StackPanel Spacing="6" Visibility="{x:Bind ViewModel.IsPerformingSearch, Mode=OneWay}">
<TextBlock HorizontalTextAlignment="Center">
<Run Text="{x:Bind domain:Translator.SearchingIn}" /><Run FontWeight="SemiBold" Text="{x:Bind ViewModel.ActiveFolder.FolderName, Mode=OneWay}" />
</TextBlock>
<muxc:ProgressRing />
</StackPanel>
</Grid>
<!-- Update Info Bar -->
<controls:WinoInfoBar

View File

@@ -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)
@@ -532,5 +533,13 @@ namespace Wino.Views
{
ViewModel.SyncFolderCommand?.Execute(null);
}
private async void SearchBar_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if(args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && string.IsNullOrWhiteSpace(sender.Text))
{
await ViewModel.PerformSearchAsync();
}
}
}
}