diff --git a/Wino.Core.Domain/Entities/MailAccount.cs b/Wino.Core.Domain/Entities/MailAccount.cs
index c8f39c1d..7d87128b 100644
--- a/Wino.Core.Domain/Entities/MailAccount.cs
+++ b/Wino.Core.Domain/Entities/MailAccount.cs
@@ -50,6 +50,11 @@ namespace Wino.Core.Domain.Entities
///
public Guid? SignatureId { get; set; }
+ ///
+ /// Gets or sets the listing order of the account in the accounts list.
+ ///
+ public int Order { get; set; }
+
///
/// Gets or sets whether the account has any reason for an interactive user action to fix continue operating.
///
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/IAccountService.cs b/Wino.Core.Domain/Interfaces/IAccountService.cs
index 99190af3..78682cda 100644
--- a/Wino.Core.Domain/Interfaces/IAccountService.cs
+++ b/Wino.Core.Domain/Interfaces/IAccountService.cs
@@ -68,12 +68,37 @@ namespace Wino.Core.Domain.Interfaces
/// Current account synchronization modifier.
Task UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier);
+ ///
+ /// Renames the merged inbox with the given id.
+ ///
+ /// Merged Inbox id
+ /// New name for the merged/linked inbox.
Task RenameMergedAccountAsync(Guid mergedInboxId, string newName);
+ ///
+ /// Creates a new merged inbox with the given accounts.
+ ///
+ /// Merged inbox properties.
+ /// List of accounts to merge together.
Task CreateMergeAccountsAsync(MergedInbox mergedInbox, IEnumerable accountsToMerge);
+ ///
+ /// Updates the merged inbox with the given id with the new linked accounts.
+ ///
+ /// Updating merged inbox id.
+ /// List of linked account ids.
Task UpdateMergedInboxAsync(Guid mergedInboxId, IEnumerable linkedAccountIds);
+ ///
+ /// Destroys the merged inbox with the given id.
+ ///
+ /// Merged inbox id to destroy.
Task UnlinkMergedInboxAsync(Guid mergedInboxId);
+
+ ///
+ /// Updates the account listing orders.
+ ///
+ /// AccountId-OrderNumber pair for all accounts.
+ Task UpdateAccountOrdersAsync(Dictionary accountIdOrderPair);
}
}
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.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json
index ee59438a..88b8b57f 100644
--- a/Wino.Core.Domain/Translations/en_US/resources.json
+++ b/Wino.Core.Domain/Translations/en_US/resources.json
@@ -407,6 +407,8 @@
"SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail",
"SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.",
"SettingsManageAccountSettings_Title": "Manage Account Settings",
+ "SettingsReorderAccounts_Title": "Reorder Accounts",
+ "SettingsReorderAccounts_Description": "Change the order of accounts in the account list.",
"SettingsManageLink_Description": "Move items to add new link or remove existing link.",
"SettingsManageLink_Title": "Manage Link",
"SettingsMarkAsRead_Description": "Change what should happen to the selected item.",
diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs
index ee2312c1..9c83ca16 100644
--- a/Wino.Core.Domain/Translator.Designer.cs
+++ b/Wino.Core.Domain/Translator.Designer.cs
@@ -2481,6 +2481,18 @@ namespace Wino.Core.Domain
public static string SettingsManageAccountSettings_Title => Resources.GetTranslatedString(@"SettingsManageAccountSettings_Title");
+ ///
+ /// Reorder Accounts
+ ///
+ public static string SettingsReorderAccounts_Title => Resources.GetTranslatedString(@"SettingsReorderAccounts_Title");
+
+
+ ///
+ /// Change the order of accounts in the account list.
+ ///
+ public static string SettingsReorderAccounts_Description => Resources.GetTranslatedString(@"SettingsReorderAccounts_Description");
+
+
///
/// Move items to add new link or remove existing link.
///
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 850139f2..80660e87 100644
--- a/Wino.Core/Services/AccountService.cs
+++ b/Wino.Core/Services/AccountService.cs
@@ -215,7 +215,7 @@ namespace Wino.Core.Services
public async Task> GetAccountsAsync()
{
- var accounts = await Connection.Table().ToListAsync();
+ var accounts = await Connection.Table().OrderBy(a => a.Order).ToListAsync();
foreach (var account in accounts)
{
@@ -301,12 +301,21 @@ namespace Wino.Core.Services
{
var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId);
- if (account?.ProviderType == MailProviderType.IMAP4)
- account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id);
+ if (account == null)
+ {
+ _logger.Error("Could not find account with id {AccountId}", accountId);
+ }
+ else
+ {
+ if (account.ProviderType == MailProviderType.IMAP4)
+ account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id);
- account.Preferences = await GetAccountPreferencesAsync(account.Id);
+ account.Preferences = await GetAccountPreferencesAsync(account.Id);
- return account;
+ return account;
+ }
+
+ return null;
}
public Task GetAccountCustomServerInformationAsync(Guid accountId)
@@ -336,6 +345,12 @@ namespace Wino.Core.Services
{
_preferencesService.StartupEntityId = account.Id;
}
+ else
+ {
+ // Set the order of the account.
+ // This can be changed by the user later in manage accounts page.
+ account.Order = accountCount;
+ }
await Connection.InsertAsync(account);
@@ -352,6 +367,8 @@ namespace Wino.Core.Services
// Outlook & Office 365 supports Focused inbox. Enabled by default.
bool isMicrosoftProvider = account.ProviderType == MailProviderType.Outlook || account.ProviderType == MailProviderType.Office365;
+ // TODO: This should come from account settings API.
+ // Wino doesn't have MailboxSettings yet.
if (isMicrosoftProvider)
account.Preferences.IsFocusedInboxEnabled = true;
@@ -398,6 +415,24 @@ namespace Wino.Core.Services
return account.SynchronizationDeltaIdentifier;
}
+ public async Task UpdateAccountOrdersAsync(Dictionary accountIdOrderPair)
+ {
+ foreach (var pair in accountIdOrderPair)
+ {
+ var account = await GetAccountAsync(pair.Key);
+ if (account == null)
+ {
+ _logger.Information("Could not find account with id {Key} for reordering. It may be a linked account.", pair.Key);
+ continue;
+ }
+
+ account.Order = pair.Value;
+
+ await Connection.UpdateAsync(account);
+ }
+
+ Messenger.Send(new AccountMenuItemsReordered(accountIdOrderPair));
+ }
}
}
diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
index e370f190..712ac35a 100644
--- a/Wino.Mail.ViewModels/AccountManagementViewModel.cs
+++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs
@@ -42,6 +42,7 @@ namespace Wino.Mail.ViewModels
public bool IsPurchasePanelVisible => !HasUnlimitedAccountProduct;
public bool IsAccountCreationAlmostOnLimit => Accounts != null && Accounts.Count == FREE_ACCOUNT_COUNT - 1;
public bool HasAccountsDefined => Accounts != null && Accounts.Any();
+ public bool CanReorderAccounts => Accounts?.Count > 1;
public string UsedAccountsString => string.Format(Translator.WinoUpgradeRemainingAccountsMessage, Accounts.Count, FREE_ACCOUNT_COUNT);
@@ -263,6 +264,9 @@ namespace Wino.Mail.ViewModels
mergedAccountProviderDetailViewModel));
}
+ [RelayCommand(CanExecute = nameof(CanReorderAccounts))]
+ private Task ReorderAccountsAsync() => DialogService.ShowAccountReorderDialogAsync(availableAccounts: Accounts);
+
public override void OnNavigatedFrom(NavigationMode mode, object parameters)
{
base.OnNavigatedFrom(mode, parameters);
@@ -276,6 +280,9 @@ namespace Wino.Mail.ViewModels
{
OnPropertyChanged(nameof(HasAccountsDefined));
OnPropertyChanged(nameof(UsedAccountsString));
+ OnPropertyChanged(nameof(IsAccountCreationAlmostOnLimit));
+
+ ReorderAccountsCommand.NotifyCanExecuteChanged();
}
private void PagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
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 266d2c04..1f79d9cd 100644
--- a/Wino.Mail/Views/Account/AccountManagementPage.xaml
+++ b/Wino.Mail/Views/Account/AccountManagementPage.xaml
@@ -222,6 +222,17 @@
+
+
+
+
+
+
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