diff --git a/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs b/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs index b535fea1..2778acd2 100644 --- a/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs +++ b/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs @@ -13,5 +13,21 @@ namespace Wino.Core.Domain.Interfaces /// Name representation of the view model that will be used to identify the startup entity on launch. /// string StartupEntityTitle { get; } + + /// + /// E-mail addresses that this account holds. + /// + + string StartupEntityAddresses { get; } + + /// + /// Represents the account order in the accounts list. + /// + int Order { get; } + + /// + /// Provider details of the account. + /// + IProviderDetail ProviderDetail { get; set; } } } diff --git a/Wino.Core.Domain/Interfaces/IDialogService.cs b/Wino.Core.Domain/Interfaces/IDialogService.cs index af54f79f..6493fcb7 100644 --- a/Wino.Core.Domain/Interfaces/IDialogService.cs +++ b/Wino.Core.Domain/Interfaces/IDialogService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading.Tasks; using Wino.Core.Domain.Entities; using Wino.Core.Domain.Enums; @@ -32,6 +33,13 @@ namespace Wino.Core.Domain.Interfaces Task ShowEditAccountDialogAsync(MailAccount account); Task ShowAccountPickerDialogAsync(List availableAccounts); + /// + /// Displays a dialog to the user for reordering accounts. + /// + /// Available accounts in order. + /// Result model that has dict of AccountId-AccountOrder. + Task ShowAccountReorderDialogAsync(ObservableCollection availableAccounts); + /// /// Presents a dialog to the user for selecting folder. /// diff --git a/Wino.Core/Messages/Accounts/AccountMenuItemsReordered.cs b/Wino.Core/Messages/Accounts/AccountMenuItemsReordered.cs new file mode 100644 index 00000000..d9a9d74b --- /dev/null +++ b/Wino.Core/Messages/Accounts/AccountMenuItemsReordered.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace Wino.Core.Messages.Accounts +{ + /// + /// Emitted when account menu items are reordered. + /// + /// New order info. + public record AccountMenuItemsReordered(Dictionary newOrderDictionary); +} diff --git a/Wino.Core/Services/AccountService.cs b/Wino.Core/Services/AccountService.cs index dd8a9f8a..80660e87 100644 --- a/Wino.Core/Services/AccountService.cs +++ b/Wino.Core/Services/AccountService.cs @@ -423,7 +423,7 @@ namespace Wino.Core.Services if (account == null) { - _logger.Error("Could not find account with id {AccountId}", pair.Key); + _logger.Information("Could not find account with id {Key} for reordering. It may be a linked account.", pair.Key); continue; } @@ -431,6 +431,8 @@ namespace Wino.Core.Services await Connection.UpdateAsync(account); } + + Messenger.Send(new AccountMenuItemsReordered(accountIdOrderPair)); } } } diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs index 4c06374d..712ac35a 100644 --- a/Wino.Mail.ViewModels/AccountManagementViewModel.cs +++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs @@ -264,11 +264,8 @@ namespace Wino.Mail.ViewModels mergedAccountProviderDetailViewModel)); } - [RelayCommand] - private async Task ReorderAccountsAsync() - { - - } + [RelayCommand(CanExecute = nameof(CanReorderAccounts))] + private Task ReorderAccountsAsync() => DialogService.ShowAccountReorderDialogAsync(availableAccounts: Accounts); public override void OnNavigatedFrom(NavigationMode mode, object parameters) { diff --git a/Wino.Mail.ViewModels/AppShellViewModel.cs b/Wino.Mail.ViewModels/AppShellViewModel.cs index 33fb15cf..5b0a4cb5 100644 --- a/Wino.Mail.ViewModels/AppShellViewModel.cs +++ b/Wino.Mail.ViewModels/AppShellViewModel.cs @@ -39,7 +39,8 @@ namespace Wino.Mail.ViewModels IRecipient, IRecipient, IRecipient, - IRecipient + IRecipient, + IRecipient { #region Menu Items @@ -1059,5 +1060,19 @@ namespace Wino.Mail.ViewModels ChangeLoadedAccount(latestSelectedAccountMenuItem, navigateInbox: false); } + + private void ReorderAccountMenuItems(Dictionary newAccountOrder) + { + foreach (var item in newAccountOrder) + { + var menuItem = MenuItems.GetAccountMenuItem(item.Key); + + if (menuItem == null) continue; + + MenuItems.Move(MenuItems.IndexOf(menuItem), item.Value); + } + } + + public void Receive(AccountMenuItemsReordered message) => ReorderAccountMenuItems(message.newOrderDictionary); } } diff --git a/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs b/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs index 4f629e99..cd2d2a89 100644 --- a/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs +++ b/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs @@ -17,6 +17,10 @@ namespace Wino.Mail.ViewModels.Data public string StartupEntityTitle => Account.Name; + public int Order => Account.Order; + + public string StartupEntityAddresses => Account.Address; + public AccountProviderDetailViewModel(IProviderDetail providerDetail, MailAccount account) { ProviderDetail = providerDetail; diff --git a/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs b/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs index 6dda7ac3..1bd7de5b 100644 --- a/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs +++ b/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs @@ -18,6 +18,12 @@ namespace Wino.Mail.ViewModels.Data public string StartupEntityTitle => MergedInbox.Name; + public int Order => 0; + + public IProviderDetail ProviderDetail { get; set; } + + public string StartupEntityAddresses => AccountAddresses; + public MergedAccountProviderDetailViewModel(MergedInbox mergedInbox, List holdingAccounts) { MergedInbox = mergedInbox; diff --git a/Wino.Mail/Dialogs/AccountReorderDialog.xaml b/Wino.Mail/Dialogs/AccountReorderDialog.xaml new file mode 100644 index 00000000..4f733a3a --- /dev/null +++ b/Wino.Mail/Dialogs/AccountReorderDialog.xaml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/AccountReorderDialog.xaml.cs b/Wino.Mail/Dialogs/AccountReorderDialog.xaml.cs new file mode 100644 index 00000000..c5433c7e --- /dev/null +++ b/Wino.Mail/Dialogs/AccountReorderDialog.xaml.cs @@ -0,0 +1,52 @@ +using System.Collections.ObjectModel; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Dialogs +{ + public sealed partial class AccountReorderDialog : ContentDialog + { + public ObservableCollection Accounts { get; } + + private int count; + private bool isOrdering = false; + + private readonly IAccountService _accountService = App.Current.Services.GetService(); + + public AccountReorderDialog(ObservableCollection accounts) + { + Accounts = accounts; + + count = accounts.Count; + + InitializeComponent(); + } + + private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + Accounts.CollectionChanged -= AccountsChanged; + Accounts.CollectionChanged += AccountsChanged; + } + + private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => Accounts.CollectionChanged -= AccountsChanged; + + private async void AccountsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (count - 1 == Accounts.Count) + isOrdering = true; + + if (count == Accounts.Count && isOrdering) + { + // Order is completed. Apply changes. + + var dict = Accounts.ToDictionary(a => a.StartupEntityId, a => Accounts.IndexOf(a)); + + await _accountService.UpdateAccountOrdersAsync(dict); + + isOrdering = false; + } + } + } +} diff --git a/Wino.Mail/Selectors/AccountReorderTemplateSelector.cs b/Wino.Mail/Selectors/AccountReorderTemplateSelector.cs new file mode 100644 index 00000000..07574892 --- /dev/null +++ b/Wino.Mail/Selectors/AccountReorderTemplateSelector.cs @@ -0,0 +1,22 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Selectors +{ + public class AccountReorderTemplateSelector : DataTemplateSelector + { + public DataTemplate MergedAccountReorderTemplate { get; set; } + public DataTemplate RootAccountReorderTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item is MergedAccountProviderDetailViewModel) + { + return MergedAccountReorderTemplate; + } + + return RootAccountReorderTemplate; + } + } +} diff --git a/Wino.Mail/Services/DialogService.cs b/Wino.Mail/Services/DialogService.cs index e38ba569..2d751080 100644 --- a/Wino.Mail/Services/DialogService.cs +++ b/Wino.Mail/Services/DialogService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.Messaging; @@ -331,6 +332,14 @@ namespace Wino.Services return accountPicker.PickedAccount; } + public async Task ShowAccountReorderDialogAsync(ObservableCollection availableAccounts) + { + var accountReorderDialog = new AccountReorderDialog(availableAccounts) + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + await HandleDialogPresentationAsync(accountReorderDialog); + } } } diff --git a/Wino.Mail/Views/Account/AccountManagementPage.xaml b/Wino.Mail/Views/Account/AccountManagementPage.xaml index 6876ae12..1f79d9cd 100644 --- a/Wino.Mail/Views/Account/AccountManagementPage.xaml +++ b/Wino.Mail/Views/Account/AccountManagementPage.xaml @@ -220,7 +220,6 @@ Description="{x:Bind domain:Translator.SettingsLinkAccounts_Description}"> - diff --git a/Wino.Mail/Wino.Mail.csproj b/Wino.Mail/Wino.Mail.csproj index c27cf3d5..a0e55b3b 100644 --- a/Wino.Mail/Wino.Mail.csproj +++ b/Wino.Mail/Wino.Mail.csproj @@ -252,6 +252,9 @@ AccountPickerDialog.xaml + + AccountReorderDialog.xaml + CustomThemeBuilderDialog.xaml @@ -319,6 +322,7 @@ + @@ -484,6 +488,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile