diff --git a/lib/features/base/widget/dialog_picker/date_time_dialog_picker.dart b/lib/features/base/widget/dialog_picker/date_time_dialog_picker.dart new file mode 100644 index 000000000..abfd5faa5 --- /dev/null +++ b/lib/features/base/widget/dialog_picker/date_time_dialog_picker.dart @@ -0,0 +1,113 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:get/get.dart'; + +class DateTimeDialogPicker { + static final _instance = DateTimeDialogPicker._(); + + factory DateTimeDialogPicker() => _instance; + + DateTimeDialogPicker._(); + + final _isOpened = RxBool(false); + + RxBool get isOpened => _isOpened; + + Future showTwakeTimePicker({ + required BuildContext context, + required TimeOfDay initialTime, + bool use24HourFormat = false, + String? cancelText, + String? confirmText, + }) { + _isOpened.value = true; + + return showTimePicker( + context: context, + initialTime: initialTime, + builder: (context, child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: const ColorScheme.light( + primary: AppColor.primaryColor, + onPrimary: Colors.white, + onSurface: Colors.black, + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColor.primaryColor, + ), + ), + ), + child: MediaQuery( + data: MediaQueryData(alwaysUse24HourFormat: use24HourFormat), + child: PlatformInfo.isWeb + ? KeyboardListener( + focusNode: FocusNode(), + autofocus: true, + onKeyEvent: (event) { + if (event.logicalKey == LogicalKeyboardKey.escape) { + Navigator.of(context).pop(); + } + }, + child: child!, + ) + : child!, + ), + ); + }, + cancelText: cancelText, + confirmText: confirmText, + ).whenComplete(() => _isOpened.value = false); + } + + Future showTwakeDatePicker({ + required BuildContext context, + required DateTime initialDate, + String? cancelText, + String? confirmText, + }) { + _isOpened.value = true; + + return showDatePicker( + context: context, + initialDate: initialDate, + initialDatePickerMode: DatePickerMode.day, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + locale: Localizations.localeOf(context), + builder: (context, child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: const ColorScheme.light( + primary: AppColor.primaryColor, + onPrimary: Colors.white, + onSurface: Colors.black, + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: AppColor.primaryColor, + ), + ), + ), + child: PlatformInfo.isWeb + ? KeyboardListener( + focusNode: FocusNode(), + autofocus: true, + onKeyEvent: (event) { + if (event.logicalKey == LogicalKeyboardKey.escape) { + Navigator.of(context).pop(); + } + }, + child: child!, + ) + : child!, + ); + }, + cancelText: cancelText, + confirmText: confirmText, + ).whenComplete(() => _isOpened.value = false); + } +} diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart index 3a9c02fcc..d88c1faa6 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -15,6 +15,7 @@ import 'package:rule_filter/rule_filter/capability_rule_filter.dart'; import 'package:server_settings/server_settings/capability_server_settings.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart'; @@ -57,7 +58,6 @@ class ManageAccountDashBoardController extends ReloadableController { final ownEmailAddress = Rx(''); Session? sessionCurrent; - bool? isVacationDateDialogDisplayed; Uri? previousUri; int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete; @@ -383,7 +383,7 @@ class ManageAccountDashBoardController extends ReloadableController { bool _onBackButtonInterceptor(bool stopDefaultButtonEvent, RouteInfo routeInfo) { log('ManageAccountDashBoardController::_onBackButtonInterceptor:currentRoute: ${Get.currentRoute} | _isDialogViewOpen: $_isDialogViewOpen'); - if (_isDialogViewOpen || isVacationDateDialogDisplayed == true) { + if (_isDialogViewOpen || DateTimeDialogPicker().isOpened.isTrue) { popBack(); _replaceBrowserHistory(); return true; diff --git a/lib/features/manage_account/presentation/vacation/vacation_controller.dart b/lib/features/manage_account/presentation/vacation/vacation_controller.dart index 62c577412..0c221fb4d 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_controller.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_controller.dart @@ -3,10 +3,10 @@ import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/commons/constants.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart'; @@ -138,34 +138,10 @@ class VacationController extends BaseController { } FocusScope.of(context).unfocus(); - _accountDashBoardController.isVacationDateDialogDisplayed = true; - final datePicked = await showDatePicker( - context: context, - initialDate: currentDate ?? DateTime.now(), - initialDatePickerMode: DatePickerMode.day, - firstDate: DateTime(1900), - lastDate: DateTime(2100), - locale: Localizations.localeOf(context), - builder: (context, child) { - return PointerInterceptor( - child: Theme( - data: Theme.of(context).copyWith( - colorScheme: const ColorScheme.light( - primary: AppColor.primaryColor, - onPrimary: Colors.white, - onSurface: Colors.black - ), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom( - foregroundColor: AppColor.primaryColor - ) - ) - ), - child: child! - ), - ); - } - ).whenComplete(() => _accountDashBoardController.isVacationDateDialogDisplayed = false); + final datePicked = await DateTimeDialogPicker().showTwakeDatePicker( + context: context, + initialDate: currentDate ?? DateTime.now(), + ); if (datePicked == null) { return; @@ -184,27 +160,10 @@ class VacationController extends BaseController { } FocusScope.of(context).unfocus(); - _accountDashBoardController.isVacationDateDialogDisplayed = true; - final timePicked = await showTimePicker( + final timePicked = await DateTimeDialogPicker().showTwakeTimePicker( context: context, initialTime: currentTime ?? TimeOfDay.now(), - builder: (context, child) { - return PointerInterceptor( - child: Theme( - data: Theme.of(context).copyWith( - colorScheme: const ColorScheme.light( - primary: AppColor.primaryColor, - onPrimary: Colors.white, - onSurface: Colors.black), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom(foregroundColor: AppColor.primaryColor))), - child: MediaQuery( - data: const MediaQueryData(alwaysUse24HourFormat: false), - child: child!), - ), - ); - } - ).whenComplete(() => _accountDashBoardController.isVacationDateDialogDisplayed = false); + ); if (timePicked == null) { return; diff --git a/lib/features/manage_account/presentation/vacation/vacation_view.dart b/lib/features/manage_account/presentation/vacation/vacation_view.dart index 79137be93..37b93a00d 100644 --- a/lib/features/manage_account/presentation/vacation/vacation_view.dart +++ b/lib/features/manage_account/presentation/vacation/vacation_view.dart @@ -12,8 +12,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser; +import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart'; import 'package:tmail_ui_user/features/base/widget/label_border_button_field.dart'; import 'package:tmail_ui_user/features/base/widget/switch_label_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart'; @@ -399,7 +401,24 @@ class VacationView extends GetWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible(child: _buildMessageHtmlTextEditor(context)), + Flexible( + child: Stack( + children: [ + _buildMessageHtmlTextEditor(context), + Obx(() { + if (DateTimeDialogPicker().isOpened.isTrue) { + return Positioned.fill( + child: PointerInterceptor( + child: const SizedBox.expand(), + ), + ); + } else { + return const SizedBox.shrink(); + } + }), + ], + ), + ), ToolbarRichTextWidget( richTextController: controller.richTextControllerForWeb!, scrollListController: controller.richTextButtonScrollController, @@ -444,7 +463,24 @@ class VacationView extends GetWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible(child: _buildMessageHtmlTextEditor(context)), + Flexible( + child: Stack( + children: [ + _buildMessageHtmlTextEditor(context), + Obx(() { + if (DateTimeDialogPicker().isOpened.isTrue) { + return Positioned.fill( + child: PointerInterceptor( + child: const SizedBox.expand(), + ), + ); + } else { + return const SizedBox.shrink(); + } + }), + ], + ), + ), const SizedBox(height: 8), ToolbarRichTextWidget( richTextController: controller.richTextControllerForWeb!, diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 938fffd0b..8c646e328 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-08-08T16:24:37.279860", + "@@last_modified": "2025-08-18T13:21:17.206262", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -4647,5 +4647,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "ok": "OK", + "@ok": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index bf22e666c..e95a106f6 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4898,4 +4898,11 @@ class AppLocalizations { name: 'creatingAnArchiveForDownloading', ); } + + String get ok { + return Intl.message( + 'OK', + name: 'ok', + ); + } }