TF-2118 Handle drop n drop file as attachment
(cherry picked from commit cb1f15dcba46a15dbd5c5a8493593854855be331)
This commit is contained in:
@@ -49,6 +49,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_dra
|
||||
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/file_upload_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_identities_extension.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart';
|
||||
@@ -1839,10 +1840,10 @@ class ComposerController extends BaseController {
|
||||
onEditorFocusChange(true);
|
||||
}
|
||||
|
||||
void handleImageUploadSuccess(
|
||||
void handleImageUploadSuccess (
|
||||
BuildContext context,
|
||||
web_html_editor.FileUpload fileUpload
|
||||
) {
|
||||
) async {
|
||||
log('ComposerController::handleImageUploadSuccess:NAME: ${fileUpload.name} | TYPE: ${fileUpload.type} | SIZE: ${fileUpload.size}');
|
||||
if (fileUpload.base64 == null) {
|
||||
_appToast.showToastErrorMessage(
|
||||
@@ -1853,6 +1854,15 @@ class ComposerController extends BaseController {
|
||||
}
|
||||
|
||||
if (fileUpload.type == null) {
|
||||
final fileInfo = await fileUpload.toFileInfo();
|
||||
if (fileInfo != null) {
|
||||
_addAttachmentFromDragAndDrop(fileInfo: fileInfo);
|
||||
} else if (context.mounted) {
|
||||
_appToast.showToastErrorMessage(
|
||||
context,
|
||||
AppLocalizations.of(context).can_not_upload_this_file_as_attachments
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1864,6 +1874,16 @@ class ComposerController extends BaseController {
|
||||
type: mediaType,
|
||||
size: fileUpload.size,
|
||||
);
|
||||
} else {
|
||||
final fileInfo = await fileUpload.toFileInfo();
|
||||
if (fileInfo != null) {
|
||||
_addAttachmentFromDragAndDrop(fileInfo: fileInfo);
|
||||
} else if (context.mounted) {
|
||||
_appToast.showToastErrorMessage(
|
||||
context,
|
||||
AppLocalizations.of(context).can_not_upload_this_file_as_attachments
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1895,6 +1915,23 @@ class ComposerController extends BaseController {
|
||||
);
|
||||
}
|
||||
|
||||
void _addAttachmentFromDragAndDrop({required FileInfo fileInfo}) {
|
||||
if (uploadController.hasEnoughMaxAttachmentSize(listFiles: [fileInfo])) {
|
||||
_uploadAttachmentsAction([fileInfo]);
|
||||
} else {
|
||||
if (currentContext != null) {
|
||||
showConfirmDialogAction(
|
||||
currentContext!,
|
||||
AppLocalizations.of(currentContext!).message_dialog_upload_attachments_exceeds_maximum_size(
|
||||
filesize(mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value ?? 0, 0)),
|
||||
AppLocalizations.of(currentContext!).got_it,
|
||||
title: AppLocalizations.of(currentContext!).maximum_files_size,
|
||||
hasCancelButton: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FocusNode? getNextFocusOfToEmailAddress() {
|
||||
if (ccRecipientState.value == PrefixRecipientState.enabled) {
|
||||
return ccAddressFocusNode;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'dart:convert' as convert;
|
||||
import 'dart:typed_data' as type_data;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get_connect/http/src/request/request.dart';
|
||||
import 'package:html_editor_enhanced/utils/file_upload_model.dart';
|
||||
import 'package:model/upload/file_info.dart';
|
||||
|
||||
extension FileUploadExtension on FileUpload {
|
||||
|
||||
String? get base64Data {
|
||||
if (base64 != null) {
|
||||
if (!base64!.contains(',')) {
|
||||
return base64;
|
||||
}
|
||||
final listData = base64!.split(',');
|
||||
if (listData.length < 2) {
|
||||
return base64;
|
||||
}
|
||||
|
||||
final base64Origin = listData[1];
|
||||
return base64Origin;
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
Future<FileInfo?> toFileInfo() async {
|
||||
if (base64Data != null) {
|
||||
final bytesStream = await compute(convertBytesToStream, base64Data!);
|
||||
return FileInfo.fromStream(
|
||||
stream: bytesStream,
|
||||
name: name,
|
||||
size: size
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<List<int>> convertBytesToStream(String base64) {
|
||||
type_data.Uint8List decodeBytes = convert.base64Decode(base64);
|
||||
final bytesStream = BodyBytesStream.fromBytes(decodeBytes);
|
||||
return bytesStream;
|
||||
}
|
||||
}
|
||||
@@ -57,11 +57,15 @@ class UploadAttachment with EquatableMixin {
|
||||
if (attachment != null) {
|
||||
_updateEvent(Right(SuccessAttachmentUploadState(uploadTaskId, attachment, fileInfo)));
|
||||
} else {
|
||||
_updateEvent(Left(ErrorAttachmentUploadState(uploadTaskId)));
|
||||
_updateEvent(Left(ErrorAttachmentUploadState(uploadId: uploadTaskId)));
|
||||
}
|
||||
} catch (e) {
|
||||
logError('UploadAttachment::upload():ERROR: $e');
|
||||
_updateEvent(Left(ErrorAttachmentUploadState(uploadTaskId)));
|
||||
if (e is DioError && e.type == DioErrorType.cancel) {
|
||||
_updateEvent(Left(CancelAttachmentUploadState(uploadTaskId)));
|
||||
} else {
|
||||
_updateEvent(Left(ErrorAttachmentUploadState(uploadId: uploadTaskId, exception: e)));
|
||||
}
|
||||
} finally {
|
||||
await _progressStateController.close();
|
||||
}
|
||||
|
||||
@@ -51,13 +51,16 @@ class SuccessAttachmentUploadState extends Success {
|
||||
];
|
||||
}
|
||||
|
||||
class ErrorAttachmentUploadState extends Failure {
|
||||
class ErrorAttachmentUploadState extends FeatureFailure {
|
||||
final UploadTaskId uploadId;
|
||||
|
||||
ErrorAttachmentUploadState(this.uploadId);
|
||||
ErrorAttachmentUploadState({
|
||||
required this.uploadId,
|
||||
dynamic exception
|
||||
}) : super(exception: exception);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uploadId];
|
||||
List<Object?> get props => [uploadId, ...super.props];
|
||||
}
|
||||
|
||||
class CancelAttachmentUploadState extends Failure {
|
||||
|
||||
@@ -258,7 +258,7 @@ class UploadController extends BaseController {
|
||||
if (currentContext != null && currentOverlayContext != null) {
|
||||
_appToast.showToastErrorMessage(
|
||||
currentOverlayContext!,
|
||||
AppLocalizations.of(currentContext!).can_not_upload_this_file_as_attachments,
|
||||
'${AppLocalizations.of(currentContext!).can_not_upload_this_file_as_attachments}. ${failure.exception ?? ''}',
|
||||
leadingSVGIconColor: Colors.white,
|
||||
leadingSVGIcon: _imagePaths.icAttachment);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,14 @@ class FileInfo with EquatableMixin {
|
||||
return FileInfo('', '', 0);
|
||||
}
|
||||
|
||||
factory FileInfo.fromStream({
|
||||
required Stream<List<int>> stream,
|
||||
String? name,
|
||||
int? size
|
||||
}) {
|
||||
return FileInfo(name ?? '', '', size ?? 0, readStream: stream);
|
||||
}
|
||||
|
||||
String get fileExtension => fileName.split('.').last;
|
||||
|
||||
String get mimeType => lookupMimeType(kIsWeb ? fileName : filePath) ?? 'application/json; charset=UTF-8';
|
||||
|
||||
Reference in New Issue
Block a user