TF-3911 Add Keyboard shortcuts dictionary in Setting

This commit is contained in:
dab246
2025-10-02 13:25:29 +07:00
committed by Dat H. Pham
parent 846c9c6583
commit 0bd60ea83d
26 changed files with 991 additions and 147 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.9226 2.85742H13.0774C14.9878 2.85742 15.6805 3.05633 16.3789 3.42985C17.0774 3.80336 17.6255 4.35148 17.999 5.0499C18.3725 5.74831 18.5714 6.44106 18.5714 8.35146V11.6491C18.5714 13.5595 18.3725 14.2522 17.999 14.9507C17.6255 15.6491 17.0774 16.1972 16.3789 16.5707C15.6805 16.9442 14.9878 17.1431 13.0774 17.1431H6.9226C5.0122 17.1431 4.31944 16.9442 3.62103 16.5707C2.92262 16.1972 2.3745 15.6491 2.00099 14.9507C1.62747 14.2522 1.42856 13.5595 1.42856 11.6491V8.35146C1.42856 6.44106 1.62747 5.74831 2.00099 5.0499C2.3745 4.35148 2.92262 3.80336 3.62103 3.42985C4.31944 3.05633 5.0122 2.85742 6.9226 2.85742ZM6.9226 4.28599C5.39651 4.28599 4.85322 4.39091 4.29474 4.68958C3.84529 4.92995 3.50109 5.27415 3.26072 5.72361C2.96204 6.28208 2.85713 6.82538 2.85713 8.35146V11.6491C2.85713 13.1752 2.96204 13.7185 3.26072 14.2769C3.50109 14.7264 3.84529 15.0706 4.29474 15.311C4.85322 15.6096 5.39651 15.7146 6.9226 15.7146H13.0774C14.6035 15.7146 15.1468 15.6096 15.7052 15.311C16.1547 15.0706 16.4989 14.7264 16.7393 14.2769C17.0379 13.7185 17.1428 13.1752 17.1428 11.6491V8.35146C17.1428 6.82538 17.0379 6.28208 16.7393 5.72361C16.4989 5.27415 16.1547 4.92995 15.7052 4.68958C15.1468 4.39091 14.6035 4.28599 13.0774 4.28599H6.9226ZM5.71427 8.21456C5.12254 8.21456 4.64284 7.73487 4.64284 7.14314C4.64284 6.5514 5.12254 6.07171 5.71427 6.07171C6.30601 6.07171 6.7857 6.5514 6.7857 7.14314C6.7857 7.73487 6.30601 8.21456 5.71427 8.21456ZM8.57141 8.21456C7.97968 8.21456 7.49999 7.73487 7.49999 7.14314C7.49999 6.5514 7.97968 6.07171 8.57141 6.07171C9.16315 6.07171 9.64284 6.5514 9.64284 7.14314C9.64284 7.73487 9.16315 8.21456 8.57141 8.21456ZM11.4286 8.21456C10.8368 8.21456 10.3571 7.73487 10.3571 7.14314C10.3571 6.5514 10.8368 6.07171 11.4286 6.07171C12.0203 6.07171 12.5 6.5514 12.5 7.14314C12.5 7.73487 12.0203 8.21456 11.4286 8.21456ZM14.2857 8.21456C13.694 8.21456 13.2143 7.73487 13.2143 7.14314C13.2143 6.5514 13.694 6.07171 14.2857 6.07171C14.8774 6.07171 15.3571 6.5514 15.3571 7.14314C15.3571 7.73487 14.8774 8.21456 14.2857 8.21456ZM14.2857 11.4289C13.694 11.4289 13.2143 10.9492 13.2143 10.3574C13.2143 9.76569 13.694 9.28599 14.2857 9.28599C14.8774 9.28599 15.3571 9.76569 15.3571 10.3574C15.3571 10.9492 14.8774 11.4289 14.2857 11.4289ZM11.4286 11.4289C10.8368 11.4289 10.3571 10.9492 10.3571 10.3574C10.3571 9.76569 10.8368 9.28599 11.4286 9.28599C12.0203 9.28599 12.5 9.76569 12.5 10.3574C12.5 10.9492 12.0203 11.4289 11.4286 11.4289ZM8.57141 11.4289C7.97968 11.4289 7.49999 10.9492 7.49999 10.3574C7.49999 9.76569 7.97968 9.28599 8.57141 9.28599C9.16315 9.28599 9.64284 9.76569 9.64284 10.3574C9.64284 10.9492 9.16315 11.4289 8.57141 11.4289ZM5.71427 11.4289C5.12254 11.4289 4.64284 10.9492 4.64284 10.3574C4.64284 9.76569 5.12254 9.28599 5.71427 9.28599C6.30601 9.28599 6.7857 9.76569 6.7857 10.3574C6.7857 10.9492 6.30601 11.4289 5.71427 11.4289ZM6.42856 12.8574H13.5714C13.9659 12.8574 14.2857 13.1772 14.2857 13.5717C14.2857 13.9662 13.9659 14.286 13.5714 14.286H6.42856C6.03407 14.286 5.71427 13.9662 5.71427 13.5717C5.71427 13.1772 6.03407 12.8574 6.42856 12.8574Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M3.73156 15.1869C4.09231 15.5476 4.62474 15.728 5.32886 15.728H15.6775C16.861 15.728 17.8204 14.7686 17.8204 13.5852V6.39848C17.8204 5.7074 17.64 5.18366 17.2793 4.82726C16.9185 4.46651 16.3861 4.28613 15.682 4.28613H5.16587C4.53999 4.28613 4.05319 4.46651 3.70548 4.82726C3.36211 5.18366 3.19043 5.7074 3.19043 6.39848V13.6092C3.19043 14.3046 3.37081 14.8305 3.73156 15.1869ZM12.2891 10.7625L11.9267 11.1187C11.692 11.349 11.4594 11.5185 11.2291 11.6272C10.9987 11.7315 10.7618 11.7837 10.5185 11.7837C10.2751 11.7837 10.036 11.7315 9.8013 11.6272C9.56659 11.5185 9.33406 11.349 9.1037 11.1187L8.7413 10.7625L4.99236 14.4763C5.09259 14.5024 5.20476 14.5154 5.32886 14.5154H15.8482L12.2891 10.7625ZM16.6208 6.5062L13.0816 9.98381L16.6208 13.718V6.5062ZM7.94881 9.98376L4.39003 13.5091V6.48657L7.94881 9.98376ZM5.32886 5.49878C5.2121 5.49878 5.1059 5.51062 5.01027 5.5343L9.7687 10.2385C9.90344 10.3689 10.0295 10.4667 10.1468 10.5319C10.2685 10.5928 10.3924 10.6232 10.5185 10.6232C10.6358 10.6232 10.7532 10.5928 10.8705 10.5319C10.9922 10.4711 11.1226 10.3733 11.2617 10.2385L16.0201 5.54052C15.9195 5.51269 15.8067 5.49878 15.682 5.49878H5.32886Z"
fill="#686E76" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9.4707 11.4857L4.41076 10.9586C3.72968 10.8877 3.14916 10.4335 2.91639 9.78952C2.58115 8.86202 3.06127 7.83837 3.98877 7.50314L14.5138 3.69889C14.9845 3.52876 15.5 3.52876 15.9707 3.69889C17.0837 4.10118 17.6598 5.32956 17.2575 6.44255L13.4533 16.9676C13.2205 17.6116 12.64 18.0658 11.9589 18.1367C10.978 18.2389 10.1 17.5266 9.99778 16.5456L9.4707 11.4857ZM4.41595 8.87387C4.26955 8.95895 4.2003 9.13903 4.25989 9.30392C4.30645 9.43271 4.42255 9.52355 4.55877 9.53774L10.1951 10.1249C10.531 10.1598 10.7965 10.4254 10.8315 10.7613L11.4187 16.3976C11.4391 16.5938 11.6147 16.7363 11.8109 16.7158C11.9471 16.7017 12.0632 16.6108 12.1098 16.482L15.914 5.95695C16.0481 5.58595 15.8561 5.17649 15.4851 5.04239C15.3282 4.98568 15.1564 4.98568 14.9995 5.04239L4.47437 8.84664L4.41595 8.87387Z"
fill="#686E76" />
</svg>

