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
@@ -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),
),
);
}
}