diff --git a/assets/images/ic_keyboard.svg b/assets/images/ic_keyboard.svg
new file mode 100644
index 000000000..ae335c58b
--- /dev/null
+++ b/assets/images/ic_keyboard.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_message.svg b/assets/images/ic_message.svg
new file mode 100644
index 000000000..55504f9ec
--- /dev/null
+++ b/assets/images/ic_message.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_navigation.svg b/assets/images/ic_navigation.svg
new file mode 100644
index 000000000..82b8c5908
--- /dev/null
+++ b/assets/images/ic_navigation.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/images/ic_reading.svg b/assets/images/ic_reading.svg
new file mode 100644
index 000000000..8e7e76b50
--- /dev/null
+++ b/assets/images/ic_reading.svg
@@ -0,0 +1,5 @@
+
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 9304e58e9..93557e81e 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -255,6 +255,7 @@ extension AppColor on Color {
static const iconFolder = Color(0xFF297EF2);
static const folderDivider = Color(0xFFE4E8EC);
static const gray424244 = Color(0xFF424244);
+ static const gray200 = Color(0xFFCCCCCC);
static const lightGrayF4F4F4 = Color(0xFFF4F4F4);
static const gray959DAD = Color(0xFF959DAD);
static const gray9AA7B6 = Color(0xFF9AA7B6);
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index edf946446..2a5d2cb7e 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -251,6 +251,10 @@ class ImagePaths {
String get icNoRules => _getImagePath('ic_no_rules.svg');
String get icStorage => _getImagePath('ic_storage.svg');
String get icWarning => _getImagePath('ic_warning.svg');
+ String get icKeyboard => _getImagePath('ic_keyboard.svg');
+ String get icMessage => _getImagePath('ic_message.svg');
+ String get icNavigation => _getImagePath('ic_navigation.svg');
+ String get icReading => _getImagePath('ic_reading.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/lib/features/manage_account/presentation/extensions/keyboard_shortcut_extension.dart b/lib/features/manage_account/presentation/extensions/keyboard_shortcut_extension.dart
new file mode 100644
index 000000000..c1dfff28c
--- /dev/null
+++ b/lib/features/manage_account/presentation/extensions/keyboard_shortcut_extension.dart
@@ -0,0 +1,66 @@
+import 'package:core/presentation/resources/image_paths.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart';
+import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
+
+extension ShortcutContextText on ShortcutContext {
+ String getDisplayName(AppLocalizations appLocalizations) {
+ switch (this) {
+ case ShortcutContext.mailComposer:
+ return appLocalizations.mailComposer;
+ case ShortcutContext.focusOnSearch:
+ return appLocalizations.focusOnSearch;
+ case ShortcutContext.openedModal:
+ return appLocalizations.openedModal;
+ case ShortcutContext.mailboxList:
+ return appLocalizations.mailboxList;
+ case ShortcutContext.openedMailView:
+ return appLocalizations.openedMailView;
+ case ShortcutContext.mailboxListWithSelectedEmail:
+ return appLocalizations.mailboxListWithSelectedMail;
+ }
+ }
+}
+
+extension ShortcutCategoryText on ShortcutCategory {
+ String getDisplayName(
+ AppLocalizations appLocalizations, {
+ bool isDesktop = false,
+ }) {
+ switch (this) {
+ case ShortcutCategory.navigationAndClosing:
+ return !isDesktop
+ ? appLocalizations.navigation
+ : appLocalizations.navigationAndClosing;
+ case ShortcutCategory.readingAndReplying:
+ return !isDesktop
+ ? appLocalizations.reading
+ : appLocalizations.readingAndReplying;
+ case ShortcutCategory.messageManagementAndSelection:
+ return !isDesktop
+ ? appLocalizations.message
+ : appLocalizations.messageManagementAndSelection;
+ }
+ }
+
+ String getIcon(ImagePaths imagePaths) {
+ switch (this) {
+ case ShortcutCategory.navigationAndClosing:
+ return imagePaths.icNavigation;
+ case ShortcutCategory.readingAndReplying:
+ return imagePaths.icReading;
+ case ShortcutCategory.messageManagementAndSelection:
+ return imagePaths.icMessage;
+ }
+ }
+
+ double getTabWidth() {
+ switch (this) {
+ case ShortcutCategory.navigationAndClosing:
+ return 174;
+ case ShortcutCategory.readingAndReplying:
+ return 164;
+ case ShortcutCategory.messageManagementAndSelection:
+ return 280;
+ }
+ }
+}
diff --git a/lib/features/manage_account/presentation/keyboard_shortcuts/keyboard_shortcuts_view.dart b/lib/features/manage_account/presentation/keyboard_shortcuts/keyboard_shortcuts_view.dart
new file mode 100644
index 000000000..ff9520b20
--- /dev/null
+++ b/lib/features/manage_account/presentation/keyboard_shortcuts/keyboard_shortcuts_view.dart
@@ -0,0 +1,190 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:core/presentation/resources/image_paths.dart';
+import 'package:core/presentation/utils/responsive_utils.dart';
+import 'package:core/presentation/utils/theme_utils.dart';
+import 'package:core/utils/platform_info.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_custom_tab_bar/custom_tab_bar.dart';
+import 'package:flutter_custom_tab_bar/indicator/custom_indicator.dart';
+import 'package:flutter_custom_tab_bar/indicator/linear_indicator.dart';
+import 'package:get/get.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/base/setting_detail_view_builder.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/extensions/keyboard_shortcut_extension.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_category_list_widget.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_tab_bar_widget.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcuts_manager.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_explanation_widget.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/widgets/setting_header_widget.dart';
+import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
+
+class KeyboardShortcutsView extends StatefulWidget {
+ const KeyboardShortcutsView({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _KeyboardShortcutsViewState();
+}
+
+class _KeyboardShortcutsViewState extends State {
+ static const double _desktopTabViewMaxWidth = 618;
+
+ final _responsiveUtils = Get.find();
+ final _imagePaths = Get.find();
+ final _categories = ShortcutCategory.values;
+
+ late final CustomTabBarController _tabBarController;
+ late final PageController _pageController;
+
+ @override
+ void initState() {
+ super.initState();
+ _tabBarController = CustomTabBarController();
+ _pageController = PageController();
+ }
+
+ @override
+ void dispose() {
+ _pageController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final appLocalizations = AppLocalizations.of(context);
+ final allShortcuts = KeyboardShortcutsManager.generateKeyboardShortcuts(
+ appLocalizations,
+ );
+ final isDesktop = _responsiveUtils.isDesktop(context);
+ double tabBarMaxHeight = isDesktop ? 52 : 82;
+
+ return SettingDetailViewBuilder(
+ responsiveUtils: _responsiveUtils,
+ child: Container(
+ color: SettingsUtils.getContentBackgroundColor(
+ context,
+ _responsiveUtils,
+ ),
+ decoration: SettingsUtils.getBoxDecorationForContent(
+ context,
+ _responsiveUtils,
+ ),
+ width: double.infinity,
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(vertical: 30, horizontal: 22)
+ : null,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (_responsiveUtils.isWebDesktop(context))
+ SettingHeaderWidget(
+ menuItem: AccountMenuItem.keyboardShortcuts,
+ textStyle: ThemeUtils.textStyleInter600().copyWith(
+ color: Colors.black.withValues(alpha: 0.9),
+ ),
+ padding: EdgeInsets.zero,
+ )
+ else
+ const SettingExplanationWidget(
+ menuItem: AccountMenuItem.keyboardShortcuts,
+ padding: EdgeInsetsDirectional.only(
+ start: 16,
+ end: 16,
+ bottom: 16,
+ ),
+ isCenter: true,
+ textAlign: TextAlign.center,
+ ),
+ Expanded(
+ child: Padding(
+ padding: SettingsUtils.getBodyPadding(
+ context,
+ _responsiveUtils,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LayoutBuilder(
+ builder: (context, constraints) {
+ final maxWidth = constraints.maxWidth;
+ final countCategories = _categories.length;
+
+ return Stack(
+ children: [
+ CustomTabBar(
+ tabBarController: _tabBarController,
+ height: tabBarMaxHeight,
+ width: isDesktop
+ ? _desktopTabViewMaxWidth
+ : maxWidth,
+ itemCount: countCategories,
+ builder: (_, index) {
+ final category = _categories[index];
+ return ShortcutTabBarWidget(
+ index: index,
+ label: category.getDisplayName(
+ appLocalizations,
+ isDesktop: isDesktop,
+ ),
+ icon: isDesktop
+ ? null
+ : category.getIcon(_imagePaths),
+ width: isDesktop
+ ? category.getTabWidth()
+ : maxWidth / countCategories,
+ height: tabBarMaxHeight,
+ );
+ },
+ indicator: LinearIndicator(
+ color: AppColor.primaryLinShare,
+ height: 1,
+ bottom: 0,
+ ),
+ pageController: _pageController,
+ ),
+ PositionedDirectional(
+ bottom: 0,
+ start: 0,
+ end: 0,
+ child: Divider(
+ color: Colors.black.withValues(alpha: 0.12),
+ ),
+ ),
+ ],
+ );
+ }
+ ),
+ Expanded(
+ child: SizedBox(
+ width: isDesktop
+ ? _desktopTabViewMaxWidth
+ : double.infinity,
+ child: PageView.builder(
+ controller: _pageController,
+ itemCount: _categories.length,
+ physics: PlatformInfo.isMobile
+ ? const PageScrollPhysics()
+ : const NeverScrollableScrollPhysics(),
+ itemBuilder: (_, index) {
+ final category = _categories[index];
+ final shortcutsByCategory = allShortcuts
+ .where((shortcut) => shortcut.category == category)
+ .toList();
+ return ShortcutCategoryList(
+ shortcutsByCategory: shortcutsByCategory,
+ );
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_category_list_widget.dart b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_category_list_widget.dart
new file mode 100644
index 000000000..61510d38c
--- /dev/null
+++ b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_category_list_widget.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_row_widget.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart';
+
+class ShortcutCategoryList extends StatelessWidget {
+ final List shortcutsByCategory;
+
+ const ShortcutCategoryList({
+ super.key,
+ required this.shortcutsByCategory,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView.builder(
+ padding: const EdgeInsets.only(top: 40),
+ itemCount: shortcutsByCategory.length,
+ itemBuilder: (_, index) {
+ final item = shortcutsByCategory[index];
+ return ShortcutRow(item: item);
+ },
+ );
+ }
+}
diff --git a/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_key_widget.dart b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_key_widget.dart
new file mode 100644
index 000000000..2e2c921fb
--- /dev/null
+++ b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_key_widget.dart
@@ -0,0 +1,30 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:flutter/material.dart';
+
+class ShortcutKeyWidget extends StatelessWidget {
+ final String label;
+
+ const ShortcutKeyWidget({super.key, required this.label});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsetsDirectional.only(end: 4),
+ padding: const EdgeInsets.all(6),
+ decoration: BoxDecoration(
+ color: AppColor.blue100,
+ borderRadius: const BorderRadius.all(Radius.circular(2)),
+ border: Border.all(
+ width: 1,
+ color: AppColor.gray200,
+ ),
+ ),
+ child: Text(
+ label,
+ style: Theme.of(context).textTheme.bodySmall?.copyWith(
+ color: Colors.black.withValues(alpha: 0.88),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_row_widget.dart b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_row_widget.dart
new file mode 100644
index 000000000..74e692b78
--- /dev/null
+++ b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_row_widget.dart
@@ -0,0 +1,47 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:core/presentation/utils/theme_utils.dart';
+import 'package:flutter/material.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/extensions/keyboard_shortcut_extension.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_key_widget.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart';
+import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
+
+class ShortcutRow extends StatelessWidget {
+ final KeyboardShortcut item;
+
+ const ShortcutRow({super.key, required this.item});
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsetsDirectional.only(
+ top: 5,
+ bottom: 21,
+ ),
+ child: Wrap(
+ spacing: 27,
+ runSpacing: 8,
+ crossAxisAlignment: WrapCrossAlignment.center,
+ children: [
+ Text(
+ item.label,
+ style: ThemeUtils.textStyleBodyBody3(
+ color: Colors.black,
+ ),
+ ),
+ Text(
+ item.context.getDisplayName(AppLocalizations.of(context)),
+ style: ThemeUtils.textStyleBodyBody3(
+ color: AppColor.gray424244.withValues(alpha: 0.64),
+ ),
+ ),
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children:
+ item.keys.map((key) => ShortcutKeyWidget(label: key)).toList(),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_tab_bar_widget.dart b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_tab_bar_widget.dart
new file mode 100644
index 000000000..d46220ea7
--- /dev/null
+++ b/lib/features/manage_account/presentation/keyboard_shortcuts/widgets/shortcut_tab_bar_widget.dart
@@ -0,0 +1,79 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:core/presentation/utils/theme_utils.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_custom_tab_bar/library.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class ShortcutTabBarWidget extends StatelessWidget {
+ final int index;
+ final String label;
+ final String? icon;
+ final double? height;
+ final double? width;
+
+ const ShortcutTabBarWidget({
+ super.key,
+ required this.index,
+ required this.label,
+ this.icon,
+ this.height,
+ this.width,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final labelWidget = Text(
+ label,
+ style: ThemeUtils.textStyleInter400.copyWith(
+ fontSize: 14,
+ height: 20 / 14,
+ letterSpacing: 0.25,
+ color: AppColor.gray424244,
+ ),
+ textAlign: TextAlign.center,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ );
+
+ return TabBarItem(
+ index: index,
+ transform: ColorsTransform(
+ highlightColor: AppColor.gray424244,
+ normalColor: AppColor.gray424244,
+ builder: (context, _) {
+ if (icon != null) {
+ return Container(
+ height: height,
+ width: width,
+ alignment: Alignment.center,
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SvgPicture.asset(
+ icon!,
+ width: 20,
+ height: 20,
+ colorFilter: AppColor.gray686E76.asFilter(),
+ fit: BoxFit.fill,
+ ),
+ const SizedBox(height: 10),
+ labelWidget,
+ ],
+ ),
+ );
+ } else {
+ return Container(
+ height: height,
+ width: width,
+ alignment: Alignment.center,
+ margin: EdgeInsets.zero,
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: labelWidget,
+ );
+ }
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart
index 5fc4864f0..5a6ca415c 100644
--- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart
+++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart
@@ -14,6 +14,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/validate_setting_capability_supported_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_view.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/keyboard_shortcuts_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
@@ -175,6 +176,8 @@ class ManageAccountDashBoardView extends GetWidget([
AccountMenuItem.profiles,
AccountMenuItem.mailboxVisibility,
- AccountMenuItem.languageAndRegion,
+ if (PlatformInfo.isWeb)
+ AccountMenuItem.keyboardShortcuts,
]);
late Worker _quotaRxWorker;
@@ -58,6 +60,8 @@ class ManageAccountMenuController extends GetxController with ContactSupportMixi
AccountMenuItem.mailboxVisibility,
if (dashBoardController.isLanguageSettingDisplayed)
AccountMenuItem.languageAndRegion,
+ if (PlatformInfo.isWeb)
+ AccountMenuItem.keyboardShortcuts,
];
listAccountMenuItem.value = newListMenuSetting;
diff --git a/lib/features/manage_account/presentation/menu/settings/settings_controller.dart b/lib/features/manage_account/presentation/menu/settings/settings_controller.dart
index e467bac82..6cf0d6922 100644
--- a/lib/features/manage_account/presentation/menu/settings/settings_controller.dart
+++ b/lib/features/manage_account/presentation/menu/settings/settings_controller.dart
@@ -3,7 +3,9 @@ import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart';
+import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/profile_setting/profile_setting_action_type.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/export_trace_log_extension.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/extensions/handle_profile_setting_action_type_click_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart';
@@ -13,7 +15,16 @@ class SettingsController extends GetxController with ContactSupportMixin {
final responsiveUtils = Get.find();
final imagePaths = Get.find();
- void selectSettings(AccountMenuItem accountMenuItem) => manageAccountDashboardController.selectSettings(accountMenuItem);
+ void selectSettings(BuildContext context, AccountMenuItem accountMenuItem) {
+ if (accountMenuItem == AccountMenuItem.signOut) {
+ manageAccountDashboardController.handleProfileSettingActionTypeClick(
+ context: context,
+ actionType: ProfileSettingActionType.signOut,
+ );
+ } else {
+ manageAccountDashboardController.selectSettings(accountMenuItem);
+ }
+ }
void backToUniversalSettings() => manageAccountDashboardController.backToUniversalSettings();
diff --git a/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart b/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart
index 2d71e7ac6..461bcd321 100644
--- a/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart
+++ b/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart
@@ -5,8 +5,6 @@ import 'package:get/get.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget.dart';
-import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/profile_setting/profile_setting_action_type.dart';
-import 'package:tmail_ui_user/features/manage_account/presentation/extensions/handle_profile_setting_action_type_click_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/settings_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.dart';
@@ -19,6 +17,20 @@ class SettingsFirstLevelView extends GetWidget {
@override
Widget build(BuildContext context) {
+ final indent = SettingsUtils.getHorizontalPadding(
+ context,
+ controller.responsiveUtils,
+ );
+
+ final appLocalizations = AppLocalizations.of(context);
+
+ final divider = Divider(
+ color: AppColor.colorDividerHorizontal,
+ height: 1,
+ indent: indent,
+ endIndent: indent,
+ );
+
return SingleChildScrollView(
key: const Key('setting_menu'),
child: Column(children: [
@@ -43,40 +55,23 @@ class SettingsFirstLevelView extends GetWidget {
titlePadding: const EdgeInsetsDirectional.only(start: 16),
);
}),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
- ),
- SettingFirstLevelTileBuilder(
+ divider,
+ _buildSettingItem(
key: const Key('setting_profiles'),
- AppLocalizations.of(context).profiles,
- AccountMenuItem.profiles.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).profilesSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.profiles)
- ),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ context: context,
+ menuItem: AccountMenuItem.profiles,
+ appLocalizations: appLocalizations,
),
+ divider,
Obx(() {
if (controller.manageAccountDashboardController.isRuleFilterCapabilitySupported) {
return Column(children: [
- SettingFirstLevelTileBuilder(
- AccountMenuItem.emailRules.getName(AppLocalizations.of(context)),
- AccountMenuItem.emailRules.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).emailRuleSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.emailRules)
- ),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.emailRules,
+ appLocalizations: appLocalizations,
),
+ divider,
]);
} else {
return const SizedBox.shrink();
@@ -85,19 +80,13 @@ class SettingsFirstLevelView extends GetWidget {
Obx(() {
if (controller.manageAccountDashboardController.isServerSettingsCapabilitySupported) {
return Column(children: [
- SettingFirstLevelTileBuilder(
+ _buildSettingItem(
key: const ValueKey('setting_preferences'),
- AccountMenuItem.preferences.getName(AppLocalizations.of(context)),
- AccountMenuItem.preferences.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).emailReadReceiptsSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.preferences)
- ),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ context: context,
+ menuItem: AccountMenuItem.preferences,
+ appLocalizations: appLocalizations,
),
+ divider,
]);
} else {
return const SizedBox.shrink();
@@ -106,18 +95,12 @@ class SettingsFirstLevelView extends GetWidget {
Obx(() {
if (controller.manageAccountDashboardController.isForwardCapabilitySupported) {
return Column(children: [
- SettingFirstLevelTileBuilder(
- AccountMenuItem.forward.getName(AppLocalizations.of(context)),
- AccountMenuItem.forward.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).forwardingSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.forward)
- ),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.forward,
+ appLocalizations: appLocalizations,
),
+ divider,
]);
} else {
return const SizedBox.shrink();
@@ -126,28 +109,21 @@ class SettingsFirstLevelView extends GetWidget {
Obx(() {
if (controller.manageAccountDashboardController.isVacationCapabilitySupported) {
return Column(children: [
- SettingFirstLevelTileBuilder(
- AccountMenuItem.vacation.getName(AppLocalizations.of(context)),
- AccountMenuItem.vacation.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).vacationSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.vacation)
- ),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.vacation,
+ appLocalizations: appLocalizations,
),
+ divider,
]);
} else {
return const SizedBox.shrink();
}
}),
- SettingFirstLevelTileBuilder(
- AccountMenuItem.mailboxVisibility.getName(AppLocalizations.of(context)),
- AccountMenuItem.mailboxVisibility.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).folderVisibilitySubtitle,
- () => controller.selectSettings(AccountMenuItem.mailboxVisibility)
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.mailboxVisibility,
+ appLocalizations: appLocalizations,
),
Obx(() {
if (!controller.manageAccountDashboardController.isLanguageSettingDisplayed) {
@@ -155,41 +131,24 @@ class SettingsFirstLevelView extends GetWidget {
}
return Column(children: [
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
- ),
- SettingFirstLevelTileBuilder(
+ divider,
+ _buildSettingItem(
key: const Key('setting_language_region'),
- AccountMenuItem.languageAndRegion.getName(AppLocalizations.of(context)),
- AccountMenuItem.languageAndRegion.getIcon(controller.imagePaths),
- () => controller.selectSettings(AccountMenuItem.languageAndRegion)
+ context: context,
+ menuItem: AccountMenuItem.languageAndRegion,
+ appLocalizations: appLocalizations,
),
]);
}),
Obx(() {
if (controller.manageAccountDashboardController.octetsQuota.value != null) {
return Column(children: [
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(
- context,
- controller.responsiveUtils,
- ),
- endIndent: SettingsUtils.getHorizontalPadding(
- context,
- controller.responsiveUtils,
- ),
- ),
- SettingFirstLevelTileBuilder(
+ divider,
+ _buildSettingItem(
key: const ValueKey('setting_storage'),
- AccountMenuItem.storage.getName(AppLocalizations.of(context)),
- AccountMenuItem.storage.getIcon(controller.imagePaths),
- subtitle: AppLocalizations.of(context).storageSettingExplanation,
- () => controller.selectSettings(AccountMenuItem.storage),
+ context: context,
+ menuItem: AccountMenuItem.keyboardShortcuts,
+ appLocalizations: appLocalizations,
),
]);
} else {
@@ -199,18 +158,12 @@ class SettingsFirstLevelView extends GetWidget {
Obx(() {
if (controller.manageAccountDashboardController.isFcmCapabilitySupported && PlatformInfo.isMobile) {
return Column(children: [
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
+ divider,
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.notification,
+ appLocalizations: appLocalizations,
),
- SettingFirstLevelTileBuilder(
- AppLocalizations.of(context).notification,
- controller.imagePaths.icNotification,
- () => controller.selectSettings(AccountMenuItem.notification),
- subtitle: AppLocalizations.of(context).allowsTwakeMailToNotifyYouWhenANewMessageArrivesOnYourPhone,
- )
]);
} else {
return const SizedBox.shrink();
@@ -232,45 +185,41 @@ class SettingsFirstLevelView extends GetWidget {
if (contactSupportCapability?.isAvailable != true) return const SizedBox.shrink();
return Column(children: [
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(
- context,
- controller.responsiveUtils,
- ),
- endIndent: SettingsUtils.getHorizontalPadding(
- context,
- controller.responsiveUtils,
- ),
- ),
- SettingFirstLevelTileBuilder(
- AccountMenuItem.contactSupport.getName(AppLocalizations.of(context)),
- AccountMenuItem.contactSupport.getIcon(controller.imagePaths),
- () => controller.onGetHelpOrReportBug(
+ divider,
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.contactSupport,
+ appLocalizations: appLocalizations,
+ onActionCallback: () => controller.onGetHelpOrReportBug(
contactSupportCapability!,
route: AppRoutes.settings,
),
),
]);
}),
- Divider(
- color: AppColor.colorDividerHorizontal,
- height: 1,
- indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
- endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
- ),
- SettingFirstLevelTileBuilder(
- AppLocalizations.of(context).sign_out,
- controller.imagePaths.icSignOut,
- () => controller
- .manageAccountDashboardController
- .handleProfileSettingActionTypeClick(
- context: context,
- actionType: ProfileSettingActionType.signOut,
- )
+ divider,
+ _buildSettingItem(
+ context: context,
+ menuItem: AccountMenuItem.signOut,
+ appLocalizations: appLocalizations,
),
]),
);
}
+
+ Widget _buildSettingItem({
+ required BuildContext context,
+ required AccountMenuItem menuItem,
+ required AppLocalizations appLocalizations,
+ Key? key,
+ VoidCallback? onActionCallback,
+ }) {
+ return SettingFirstLevelTileBuilder(
+ key: key,
+ menuItem.getName(appLocalizations),
+ menuItem.getIcon(controller.imagePaths),
+ subtitle: menuItem.getExplanation(appLocalizations),
+ onActionCallback ?? () => controller.selectSettings(context, menuItem),
+ );
+ }
}
diff --git a/lib/features/manage_account/presentation/menu/settings/settings_view.dart b/lib/features/manage_account/presentation/menu/settings/settings_view.dart
index 2ffddefed..78845f1c2 100644
--- a/lib/features/manage_account/presentation/menu/settings/settings_view.dart
+++ b/lib/features/manage_account/presentation/menu/settings/settings_view.dart
@@ -5,6 +5,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/validate_setting_capability_supported_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/identities/identities_view.dart';
+import 'package:tmail_ui_user/features/manage_account/presentation/keyboard_shortcuts/keyboard_shortcuts_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/setting_app_bar.dart';
@@ -145,6 +146,8 @@ class SettingsView extends GetWidget {
} else {
return const SizedBox.shrink();
}
+ case AccountMenuItem.keyboardShortcuts:
+ return const KeyboardShortcutsView();
default:
return const SizedBox.shrink();
}
diff --git a/lib/features/manage_account/presentation/menu/settings_utils.dart b/lib/features/manage_account/presentation/menu/settings_utils.dart
index 237ca2e7a..17b43795e 100644
--- a/lib/features/manage_account/presentation/menu/settings_utils.dart
+++ b/lib/features/manage_account/presentation/menu/settings_utils.dart
@@ -149,4 +149,17 @@ class SettingsUtils {
return const EdgeInsetsDirectional.symmetric(vertical: 8);
}
}
+
+ static EdgeInsetsGeometry getBodyPadding(
+ BuildContext context,
+ ResponsiveUtils responsiveUtils,
+ ) {
+ if (responsiveUtils.isMobile(context)) {
+ return const EdgeInsetsDirectional.only(top: 16, start: 24, end: 24);
+ } else if (responsiveUtils.isDesktop(context)) {
+ return const EdgeInsetsDirectional.only(top: 36);
+ } else {
+ return const EdgeInsetsDirectional.only(top: 16, start: 44, end: 44);
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.dart b/lib/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.dart
index 5beeb5792..4263cf73d 100644
--- a/lib/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.dart
+++ b/lib/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.dart
@@ -20,7 +20,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
this.clickAction,
{
Key? key,
- this.subtitle
+ this.subtitle = '',
}
) : super(key: key);
@@ -29,7 +29,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
final String settingIcon;
final String title;
- final String? subtitle;
+ final String subtitle;
final SettingFirstLevelTileClickAction clickAction;
@override
@@ -70,7 +70,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
))
],
),
- subtitle != null
+ subtitle.isNotEmpty
? Padding(
padding: EdgeInsets.only(
left: AppUtils.isDirectionRTL(context) ? 12 : _getSubtitleLeftPadding(context),
@@ -78,7 +78,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
top: 12
),
child: Text(
- subtitle!,
+ subtitle,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 13,
color: AppColor.colorContentEmail,
diff --git a/lib/features/manage_account/presentation/model/account_menu_item.dart b/lib/features/manage_account/presentation/model/account_menu_item.dart
index 9ef9bd47f..5a22f7cd1 100644
--- a/lib/features/manage_account/presentation/model/account_menu_item.dart
+++ b/lib/features/manage_account/presentation/model/account_menu_item.dart
@@ -13,6 +13,7 @@ enum AccountMenuItem {
notification,
contactSupport,
storage,
+ keyboardShortcuts,
signOut,
none;
@@ -38,6 +39,8 @@ enum AccountMenuItem {
return imagePaths.icHelp;
case AccountMenuItem.storage:
return imagePaths.icStorage;
+ case AccountMenuItem.keyboardShortcuts:
+ return imagePaths.icKeyboard;
case AccountMenuItem.signOut:
return imagePaths.icSignOut;
case AccountMenuItem.none:
@@ -67,6 +70,8 @@ enum AccountMenuItem {
return appLocalizations.contactSupport;
case AccountMenuItem.storage:
return appLocalizations.storageQuotas;
+ case AccountMenuItem.keyboardShortcuts:
+ return appLocalizations.keyboardShortcuts;
case AccountMenuItem.signOut:
return appLocalizations.sign_out;
case AccountMenuItem.none:
@@ -90,6 +95,10 @@ enum AccountMenuItem {
return appLocalizations.folderVisibilitySubtitle;
case AccountMenuItem.storage:
return appLocalizations.storageSettingExplanation;
+ case AccountMenuItem.notification:
+ return appLocalizations.allowsTwakeMailToNotifyYouWhenANewMessageArrivesOnYourPhone;
+ case AccountMenuItem.keyboardShortcuts:
+ return appLocalizations.keyboardShortcutsSettingExplanation;
default:
return '';
}
@@ -117,6 +126,8 @@ enum AccountMenuItem {
return 'contact-support';
case AccountMenuItem.storage:
return 'storage';
+ case AccountMenuItem.keyboardShortcuts:
+ return 'keyboard-shortcuts';
case AccountMenuItem.signOut:
return 'sign-out';
case AccountMenuItem.none:
diff --git a/lib/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart b/lib/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart
new file mode 100644
index 000000000..8c3c073ba
--- /dev/null
+++ b/lib/features/manage_account/presentation/model/keyboard_shortcuts/keyboard_shortcut.dart
@@ -0,0 +1,38 @@
+import 'package:equatable/equatable.dart';
+
+enum ShortcutCategory {
+ navigationAndClosing,
+ readingAndReplying,
+ messageManagementAndSelection,
+}
+
+enum ShortcutContext {
+ mailComposer,
+ focusOnSearch,
+ openedModal,
+ mailboxList,
+ mailboxListWithSelectedEmail,
+ openedMailView,
+}
+
+class KeyboardShortcut with EquatableMixin {
+ final String label;
+ final ShortcutCategory category;
+ final ShortcutContext context;
+ final List keys;
+
+ const KeyboardShortcut({
+ required this.label,
+ required this.category,
+ required this.context,
+ required this.keys,
+ });
+
+ @override
+ List