TF-2118 Handle drop n drop image as inline
(cherry picked from commit 2eb6ad6a05224b3caafc59565fd5fa6bdd819870)
This commit is contained in:
@@ -9,6 +9,8 @@ extension MediaTypeExtension on MediaType {
|
|||||||
|
|
||||||
bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(mimeType);
|
bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(mimeType);
|
||||||
|
|
||||||
|
bool isImageValid() => type == 'image';
|
||||||
|
|
||||||
bool isDocFile() => SupportedPreviewFileTypes.docMimeTypes.contains(mimeType);
|
bool isDocFile() => SupportedPreviewFileTypes.docMimeTypes.contains(mimeType);
|
||||||
|
|
||||||
bool isPowerPointFile() => SupportedPreviewFileTypes.pptMimeTypes.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/services.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.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:http_parser/http_parser.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
@@ -1838,6 +1839,62 @@ class ComposerController extends BaseController {
|
|||||||
onEditorFocusChange(true);
|
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() {
|
FocusNode? getNextFocusOfToEmailAddress() {
|
||||||
if (ccRecipientState.value == PrefixRecipientState.enabled) {
|
if (ccRecipientState.value == PrefixRecipientState.enabled) {
|
||||||
return ccAddressFocusNode;
|
return ccAddressFocusNode;
|
||||||
|
|||||||
@@ -161,6 +161,15 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
|
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
|
||||||
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
||||||
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
|
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(
|
Obx(() => Align(
|
||||||
@@ -330,6 +339,15 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
|
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
|
||||||
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
||||||
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
|
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,
|
onUnFocus: controller.handleOnUnFocusHtmlEditorWeb,
|
||||||
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
onMouseDown: controller.handleOnMouseDownHtmlEditorWeb,
|
||||||
onEditorSettings: controller.richTextWebController.onEditorSettingsChange,
|
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:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.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/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/dropdown_menu_font_status.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/model/formatting_options_state.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) {
|
void applyNewFontStyle(FontNameType? newFont) {
|
||||||
final fontSelected = newFont ?? FontNameType.sansSerif;
|
final fontSelected = newFont ?? FontNameType.sansSerif;
|
||||||
selectedFontName.value = fontSelected;
|
selectedFontName.value = fontSelected;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
final VoidCallback? onUnFocus;
|
final VoidCallback? onUnFocus;
|
||||||
final OnMouseDownEditorAction? onMouseDown;
|
final OnMouseDownEditorAction? onMouseDown;
|
||||||
final OnEditorSettingsChange? onEditorSettings;
|
final OnEditorSettingsChange? onEditorSettings;
|
||||||
|
final OnImageUploadSuccessAction? onImageUploadSuccessAction;
|
||||||
|
final OnImageUploadFailureAction? onImageUploadFailureAction;
|
||||||
|
|
||||||
const WebEditorView({
|
const WebEditorView({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -39,6 +41,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
this.onUnFocus,
|
this.onUnFocus,
|
||||||
this.onMouseDown,
|
this.onMouseDown,
|
||||||
this.onEditorSettings,
|
this.onEditorSettings,
|
||||||
|
this.onImageUploadSuccessAction,
|
||||||
|
this.onImageUploadFailureAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -61,6 +65,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
);
|
);
|
||||||
case EmailActionType.editDraft:
|
case EmailActionType.editDraft:
|
||||||
case EmailActionType.editSendingEmail:
|
case EmailActionType.editSendingEmail:
|
||||||
@@ -80,6 +86,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
),
|
),
|
||||||
(success) {
|
(success) {
|
||||||
if (success is GetEmailContentLoading) {
|
if (success is GetEmailContentLoading) {
|
||||||
@@ -101,6 +109,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,6 +139,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(success) {
|
(success) {
|
||||||
@@ -153,6 +165,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,6 +182,8 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
|
|||||||
onUnFocus: onUnFocus,
|
onUnFocus: onUnFocus,
|
||||||
onMouseDown: onMouseDown,
|
onMouseDown: onMouseDown,
|
||||||
onEditorSettings: onEditorSettings,
|
onEditorSettings: onEditorSettings,
|
||||||
|
onImageUploadSuccessAction: onImageUploadSuccessAction,
|
||||||
|
onImageUploadFailureAction: onImageUploadFailureAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ typedef OnChangeContentEditorAction = Function(String? text);
|
|||||||
typedef OnInitialContentEditorAction = Function(String text);
|
typedef OnInitialContentEditorAction = Function(String text);
|
||||||
typedef OnMouseDownEditorAction = Function(BuildContext context);
|
typedef OnMouseDownEditorAction = Function(BuildContext context);
|
||||||
typedef OnEditorSettingsChange = Function(EditorSettings settings);
|
typedef OnEditorSettingsChange = Function(EditorSettings settings);
|
||||||
|
typedef OnImageUploadSuccessAction = Function(FileUpload fileUpload);
|
||||||
|
typedef OnImageUploadFailureAction = Function(FileUpload? fileUpload, String? base64Str, UploadError error);
|
||||||
|
|
||||||
class WebEditorWidget extends StatefulWidget {
|
class WebEditorWidget extends StatefulWidget {
|
||||||
|
|
||||||
@@ -20,6 +22,8 @@ class WebEditorWidget extends StatefulWidget {
|
|||||||
final VoidCallback? onUnFocus;
|
final VoidCallback? onUnFocus;
|
||||||
final OnMouseDownEditorAction? onMouseDown;
|
final OnMouseDownEditorAction? onMouseDown;
|
||||||
final OnEditorSettingsChange? onEditorSettings;
|
final OnEditorSettingsChange? onEditorSettings;
|
||||||
|
final OnImageUploadSuccessAction? onImageUploadSuccessAction;
|
||||||
|
final OnImageUploadFailureAction? onImageUploadFailureAction;
|
||||||
|
|
||||||
const WebEditorWidget({
|
const WebEditorWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -32,6 +36,8 @@ class WebEditorWidget extends StatefulWidget {
|
|||||||
this.onUnFocus,
|
this.onUnFocus,
|
||||||
this.onMouseDown,
|
this.onMouseDown,
|
||||||
this.onEditorSettings,
|
this.onEditorSettings,
|
||||||
|
this.onImageUploadSuccessAction,
|
||||||
|
this.onImageUploadFailureAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -82,7 +88,9 @@ class _WebEditorState extends State<WebEditorWidget> {
|
|||||||
onBlur: widget.onUnFocus,
|
onBlur: widget.onUnFocus,
|
||||||
onMouseDown: () => widget.onMouseDown?.call(context),
|
onMouseDown: () => widget.onMouseDown?.call(context),
|
||||||
onChangeSelection: widget.onEditorSettings,
|
onChangeSelection: widget.onEditorSettings,
|
||||||
onChangeCodeview: widget.onChangeContent
|
onChangeCodeview: widget.onChangeContent,
|
||||||
|
onImageUpload: widget.onImageUploadSuccessAction,
|
||||||
|
onImageUploadError: widget.onImageUploadFailureAction,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user