Improved keyboad shortcuts.
This commit is contained in:
@@ -17,6 +17,7 @@ using Wino.Core.Domain.Enums;
|
|||||||
using Wino.Core.Domain.Extensions;
|
using Wino.Core.Domain.Extensions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Calendar;
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
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.ViewModels;
|
using Wino.Core.ViewModels;
|
||||||
@@ -393,6 +394,18 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Calendar)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Action == KeyboardShortcutAction.NewEvent)
|
||||||
|
{
|
||||||
|
await NewEventAsync();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Serilog;
|
|||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
using Wino.Calendar.ViewModels.Interfaces;
|
using Wino.Calendar.ViewModels.Interfaces;
|
||||||
using Wino.Calendar.ViewModels.Messages;
|
using Wino.Calendar.ViewModels.Messages;
|
||||||
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Collections;
|
using Wino.Core.Domain.Collections;
|
||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
@@ -21,6 +22,7 @@ using Wino.Core.Domain.Extensions;
|
|||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Calendar;
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
using Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
using Wino.Core.ViewModels;
|
using Wino.Core.ViewModels;
|
||||||
using Wino.Messaging.Client.Calendar;
|
using Wino.Messaging.Client.Calendar;
|
||||||
@@ -135,6 +137,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
private readonly INativeAppService _nativeAppService;
|
private readonly INativeAppService _nativeAppService;
|
||||||
private readonly IPreferencesService _preferencesService;
|
private readonly IPreferencesService _preferencesService;
|
||||||
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
private readonly IWinoRequestDelegator _winoRequestDelegator;
|
||||||
|
private readonly IMailDialogService _dialogService;
|
||||||
|
|
||||||
// Store latest rendered options.
|
// Store latest rendered options.
|
||||||
private CalendarDisplayType _currentDisplayType;
|
private CalendarDisplayType _currentDisplayType;
|
||||||
@@ -156,7 +159,8 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
INativeAppService nativeAppService,
|
INativeAppService nativeAppService,
|
||||||
IAccountCalendarStateService accountCalendarStateService,
|
IAccountCalendarStateService accountCalendarStateService,
|
||||||
IPreferencesService preferencesService,
|
IPreferencesService preferencesService,
|
||||||
IWinoRequestDelegator winoRequestDelegator)
|
IWinoRequestDelegator winoRequestDelegator,
|
||||||
|
IMailDialogService dialogService)
|
||||||
{
|
{
|
||||||
StatePersistanceService = statePersistanceService;
|
StatePersistanceService = statePersistanceService;
|
||||||
AccountCalendarStateService = accountCalendarStateService;
|
AccountCalendarStateService = accountCalendarStateService;
|
||||||
@@ -167,6 +171,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
_nativeAppService = nativeAppService;
|
_nativeAppService = nativeAppService;
|
||||||
_preferencesService = preferencesService;
|
_preferencesService = preferencesService;
|
||||||
_winoRequestDelegator = winoRequestDelegator;
|
_winoRequestDelegator = winoRequestDelegator;
|
||||||
|
_dialogService = dialogService;
|
||||||
|
|
||||||
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
AccountCalendarStateService.AccountCalendarSelectionStateChanged += UpdateAccountCalendarRequested;
|
||||||
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
|
AccountCalendarStateService.CollectiveAccountGroupSelectionStateChanged += AccountCalendarStateCollectivelyChanged;
|
||||||
@@ -175,6 +180,35 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
|
|||||||
RegisterRecipients();
|
RegisterRecipients();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Calendar || args.Action != KeyboardShortcutAction.Delete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (DisplayDetailsCalendarItemViewModel?.CalendarItem == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (DisplayDetailsCalendarItemViewModel.CalendarItem.IsRecurringParent)
|
||||||
|
{
|
||||||
|
var confirmed = await _dialogService.ShowConfirmationDialogAsync(
|
||||||
|
Translator.DialogMessage_DeleteRecurringSeriesMessage,
|
||||||
|
Translator.DialogMessage_DeleteRecurringSeriesTitle,
|
||||||
|
Translator.Buttons_Delete);
|
||||||
|
|
||||||
|
if (!confirmed)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var preparationRequest = new CalendarOperationPreparationRequest(
|
||||||
|
CalendarSynchronizerOperation.DeleteEvent,
|
||||||
|
DisplayDetailsCalendarItemViewModel.CalendarItem,
|
||||||
|
null);
|
||||||
|
|
||||||
|
await _winoRequestDelegator.ExecuteAsync(preparationRequest);
|
||||||
|
DisplayDetailsCalendarItemViewModel = null;
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void RegisterRecipients()
|
protected override void RegisterRecipients()
|
||||||
{
|
{
|
||||||
base.RegisterRecipients();
|
base.RegisterRecipients();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using Wino.Core.Domain.Entities.Calendar;
|
|||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Calendar;
|
using Wino.Core.Domain.Models.Calendar;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
using Wino.Core.Services;
|
using Wino.Core.Services;
|
||||||
using Wino.Core.ViewModels;
|
using Wino.Core.ViewModels;
|
||||||
@@ -498,6 +499,15 @@ public partial class EventDetailsPageViewModel : CalendarBaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Calendar || args.Action != KeyboardShortcutAction.Delete)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await DeleteAsync();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private Task JoinOnlineAsync()
|
private Task JoinOnlineAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ public class KeyboardShortcut
|
|||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The application mode this shortcut applies to.
|
||||||
|
/// </summary>
|
||||||
|
public WinoApplicationMode Mode { get; set; } = WinoApplicationMode.Mail;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key combination string (e.g., "D", "Delete", "F1").
|
/// The key combination string (e.g., "D", "Delete", "F1").
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -23,9 +28,9 @@ public class KeyboardShortcut
|
|||||||
public ModifierKeys ModifierKeys { get; set; }
|
public ModifierKeys ModifierKeys { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mail operation this shortcut triggers.
|
/// The shortcut action this shortcut triggers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MailOperation MailOperation { get; set; }
|
public KeyboardShortcutAction Action { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this shortcut is enabled.
|
/// Whether this shortcut is enabled.
|
||||||
@@ -55,6 +60,6 @@ public class KeyboardShortcut
|
|||||||
modifierText += "Win+";
|
modifierText += "Win+";
|
||||||
|
|
||||||
return modifierText + Key;
|
return modifierText + Key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Wino.Core.Domain.Enums;
|
||||||
|
|
||||||
|
public enum KeyboardShortcutAction
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
NewMail,
|
||||||
|
ToggleReadUnread,
|
||||||
|
ToggleFlag,
|
||||||
|
ToggleArchive,
|
||||||
|
Delete,
|
||||||
|
Move,
|
||||||
|
Reply,
|
||||||
|
ReplyAll,
|
||||||
|
Send,
|
||||||
|
NewEvent
|
||||||
|
}
|
||||||
@@ -37,21 +37,23 @@ public interface IKeyboardShortcutService
|
|||||||
Task DeleteKeyboardShortcutAsync(Guid shortcutId);
|
Task DeleteKeyboardShortcutAsync(Guid shortcutId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mail operation for the given key combination.
|
/// Gets the keyboard shortcut for the given key combination in a specific mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="mode">The application mode to search within.</param>
|
||||||
/// <param name="key">The pressed key.</param>
|
/// <param name="key">The pressed key.</param>
|
||||||
/// <param name="modifierKeys">The modifier keys pressed.</param>
|
/// <param name="modifierKeys">The modifier keys pressed.</param>
|
||||||
/// <returns>The mail operation if found, otherwise null.</returns>
|
/// <returns>The matching shortcut if found, otherwise null.</returns>
|
||||||
Task<MailOperation?> GetMailOperationForKeyAsync(string key, ModifierKeys modifierKeys);
|
Task<KeyboardShortcut> GetShortcutForKeyAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a key combination is already assigned to another shortcut.
|
/// Checks if a key combination is already assigned to another shortcut.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="mode">The application mode to check within.</param>
|
||||||
/// <param name="key">The key to check.</param>
|
/// <param name="key">The key to check.</param>
|
||||||
/// <param name="modifierKeys">The modifier keys to check.</param>
|
/// <param name="modifierKeys">The modifier keys to check.</param>
|
||||||
/// <param name="excludeShortcutId">Optional ID to exclude from the check (for updates).</param>
|
/// <param name="excludeShortcutId">Optional ID to exclude from the check (for updates).</param>
|
||||||
/// <returns>True if the combination is already used, false otherwise.</returns>
|
/// <returns>True if the combination is already used, false otherwise.</returns>
|
||||||
Task<bool> IsKeyCombinationInUseAsync(string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null);
|
Task<bool> IsKeyCombinationInUseAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates default keyboard shortcuts for common mail operations.
|
/// Creates default keyboard shortcuts for common mail operations.
|
||||||
@@ -62,4 +64,4 @@ public interface IKeyboardShortcutService
|
|||||||
/// Resets all shortcuts to defaults.
|
/// Resets all shortcuts to defaults.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task ResetToDefaultShortcutsAsync();
|
Task ResetToDefaultShortcutsAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ public class KeyboardShortcutDialogResult
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSuccess { get; set; }
|
public bool IsSuccess { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The application mode selected by the user.
|
||||||
|
/// </summary>
|
||||||
|
public WinoApplicationMode Mode { get; set; } = WinoApplicationMode.Mail;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key combination entered by the user.
|
/// The key combination entered by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -23,21 +28,22 @@ public class KeyboardShortcutDialogResult
|
|||||||
public ModifierKeys ModifierKeys { get; set; }
|
public ModifierKeys ModifierKeys { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mail operation selected by the user.
|
/// The shortcut action selected by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MailOperation MailOperation { get; set; }
|
public KeyboardShortcutAction Action { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a successful result.
|
/// Creates a successful result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static KeyboardShortcutDialogResult Success(string key, ModifierKeys modifierKeys, MailOperation mailOperation)
|
public static KeyboardShortcutDialogResult Success(WinoApplicationMode mode, string key, ModifierKeys modifierKeys, KeyboardShortcutAction action)
|
||||||
{
|
{
|
||||||
return new KeyboardShortcutDialogResult
|
return new KeyboardShortcutDialogResult
|
||||||
{
|
{
|
||||||
IsSuccess = true,
|
IsSuccess = true,
|
||||||
|
Mode = mode,
|
||||||
Key = key,
|
Key = key,
|
||||||
ModifierKeys = modifierKeys,
|
ModifierKeys = modifierKeys,
|
||||||
MailOperation = mailOperation
|
Action = action
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,4 +57,4 @@ public class KeyboardShortcutDialogResult
|
|||||||
IsSuccess = false
|
IsSuccess = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
|
||||||
|
namespace Wino.Core.Domain.Models;
|
||||||
|
|
||||||
|
public class KeyboardShortcutTriggerDetails
|
||||||
|
{
|
||||||
|
public Guid ShortcutId { get; init; }
|
||||||
|
public WinoApplicationMode Mode { get; init; }
|
||||||
|
public KeyboardShortcutAction Action { get; init; }
|
||||||
|
public string Key { get; init; } = string.Empty;
|
||||||
|
public ModifierKeys ModifierKeys { get; init; }
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
public object Sender { get; init; }
|
||||||
|
public object Origin { get; init; }
|
||||||
|
}
|
||||||
@@ -362,19 +362,29 @@
|
|||||||
"KeyboardShortcuts_FailedToReset": "Failed to reset keyboard shortcuts.",
|
"KeyboardShortcuts_FailedToReset": "Failed to reset keyboard shortcuts.",
|
||||||
"KeyboardShortcuts_FailedToUpdate": "Failed to update keyboard shortcuts",
|
"KeyboardShortcuts_FailedToUpdate": "Failed to update keyboard shortcuts",
|
||||||
"KeyboardShortcuts_MailoperationAction": "Action",
|
"KeyboardShortcuts_MailoperationAction": "Action",
|
||||||
|
"KeyboardShortcuts_Action": "Action",
|
||||||
"KeyboardShortcuts_FailedToLoad": "Failed to load keyboard shortcuts.",
|
"KeyboardShortcuts_FailedToLoad": "Failed to load keyboard shortcuts.",
|
||||||
"KeyboardShortcuts_EnterKeyForShortcut": "Please enter a key for the shortcut.",
|
"KeyboardShortcuts_EnterKeyForShortcut": "Please enter a key for the shortcut.",
|
||||||
"KeyboardShortcuts_SelectOperationForShortcut": "Please an action to perform for the shortcut.",
|
"KeyboardShortcuts_SelectOperationForShortcut": "Please an action to perform for the shortcut.",
|
||||||
|
"KeyboardShortcuts_EnterKey": "Please enter a key for the shortcut.",
|
||||||
|
"KeyboardShortcuts_SelectOperation": "Please select an action for the shortcut.",
|
||||||
"KeyboardShortcuts_ShortcutInUse": "This shortcut is already in use by another s hortcut.",
|
"KeyboardShortcuts_ShortcutInUse": "This shortcut is already in use by another s hortcut.",
|
||||||
"KeyboardShortcuts_FailedToSave": "Failed to save the shortcut.",
|
"KeyboardShortcuts_FailedToSave": "Failed to save the shortcut.",
|
||||||
"KeyboardShortcuts_FailedToDelete": "Failed to delete the shortcut.",
|
"KeyboardShortcuts_FailedToDelete": "Failed to delete the shortcut.",
|
||||||
"KeyboardShortcuts_PageDescription": "Set up keyboard shortcuts for quick mail operations. Press keys while focused on the key input field to capture shortcuts.",
|
"KeyboardShortcuts_PageDescription": "Set up keyboard shortcuts for quick mail operations. Press keys while focused on the key input field to capture shortcuts.",
|
||||||
"KeyboardShortcuts_Add": "Add shortcut",
|
"KeyboardShortcuts_Add": "Add shortcut",
|
||||||
|
"KeyboardShortcuts_EditTitle": "Edit Keyboard Shortcut",
|
||||||
"KeyboardShortcuts_ResetToDefaults": "Reset to Defaults",
|
"KeyboardShortcuts_ResetToDefaults": "Reset to Defaults",
|
||||||
"KeyboardShortcuts_PressKeysHere": "Press keys here...",
|
"KeyboardShortcuts_PressKeysHere": "Press keys here...",
|
||||||
"KeyboardShortcuts_KeyCombination": "Key Combination",
|
"KeyboardShortcuts_KeyCombination": "Key Combination",
|
||||||
"KeyboardShortcuts_FocusArea": "Focus the field above and press the desired key combination",
|
"KeyboardShortcuts_FocusArea": "Focus the field above and press the desired key combination",
|
||||||
"KeyboardShortcuts_Modifiers": "Modifier Keys",
|
"KeyboardShortcuts_Modifiers": "Modifier Keys",
|
||||||
|
"KeyboardShortcuts_Mode": "App Mode",
|
||||||
|
"KeyboardShortcuts_ModeMail": "Mail",
|
||||||
|
"KeyboardShortcuts_ModeCalendar": "Calendar",
|
||||||
|
"KeyboardShortcuts_ActionToggleReadUnread": "Toggle read/unread",
|
||||||
|
"KeyboardShortcuts_ActionToggleFlag": "Toggle flag",
|
||||||
|
"KeyboardShortcuts_ActionToggleArchive": "Toggle archive/unarchive",
|
||||||
"ImageRenderingDisabled": "Image rendering is disabled for this message.",
|
"ImageRenderingDisabled": "Image rendering is disabled for this message.",
|
||||||
"ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method",
|
"ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method",
|
||||||
"ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security",
|
"ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
|
|
||||||
namespace Wino.Core.ViewModels;
|
namespace Wino.Core.ViewModels;
|
||||||
@@ -40,6 +41,8 @@ public class CoreBaseViewModel : ObservableRecipient, INavigationAware
|
|||||||
|
|
||||||
public virtual void OnPageLoaded() { }
|
public virtual void OnPageLoaded() { }
|
||||||
|
|
||||||
|
public virtual Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args) => Task.CompletedTask;
|
||||||
|
|
||||||
public Task ExecuteUIThread(Action action)
|
public Task ExecuteUIThread(Action action)
|
||||||
{
|
{
|
||||||
if (action == null) return Task.CompletedTask;
|
if (action == null) return Task.CompletedTask;
|
||||||
|
|||||||
@@ -13,13 +13,16 @@ public partial class BreadcrumbNavigationItemViewModel : ObservableObject
|
|||||||
|
|
||||||
public int StepNumber { get; set; }
|
public int StepNumber { get; set; }
|
||||||
|
|
||||||
|
public int BackStackDepth { get; set; }
|
||||||
|
|
||||||
public BreadcrumbNavigationRequested Request { get; set; }
|
public BreadcrumbNavigationRequested Request { get; set; }
|
||||||
|
|
||||||
public BreadcrumbNavigationItemViewModel(BreadcrumbNavigationRequested request, bool isActive, int stepNumber = 0)
|
public BreadcrumbNavigationItemViewModel(BreadcrumbNavigationRequested request, bool isActive, int stepNumber = 0, int backStackDepth = 0)
|
||||||
{
|
{
|
||||||
Request = request;
|
Request = request;
|
||||||
Title = request.PageTitle;
|
Title = request.PageTitle;
|
||||||
IsActive = isActive;
|
IsActive = isActive;
|
||||||
StepNumber = stepNumber;
|
StepNumber = stepNumber;
|
||||||
|
BackStackDepth = backStackDepth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Wino.Core.Domain;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
|
||||||
|
namespace Wino.Core.ViewModels.Data;
|
||||||
|
|
||||||
|
public class KeyboardShortcutActionViewModel
|
||||||
|
{
|
||||||
|
public WinoApplicationMode Mode { get; }
|
||||||
|
public KeyboardShortcutAction Action { get; }
|
||||||
|
|
||||||
|
public string DisplayName => Action switch
|
||||||
|
{
|
||||||
|
KeyboardShortcutAction.NewMail => Translator.MenuNewMail,
|
||||||
|
KeyboardShortcutAction.ToggleReadUnread => Translator.KeyboardShortcuts_ActionToggleReadUnread,
|
||||||
|
KeyboardShortcutAction.ToggleFlag => Translator.KeyboardShortcuts_ActionToggleFlag,
|
||||||
|
KeyboardShortcutAction.ToggleArchive => Translator.KeyboardShortcuts_ActionToggleArchive,
|
||||||
|
KeyboardShortcutAction.Delete => Translator.Buttons_Delete,
|
||||||
|
KeyboardShortcutAction.Move => Translator.MailOperation_Move,
|
||||||
|
KeyboardShortcutAction.Reply => Translator.MailOperation_Reply,
|
||||||
|
KeyboardShortcutAction.ReplyAll => Translator.MailOperation_ReplyAll,
|
||||||
|
KeyboardShortcutAction.Send => Translator.Buttons_Send,
|
||||||
|
KeyboardShortcutAction.NewEvent => Translator.CalendarEventCompose_NewEventButton,
|
||||||
|
_ => Action.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
public KeyboardShortcutActionViewModel(WinoApplicationMode mode, KeyboardShortcutAction action)
|
||||||
|
{
|
||||||
|
Mode = mode;
|
||||||
|
Action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => DisplayName;
|
||||||
|
}
|
||||||
@@ -15,9 +15,10 @@ public partial class KeyboardShortcutViewModel : ObservableObject
|
|||||||
public partial bool IsEnabled { get; set; }
|
public partial bool IsEnabled { get; set; }
|
||||||
|
|
||||||
public Guid Id { get; }
|
public Guid Id { get; }
|
||||||
|
public WinoApplicationMode Mode { get; }
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
public ModifierKeys ModifierKeys { get; }
|
public ModifierKeys ModifierKeys { get; }
|
||||||
public MailOperation MailOperation { get; }
|
public KeyboardShortcutAction Action { get; }
|
||||||
public DateTime CreatedAt { get; }
|
public DateTime CreatedAt { get; }
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
@@ -38,25 +39,30 @@ public partial class KeyboardShortcutViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MailOperationDisplayName
|
public string ModeDisplayName => Mode switch
|
||||||
|
{
|
||||||
|
WinoApplicationMode.Mail => Translator.KeyboardShortcuts_ModeMail,
|
||||||
|
WinoApplicationMode.Calendar => Translator.KeyboardShortcuts_ModeCalendar,
|
||||||
|
_ => Mode.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
public string ActionDisplayName
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return MailOperation switch
|
return Action switch
|
||||||
{
|
{
|
||||||
MailOperation.Archive => Translator.MailOperation_Archive,
|
KeyboardShortcutAction.NewMail => Translator.MenuNewMail,
|
||||||
MailOperation.UnArchive => Translator.MailOperation_Unarchive,
|
KeyboardShortcutAction.ToggleReadUnread => Translator.KeyboardShortcuts_ActionToggleReadUnread,
|
||||||
MailOperation.SoftDelete => Translator.MailOperation_Delete,
|
KeyboardShortcutAction.ToggleFlag => Translator.KeyboardShortcuts_ActionToggleFlag,
|
||||||
MailOperation.Move => Translator.MailOperation_Move,
|
KeyboardShortcutAction.ToggleArchive => Translator.KeyboardShortcuts_ActionToggleArchive,
|
||||||
MailOperation.MoveToJunk => Translator.MailOperation_MoveJunk,
|
KeyboardShortcutAction.Delete => Translator.Buttons_Delete,
|
||||||
MailOperation.SetFlag => Translator.MailOperation_SetFlag,
|
KeyboardShortcutAction.Move => Translator.MailOperation_Move,
|
||||||
MailOperation.ClearFlag => Translator.MailOperation_ClearFlag,
|
KeyboardShortcutAction.Reply => Translator.MailOperation_Reply,
|
||||||
MailOperation.MarkAsRead => Translator.MailOperation_MarkAsRead,
|
KeyboardShortcutAction.ReplyAll => Translator.MailOperation_ReplyAll,
|
||||||
MailOperation.MarkAsUnread => Translator.MailOperation_MarkAsUnread,
|
KeyboardShortcutAction.Send => Translator.Buttons_Send,
|
||||||
MailOperation.Reply => Translator.MailOperation_Reply,
|
KeyboardShortcutAction.NewEvent => Translator.CalendarEventCompose_NewEventButton,
|
||||||
MailOperation.ReplyAll => Translator.MailOperation_ReplyAll,
|
_ => Action.ToString()
|
||||||
MailOperation.Forward => Translator.MailOperation_Forward,
|
|
||||||
_ => MailOperation.ToString()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,9 +70,10 @@ public partial class KeyboardShortcutViewModel : ObservableObject
|
|||||||
public KeyboardShortcutViewModel(KeyboardShortcut shortcut)
|
public KeyboardShortcutViewModel(KeyboardShortcut shortcut)
|
||||||
{
|
{
|
||||||
Id = shortcut.Id;
|
Id = shortcut.Id;
|
||||||
|
Mode = shortcut.Mode;
|
||||||
Key = shortcut.Key;
|
Key = shortcut.Key;
|
||||||
ModifierKeys = shortcut.ModifierKeys;
|
ModifierKeys = shortcut.ModifierKeys;
|
||||||
MailOperation = shortcut.MailOperation;
|
Action = shortcut.Action;
|
||||||
CreatedAt = shortcut.CreatedAt;
|
CreatedAt = shortcut.CreatedAt;
|
||||||
IsEnabled = shortcut.IsEnabled;
|
IsEnabled = shortcut.IsEnabled;
|
||||||
}
|
}
|
||||||
@@ -76,9 +83,10 @@ public partial class KeyboardShortcutViewModel : ObservableObject
|
|||||||
return new KeyboardShortcut
|
return new KeyboardShortcut
|
||||||
{
|
{
|
||||||
Id = Id,
|
Id = Id,
|
||||||
|
Mode = Mode,
|
||||||
Key = Key,
|
Key = Key,
|
||||||
ModifierKeys = ModifierKeys,
|
ModifierKeys = ModifierKeys,
|
||||||
MailOperation = MailOperation,
|
Action = Action,
|
||||||
CreatedAt = CreatedAt,
|
CreatedAt = CreatedAt,
|
||||||
IsEnabled = IsEnabled
|
IsEnabled = IsEnabled
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public partial class KeyboardShortcutsPageViewModel : CoreBaseViewModel
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Check if key combination is already in use
|
// Check if key combination is already in use
|
||||||
var isInUse = await _keyboardShortcutService.IsKeyCombinationInUseAsync(result.Key, result.ModifierKeys, null);
|
var isInUse = await _keyboardShortcutService.IsKeyCombinationInUseAsync(result.Mode, result.Key, result.ModifierKeys, null);
|
||||||
if (isInUse)
|
if (isInUse)
|
||||||
{
|
{
|
||||||
await _dialogService.ShowMessageAsync(Translator.KeyboardShortcuts_ShortcutInUse, Translator.GeneralTitle_Error, WinoCustomMessageDialogIcon.Error);
|
await _dialogService.ShowMessageAsync(Translator.KeyboardShortcuts_ShortcutInUse, Translator.GeneralTitle_Error, WinoCustomMessageDialogIcon.Error);
|
||||||
@@ -80,9 +80,10 @@ public partial class KeyboardShortcutsPageViewModel : CoreBaseViewModel
|
|||||||
// Create new shortcut
|
// Create new shortcut
|
||||||
var shortcut = new KeyboardShortcut
|
var shortcut = new KeyboardShortcut
|
||||||
{
|
{
|
||||||
|
Mode = result.Mode,
|
||||||
Key = result.Key,
|
Key = result.Key,
|
||||||
ModifierKeys = result.ModifierKeys,
|
ModifierKeys = result.ModifierKeys,
|
||||||
MailOperation = result.MailOperation,
|
Action = result.Action,
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ public partial class KeyboardShortcutsPageViewModel : CoreBaseViewModel
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Check if key combination is already in use (excluding current shortcut)
|
// Check if key combination is already in use (excluding current shortcut)
|
||||||
var isInUse = await _keyboardShortcutService.IsKeyCombinationInUseAsync(result.Key, result.ModifierKeys, shortcut.Id);
|
var isInUse = await _keyboardShortcutService.IsKeyCombinationInUseAsync(result.Mode, result.Key, result.ModifierKeys, shortcut.Id);
|
||||||
if (isInUse)
|
if (isInUse)
|
||||||
{
|
{
|
||||||
await _dialogService.ShowMessageAsync(Translator.KeyboardShortcuts_ShortcutInUse, Translator.GeneralTitle_Error, WinoCustomMessageDialogIcon.Error);
|
await _dialogService.ShowMessageAsync(Translator.KeyboardShortcuts_ShortcutInUse, Translator.GeneralTitle_Error, WinoCustomMessageDialogIcon.Error);
|
||||||
@@ -125,9 +126,10 @@ public partial class KeyboardShortcutsPageViewModel : CoreBaseViewModel
|
|||||||
|
|
||||||
// Update existing shortcut
|
// Update existing shortcut
|
||||||
var updatedShortcut = shortcut.ToEntity();
|
var updatedShortcut = shortcut.ToEntity();
|
||||||
|
updatedShortcut.Mode = result.Mode;
|
||||||
updatedShortcut.Key = result.Key;
|
updatedShortcut.Key = result.Key;
|
||||||
updatedShortcut.ModifierKeys = result.ModifierKeys;
|
updatedShortcut.ModifierKeys = result.ModifierKeys;
|
||||||
updatedShortcut.MailOperation = result.MailOperation;
|
updatedShortcut.Action = result.Action;
|
||||||
|
|
||||||
await _keyboardShortcutService.SaveKeyboardShortcutAsync(updatedShortcut);
|
await _keyboardShortcutService.SaveKeyboardShortcutAsync(updatedShortcut);
|
||||||
await LoadShortcutsAsync();
|
await LoadShortcutsAsync();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using Wino.Core.Domain.Exceptions;
|
|||||||
using Wino.Core.Domain.Extensions;
|
using Wino.Core.Domain.Extensions;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
using Wino.Core.Extensions;
|
using Wino.Core.Extensions;
|
||||||
using Wino.Core.Services;
|
using Wino.Core.Services;
|
||||||
@@ -38,6 +39,18 @@ public partial class ComposePageViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
public Func<Task<string>> GetHTMLBodyFunction;
|
public Func<Task<string>> GetHTMLBodyFunction;
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Mail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Action == KeyboardShortcutAction.Send)
|
||||||
|
{
|
||||||
|
await SendAsync();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When we send the message or discard it, we need to block the mime update
|
// When we send the message or discard it, we need to block the mime update
|
||||||
// Update is triggered when we leave the page.
|
// Update is triggered when we leave the page.
|
||||||
private bool isUpdatingMimeBlocked = false;
|
private bool isUpdatingMimeBlocked = false;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using Wino.Core.Domain.Enums;
|
|||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.MenuItems;
|
using Wino.Core.Domain.MenuItems;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
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;
|
||||||
@@ -935,6 +936,18 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
await _winoRequestDelegator.ExecuteAsync(draftPreparationRequest);
|
await _winoRequestDelegator.ExecuteAsync(draftPreparationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Mail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Action == KeyboardShortcutAction.NewMail)
|
||||||
|
{
|
||||||
|
await HandleCreateNewMailAsync();
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Handle by messaging.
|
// TODO: Handle by messaging.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using Wino.Core.Domain.Entities.Shared;
|
|||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
using Wino.Core.Domain.Models.Menus;
|
using Wino.Core.Domain.Models.Menus;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
@@ -72,6 +73,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly IMailDialogService _mailDialogService;
|
private readonly IMailDialogService _mailDialogService;
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
|
private readonly IMimeFileService _mimeFileService;
|
||||||
private readonly INotificationBuilder _notificationBuilder;
|
private readonly INotificationBuilder _notificationBuilder;
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly IContextMenuItemService _contextMenuItemService;
|
private readonly IContextMenuItemService _contextMenuItemService;
|
||||||
@@ -165,6 +167,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
IMailDialogService mailDialogService,
|
IMailDialogService mailDialogService,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
|
IMimeFileService mimeFileService,
|
||||||
IStatePersistanceService statePersistenceService,
|
IStatePersistanceService statePersistenceService,
|
||||||
INotificationBuilder notificationBuilder,
|
INotificationBuilder notificationBuilder,
|
||||||
IFolderService folderService,
|
IFolderService folderService,
|
||||||
@@ -179,6 +182,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_mailDialogService = mailDialogService;
|
_mailDialogService = mailDialogService;
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
|
_mimeFileService = mimeFileService;
|
||||||
_folderService = folderService;
|
_folderService = folderService;
|
||||||
_contextMenuItemService = contextMenuItemService;
|
_contextMenuItemService = contextMenuItemService;
|
||||||
_winoRequestDelegator = winoRequestDelegator;
|
_winoRequestDelegator = winoRequestDelegator;
|
||||||
@@ -610,6 +614,87 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package);
|
public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package);
|
||||||
|
|
||||||
|
public override async Task KeyboardShortcutHook(KeyboardShortcutTriggerDetails args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Mode != WinoApplicationMode.Mail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var targetItems = GetShortcutTargetItems().ToList();
|
||||||
|
|
||||||
|
switch (args.Action)
|
||||||
|
{
|
||||||
|
case KeyboardShortcutAction.ToggleReadUnread:
|
||||||
|
if (!targetItems.Any()) return;
|
||||||
|
await ExecuteMailOperationAsync(new MailOperationPreperationRequest(MailOperation.MarkAsRead, targetItems.Select(x => x.MailCopy), true));
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.ToggleFlag:
|
||||||
|
if (!targetItems.Any()) return;
|
||||||
|
await ExecuteMailOperationAsync(new MailOperationPreperationRequest(MailOperation.SetFlag, targetItems.Select(x => x.MailCopy), true));
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.ToggleArchive:
|
||||||
|
if (!targetItems.Any()) return;
|
||||||
|
await ExecuteMailOperationAsync(new MailOperationPreperationRequest(MailOperation.Archive, targetItems.Select(x => x.MailCopy), true));
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.Delete:
|
||||||
|
if (!targetItems.Any()) return;
|
||||||
|
await ExecuteMailOperationAsync(new MailOperationPreperationRequest(MailOperation.SoftDelete, targetItems.Select(x => x.MailCopy)));
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.Move:
|
||||||
|
if (!targetItems.Any()) return;
|
||||||
|
await ExecuteMailOperationAsync(new MailOperationPreperationRequest(MailOperation.Move, targetItems.Select(x => x.MailCopy)));
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.Reply:
|
||||||
|
await CreateReplyDraftAsync(DraftCreationReason.Reply);
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
case KeyboardShortcutAction.ReplyAll:
|
||||||
|
await CreateReplyDraftAsync(DraftCreationReason.ReplyAll);
|
||||||
|
args.Handled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<MailItemViewModel> GetShortcutTargetItems()
|
||||||
|
{
|
||||||
|
if (MailCollection.SelectedItemsCount > 0)
|
||||||
|
return MailCollection.SelectedItems.OfType<MailItemViewModel>();
|
||||||
|
|
||||||
|
if (_activeMailItem != null)
|
||||||
|
return [_activeMailItem];
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateReplyDraftAsync(DraftCreationReason reason)
|
||||||
|
{
|
||||||
|
var targetMail = GetShortcutTargetItems().FirstOrDefault();
|
||||||
|
if (targetMail?.MailCopy == null || targetMail.MailCopy.FileId == Guid.Empty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mimeInformation = await _mimeFileService.GetMimeMessageInformationAsync(targetMail.MailCopy.FileId, targetMail.MailCopy.AssignedAccount.Id);
|
||||||
|
if (mimeInformation?.MimeMessage == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var draftOptions = new DraftCreationOptions
|
||||||
|
{
|
||||||
|
Reason = reason,
|
||||||
|
ReferencedMessage = new ReferencedMessage
|
||||||
|
{
|
||||||
|
MimeMessage = mimeInformation.MimeMessage,
|
||||||
|
MailCopy = targetMail.MailCopy
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(targetMail.MailCopy.AssignedAccount.Id, draftOptions).ConfigureAwait(false);
|
||||||
|
var draftPreparationRequest = new DraftPreparationRequest(targetMail.MailCopy.AssignedAccount, draftMailCopy, draftBase64MimeMessage, draftOptions.Reason, targetMail.MailCopy);
|
||||||
|
await _winoRequestDelegator.ExecuteAsync(draftPreparationRequest);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<MailOperationMenuItem> GetAvailableMailActions(IEnumerable<MailItemViewModel> contextMailItems)
|
public IEnumerable<MailOperationMenuItem> GetAvailableMailActions(IEnumerable<MailItemViewModel> contextMailItems)
|
||||||
=> _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems.Select(a => a.MailCopy));
|
=> _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems.Select(a => a.MailCopy));
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,14 @@ public partial class BasePage : Page, IRecipient<LanguageChanged>
|
|||||||
/// Unregister message recipients for this page. Override to unregister specific message types.
|
/// Unregister message recipients for this page. Override to unregister specific message types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void UnregisterRecipients() { }
|
protected virtual void UnregisterRecipients() { }
|
||||||
|
|
||||||
|
public virtual CoreBaseViewModel? AssociatedViewModel => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
|
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
|
||||||
{
|
{
|
||||||
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>() ?? throw new ArgumentException($"Can't resolve '{typeof(T)}' as view model.");
|
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>() ?? throw new ArgumentException($"Can't resolve '{typeof(T)}' as view model.");
|
||||||
|
public override CoreBaseViewModel AssociatedViewModel => ViewModel;
|
||||||
|
|
||||||
protected BasePage()
|
protected BasePage()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,22 +21,40 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Mail Operation -->
|
<StackPanel Grid.Row="1" Margin="0,0,0,20">
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,0,0,8"
|
||||||
|
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="{x:Bind domain:Translator.KeyboardShortcuts_Mode}" />
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||||
|
<RadioButton
|
||||||
|
Content="{x:Bind domain:Translator.KeyboardShortcuts_ModeMail}"
|
||||||
|
GroupName="ShortcutMode"
|
||||||
|
IsChecked="{x:Bind IsMailModeSelected, Mode=TwoWay}" />
|
||||||
|
<RadioButton
|
||||||
|
Content="{x:Bind domain:Translator.KeyboardShortcuts_ModeCalendar}"
|
||||||
|
GroupName="ShortcutMode"
|
||||||
|
IsChecked="{x:Bind IsCalendarModeSelected, Mode=TwoWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
<StackPanel Grid.Row="0" Margin="0,0,0,20">
|
<StackPanel Grid.Row="0" Margin="0,0,0,20">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,0,0,4"
|
Margin="0,0,0,4"
|
||||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_MailoperationAction}" />
|
Text="{x:Bind domain:Translator.KeyboardShortcuts_Action}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="MailOperationComboBox"
|
x:Name="ActionComboBox"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
ItemsSource="{x:Bind AvailableMailOperations}"
|
ItemsSource="{x:Bind AvailableActions}"
|
||||||
SelectedItem="{x:Bind SelectedMailOperation, Mode=TwoWay}">
|
SelectedItem="{x:Bind SelectedAction, Mode=TwoWay}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
|
|
||||||
<DataTemplate x:DataType="data:MailOperationViewModel">
|
<DataTemplate x:DataType="data:KeyboardShortcutActionViewModel">
|
||||||
<TextBlock Text="{x:Bind DisplayName}" />
|
<TextBlock Text="{x:Bind DisplayName}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
@@ -44,7 +62,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Key Input -->
|
<!-- Key Input -->
|
||||||
<StackPanel Grid.Row="1" Margin="0,0,0,20">
|
<StackPanel Grid.Row="2" Margin="0,0,0,20">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,0,0,4"
|
Margin="0,0,0,4"
|
||||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||||
@@ -61,31 +79,10 @@
|
|||||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_FocusArea}" />
|
Text="{x:Bind domain:Translator.KeyboardShortcuts_FocusArea}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Modifiers -->
|
|
||||||
<StackPanel Grid.Row="2" Margin="0,0,0,20">
|
|
||||||
<TextBlock
|
|
||||||
Margin="0,0,0,8"
|
|
||||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
|
||||||
Text="{x:Bind domain:Translator.KeyboardShortcuts_Modifiers}" />
|
|
||||||
<Border
|
|
||||||
Padding="16,12"
|
|
||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
|
||||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="6">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
|
||||||
<CheckBox Content="Ctrl" IsChecked="{x:Bind IsControlPressed, Mode=TwoWay}" />
|
|
||||||
<CheckBox Content="Alt" IsChecked="{x:Bind IsAltPressed, Mode=TwoWay}" />
|
|
||||||
<CheckBox Content="Shift" IsChecked="{x:Bind IsShiftPressed, Mode=TwoWay}" />
|
|
||||||
<CheckBox Content="Win" IsChecked="{x:Bind IsWindowsPressed, Mode=TwoWay}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Error Message -->
|
<!-- Error Message -->
|
||||||
<Border
|
<Border
|
||||||
x:Name="ErrorBorder"
|
x:Name="ErrorBorder"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Padding="12,8"
|
Padding="12,8"
|
||||||
Background="{ThemeResource SystemFillColorCriticalBackgroundBrush}"
|
Background="{ThemeResource SystemFillColorCriticalBackgroundBrush}"
|
||||||
CornerRadius="4"
|
CornerRadius="4"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Microsoft.UI.Xaml.Controls;
|
|||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
using Wino.Core.Domain.Entities.Shared;
|
using Wino.Core.Domain.Entities.Shared;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Models;
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.ViewModels.Data;
|
using Wino.Core.ViewModels.Data;
|
||||||
|
|
||||||
@@ -13,35 +14,51 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
|||||||
{
|
{
|
||||||
public KeyboardShortcutDialogResult Result { get; private set; } = KeyboardShortcutDialogResult.Canceled();
|
public KeyboardShortcutDialogResult Result { get; private set; } = KeyboardShortcutDialogResult.Canceled();
|
||||||
|
|
||||||
public List<MailOperationViewModel> AvailableMailOperations { get; }
|
public List<KeyboardShortcutActionViewModel> AvailableActions { get; private set; } = [];
|
||||||
|
|
||||||
public MailOperationViewModel SelectedMailOperation { get; set; }
|
public KeyboardShortcutActionViewModel SelectedAction { get; set; } = null!;
|
||||||
public bool IsControlPressed { get; set; }
|
public WinoApplicationMode SelectedMode { get; set; } = WinoApplicationMode.Mail;
|
||||||
public bool IsAltPressed { get; set; }
|
public bool IsMailModeSelected
|
||||||
public bool IsShiftPressed { get; set; }
|
{
|
||||||
public bool IsWindowsPressed { get; set; }
|
get => SelectedMode == WinoApplicationMode.Mail;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!value || SelectedMode == WinoApplicationMode.Mail) return;
|
||||||
|
SelectedMode = WinoApplicationMode.Mail;
|
||||||
|
RefreshAvailableActions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCalendarModeSelected
|
||||||
|
{
|
||||||
|
get => SelectedMode == WinoApplicationMode.Calendar;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!value || SelectedMode == WinoApplicationMode.Calendar) return;
|
||||||
|
SelectedMode = WinoApplicationMode.Calendar;
|
||||||
|
RefreshAvailableActions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModifierKeys _modifierKeys;
|
||||||
|
private string _key = string.Empty;
|
||||||
|
|
||||||
public KeyboardShortcutDialog()
|
public KeyboardShortcutDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
AvailableMailOperations = GetAvailableMailOperations();
|
RefreshAvailableActions();
|
||||||
SelectedMailOperation = AvailableMailOperations.FirstOrDefault()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyboardShortcutDialog(KeyboardShortcut existingShortcut) : this()
|
public KeyboardShortcutDialog(KeyboardShortcut existingShortcut) : this()
|
||||||
{
|
{
|
||||||
if (existingShortcut != null)
|
if (existingShortcut != null)
|
||||||
{
|
{
|
||||||
KeyInputTextBox.Text = existingShortcut.Key;
|
SelectedMode = existingShortcut.Mode;
|
||||||
SelectedMailOperation = AvailableMailOperations.FirstOrDefault(x => x.Operation == existingShortcut.MailOperation)!;
|
_modifierKeys = existingShortcut.ModifierKeys;
|
||||||
|
_key = existingShortcut.Key;
|
||||||
var modifiers = existingShortcut.ModifierKeys;
|
RefreshAvailableActions(existingShortcut.Action);
|
||||||
IsControlPressed = modifiers.HasFlag(ModifierKeys.Control);
|
KeyInputTextBox.Text = BuildDisplayString(_key, _modifierKeys);
|
||||||
IsAltPressed = modifiers.HasFlag(ModifierKeys.Alt);
|
Title = Translator.KeyboardShortcuts_EditTitle;
|
||||||
IsShiftPressed = modifiers.HasFlag(ModifierKeys.Shift);
|
|
||||||
IsWindowsPressed = modifiers.HasFlag(ModifierKeys.Windows);
|
|
||||||
|
|
||||||
Title = "Edit Keyboard Shortcut";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,61 +68,47 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
|||||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
||||||
|
|
||||||
// Validate input
|
// Validate input
|
||||||
if (string.IsNullOrWhiteSpace(KeyInputTextBox.Text))
|
if (string.IsNullOrWhiteSpace(_key))
|
||||||
{
|
{
|
||||||
ShowError("Please enter a key for the shortcut.");
|
ShowError(Translator.KeyboardShortcuts_EnterKey);
|
||||||
args.Cancel = true;
|
args.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SelectedMailOperation == null || SelectedMailOperation.Operation == MailOperation.None)
|
if (SelectedAction == null || SelectedAction.Action == KeyboardShortcutAction.None)
|
||||||
{
|
{
|
||||||
ShowError("Please select a mail operation for the shortcut.");
|
ShowError(Translator.KeyboardShortcuts_SelectOperation);
|
||||||
args.Cancel = true;
|
args.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get modifier keys
|
Result = KeyboardShortcutDialogResult.Success(SelectedMode, _key, _modifierKeys, SelectedAction.Action);
|
||||||
var modifierKeys = GetSelectedModifierKeys();
|
|
||||||
|
|
||||||
// Create successful result
|
|
||||||
Result = KeyboardShortcutDialogResult.Success(KeyInputTextBox.Text, modifierKeys, SelectedMailOperation.Operation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void KeyInputTextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
private void KeyInputTextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
// Clear error when user starts typing
|
|
||||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
|
||||||
|
|
||||||
var key = e.Key.ToString();
|
_modifierKeys = GetCurrentModifierKeys();
|
||||||
|
var key = NormalizeKey(e.Key);
|
||||||
|
|
||||||
// Update modifier states based on current key press
|
if (!string.IsNullOrEmpty(key))
|
||||||
IsControlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
|
||||||
IsAltPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
|
||||||
IsShiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
|
||||||
IsWindowsPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
|
||||||
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down);
|
|
||||||
|
|
||||||
// Set the key (ignore modifier keys themselves)
|
|
||||||
if (key != "Control" && key != "Menu" && key != "Shift" && key != "LeftWindows" && key != "RightWindows")
|
|
||||||
{
|
{
|
||||||
KeyInputTextBox.Text = key;
|
_key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent the key from being processed further
|
KeyInputTextBox.Text = string.IsNullOrEmpty(_key)
|
||||||
// e.Handled = true;
|
? BuildDisplayString(string.Empty, _modifierKeys)
|
||||||
|
: BuildDisplayString(_key, _modifierKeys);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModifierKeys GetSelectedModifierKeys()
|
private void RefreshAvailableActions(KeyboardShortcutAction selectedAction = KeyboardShortcutAction.None)
|
||||||
{
|
{
|
||||||
var modifiers = ModifierKeys.None;
|
AvailableActions = GetAvailableActions(SelectedMode);
|
||||||
|
SelectedAction = AvailableActions.FirstOrDefault(x => x.Action == selectedAction) ?? AvailableActions.FirstOrDefault()!;
|
||||||
if (IsControlPressed) modifiers |= ModifierKeys.Control;
|
Bindings.Update();
|
||||||
if (IsAltPressed) modifiers |= ModifierKeys.Alt;
|
|
||||||
if (IsShiftPressed) modifiers |= ModifierKeys.Shift;
|
|
||||||
if (IsWindowsPressed) modifiers |= ModifierKeys.Windows;
|
|
||||||
|
|
||||||
return modifiers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowError(string message)
|
private void ShowError(string message)
|
||||||
@@ -114,32 +117,91 @@ public sealed partial class KeyboardShortcutDialog : ContentDialog
|
|||||||
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
ErrorBorder.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<MailOperationViewModel> GetAvailableMailOperations()
|
private static List<KeyboardShortcutActionViewModel> GetAvailableActions(WinoApplicationMode mode)
|
||||||
{
|
{
|
||||||
var operations = new List<MailOperationViewModel>();
|
KeyboardShortcutAction[] actions = mode switch
|
||||||
|
|
||||||
// Add commonly used mail operations that make sense for keyboard shortcuts
|
|
||||||
var validOperations = new[]
|
|
||||||
{
|
{
|
||||||
MailOperation.Archive,
|
WinoApplicationMode.Mail =>
|
||||||
MailOperation.UnArchive,
|
[
|
||||||
MailOperation.SoftDelete,
|
KeyboardShortcutAction.NewMail,
|
||||||
MailOperation.Move,
|
KeyboardShortcutAction.ToggleReadUnread,
|
||||||
MailOperation.MoveToJunk,
|
KeyboardShortcutAction.ToggleFlag,
|
||||||
MailOperation.SetFlag,
|
KeyboardShortcutAction.ToggleArchive,
|
||||||
MailOperation.ClearFlag,
|
KeyboardShortcutAction.Delete,
|
||||||
MailOperation.MarkAsRead,
|
KeyboardShortcutAction.Move,
|
||||||
MailOperation.MarkAsUnread,
|
KeyboardShortcutAction.Reply,
|
||||||
MailOperation.Reply,
|
KeyboardShortcutAction.ReplyAll,
|
||||||
MailOperation.ReplyAll,
|
KeyboardShortcutAction.Send
|
||||||
MailOperation.Forward
|
],
|
||||||
|
WinoApplicationMode.Calendar =>
|
||||||
|
[
|
||||||
|
KeyboardShortcutAction.NewEvent,
|
||||||
|
KeyboardShortcutAction.Delete
|
||||||
|
],
|
||||||
|
_ => []
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var operation in validOperations)
|
return actions
|
||||||
|
.Select(action => new KeyboardShortcutActionViewModel(mode, action))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModifierKeys GetCurrentModifierKeys()
|
||||||
|
{
|
||||||
|
var modifiers = ModifierKeys.None;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Control;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Alt;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Shift;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
||||||
|
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
{
|
{
|
||||||
operations.Add(new MailOperationViewModel(operation));
|
modifiers |= ModifierKeys.Windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
return operations.OrderBy(x => x.DisplayName).ToList();
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeKey(Windows.System.VirtualKey key)
|
||||||
|
{
|
||||||
|
return key switch
|
||||||
|
{
|
||||||
|
Windows.System.VirtualKey.Control or
|
||||||
|
Windows.System.VirtualKey.LeftControl or
|
||||||
|
Windows.System.VirtualKey.RightControl or
|
||||||
|
Windows.System.VirtualKey.Menu or
|
||||||
|
Windows.System.VirtualKey.LeftMenu or
|
||||||
|
Windows.System.VirtualKey.RightMenu or
|
||||||
|
Windows.System.VirtualKey.Shift or
|
||||||
|
Windows.System.VirtualKey.LeftShift or
|
||||||
|
Windows.System.VirtualKey.RightShift or
|
||||||
|
Windows.System.VirtualKey.LeftWindows or
|
||||||
|
Windows.System.VirtualKey.RightWindows => string.Empty,
|
||||||
|
_ => key.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildDisplayString(string key, ModifierKeys modifierKeys)
|
||||||
|
{
|
||||||
|
var parts = new List<string>();
|
||||||
|
|
||||||
|
if (modifierKeys.HasFlag(ModifierKeys.Control))
|
||||||
|
parts.Add("Ctrl");
|
||||||
|
if (modifierKeys.HasFlag(ModifierKeys.Alt))
|
||||||
|
parts.Add("Alt");
|
||||||
|
if (modifierKeys.HasFlag(ModifierKeys.Shift))
|
||||||
|
parts.Add("Shift");
|
||||||
|
if (modifierKeys.HasFlag(ModifierKeys.Windows))
|
||||||
|
parts.Add("Win");
|
||||||
|
if (!string.IsNullOrEmpty(key))
|
||||||
|
parts.Add(key);
|
||||||
|
|
||||||
|
return string.Join("+", parts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Mail.ViewModels.Data;
|
||||||
|
using Wino.Messaging.Client.Navigation;
|
||||||
|
|
||||||
|
namespace Wino.Helpers;
|
||||||
|
|
||||||
|
public static class BreadcrumbNavigationHelper
|
||||||
|
{
|
||||||
|
public static bool Navigate(
|
||||||
|
Frame frame,
|
||||||
|
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||||
|
BreadcrumbNavigationRequested message,
|
||||||
|
Func<WinoPage, Type> getPageType)
|
||||||
|
{
|
||||||
|
var pageType = getPageType(message.PageType);
|
||||||
|
|
||||||
|
if (pageType == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
frame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo
|
||||||
|
{
|
||||||
|
Effect = SlideNavigationTransitionEffect.FromRight
|
||||||
|
});
|
||||||
|
|
||||||
|
SetActiveItem(pageHistory, null);
|
||||||
|
pageHistory.Add(new BreadcrumbNavigationItemViewModel(
|
||||||
|
message,
|
||||||
|
isActive: true,
|
||||||
|
stepNumber: pageHistory.Count + 1,
|
||||||
|
backStackDepth: frame.BackStack.Count + 1));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GoBack(
|
||||||
|
Frame frame,
|
||||||
|
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||||
|
NavigationTransitionEffect slideEffect)
|
||||||
|
{
|
||||||
|
if (!frame.CanGoBack || pageHistory.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pageHistory.RemoveAt(pageHistory.Count - 1);
|
||||||
|
frame.GoBack(new SlideNavigationTransitionInfo
|
||||||
|
{
|
||||||
|
Effect = slideEffect == NavigationTransitionEffect.FromLeft
|
||||||
|
? SlideNavigationTransitionEffect.FromLeft
|
||||||
|
: SlideNavigationTransitionEffect.FromRight
|
||||||
|
});
|
||||||
|
|
||||||
|
SetActiveItem(pageHistory, pageHistory.Count > 0 ? pageHistory[^1] : null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NavigateTo(
|
||||||
|
Frame frame,
|
||||||
|
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||||
|
int targetIndex)
|
||||||
|
{
|
||||||
|
if (targetIndex < 0 || targetIndex >= pageHistory.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var activeIndex = GetActiveIndex(pageHistory);
|
||||||
|
if (activeIndex <= 0 || targetIndex >= activeIndex)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var targetItem = pageHistory[targetIndex];
|
||||||
|
|
||||||
|
while (frame.BackStack.Count > targetItem.BackStackDepth)
|
||||||
|
{
|
||||||
|
frame.BackStack.RemoveAt(frame.BackStack.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame.CanGoBack)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
frame.GoBack(new SlideNavigationTransitionInfo
|
||||||
|
{
|
||||||
|
Effect = SlideNavigationTransitionEffect.FromLeft
|
||||||
|
});
|
||||||
|
|
||||||
|
while (pageHistory.Count > targetIndex + 1)
|
||||||
|
{
|
||||||
|
pageHistory.RemoveAt(pageHistory.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetActiveItem(pageHistory, targetItem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetActiveIndex(ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < pageHistory.Count; i++)
|
||||||
|
{
|
||||||
|
if (pageHistory[i].IsActive)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetActiveItem(
|
||||||
|
ObservableCollection<BreadcrumbNavigationItemViewModel> pageHistory,
|
||||||
|
BreadcrumbNavigationItemViewModel? activeItem)
|
||||||
|
{
|
||||||
|
foreach (var item in pageHistory)
|
||||||
|
{
|
||||||
|
item.IsActive = ReferenceEquals(item, activeItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using CommunityToolkit.WinUI;
|
using CommunityToolkit.WinUI;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
@@ -14,6 +15,7 @@ using Wino.Core.Domain;
|
|||||||
using Wino.Core.Domain.Entities.Mail;
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
using Wino.Core.Domain.Models.MailItem;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
@@ -43,6 +45,7 @@ public sealed partial class MailAppShell : MailAppShellAbstract,
|
|||||||
public MailAppShell() : base()
|
public MailAppShell() : base()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
PreviewKeyDown += OnPreviewKeyDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
@@ -315,6 +318,100 @@ public sealed partial class MailAppShell : MailAppShellAbstract,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var key = NormalizeKey(e.Key);
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
|
||||||
|
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Mail, key, GetCurrentModifierKeys());
|
||||||
|
|
||||||
|
if (shortcut == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var details = new KeyboardShortcutTriggerDetails
|
||||||
|
{
|
||||||
|
ShortcutId = shortcut.Id,
|
||||||
|
Mode = shortcut.Mode,
|
||||||
|
Action = shortcut.Action,
|
||||||
|
Key = shortcut.Key,
|
||||||
|
ModifierKeys = shortcut.ModifierKeys,
|
||||||
|
Sender = sender,
|
||||||
|
Origin = FocusManager.GetFocusedElement(XamlRoot)
|
||||||
|
};
|
||||||
|
|
||||||
|
await ViewModel.KeyboardShortcutHook(details);
|
||||||
|
|
||||||
|
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
|
||||||
|
{
|
||||||
|
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.Handled)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldIgnoreShortcut()
|
||||||
|
{
|
||||||
|
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
|
||||||
|
|
||||||
|
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (focusedElement is FrameworkElement frameworkElement)
|
||||||
|
{
|
||||||
|
var typeName = frameworkElement.GetType().Name;
|
||||||
|
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModifierKeys GetCurrentModifierKeys()
|
||||||
|
{
|
||||||
|
var modifiers = ModifierKeys.None;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Control;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Alt;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Shift;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
||||||
|
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
{
|
||||||
|
modifiers |= ModifierKeys.Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeKey(Windows.System.VirtualKey key)
|
||||||
|
{
|
||||||
|
return key switch
|
||||||
|
{
|
||||||
|
Windows.System.VirtualKey.Control or
|
||||||
|
Windows.System.VirtualKey.LeftControl or
|
||||||
|
Windows.System.VirtualKey.RightControl or
|
||||||
|
Windows.System.VirtualKey.Menu or
|
||||||
|
Windows.System.VirtualKey.LeftMenu or
|
||||||
|
Windows.System.VirtualKey.RightMenu or
|
||||||
|
Windows.System.VirtualKey.Shift or
|
||||||
|
Windows.System.VirtualKey.LeftShift or
|
||||||
|
Windows.System.VirtualKey.RightShift or
|
||||||
|
Windows.System.VirtualKey.LeftWindows or
|
||||||
|
Windows.System.VirtualKey.RightWindows => string.Empty,
|
||||||
|
_ => key.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected override void RegisterRecipients()
|
protected override void RegisterRecipients()
|
||||||
{
|
{
|
||||||
base.RegisterRecipients();
|
base.RegisterRecipients();
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using System;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models;
|
||||||
using Wino.Mail.Views.Abstract;
|
using Wino.Mail.Views.Abstract;
|
||||||
using Wino.Messaging.Client.Calendar;
|
using Wino.Messaging.Client.Calendar;
|
||||||
|
|
||||||
@@ -17,14 +23,13 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
|||||||
public CalendarAppShell()
|
public CalendarAppShell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
PreviewKeyDown += OnPreviewKeyDown;
|
||||||
|
|
||||||
// Window.Current.SetTitleBar(DragArea);
|
|
||||||
ManageCalendarDisplayType(ViewModel.StatePersistenceService.CalendarDisplayType);
|
ManageCalendarDisplayType(ViewModel.StatePersistenceService.CalendarDisplayType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ManageCalendarDisplayType(Core.Domain.Enums.CalendarDisplayType displayType)
|
private void ManageCalendarDisplayType(Core.Domain.Enums.CalendarDisplayType displayType)
|
||||||
{
|
{
|
||||||
// Go to different states based on the display type.
|
|
||||||
if (displayType == Core.Domain.Enums.CalendarDisplayType.Month)
|
if (displayType == Core.Domain.Enums.CalendarDisplayType.Month)
|
||||||
{
|
{
|
||||||
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
|
||||||
@@ -44,12 +49,6 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
|||||||
ManageCalendarDisplayType(message.NewDisplayType);
|
ManageCalendarDisplayType(message.NewDisplayType);
|
||||||
}
|
}
|
||||||
|
|
||||||
//private void ShellFrameContentNavigated(object sender, Microsoft.UI.Xaml.Navigation.NavigationEventArgs e)
|
|
||||||
// => RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
|
|
||||||
|
|
||||||
//private void AppBarBackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, RoutedEventArgs args)
|
|
||||||
// => ViewModel.NavigationService.GoBack();
|
|
||||||
|
|
||||||
protected override void RegisterRecipients()
|
protected override void RegisterRecipients()
|
||||||
{
|
{
|
||||||
base.RegisterRecipients();
|
base.RegisterRecipients();
|
||||||
@@ -63,4 +62,98 @@ public sealed partial class CalendarAppShell : CalendarAppShellAbstract,
|
|||||||
|
|
||||||
WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
|
WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var key = NormalizeKey(e.Key);
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
|
||||||
|
var shortcut = await shortcutService.GetShortcutForKeyAsync(WinoApplicationMode.Calendar, key, GetCurrentModifierKeys());
|
||||||
|
|
||||||
|
if (shortcut == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var details = new KeyboardShortcutTriggerDetails
|
||||||
|
{
|
||||||
|
ShortcutId = shortcut.Id,
|
||||||
|
Mode = shortcut.Mode,
|
||||||
|
Action = shortcut.Action,
|
||||||
|
Key = shortcut.Key,
|
||||||
|
ModifierKeys = shortcut.ModifierKeys,
|
||||||
|
Sender = sender,
|
||||||
|
Origin = FocusManager.GetFocusedElement(XamlRoot)
|
||||||
|
};
|
||||||
|
|
||||||
|
await ViewModel.KeyboardShortcutHook(details);
|
||||||
|
|
||||||
|
if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
|
||||||
|
{
|
||||||
|
await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.Handled)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldIgnoreShortcut()
|
||||||
|
{
|
||||||
|
var focusedElement = FocusManager.GetFocusedElement(XamlRoot);
|
||||||
|
|
||||||
|
if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (focusedElement is FrameworkElement frameworkElement)
|
||||||
|
{
|
||||||
|
var typeName = frameworkElement.GetType().Name;
|
||||||
|
if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModifierKeys GetCurrentModifierKeys()
|
||||||
|
{
|
||||||
|
var modifiers = ModifierKeys.None;
|
||||||
|
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Control;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Alt;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
modifiers |= ModifierKeys.Shift;
|
||||||
|
if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
|
||||||
|
Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
|
||||||
|
{
|
||||||
|
modifiers |= ModifierKeys.Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeKey(Windows.System.VirtualKey key)
|
||||||
|
{
|
||||||
|
return key switch
|
||||||
|
{
|
||||||
|
Windows.System.VirtualKey.Control or
|
||||||
|
Windows.System.VirtualKey.LeftControl or
|
||||||
|
Windows.System.VirtualKey.RightControl or
|
||||||
|
Windows.System.VirtualKey.Menu or
|
||||||
|
Windows.System.VirtualKey.LeftMenu or
|
||||||
|
Windows.System.VirtualKey.RightMenu or
|
||||||
|
Windows.System.VirtualKey.Shift or
|
||||||
|
Windows.System.VirtualKey.LeftShift or
|
||||||
|
Windows.System.VirtualKey.RightShift or
|
||||||
|
Windows.System.VirtualKey.LeftWindows or
|
||||||
|
Windows.System.VirtualKey.RightWindows => string.Empty,
|
||||||
|
_ => key.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,8 +470,8 @@
|
|||||||
<Border
|
<Border
|
||||||
x:Name="AttachmentsPane"
|
x:Name="AttachmentsPane"
|
||||||
Margin="0,8,0,0"
|
Margin="0,8,0,0"
|
||||||
AllowDrop="True"
|
|
||||||
Padding="16"
|
Padding="16"
|
||||||
|
AllowDrop="True"
|
||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
CornerRadius="{StaticResource ControlCornerRadius}"
|
CornerRadius="{StaticResource ControlCornerRadius}"
|
||||||
DragLeave="AttachmentsPane_DragLeave"
|
DragLeave="AttachmentsPane_DragLeave"
|
||||||
@@ -487,7 +487,10 @@
|
|||||||
Style="{StaticResource TransparentActionButtonStyle}">
|
Style="{StaticResource TransparentActionButtonStyle}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
<coreControls:WinoFontIcon FontSize="14" Icon="AttachmentNew" />
|
<coreControls:WinoFontIcon FontSize="14" Icon="AttachmentNew" />
|
||||||
<TextBlock FontSize="18" FontWeight="SemiBold" Text="+" />
|
<TextBlock
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="+" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -560,7 +563,20 @@
|
|||||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.CalendarEventCompose_Notes}" />
|
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.CalendarEventCompose_Notes}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- Notes Editor -->
|
<!-- Notes Editor -->
|
||||||
<mailControls:EditorTabbedCommandBarControl CommandTarget="{x:Bind NotesEditor}" />
|
<mailControls:EditorTabbedCommandBarControl CommandTarget="{x:Bind NotesEditor}">
|
||||||
|
<mailControls:EditorTabbedCommandBarControl.PaneCustomContent>
|
||||||
|
<toolkit:TabbedCommandBarItem
|
||||||
|
CommandAlignment="Right"
|
||||||
|
IsDynamicOverflowEnabled="True"
|
||||||
|
OverflowButtonAlignment="Left">
|
||||||
|
<AppBarButton Click="ToggleNotesEditorThemeClicked" ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(NotesEditor.IsEditorDarkMode), Mode=OneWay}">
|
||||||
|
<AppBarButton.Icon>
|
||||||
|
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(NotesEditor.IsEditorDarkMode), Mode=OneWay}" />
|
||||||
|
</AppBarButton.Icon>
|
||||||
|
</AppBarButton>
|
||||||
|
</toolkit:TabbedCommandBarItem>
|
||||||
|
</mailControls:EditorTabbedCommandBarControl.PaneCustomContent>
|
||||||
|
</mailControls:EditorTabbedCommandBarControl>
|
||||||
<mailControls:WebViewEditorControl
|
<mailControls:WebViewEditorControl
|
||||||
x:Name="NotesEditor"
|
x:Name="NotesEditor"
|
||||||
MinHeight="500"
|
MinHeight="500"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using Windows.Storage;
|
|||||||
using Wino.Core.Domain;
|
using Wino.Core.Domain;
|
||||||
using Wino.Messaging.Client.Shell;
|
using Wino.Messaging.Client.Shell;
|
||||||
using Wino.Calendar.ViewModels.Data;
|
using Wino.Calendar.ViewModels.Data;
|
||||||
|
using Wino.Mail.WinUI.Controls;
|
||||||
using Wino.Mail.WinUI.Views.Abstract;
|
using Wino.Mail.WinUI.Views.Abstract;
|
||||||
|
|
||||||
namespace Wino.Calendar.Views;
|
namespace Wino.Calendar.Views;
|
||||||
@@ -29,6 +30,15 @@ public sealed partial class CalendarEventComposePage : CalendarEventComposePageA
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WinoIconGlyph GetEditorThemeIcon(bool isDarkMode) => isDarkMode ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;
|
||||||
|
|
||||||
|
public string GetEditorThemeToolTip(bool isDarkMode) => isDarkMode ? Translator.Composer_LightTheme : Translator.Composer_DarkTheme;
|
||||||
|
|
||||||
|
private void ToggleNotesEditorThemeClicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
NotesEditor.ToggleEditorTheme();
|
||||||
|
}
|
||||||
|
|
||||||
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
protected override async void OnNavigatedTo(NavigationEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
|
|||||||
@@ -145,14 +145,13 @@
|
|||||||
Visibility="{x:Bind ViewModel.IsDraftBusy, Mode=OneWay}">
|
Visibility="{x:Bind ViewModel.IsDraftBusy, Mode=OneWay}">
|
||||||
<ProgressRing IsActive="True" />
|
<ProgressRing IsActive="True" />
|
||||||
</AppBarButton>
|
</AppBarButton>
|
||||||
<AppBarToggleButton
|
<AppBarButton
|
||||||
x:Name="EditorThemeToggleButton"
|
Click="ToggleEditorThemeClicked"
|
||||||
IsChecked="{x:Bind WebViewEditor.IsEditorDarkMode, Mode=TwoWay}"
|
ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(WebViewEditor.IsEditorDarkMode), Mode=OneWay}">
|
||||||
ToolTipService.ToolTip="Toggle editor dark mode">
|
<AppBarButton.Icon>
|
||||||
<AppBarToggleButton.Icon>
|
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(WebViewEditor.IsEditorDarkMode), Mode=OneWay}" />
|
||||||
<coreControls:WinoFontIcon Icon="DarkEditor" />
|
</AppBarButton.Icon>
|
||||||
</AppBarToggleButton.Icon>
|
</AppBarButton>
|
||||||
</AppBarToggleButton>
|
|
||||||
<AppBarButton Command="{x:Bind ViewModel.DiscardCommand}" Label="{x:Bind domain:Translator.Buttons_Discard}">
|
<AppBarButton Command="{x:Bind ViewModel.DiscardCommand}" Label="{x:Bind domain:Translator.Buttons_Discard}">
|
||||||
<AppBarButton.Icon>
|
<AppBarButton.Icon>
|
||||||
<coreControls:WinoFontIcon Icon="Delete" />
|
<coreControls:WinoFontIcon Icon="Delete" />
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using Wino.Core.Domain.Entities.Shared;
|
|||||||
using Wino.Core.Domain.Models.Reader;
|
using Wino.Core.Domain.Models.Reader;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Mail.ViewModels.Messages;
|
using Wino.Mail.ViewModels.Messages;
|
||||||
|
using Wino.Mail.WinUI.Controls;
|
||||||
using Wino.Mail.WinUI.Extensions;
|
using Wino.Mail.WinUI.Extensions;
|
||||||
using Wino.Messaging.Client.Mails;
|
using Wino.Messaging.Client.Mails;
|
||||||
using Wino.Messaging.Client.Shell;
|
using Wino.Messaging.Client.Shell;
|
||||||
@@ -43,6 +44,15 @@ public sealed partial class ComposePage : ComposePageAbstract,
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WinoIconGlyph GetEditorThemeIcon(bool isDarkMode) => isDarkMode ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;
|
||||||
|
|
||||||
|
public string GetEditorThemeToolTip(bool isDarkMode) => isDarkMode ? Translator.Composer_LightTheme : Translator.Composer_DarkTheme;
|
||||||
|
|
||||||
|
private void ToggleEditorThemeClicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
WebViewEditor.ToggleEditorTheme();
|
||||||
|
}
|
||||||
|
|
||||||
private async void GlobalFocusManagerGotFocus(object? sender, FocusManagerGotFocusEventArgs e)
|
private async void GlobalFocusManagerGotFocus(object? sender, FocusManagerGotFocusEventArgs e)
|
||||||
{
|
{
|
||||||
// In order to delegate cursor to the inner editor for WebView2.
|
// In order to delegate cursor to the inner editor for WebView2.
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
|
|
||||||
private IStatePersistanceService StatePersistenceService { get; } = WinoApplication.Current.Services.GetService<IStatePersistanceService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
private IStatePersistanceService StatePersistenceService { get; } = WinoApplication.Current.Services.GetService<IStatePersistanceService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||||
private IKeyPressService KeyPressService { get; } = WinoApplication.Current.Services.GetService<IKeyPressService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
private IKeyPressService KeyPressService { get; } = WinoApplication.Current.Services.GetService<IKeyPressService>() ?? throw new Exception($"Can't resolve {nameof(KeyPressService)}");
|
||||||
private IKeyboardShortcutService KeyboardShortcutService { get; } = WinoApplication.Current.Services.GetService<IKeyboardShortcutService>() ?? throw new Exception($"Can't resolve {nameof(IKeyboardShortcutService)}");
|
|
||||||
public MailListPage()
|
public MailListPage()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -671,19 +670,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check keyboard shortcuts from service.
|
args.Handled = false;
|
||||||
ModifierKeys modifiers = args.Modifiers.ToDomainModifierKeys();
|
|
||||||
|
|
||||||
var operation = await KeyboardShortcutService.GetMailOperationForKeyAsync(args.Key.ToString(), modifiers);
|
|
||||||
|
|
||||||
if (operation != null)
|
|
||||||
{
|
|
||||||
ViewModel.ExecuteMailOperationCommand.Execute(operation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
args.Handled = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ using System.Linq;
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using MoreLinq;
|
|
||||||
using Wino.Core.Domain;
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Helpers;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Mail.WinUI.Views.Abstract;
|
using Wino.Mail.WinUI.Views.Abstract;
|
||||||
using Wino.Messaging.Client.Navigation;
|
using Wino.Messaging.Client.Navigation;
|
||||||
@@ -42,7 +42,7 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
|||||||
AccountPagesFrame.Navigated += AccountPagesFrameNavigated;
|
AccountPagesFrame.Navigated += AccountPagesFrameNavigated;
|
||||||
|
|
||||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuManageAccounts, WinoPage.AccountManagementPage);
|
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuManageAccounts, WinoPage.AccountManagementPage);
|
||||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true, backStackDepth: AccountPagesFrame.BackStack.Count + 1));
|
||||||
|
|
||||||
var accountManagementPageType = ViewModel.NavigationService.GetPageType(WinoPage.AccountManagementPage);
|
var accountManagementPageType = ViewModel.NavigationService.GetPageType(WinoPage.AccountManagementPage);
|
||||||
|
|
||||||
@@ -69,15 +69,7 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
|||||||
|
|
||||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||||
{
|
{
|
||||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
BreadcrumbNavigationHelper.Navigate(AccountPagesFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||||
|
|
||||||
if (pageType == null) return;
|
|
||||||
|
|
||||||
AccountPagesFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight });
|
|
||||||
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
|
|
||||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
|
||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,40 +81,20 @@ public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
|||||||
|
|
||||||
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
||||||
{
|
{
|
||||||
if (AccountPagesFrame.CanGoBack)
|
if (!BreadcrumbNavigationHelper.GoBack(AccountPagesFrame, PageHistory, slideEffect))
|
||||||
{
|
return;
|
||||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
|
||||||
|
|
||||||
var winuiEffect = slideEffect switch
|
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
||||||
{
|
UpdateWindowTitle();
|
||||||
Core.Domain.Enums.NavigationTransitionEffect.FromLeft => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromLeft,
|
|
||||||
_ => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight,
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = winuiEffect });
|
|
||||||
|
|
||||||
// Set the new last item as active
|
|
||||||
if (PageHistory.Count > 0)
|
|
||||||
{
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update back button visibility after navigation
|
|
||||||
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
|
||||||
UpdateWindowTitle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||||
{
|
{
|
||||||
var clickedPageHistory = PageHistory[args.Index];
|
if (!BreadcrumbNavigationHelper.NavigateTo(AccountPagesFrame, PageHistory, args.Index))
|
||||||
|
return;
|
||||||
|
|
||||||
// Trigger GoBack repeatedly until we reach the clicked breadcrumb item
|
ViewModel.StatePersistenceService.IsManageAccountsNavigating = AccountPagesFrame.CanGoBack;
|
||||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
UpdateWindowTitle();
|
||||||
{
|
|
||||||
ViewModel.NavigationService.GoBack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(BackBreadcrumNavigationRequested message)
|
public void Receive(BackBreadcrumNavigationRequested message)
|
||||||
|
|||||||
@@ -221,6 +221,17 @@
|
|||||||
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="" />
|
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="" />
|
||||||
</controls:SettingsCard.HeaderIcon>
|
</controls:SettingsCard.HeaderIcon>
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
|
<controls:SettingsCard
|
||||||
|
Click="SettingOptionClicked"
|
||||||
|
Description="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Description}"
|
||||||
|
Header="{x:Bind domain:Translator.Settings_KeyboardShortcuts_Title}"
|
||||||
|
IsClickEnabled="True"
|
||||||
|
Tag="{x:Bind enums:WinoPage.KeyboardShortcutsPage}">
|
||||||
|
<controls:SettingsCard.HeaderIcon>
|
||||||
|
<FontIcon Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Glyph="" />
|
||||||
|
</controls:SettingsCard.HeaderIcon>
|
||||||
|
</controls:SettingsCard>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</abstract:SettingOptionsPageAbstract>
|
</abstract:SettingOptionsPageAbstract>
|
||||||
|
|||||||
@@ -52,19 +52,28 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<StackPanel Grid.Column="0">
|
<StackPanel Grid.Column="0">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,0,0,4"
|
Margin="0,0,0,4"
|
||||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
||||||
Text="{x:Bind MailOperationDisplayName}" />
|
Text="{x:Bind ActionDisplayName}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Opacity="0.8"
|
Opacity="0.8"
|
||||||
Style="{ThemeResource CaptionTextBlockStyle}"
|
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||||
Text="{x:Bind DisplayName}" />
|
Text="{x:Bind DisplayName}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Style="{ThemeResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind ModeDisplayName}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="4,0"
|
Margin="4,0"
|
||||||
@@ -73,7 +82,7 @@
|
|||||||
Content=""
|
Content=""
|
||||||
FontFamily="Segoe MDL2 Assets"
|
FontFamily="Segoe MDL2 Assets"
|
||||||
Style="{ThemeResource SubtleButtonStyle}"
|
Style="{ThemeResource SubtleButtonStyle}"
|
||||||
ToolTipService.ToolTip="Edit" />
|
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_Edit}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Grid.Column="3"
|
Grid.Column="3"
|
||||||
@@ -83,7 +92,7 @@
|
|||||||
Content=""
|
Content=""
|
||||||
FontFamily="Segoe MDL2 Assets"
|
FontFamily="Segoe MDL2 Assets"
|
||||||
Style="{ThemeResource SubtleButtonStyle}"
|
Style="{ThemeResource SubtleButtonStyle}"
|
||||||
ToolTipService.ToolTip="Delete" />
|
ToolTipService.ToolTip="{x:Bind domain:Translator.Buttons_Delete}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ using System.Linq;
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using MoreLinq;
|
|
||||||
using Wino.Core.Domain;
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Helpers;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Messaging.Client.Navigation;
|
using Wino.Messaging.Client.Navigation;
|
||||||
using Wino.Views.Abstract;
|
using Wino.Views.Abstract;
|
||||||
@@ -35,7 +35,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
||||||
|
|
||||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
||||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true, backStackDepth: SettingsFrame.BackStack.Count + 1));
|
||||||
|
|
||||||
if (e.Parameter is WinoPage parameterPage)
|
if (e.Parameter is WinoPage parameterPage)
|
||||||
{
|
{
|
||||||
@@ -99,15 +99,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
|
|
||||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||||
{
|
{
|
||||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
BreadcrumbNavigationHelper.Navigate(SettingsFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||||
|
|
||||||
if (pageType == null) return;
|
|
||||||
|
|
||||||
SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
|
||||||
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
|
|
||||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
|
||||||
UpdateWindowTitle();
|
UpdateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,40 +111,20 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
|
|||||||
|
|
||||||
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
private void GoBackFrame(Core.Domain.Enums.NavigationTransitionEffect slideEffect)
|
||||||
{
|
{
|
||||||
if (SettingsFrame.CanGoBack)
|
if (!BreadcrumbNavigationHelper.GoBack(SettingsFrame, PageHistory, slideEffect))
|
||||||
{
|
return;
|
||||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
|
||||||
|
|
||||||
var winuiEffect = slideEffect switch
|
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
||||||
{
|
UpdateWindowTitle();
|
||||||
Core.Domain.Enums.NavigationTransitionEffect.FromLeft => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromLeft,
|
|
||||||
_ => Microsoft.UI.Xaml.Media.Animation.SlideNavigationTransitionEffect.FromRight,
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = winuiEffect });
|
|
||||||
|
|
||||||
// Set the new last item as active
|
|
||||||
if (PageHistory.Count > 0)
|
|
||||||
{
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update back button visibility after navigation
|
|
||||||
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
|
||||||
UpdateWindowTitle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||||
{
|
{
|
||||||
var clickedPageHistory = PageHistory[args.Index];
|
if (!BreadcrumbNavigationHelper.NavigateTo(SettingsFrame, PageHistory, args.Index))
|
||||||
|
return;
|
||||||
|
|
||||||
// Trigger GoBack repeatedly until we reach the clicked breadcrumb item
|
ViewModel.StatePersistenceService.IsSettingsNavigating = SettingsFrame.CanGoBack;
|
||||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
UpdateWindowTitle();
|
||||||
{
|
|
||||||
ViewModel.NavigationService.GoBack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(BackBreadcrumNavigationRequested message)
|
public void Receive(BackBreadcrumNavigationRequested message)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ using System.Linq;
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
using MoreLinq;
|
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Helpers;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Mail.WinUI.Views.Abstract;
|
using Wino.Mail.WinUI.Views.Abstract;
|
||||||
using Wino.Messaging.Client.Navigation;
|
using Wino.Messaging.Client.Navigation;
|
||||||
@@ -45,16 +45,7 @@ public sealed partial class WelcomeHostPage : WelcomeHostPageAbstract,
|
|||||||
|
|
||||||
public void Receive(BreadcrumbNavigationRequested message)
|
public void Receive(BreadcrumbNavigationRequested message)
|
||||||
{
|
{
|
||||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
BreadcrumbNavigationHelper.Navigate(WizardFrame, PageHistory, message, ViewModel.NavigationService.GetPageType);
|
||||||
if (pageType == null) return;
|
|
||||||
|
|
||||||
WizardFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo
|
|
||||||
{
|
|
||||||
Effect = SlideNavigationTransitionEffect.FromRight
|
|
||||||
});
|
|
||||||
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, isActive: true, stepNumber: PageHistory.Count + 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(BackBreadcrumNavigationRequested message)
|
public void Receive(BackBreadcrumNavigationRequested message)
|
||||||
@@ -64,33 +55,11 @@ public sealed partial class WelcomeHostPage : WelcomeHostPageAbstract,
|
|||||||
|
|
||||||
private void GoBackFrame()
|
private void GoBackFrame()
|
||||||
{
|
{
|
||||||
if (!WizardFrame.CanGoBack) return;
|
BreadcrumbNavigationHelper.GoBack(WizardFrame, PageHistory, NavigationTransitionEffect.FromLeft);
|
||||||
|
|
||||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
|
||||||
WizardFrame.GoBack(new SlideNavigationTransitionInfo
|
|
||||||
{
|
|
||||||
Effect = SlideNavigationTransitionEffect.FromLeft
|
|
||||||
});
|
|
||||||
|
|
||||||
if (PageHistory.Count > 0)
|
|
||||||
{
|
|
||||||
PageHistory.ForEach(a => a.IsActive = false);
|
|
||||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||||
{
|
{
|
||||||
var clickedItem = PageHistory[args.Index];
|
BreadcrumbNavigationHelper.NavigateTo(WizardFrame, PageHistory, args.Index);
|
||||||
var currentActive = PageHistory.FirstOrDefault(a => a.IsActive);
|
|
||||||
|
|
||||||
// Only allow navigating backwards (clicking items before current)
|
|
||||||
if (currentActive == null || args.Index >= PageHistory.IndexOf(currentActive))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedItem && WizardFrame.CanGoBack)
|
|
||||||
{
|
|
||||||
GoBackFrame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using SQLite;
|
|||||||
using Wino.Core.Domain.Entities.Calendar;
|
using Wino.Core.Domain.Entities.Calendar;
|
||||||
using Wino.Core.Domain.Entities.Mail;
|
using Wino.Core.Domain.Entities.Mail;
|
||||||
using Wino.Core.Domain.Entities.Shared;
|
using Wino.Core.Domain.Entities.Shared;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Services;
|
namespace Wino.Services;
|
||||||
@@ -74,6 +75,8 @@ public class DatabaseService : IDatabaseService
|
|||||||
|
|
||||||
private async Task EnsureSchemaUpgradesAsync()
|
private async Task EnsureSchemaUpgradesAsync()
|
||||||
{
|
{
|
||||||
|
await EnsureKeyboardShortcutSchemaAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var folderColumns = await Connection.GetTableInfoAsync(nameof(MailItemFolder)).ConfigureAwait(false);
|
var folderColumns = await Connection.GetTableInfoAsync(nameof(MailItemFolder)).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!folderColumns.Any(c => c.Name == nameof(MailItemFolder.HighestKnownUid)))
|
if (!folderColumns.Any(c => c.Name == nameof(MailItemFolder.HighestKnownUid)))
|
||||||
@@ -139,6 +142,44 @@ public class DatabaseService : IDatabaseService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EnsureKeyboardShortcutSchemaAsync()
|
||||||
|
{
|
||||||
|
var keyboardShortcutColumns = await Connection.GetTableInfoAsync(nameof(KeyboardShortcut)).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!keyboardShortcutColumns.Any(c => c.Name == nameof(KeyboardShortcut.Mode)))
|
||||||
|
{
|
||||||
|
await Connection
|
||||||
|
.ExecuteAsync($"ALTER TABLE {nameof(KeyboardShortcut)} ADD COLUMN {nameof(KeyboardShortcut.Mode)} INTEGER NOT NULL DEFAULT 0")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyboardShortcutColumns.Any(c => c.Name == nameof(KeyboardShortcut.Action)))
|
||||||
|
{
|
||||||
|
await Connection
|
||||||
|
.ExecuteAsync($"ALTER TABLE {nameof(KeyboardShortcut)} ADD COLUMN {nameof(KeyboardShortcut.Action)} INTEGER NOT NULL DEFAULT 0")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Connection.ExecuteAsync($@"
|
||||||
|
UPDATE {nameof(KeyboardShortcut)}
|
||||||
|
SET {nameof(KeyboardShortcut.Action)} =
|
||||||
|
CASE
|
||||||
|
WHEN MailOperation = {(int)MailOperation.Archive} THEN {(int)KeyboardShortcutAction.ToggleArchive}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.UnArchive} THEN {(int)KeyboardShortcutAction.ToggleArchive}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.SoftDelete} THEN {(int)KeyboardShortcutAction.Delete}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.HardDelete} THEN {(int)KeyboardShortcutAction.Delete}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.Move} THEN {(int)KeyboardShortcutAction.Move}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.SetFlag} THEN {(int)KeyboardShortcutAction.ToggleFlag}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.ClearFlag} THEN {(int)KeyboardShortcutAction.ToggleFlag}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.MarkAsRead} THEN {(int)KeyboardShortcutAction.ToggleReadUnread}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.MarkAsUnread} THEN {(int)KeyboardShortcutAction.ToggleReadUnread}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.Reply} THEN {(int)KeyboardShortcutAction.Reply}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.ReplyAll} THEN {(int)KeyboardShortcutAction.ReplyAll}
|
||||||
|
WHEN MailOperation = {(int)MailOperation.Forward} THEN {(int)KeyboardShortcutAction.Reply}
|
||||||
|
ELSE {(int)KeyboardShortcutAction.None}
|
||||||
|
END").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task EnsureIndexesAsync()
|
private async Task EnsureIndexesAsync()
|
||||||
{
|
{
|
||||||
// Mail indexes
|
// Mail indexes
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using Wino.Services.Extensions;
|
|||||||
namespace Wino.Services;
|
namespace Wino.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Service for managing keyboard shortcuts for mail operations.
|
/// Service for managing keyboard shortcuts for mail and calendar actions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutService
|
public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutService
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
public async Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync()
|
public async Task<IEnumerable<KeyboardShortcut>> GetKeyboardShortcutsAsync()
|
||||||
{
|
{
|
||||||
return await Connection.QueryAsync<KeyboardShortcut>(
|
return await Connection.QueryAsync<KeyboardShortcut>(
|
||||||
"SELECT * FROM KeyboardShortcut ORDER BY MailOperation");
|
"SELECT * FROM KeyboardShortcut ORDER BY Mode, Action");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,7 +33,7 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
public async Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync()
|
public async Task<IEnumerable<KeyboardShortcut>> GetEnabledKeyboardShortcutsAsync()
|
||||||
{
|
{
|
||||||
return await Connection.QueryAsync<KeyboardShortcut>(
|
return await Connection.QueryAsync<KeyboardShortcut>(
|
||||||
"SELECT * FROM KeyboardShortcut WHERE IsEnabled = ? ORDER BY MailOperation",
|
"SELECT * FROM KeyboardShortcut WHERE IsEnabled = ? ORDER BY Mode, Action",
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,34 +65,33 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mail operation for the given key combination.
|
/// Gets the shortcut for the given key combination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<MailOperation?> GetMailOperationForKeyAsync(string key, ModifierKeys modifierKeys)
|
public async Task<KeyboardShortcut> GetShortcutForKeyAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys)
|
||||||
{
|
{
|
||||||
const string query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? AND IsEnabled = ? LIMIT 1";
|
const string query = "SELECT * FROM KeyboardShortcut WHERE Mode = ? AND Key = ? AND ModifierKeys = ? AND IsEnabled = ? LIMIT 1";
|
||||||
var shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys, 1);
|
return await Connection.FindWithQueryAsync<KeyboardShortcut>(query, (int)mode, key, (int)modifierKeys, 1);
|
||||||
return shortcut?.MailOperation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a key combination is already assigned to another shortcut.
|
/// Checks if a key combination is already assigned to another shortcut.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> IsKeyCombinationInUseAsync(string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null)
|
public async Task<bool> IsKeyCombinationInUseAsync(WinoApplicationMode mode, string key, ModifierKeys modifierKeys, Guid? excludeShortcutId = null)
|
||||||
{
|
{
|
||||||
string query;
|
string query;
|
||||||
KeyboardShortcut shortcut;
|
KeyboardShortcut shortcut;
|
||||||
|
|
||||||
if (excludeShortcutId.HasValue)
|
if (excludeShortcutId.HasValue)
|
||||||
{
|
{
|
||||||
query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? AND Id != ? LIMIT 1";
|
query = "SELECT * FROM KeyboardShortcut WHERE Mode = ? AND Key = ? AND ModifierKeys = ? AND Id != ? LIMIT 1";
|
||||||
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys, excludeShortcutId.Value);
|
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, (int)mode, key, (int)modifierKeys, excludeShortcutId.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
query = "SELECT * FROM KeyboardShortcut WHERE Key = ? AND ModifierKeys = ? LIMIT 1";
|
query = "SELECT * FROM KeyboardShortcut WHERE Mode = ? AND Key = ? AND ModifierKeys = ? LIMIT 1";
|
||||||
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, key, (int)modifierKeys);
|
shortcut = await Connection.FindWithQueryAsync<KeyboardShortcut>(query, (int)mode, key, (int)modifierKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
return shortcut != null;
|
return shortcut != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +105,7 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
foreach (var shortcut in defaultShortcuts)
|
foreach (var shortcut in defaultShortcuts)
|
||||||
{
|
{
|
||||||
// Only create if it doesn't exist already
|
// Only create if it doesn't exist already
|
||||||
var exists = await IsKeyCombinationInUseAsync(shortcut.Key, shortcut.ModifierKeys);
|
var exists = await IsKeyCombinationInUseAsync(shortcut.Mode, shortcut.Key, shortcut.ModifierKeys);
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
await SaveKeyboardShortcutAsync(shortcut);
|
await SaveKeyboardShortcutAsync(shortcut);
|
||||||
@@ -138,15 +137,17 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "Delete",
|
Key = "Delete",
|
||||||
ModifierKeys = ModifierKeys.None,
|
ModifierKeys = ModifierKeys.None,
|
||||||
MailOperation = MailOperation.SoftDelete,
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.Delete,
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
},
|
},
|
||||||
new KeyboardShortcut
|
new KeyboardShortcut
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "Delete",
|
Key = "N",
|
||||||
ModifierKeys = ModifierKeys.Shift,
|
ModifierKeys = ModifierKeys.Control,
|
||||||
MailOperation = MailOperation.HardDelete,
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.NewMail,
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
},
|
},
|
||||||
new KeyboardShortcut
|
new KeyboardShortcut
|
||||||
@@ -154,7 +155,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "A",
|
Key = "A",
|
||||||
ModifierKeys = ModifierKeys.Control,
|
ModifierKeys = ModifierKeys.Control,
|
||||||
MailOperation = MailOperation.Archive,
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.ToggleArchive,
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
},
|
},
|
||||||
new KeyboardShortcut
|
new KeyboardShortcut
|
||||||
@@ -162,15 +164,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "R",
|
Key = "R",
|
||||||
ModifierKeys = ModifierKeys.Control,
|
ModifierKeys = ModifierKeys.Control,
|
||||||
MailOperation = MailOperation.MarkAsRead,
|
Mode = WinoApplicationMode.Mail,
|
||||||
IsEnabled = true
|
Action = KeyboardShortcutAction.ToggleReadUnread,
|
||||||
},
|
|
||||||
new KeyboardShortcut
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Key = "U",
|
|
||||||
ModifierKeys = ModifierKeys.Control,
|
|
||||||
MailOperation = MailOperation.MarkAsUnread,
|
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
},
|
},
|
||||||
new KeyboardShortcut
|
new KeyboardShortcut
|
||||||
@@ -178,23 +173,8 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "F",
|
Key = "F",
|
||||||
ModifierKeys = ModifierKeys.Control,
|
ModifierKeys = ModifierKeys.Control,
|
||||||
MailOperation = MailOperation.SetFlag,
|
Mode = WinoApplicationMode.Mail,
|
||||||
IsEnabled = true
|
Action = KeyboardShortcutAction.ToggleFlag,
|
||||||
},
|
|
||||||
new KeyboardShortcut
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Key = "F",
|
|
||||||
ModifierKeys = ModifierKeys.Control | ModifierKeys.Shift,
|
|
||||||
MailOperation = MailOperation.ClearFlag,
|
|
||||||
IsEnabled = true
|
|
||||||
},
|
|
||||||
new KeyboardShortcut
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Key = "J",
|
|
||||||
ModifierKeys = ModifierKeys.Control,
|
|
||||||
MailOperation = MailOperation.MoveToJunk,
|
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
},
|
},
|
||||||
new KeyboardShortcut
|
new KeyboardShortcut
|
||||||
@@ -202,9 +182,37 @@ public class KeyboardShortcutService : BaseDatabaseService, IKeyboardShortcutSer
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
Key = "M",
|
Key = "M",
|
||||||
ModifierKeys = ModifierKeys.Control,
|
ModifierKeys = ModifierKeys.Control,
|
||||||
MailOperation = MailOperation.Move,
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.Move,
|
||||||
|
IsEnabled = true
|
||||||
|
},
|
||||||
|
new KeyboardShortcut
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Key = "R",
|
||||||
|
ModifierKeys = ModifierKeys.Control,
|
||||||
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.Reply,
|
||||||
|
IsEnabled = true
|
||||||
|
},
|
||||||
|
new KeyboardShortcut
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Key = "R",
|
||||||
|
ModifierKeys = ModifierKeys.Control | ModifierKeys.Shift,
|
||||||
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.ReplyAll,
|
||||||
|
IsEnabled = true
|
||||||
|
},
|
||||||
|
new KeyboardShortcut
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Key = "Enter",
|
||||||
|
ModifierKeys = ModifierKeys.Control,
|
||||||
|
Mode = WinoApplicationMode.Mail,
|
||||||
|
Action = KeyboardShortcutAction.Send,
|
||||||
IsEnabled = true
|
IsEnabled = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user