Files
workavia-mail-front/lib/features/composer/presentation/composer_view_web.dart
T
dab246 5f053be2de TF-2134 Open composer from mailto uri
(cherry picked from commit 964013fb35ee6a7ab98f7d7c4a1146d87843dd81)
2023-09-14 16:58:31 +07:00

495 lines
20 KiB
Dart

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: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/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/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 {
ComposerView({Key? key}) : super(key: key);
@override
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),
)),
]))
)
)
)),
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(
context,
constraints
)
))
)
)
)
)
)
);
}
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
)
)),
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),
]
),
);
}
@override
List<PopupMenuEntry> popUpMoreActionMenu(BuildContext context) {
return [
PopupMenuItem(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: PointerInterceptor(
child: IntrinsicHeight(
child: Row(
children: [
Obx(() => buildIconWeb(
icon: Icon(controller.hasRequestReadReceipt.value ? Icons.done : null, color: Colors.black))),
Expanded(
child: InkResponse(
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: Center(
child: Text(
AppLocalizations.of(context).requestReadReceipt,
style: const TextStyle(color: Colors.black, fontSize: 15),
)
),
),
),
),
]),
),
),
onTap: () {
controller.toggleRequestReadReceipt();
},
)
];
}
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),
),
);
}
}