Add account context menu actions

This commit is contained in:
Burak Kaan Köse
2026-04-16 13:46:52 +02:00
parent 784144cd13
commit e1f53c7f9f
13 changed files with 145 additions and 1 deletions
+1
View File
@@ -23,6 +23,7 @@ public enum FolderSynchronizerOperation
MarkFolderRead, MarkFolderRead,
DeleteFolder, DeleteFolder,
CreateSubFolder, CreateSubFolder,
CreateRootFolder,
} }
public enum CalendarSynchronizerOperation public enum CalendarSynchronizerOperation
@@ -41,6 +41,7 @@ public interface IMailShellClient : IShellClient
Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true); Task ChangeLoadedAccountAsync(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true);
Task PerformFolderOperationAsync(FolderOperation operation, IBaseFolderMenuItem folderMenuItem); Task PerformFolderOperationAsync(FolderOperation operation, IBaseFolderMenuItem folderMenuItem);
Task PerformMoveOperationAsync(IEnumerable<MailCopy> items, IBaseFolderMenuItem targetFolderMenuItem); Task PerformMoveOperationAsync(IEnumerable<MailCopy> items, IBaseFolderMenuItem targetFolderMenuItem);
Task CreateRootFolderAsync(IAccountMenuItem accountMenuItem);
Task CreateNewMailForAsync(MailAccount account); Task CreateNewMailForAsync(MailAccount account);
} }
@@ -315,6 +315,7 @@
"DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.", "DialogMessage_PrintingSuccessMessage": "Mail is sent to printer.",
"DialogMessage_PrintingSuccessTitle": "Success", "DialogMessage_PrintingSuccessTitle": "Success",
"DialogMessage_RenameFolderMessage": "Enter new name for this folder", "DialogMessage_RenameFolderMessage": "Enter new name for this folder",
"DialogMessage_CreateFolderMessage": "Enter name for the new folder",
"DialogMessage_RenameFolderTitle": "Rename Folder", "DialogMessage_RenameFolderTitle": "Rename Folder",
"DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account",
"DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account",
@@ -1534,5 +1535,7 @@
"AccountSetup_GoBackButton": "Go Back", "AccountSetup_GoBackButton": "Go Back",
"AccountSetup_TryAgainButton": "Try Again", "AccountSetup_TryAgainButton": "Try Again",
"Exception_FailedToSynchronizeCategories": "Failed to synchronize categories", "Exception_FailedToSynchronizeCategories": "Failed to synchronize categories",
"ImapCalDavSettings_AutoDiscoveryFailed": "Auto-discovery failed. Please enter settings manually in the Advanced tab." "ImapCalDavSettings_AutoDiscoveryFailed": "Auto-discovery failed. Please enter settings manually in the Advanced tab.",
"AccountContextMenu_ManageAccountSettings": "Manage account settings",
"AccountContextMenu_CreateFolder": "Create folder"
} }
@@ -124,6 +124,7 @@ public static class SynchronizationActionHelper
MarkFolderAsReadRequest => Translator.SyncAction_MarkingFolderAsRead, MarkFolderAsReadRequest => Translator.SyncAction_MarkingFolderAsRead,
DeleteFolderRequest => Translator.FolderOperation_Delete, DeleteFolderRequest => Translator.FolderOperation_Delete,
CreateSubFolderRequest => Translator.FolderOperation_CreateSubFolder, CreateSubFolderRequest => Translator.FolderOperation_CreateSubFolder,
CreateRootFolderRequest => Translator.AccountContextMenu_CreateFolder,
_ => null _ => null
}; };
} }
@@ -0,0 +1,11 @@
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Requests;
namespace Wino.Core.Requests.Folder;
public record CreateRootFolderRequest(MailItemFolder Folder, string NewFolderName) : FolderRequestBase(Folder, FolderSynchronizerOperation.CreateRootFolder)
{
public override void ApplyUIChanges() { }
public override void RevertUIChanges() { }
}
@@ -15,6 +15,7 @@ using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization; using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.Helpers; using Wino.Core.Helpers;
using Wino.Core.Requests.Calendar; using Wino.Core.Requests.Calendar;
using Wino.Core.Requests.Folder;
using Wino.Core.Requests.Mail; using Wino.Core.Requests.Mail;
using Wino.Messaging.Server; using Wino.Messaging.Server;
using Wino.Messaging.UI; using Wino.Messaging.UI;
@@ -220,6 +221,11 @@ public class WinoRequestDelegator : IWinoRequestDelegator
await SendSyncActionsAddedAsync(requestList, accountId).ConfigureAwait(false); await SendSyncActionsAddedAsync(requestList, accountId).ConfigureAwait(false);
await QueueSynchronizationAsync(accountId).ConfigureAwait(false); await QueueSynchronizationAsync(accountId).ConfigureAwait(false);
if (requestList.Any(r => r is DeleteFolderRequest or CreateSubFolderRequest or CreateRootFolderRequest))
{
await QueueFoldersOnlySynchronizationAsync(accountId).ConfigureAwait(false);
}
} }
private async Task<IRequestBase> CreateCalendarEventRequestAsync(CalendarOperationPreparationRequest calendarPreparationRequest) private async Task<IRequestBase> CreateCalendarEventRequestAsync(CalendarOperationPreparationRequest calendarPreparationRequest)
@@ -1724,6 +1724,17 @@ public class GmailSynchronizer : WinoSynchronizer<IClientServiceRequest, Message
return [new HttpRequestBundle<IClientServiceRequest>(networkCall, request, request)]; return [new HttpRequestBundle<IClientServiceRequest>(networkCall, request, request)];
} }
public override List<IRequestBundle<IClientServiceRequest>> CreateRootFolder(CreateRootFolderRequest request)
{
var label = new Label()
{
Name = request.NewFolderName
};
var networkCall = _gmailService.Users.Labels.Create(label, "me");
return [new HttpRequestBundle<IClientServiceRequest>(networkCall, request, request)];
}
#endregion #endregion
#region Request Execution #region Request Execution
@@ -317,6 +317,15 @@ public class ImapSynchronizer : WinoSynchronizer<ImapRequest, ImapMessageCreatio
}, request, request); }, request, request);
} }
public override List<IRequestBundle<ImapRequest>> CreateRootFolder(CreateRootFolderRequest request)
{
return CreateSingleTaskBundle(async (client, item) =>
{
var rootFolder = client.GetFolder(client.PersonalNamespaces[0]);
await rootFolder.CreateAsync(request.NewFolderName, true).ConfigureAwait(false);
}, request, request);
}
public override List<IRequestBundle<ImapRequest>> CreateCalendarEvent(CreateCalendarEventRequest request) public override List<IRequestBundle<ImapRequest>> CreateCalendarEvent(CreateCalendarEventRequest request)
{ {
var handler = ResolveCalendarOperationHandler(); var handler = ResolveCalendarOperationHandler();
@@ -2003,6 +2003,17 @@ public class OutlookSynchronizer : WinoSynchronizer<RequestInformation, Message,
return [new HttpRequestBundle<RequestInformation>(networkCall, request)]; return [new HttpRequestBundle<RequestInformation>(networkCall, request)];
} }
public override List<IRequestBundle<RequestInformation>> CreateRootFolder(CreateRootFolderRequest request)
{
var requestBody = new MailFolder
{
DisplayName = request.NewFolderName
};
var networkCall = _graphClient.Me.MailFolders.ToPostRequestInformation(requestBody);
return [new HttpRequestBundle<RequestInformation>(networkCall, request)];
}
#endregion #endregion
public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<RequestInformation>> batchedRequests, CancellationToken cancellationToken = default) public override async Task ExecuteNativeRequestsAsync(List<IRequestBundle<RequestInformation>> batchedRequests, CancellationToken cancellationToken = default)
@@ -222,6 +222,9 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
case FolderSynchronizerOperation.CreateSubFolder: case FolderSynchronizerOperation.CreateSubFolder:
nativeRequests.AddRange(CreateSubFolder(group.ElementAt(0) as CreateSubFolderRequest)); nativeRequests.AddRange(CreateSubFolder(group.ElementAt(0) as CreateSubFolderRequest));
break; break;
case FolderSynchronizerOperation.CreateRootFolder:
nativeRequests.AddRange(CreateRootFolder(group.ElementAt(0) as CreateRootFolderRequest));
break;
default: default:
break; break;
} }
@@ -658,6 +661,7 @@ public abstract class WinoSynchronizer<TBaseRequest, TMessageType, TCalendarEven
public virtual List<IRequestBundle<TBaseRequest>> MarkFolderAsRead(MarkFolderAsReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> MarkFolderAsRead(MarkFolderAsReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> DeleteFolder(DeleteFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> DeleteFolder(DeleteFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> CreateSubFolder(CreateSubFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> CreateSubFolder(CreateSubFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> CreateRootFolder(CreateRootFolderRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> UpdateCategories(BatchMailCategoryAssignmentRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> UpdateCategories(BatchMailCategoryAssignmentRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> CreateCategory(MailCategoryCreateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> CreateCategory(MailCategoryCreateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
public virtual List<IRequestBundle<TBaseRequest>> UpdateCategory(MailCategoryUpdateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); public virtual List<IRequestBundle<TBaseRequest>> UpdateCategory(MailCategoryUpdateRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType()));
@@ -21,6 +21,7 @@ using Wino.Core.Domain.Models.Launch;
using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Domain.Models.Synchronization; using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.Requests.Folder;
using Wino.Core.Services; using Wino.Core.Services;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Accounts; using Wino.Messaging.Client.Accounts;
@@ -624,6 +625,29 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
} }
} }
public async Task CreateRootFolderAsync(IAccountMenuItem accountMenuItem)
{
var account = accountMenuItem?.HoldingAccounts?.FirstOrDefault();
if (account == null)
return;
var folderName = await _dialogService.ShowTextInputDialogAsync(
string.Empty,
Translator.AccountContextMenu_CreateFolder,
Translator.DialogMessage_CreateFolderMessage,
Translator.Buttons_Create);
if (string.IsNullOrWhiteSpace(folderName))
return;
var placeholderFolder = new MailItemFolder
{
MailAccountId = account.Id
};
await _winoRequestDelegator.ExecuteAsync(account.Id, [new CreateRootFolderRequest(placeholderFolder, folderName.Trim())]);
}
public Task HandleAccountAttentionAsync(MailAccount account) public Task HandleAccountAttentionAsync(MailAccount account)
=> FixAccountIssuesAsync(account); => FixAccountIssuesAsync(account);
+1
View File
@@ -31,6 +31,7 @@
<DataTemplate x:Key="ClickableAccountMenuTemplate" x:DataType="menu:AccountMenuItem"> <DataTemplate x:Key="ClickableAccountMenuTemplate" x:DataType="menu:AccountMenuItem">
<controls:AccountNavigationItem <controls:AccountNavigationItem
x:Name="AccountItem" x:Name="AccountItem"
ContextRequested="AccountMenuItemContextRequested"
Height="50" Height="50"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
@@ -27,6 +27,7 @@ using Wino.Calendar.ViewModels;
using Wino.Mail.ViewModels; using Wino.Mail.ViewModels;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Mail.WinUI.ViewModels; using Wino.Mail.WinUI.ViewModels;
using Wino.Controls;
using Wino.Mail.WinUI.Controls; using Wino.Mail.WinUI.Controls;
using Wino.Mail.WinUI.Helpers; using Wino.Mail.WinUI.Helpers;
using Wino.Helpers; using Wino.Helpers;
@@ -36,6 +37,7 @@ using Wino.Messaging.Client.Accounts;
using Wino.Messaging.Client.Calendar; using Wino.Messaging.Client.Calendar;
using Wino.Messaging.Client.Contacts; using Wino.Messaging.Client.Contacts;
using Wino.Messaging.Client.Mails; using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Navigation;
using Wino.Messaging.Client.Shell; using Wino.Messaging.Client.Shell;
using Wino.Messaging.UI; using Wino.Messaging.UI;
using Wino.Views; using Wino.Views;
@@ -518,6 +520,65 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
} }
} }
private void AccountMenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
if (!ViewModel.IsMailMode)
return;
if (sender is not AccountNavigationItem accountNavigationItem ||
accountNavigationItem.DataContext is not AccountMenuItem accountMenuItem ||
!args.TryGetPosition(sender, out Point p))
{
return;
}
args.Handled = true;
var flyout = new MenuFlyout();
var manageAccountSettingsItem = new MenuFlyoutItem
{
Text = Translator.AccountContextMenu_ManageAccountSettings
};
manageAccountSettingsItem.Icon = new WinoFontIcon { Icon = WinoIconGlyph.ManageAccounts };
manageAccountSettingsItem.Click += (_, _) => NavigateToAccountSettings(accountMenuItem);
flyout.Items.Add(manageAccountSettingsItem);
var createFolderItem = new MenuFlyoutItem
{
Text = Translator.AccountContextMenu_CreateFolder
};
createFolderItem.Icon = new WinoFontIcon { Icon = WinoIconGlyph.CreateFolder };
createFolderItem.Click += async (_, _) => await ViewModel.MailClient.CreateRootFolderAsync(accountMenuItem);
flyout.Items.Add(createFolderItem);
flyout.ShowAt(accountNavigationItem, new FlyoutShowOptions
{
ShowMode = FlyoutShowMode.Standard,
Position = new Point(p.X + 30, p.Y - 20)
});
}
private void NavigateToAccountSettings(AccountMenuItem accountMenuItem)
{
ViewModel.NavigationService.ChangeApplicationMode(
WinoApplicationMode.Settings,
new ShellModeActivationContext
{
Parameter = WinoPage.ManageAccountsPage,
SuppressStartupFlows = true
});
_ = DispatcherQueue.EnqueueAsync(() =>
{
WeakReferenceMessenger.Default.Send(new SettingsRootNavigationRequested(WinoPage.ManageAccountsPage));
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(
accountMenuItem.AccountName,
WinoPage.AccountDetailsPage,
accountMenuItem.AccountId));
});
}
public void Receive(CreateNewMailWithMultipleAccountsRequested message) public void Receive(CreateNewMailWithMultipleAccountsRequested message)
{ {
if (!ViewModel.IsMailMode) if (!ViewModel.IsMailMode)