TF-2118 Handle drop n drop image as inline

(cherry picked from commit 2eb6ad6a05224b3caafc59565fd5fa6bdd819870)
This commit is contained in:
dab246
2023-09-27 19:11:43 +07:00
committed by Dat H. Pham
parent 73de2f37fa
commit 25b26acc42
6 changed files with 123 additions and 1 deletions
@@ -9,6 +9,8 @@ extension MediaTypeExtension on MediaType {
bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(mimeType);
bool isImageValid() => type == 'image';
bool isDocFile() => SupportedPreviewFileTypes.docMimeTypes.contains(mimeType);
bool isPowerPointFile() => SupportedPreviewFileTypes.pptMimeTypes.contains(mimeType);
@@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart' as web_html_editor;
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
@@ -1838,6 +1839,62 @@ class ComposerController extends BaseController {
onEditorFocusChange(true);
}
void handleImageUploadSuccess(
BuildContext context,
web_html_editor.FileUpload fileUpload
) {
log('ComposerController::handleImageUploadSuccess:NAME: ${fileUpload.name} | TYPE: ${fileUpload.type} | SIZE: ${fileUpload.size}');
if (fileUpload.base64 == null) {
_appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).can_not_upload_this_file_as_attachments
);
return;
}
if (fileUpload.type == null) {
return;
}
final mediaType = MediaType.parse(fileUpload.type!);
if (mediaType.isImageValid()) {
_addInlineImageFromDragAndDrop(
base64Data: fileUpload.base64!,
name: fileUpload.name,
type: mediaType,
size: fileUpload.size,
);
}
}
void _addInlineImageFromDragAndDrop({
required String base64Data,
String? name,
MediaType? type,
int? size
}) {
log('ComposerController::_addInlineImageFromDragAndDrop:name: $name');
richTextWebController.insertInlineImage(
base64Data: base64Data,
name: name,
type: type,
size: size,
);
}
void handleImageUploadFailure({
required BuildContext context,
required web_html_editor.UploadError uploadError,
web_html_editor.FileUpload? fileUpload,
String? base64Str,
}) {
logError('ComposerController::handleImageUploadFailure:fileUpload: $fileUpload | uploadError: $uploadError');
_appToast.showToastErrorMessage(
context,
'${AppLocalizations.of(context).can_not_upload_this_file_as_attachments}. (${uploadError.name})'
);
}
FocusNode? getNextFocusOfToEmailAddress() {
if (ccRecipientState.value == PrefixRecipientState.enabled) {
return ccAddressFocusNode;
@@ -161,6 +161,15 @@ class ComposerView extends GetWidget<ComposerController> {
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
onImageUploadSuccessAction: (fileUpload) => controller.handleImageUploadSuccess(context, fileUpload),
onImageUploadFailureAction: (fileUpload, base64Str, uploadError) {
return controller.handleImageUploadFailure(
context: context,
uploadError: uploadError,
fileUpload: fileUpload,
base64Str: base64Str,
);
}
),
),
Obx(() => Align(
@@ -330,6 +339,15 @@ class ComposerView extends GetWidget<ComposerController> {
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
onImageUploadSuccessAction: (fileUpload) => controller.handleImageUploadSuccess(context, fileUpload),
onImageUploadFailureAction: (fileUpload, base64Str, uploadError) {
return controller.handleImageUploadFailure(
context: context,
uploadError: uploadError,
fileUpload: fileUpload,
base64Str: base64Str,
);
}
),
),
)),
@@ -523,6 +541,15 @@ class ComposerView extends GetWidget<ComposerController> {
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
onImageUploadSuccessAction: (fileUpload) => controller.handleImageUploadSuccess(context, fileUpload),
onImageUploadFailureAction: (fileUpload, base64Str, uploadError) {
return controller.handleImageUploadFailure(
context: context,
uploadError: uploadError,
fileUpload: fileUpload,
base64Str: base64Str,
);
}
),
),
)),
@@ -8,6 +8,7 @@ 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';
import 'package:http_parser/http_parser.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';
@@ -185,6 +186,17 @@ class RichTextWebController extends BaseRichTextController {
}
}
void insertInlineImage({
required String base64Data,
String? name,
MediaType? type,
int? size,
}) async {
log('RichTextWebController::insertInlineImage():name: $name | type: $type');
final base64Uri = '<img src="$base64Data" data-filename="$name" alt="Inline Image TeamMail" style="max-width: 100%" />';
editorController.insertHtml(base64Uri);
}
void applyNewFontStyle(FontNameType? newFont) {
final fontSelected = newFont ?? FontNameType.sansSerif;
selectedFontName.value = fontSelected;
@@ -26,6 +26,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
final VoidCallback? onUnFocus;
final OnMouseDownEditorAction? onMouseDown;
final OnEditorSettingsChange? onEditorSettings;
final OnImageUploadSuccessAction? onImageUploadSuccessAction;
final OnImageUploadFailureAction? onImageUploadFailureAction;
const WebEditorView({
super.key,
@@ -39,6 +41,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
this.onUnFocus,
this.onMouseDown,
this.onEditorSettings,
this.onImageUploadSuccessAction,
this.onImageUploadFailureAction,
});
@override
@@ -61,6 +65,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
);
case EmailActionType.editDraft:
case EmailActionType.editSendingEmail:
@@ -80,6 +86,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
),
(success) {
if (success is GetEmailContentLoading) {
@@ -101,6 +109,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
);
}
}
@@ -129,6 +139,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
);
},
(success) {
@@ -153,6 +165,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
);
}
}
@@ -168,6 +182,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onUnFocus: onUnFocus,
onMouseDown: onMouseDown,
onEditorSettings: onEditorSettings,
onImageUploadSuccessAction: onImageUploadSuccessAction,
onImageUploadFailureAction: onImageUploadFailureAction,
);
}
}
@@ -8,6 +8,8 @@ typedef OnChangeContentEditorAction = Function(String? text);
typedef OnInitialContentEditorAction = Function(String text);
typedef OnMouseDownEditorAction = Function(BuildContext context);
typedef OnEditorSettingsChange = Function(EditorSettings settings);
typedef OnImageUploadSuccessAction = Function(FileUpload fileUpload);
typedef OnImageUploadFailureAction = Function(FileUpload? fileUpload, String? base64Str, UploadError error);
class WebEditorWidget extends StatefulWidget {
@@ -20,6 +22,8 @@ class WebEditorWidget extends StatefulWidget {
final VoidCallback? onUnFocus;
final OnMouseDownEditorAction? onMouseDown;
final OnEditorSettingsChange? onEditorSettings;
final OnImageUploadSuccessAction? onImageUploadSuccessAction;
final OnImageUploadFailureAction? onImageUploadFailureAction;
const WebEditorWidget({
super.key,
@@ -32,6 +36,8 @@ class WebEditorWidget extends StatefulWidget {
this.onUnFocus,
this.onMouseDown,
this.onEditorSettings,
this.onImageUploadSuccessAction,
this.onImageUploadFailureAction,
});
@override
@@ -82,7 +88,9 @@ class _WebEditorState extends State<WebEditorWidget> {
onBlur: widget.onUnFocus,
onMouseDown: () => widget.onMouseDown?.call(context),
onChangeSelection: widget.onEditorSettings,
onChangeCodeview: widget.onChangeContent
onChangeCodeview: widget.onChangeContent,
onImageUpload: widget.onImageUploadSuccessAction,
onImageUploadError: widget.onImageUploadFailureAction,
),
);
}