TF-3911 Support mail keyboard shortcut actions in email detailed view on web

This commit is contained in:
dab246
2025-07-29 02:00:32 +07:00
committed by Dat H. Pham
parent 1c7778d6cc
commit b2989521d9
36 changed files with 763 additions and 270 deletions
@@ -0,0 +1,13 @@
import 'package:flutter/services.dart';
extension LogicalKeySetHelper on Set<LogicalKeyboardKey> {
bool isKey(LogicalKeyboardKey key) => contains(key);
bool isOnly(LogicalKeyboardKey key) => length == 1 && contains(key);
bool isShift() =>
contains(LogicalKeyboardKey.shiftLeft) ||
contains(LogicalKeyboardKey.shiftRight);
bool isShiftPlus(LogicalKeyboardKey key) => isShift() && contains(key);
}
@@ -0,0 +1,27 @@
import 'package:core/utils/app_logger.dart';
import 'package:flutter/services.dart';
import 'package:tmail_ui_user/features/base/extensions/logical_key_set_helper.dart';
import 'package:tmail_ui_user/features/base/shortcut/mail/mail_view_action_shortcut_type.dart';
class AppShortcutManager {
static MailViewActionShortcutType? getMailViewActionFromEvent(KeyEvent event) {
final keysPressed = HardwareKeyboard.instance.logicalKeysPressed;
log('AppShortcutManager::getMailViewActionFromEvent: Keys pressed: $keysPressed');
if (keysPressed.isOnly(LogicalKeyboardKey.keyR)) {
return MailViewActionShortcutType.reply;
} else if (keysPressed.isShiftPlus(LogicalKeyboardKey.keyR)) {
return MailViewActionShortcutType.replyAll;
} else if (keysPressed.isOnly(LogicalKeyboardKey.keyF)) {
return MailViewActionShortcutType.forward;
} else if (keysPressed.isOnly(LogicalKeyboardKey.delete) ||
keysPressed.isOnly(LogicalKeyboardKey.backspace)) {
return MailViewActionShortcutType.delete;
} else if (keysPressed.isOnly(LogicalKeyboardKey.keyN)) {
return MailViewActionShortcutType.newMessage;
} else if (keysPressed.isOnly(LogicalKeyboardKey.keyU)) {
return MailViewActionShortcutType.markAsUnread;
} else {
return null;
}
}
}
@@ -0,0 +1,39 @@
import 'package:model/email/email_action_type.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/presentation_email_extension.dart';
enum MailViewActionShortcutType {
reply,
replyAll,
forward,
delete,
newMessage,
markAsUnread;
EmailActionType? getEmailActionType({
required PresentationEmail currentEmail,
required String ownerEmailAddress,
}) {
switch(this) {
case MailViewActionShortcutType.reply:
return EmailActionType.reply;
case MailViewActionShortcutType.replyAll:
if (currentEmail.isReplyAllEnabled(ownerEmailAddress)) {
return EmailActionType.replyAll;
} else {
return null;
}
case MailViewActionShortcutType.forward:
return EmailActionType.forward;
case MailViewActionShortcutType.delete:
return currentEmail.isDeletePermanentlyEnabled
? EmailActionType.deletePermanently
: EmailActionType.moveToTrash;
case MailViewActionShortcutType.newMessage:
return EmailActionType.compose;
case MailViewActionShortcutType.markAsUnread:
return EmailActionType.markAsUnread;
}
}
}
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
typedef OnHandleKeyEventAction = void Function(KeyEvent event);
class KeyboardHandlerWrapper extends StatelessWidget {
final Widget child;
final FocusNode focusNode;
final OnHandleKeyEventAction onKeyDownEventAction;
final OnHandleKeyEventAction? onKeyUpEventAction;
const KeyboardHandlerWrapper({
Key? key,
required this.child,
required this.focusNode,
required this.onKeyDownEventAction,
this.onKeyUpEventAction,
}) : super(key: key);
void _handleKeyEvent(KeyEvent event) {
if (event is KeyDownEvent) {
onKeyDownEventAction(event);
} else if (event is KeyUpEvent) {
onKeyUpEventAction?.call(event);
}
}
@override
Widget build(BuildContext context) {
return KeyboardListener(
focusNode: focusNode,
autofocus: true,
onKeyEvent: _handleKeyEvent,
child: child,
);
}
}