TF-858 Add HtmlEditor for message of vacation on web
This commit is contained in:
@@ -3,6 +3,17 @@ import 'package:core/core.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/popup_menu_overlay_widget.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/button_layout_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/order_list_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/drop_down_menu_header_style_widget.dart';
|
||||||
|
|
||||||
mixin RichTextButtonMixin {
|
mixin RichTextButtonMixin {
|
||||||
|
|
||||||
@@ -17,9 +28,33 @@ mixin RichTextButtonMixin {
|
|||||||
double? spacing,
|
double? spacing,
|
||||||
String tooltip = '',
|
String tooltip = '',
|
||||||
}){
|
}){
|
||||||
final buttonIcon = Tooltip(
|
late Widget buttonIcon;
|
||||||
message: tooltip,
|
if (tooltip.isNotEmpty) {
|
||||||
child: Container(
|
buttonIcon = Tooltip(
|
||||||
|
message: tooltip,
|
||||||
|
child: Container(
|
||||||
|
padding: padding ?? const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected == true
|
||||||
|
? AppColor.colorBackgroundWrapIconStyleCode
|
||||||
|
: Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColor.colorBorderWrapIconStyleCode,
|
||||||
|
width: 0.5),
|
||||||
|
borderRadius: BorderRadius.circular(8)),
|
||||||
|
child: hasDropdown
|
||||||
|
? Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
if (spacing != null) SizedBox(width: spacing),
|
||||||
|
SvgPicture.asset(_imagePaths.icStyleArrowDown)
|
||||||
|
])
|
||||||
|
: icon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
buttonIcon = Container(
|
||||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
padding: padding ?? const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected == true
|
color: isSelected == true
|
||||||
@@ -38,8 +73,8 @@ mixin RichTextButtonMixin {
|
|||||||
SvgPicture.asset(_imagePaths.icStyleArrowDown)
|
SvgPicture.asset(_imagePaths.icStyleArrowDown)
|
||||||
])
|
])
|
||||||
: icon,
|
: icon,
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@@ -83,12 +118,15 @@ mixin RichTextButtonMixin {
|
|||||||
? AppColor.colorDefaultRichTextButton
|
? AppColor.colorDefaultRichTextButton
|
||||||
: color;
|
: color;
|
||||||
|
|
||||||
return Tooltip(
|
return tooltip?.isNotEmpty == true
|
||||||
child: SvgPicture.asset(path,
|
? Tooltip(
|
||||||
color: newColor?.withOpacity(opacity),
|
child: SvgPicture.asset(path,
|
||||||
fit: BoxFit.fill),
|
color: newColor?.withOpacity(opacity),
|
||||||
message: tooltip,
|
fit: BoxFit.fill),
|
||||||
);
|
message: tooltip)
|
||||||
|
: SvgPicture.asset(path,
|
||||||
|
color: newColor?.withOpacity(opacity),
|
||||||
|
fit: BoxFit.fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildIcon({
|
Widget buildIcon({
|
||||||
@@ -134,4 +172,184 @@ mixin RichTextButtonMixin {
|
|||||||
message: tooltip,
|
message: tooltip,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildToolbarRichTextForWeb(
|
||||||
|
BuildContext context,
|
||||||
|
RichTextWebController richTextController,
|
||||||
|
{ButtonLayoutType layoutType = ButtonLayoutType.wrapParent}
|
||||||
|
) {
|
||||||
|
late Widget parentRichTextButton;
|
||||||
|
|
||||||
|
switch(layoutType) {
|
||||||
|
case ButtonLayoutType.scrollHorizontal:
|
||||||
|
parentRichTextButton = Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: Colors.transparent,
|
||||||
|
height: 38,
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
children: listButtonActionForRichText(context, richTextController)),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ButtonLayoutType.scrollVertical:
|
||||||
|
parentRichTextButton = ListView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
children: listButtonActionForRichText(context, richTextController));
|
||||||
|
break;
|
||||||
|
case ButtonLayoutType.wrapParent:
|
||||||
|
parentRichTextButton = Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: listButtonActionForRichText(context, richTextController)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: Colors.transparent,
|
||||||
|
height: 50,
|
||||||
|
child: parentRichTextButton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> listButtonActionForRichText(
|
||||||
|
BuildContext context,
|
||||||
|
RichTextWebController richTextController
|
||||||
|
) {
|
||||||
|
return [
|
||||||
|
DropDownMenuHeaderStyleWidget(
|
||||||
|
icon: buildWrapIconStyleText(
|
||||||
|
isSelected: richTextController.isMenuHeaderStyleOpen,
|
||||||
|
icon: SvgPicture.asset(RichTextStyleType.headerStyle.getIcon(_imagePaths),
|
||||||
|
color: AppColor.colorDefaultRichTextButton,
|
||||||
|
fit: BoxFit.fill),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
|
||||||
|
tooltip: RichTextStyleType.headerStyle.getTooltipButton(context)
|
||||||
|
),
|
||||||
|
items: HeaderStyleType.values,
|
||||||
|
onMenuStateChange: (isOpen) {
|
||||||
|
final newStatus = isOpen
|
||||||
|
? DropdownMenuFontStatus.open
|
||||||
|
: DropdownMenuFontStatus.closed;
|
||||||
|
richTextController.menuHeaderStyleStatus.value = newStatus;
|
||||||
|
},
|
||||||
|
onChanged: (newStyle) => richTextController.applyHeaderStyle(newStyle)),
|
||||||
|
Container(
|
||||||
|
width: 130,
|
||||||
|
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
|
||||||
|
child: DropDownButtonWidget<FontNameType>(
|
||||||
|
items: FontNameType.values,
|
||||||
|
itemSelected: richTextController.selectedFontName.value,
|
||||||
|
onChanged: (newFont) => richTextController.applyNewFontStyle(newFont),
|
||||||
|
onMenuStateChange: (isOpen) {
|
||||||
|
final newStatus = isOpen
|
||||||
|
? DropdownMenuFontStatus.open
|
||||||
|
: DropdownMenuFontStatus.closed;
|
||||||
|
richTextController.menuFontStatus.value = newStatus;
|
||||||
|
},
|
||||||
|
heightItem: 40,
|
||||||
|
sizeIconChecked: 16,
|
||||||
|
radiusButton: 8,
|
||||||
|
dropdownWidth: 200,
|
||||||
|
colorButton: richTextController.isMenuFontOpen
|
||||||
|
? AppColor.colorBackgroundWrapIconStyleCode
|
||||||
|
: Colors.white,
|
||||||
|
iconArrowDown: SvgPicture.asset(_imagePaths.icStyleArrowDown),
|
||||||
|
tooltip: RichTextStyleType.fontName.getTooltipButton(context),
|
||||||
|
supportSelectionIcon: true)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: buildWrapIconStyleText(
|
||||||
|
icon: buildIconWithTooltip(
|
||||||
|
path: RichTextStyleType.textColor.getIcon(_imagePaths),
|
||||||
|
color: richTextController.selectedTextColor.value,
|
||||||
|
tooltip: RichTextStyleType.textColor.getTooltipButton(context),
|
||||||
|
),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.textColor)),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: buildWrapIconStyleText(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 7),
|
||||||
|
spacing: 3,
|
||||||
|
icon: buildIconColorBackgroundText(
|
||||||
|
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
|
||||||
|
colorSelected: richTextController.selectedTextBackgroundColor.value,
|
||||||
|
tooltip: RichTextStyleType.textBackgroundColor.getTooltipButton(context),
|
||||||
|
),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: buildWrapIconStyleText(
|
||||||
|
hasDropdown: false,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
|
||||||
|
icon: Wrap(children: [
|
||||||
|
buildIconStyleText(
|
||||||
|
path: RichTextStyleType.bold.getIcon(_imagePaths),
|
||||||
|
isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.bold),
|
||||||
|
tooltip: RichTextStyleType.bold.getTooltipButton(context),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.bold)),
|
||||||
|
buildIconStyleText(
|
||||||
|
path: RichTextStyleType.italic.getIcon(_imagePaths),
|
||||||
|
isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.italic),
|
||||||
|
tooltip: RichTextStyleType.italic.getTooltipButton(context),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.italic)),
|
||||||
|
buildIconStyleText(
|
||||||
|
path: RichTextStyleType.underline.getIcon(_imagePaths),
|
||||||
|
isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.underline),
|
||||||
|
tooltip: RichTextStyleType.underline.getTooltipButton(context),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.underline)),
|
||||||
|
buildIconStyleText(
|
||||||
|
path: RichTextStyleType.strikeThrough.getIcon(_imagePaths),
|
||||||
|
isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.strikeThrough),
|
||||||
|
tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
|
||||||
|
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.strikeThrough))
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: PopupMenuOverlayWidget(
|
||||||
|
controller: richTextController.menuParagraphController,
|
||||||
|
listButtonAction: ParagraphType.values
|
||||||
|
.map((paragraph) => paragraph.buildButtonWidget(
|
||||||
|
context,
|
||||||
|
_imagePaths,
|
||||||
|
(paragraph) => richTextController.applyParagraphType(paragraph)))
|
||||||
|
.toList(),
|
||||||
|
iconButton: buildWrapIconStyleText(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
|
||||||
|
spacing: 3,
|
||||||
|
isSelected: richTextController.focusMenuParagraph.value,
|
||||||
|
icon: buildIconWithTooltip(
|
||||||
|
path: richTextController.selectedParagraph.value.getIcon(_imagePaths),
|
||||||
|
color: AppColor.colorDefaultRichTextButton,
|
||||||
|
tooltip: RichTextStyleType.paragraph.getTooltipButton(context))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: PopupMenuOverlayWidget(
|
||||||
|
controller: richTextController.menuOrderListController,
|
||||||
|
listButtonAction: OrderListType.values
|
||||||
|
.map((orderType) => orderType.buildButtonWidget(
|
||||||
|
context,
|
||||||
|
_imagePaths,
|
||||||
|
(orderType) => richTextController.applyOrderListType(orderType)))
|
||||||
|
.toList(),
|
||||||
|
iconButton: buildWrapIconStyleText(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
|
||||||
|
spacing: 3,
|
||||||
|
isSelected: richTextController.focusMenuOrderList.value,
|
||||||
|
icon: buildIconWithTooltip(
|
||||||
|
path: richTextController.selectedOrderList.value.getIcon(_imagePaths),
|
||||||
|
color: AppColor.colorDefaultRichTextButton,
|
||||||
|
tooltip: RichTextStyleType.orderList.getTooltipButton(context))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
enum ButtonLayoutType {
|
||||||
|
scrollHorizontal,
|
||||||
|
scrollVertical,
|
||||||
|
wrapParent
|
||||||
|
}
|
||||||
+2
-1
@@ -23,7 +23,8 @@ extension VacationResponseExtension on VacationResponse {
|
|||||||
endTime: toDate?.value != null
|
endTime: toDate?.value != null
|
||||||
? TimeOfDay.fromDateTime(toDate!.value.toUtc())
|
? TimeOfDay.fromDateTime(toDate!.value.toUtc())
|
||||||
: null,
|
: null,
|
||||||
messageBody: textBody ?? htmlBody,
|
messagePlainText: textBody,
|
||||||
|
messageHtmlText: htmlBody,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
vacationStopEnabled: toDate != null
|
vacationStopEnabled: toDate != null
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
enum VacationMessageType {
|
||||||
|
plainText,
|
||||||
|
htmlTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VacationMessageTypeExtension on VacationMessageType {
|
||||||
|
|
||||||
|
String getTitle(BuildContext context) {
|
||||||
|
switch(this) {
|
||||||
|
case VacationMessageType.plainText:
|
||||||
|
return AppLocalizations.of(context).plain_text;
|
||||||
|
case VacationMessageType.htmlTemplate:
|
||||||
|
return AppLocalizations.of(context).html_template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,8 @@ class VacationPresentation with EquatableMixin {
|
|||||||
final TimeOfDay? startTime;
|
final TimeOfDay? startTime;
|
||||||
final DateTime? endDate;
|
final DateTime? endDate;
|
||||||
final TimeOfDay? endTime;
|
final TimeOfDay? endTime;
|
||||||
final String? messageBody;
|
final String? messagePlainText;
|
||||||
|
final String? messageHtmlText;
|
||||||
final String? subject;
|
final String? subject;
|
||||||
final bool vacationStopEnabled;
|
final bool vacationStopEnabled;
|
||||||
|
|
||||||
@@ -22,7 +23,8 @@ class VacationPresentation with EquatableMixin {
|
|||||||
this.startTime,
|
this.startTime,
|
||||||
this.endDate,
|
this.endDate,
|
||||||
this.endTime,
|
this.endTime,
|
||||||
this.messageBody,
|
this.messagePlainText,
|
||||||
|
this.messageHtmlText,
|
||||||
this.subject,
|
this.subject,
|
||||||
this.vacationStopEnabled = false,
|
this.vacationStopEnabled = false,
|
||||||
});
|
});
|
||||||
@@ -37,7 +39,8 @@ class VacationPresentation with EquatableMixin {
|
|||||||
TimeOfDay? startTime,
|
TimeOfDay? startTime,
|
||||||
DateTime? endDate,
|
DateTime? endDate,
|
||||||
TimeOfDay? endTime,
|
TimeOfDay? endTime,
|
||||||
String? messageBody,
|
String? messagePlainText,
|
||||||
|
String? messageHtmlText,
|
||||||
String? subject,
|
String? subject,
|
||||||
bool? vacationStopEnabled,
|
bool? vacationStopEnabled,
|
||||||
}) {
|
}) {
|
||||||
@@ -47,7 +50,8 @@ class VacationPresentation with EquatableMixin {
|
|||||||
startTime: startTime ?? this.startTime,
|
startTime: startTime ?? this.startTime,
|
||||||
endDate: endDate ?? this.endDate,
|
endDate: endDate ?? this.endDate,
|
||||||
endTime: endTime ?? this.endTime,
|
endTime: endTime ?? this.endTime,
|
||||||
messageBody: messageBody ?? this.messageBody,
|
messagePlainText: messagePlainText ?? this.messagePlainText,
|
||||||
|
messageHtmlText: messageHtmlText ?? this.messageHtmlText,
|
||||||
subject: subject ?? this.subject,
|
subject: subject ?? this.subject,
|
||||||
vacationStopEnabled: vacationStopEnabled ?? this.vacationStopEnabled
|
vacationStopEnabled: vacationStopEnabled ?? this.vacationStopEnabled
|
||||||
);
|
);
|
||||||
@@ -75,7 +79,8 @@ class VacationPresentation with EquatableMixin {
|
|||||||
endDate,
|
endDate,
|
||||||
endTime,
|
endTime,
|
||||||
vacationStopEnabled,
|
vacationStopEnabled,
|
||||||
messageBody,
|
messagePlainText,
|
||||||
|
messageHtmlText,
|
||||||
subject,
|
subject,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -86,7 +91,8 @@ extension VacationPresentationExtension on VacationPresentation {
|
|||||||
isEnabled: isEnabled,
|
isEnabled: isEnabled,
|
||||||
fromDate: fromDate != null ? UTCDate(fromDate!) : null,
|
fromDate: fromDate != null ? UTCDate(fromDate!) : null,
|
||||||
toDate: toDate != null ? UTCDate(toDate!) : null,
|
toDate: toDate != null ? UTCDate(toDate!) : null,
|
||||||
textBody: messageBody,
|
textBody: messagePlainText,
|
||||||
|
htmlBody: messageHtmlText,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
class VacationUtils {
|
||||||
|
static const String vacationTagName = 'Vacation';
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
@@ -15,8 +16,11 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va
|
|||||||
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/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/model/vacation/date_type.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/date_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_message_type.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_presentation.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_presentation.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/utils/vacation_utils.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller_bindings.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
@@ -26,6 +30,7 @@ class VacationController extends BaseController {
|
|||||||
final _appToast = Get.find<AppToast>();
|
final _appToast = Get.find<AppToast>();
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
final _settingController = Get.find<SettingsController>();
|
final _settingController = Get.find<SettingsController>();
|
||||||
|
final _richTextControllerForWeb = Get.find<RichTextWebController>(tag: VacationUtils.vacationTagName);
|
||||||
|
|
||||||
final GetAllVacationInteractor _getAllVacationInteractor;
|
final GetAllVacationInteractor _getAllVacationInteractor;
|
||||||
final UpdateVacationInteractor _updateVacationInteractor;
|
final UpdateVacationInteractor _updateVacationInteractor;
|
||||||
@@ -33,11 +38,14 @@ class VacationController extends BaseController {
|
|||||||
|
|
||||||
final vacationPresentation = VacationPresentation.initialize().obs;
|
final vacationPresentation = VacationPresentation.initialize().obs;
|
||||||
final errorMessageBody = Rxn<String>();
|
final errorMessageBody = Rxn<String>();
|
||||||
|
final vacationMessageType = Rx<VacationMessageType>(VacationMessageType.plainText);
|
||||||
|
|
||||||
final messageTextController = TextEditingController();
|
final messageTextController = TextEditingController();
|
||||||
final subjectTextController = TextEditingController();
|
final subjectTextController = TextEditingController();
|
||||||
|
|
||||||
VacationResponse? currentVacation;
|
VacationResponse? currentVacation;
|
||||||
|
String? _vacationMessageHtmlText;
|
||||||
|
|
||||||
late Worker vacationWorker;
|
late Worker vacationWorker;
|
||||||
|
|
||||||
VacationController(
|
VacationController(
|
||||||
@@ -46,6 +54,10 @@ class VacationController extends BaseController {
|
|||||||
this._verifyNameInteractor
|
this._verifyNameInteractor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String? get vacationMessageHtmlText => _vacationMessageHtmlText;
|
||||||
|
|
||||||
|
RichTextWebController get richTextControllerForWeb => _richTextControllerForWeb;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
_initWorker();
|
_initWorker();
|
||||||
@@ -80,9 +92,7 @@ class VacationController extends BaseController {
|
|||||||
if (vacation is VacationResponse) {
|
if (vacation is VacationResponse) {
|
||||||
currentVacation = vacation;
|
currentVacation = vacation;
|
||||||
final newVacationPresentation = currentVacation?.toVacationPresentation();
|
final newVacationPresentation = currentVacation?.toVacationPresentation();
|
||||||
vacationPresentation.value = newVacationPresentation ?? VacationPresentation.initialize();
|
_initializeValueForVacation(newVacationPresentation ?? VacationPresentation.initialize());
|
||||||
messageTextController.text = newVacationPresentation?.messageBody ?? '';
|
|
||||||
subjectTextController.text = newVacationPresentation?.subject ?? '';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -101,13 +111,19 @@ class VacationController extends BaseController {
|
|||||||
|
|
||||||
if (currentVacation != null) {
|
if (currentVacation != null) {
|
||||||
final newVacationPresentation = currentVacation!.toVacationPresentation();
|
final newVacationPresentation = currentVacation!.toVacationPresentation();
|
||||||
vacationPresentation.value = newVacationPresentation;
|
_initializeValueForVacation(newVacationPresentation);
|
||||||
messageTextController.text = newVacationPresentation.messageBody ?? '';
|
|
||||||
subjectTextController.text = newVacationPresentation.subject ?? '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _initializeValueForVacation(VacationPresentation newVacation) {
|
||||||
|
vacationPresentation.value = newVacation;
|
||||||
|
messageTextController.text = newVacation.messagePlainText ?? '';
|
||||||
|
subjectTextController.text = newVacation.subject ?? '';
|
||||||
|
updateMessageHtmlText(newVacation.messageHtmlText ?? '');
|
||||||
|
_richTextControllerForWeb.editorController.setText(newVacation.messageHtmlText ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
bool get isVacationDeactivated => !vacationPresentation.value.isEnabled;
|
bool get isVacationDeactivated => !vacationPresentation.value.isEnabled;
|
||||||
|
|
||||||
bool get isVacationStopEnabled => vacationPresentation.value.vacationStopEnabled;
|
bool get isVacationStopEnabled => vacationPresentation.value.vacationStopEnabled;
|
||||||
@@ -121,7 +137,8 @@ class VacationController extends BaseController {
|
|||||||
DateTime? endDate,
|
DateTime? endDate,
|
||||||
TimeOfDay? endTime,
|
TimeOfDay? endTime,
|
||||||
bool? vacationStopEnabled,
|
bool? vacationStopEnabled,
|
||||||
String? messageBody
|
String? messagePlainText,
|
||||||
|
String? messageHtmlText,
|
||||||
}) {
|
}) {
|
||||||
final currentVacation = vacationPresentation.value;
|
final currentVacation = vacationPresentation.value;
|
||||||
final newVacation = currentVacation.copyWidth(
|
final newVacation = currentVacation.copyWidth(
|
||||||
@@ -131,7 +148,9 @@ class VacationController extends BaseController {
|
|||||||
endDate: endDate,
|
endDate: endDate,
|
||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
vacationStopEnabled: vacationStopEnabled,
|
vacationStopEnabled: vacationStopEnabled,
|
||||||
messageBody: messageBody);
|
messagePlainText: messagePlainText,
|
||||||
|
messageHtmlText: messageHtmlText
|
||||||
|
);
|
||||||
log('VacationController::updateVacationPresentation():newVacation: $newVacation');
|
log('VacationController::updateVacationPresentation():newVacation: $newVacation');
|
||||||
vacationPresentation.value = newVacation;
|
vacationPresentation.value = newVacation;
|
||||||
}
|
}
|
||||||
@@ -241,8 +260,9 @@ class VacationController extends BaseController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final messageBody = messageTextController.text;
|
final messagePlainText = messageTextController.text;
|
||||||
if (messageBody.isEmpty) {
|
final messageHtmlText = _vacationMessageHtmlText ?? '';
|
||||||
|
if (messagePlainText.isEmpty && messageHtmlText.isEmpty) {
|
||||||
_appToast.showToastWithIcon(
|
_appToast.showToastWithIcon(
|
||||||
context,
|
context,
|
||||||
bgColor: AppColor.toastErrorBackgroundColor,
|
bgColor: AppColor.toastErrorBackgroundColor,
|
||||||
@@ -254,7 +274,8 @@ class VacationController extends BaseController {
|
|||||||
final subjectVacation = subjectTextController.text;
|
final subjectVacation = subjectTextController.text;
|
||||||
|
|
||||||
final newVacationPresentation = vacationPresentation.value.copyWidth(
|
final newVacationPresentation = vacationPresentation.value.copyWidth(
|
||||||
messageBody: messageBody,
|
messagePlainText: messagePlainText,
|
||||||
|
messageHtmlText: messageHtmlText,
|
||||||
subject: subjectVacation);
|
subject: subjectVacation);
|
||||||
log('VacationController::saveVacation(): newVacationPresentation: $newVacationPresentation');
|
log('VacationController::saveVacation(): newVacationPresentation: $newVacationPresentation');
|
||||||
final newVacationResponse = newVacationPresentation.toVacationResponse();
|
final newVacationResponse = newVacationPresentation.toVacationResponse();
|
||||||
@@ -289,15 +310,32 @@ class VacationController extends BaseController {
|
|||||||
|
|
||||||
if (currentVacation != null) {
|
if (currentVacation != null) {
|
||||||
final newVacationPresentation = currentVacation!.toVacationPresentation();
|
final newVacationPresentation = currentVacation!.toVacationPresentation();
|
||||||
vacationPresentation.value = newVacationPresentation;
|
_initializeValueForVacation(newVacationPresentation);
|
||||||
messageTextController.text = newVacationPresentation.messageBody ?? '';
|
|
||||||
subjectTextController.text = newVacationPresentation.subject ?? '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_accountDashBoardController.updateVacationResponse(currentVacation);
|
_accountDashBoardController.updateVacationResponse(currentVacation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateMessageHtmlText(String? text) => _vacationMessageHtmlText = text;
|
||||||
|
|
||||||
|
Future<String?> _getMessageHtmlText() {
|
||||||
|
return _richTextControllerForWeb.editorController.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectVacationMessageType(BuildContext context, VacationMessageType newMessageType) async {
|
||||||
|
if (newMessageType == VacationMessageType.plainText && !BuildUtils.isWeb) {
|
||||||
|
final messageHtml = await _getMessageHtmlText();
|
||||||
|
updateMessageHtmlText(messageHtml);
|
||||||
|
}
|
||||||
|
clearFocusEditor(context);
|
||||||
|
vacationMessageType.value = newMessageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearFocusEditor(BuildContext context) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
|
|
||||||
void backToUniversalSettings() {
|
void backToUniversalSettings() {
|
||||||
_settingController.backToUniversalSettings();
|
_settingController.backToUniversalSettings();
|
||||||
}
|
}
|
||||||
@@ -307,6 +345,7 @@ class VacationController extends BaseController {
|
|||||||
messageTextController.dispose();
|
messageTextController.dispose();
|
||||||
subjectTextController.dispose();
|
subjectTextController.dispose();
|
||||||
vacationWorker.dispose();
|
vacationWorker.dispose();
|
||||||
|
VacationControllerBindings().dispose();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_bindings.dart';
|
import 'package:tmail_ui_user/features/base/base_bindings.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/utils/vacation_utils.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller.dart';
|
||||||
|
|
||||||
class VacationControllerBindings extends BaseBindings {
|
class VacationControllerBindings extends BaseBindings {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void bindingsController() {
|
void bindingsController() {
|
||||||
|
Get.lazyPut(() => RichTextWebController(), tag: VacationUtils.vacationTagName);
|
||||||
Get.lazyPut(() => VacationController(
|
Get.lazyPut(() => VacationController(
|
||||||
Get.find<GetAllVacationInteractor>(),
|
Get.find<GetAllVacationInteractor>(),
|
||||||
Get.find<UpdateVacationInteractor>(),
|
Get.find<UpdateVacationInteractor>(),
|
||||||
@@ -35,4 +38,8 @@ class VacationControllerBindings extends BaseBindings {
|
|||||||
@override
|
@override
|
||||||
void bindingsRepositoryImpl() {
|
void bindingsRepositoryImpl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
Get.delete<RichTextWebController>(tag: VacationUtils.vacationTagName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,21 @@ import 'package:core/core.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/border_button_field.dart';
|
import 'package:tmail_ui_user/features/base/widget/border_button_field.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/text_input_decoration_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/text_input_field_builder.dart';
|
import 'package:tmail_ui_user/features/base/widget/text_input_field_builder.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/model/button_layout_type.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/model/vacation/date_type.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/date_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_message_type.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/model/vacation/vacation_responder_status.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser;
|
||||||
|
|
||||||
class VacationView extends GetWidget<VacationController> {
|
class VacationView extends GetWidget<VacationController> with RichTextButtonMixin {
|
||||||
|
|
||||||
final _responsiveUtils = Get.find<ResponsiveUtils>();
|
final _responsiveUtils = Get.find<ResponsiveUtils>();
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
@@ -257,18 +263,7 @@ class VacationView extends GetWidget<VacationController> {
|
|||||||
absorbing: controller.isVacationDeactivated,
|
absorbing: controller.isVacationDeactivated,
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: controller.isVacationDeactivated ? 0.3 : 1.0,
|
opacity: controller.isVacationDeactivated ? 0.3 : 1.0,
|
||||||
child: TextInputFieldBuilder(
|
child: _buildVacationMessage(context),
|
||||||
label: AppLocalizations.of(context).message,
|
|
||||||
hint: AppLocalizations.of(context).hintMessageBodyVacation,
|
|
||||||
minLines: 10,
|
|
||||||
maxLines: null,
|
|
||||||
inputType: TextInputType.multiline,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
error: controller.isVacationDeactivated
|
|
||||||
? null
|
|
||||||
: controller.errorMessageBody.value,
|
|
||||||
onChangeInputAction: (value) => controller.updateMessageBody(context, value),
|
|
||||||
editingController: controller.messageTextController),
|
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
@@ -360,4 +355,119 @@ class VacationView extends GetWidget<VacationController> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildVacationMessage(BuildContext context) {
|
||||||
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Row(children: [
|
||||||
|
Expanded(child: Text(AppLocalizations.of(context).message,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: AppColor.colorContentEmail))),
|
||||||
|
_buildVacationMessageTypeButton(context, VacationMessageType.plainText),
|
||||||
|
_buildVacationMessageTypeButton(context, VacationMessageType.htmlTemplate),
|
||||||
|
]),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildMessageTextEditor(context)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMessageTextEditor(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
if (controller.vacationMessageType.value == VacationMessageType.plainText) {
|
||||||
|
return _buildMessagePlainTextEditor(context);
|
||||||
|
} else {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(color: AppColor.colorInputBorderCreateMailbox),
|
||||||
|
color: Colors.white),
|
||||||
|
padding: const EdgeInsets.only(left: 12, right: 12, top: 12),
|
||||||
|
child: Column(children: [
|
||||||
|
_buildMessageHtmlTextEditor(context),
|
||||||
|
Center(child: Obx(() {
|
||||||
|
return PointerInterceptor(
|
||||||
|
child: buildToolbarRichTextForWeb(
|
||||||
|
context,
|
||||||
|
controller.richTextControllerForWeb,
|
||||||
|
layoutType: ButtonLayoutType.scrollHorizontal),
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMessagePlainTextEditor(BuildContext context) {
|
||||||
|
return (TextFieldBuilder()
|
||||||
|
..onChange((value) => controller.updateMessageBody(context, value))
|
||||||
|
..textInputAction(TextInputAction.next)
|
||||||
|
..addController(controller.messageTextController)
|
||||||
|
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||||
|
..keyboardType(TextInputType.text)
|
||||||
|
..minLines(10)
|
||||||
|
..maxLines(null)
|
||||||
|
..textDecoration((TextInputDecorationBuilder()
|
||||||
|
..setContentPadding(const EdgeInsets.all(16))
|
||||||
|
..setHintText(AppLocalizations.of(context).hintMessageBodyVacation)
|
||||||
|
..setFillColor(Colors.white)
|
||||||
|
..setErrorText(controller.isVacationDeactivated
|
||||||
|
? null
|
||||||
|
: controller.errorMessageBody.value))
|
||||||
|
.build()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMessageHtmlTextEditor(BuildContext context) {
|
||||||
|
if (BuildUtils.isWeb) {
|
||||||
|
return html_editor_browser.HtmlEditor(
|
||||||
|
key: const Key('vacation_message_html_text_editor_web'),
|
||||||
|
controller: controller.richTextControllerForWeb.editorController,
|
||||||
|
htmlEditorOptions: html_editor_browser.HtmlEditorOptions(
|
||||||
|
hint: '',
|
||||||
|
darkMode: false,
|
||||||
|
customBodyCssStyle: bodyCssStyleForEditor),
|
||||||
|
blockQuotedContent: controller.vacationMessageHtmlText ?? '',
|
||||||
|
htmlToolbarOptions: const html_editor_browser.HtmlToolbarOptions(
|
||||||
|
toolbarType: html_editor_browser.ToolbarType.hide,
|
||||||
|
defaultToolbarButtons: []),
|
||||||
|
otherOptions: const html_editor_browser.OtherOptions(height: 150),
|
||||||
|
callbacks: html_editor_browser.Callbacks(
|
||||||
|
onInit: () {
|
||||||
|
controller.richTextControllerForWeb.setFullScreenEditor();
|
||||||
|
}, onChangeSelection: (settings) {
|
||||||
|
controller.richTextControllerForWeb.onEditorSettingsChange(settings);
|
||||||
|
}, onChangeContent: (String? changed) {
|
||||||
|
controller.updateMessageHtmlText(changed);
|
||||||
|
}, onFocus: () {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
controller.richTextControllerForWeb.editorController.setFocus();
|
||||||
|
});
|
||||||
|
controller.richTextControllerForWeb.closeAllMenuPopup();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVacationMessageTypeButton(BuildContext context, VacationMessageType messageType) {
|
||||||
|
return buildButtonWrapText(
|
||||||
|
messageType.getTitle(context),
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14,
|
||||||
|
color: controller.vacationMessageType.value == messageType
|
||||||
|
? AppColor.colorContentEmail
|
||||||
|
: AppColor.colorHintSearchBar),
|
||||||
|
bgColor: controller.vacationMessageType.value == messageType
|
||||||
|
? AppColor.emailAddressChipColor
|
||||||
|
: Colors.transparent,
|
||||||
|
height: 35,
|
||||||
|
radius: 10,
|
||||||
|
onTap: () => controller.selectVacationMessageType(context, messageType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user