Merge branch 'codex/mail-categories-v1'

This commit is contained in:
Burak Kaan Köse
2026-04-15 01:18:12 +02:00
61 changed files with 2171 additions and 75 deletions
@@ -0,0 +1,25 @@
using System;
using SQLite;
using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Entities.Mail;
public class MailCategory
{
[PrimaryKey]
public Guid Id { get; set; }
public Guid MailAccountId { get; set; }
public string RemoteId { get; set; }
public string Name { get; set; }
public bool IsFavorite { get; set; }
public string BackgroundColorHex { get; set; }
public string TextColorHex { get; set; }
public MailCategorySource Source { get; set; } = MailCategorySource.Local;
}
@@ -0,0 +1,14 @@
using System;
using SQLite;
namespace Wino.Core.Domain.Entities.Mail;
public class MailCategoryAssignment
{
[PrimaryKey]
public Guid Id { get; set; }
public Guid MailCategoryId { get; set; }
public Guid MailCopyUniqueId { get; set; }
}
@@ -132,5 +132,10 @@ public class MailAccount
/// </summary>
public bool IsAliasSyncSupported => ProviderType == MailProviderType.Gmail || ProviderType == MailProviderType.Outlook;
/// <summary>
/// Gets whether the account can perform category definition sync type.
/// </summary>
public bool IsCategorySyncSupported => ProviderType == MailProviderType.Outlook;
public override string ToString() => Name;
}
@@ -0,0 +1,7 @@
namespace Wino.Core.Domain.Enums;
public enum MailCategorySource
{
Local,
Outlook
}
+8
View File
@@ -13,6 +13,7 @@ public enum MailSynchronizerOperation
AlwaysMoveTo,
MoveToFocused,
Archive,
UpdateCategories,
}
public enum FolderSynchronizerOperation
@@ -35,6 +36,13 @@ public enum CalendarSynchronizerOperation
TentativeEvent,
}
public enum CategorySynchronizerOperation
{
CreateCategory,
UpdateCategory,
DeleteCategory,
}
// UI requests
public enum MailOperation
{
@@ -3,6 +3,7 @@
public enum MailSynchronizationType
{
UpdateProfile, // Only update profile information
Categories, // Only update mail categories
ExecuteRequests, // Run the queued requests, and then synchronize if needed.
FoldersOnly, // Only synchronize folder metadata.
InboxOnly, // Only Inbox, Sent, Draft and Deleted folders.
+1
View File
@@ -24,6 +24,7 @@ public enum WinoPage
AppPreferencesPage,
SettingOptionsPage,
AliasManagementPage,
MailCategoryManagementPage,
ImapCalDavSettingsPage,
KeyboardShortcutsPage,
CalendarPage,
@@ -14,6 +14,22 @@ public interface IFolderMenuItem : IBaseFolderMenuItem
public interface IMergedAccountFolderMenuItem : IBaseFolderMenuItem { }
public interface IMailCategoryMenuItem : IBaseFolderMenuItem
{
Entities.Mail.MailCategory MailCategory { get; }
string TextColorHex { get; }
string BackgroundColorHex { get; }
bool HasTextColor { get; }
}
public interface IMergedMailCategoryMenuItem : IBaseFolderMenuItem
{
IReadOnlyList<Entities.Mail.MailCategory> Categories { get; }
string TextColorHex { get; }
string BackgroundColorHex { get; }
bool HasTextColor { get; }
}
public interface IBaseFolderMenuItem : IMenuItem
{
string FolderName { get; }
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Interfaces;
public interface IMailCategoryService
{
Task<List<MailCategory>> GetCategoriesAsync(Guid accountId);
Task<List<MailCategory>> GetFavoriteCategoriesAsync(Guid accountId);
Task<MailCategory> GetCategoryAsync(Guid categoryId);
Task<bool> CategoryNameExistsAsync(Guid accountId, string name, Guid? excludedCategoryId = null);
Task<MailCategory> CreateCategoryAsync(MailCategory category);
Task UpdateCategoryAsync(MailCategory category);
Task DeleteCategoryAsync(Guid categoryId);
Task DeleteCategoriesAsync(Guid accountId);
Task ToggleFavoriteAsync(Guid categoryId, bool isFavorite);
Task UpdateRemoteIdAsync(Guid categoryId, string remoteId);
Task ReplaceCategoriesAsync(Guid accountId, IEnumerable<MailCategory> categories);
Task ReplaceMailAssignmentsAsync(Guid accountId, Guid mailCopyUniqueId, IEnumerable<string> categoryNames);
Task AssignCategoryAsync(Guid categoryId, IEnumerable<Guid> mailCopyUniqueIds);
Task UnassignCategoryAsync(Guid categoryId, IEnumerable<Guid> mailCopyUniqueIds);
Task<List<MailCategory>> GetCategoriesForMailAsync(Guid accountId, IEnumerable<Guid> mailCopyUniqueIds);
Task<List<Guid>> GetAssignedCategoryIdsForAllAsync(IEnumerable<Guid> mailCopyUniqueIds);
Task<List<string>> GetCategoryNamesForMailAsync(Guid mailCopyUniqueId);
Task<List<MailCopy>> GetMailCopiesForCategoryAsync(Guid categoryId);
Task<List<UnreadCategoryCountResult>> GetUnreadCategoryCountResultsAsync(IEnumerable<Guid> accountIds);
}
@@ -11,6 +11,7 @@ using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
namespace Wino.Core.Domain.Interfaces;
@@ -52,6 +53,13 @@ public interface IMailDialogService : IDialogServiceBase
/// <returns>Created alias model if not canceled.</returns>
Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync();
/// <summary>
/// Presents a dialog to the user for mail category creation/modification.
/// </summary>
#pragma warning disable CS8625
Task<MailCategoryDialogResult> ShowEditMailCategoryDialogAsync(MailCategory category = null);
#pragma warning restore CS8625
/// <summary>
/// Presents a dialog to the user to show email source.
/// </summary>
@@ -72,3 +72,9 @@ public interface ICalendarActionRequest : IRequestBase
Guid? LocalCalendarItemId { get; }
CalendarSynchronizerOperation Operation { get; }
}
public interface ICategoryActionRequest : IRequestBase
{
Guid AccountId { get; }
CategorySynchronizerOperation Operation { get; }
}
@@ -63,6 +63,12 @@ public interface ISynchronizationManager
Task<MailSynchronizationResult> SynchronizeAliasesAsync(Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Handles category synchronization for the given account.
/// </summary>
Task<MailSynchronizationResult> SynchronizeCategoriesAsync(Guid accountId,
CancellationToken cancellationToken = default);
/// <summary>
/// Handles profile synchronization for the given account.
/// </summary>
@@ -1,4 +1,6 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
@@ -36,4 +38,9 @@ public interface IWinoRequestDelegator
/// </summary>
/// <param name="calendarOperationPreparationRequest">Calendar preparation request.</param>
Task ExecuteAsync(CalendarOperationPreparationRequest calendarOperationPreparationRequest);
/// <summary>
/// Queues pre-built requests for a single account and triggers synchronization.
/// </summary>
Task ExecuteAsync(Guid accountId, IEnumerable<IRequestBase> requests);
}
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
namespace Wino.Core.Domain.MenuItems;
public partial class MailCategoryMenuItem : MenuItemBase<MailCategory, IMenuItem>, IFolderMenuItem, IMailCategoryMenuItem
{
private IReadOnlyList<IMailItemFolder> _handlingFolders;
[ObservableProperty]
private int unreadItemCount;
public MailCategoryMenuItem(MailCategory category, MailAccount parentAccount, IEnumerable<IMailItemFolder> handlingFolders, IMenuItem parentMenuItem)
: base(category, category.Id, parentMenuItem)
{
ParentAccount = parentAccount;
_handlingFolders = handlingFolders?.ToList() ?? [];
}
public string FolderName => Parameter.Name;
public bool IsSynchronizationEnabled => false;
public SpecialFolderType SpecialFolderType => SpecialFolderType.Other;
public IEnumerable<IMailItemFolder> HandlingFolders => _handlingFolders;
public new ObservableCollection<IMenuItem> SubMenuItems { get; } = [];
public bool IsMoveTarget => true;
public bool IsSticky => false;
public bool IsSystemFolder => false;
public bool ShowUnreadCount => true;
public string AssignedAccountName => ParentAccount?.Name;
public MailAccount ParentAccount { get; private set; }
public string TextColorHex => Parameter.TextColorHex;
public string BackgroundColorHex => Parameter.BackgroundColorHex;
public bool HasTextColor => !string.IsNullOrWhiteSpace(Parameter.TextColorHex);
public MailCategory MailCategory => Parameter;
public void UpdateFolder(IMailItemFolder folder)
{
}
public void UpdateParentAccounnt(MailAccount account) => ParentAccount = account;
}
@@ -22,11 +22,13 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public IEnumerable<IAccountMenuItem> GetAllAccountMenuItems()
{
foreach (var item in this)
var rootItems = this.ToList();
foreach (var item in rootItems)
{
if (item is MergedAccountMenuItem mergedAccountMenuItem)
{
foreach (var singleItem in mergedAccountMenuItem.SubMenuItems.OfType<IAccountMenuItem>())
foreach (var singleItem in mergedAccountMenuItem.SubMenuItems.OfType<IAccountMenuItem>().ToList())
{
yield return singleItem;
}
@@ -40,9 +42,11 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public IEnumerable<IBaseFolderMenuItem> GetAllFolderMenuItems(Guid folderId)
{
foreach (var item in this)
var rootItems = this.ToList();
foreach (var item in rootItems)
{
if (item is IBaseFolderMenuItem folderMenuItem)
if (item is IBaseFolderMenuItem folderMenuItem && item is not IMailCategoryMenuItem && item is not IMergedMailCategoryMenuItem)
{
if (folderMenuItem.HandlingFolders.Any(a => a.Id == folderId))
{
@@ -50,7 +54,7 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
}
else if (folderMenuItem.SubMenuItems.Any())
{
foreach (var subItem in folderMenuItem.SubMenuItems.OfType<IBaseFolderMenuItem>())
foreach (var subItem in folderMenuItem.SubMenuItems.OfType<IBaseFolderMenuItem>().ToList())
{
if (subItem.HandlingFolders.Any(a => a.Id == folderId))
{
@@ -65,8 +69,10 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public bool TryGetAccountMenuItem(Guid accountId, out IAccountMenuItem value)
{
value = this.OfType<AccountMenuItem>().FirstOrDefault(a => a.AccountId == accountId);
value ??= this.OfType<MergedAccountMenuItem>().FirstOrDefault(a => a.SubMenuItems.OfType<AccountMenuItem>().Where(b => b.AccountId == accountId) != null);
var rootItems = this.ToList();
value = rootItems.OfType<AccountMenuItem>().FirstOrDefault(a => a.AccountId == accountId);
value ??= rootItems.OfType<MergedAccountMenuItem>().FirstOrDefault(a => a.SubMenuItems.OfType<AccountMenuItem>().Any(b => b.AccountId == accountId));
return value != null;
}
@@ -74,7 +80,9 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
// Pattern: Look for special folder menu item inside the loaded folders for Windows Mail style menu items.
public bool TryGetWindowsStyleRootSpecialFolderMenuItem(Guid accountId, SpecialFolderType specialFolderType, out FolderMenuItem value)
{
value = this.OfType<IBaseFolderMenuItem>()
var rootItems = this.ToList();
value = rootItems.OfType<IBaseFolderMenuItem>()
.FirstOrDefault(a => a.HandlingFolders.Any(b => b.MailAccountId == accountId && b.SpecialFolderType == specialFolderType)) as FolderMenuItem;
return value != null;
@@ -84,7 +92,9 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
// This will not look for the folders inside individual account menu items inside merged account menu item.
public bool TryGetMergedAccountSpecialFolderMenuItem(Guid mergedInboxId, SpecialFolderType specialFolderType, out IBaseFolderMenuItem value)
{
value = this.OfType<MergedAccountFolderMenuItem>()
var rootItems = this.ToList();
value = rootItems.OfType<MergedAccountFolderMenuItem>()
.Where(a => a.MergedInbox.Id == mergedInboxId)
.FirstOrDefault(a => a.SpecialFolderType == specialFolderType);
@@ -93,11 +103,14 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public bool TryGetFolderMenuItem(Guid folderId, out IBaseFolderMenuItem value)
{
var rootItems = this.ToList();
// Root folders
value = this.OfType<IBaseFolderMenuItem>()
value = rootItems.OfType<IBaseFolderMenuItem>()
.Where(a => a is not IMailCategoryMenuItem && a is not IMergedMailCategoryMenuItem)
.FirstOrDefault(a => a.HandlingFolders.Any(b => b.Id == folderId));
value ??= this.OfType<FolderMenuItem>()
value ??= rootItems.OfType<FolderMenuItem>()
.SelectMany(a => a.SubMenuItems)
.OfType<IBaseFolderMenuItem>()
.FirstOrDefault(a => a.HandlingFolders.Any(b => b.Id == folderId));
@@ -105,10 +118,23 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
return value != null;
}
public bool TryGetCategoryMenuItem(Guid categoryId, out IBaseFolderMenuItem value)
{
var rootItems = this.ToList();
value = rootItems.OfType<IMailCategoryMenuItem>()
.FirstOrDefault(a => a.MailCategory.Id == categoryId);
value ??= rootItems.OfType<IMergedMailCategoryMenuItem>()
.FirstOrDefault(a => a.Categories.Any(b => b.Id == categoryId)) as IBaseFolderMenuItem;
return value != null;
}
public void UpdateUnreadItemCountsToZero()
{
// Handle the root folders.
foreach (var item in this.OfType<IBaseFolderMenuItem>())
foreach (var item in this.OfType<IBaseFolderMenuItem>().ToList())
{
RecursivelyResetUnreadItemCount(item);
}
@@ -120,7 +146,7 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
if (baseFolderMenuItem.SubMenuItems == null) return;
foreach (var subMenuItem in baseFolderMenuItem.SubMenuItems.OfType<IBaseFolderMenuItem>())
foreach (var subMenuItem in baseFolderMenuItem.SubMenuItems.OfType<IBaseFolderMenuItem>().ToList())
{
RecursivelyResetUnreadItemCount(subMenuItem);
}
@@ -128,7 +154,9 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public bool TryGetSpecialFolderMenuItem(Guid accountId, SpecialFolderType specialFolderType, out FolderMenuItem value)
{
value = this.OfType<IBaseFolderMenuItem>()
var rootItems = this.ToList();
value = rootItems.OfType<IBaseFolderMenuItem>()
.FirstOrDefault(a => a.HandlingFolders.Any(b => b.MailAccountId == accountId && b.SpecialFolderType == specialFolderType)) as FolderMenuItem;
return value != null;
@@ -142,11 +170,12 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
public AccountMenuItem GetSpecificAccountMenuItem(Guid accountId)
{
AccountMenuItem accountMenuItem = null;
var rootItems = this.ToList();
accountMenuItem = this.OfType<AccountMenuItem>().FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId));
accountMenuItem = rootItems.OfType<AccountMenuItem>().FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId));
// Look for the items inside the merged accounts if regular menu item is not found.
accountMenuItem ??= this.OfType<MergedAccountMenuItem>()
accountMenuItem ??= rootItems.OfType<MergedAccountMenuItem>()
.FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId))?.SubMenuItems
.OfType<AccountMenuItem>()
.FirstOrDefault(a => a.AccountId == accountId);
@@ -167,7 +196,7 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
/// <param name="isEnabled">Whether menu items should be enabled or disabled.</param>
public async Task SetAccountMenuItemEnabledStatusAsync(bool isEnabled)
{
var accountItems = this.Where(a => a is IAccountMenuItem).Cast<IAccountMenuItem>();
var accountItems = this.Where(a => a is IAccountMenuItem).Cast<IAccountMenuItem>().ToList();
await _dispatcher.ExecuteOnUIThread(() =>
{
@@ -192,6 +221,7 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
{
// Check root-level items.
var rootItem = this.OfType<IBaseFolderMenuItem>()
.Where(a => a is not IMailCategoryMenuItem && a is not IMergedMailCategoryMenuItem)
.FirstOrDefault(a => a.HandlingFolders.Any(b => b.Id == folderId));
if (rootItem != null)
@@ -201,7 +231,7 @@ public class MenuItemCollection : ObservableRangeCollection<IMenuItem>
}
// Check sub-items of root folders.
foreach (var rootFolder in this.OfType<IBaseFolderMenuItem>())
foreach (var rootFolder in this.OfType<IBaseFolderMenuItem>().ToList())
{
var subItem = rootFolder.SubMenuItems
.OfType<IBaseFolderMenuItem>()
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
namespace Wino.Core.Domain.MenuItems;
public partial class MergedMailCategoryMenuItem : MenuItemBase<List<MailCategory>, IMenuItem>, IMergedAccountFolderMenuItem, IMergedMailCategoryMenuItem
{
private readonly IReadOnlyList<IMailItemFolder> _handlingFolders;
[ObservableProperty]
private int unreadItemCount;
public MergedMailCategoryMenuItem(List<MailCategory> categories, IEnumerable<IMailItemFolder> handlingFolders, MergedInbox mergedInbox)
: base(categories, null, null)
{
_handlingFolders = handlingFolders?.ToList() ?? [];
MergedInbox = mergedInbox;
}
public string FolderName => Parameter.FirstOrDefault()?.Name ?? string.Empty;
public bool IsSynchronizationEnabled => false;
public SpecialFolderType SpecialFolderType => SpecialFolderType.Other;
public IEnumerable<IMailItemFolder> HandlingFolders => _handlingFolders;
public bool IsMoveTarget => true;
public bool IsSticky => false;
public bool IsSystemFolder => false;
public bool ShowUnreadCount => true;
public string AssignedAccountName => MergedInbox?.Name;
public MergedInbox MergedInbox { get; }
public string TextColorHex => Parameter.FirstOrDefault()?.TextColorHex;
public string BackgroundColorHex => Parameter.FirstOrDefault()?.BackgroundColorHex;
public bool HasTextColor => !string.IsNullOrWhiteSpace(TextColorHex);
public IReadOnlyList<MailCategory> Categories => Parameter;
public void UpdateFolder(IMailItemFolder folder)
{
}
}
@@ -0,0 +1,10 @@
using System;
namespace Wino.Core.Domain.Models.Accounts;
public class UnreadCategoryCountResult
{
public Guid CategoryId { get; set; }
public Guid AccountId { get; set; }
public int UnreadItemCount { get; set; }
}
@@ -0,0 +1,3 @@
namespace Wino.Core.Domain.Models.MailItem;
public sealed record MailCategoryColorOption(string BackgroundColorHex, string TextColorHex);
@@ -0,0 +1,3 @@
namespace Wino.Core.Domain.Models.MailItem;
public sealed record MailCategoryDialogResult(string Name, string BackgroundColorHex, string TextColorHex);
@@ -0,0 +1,30 @@
using System.Collections.Generic;
namespace Wino.Core.Domain.Models.MailItem;
public static class MailCategoryPalette
{
public static IReadOnlyList<MailCategoryColorOption> DefaultOptions { get; } =
[
new("#FEE2E2", "#991B1B"),
new("#FECACA", "#7F1D1D"),
new("#FFEDD5", "#9A3412"),
new("#FED7AA", "#7C2D12"),
new("#FEF3C7", "#92400E"),
new("#FDE68A", "#78350F"),
new("#ECFCCB", "#3F6212"),
new("#D9F99D", "#365314"),
new("#DCFCE7", "#166534"),
new("#BBF7D0", "#14532D"),
new("#CCFBF1", "#115E59"),
new("#99F6E4", "#134E4A"),
new("#CFFAFE", "#155E75"),
new("#A5F3FC", "#164E63"),
new("#DBEAFE", "#1D4ED8"),
new("#BFDBFE", "#1E3A8A"),
new("#E0E7FF", "#4338CA"),
new("#DDD6FE", "#5B21B6"),
new("#F3E8FF", "#7E22CE"),
new("#FCE7F3", "#9D174D")
];
}
@@ -9,4 +9,5 @@ public record NewMailItemPackage(
MailCopy Copy,
MimeMessage Mime,
string AssignedRemoteFolderId,
IReadOnlyList<AccountContact> ExtractedContacts = null);
IReadOnlyList<AccountContact> ExtractedContacts = null,
IReadOnlyList<string> CategoryNames = null);
@@ -17,4 +17,8 @@ public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders
List<MailCopy> PreFetchMailCopies = null,
bool DeduplicateByServerId = false,
int Skip = 0,
int Take = 0);
int Take = 0)
{
public IReadOnlyList<Guid> CategoryIds { get; init; }
public bool IsCategoryView => CategoryIds?.Count > 0;
}
@@ -35,6 +35,10 @@ public abstract record CalendarRequestBase(CalendarItem Item) : RequestBase<Cale
public virtual Guid? LocalCalendarItemId => Item?.Id;
}
public abstract record CategoryRequestBase(Guid AccountId) : RequestBase<CategorySynchronizerOperation>, ICategoryActionRequest
{
}
public class BatchCollection<TRequestType> : List<TRequestType>, IUIChangeRequest where TRequestType : IUIChangeRequest
{
public BatchCollection(IEnumerable<TRequestType> collection) : base(collection)
@@ -170,6 +170,7 @@ public static class SettingsNavigationInfoProvider
WinoPage.AccountDetailsPage => WinoPage.ManageAccountsPage,
WinoPage.MergedAccountDetailsPage => WinoPage.ManageAccountsPage,
WinoPage.AliasManagementPage => WinoPage.ManageAccountsPage,
WinoPage.MailCategoryManagementPage => WinoPage.ManageAccountsPage,
WinoPage.SignatureManagementPage => WinoPage.ManageAccountsPage,
WinoPage.ImapCalDavSettingsPage => WinoPage.ManageAccountsPage,
WinoPage.CreateEmailTemplatePage => WinoPage.EmailTemplatesPage,
@@ -67,6 +67,7 @@
"BasicIMAPSetupDialog_Password": "Password",
"BasicIMAPSetupDialog_Title": "IMAP Account",
"Busy": "Busy",
"Buttons_Add": "Add",
"Buttons_AddAccount": "Add Account",
"Buttons_FixAccount": "Fix Account",
"Buttons_AddNewAlias": "Add New Alias",
@@ -877,10 +878,28 @@
"SettingsManageAccountSettings_Title": "Manage Accounts",
"SettingsManageAliases_Description": "See e-mail aliases assigned for this account, update or delete them.",
"SettingsManageAliases_Title": "Aliases",
"SettingsMailCategories_Description": "Manage synchronized and local categories for this account.",
"SettingsMailCategories_Title": "Categories",
"SettingsEditAccountDetails_Title": "Edit Account Details",
"SettingsEditAccountDetails_Description": "Change account name, sender name and assign a new color if you like.",
"EditAccountDetailsPage_SaveSuccess_Title": "Changes Saved",
"EditAccountDetailsPage_SaveSuccess_Message": "Your account details have been updated successfully.",
"MailCategoryManagementPage_Title": "Categories",
"MailCategoryManagementPage_Description": "Create, edit, delete, and favorite categories for this account.",
"MailCategoryManagementPage_Empty": "No categories yet.",
"MailCategoryManagementPage_DeleteConfirmationTitle": "Delete Category",
"MailCategoryManagementPage_DeleteConfirmationMessage": "Delete category \"{0}\"?",
"MailCategoryManagementPage_RefreshConfirmationMessage": "This will delete all your local categories, and re-synchronize everything from the server. Do you want to continue?",
"MailCategoryMenuItem": "Category",
"MailCategoryDialog_CreateTitle": "Create category",
"MailCategoryDialog_EditTitle": "Edit category",
"MailCategoryDialog_Name": "Name",
"MailCategoryDialog_NamePlaceholder": "Category name",
"MailCategoryDialog_Color": "Color",
"MailCategoryDialog_InvalidNameTitle": "Category name required",
"MailCategoryDialog_InvalidNameMessage": "Enter a category name to continue.",
"MailCategoryDialog_DuplicateTitle": "Category already exists",
"MailCategoryDialog_DuplicateMessage": "A category with the same name already exists for this account.",
"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.",
@@ -1493,11 +1512,13 @@
"AccountSetup_Step_TestingCalendarAuth": "Testing calendar authentication",
"AccountSetup_Step_SavingAccount": "Saving account information",
"AccountSetup_Step_FetchingCalendarMetadata": "Fetching calendar metadata",
"AccountSetup_Step_SyncingCategories": "Synchronizing categories",
"AccountSetup_Step_SyncingAliases": "Synchronizing aliases",
"AccountSetup_Step_Finalizing": "Finalizing setup",
"AccountSetup_FailureMessage": "Setup failed. Go back to fix your settings, or try again later.",
"AccountSetup_SuccessMessage": "Your account has been set up successfully!",
"AccountSetup_GoBackButton": "Go Back",
"AccountSetup_TryAgainButton": "Try Again",
"Exception_FailedToSynchronizeCategories": "Failed to synchronize categories",
"ImapCalDavSettings_AutoDiscoveryFailed": "Auto-discovery failed. Please enter settings manually in the Advanced tab."
}