TF-1486 Add insert image in signature on web
(cherry picked from commit 50f0e54b2a47fe2e857372f56293d1c1800ded2b)
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:custom_pop_up_menu/custom_pop_up_menu.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:html_editor_enhanced/html_editor.dart';
|
||||
@@ -257,6 +260,17 @@ class RichTextWebController extends BaseRichTextController {
|
||||
menuOrderListController.hideMenu();
|
||||
}
|
||||
|
||||
void insertImageAsBase64({required PlatformFile platformFile}) {
|
||||
if (platformFile.bytes != null) {
|
||||
final base64Data = base64Encode(platformFile.bytes!);
|
||||
editorController.insertHtml(
|
||||
'<img src="data:image/${platformFile.extension};base64,$base64Data" data-filename="${platformFile.name}" alt="Image in my signature" style="max-width: 100%"/>'
|
||||
);
|
||||
} else {
|
||||
logError("RichTextWebController::insertImageAsBase64: bytes is null");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
menuParagraphController.dispose();
|
||||
|
||||
@@ -22,11 +22,13 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin
|
||||
final RichTextWebController richTextWebController;
|
||||
final ImagePaths _imagePaths = Get.find<ImagePaths>();
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final List<Widget>? extendedOption;
|
||||
|
||||
ToolbarRichTextWebBuilder({
|
||||
Key? key,
|
||||
required this.richTextWebController,
|
||||
this.padding,
|
||||
this.extendedOption,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -42,6 +44,8 @@ class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
if (extendedOption?.isNotEmpty == true)
|
||||
...extendedOption!,
|
||||
AbsorbPointer(
|
||||
absorbing: codeViewEnabled,
|
||||
child: DropDownMenuHeaderStyleWidget(
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
extension SizeExtension on int {
|
||||
|
||||
int get kiloByteToBytes => this * 1024;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
|
||||
import 'package:core/presentation/utils/app_toast.dart';
|
||||
import 'package:core/presentation/utils/keyboard_utils.dart';
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
@@ -20,7 +21,9 @@ import 'package:model/user/user_profile.dart';
|
||||
import 'package:rich_text_composer/rich_text_composer.dart';
|
||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/presentation/extesions/size_extension.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/presentation/model/identity_creator_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/presentation/utils/identity_creator_constants.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/email_address_validator.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart';
|
||||
@@ -34,6 +37,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/id
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/model/identity_action_type.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/utils/identity_utils.dart';
|
||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
@@ -44,6 +48,7 @@ class IdentityCreatorController extends BaseController {
|
||||
final IdentityUtils _identityUtils;
|
||||
|
||||
final _uuid = Get.find<Uuid>();
|
||||
final _appToast = Get.find<AppToast>();
|
||||
|
||||
final noneEmailAddress = EmailAddress(null, 'None');
|
||||
final listEmailAddressDefault = <EmailAddress>[].obs;
|
||||
@@ -302,12 +307,14 @@ class IdentityCreatorController extends BaseController {
|
||||
final error = _getErrorInputNameString(context);
|
||||
if (error?.isNotEmpty == true) {
|
||||
errorNameIdentity.value = error;
|
||||
inputNameIdentityFocusNode.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
final errorBcc = _getErrorInputAddressString(context);
|
||||
if (errorBcc?.isNotEmpty == true) {
|
||||
errorBccIdentity.value = errorBcc;
|
||||
inputBccIdentityFocusNode.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -456,4 +463,41 @@ class IdentityCreatorController extends BaseController {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void pickImage(BuildContext context) async {
|
||||
final filePickerResult = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
withData: true
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
final platformFile = filePickerResult?.files.single;
|
||||
if (platformFile != null) {
|
||||
_insertInlineImage(context, platformFile);
|
||||
} else {
|
||||
_appToast.showToastErrorMessage(
|
||||
context,
|
||||
AppLocalizations.of(context).cannotSelectThisImage
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logError("IdentityCreatorController::pickImage: context is unmounted");
|
||||
}
|
||||
}
|
||||
|
||||
bool _isExceedMaxSizeInlineImage(int fileSize) =>
|
||||
fileSize > IdentityCreatorConstants.maxSizeIdentityInlineImage.kiloByteToBytes;
|
||||
|
||||
void _insertInlineImage(BuildContext context, PlatformFile platformFile) {
|
||||
if (_isExceedMaxSizeInlineImage(platformFile.size)) {
|
||||
_appToast.showToastErrorMessage(
|
||||
context,
|
||||
AppLocalizations.of(context).pleaseChooseAnImageSizeCorrectly(
|
||||
IdentityCreatorConstants.maxSizeIdentityInlineImage
|
||||
)
|
||||
);
|
||||
} else {
|
||||
richTextWebController.insertImageAsBase64(platformFile: platformFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@@ -18,6 +17,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.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/mixin/rich_text_button_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/toolbar_rich_text_builder.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/presentation/identity_creator_controller.dart';
|
||||
import 'package:tmail_ui_user/features/identity_creator/presentation/widgets/identity_drop_list_field_builder.dart';
|
||||
@@ -30,7 +30,8 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/identit
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
||||
|
||||
class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
|
||||
class IdentityCreatorView extends GetWidget<IdentityCreatorController>
|
||||
with RichTextButtonMixin {
|
||||
|
||||
final _imagePaths = Get.find<ImagePaths>();
|
||||
final _responsiveUtils = Get.find<ResponsiveUtils>();
|
||||
@@ -163,7 +164,7 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
|
||||
controller.errorNameIdentity.value,
|
||||
AppLocalizations.of(context).required,
|
||||
editingController: controller.inputNameIdentityController,
|
||||
focusNode: PlatformInfo.isWeb ? null : controller.inputNameIdentityFocusNode,
|
||||
focusNode: controller.inputNameIdentityFocusNode,
|
||||
isMandatory: true,
|
||||
onChangeInputNameAction: (value) => controller.updateNameIdentity(context, value)
|
||||
)),
|
||||
@@ -195,7 +196,7 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
|
||||
AppLocalizations.of(context).bcc_to,
|
||||
controller.errorBccIdentity.value,
|
||||
controller.inputBccIdentityController,
|
||||
focusNode: PlatformInfo.isWeb ? null : controller.inputBccIdentityFocusNode,
|
||||
focusNode: controller.inputBccIdentityFocusNode,
|
||||
onSelectedSuggestionAction: (newEmailAddress) {
|
||||
controller.inputBccIdentityController.text = newEmailAddress?.email ?? '';
|
||||
controller.updateBccOfIdentity(newEmailAddress);
|
||||
@@ -306,7 +307,20 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
|
||||
if (PlatformInfo.isWeb)
|
||||
ToolbarRichTextWebBuilder(
|
||||
richTextWebController: controller.richTextWebController,
|
||||
padding: const EdgeInsets.only(bottom: 12)
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
extendedOption: [
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 4.0),
|
||||
child: buildWrapIconStyleText(
|
||||
icon: buildIconWithTooltip(
|
||||
path: _imagePaths.icAddPicture,
|
||||
tooltip: AppLocalizations.of(context).insertImage
|
||||
),
|
||||
hasDropdown: false,
|
||||
onTap: () => controller.pickImage(context)
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
htmlEditor,
|
||||
],
|
||||
@@ -314,7 +328,6 @@ class IdentityCreatorView extends GetWidget<IdentityCreatorController> {
|
||||
}
|
||||
|
||||
Widget _buildHtmlEditorWeb(BuildContext context, String initContent) {
|
||||
log('IdentityCreatorView::_buildHtmlEditorWeb(): initContent: $initContent');
|
||||
return html_editor_browser.HtmlEditor(
|
||||
key: const Key('identity_create_editor_web'),
|
||||
controller: controller.richTextWebController.editorController,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
class IdentityCreatorConstants {
|
||||
static const int maxSizeIdentityInlineImage = 16; // Kilobyte
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2023-07-07T16:07:02.053606",
|
||||
"@@last_modified": "2023-07-12T18:53:48.611802",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -2965,5 +2965,15 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"pleaseChooseAnImageSizeCorrectly": "Please choose an image size <= {maxSize}KB",
|
||||
"@pleaseChooseAnImageSizeCorrectly": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"maxSize"
|
||||
],
|
||||
"placeholders": {
|
||||
"maxSize": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3064,4 +3064,11 @@ class AppLocalizations {
|
||||
name: 'archiveMailboxDisplayName',
|
||||
);
|
||||
}
|
||||
|
||||
String pleaseChooseAnImageSizeCorrectly(int maxSize) {
|
||||
return Intl.message(
|
||||
'Please choose an image size <= ${maxSize}KB',
|
||||
name: 'pleaseChooseAnImageSizeCorrectly',
|
||||
args: [maxSize]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user