TF-2116 Apply new design for web composer

(cherry picked from commit 1295eb59b2f5abd1095c00ade53c90f7d4f1ecd6)
This commit is contained in:
dab246
2023-09-06 11:53:26 +07:00
committed by Dat H. Pham
parent 0b3f72915a
commit 11dcc38fab
10 changed files with 820 additions and 790 deletions
@@ -25,9 +25,10 @@ import 'package:tmail_ui_user/features/base/widget/custom_scroll_behavior.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/mixin/composer_loading_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.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/composer/presentation/widgets/recipient_composer_widget.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/main/localizations/app_localizations.dart';
@@ -148,104 +149,65 @@ abstract class BaseComposerView extends GetWidget<ComposerController>
}
Widget buildEmailAddress(BuildContext context) {
return Column(
return Obx(() => Column(
children: [
Obx(() {
return (EmailAddressInputBuilder(
context,
imagePaths,
responsiveUtils,
PrefixEmailAddress.to,
controller.listToEmailAddress,
controller.listEmailAddressType,
expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
RecipientComposerWidget(
prefix: PrefixEmailAddress.to,
listEmailAddress: controller.listToEmailAddress,
ccState: controller.ccRecipientState.value,
bccState: controller.bccRecipientState.value,
expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyToEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfToEmailAddress(),
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onAddEmailAddressTypeAction: controller.addEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.cc,
listEmailAddress: controller.listCcEmailAddress,
expandMode: controller.ccAddressExpandMode.value,
controller: controller.ccEmailAddressController,
focusNode: controller.ccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyToEmailTagEditor,
keyTagEditor: controller.keyCcEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfToEmailAddress()
)
..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange)
..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress)
..addOnAddEmailAddressTypeAction(controller.addEmailAddressType)
..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress)
..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)
..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)
).build();
}),
Obx(() {
if (controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true) {
return const Divider(color: AppColor.colorDividerComposer, height: 1);
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.listEmailAddressType.contains(PrefixEmailAddress.cc) == true) {
return (EmailAddressInputBuilder(
context,
imagePaths,
responsiveUtils,
PrefixEmailAddress.cc,
controller.listCcEmailAddress,
controller.listEmailAddressType,
focusNode: controller.ccAddressFocusNode,
expandMode: controller.ccAddressExpandMode.value,
controller: controller.ccEmailAddressController,
keyTagEditor: controller.keyCcEmailTagEditor,
autoDisposeFocusNode: false,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfCcEmailAddress()
)
..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange)
..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress)
..addOnDeleteEmailAddressTypeAction(controller.deleteEmailAddressType)
..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress)
..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)
..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)
).build();
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) {
return const Divider(color: AppColor.colorDividerComposer, height: 1);
} else {
return const SizedBox.shrink();
}
}),
Obx(() {
if (controller.listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) {
return (EmailAddressInputBuilder(
context,
imagePaths,
responsiveUtils,
PrefixEmailAddress.bcc,
controller.listBccEmailAddress,
controller.listEmailAddressType,
focusNode: controller.bccAddressFocusNode,
expandMode: controller.bccAddressExpandMode.value,
controller: controller.bccEmailAddressController,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyBccEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.subjectEmailInputFocusNode
)
..addOnFocusEmailAddressChangeAction(controller.onEmailAddressFocusChange)
..addOnShowFullListEmailAddressAction(controller.showFullEmailAddress)
..addOnDeleteEmailAddressTypeAction(controller.deleteEmailAddressType)
..addOnUpdateListEmailAddressAction(controller.updateListEmailAddress)
..addOnSuggestionEmailAddress(controller.getAutoCompleteSuggestion)
..addOnFocusNextAddressAction(controller.handleFocusNextAddressAction)
).build();
} else {
return const SizedBox.shrink();
}
}),
nextFocusNode: controller.getNextFocusOfCcEmailAddress(),
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.bcc,
listEmailAddress: controller.listBccEmailAddress,
expandMode: controller.bccAddressExpandMode.value,
controller: controller.bccEmailAddressController,
focusNode: controller.bccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyBccEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.subjectEmailInputFocusNode,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
],
);
));
}
Widget buildSubjectEmail(BuildContext context) {
@@ -279,7 +241,7 @@ abstract class BaseComposerView extends GetWidget<ComposerController>
);
}
Widget buildDivider() => const Divider(color: AppColor.colorDividerComposer, height: 1);
Widget buildDivider() => const Divider(color: AppColor.colorLineComposer, height: 1);
Widget buildAttachmentsWidget(BuildContext context) {
return Obx(() {
@@ -50,6 +50,7 @@ import 'package:tmail_ui_user/features/composer/presentation/controller/rich_tex
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/save_to_draft_view_event.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
@@ -95,7 +96,6 @@ class ComposerController extends BaseController {
final composerArguments = Rxn<ComposerArguments>();
final isEnableEmailSendButton = false.obs;
final isInitialRecipient = false.obs;
final listEmailAddressType = <PrefixEmailAddress>[].obs;
final subjectEmail = Rxn<String>();
final screenDisplayMode = ScreenDisplayMode.normal.obs;
final toAddressExpandMode = ExpandMode.EXPAND.obs;
@@ -105,6 +105,8 @@ class ComposerController extends BaseController {
final listIdentities = <Identity>[].obs;
final emailContentsViewState = Rxn<Either<Failure, Success>>();
final hasRequestReadReceipt = false.obs;
final ccRecipientState = PrefixRecipientState.disabled.obs;
final bccRecipientState = PrefixRecipientState.disabled.obs;
final LocalFilePickerInteractor _localFilePickerInteractor;
final DeviceInfoPlugin _deviceInfoPlugin;
@@ -594,12 +596,12 @@ class ComposerController extends BaseController {
}
if (listCcEmailAddress.isNotEmpty) {
listEmailAddressType.add(PrefixEmailAddress.cc);
ccRecipientState.value = PrefixRecipientState.enabled;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
}
if (listBccEmailAddress.isNotEmpty) {
listEmailAddressType.add(PrefixEmailAddress.bcc);
bccRecipientState.value = PrefixRecipientState.enabled;
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
}
@@ -1375,18 +1377,28 @@ class ComposerController extends BaseController {
}
void addEmailAddressType(PrefixEmailAddress prefixEmailAddress) {
listEmailAddressType.add(prefixEmailAddress);
switch(prefixEmailAddress) {
case PrefixEmailAddress.cc:
ccRecipientState.value = PrefixRecipientState.enabled;
break;
case PrefixEmailAddress.bcc:
bccRecipientState.value = PrefixRecipientState.enabled;
break;
default:
break;
}
}
void deleteEmailAddressType(PrefixEmailAddress prefixEmailAddress) {
listEmailAddressType.remove(prefixEmailAddress);
updateListEmailAddress(prefixEmailAddress, <EmailAddress>[]);
switch(prefixEmailAddress) {
case PrefixEmailAddress.cc:
ccRecipientState.value = PrefixRecipientState.disabled;
ccAddressFocusNode = FocusNode();
ccEmailAddressController.clear();
break;
case PrefixEmailAddress.bcc:
bccRecipientState.value = PrefixRecipientState.disabled;
bccAddressFocusNode = FocusNode();
bccEmailAddressController.clear();
break;
@@ -1570,8 +1582,8 @@ class ComposerController extends BaseController {
}
void _applyBccEmailAddressFromIdentity(Set<EmailAddress> listEmailAddress) {
if (!listEmailAddressType.contains(PrefixEmailAddress.bcc)) {
listEmailAddressType.add(PrefixEmailAddress.bcc);
if (bccRecipientState.value == PrefixRecipientState.disabled) {
bccRecipientState.value = PrefixRecipientState.enabled;
}
listBccEmailAddress = listEmailAddress.toList();
toAddressExpandMode.value = ExpandMode.COLLAPSE;
@@ -1585,7 +1597,7 @@ class ComposerController extends BaseController {
.where((address) => !listEmailAddress.contains(address))
.toList();
if (listBccEmailAddress.isEmpty) {
listEmailAddressType.remove(PrefixEmailAddress.bcc);
bccRecipientState.value = PrefixRecipientState.disabled;
}
toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
@@ -1823,9 +1835,9 @@ class ComposerController extends BaseController {
}
FocusNode? getNextFocusOfToEmailAddress() {
if (listEmailAddressType.contains(PrefixEmailAddress.cc) == true) {
if (ccRecipientState.value == PrefixRecipientState.enabled) {
return ccAddressFocusNode;
} else if (listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) {
} else if (bccRecipientState.value == PrefixRecipientState.enabled) {
return bccAddressFocusNode;
} else {
return subjectEmailInputFocusNode;
@@ -1833,7 +1845,7 @@ class ComposerController extends BaseController {
}
FocusNode? getNextFocusOfCcEmailAddress() {
if (listEmailAddressType.contains(PrefixEmailAddress.bcc) == true) {
if (bccRecipientState.value == PrefixRecipientState.enabled) {
return bccAddressFocusNode;
} else {
return subjectEmailInputFocusNode;
@@ -1847,4 +1859,8 @@ class ComposerController extends BaseController {
bool get isNetworkConnectionAvailable => networkConnectionController.isNetworkConnectionAvailable();
UserProfile? get userProfile => mailboxDashBoardController.userProfile.value;
void openContextMenuOption(BuildContext context) {
}
}
@@ -1,25 +1,24 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/presentation/views/image/avatar_builder.dart';
import 'package:core/presentation/views/responsive/responsive_widget.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/email/prefix_email_address.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/widget/cupertino_loading_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/base_composer_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_editor_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/desktop_container_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/mobile_container_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/tablet_container_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/web_editor_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/app_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/bottom_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile_app_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/subject_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/transform_html_email_content_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
class ComposerView extends BaseComposerView {
@@ -29,185 +28,475 @@ class ComposerView extends BaseComposerView {
Widget build(BuildContext context) {
return ResponsiveWidget(
responsiveUtils: responsiveUtils,
mobile: Scaffold(
backgroundColor: Colors.white,
body: Container(
color: Colors.white,
child: LayoutBuilder(builder: (context, constraints) {
return PointerInterceptor(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildAppBar(context),
buildDivider(),
buildFromEmailAddress(context),
buildDivider(),
_buildEmailAddressWeb(context, constraints),
buildDivider(),
buildSubjectEmail(context),
buildDivider(),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 16),
child: _buildListButton(context, constraints)
),
buildDivider(),
buildAttachmentsWidget(context),
ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController,
padding: ComposerStyle.getRichTextButtonPadding(context, responsiveUtils),
),
buildInlineLoadingView(controller),
Expanded(child: _buildEditorForm(context))
]
));
})
)
),
desktop: Obx(() {
return Stack(children: [
if (controller.screenDisplayMode.value == ScreenDisplayMode.normal)
PositionedDirectional(end: 5, bottom: 5, child: Card(
elevation: 20,
color: Colors.transparent,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
child: Container(
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
width: responsiveUtils.getSizeScreenWidth(context) * 0.5,
height: responsiveUtils.getSizeScreenHeight(context) * 0.75,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)),
child: LayoutBuilder(builder: (context, constraints) =>
PointerInterceptor(child: _buildBodyForDesktop(context, constraints))
)
)
)
)),
if (controller.screenDisplayMode.value == ScreenDisplayMode.minimize)
PositionedDirectional(end: 5, bottom: 5, child: Card(
elevation: 20,
color: Colors.transparent,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
child: Container(
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(16))),
width: 500,
height: 50,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: PointerInterceptor(child: Row(children: [
Padding(
padding: const EdgeInsetsDirectional.only(start: 10),
child: buildIconWeb(
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).saveAndClose,
onTap: () => controller.saveToDraftAndClose(context)
)),
buildIconWeb(
icon: SvgPicture.asset(imagePaths.icFullScreenComposer, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).fullscreen,
onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.fullScreen)),
buildIconWeb(
icon: SvgPicture.asset(imagePaths.icChevronUp, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).show,
onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.normal)),
Expanded(child: Padding(
padding: const EdgeInsetsDirectional.only(start: 16, end: 80),
child: buildTitleComposer(context),
)),
]))
)
)
mobile: MobileContainerView(
childBuilder: (context, constraints) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() => MobileAppBarComposerWidget(
isCodeViewEnabled: controller.richTextWebController.codeViewEnabled,
isFormattingOptionsEnabled: controller.richTextWebController.isFormattingOptionsEnabled,
openRichToolbarAction: controller.richTextWebController.toggleFormattingOptions,
isSendButtonEnabled: controller.isEnableEmailSendButton.value,
onCloseViewAction: () => controller.saveToDraftAndClose(context),
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
sendMessageAction: () => controller.sendEmailAction(context),
openContextMenuAction: () => {},
)),
if (controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen)
Scaffold(
backgroundColor: Colors.black38,
body: Align(alignment: Alignment.center, child: Card(
color: Colors.transparent,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
child: Container(
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
width: responsiveUtils.getSizeScreenWidth(context) * 0.85,
height: responsiveUtils.getSizeScreenHeight(context) * 0.9,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)),
child: LayoutBuilder(builder: (context, constraints) =>
PointerInterceptor(child: _buildBodyForDesktop(context, constraints)))
)
)
)
)
)
]);
}),
tablet: Scaffold(
backgroundColor: Colors.black38,
body: Center(
child: Card(
elevation: 20,
margin: ComposerStyle.getMarginForTabletWeb(context, responsiveUtils),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(ComposerStyle.radius))),
width: ComposerStyle.getWidthForTabletWeb(context, responsiveUtils),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(ComposerStyle.radius)),
child: LayoutBuilder(builder: (context, constraints) => PointerInterceptor(
child: _buildBodyForDesktop(
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget(
context,
constraints
constraints,
responsiveUtils
)
))
)
)
)
)
)
);
}
Widget _buildAppBarDesktop(BuildContext context) {
return Container(
height: 52,
padding: const EdgeInsetsDirectional.symmetric(horizontal: 32),
child: Row(
children: [
buildIconWeb(
minSize: 40,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(imagePaths.icCloseMailbox, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).saveAndClose,
onTap: () => controller.saveToDraftAndClose(context)
),
if (responsiveUtils.isDesktop(context))
...[
Obx(() => buildIconWeb(
minSize: 40,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(
controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen
? imagePaths.icFullScreenExit
: imagePaths.icFullScreenComposer,
fit: BoxFit.fill
),
tooltip: AppLocalizations.of(context).fullscreen,
onTap: () => controller.displayScreenTypeComposerAction(
controller.screenDisplayMode.value == ScreenDisplayMode.fullScreen
? ScreenDisplayMode.normal
: ScreenDisplayMode.fullScreen
child: SingleChildScrollView(
controller: controller.scrollControllerEmailAddress,
child: Obx(() => Column(
children: [
RecipientComposerWidget(
prefix: PrefixEmailAddress.to,
listEmailAddress: controller.listToEmailAddress,
ccState: controller.ccRecipientState.value,
bccState: controller.bccRecipientState.value,
expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyToEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
padding: ComposerStyle.mobileRecipientPadding,
margin: ComposerStyle.mobileRecipientMargin,
nextFocusNode: controller.getNextFocusOfToEmailAddress(),
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onAddEmailAddressTypeAction: controller.addEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.cc,
listEmailAddress: controller.listCcEmailAddress,
expandMode: controller.ccAddressExpandMode.value,
controller: controller.ccEmailAddressController,
focusNode: controller.ccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyCcEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfCcEmailAddress(),
padding: ComposerStyle.mobileRecipientPadding,
margin: ComposerStyle.mobileRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.bcc,
listEmailAddress: controller.listBccEmailAddress,
expandMode: controller.bccAddressExpandMode.value,
controller: controller.bccEmailAddressController,
focusNode: controller.bccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyBccEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.subjectEmailInputFocusNode,
padding: ComposerStyle.mobileRecipientPadding,
margin: ComposerStyle.mobileRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
],
)),
)
)),
buildIconWeb(
minSize: 40,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(imagePaths.icMinimize, fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).minimize,
onTap: () => controller.displayScreenTypeComposerAction(ScreenDisplayMode.minimize)
),
],
Expanded(child: buildTitleComposer(context)),
const SizedBox(width: 120),
]
SubjectComposerWidget(
focusNode: controller.subjectEmailInputFocusNode,
textController: controller.subjectEmailInputController,
onTextChange: controller.setSubjectEmail,
padding: ComposerStyle.mobileSubjectPadding,
margin: ComposerStyle.mobileSubjectMargin,
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ComposerStyle.borderColor,
width: 1
)
),
color: ComposerStyle.backgroundEditorColor
),
child: Stack(
children: [
Obx(() => Padding(
padding: ComposerStyle.mobileEditorPadding,
child: WebEditorView(
editorController: controller.richTextWebController.editorController,
arguments: controller.composerArguments.value,
contentViewState: controller.emailContentsViewState.value,
currentWebContent: controller.textEditorWeb,
onInitial: controller.handleInitHtmlEditorWeb,
onChangeContent: controller.onChangeTextEditorWeb,
onFocus: controller.handleOnFocusHtmlEditorWeb,
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
),
)),
Obx(() {
if (controller.richTextWebController.isFormattingOptionsEnabled) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController,
padding: ComposerStyle.richToolbarPadding,
decoration: const BoxDecoration(
color: ComposerStyle.richToolbarColor,
boxShadow: ComposerStyle.richToolbarShadow
),
),
);
} else {
return const SizedBox.shrink();
}
})
],
),
)
),
]
);
}
),
desktop: Obx(() => DesktopContainerView(
childBuilder: (context, constraints) {
return Column(children: [
Obx(() => AppBarComposerWidget(
emailSubject: controller.subjectEmail.value ?? '',
displayMode: controller.screenDisplayMode.value,
onCloseViewAction: () => controller.saveToDraftAndClose(context),
onChangeDisplayModeAction: controller.displayScreenTypeComposerAction,
constraints: constraints,
)),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget(
context,
constraints,
responsiveUtils
)
),
child: SingleChildScrollView(
controller: controller.scrollControllerEmailAddress,
child: Obx(() => Column(
children: [
RecipientComposerWidget(
prefix: PrefixEmailAddress.to,
listEmailAddress: controller.listToEmailAddress,
ccState: controller.ccRecipientState.value,
bccState: controller.bccRecipientState.value,
expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyToEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
padding: ComposerStyle.desktopRecipientPadding,
margin: ComposerStyle.desktopRecipientMargin,
nextFocusNode: controller.getNextFocusOfToEmailAddress(),
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onAddEmailAddressTypeAction: controller.addEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.cc,
listEmailAddress: controller.listCcEmailAddress,
expandMode: controller.ccAddressExpandMode.value,
controller: controller.ccEmailAddressController,
focusNode: controller.ccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyCcEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfCcEmailAddress(),
padding: ComposerStyle.desktopRecipientPadding,
margin: ComposerStyle.desktopRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.bcc,
listEmailAddress: controller.listBccEmailAddress,
expandMode: controller.bccAddressExpandMode.value,
controller: controller.bccEmailAddressController,
focusNode: controller.bccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyBccEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.subjectEmailInputFocusNode,
padding: ComposerStyle.desktopRecipientPadding,
margin: ComposerStyle.desktopRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
],
)),
)
),
SubjectComposerWidget(
focusNode: controller.subjectEmailInputFocusNode,
textController: controller.subjectEmailInputController,
onTextChange: controller.setSubjectEmail,
padding: ComposerStyle.desktopSubjectPadding,
margin: ComposerStyle.desktopSubjectMargin,
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ComposerStyle.borderColor,
width: 1
)
),
color: ComposerStyle.backgroundEditorColor
),
child: Stack(
children: [
Obx(() => Padding(
padding: ComposerStyle.desktopEditorPadding,
child: WebEditorView(
editorController: controller.richTextWebController.editorController,
arguments: controller.composerArguments.value,
contentViewState: controller.emailContentsViewState.value,
currentWebContent: controller.textEditorWeb,
onInitial: controller.handleInitHtmlEditorWeb,
onChangeContent: controller.onChangeTextEditorWeb,
onFocus: controller.handleOnFocusHtmlEditorWeb,
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
),
)),
Obx(() {
if (controller.richTextWebController.isFormattingOptionsEnabled) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController,
padding: ComposerStyle.richToolbarPadding,
decoration: const BoxDecoration(
color: ComposerStyle.richToolbarColor,
boxShadow: ComposerStyle.richToolbarShadow
),
),
);
} else {
return const SizedBox.shrink();
}
})
],
),
)
),
Obx(() => BottomBarComposerWidget(
isCodeViewEnabled: controller.richTextWebController.codeViewEnabled,
isFormattingOptionsEnabled: controller.richTextWebController.isFormattingOptionsEnabled,
openRichToolbarAction: controller.richTextWebController.toggleFormattingOptions,
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
showCodeViewAction: controller.richTextWebController.toggleCodeView,
deleteComposerAction: () => controller.closeComposer(context),
saveToDraftAction: () => controller.saveToDraftAction(context),
sendMessageAction: () => controller.sendEmailAction(context),
)),
]);
},
displayMode: controller.screenDisplayMode.value,
emailSubject: controller.subjectEmail.value ?? '',
onCloseViewAction: () => controller.saveToDraftAndClose(context),
onChangeDisplayModeAction: controller.displayScreenTypeComposerAction,
)),
tablet: TabletContainerView(
childBuilder: (context, constraints) {
return Column(children: [
Obx(() => AppBarComposerWidget(
emailSubject: controller.subjectEmail.value ?? '',
onCloseViewAction: () => controller.saveToDraftAndClose(context),
constraints: constraints,
)),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget(
context,
constraints,
responsiveUtils
)
),
child: SingleChildScrollView(
controller: controller.scrollControllerEmailAddress,
child: Obx(() => Column(
children: [
RecipientComposerWidget(
prefix: PrefixEmailAddress.to,
listEmailAddress: controller.listToEmailAddress,
ccState: controller.ccRecipientState.value,
bccState: controller.bccRecipientState.value,
expandMode: controller.toAddressExpandMode.value,
controller: controller.toEmailAddressController,
focusNode: controller.toAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyToEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
padding: ComposerStyle.tabletRecipientPadding,
margin: ComposerStyle.tabletRecipientMargin,
nextFocusNode: controller.getNextFocusOfToEmailAddress(),
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onAddEmailAddressTypeAction: controller.addEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.ccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.cc,
listEmailAddress: controller.listCcEmailAddress,
expandMode: controller.ccAddressExpandMode.value,
controller: controller.ccEmailAddressController,
focusNode: controller.ccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyCcEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.getNextFocusOfCcEmailAddress(),
padding: ComposerStyle.tabletRecipientPadding,
margin: ComposerStyle.tabletRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
if (controller.bccRecipientState.value == PrefixRecipientState.enabled)
RecipientComposerWidget(
prefix: PrefixEmailAddress.bcc,
listEmailAddress: controller.listBccEmailAddress,
expandMode: controller.bccAddressExpandMode.value,
controller: controller.bccEmailAddressController,
focusNode: controller.bccAddressFocusNode,
autoDisposeFocusNode: false,
keyTagEditor: controller.keyBccEmailTagEditor,
isInitial: controller.isInitialRecipient.value,
nextFocusNode: controller.subjectEmailInputFocusNode,
padding: ComposerStyle.tabletRecipientPadding,
margin: ComposerStyle.tabletRecipientMargin,
onFocusEmailAddressChangeAction: controller.onEmailAddressFocusChange,
onShowFullListEmailAddressAction: controller.showFullEmailAddress,
onDeleteEmailAddressTypeAction: controller.deleteEmailAddressType,
onUpdateListEmailAddressAction: controller.updateListEmailAddress,
onSuggestionEmailAddress: controller.getAutoCompleteSuggestion,
onFocusNextAddressAction: controller.handleFocusNextAddressAction,
),
],
)),
)
),
SubjectComposerWidget(
focusNode: controller.subjectEmailInputFocusNode,
textController: controller.subjectEmailInputController,
onTextChange: controller.setSubjectEmail,
padding: ComposerStyle.tabletSubjectPadding,
margin: ComposerStyle.tabletSubjectMargin,
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ComposerStyle.borderColor,
width: 1
)
),
color: ComposerStyle.backgroundEditorColor
),
child: Stack(
children: [
Obx(() => Padding(
padding: ComposerStyle.tabletEditorPadding,
child: WebEditorView(
editorController: controller.richTextWebController.editorController,
arguments: controller.composerArguments.value,
contentViewState: controller.emailContentsViewState.value,
currentWebContent: controller.textEditorWeb,
onInitial: controller.handleInitHtmlEditorWeb,
onChangeContent: controller.onChangeTextEditorWeb,
onFocus: controller.handleOnFocusHtmlEditorWeb,
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
),
)),
Obx(() {
if (controller.richTextWebController.isFormattingOptionsEnabled) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController,
padding: ComposerStyle.richToolbarPadding,
decoration: const BoxDecoration(
color: ComposerStyle.richToolbarColor,
boxShadow: ComposerStyle.richToolbarShadow
),
),
);
} else {
return const SizedBox.shrink();
}
})
],
),
)
),
Obx(() => BottomBarComposerWidget(
isCodeViewEnabled: controller.richTextWebController.codeViewEnabled,
isFormattingOptionsEnabled: controller.richTextWebController.isFormattingOptionsEnabled,
openRichToolbarAction: controller.richTextWebController.toggleFormattingOptions,
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
showCodeViewAction: controller.richTextWebController.toggleCodeView,
deleteComposerAction: () => controller.closeComposer(context),
saveToDraftAction: () => controller.saveToDraftAction(context),
sendMessageAction: () => controller.sendEmailAction(context),
)),
]);
},
)
);
}
@@ -245,251 +534,4 @@ class ComposerView extends BaseComposerView {
)
];
}
Widget _buildBottomBarDesktop(BuildContext context, BoxConstraints constraints) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 16),
color: Colors.white,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: constraints.widthConstraints(),
child: buildBottomBar(context),
),
),
);
}
Widget _buildBodyForDesktop(BuildContext context, BoxConstraints constraints) {
return Column(children: [
_buildAppBarDesktop(context),
buildDivider(),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 16),
Padding(
padding: const EdgeInsets.only(top: 20),
child: (AvatarBuilder()
..text(controller.mailboxDashBoardController.userProfile.value?.getAvatarText() ?? '')
..size(56)
..addTextStyle(const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 28,
color: Colors.white
))
..backgroundColor(AppColor.colorAvatar)
).build()
),
const SizedBox(width: 16),
Expanded(child: Column(children: [
buildFromEmailAddress(context),
buildDivider(),
_buildEmailAddressWeb(context, constraints),
buildDivider(),
buildSubjectEmail(context),
buildDivider(),
_buildListButton(context, constraints),
]))
]
),
buildDivider(),
buildAttachmentsWidget(context),
ToolbarRichTextWebBuilder(
richTextWebController: controller.richTextWebController,
padding: ComposerStyle.getRichTextButtonPadding(context, responsiveUtils),
),
buildInlineLoadingView(controller),
Expanded(child: _buildEditorForm(context)),
buildDivider(),
_buildBottomBarDesktop(context, constraints),
]);
}
Widget _buildEmailAddressWeb(BuildContext context, BoxConstraints constraints) {
return ConstrainedBox(
constraints: BoxConstraints(maxHeight: ComposerStyle.getMaxHeightEmailAddressWidget(context, constraints, responsiveUtils)),
child: SingleChildScrollView(
controller: controller.scrollControllerEmailAddress,
child: buildEmailAddress(context),
)
);
}
Widget _buildListButton(BuildContext context, BoxConstraints constraints) {
return Transform(
transform: Matrix4.translationValues(
DirectionUtils.isDirectionRTLByLanguage(context) ? 0.0 : -5.0,
0.0,
0.0
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Row(children: [
buildIconWeb(
minSize: 40,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(imagePaths.icAttachmentsComposer,
width: 24,
height: 24,
colorFilter: AppColor.colorTextButton.asFilter(),
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).attach_file,
onTap: () => controller.openFilePickerByType(context, FileType.any)
),
const SizedBox(width: 4),
Obx(() {
final opacity = controller.richTextWebController.codeViewEnabled ? 0.5 : 1.0;
return AbsorbPointer(
absorbing: controller.richTextWebController.codeViewEnabled,
child: buildIconWeb(
minSize: 40,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(
imagePaths.icInsertImage,
colorFilter: AppColor.colorTextButton.withOpacity(opacity).asFilter(),
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).insertImage,
onTap: () => controller.insertImage(context, constraints.maxWidth)
),
);
}),
const SizedBox(width: 4),
Obx(() {
return buildIconWeb(
minSize: 40,
colorSelected: controller.richTextWebController.codeViewEnabled
? AppColor.colorSelectedRichTextButton
: Colors.transparent,
iconPadding: EdgeInsets.zero,
icon: SvgPicture.asset(
imagePaths.icStyleCodeView,
colorFilter: AppColor.colorTextButton.asFilter(),
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).codeView,
onTap: controller.richTextWebController.toggleCodeView);
}),
])
)
);
}
Widget _buildEditorForm(BuildContext context) {
return Obx(() {
final argsComposer = controller.composerArguments.value;
if (argsComposer == null) {
return const SizedBox.shrink();
}
final currentTextEditor = controller.textEditorWeb;
switch(argsComposer.emailActionType) {
case EmailActionType.compose:
case EmailActionType.composeFromEmailAddress:
case EmailActionType.composeFromMailtoURL:
case EmailActionType.composeFromFileShared:
return _buildHtmlEditor(
context,
currentTextEditor ?? HtmlExtension.editorStartTags
);
case EmailActionType.editDraft:
case EmailActionType.editSendingEmail:
case EmailActionType.composeFromContentShared:
case EmailActionType.reopenComposerBrowser:
final emailContentsViewState = controller.emailContentsViewState.value;
if (emailContentsViewState == null) {
return const SizedBox.shrink();
}
return emailContentsViewState.fold(
(failure) => _buildHtmlEditor(
context,
currentTextEditor ?? HtmlExtension.editorStartTags
),
(success) {
if (success is GetEmailContentLoading) {
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
} else if (success is GetEmailContentSuccess) {
var contentHtml = success.htmlEmailContent;
if (contentHtml.isEmpty == true) {
contentHtml = HtmlExtension.editorStartTags;
}
return _buildHtmlEditor(
context,
currentTextEditor ?? contentHtml
);
} else {
return _buildHtmlEditor(
context,
currentTextEditor ?? HtmlExtension.editorStartTags
);
}
});
case EmailActionType.reply:
case EmailActionType.replyAll:
case EmailActionType.forward:
final emailContentsViewState = controller.emailContentsViewState.value;
if (emailContentsViewState == null) {
return const SizedBox.shrink();
}
return emailContentsViewState.fold(
(failure) {
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
context: context,
emailContent: '',
emailActionType: argsComposer.emailActionType,
presentationEmail: argsComposer.presentationEmail!
);
return _buildHtmlEditor(
context,
currentTextEditor ?? emailContentQuoted
);
},
(success) {
if (success is TransformHtmlEmailContentLoading) {
return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0));
} else if (success is TransformHtmlEmailContentSuccess) {
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
context: context,
emailContent: success.htmlContent,
emailActionType: argsComposer.emailActionType,
presentationEmail: argsComposer.presentationEmail!
);
return _buildHtmlEditor(
context,
currentTextEditor ?? emailContentQuoted
);
} else {
final emailContentQuoted = controller.getEmailContentQuotedAsHtml(
context: context,
emailContent: '',
emailActionType: argsComposer.emailActionType,
presentationEmail: argsComposer.presentationEmail!
);
return _buildHtmlEditor(
context,
currentTextEditor ?? emailContentQuoted
);
}
}
);
default:
return _buildHtmlEditor(
context,
currentTextEditor ?? HtmlExtension.editorStartTags
);
}
});
}
Widget _buildHtmlEditor(BuildContext context, String initContent) {
return Padding(
padding: ComposerStyle.getEditorPadding(context, responsiveUtils),
child: EmailEditorWidget(
controller: controller,
content: initContent,
direction: AppUtils.getCurrentDirection(context),
),
);
}
}
@@ -10,6 +10,7 @@ import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/code_view_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/formatting_options_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
@@ -35,6 +36,7 @@ class RichTextWebController extends BaseRichTextController {
final focusMenuParagraph = RxBool(false);
final menuFontStatus = DropdownMenuFontStatus.closed.obs;
final menuHeaderStyleStatus = DropdownMenuFontStatus.closed.obs;
final formattingOptionsState = FormattingOptionsState.disabled.obs;
final menuParagraphController = CustomPopupMenuController();
final menuOrderListController = CustomPopupMenuController();
@@ -271,6 +273,16 @@ class RichTextWebController extends BaseRichTextController {
}
}
void toggleFormattingOptions() {
final newState = isFormattingOptionsEnabled
? FormattingOptionsState.disabled
: FormattingOptionsState.enabled;
formattingOptionsState.value = newState;
}
bool get isFormattingOptionsEnabled => formattingOptionsState.value == FormattingOptionsState.enabled;
@override
void onClose() {
menuParagraphController.dispose();
@@ -0,0 +1,5 @@
enum FormattingOptionsState {
enabled,
disabled
}
@@ -0,0 +1,5 @@
enum PrefixRecipientState {
enabled,
disabled
}
@@ -1,11 +1,43 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ComposerStyle {
static const double radius = 28;
static const double radius = 24;
static const Color borderColor = AppColor.colorLineComposer;
static const Color backgroundEditorColor = Colors.white;
static const Color richToolbarColor = Colors.white;
static const EdgeInsetsGeometry richToolbarPadding = EdgeInsetsDirectional.symmetric(horizontal: 24, vertical: 8);
static const EdgeInsetsGeometry desktopRecipientPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry desktopRecipientMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry desktopSubjectMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry desktopSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
static const EdgeInsetsGeometry desktopEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
static const EdgeInsetsGeometry tabletRecipientPadding = EdgeInsetsDirectional.only(end: 24);
static const EdgeInsetsGeometry tabletRecipientMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry tabletSubjectMargin = EdgeInsetsDirectional.only(start: 24);
static const EdgeInsetsGeometry tabletSubjectPadding = EdgeInsetsDirectional.only(end: 24, top: 12, bottom: 12);
static const EdgeInsetsGeometry tabletEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 20);
static const EdgeInsetsGeometry mobileRecipientPadding = EdgeInsetsDirectional.only(end: 16);
static const EdgeInsetsGeometry mobileRecipientMargin = EdgeInsetsDirectional.only(start: 16);
static const EdgeInsetsGeometry mobileSubjectMargin = EdgeInsetsDirectional.only(start: 16);
static const EdgeInsetsGeometry mobileSubjectPadding = EdgeInsetsDirectional.only(end: 16, top: 12, bottom: 12);
static const EdgeInsetsGeometry mobileEditorPadding = EdgeInsetsDirectional.symmetric(horizontal: 12);
static const List<BoxShadow> richToolbarShadow = [
BoxShadow(
color: AppColor.colorShadowBgContentEmail,
blurRadius: 24
),
BoxShadow(
color: AppColor.colorShadowBgContentEmail,
blurRadius: 2
),
];
static EdgeInsetsGeometry getAppBarPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
@@ -23,38 +55,6 @@ class ComposerStyle {
}
}
static EdgeInsetsGeometry getToAddressPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (PlatformInfo.isWeb) {
if (responsiveUtils.isMobile(context)) {
return const EdgeInsetsDirectional.only(start: 16, end: 8);
} else {
return const EdgeInsetsDirectional.symmetric(horizontal: 8);
}
} else {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
return const EdgeInsetsDirectional.symmetric(horizontal: 16);
} else {
return const EdgeInsetsDirectional.only(start: 8, end: 16);
}
}
}
static EdgeInsetsGeometry getCcBccAddressPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (PlatformInfo.isWeb) {
if (responsiveUtils.isMobile(context)) {
return const EdgeInsetsDirectional.only(start: 16);
} else {
return const EdgeInsetsDirectional.symmetric(horizontal: 8);
}
} else {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
return const EdgeInsetsDirectional.only(start: 16);
} else {
return const EdgeInsetsDirectional.symmetric(horizontal: 8);
}
}
}
static EdgeInsetsGeometry getSubjectPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
return const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 8);
@@ -71,14 +71,6 @@ class ComposerStyle {
}
}
static EdgeInsetsGeometry getPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
return const EdgeInsetsDirectional.symmetric(horizontal: 8);
} else {
return const EdgeInsetsDirectional.symmetric(horizontal: 32);
}
}
static double getAppBarHeight(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isPortraitMobile(context) || responsiveUtils.isLandscapeMobile(context)) {
return 57;
@@ -103,14 +95,6 @@ class ComposerStyle {
}
}
static EdgeInsetsGeometry getRichTextButtonPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isMobile(context)) {
return const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 8);
} else {
return const EdgeInsetsDirectional.only(start: 88, end: 48, top: 8, bottom: 8);
}
}
static EdgeInsetsGeometry getEditorPadding(BuildContext context, ResponsiveUtils responsiveUtils) {
if (PlatformInfo.isWeb) {
if (responsiveUtils.isMobile(context)) {
@@ -144,23 +128,6 @@ class ComposerStyle {
}
}
static EdgeInsetsGeometry getMarginForTabletWeb(BuildContext context, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isTablet(context)) {
return const EdgeInsetsDirectional.all(24);
} else {
return const EdgeInsetsDirectional.symmetric(vertical: 24);
}
}
static double getWidthForTabletWeb(BuildContext context, ResponsiveUtils responsiveUtils) {
final currentWidth = responsiveUtils.getSizeScreenWidth(context);
if (responsiveUtils.isTablet(context)) {
return currentWidth;
} else {
return currentWidth * 0.7;
}
}
static int getMaxItemRowListAttachment(BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth < ResponsiveUtils.minTabletWidth) {
return 2;
@@ -175,7 +142,6 @@ class ComposerStyle {
return constraints.maxWidth / getMaxItemRowListAttachment(context, constraints);
}
static double getMaxHeightEmailAddressWidget(BuildContext context, BoxConstraints constraints, ResponsiveUtils responsiveUtils) {
if (responsiveUtils.isDesktop(context)) {
return constraints.maxHeight > 0 ? constraints.maxHeight * 0.3 : 150.0;
@@ -4,6 +4,7 @@ import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.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';
@@ -22,12 +23,16 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin
final ImagePaths _imagePaths = Get.find<ImagePaths>();
final EdgeInsetsGeometry? padding;
final List<Widget>? extendedOption;
final AlignmentGeometry? alignment;
final Decoration? decoration;
ToolbarRichTextWebBuilder({
Key? key,
required this.richTextWebController,
this.padding,
this.extendedOption,
this.alignment,
this.decoration,
}) : super(key: key);
@override
@@ -36,187 +41,190 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin
final codeViewEnabled = richTextWebController.codeViewEnabled;
final opacity = codeViewEnabled ? 0.5 : 1.0;
return Container(
padding: padding ?? const EdgeInsetsDirectional.only(start: 20, top: 8, bottom: 8),
alignment: AlignmentDirectional.centerStart,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 8,
children: [
if (extendedOption?.isNotEmpty == true)
...extendedOption!,
AbsorbPointer(
absorbing: codeViewEnabled,
child: DropDownMenuHeaderStyleWidget(
icon: buildWrapIconStyleText(
isSelected: richTextWebController.isMenuHeaderStyleOpen,
icon: SvgPicture.asset(
RichTextStyleType.headerStyle.getIcon(_imagePaths),
colorFilter: AppColor.colorDefaultRichTextButton.withOpacity(opacity).asFilter(),
fit: BoxFit.fill),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
tooltip: RichTextStyleType.headerStyle.getTooltipButton(context)
return PointerInterceptor(
child: Container(
padding: padding ?? const EdgeInsetsDirectional.only(start: 20, top: 8, bottom: 8),
decoration: decoration,
width: double.infinity,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 8,
children: [
if (extendedOption?.isNotEmpty == true)
...extendedOption!,
AbsorbPointer(
absorbing: codeViewEnabled,
child: DropDownMenuHeaderStyleWidget(
icon: buildWrapIconStyleText(
isSelected: richTextWebController.isMenuHeaderStyleOpen,
icon: SvgPicture.asset(
RichTextStyleType.headerStyle.getIcon(_imagePaths),
colorFilter: AppColor.colorDefaultRichTextButton.withOpacity(opacity).asFilter(),
fit: BoxFit.fill),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
tooltip: RichTextStyleType.headerStyle.getTooltipButton(context)
),
items: HeaderStyleType.values,
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuHeaderStyleStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuHeaderStyleStatus.value = newStatus;
},
onChanged: (newStyle) => richTextWebController.applyHeaderStyle(newStyle)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: Container(
width: 130,
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
child: DropDownButtonWidget<FontNameType>(
items: FontNameType.values,
itemSelected: richTextWebController.selectedFontName.value,
onChanged: (newFont) => richTextWebController.applyNewFontStyle(newFont),
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuFontStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuFontStatus.value = newStatus;
},
heightItem: 40,
sizeIconChecked: 16,
radiusButton: 8,
opacity: opacity,
dropdownWidth: 200,
colorButton: richTextWebController.isMenuFontOpen
? AppColor.colorBackgroundWrapIconStyleCode
: Colors.white,
iconArrowDown: SvgPicture.asset(_imagePaths.icStyleArrowDown),
tooltip: RichTextStyleType.fontName.getTooltipButton(context),
supportSelectionIcon: true)),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
icon: buildIconWithTooltip(
path: RichTextStyleType.textColor.getIcon(_imagePaths),
color: richTextWebController.selectedTextColor.value,
tooltip: RichTextStyleType.textColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textColor)),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 7),
spacing: 3,
icon: buildIconColorBackgroundText(
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
colorSelected: richTextWebController.selectedTextBackgroundColor.value,
tooltip: RichTextStyleType.textBackgroundColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: buildWrapIconStyleText(
hasDropdown: false,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
icon: Wrap(children: [
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.bold.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.bold),
tooltip: RichTextStyleType.bold.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.bold)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.italic.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.italic),
tooltip: RichTextStyleType.italic.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.italic)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.underline.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.underline),
tooltip: RichTextStyleType.underline.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.underline)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.strikeThrough.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(
RichTextStyleType.strikeThrough),
tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)),
)
])),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuParagraphController,
listButtonAction: ParagraphType.values
.map((paragraph) => paragraph.buildButtonWidget(
context,
_imagePaths,
(paragraph) => richTextWebController.applyParagraphType(paragraph)))
.toList(),
iconButton: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
spacing: 3,
isSelected: richTextWebController.focusMenuParagraph.value,
icon: buildIconWithTooltip(
path: richTextWebController.selectedParagraph.value.getIcon(_imagePaths),
color: AppColor.colorDefaultRichTextButton,
opacity: opacity,
tooltip: RichTextStyleType.paragraph.getTooltipButton(context))),
),
items: HeaderStyleType.values,
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuHeaderStyleStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuHeaderStyleStatus.value = newStatus;
},
onChanged: (newStyle) => richTextWebController.applyHeaderStyle(newStyle)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: Container(
width: 130,
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
child: DropDownButtonWidget<FontNameType>(
items: FontNameType.values,
itemSelected: richTextWebController.selectedFontName.value,
onChanged: (newFont) => richTextWebController.applyNewFontStyle(newFont),
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuFontStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuFontStatus.value = newStatus;
},
heightItem: 40,
sizeIconChecked: 16,
radiusButton: 8,
opacity: opacity,
dropdownWidth: 200,
colorButton: richTextWebController.isMenuFontOpen
? AppColor.colorBackgroundWrapIconStyleCode
: Colors.white,
iconArrowDown: SvgPicture.asset(_imagePaths.icStyleArrowDown),
tooltip: RichTextStyleType.fontName.getTooltipButton(context),
supportSelectionIcon: true)),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
icon: buildIconWithTooltip(
path: RichTextStyleType.textColor.getIcon(_imagePaths),
color: richTextWebController.selectedTextColor.value,
tooltip: RichTextStyleType.textColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textColor)),
),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 7),
spacing: 3,
icon: buildIconColorBackgroundText(
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
colorSelected: richTextWebController.selectedTextBackgroundColor.value,
tooltip: RichTextStyleType.textBackgroundColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: buildWrapIconStyleText(
hasDropdown: false,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
icon: Wrap(children: [
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.bold.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.bold),
tooltip: RichTextStyleType.bold.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.bold)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.italic.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.italic),
tooltip: RichTextStyleType.italic.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.italic)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.underline.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.underline),
tooltip: RichTextStyleType.underline.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.underline)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.strikeThrough.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(
RichTextStyleType.strikeThrough),
tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)),
)
])),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuParagraphController,
listButtonAction: ParagraphType.values
.map((paragraph) => paragraph.buildButtonWidget(
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuOrderListController,
listButtonAction: OrderListType.values
.map((orderType) => orderType.buildButtonWidget(
context,
_imagePaths,
(paragraph) => richTextWebController.applyParagraphType(paragraph)))
.toList(),
iconButton: buildWrapIconStyleText(
(orderType) => richTextWebController.applyOrderListType(orderType)))
.toList(),
iconButton: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
spacing: 3,
isSelected: richTextWebController.focusMenuParagraph.value,
isSelected: richTextWebController.focusMenuOrderList.value,
icon: buildIconWithTooltip(
path: richTextWebController.selectedParagraph.value.getIcon(_imagePaths),
path: richTextWebController.selectedOrderList.value.getIcon(_imagePaths),
color: AppColor.colorDefaultRichTextButton,
opacity: opacity,
tooltip: RichTextStyleType.paragraph.getTooltipButton(context))),
tooltip: RichTextStyleType.orderList.getTooltipButton(context))),
),
),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuOrderListController,
listButtonAction: OrderListType.values
.map((orderType) => orderType.buildButtonWidget(
context,
_imagePaths,
(orderType) => richTextWebController.applyOrderListType(orderType)))
.toList(),
iconButton: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
spacing: 3,
isSelected: richTextWebController.focusMenuOrderList.value,
icon: buildIconWithTooltip(
path: richTextWebController.selectedOrderList.value.getIcon(_imagePaths),
color: AppColor.colorDefaultRichTextButton,
opacity: opacity,
tooltip: RichTextStyleType.orderList.getTooltipButton(context))),
),
),
)
]
)
]
),
),
);
});
+13 -7
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2023-08-31T14:39:56.761446",
"@@last_modified": "2023-09-06T11:36:27.238808",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -1686,12 +1686,6 @@
"placeholders_order": [],
"placeholders": {}
},
"codeView": "Code view",
"@codeView": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"headerStyle": "Style",
"@headerStyle": {
"type": "text",
@@ -3217,5 +3211,17 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"formattingOptions": "Formatting options",
"@formattingOptions": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"embedCode": "Embed code",
"@embedCode": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
}
}
+14 -6
View File
@@ -1721,12 +1721,6 @@ class AppLocalizations {
name: 'formatTextBackgroundColor');
}
String get codeView {
return Intl.message(
'Code view',
name: 'codeView');
}
String get headerStyle {
return Intl.message(
'Style',
@@ -3317,4 +3311,18 @@ class AppLocalizations {
name: 'addCondition',
);
}
String get formattingOptions {
return Intl.message(
'Formatting options',
name: 'formattingOptions'
);
}
String get embedCode {
return Intl.message(
'Embed code',
name: 'embedCode'
);
}
}