Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2 Cherry pick add style/attachment/inline image button to app bar composer on mobile (#2532, #2584) #2794

2 changes: 1 addition & 1 deletion assets/images/ic_send_disable.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 30 additions & 21 deletions lib/features/composer/presentation/composer_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/app_
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/from_composer_mobile_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/landscape_app_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/tablet_app_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/tablet_bottom_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/web/desktop_app_bar_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/web/from_composer_drop_down_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
Expand All @@ -36,18 +36,8 @@ class ComposerView extends GetWidget<ComposerController> {
return ResponsiveWidget(
responsiveUtils: controller.responsiveUtils,
mobile: MobileContainerView(
keyboardRichTextController: controller.richTextMobileTabletController!.richTextController,
onCloseViewAction: () => controller.handleClickCloseComposer(context),
onClearFocusAction: () => controller.clearFocus(context),
onAttachFileAction: () => controller.isNetworkConnectionAvailable
? controller.openPickAttachmentMenu(
context,
_pickAttachmentsActionTiles(context)
)
: null,
onInsertImageAction: (constraints) => controller.isNetworkConnectionAvailable
? controller.insertImage(context, constraints.maxWidth)
: null,
backgroundColor: MobileAppBarComposerWidgetStyle.backgroundColor,
childBuilder: (context, constraints) => SafeArea(
left: !controller.responsiveUtils.isLandscapeMobile(context),
Expand All @@ -69,6 +59,17 @@ class ComposerView extends GetWidget<ComposerController> {
radius: ComposerStyle.popupMenuRadius
);
},
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
attachFileAction: () => controller.openPickAttachmentMenu(
context,
_pickAttachmentsActionTiles(context)
),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
openRichToolbarAction: () =>
controller.richTextMobileTabletController?.showFormatStyleBottomSheet(
context: context,
richTextController: controller.richTextMobileTabletController?.richTextController
),
))
else
Obx(() => AppBarComposerWidget(
Expand All @@ -83,6 +84,17 @@ class ComposerView extends GetWidget<ComposerController> {
radius: ComposerStyle.popupMenuRadius
);
},
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
attachFileAction: () => controller.openPickAttachmentMenu(
context,
_pickAttachmentsActionTiles(context)
),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
openRichToolbarAction: () =>
controller.richTextMobileTabletController?.showFormatStyleBottomSheet(
context: context,
richTextController: controller.richTextMobileTabletController?.richTextController
),
)),
Expanded(
child: SafeArea(
Expand Down Expand Up @@ -231,23 +243,20 @@ class ComposerView extends GetWidget<ComposerController> {
keyboardRichTextController: controller.richTextMobileTabletController!.richTextController,
onCloseViewAction: () => controller.handleClickCloseComposer(context),
onClearFocusAction: () => controller.clearFocus(context),
onAttachFileAction: () => controller.isNetworkConnectionAvailable
? controller.openPickAttachmentMenu(
context,
_pickAttachmentsActionTiles(context)
)
: null,
onInsertImageAction: (constraints) => controller.isNetworkConnectionAvailable
? controller.insertImage(context, constraints.maxWidth)
: null,
childBuilder: (context, constraints) => Container(
color: ComposerStyle.mobileBackgroundColor,
child: Column(
children: [
Obx(() => DesktopAppBarComposerWidget(
Obx(() => TabletAppBarComposerWidget(
emailSubject: controller.subjectEmail.value ?? '',
onCloseViewAction: () => controller.handleClickCloseComposer(context),
constraints: constraints,
isNetworkConnectionAvailable: controller.isNetworkConnectionAvailable,
attachFileAction: () => controller.openPickAttachmentMenu(
context,
_pickAttachmentsActionTiles(context)
),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
)),
Expanded(
child: SingleChildScrollView(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import 'dart:io';

import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:rich_text_composer/rich_text_composer.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';

class RichTextMobileTabletController extends BaseRichTextController {
HtmlEditorApi? htmlEditorApi;

final RichTextController richTextController = RichTextController();

void insertImage(InlineImage inlineImage) async {
if (inlineImage.fileInfo.isShared == true) {
await htmlEditorApi?.moveCursorAtLastNode();
bool isEditorFocused = await htmlEditorApi?.hasFocus() ?? false;
log('RichTextMobileTabletController::insertImage: isEditorFocused = $isEditorFocused');
if (!isEditorFocused) {
await htmlEditorApi?.requestFocusLastChild();
}
if (inlineImage.base64Uri?.isNotEmpty == true) {
await htmlEditorApi?.insertHtml(inlineImage.base64Uri!);
await htmlEditorApi?.insertHtml('${inlineImage.base64Uri ?? ''}<br/><br/>');
}
}

Expand All @@ -44,6 +49,28 @@ class RichTextMobileTabletController extends BaseRichTextController {
}
}

Future<void> showFormatStyleBottomSheet({
required BuildContext context,
required RichTextController? richTextController
}) async {
if (Platform.isAndroid) {
await htmlEditorApi?.storeSelectionRange();
KeyboardUtils.hideSystemKeyboardMobile();
} else {
await htmlEditorApi?.unfocus();
}

if (context.mounted) {
richTextController?.showRichTextBottomSheet(
context: context,
titleFormatBottomSheet: AppLocalizations.of(context).titleFormat,
titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles,
titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground,
titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground,
);
}
}

@override
void onClose() {
richTextController.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import 'package:flutter/material.dart';
class MobileAppBarComposerWidgetStyle {
static const double height = 56;
static const double iconSize = 24;
static const double space = 12;
static const double iconRadius = 8;
static const double sendButtonIconSize = 28;
static const double space = 4;
static const double sendButtonIconSize = 30;
static const double richTextIconSize = 28;

static const Color backgroundColor = AppColor.colorComposerAppBar;
Expand All @@ -17,5 +16,5 @@ class MobileAppBarComposerWidgetStyle {

static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.symmetric(horizontal: 12);
static const EdgeInsetsGeometry iconPadding = EdgeInsetsDirectional.all(3);
static const EdgeInsetsGeometry richTextIconPadding = EdgeInsetsDirectional.all(2);
static const EdgeInsetsGeometry richTextIconPadding = EdgeInsetsDirectional.all(5);
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rich_text_composer/rich_text_composer.dart' as rich_composer;
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
import 'package:core/utils/app_logger.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/mobile/mobile_container_view_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';

typedef OnInsertImageAction = Function(BoxConstraints constraints);

class MobileContainerView extends StatelessWidget {

final Widget Function(BuildContext context, BoxConstraints constraints) childBuilder;
final rich_composer.RichTextController keyboardRichTextController;
final VoidCallback onCloseViewAction;
final VoidCallback? onAttachFileAction;
final OnInsertImageAction? onInsertImageAction;
final VoidCallback? onClearFocusAction;
final Color? backgroundColor;

final _responsiveUtils = Get.find<ResponsiveUtils>();

MobileContainerView({
const MobileContainerView({
super.key,
required this.childBuilder,
required this.keyboardRichTextController,
required this.onCloseViewAction,
this.onAttachFileAction,
this.onInsertImageAction,
this.onClearFocusAction,
this.backgroundColor,
});
Expand All @@ -48,25 +33,7 @@ class MobileContainerView extends StatelessWidget {
backgroundColor: backgroundColor ?? MobileContainerViewStyle.outSideBackgroundColor,
resizeToAvoidBottomInset: false,
body: LayoutBuilder(builder: (context, constraints) {
return rich_composer.KeyboardRichText(
richTextController: keyboardRichTextController,
keyBroadToolbar: RichTextKeyboardToolBar(
backgroundKeyboardToolBarColor: MobileContainerViewStyle.keyboardToolbarBackgroundColor,
isLandScapeMode: _responsiveUtils.isLandscapeMobile(context),
insertAttachment: onAttachFileAction,
insertImage: () => onInsertImageAction != null
? onInsertImageAction!(constraints)
: null,
richTextController: keyboardRichTextController,
titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles,
titleBackgroundBottomSheet: AppLocalizations.of(context).titleBackground,
titleForegroundBottomSheet: AppLocalizations.of(context).titleForeground,
titleFormatBottomSheet: AppLocalizations.of(context).titleFormat,
titleBack: AppLocalizations.of(context).format,
),
paddingChild: EdgeInsets.zero,
child: childBuilder(context, constraints),
);
return childBuilder(context, constraints);
})
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import 'package:get/get.dart';
import 'package:rich_text_composer/rich_text_composer.dart';
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/mobile/tablet_container_view_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/mobile/mobile_container_view.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';

typedef OnInsertImageAction = Function(BoxConstraints constraints);

class TabletContainerView extends StatelessWidget {

final Widget Function(BuildContext context, BoxConstraints constraints) childBuilder;
Expand Down Expand Up @@ -50,8 +51,8 @@ class TabletContainerView extends StatelessWidget {
backgroundKeyboardToolBarColor: TabletContainerViewStyle.keyboardToolbarBackgroundColor,
isLandScapeMode: _responsiveUtils.isLandscapeMobile(context),
insertAttachment: onAttachFileAction,
insertImage: () => onInsertImageAction != null
? onInsertImageAction!(constraints)
insertImage: onInsertImageAction != null
? () => onInsertImageAction!(constraints)
: null,
richTextController: keyboardRichTextController,
titleQuickStyleBottomSheet: AppLocalizations.of(context).titleQuickStyles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class AppBarComposerWidget extends StatelessWidget {

final bool isSendButtonEnabled;
final bool isNetworkConnectionAvailable;
final VoidCallback onCloseViewAction;
final VoidCallback sendMessageAction;
final VoidCallback? attachFileAction;
final VoidCallback? insertImageAction;
final VoidCallback openRichToolbarAction;
final OnOpenContextMenuAction openContextMenuAction;

final _imagePaths = Get.find<ImagePaths>();
Expand All @@ -21,6 +25,10 @@ class AppBarComposerWidget extends StatelessWidget {
required this.onCloseViewAction,
required this.sendMessageAction,
required this.openContextMenuAction,
required this.openRichToolbarAction,
this.isNetworkConnectionAvailable = false,
this.attachFileAction,
this.insertImageAction,
});

@override
Expand All @@ -37,10 +45,40 @@ class AppBarComposerWidget extends StatelessWidget {
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: onCloseViewAction
),
const Spacer(),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icRichToolbar,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.richTextIconSize,
padding: MobileAppBarComposerWidgetStyle.richTextIconPadding,
tooltipMessage: AppLocalizations.of(context).formattingOptions,
onTapActionCallback: openRichToolbarAction,
),
if (isNetworkConnectionAvailable)
...[
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icAttachFile,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).attach_file,
onTapActionCallback: attachFileAction,
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icInsertImage,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).insertImage,
onTapActionCallback: insertImageAction,
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
],
TMailButtonWidget.fromIcon(
icon: isSendButtonEnabled
? _imagePaths.icSendMobile
Expand All @@ -55,7 +93,6 @@ class AppBarComposerWidget extends StatelessWidget {
TMailButtonWidget.fromIcon(
icon: _imagePaths.icMore,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
borderRadius: MobileAppBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
Expand Down
Loading
Loading