After

Width:  |  Height:  |  Size: 939 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.5645 2.14258C14.1238 2.14258 14.8736 2.28737 15.6527 2.70402C16.3592 3.08185 16.9179 3.64058 17.2957 4.34707C17.7124 5.12613 17.8572 5.87594 17.8572 7.43523V12.5642C17.8572 14.1235 17.7124 14.8733 17.2957 15.6524C16.9179 16.3589 16.3592 16.9176 15.6527 17.2954C14.8736 17.7121 14.1238 17.8569 12.5645 17.8569H7.43554C5.87625 17.8569 5.12644 17.7121 4.34737 17.2954C3.64089 16.9176 3.08216 16.3589 2.70433 15.6524C2.28768 14.8733 2.14288 14.1235 2.14288 12.5642V7.43523C2.14288 5.87594 2.28768 5.12613 2.70433 4.34707C3.08216 3.64058 3.64089 3.08185 4.34737 2.70402C5.12644 2.28737 5.87625 2.14258 7.43554 2.14258H12.5645ZM12.5645 3.57115H7.43554C6.09191 3.57115 5.5649 3.67292 5.02109 3.96375C4.56355 4.20844 4.20875 4.56325 3.96406 5.02078C3.67323 5.56459 3.57145 6.0916 3.57145 7.43523V12.5642C3.57145 13.9078 3.67323 14.4349 3.96406 14.9787C4.20875 15.4362 4.56355 15.791 5.02109 16.0357C5.5649 16.3265 6.09191 16.4283 7.43554 16.4283H12.5645C13.9081 16.4283 14.4352 16.3265 14.979 16.0357C15.4365 15.791 15.7913 15.4362 16.036 14.9787C16.3268 14.4349 16.4286 13.9078 16.4286 12.5642V7.43523C16.4286 6.0916 16.3268 5.56459 16.036 5.02078C15.7913 4.56325 15.4365 4.20844 14.979 3.96375C14.4352 3.67292 13.9081 3.57115 12.5645 3.57115ZM9.28574 12.4997C9.68023 12.4997 10 12.8195 10 13.214C10 13.6085 9.68023 13.9283 9.28574 13.9283H6.4286C6.03411 13.9283 5.71431 13.6085 5.71431 13.214C5.71431 12.8195 6.03411 12.4997 6.4286 12.4997H9.28574ZM13.5715 9.28544C13.9659 9.28544 14.2857 9.60523 14.2857 9.99972C14.2857 10.3942 13.9659 10.714 13.5715 10.714H6.4286C6.03411 10.714 5.71431 10.3942 5.71431 9.99972C5.71431 9.60523 6.03411 9.28544 6.4286 9.28544H13.5715ZM13.5715 6.07115C13.9659 6.07115 14.2857 6.39095 14.2857 6.78544C14.2857 7.17992 13.9659 7.49972 13.5715 7.49972H6.4286C6.03411 7.49972 5.71431 7.17992 5.71431 6.78544C5.71431 6.39095 6.03411 6.07115 6.4286 6.07115H13.5715Z"
fill="#686E76" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -255,6 +255,7 @@ extension AppColor on Color {
static const iconFolder = Color(0xFF297EF2); static const iconFolder = Color(0xFF297EF2);
static const folderDivider = Color(0xFFE4E8EC); static const folderDivider = Color(0xFFE4E8EC);
static const gray424244 = Color(0xFF424244); static const gray424244 = Color(0xFF424244);
static const gray200 = Color(0xFFCCCCCC);
static const lightGrayF4F4F4 = Color(0xFFF4F4F4); static const lightGrayF4F4F4 = Color(0xFFF4F4F4);
static const gray959DAD = Color(0xFF959DAD); static const gray959DAD = Color(0xFF959DAD);
static const gray9AA7B6 = Color(0xFF9AA7B6); static const gray9AA7B6 = Color(0xFF9AA7B6);
@@ -251,6 +251,10 @@ class ImagePaths {
String get icNoRules => _getImagePath('ic_no_rules.svg'); String get icNoRules => _getImagePath('ic_no_rules.svg');
String get icStorage => _getImagePath('ic_storage.svg'); String get icStorage => _getImagePath('ic_storage.svg');
String get icWarning => _getImagePath('ic_warning.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) { String _getImagePath(String imageName) {
return AssetsPaths.images + imageName; return AssetsPaths.images + imageName;
@@ -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;
}
}
}
@@ -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<KeyboardShortcutsView> createState() => _KeyboardShortcutsViewState();
}
class _KeyboardShortcutsViewState extends State<KeyboardShortcutsView> {
static const double _desktopTabViewMaxWidth = 618;
final _responsiveUtils = Get.find<ResponsiveUtils>();
final _imagePaths = Get.find<ImagePaths>();
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,
);
},
),
),
),
],
),
),
),
],
),
),
);
}
}
@@ -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<KeyboardShortcut> 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);
},
);
}
}
@@ -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),
),
),
);
}
}
@@ -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(),
),
],
),
);
}
}
@@ -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,
);
}
},
),
);
}
}
@@ -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/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/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/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/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/mailbox_visibility/mailbox_visibility_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
@@ -175,6 +176,8 @@ class ManageAccountDashBoardView extends GetWidget<ManageAccountDashBoardControl
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
case AccountMenuItem.keyboardShortcuts:
return const KeyboardShortcutsView();
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -1,5 +1,6 @@
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart';
@@ -16,7 +17,8 @@ class ManageAccountMenuController extends GetxController with ContactSupportMixi
final listAccountMenuItem = RxList<AccountMenuItem>([ final listAccountMenuItem = RxList<AccountMenuItem>([
AccountMenuItem.profiles, AccountMenuItem.profiles,
AccountMenuItem.mailboxVisibility, AccountMenuItem.mailboxVisibility,
AccountMenuItem.languageAndRegion, if (PlatformInfo.isWeb)
AccountMenuItem.keyboardShortcuts,
]); ]);
late Worker _quotaRxWorker; late Worker _quotaRxWorker;
@@ -58,6 +60,8 @@ class ManageAccountMenuController extends GetxController with ContactSupportMixi
AccountMenuItem.mailboxVisibility, AccountMenuItem.mailboxVisibility,
if (dashBoardController.isLanguageSettingDisplayed) if (dashBoardController.isLanguageSettingDisplayed)
AccountMenuItem.languageAndRegion, AccountMenuItem.languageAndRegion,
if (PlatformInfo.isWeb)
AccountMenuItem.keyboardShortcuts,
]; ];
listAccountMenuItem.value = newListMenuSetting; listAccountMenuItem.value = newListMenuSetting;
@@ -3,7 +3,9 @@ import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.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/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/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/account_menu_item.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.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<ResponsiveUtils>(); final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>(); final imagePaths = Get.find<ImagePaths>();
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(); void backToUniversalSettings() => manageAccountDashboardController.backToUniversalSettings();
@@ -5,8 +5,6 @@ import 'package:get/get.dart';
import 'package:model/extensions/session_extension.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/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/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/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/settings_utils.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/widgets/setting_first_level_tile_builder.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<SettingsController> {
@override @override
Widget build(BuildContext context) { 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( return SingleChildScrollView(
key: const Key('setting_menu'), key: const Key('setting_menu'),
child: Column(children: [ child: Column(children: [
@@ -43,40 +55,23 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
titlePadding: const EdgeInsetsDirectional.only(start: 16), titlePadding: const EdgeInsetsDirectional.only(start: 16),
); );
}), }),
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
),
SettingFirstLevelTileBuilder(
key: const Key('setting_profiles'), key: const Key('setting_profiles'),
AppLocalizations.of(context).profiles, context: context,
AccountMenuItem.profiles.getIcon(controller.imagePaths), menuItem: AccountMenuItem.profiles,
subtitle: AppLocalizations.of(context).profilesSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.profiles)
),
Divider(
color: AppColor.colorDividerHorizontal,
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
), ),
divider,
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.isRuleFilterCapabilitySupported) { if (controller.manageAccountDashboardController.isRuleFilterCapabilitySupported) {
return Column(children: [ return Column(children: [
SettingFirstLevelTileBuilder( _buildSettingItem(
AccountMenuItem.emailRules.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.emailRules.getIcon(controller.imagePaths), menuItem: AccountMenuItem.emailRules,
subtitle: AppLocalizations.of(context).emailRuleSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.emailRules)
),
Divider(
color: AppColor.colorDividerHorizontal,
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
), ),
divider,
]); ]);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -85,19 +80,13 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.isServerSettingsCapabilitySupported) { if (controller.manageAccountDashboardController.isServerSettingsCapabilitySupported) {
return Column(children: [ return Column(children: [
SettingFirstLevelTileBuilder( _buildSettingItem(
key: const ValueKey('setting_preferences'), key: const ValueKey('setting_preferences'),
AccountMenuItem.preferences.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.preferences.getIcon(controller.imagePaths), menuItem: AccountMenuItem.preferences,
subtitle: AppLocalizations.of(context).emailReadReceiptsSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.preferences)
),
Divider(
color: AppColor.colorDividerHorizontal,
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
), ),
divider,
]); ]);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -106,18 +95,12 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.isForwardCapabilitySupported) { if (controller.manageAccountDashboardController.isForwardCapabilitySupported) {
return Column(children: [ return Column(children: [
SettingFirstLevelTileBuilder( _buildSettingItem(
AccountMenuItem.forward.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.forward.getIcon(controller.imagePaths), menuItem: AccountMenuItem.forward,
subtitle: AppLocalizations.of(context).forwardingSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.forward)
),
Divider(
color: AppColor.colorDividerHorizontal,
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
), ),
divider,
]); ]);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -126,28 +109,21 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.isVacationCapabilitySupported) { if (controller.manageAccountDashboardController.isVacationCapabilitySupported) {
return Column(children: [ return Column(children: [
SettingFirstLevelTileBuilder( _buildSettingItem(
AccountMenuItem.vacation.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.vacation.getIcon(controller.imagePaths), menuItem: AccountMenuItem.vacation,
subtitle: AppLocalizations.of(context).vacationSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.vacation)
),
Divider(
color: AppColor.colorDividerHorizontal,
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
), ),
divider,
]); ]);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
}), }),
SettingFirstLevelTileBuilder( _buildSettingItem(
AccountMenuItem.mailboxVisibility.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.mailboxVisibility.getIcon(controller.imagePaths), menuItem: AccountMenuItem.mailboxVisibility,
subtitle: AppLocalizations.of(context).folderVisibilitySubtitle, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.mailboxVisibility)
), ),
Obx(() { Obx(() {
if (!controller.manageAccountDashboardController.isLanguageSettingDisplayed) { if (!controller.manageAccountDashboardController.isLanguageSettingDisplayed) {
@@ -155,41 +131,24 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
} }
return Column(children: [ return Column(children: [
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils),
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils)
),
SettingFirstLevelTileBuilder(
key: const Key('setting_language_region'), key: const Key('setting_language_region'),
AccountMenuItem.languageAndRegion.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.languageAndRegion.getIcon(controller.imagePaths), menuItem: AccountMenuItem.languageAndRegion,
() => controller.selectSettings(AccountMenuItem.languageAndRegion) appLocalizations: appLocalizations,
), ),
]); ]);
}), }),
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.octetsQuota.value != null) { if (controller.manageAccountDashboardController.octetsQuota.value != null) {
return Column(children: [ return Column(children: [
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1,
indent: SettingsUtils.getHorizontalPadding(
context,
controller.responsiveUtils,
),
endIndent: SettingsUtils.getHorizontalPadding(
context,
controller.responsiveUtils,
),
),
SettingFirstLevelTileBuilder(
key: const ValueKey('setting_storage'), key: const ValueKey('setting_storage'),
AccountMenuItem.storage.getName(AppLocalizations.of(context)), context: context,
AccountMenuItem.storage.getIcon(controller.imagePaths), menuItem: AccountMenuItem.keyboardShortcuts,
subtitle: AppLocalizations.of(context).storageSettingExplanation, appLocalizations: appLocalizations,
() => controller.selectSettings(AccountMenuItem.storage),
), ),
]); ]);
} else { } else {
@@ -199,18 +158,12 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
Obx(() { Obx(() {
if (controller.manageAccountDashboardController.isFcmCapabilitySupported && PlatformInfo.isMobile) { if (controller.manageAccountDashboardController.isFcmCapabilitySupported && PlatformInfo.isMobile) {
return Column(children: [ return Column(children: [
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1, context: context,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils), menuItem: AccountMenuItem.notification,
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils) appLocalizations: appLocalizations,
), ),
SettingFirstLevelTileBuilder(
AppLocalizations.of(context).notification,
controller.imagePaths.icNotification,
() => controller.selectSettings(AccountMenuItem.notification),
subtitle: AppLocalizations.of(context).allowsTwakeMailToNotifyYouWhenANewMessageArrivesOnYourPhone,
)
]); ]);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -232,45 +185,41 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
if (contactSupportCapability?.isAvailable != true) return const SizedBox.shrink(); if (contactSupportCapability?.isAvailable != true) return const SizedBox.shrink();
return Column(children: [ return Column(children: [
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1, context: context,
indent: SettingsUtils.getHorizontalPadding( menuItem: AccountMenuItem.contactSupport,
context, appLocalizations: appLocalizations,
controller.responsiveUtils, onActionCallback: () => controller.onGetHelpOrReportBug(
),
endIndent: SettingsUtils.getHorizontalPadding(
context,
controller.responsiveUtils,
),
),
SettingFirstLevelTileBuilder(
AccountMenuItem.contactSupport.getName(AppLocalizations.of(context)),
AccountMenuItem.contactSupport.getIcon(controller.imagePaths),
() => controller.onGetHelpOrReportBug(
contactSupportCapability!, contactSupportCapability!,
route: AppRoutes.settings, route: AppRoutes.settings,
), ),
), ),
]); ]);
}), }),
Divider( divider,
color: AppColor.colorDividerHorizontal, _buildSettingItem(
height: 1, context: context,
indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils), menuItem: AccountMenuItem.signOut,
endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils) appLocalizations: appLocalizations,
),
SettingFirstLevelTileBuilder(
AppLocalizations.of(context).sign_out,
controller.imagePaths.icSignOut,
() => controller
.manageAccountDashboardController
.handleProfileSettingActionTypeClick(
context: context,
actionType: ProfileSettingActionType.signOut,
)
), ),
]), ]),
); );
} }
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),
);
}
} }
@@ -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/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/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/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/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/mailbox_visibility/mailbox_visibility_view.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/setting_app_bar.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings/setting_app_bar.dart';
@@ -145,6 +146,8 @@ class SettingsView extends GetWidget<SettingsController> {
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
case AccountMenuItem.keyboardShortcuts:
return const KeyboardShortcutsView();
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -149,4 +149,17 @@ class SettingsUtils {
return const EdgeInsetsDirectional.symmetric(vertical: 8); 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);
}
}
} }
@@ -20,7 +20,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
this.clickAction, this.clickAction,
{ {
Key? key, Key? key,
this.subtitle this.subtitle = '',
} }
) : super(key: key); ) : super(key: key);
@@ -29,7 +29,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
final String settingIcon; final String settingIcon;
final String title; final String title;
final String? subtitle; final String subtitle;
final SettingFirstLevelTileClickAction clickAction; final SettingFirstLevelTileClickAction clickAction;
@override @override
@@ -70,7 +70,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
)) ))
], ],
), ),
subtitle != null subtitle.isNotEmpty
? Padding( ? Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: AppUtils.isDirectionRTL(context) ? 12 : _getSubtitleLeftPadding(context), left: AppUtils.isDirectionRTL(context) ? 12 : _getSubtitleLeftPadding(context),
@@ -78,7 +78,7 @@ class SettingFirstLevelTileBuilder extends StatelessWidget {
top: 12 top: 12
), ),
child: Text( child: Text(
subtitle!, subtitle,
style: ThemeUtils.defaultTextStyleInterFont.copyWith( style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 13, fontSize: 13,
color: AppColor.colorContentEmail, color: AppColor.colorContentEmail,
@@ -13,6 +13,7 @@ enum AccountMenuItem {
notification, notification,
contactSupport, contactSupport,
storage, storage,
keyboardShortcuts,
signOut, signOut,
none; none;
@@ -38,6 +39,8 @@ enum AccountMenuItem {
return imagePaths.icHelp; return imagePaths.icHelp;
case AccountMenuItem.storage: case AccountMenuItem.storage:
return imagePaths.icStorage; return imagePaths.icStorage;
case AccountMenuItem.keyboardShortcuts:
return imagePaths.icKeyboard;
case AccountMenuItem.signOut: case AccountMenuItem.signOut:
return imagePaths.icSignOut; return imagePaths.icSignOut;
case AccountMenuItem.none: case AccountMenuItem.none:
@@ -67,6 +70,8 @@ enum AccountMenuItem {
return appLocalizations.contactSupport; return appLocalizations.contactSupport;
case AccountMenuItem.storage: case AccountMenuItem.storage:
return appLocalizations.storageQuotas; return appLocalizations.storageQuotas;
case AccountMenuItem.keyboardShortcuts:
return appLocalizations.keyboardShortcuts;
case AccountMenuItem.signOut: case AccountMenuItem.signOut:
return appLocalizations.sign_out; return appLocalizations.sign_out;
case AccountMenuItem.none: case AccountMenuItem.none:
@@ -90,6 +95,10 @@ enum AccountMenuItem {
return appLocalizations.folderVisibilitySubtitle; return appLocalizations.folderVisibilitySubtitle;
case AccountMenuItem.storage: case AccountMenuItem.storage:
return appLocalizations.storageSettingExplanation; return appLocalizations.storageSettingExplanation;
case AccountMenuItem.notification:
return appLocalizations.allowsTwakeMailToNotifyYouWhenANewMessageArrivesOnYourPhone;
case AccountMenuItem.keyboardShortcuts:
return appLocalizations.keyboardShortcutsSettingExplanation;
default: default:
return ''; return '';
} }
@@ -117,6 +126,8 @@ enum AccountMenuItem {
return 'contact-support'; return 'contact-support';
case AccountMenuItem.storage: case AccountMenuItem.storage:
return 'storage'; return 'storage';
case AccountMenuItem.keyboardShortcuts:
return 'keyboard-shortcuts';
case AccountMenuItem.signOut: case AccountMenuItem.signOut:
return 'sign-out'; return 'sign-out';
case AccountMenuItem.none: case AccountMenuItem.none:
@@ -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<String> keys;
const KeyboardShortcut({
required this.label,
required this.category,
required this.context,
required this.keys,
});
@override
List<Object?> get props => [
label,
category,
context,
keys,
];
}
@@ -0,0 +1,96 @@
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 KeyboardShortcutsManager {
KeyboardShortcutsManager._();
static List<KeyboardShortcut> generateKeyboardShortcuts(
AppLocalizations appLocalizations,
) {
return [
// Navigation & Closing
KeyboardShortcut(
label: appLocalizations.closeMailComposer,
category: ShortcutCategory.navigationAndClosing,
context: ShortcutContext.mailComposer,
keys: ['ESC'],
),
KeyboardShortcut(
label: appLocalizations.removeFocusFromSearch,
category: ShortcutCategory.navigationAndClosing,
context: ShortcutContext.focusOnSearch,
keys: ['ESC'],
),
KeyboardShortcut(
label: appLocalizations.closeModalWindow,
category: ShortcutCategory.navigationAndClosing,
context: ShortcutContext.openedModal,
keys: ['ESC'],
),
KeyboardShortcut(
label: appLocalizations.openNewMessage,
category: ShortcutCategory.navigationAndClosing,
context: ShortcutContext.mailboxList,
keys: ['N'],
),
// Reading & Replying
KeyboardShortcut(
label: appLocalizations.reply,
category: ShortcutCategory.readingAndReplying,
context: ShortcutContext.openedMailView,
keys: ['R'],
),
KeyboardShortcut(
label: appLocalizations.replyToAll,
category: ShortcutCategory.readingAndReplying,
context: ShortcutContext.openedMailView,
keys: ['Shift', 'R'],
),
KeyboardShortcut(
label: appLocalizations.forward,
category: ShortcutCategory.readingAndReplying,
context: ShortcutContext.openedMailView,
keys: ['F'],
),
KeyboardShortcut(
label: appLocalizations.mark_as_read,
category: ShortcutCategory.readingAndReplying,
context: ShortcutContext.openedMailView,
keys: ['Q'],
),
KeyboardShortcut(
label: appLocalizations.mark_as_unread,
category: ShortcutCategory.readingAndReplying,
context: ShortcutContext.openedMailView,
keys: ['U'],
),
// Message Management & Selection
KeyboardShortcut(
label: appLocalizations.deleteMessage,
category: ShortcutCategory.messageManagementAndSelection,
context: ShortcutContext.openedMailView,
keys: ['Delete'],
),
KeyboardShortcut(
label: appLocalizations.deleteMessage,
category: ShortcutCategory.messageManagementAndSelection,
context: ShortcutContext.mailboxListWithSelectedEmail,
keys: ['Delete'],
),
KeyboardShortcut(
label: appLocalizations.mark_as_read,
category: ShortcutCategory.messageManagementAndSelection,
context: ShortcutContext.mailboxListWithSelectedEmail,
keys: ['Q'],
),
KeyboardShortcut(
label: appLocalizations.mark_as_unread,
category: ShortcutCategory.messageManagementAndSelection,
context: ShortcutContext.mailboxListWithSelectedEmail,
keys: ['U'],
),
];
}
}
+118 -4
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2025-10-17T09:52:08.303266", "@@last_modified": "2025-10-29T10:59:38.089168",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -2688,13 +2688,13 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"noEmailInYourCurrentFolder": "There are no emails in your current folder", "noEmailInYourCurrentFolder": "We're sorry, there are no emails in your current folder",
"@noEmailInYourCurrentFolder": { "@noEmailInYourCurrentFolder": {
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"noEmailMatchYourCurrentFilter": "There are no emails that match your current filter.", "noEmailMatchYourCurrentFilter": "We're sorry, there are no emails that match your current filter.",
"@noEmailMatchYourCurrentFilter": { "@noEmailMatchYourCurrentFilter": {
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
@@ -4937,5 +4937,119 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"keyboardShortcuts": "Keyboard shortcuts",
"@keyboardShortcuts": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"keyboardShortcutsSettingExplanation": "Mailbox & email actions",
"@keyboardShortcutsSettingExplanation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"navigationAndClosing": "Navigation & Closing",
"@navigationAndClosing": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"readingAndReplying": "Reading & Replying",
"@readingAndReplying": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"messageManagementAndSelection": "Message Management & Selection",
"@messageManagementAndSelection": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"navigation": "Navigation",
"@navigation": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"reading": "Reading",
"@reading": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"closeMailComposer": "Close mail composer",
"@closeMailComposer": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"removeFocusFromSearch": "Remove focus from search",
"@removeFocusFromSearch": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"closeModalWindow": "Close modal window",
"@closeModalWindow": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"openNewMessage": "Open new message",
"@openNewMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"mailComposer": "Mail composer",
"@mailComposer": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"focusOnSearch": "Focus on search",
"@focusOnSearch": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"openedModal": "Opened modal",
"@openedModal": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"mailboxList": "Mailbox list",
"@mailboxList": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"openedMailView": "Opened mail view",
"@openedMailView": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"mailboxListWithSelectedMail": "Mailbox list (selected mail)",
"@mailboxListWithSelectedMail": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"replyToAll": "Reply to all",
"@replyToAll": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"deleteMessage": "Delete message",
"@deleteMessage": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -5212,4 +5212,137 @@ class AppLocalizations {
name: 'mailHasBeenUnstarred', name: 'mailHasBeenUnstarred',
); );
} }
String get keyboardShortcuts {
return Intl.message(
'Keyboard shortcuts',
name: 'keyboardShortcuts',
);
}
String get keyboardShortcutsSettingExplanation {
return Intl.message(
'Mailbox & email actions',
name: 'keyboardShortcutsSettingExplanation',
);
}
String get navigationAndClosing {
return Intl.message(
'Navigation & Closing',
name: 'navigationAndClosing',
);
}
String get readingAndReplying {
return Intl.message(
'Reading & Replying',
name: 'readingAndReplying',
);
}
String get messageManagementAndSelection {
return Intl.message(
'Message Management & Selection',
name: 'messageManagementAndSelection',
);
}
String get navigation {
return Intl.message(
'Navigation',
name: 'navigation',
);
}
String get reading {
return Intl.message(
'Reading',
name: 'reading',
);
}
String get closeMailComposer {
return Intl.message(
'Close mail composer',
name: 'closeMailComposer',
);
}
String get removeFocusFromSearch {
return Intl.message(
'Remove focus from search',
name: 'removeFocusFromSearch',
);
}
String get closeModalWindow {
return Intl.message(
'Close modal window',
name: 'closeModalWindow',
);
}
String get openNewMessage {
return Intl.message(
'Open new message',
name: 'openNewMessage',
);
}
String get mailComposer {
return Intl.message(
'Mail composer',
name: 'mailComposer',
);
}
String get focusOnSearch {
return Intl.message(
'Focus on search',
name: 'focusOnSearch',
);
}
String get openedModal {
return Intl.message(
'Opened modal',
name: 'openedModal',
);
}
String get mailboxList {
return Intl.message(
'Mailbox list',
name: 'mailboxList',
);
}
String get openedMailView {
return Intl.message(
'Opened mail view',
name: 'openedMailView',
);
}
String get mailboxListWithSelectedMail {
return Intl.message(
'Mailbox list (selected mail)',
name: 'mailboxListWithSelectedMail',
);
}
String get replyToAll {
return Intl.message(
'Reply to all',
name: 'replyToAll',
);
}
String get deleteMessage {
return Intl.message(
'Delete message',
name: 'deleteMessage',
);
}
} }
+8
View File
@@ -757,6 +757,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
flutter_custom_tab_bar:
dependency: "direct main"
description:
name: flutter_custom_tab_bar
sha256: "5f550d5343ebf778ae73d1de775c8d38e4a2312146cd77845472dd7ed04aafca"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_date_range_picker: flutter_date_range_picker:
dependency: "direct main" dependency: "direct main"
description: description:
+2
View File
@@ -265,6 +265,8 @@ dependencies:
lottie_native: 0.1.2 lottie_native: 0.1.2
flutter_custom_tab_bar: 1.2.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter