diff --git a/lib/features/base/widget/drop_down_button_widget.dart b/lib/features/base/widget/drop_down_button_widget.dart index 07ad6bc57..cdf064939 100644 --- a/lib/features/base/widget/drop_down_button_widget.dart +++ b/lib/features/base/widget/drop_down_button_widget.dart @@ -3,7 +3,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:enough_html_editor/enough_html_editor.dart'; +import 'package:enough_html_editor/enough_html_editor.dart' as enough_html_editor; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; @@ -40,7 +40,7 @@ class DropDownButtonWidget extends StatelessWidget { this.radiusButton = 10, this.opacity = 1.0, this.iconArrowDown, - this.colorButton, + this.colorButton = Colors.white, }) : super(key: key); @override @@ -157,7 +157,7 @@ class DropDownButtonWidget extends StatelessWidget { if (item is FontNameType) { return item.fontFamily; } - if (item is SafeFont) { + if (item is enough_html_editor.SafeFont) { return item.name; } return ''; diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 580197e0d..b367c7420 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -11,12 +11,14 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/download_image_as_base64_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; +import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart'; import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; @@ -401,28 +403,8 @@ class ComposerView extends GetWidget with AppLoaderMixin, Ri const Divider(color: AppColor.colorDividerComposer, height: 1), Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)), const Divider(color: AppColor.colorDividerComposer, height: 1), - Obx(() { - final uploadAttachments = controller.uploadController.listUploadAttachments; - if (uploadAttachments.isEmpty) { - return const SizedBox.shrink(); - } else { - return Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildAttachmentsTitle(context, - uploadAttachments, - controller.expandModeAttachments.value)), - Padding( - padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), - child: _buildAttachmentsList(context, - uploadAttachments, - controller.expandModeAttachments.value)) - ]); - } - }), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20), - child: _buildComposerEditor(context)), + _buildAttachmentsWidget(context), + _buildComposerEditor(context), ]) ); } @@ -463,83 +445,121 @@ class ComposerView extends GetWidget with AppLoaderMixin, Ri padding: const EdgeInsets.only(left: 60, right: 25), child: Column(children: [ _buildToolbarRichTextWidget(context), - const SizedBox(height: 8), - Obx(() { - final uploadAttachments = controller.uploadController.listUploadAttachments; - if (uploadAttachments.isEmpty) { - return const SizedBox.shrink(); - } else { - return Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: _buildAttachmentsTitle(context, - uploadAttachments, - controller.expandModeAttachments.value)), - Padding( - padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), - child: _buildAttachmentsList(context, - uploadAttachments, - controller.expandModeAttachments.value)) - ]); - } - }), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10), - child: _buildComposerEditor(context)), + _buildAttachmentsWidget(context), + _buildInlineLoadingView(), + _buildComposerEditor(context), ]) ) ]) ); } + Widget _buildAttachmentsWidget(BuildContext context) { + return Obx(() { + final uploadAttachments = controller.uploadController.listUploadAttachments; + if (uploadAttachments.isEmpty) { + return const SizedBox.shrink(); + } else { + return Column(children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: _buildAttachmentsTitle(context, + uploadAttachments, + controller.expandModeAttachments.value)), + Padding( + padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), + child: _buildAttachmentsList(context, + uploadAttachments, + controller.expandModeAttachments.value)) + ]); + } + }); + } + + Widget _buildInlineLoadingView() { + return Obx(() => controller.uploadController.uploadInlineViewState.value.fold( + (failure) => const SizedBox.shrink(), + (success) { + if (success is UploadingAttachmentUploadState || success is DownloadingImageAsBase64) { + return Padding( + padding: const EdgeInsets.all(5), + child: loadingWidgetWithSizeColor( + size: 30, + color: AppColor.primaryColor)); + } + return const SizedBox.shrink(); + })); + } + Widget _buildToolbarRichTextWidget(BuildContext context) { - return Container( - padding: const EdgeInsets.only(left: 20, top: 15, bottom: 8), - alignment: Alignment.centerLeft, - child: Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: RichTextStyleType.values.map((textType) => Obx(() { - switch(textType) { - case RichTextStyleType.textColor: - return buildIconColorText( - iconData: textType.getIconData(), - colorSelected: controller.richTextMobileTabletController.selectedTextColor.value, - onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType)); - case RichTextStyleType.textBackgroundColor: - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), - child: buildIconColorBackgroundText( - iconData: textType.getIconData(), - colorSelected: controller.richTextMobileTabletController.selectedTextBackgroundColor.value, - tooltip: textType.getTooltipButton(context), - onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType)), - ); - case RichTextStyleType.fontName: - return Container( - width: 200, - padding: const EdgeInsets.only(right: 2), - child: DropDownButtonWidget( - items: SafeFont.values, - itemSelected: controller.richTextMobileTabletController.selectedFontName.value, - onChanged: (newFont) => controller.richTextMobileTabletController.applyNewFontStyle(newFont), - onMenuStateChange: (isOpen) { - final newStatus = isOpen - ? DropdownMenuFontStatus.open - : DropdownMenuFontStatus.closed; - controller.richTextMobileTabletController.menuFontStatus = newStatus; - }, - heightItem: 35, - sizeIconChecked: 16, - radiusButton: 5, - supportSelectionIcon: true)); - default: - return buildIconStyleText( - path: textType.getIcon(imagePaths), - isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(textType), - onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType)); - } - })).toList() + return Obx(() => Container( + padding: const EdgeInsets.only(left: 20, top: 8, bottom: 8), + alignment: Alignment.centerLeft, + child: Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Container( + width: 200, + padding: const EdgeInsets.only(right: 2), + child: DropDownButtonWidget( + items: SafeFont.values, + itemSelected: controller.richTextMobileTabletController.selectedFontName.value, + onChanged: (newFont) => controller.richTextMobileTabletController.applyNewFontStyle(newFont), + onMenuStateChange: (isOpen) { + final newStatus = isOpen + ? DropdownMenuFontStatus.open + : DropdownMenuFontStatus.closed; + controller.richTextMobileTabletController.menuFontStatus = newStatus; + }, + heightItem: 38, + sizeIconChecked: 16, + radiusButton: 5, + colorButton: Colors.white, + supportSelectionIcon: true)), + Padding( + padding: const EdgeInsets.only(left: 8), + child: buildWrapIconStyleText( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5), + icon: buildIconColorBackgroundTextWithoutTooltip( + iconData: RichTextStyleType.textColor.getIconData(), + colorSelected: controller.richTextMobileTabletController.selectedTextColor.value, + ), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.textColor)), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: buildWrapIconStyleText( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5), + icon: buildIconColorBackgroundTextWithoutTooltip( + iconData: RichTextStyleType.textBackgroundColor.getIconData(), + colorSelected: controller.richTextMobileTabletController.selectedTextBackgroundColor.value, + ), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)), + ), + buildWrapIconStyleText( + hasDropdown: false, + padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5), + icon: Wrap(children: [ + buildIconStyleText( + path: RichTextStyleType.bold.getIcon(imagePaths), + isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.bold), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.bold)), + buildIconStyleText( + path: RichTextStyleType.italic.getIcon(imagePaths), + isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.italic), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.italic)), + buildIconStyleText( + path: RichTextStyleType.underline.getIcon(imagePaths), + isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.underline), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.underline)), + buildIconStyleText( + path: RichTextStyleType.strikeThrough.getIcon(imagePaths), + isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.strikeThrough), + onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)) + ])), + ], + ), ), ); } @@ -592,14 +612,17 @@ class ComposerView extends GetWidget with AppLoaderMixin, Ri } Widget _buildHtmlEditor(String initialContent) { - return HtmlEditor( - key: const Key('composer_editor'), - minHeight: 550, - initialContent: initialContent, - onCreated: (editorApi) { - controller.richTextMobileTabletController.htmlEditorApi = editorApi; - controller.richTextMobileTabletController.listenHtmlEditorApi(); - }); + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10), + child: HtmlEditor( + key: const Key('composer_editor'), + minHeight: 550, + initialContent: initialContent, + onCreated: (editorApi) { + controller.richTextMobileTabletController.htmlEditorApi = editorApi; + controller.richTextMobileTabletController.listenHtmlEditorApi(); + }), + ); } Widget _buildAttachmentsTitle( diff --git a/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart b/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart index 8da428480..d98a641ac 100644 --- a/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart +++ b/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart @@ -80,6 +80,33 @@ mixin RichTextButtonMixin { ); } + Widget buildIcon({ + required String path, + Color? color, + double opacity = 1.0, + }){ + final newColor = color == Colors.white + ? AppColor.colorDefaultRichTextButton + : color; + + return SvgPicture.asset(path, + color: newColor?.withOpacity(opacity), + fit: BoxFit.fill); + } + + Widget buildIconColorBackgroundTextWithoutTooltip({ + required IconData? iconData, + required Color? colorSelected, + double opacity = 1.0, + }){ + final newColor = colorSelected == Colors.white + ? AppColor.colorDefaultRichTextButton + : colorSelected; + return Icon(iconData, + color: (newColor ?? AppColor.colorDefaultRichTextButton).withOpacity(opacity), + size: 20); + } + Widget buildIconColorBackgroundText({ required IconData? iconData, required Color? colorSelected,