TF-675 Implement upload attachment on presentation layer

This commit is contained in:
dab246
2022-07-05 19:33:14 +07:00
committed by Dat H. Pham
parent 91bc3018ea
commit 737555eb14
19 changed files with 358 additions and 313 deletions
@@ -3,11 +3,8 @@ import 'package:core/core.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/composer/data/datasource/composer_datasource.dart';
import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart'; import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart';
import 'package:tmail_ui_user/features/composer/data/datasource_impl/composer_datasource_impl.dart';
import 'package:tmail_ui_user/features/composer/data/datasource_impl/contact_datasource_impl.dart'; import 'package:tmail_ui_user/features/composer/data/datasource_impl/contact_datasource_impl.dart';
import 'package:tmail_ui_user/features/composer/data/network/composer_api.dart';
import 'package:tmail_ui_user/features/composer/data/repository/auto_complete_repository_impl.dart'; import 'package:tmail_ui_user/features/composer/data/repository/auto_complete_repository_impl.dart';
import 'package:tmail_ui_user/features/composer/data/repository/composer_repository_impl.dart'; import 'package:tmail_ui_user/features/composer/data/repository/composer_repository_impl.dart';
import 'package:tmail_ui_user/features/composer/data/repository/contact_repository_impl.dart'; import 'package:tmail_ui_user/features/composer/data/repository/contact_repository_impl.dart';
@@ -21,7 +18,6 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_dra
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/session_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
@@ -41,9 +37,13 @@ import 'package:tmail_ui_user/features/manage_account/data/network/manage_accoun
import 'package:tmail_ui_user/features/manage_account/data/repository/manage_account_repository_impl.dart'; import 'package:tmail_ui_user/features/manage_account/data/repository/manage_account_repository_impl.dart';
import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/repository/manage_account_repository.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
import 'package:tmail_ui_user/features/upload/data/datasource/attachment_upload_datasource.dart';
import 'package:tmail_ui_user/features/upload/data/datasource_impl/attachment_upload_datasource_impl.dart';
import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart';
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
import 'package:uuid/uuid.dart'; import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart';
import 'package:jmap_dart_client/http/http_client.dart' as jmap_http_client; import 'package:jmap_dart_client/http/http_client.dart' as jmap_http_client;
import 'package:worker_manager/worker_manager.dart';
class ComposerBindings extends BaseBindings { class ComposerBindings extends BaseBindings {
@@ -57,7 +57,7 @@ class ComposerBindings extends BaseBindings {
} }
void _bindingsUtils() { void _bindingsUtils() {
Get.lazyPut(() => const Uuid()); Get.lazyPut(() => FileUploader(Get.find<DioClient>(), Get.find<Executor>()));
} }
@override @override
@@ -68,7 +68,7 @@ class ComposerBindings extends BaseBindings {
dataSources.add(Get.find<TMailContactDataSourceImpl>()); dataSources.add(Get.find<TMailContactDataSourceImpl>());
} }
Get.lazyPut(() => ComposerDataSourceImpl(Get.find<ComposerAPI>())); Get.lazyPut(() => AttachmentUploadDataSourceImpl(Get.find<FileUploader>()));
Get.lazyPut(() => ContactDataSourceImpl()); Get.lazyPut(() => ContactDataSourceImpl());
Get.lazyPut(() => EmailDataSourceImpl(Get.find<EmailAPI>())); Get.lazyPut(() => EmailDataSourceImpl(Get.find<EmailAPI>()));
Get.lazyPut(() => HtmlDataSourceImpl( Get.lazyPut(() => HtmlDataSourceImpl(
@@ -82,7 +82,7 @@ class ComposerBindings extends BaseBindings {
@override @override
void bindingsDataSource() { void bindingsDataSource() {
Get.lazyPut<ComposerDataSource>(() => Get.find<ComposerDataSourceImpl>()); Get.lazyPut<AttachmentUploadDataSource>(() => Get.find<AttachmentUploadDataSourceImpl>());
Get.lazyPut<ContactDataSource>(() => Get.find<ContactDataSourceImpl>()); Get.lazyPut<ContactDataSource>(() => Get.find<ContactDataSourceImpl>());
Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>()); Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>());
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>()); Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
@@ -91,7 +91,7 @@ class ComposerBindings extends BaseBindings {
@override @override
void bindingsRepositoryImpl() { void bindingsRepositoryImpl() {
Get.lazyPut(() => ComposerRepositoryImpl(Get.find<ComposerDataSource>())); Get.lazyPut(() => ComposerRepositoryImpl(Get.find<AttachmentUploadDataSource>()));
Get.lazyPut(() => AutoCompleteRepositoryImpl(dataSources)); Get.lazyPut(() => AutoCompleteRepositoryImpl(dataSources));
Get.lazyPut(() => ContactRepositoryImpl(Get.find<ContactDataSource>())); Get.lazyPut(() => ContactRepositoryImpl(Get.find<ContactDataSource>()));
Get.lazyPut(() => EmailRepositoryImpl( Get.lazyPut(() => EmailRepositoryImpl(
@@ -120,7 +120,6 @@ class ComposerBindings extends BaseBindings {
)); ));
Get.lazyPut(() => LocalFilePickerInteractor()); Get.lazyPut(() => LocalFilePickerInteractor());
Get.lazyPut(() => UploadAttachmentInteractor(Get.find<ComposerRepository>())); Get.lazyPut(() => UploadAttachmentInteractor(Get.find<ComposerRepository>()));
Get.lazyPut(() => UploadMultipleAttachmentInteractor(Get.find<UploadAttachmentInteractor>()));
Get.lazyPut(() => SendEmailInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => SendEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => SaveEmailAsDraftsInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => SaveEmailAsDraftsInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>()));
@@ -130,18 +129,23 @@ class ComposerBindings extends BaseBindings {
@override @override
void bindingsController() { void bindingsController() {
Get.lazyPut(() => UploadController(Get.find<UploadAttachmentInteractor>()));
Get.lazyPut(() => ComposerController( Get.lazyPut(() => ComposerController(
Get.find<SendEmailInteractor>(), Get.find<SendEmailInteractor>(),
Get.find<GetAutoCompleteInteractor>(), Get.find<GetAutoCompleteInteractor>(),
Get.find<GetAutoCompleteWithDeviceContactInteractor>(), Get.find<GetAutoCompleteWithDeviceContactInteractor>(),
Get.find<Uuid>(),
Get.find<DeviceInfoPlugin>(), Get.find<DeviceInfoPlugin>(),
Get.find<LocalFilePickerInteractor>(), Get.find<LocalFilePickerInteractor>(),
Get.find<UploadMultipleAttachmentInteractor>(),
Get.find<SaveEmailAsDraftsInteractor>(), Get.find<SaveEmailAsDraftsInteractor>(),
Get.find<GetEmailContentInteractor>(), Get.find<GetEmailContentInteractor>(),
Get.find<UpdateEmailDraftsInteractor>(), Get.find<UpdateEmailDraftsInteractor>(),
Get.find<GetAllIdentitiesInteractor>(), Get.find<GetAllIdentitiesInteractor>(),
Get.find<UploadController>(),
)); ));
} }
void dispose() {
Get.delete<UploadController>();
Get.delete<ComposerController>();
}
} }
@@ -31,11 +31,9 @@ import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'
import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_state.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_as_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_email_as_drafts_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/update_email_drafts_interactor.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
@@ -44,8 +42,10 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart'; import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart';
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@@ -56,12 +56,12 @@ class ComposerController extends BaseController {
final _appToast = Get.find<AppToast>(); final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>(); final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>(); final _responsiveUtils = Get.find<ResponsiveUtils>();
final _uuid = Get.find<Uuid>();
final expandModeAttachments = ExpandMode.COLLAPSE.obs; final expandModeAttachments = ExpandMode.COLLAPSE.obs;
final composerArguments = Rxn<ComposerArguments>(); final composerArguments = Rxn<ComposerArguments>();
final isEnableEmailSendButton = false.obs; final isEnableEmailSendButton = false.obs;
final isInitialRecipient = false.obs; final isInitialRecipient = false.obs;
final attachments = <Attachment>[].obs;
final emailContents = Rxn<List<EmailContent>>(); final emailContents = Rxn<List<EmailContent>>();
final listEmailAddressType = <PrefixEmailAddress>[].obs; final listEmailAddressType = <PrefixEmailAddress>[].obs;
final subjectEmail = Rxn<String>(); final subjectEmail = Rxn<String>();
@@ -75,14 +75,13 @@ class ComposerController extends BaseController {
final SendEmailInteractor _sendEmailInteractor; final SendEmailInteractor _sendEmailInteractor;
final GetAutoCompleteInteractor _getAutoCompleteInteractor; final GetAutoCompleteInteractor _getAutoCompleteInteractor;
final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor; final GetAutoCompleteWithDeviceContactInteractor _getAutoCompleteWithDeviceContactInteractor;
final Uuid _uuid;
final LocalFilePickerInteractor _localFilePickerInteractor; final LocalFilePickerInteractor _localFilePickerInteractor;
final UploadMultipleAttachmentInteractor _uploadMultipleAttachmentInteractor;
final DeviceInfoPlugin _deviceInfoPlugin; final DeviceInfoPlugin _deviceInfoPlugin;
final SaveEmailAsDraftsInteractor _saveEmailAsDraftsInteractor; final SaveEmailAsDraftsInteractor _saveEmailAsDraftsInteractor;
final GetEmailContentInteractor _getEmailContentInteractor; final GetEmailContentInteractor _getEmailContentInteractor;
final UpdateEmailDraftsInteractor _updateEmailDraftsInteractor; final UpdateEmailDraftsInteractor _updateEmailDraftsInteractor;
final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor;
final UploadController uploadController;
List<EmailAddress> listToEmailAddress = <EmailAddress>[]; List<EmailAddress> listToEmailAddress = <EmailAddress>[];
List<EmailAddress> listCcEmailAddress = <EmailAddress>[]; List<EmailAddress> listCcEmailAddress = <EmailAddress>[];
@@ -129,14 +128,13 @@ class ComposerController extends BaseController {
this._sendEmailInteractor, this._sendEmailInteractor,
this._getAutoCompleteInteractor, this._getAutoCompleteInteractor,
this._getAutoCompleteWithDeviceContactInteractor, this._getAutoCompleteWithDeviceContactInteractor,
this._uuid,
this._deviceInfoPlugin, this._deviceInfoPlugin,
this._localFilePickerInteractor, this._localFilePickerInteractor,
this._uploadMultipleAttachmentInteractor,
this._saveEmailAsDraftsInteractor, this._saveEmailAsDraftsInteractor,
this._getEmailContentInteractor, this._getEmailContentInteractor,
this._updateEmailDraftsInteractor, this._updateEmailDraftsInteractor,
this._getAllIdentitiesInteractor, this._getAllIdentitiesInteractor,
this.uploadController,
); );
@override @override
@@ -183,18 +181,11 @@ class ComposerController extends BaseController {
(failure) { (failure) {
if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) { if (failure is LocalFilePickerFailure || failure is LocalFilePickerCancel) {
_pickFileFailure(failure); _pickFileFailure(failure);
} else if (failure is UploadAttachmentFailure
|| failure is UploadMultipleAttachmentAllFailure) {
_uploadAttachmentsFailure(failure);
} }
}, },
(success) { (success) {
if (success is LocalFilePickerSuccess) { if (success is LocalFilePickerSuccess) {
_pickFileSuccess(success); _pickFileSuccess(success);
} else if (success is UploadAttachmentSuccess
|| success is UploadMultipleAttachmentAllSuccess
|| success is UploadMultipleAttachmentHasSomeFailure) {
_uploadAttachmentsSuccess(success);
} else if (success is GetEmailContentSuccess) { } else if (success is GetEmailContentSuccess) {
_getEmailContentSuccess(success); _getEmailContentSuccess(success);
} if (success is GetAllIdentitiesSuccess) { } if (success is GetAllIdentitiesSuccess) {
@@ -219,7 +210,7 @@ class ComposerController extends BaseController {
} }
popBack(); popBack();
} }
void _initEmail() { void _initEmail() {
final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments; final arguments = kIsWeb ? mailboxDashBoardController.routerArguments : Get.arguments;
if (arguments is ComposerArguments) { if (arguments is ComposerArguments) {
@@ -243,8 +234,13 @@ class ComposerController extends BaseController {
} }
void _initAttachments(ComposerArguments arguments) { void _initAttachments(ComposerArguments arguments) {
attachments.value = arguments.attachments ?? []; if (arguments.attachments?.isNotEmpty == true) {
initialAttachments = arguments.attachments ?? []; initialAttachments = arguments.attachments!;
uploadController.initializeUploadAttachments(arguments.attachments!);
}
if (BuildUtils.isWeb) {
expandModeAttachments.value = ExpandMode.EXPAND;
}
} }
void _getAllIdentities() { void _getAllIdentities() {
@@ -466,7 +462,7 @@ class ComposerController extends BaseController {
generatePartId: EmailBodyValue(emailBodyText, false, false) generatePartId: EmailBodyValue(emailBodyText, false, false)
}, },
headerUserAgent: {IndividualHeaderIdentifier.headerUserAgent : userAgent}, headerUserAgent: {IndividualHeaderIdentifier.headerUserAgent : userAgent},
attachments: attachments.isNotEmpty ? _generateAttachments() : null, attachments: uploadController.generateAttachments(),
); );
} }
@@ -485,11 +481,6 @@ class ComposerController extends BaseController {
return userAgent; return userAgent;
} }
Set<EmailBodyPart> _generateAttachments() {
return attachments.map((attachment) =>
attachment.toEmailBodyPart(ContentDisposition.attachment.value)).toSet();
}
void sendEmailAction(BuildContext context) async { void sendEmailAction(BuildContext context) async {
clearFocusEditor(context); clearFocusEditor(context);
@@ -534,7 +525,19 @@ class ComposerController extends BaseController {
return; return;
} }
if (!_validateAttachmentsSize()) { if (!uploadController.allUploadAttachmentsCompleted) {
showConfirmDialogAction(
context,
AppLocalizations.of(context).messageDialogSendEmailUploadingAttachment,
AppLocalizations.of(context).got_it,
() => {},
title: AppLocalizations.of(context).sending_failed,
icon: SvgPicture.asset(_imagePaths.icSendToastError, fit: BoxFit.fill),
hasCancelButton: false);
return;
}
if (!uploadController.hasEnoughMaxAttachmentSize()) {
showConfirmDialogAction( showConfirmDialogAction(
context, context,
AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size( AppLocalizations.of(context).message_dialog_send_email_exceeds_maximum_size(
@@ -561,7 +564,7 @@ class ComposerController extends BaseController {
final email = await _generateEmail(mapDefaultMailboxId, userProfile); final email = await _generateEmail(mapDefaultMailboxId, userProfile);
final accountId = session.accounts.keys.first; final accountId = session.accounts.keys.first;
final sentMailboxId = mapDefaultMailboxId[PresentationMailbox.roleSent]; final sentMailboxId = mapDefaultMailboxId[PresentationMailbox.roleSent];
final submissionCreateId = Id(_uuid.v1()); final submissionCreateId = Id(const Uuid().v1());
mailboxDashBoardController.consumeState(_sendEmailInteractor.execute( mailboxDashBoardController.consumeState(_sendEmailInteractor.execute(
accountId, accountId,
@@ -633,88 +636,34 @@ class ComposerController extends BaseController {
} }
} }
void _pickFileSuccess(Success success) { void _pickFileSuccess(LocalFilePickerSuccess success) {
if (success is LocalFilePickerSuccess) { if (uploadController.hasEnoughMaxAttachmentSize(listFiles: success.pickedFiles)) {
if (_validateAttachmentsSize(listFiles: success.pickedFiles)) { _uploadAttachmentsAction(success.pickedFiles);
_uploadAttachmentsAction(success.pickedFiles);
} 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);
}
}
}
}
bool _validateAttachmentsSize({List<FileInfo>? listFiles}) {
final currentTotalAttachmentsSize = attachments.totalSize();
log('ComposerController::_validateAttachmentsSize(): $currentTotalAttachmentsSize');
num uploadedTotalSize = 0;
if (listFiles != null && listFiles.isNotEmpty) {
final uploadedListSize = listFiles.map((file) => file.fileSize).toList();
uploadedTotalSize = uploadedListSize.reduce((sum, size) => sum + size);
log('ComposerController::_validateAttachmentsSize(): uploadedTotalSize: $uploadedTotalSize');
}
final totalSizeReadyToUpload = currentTotalAttachmentsSize + uploadedTotalSize;
log('ComposerController::_validateAttachmentsSize(): totalSizeReadyToUpload: $totalSizeReadyToUpload');
final maxSizeAttachmentsPerEmail = mailboxDashBoardController.maxSizeAttachmentsPerEmail?.value;
if (maxSizeAttachmentsPerEmail != null) {
return totalSizeReadyToUpload <= maxSizeAttachmentsPerEmail;
} else { } else {
return false; 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);
}
} }
} }
void _uploadAttachmentsAction(List<FileInfo> pickedFiles) async { void _uploadAttachmentsAction(List<FileInfo> pickedFiles) async {
final session = mailboxDashBoardController.sessionCurrent; final session = mailboxDashBoardController.sessionCurrent;
if (session != null) { final accountId = mailboxDashBoardController.accountId.value;
final accountId = session.accounts.keys.first; if (session != null && accountId != null) {
final uploadUrl = session.getUploadUrl(accountId); final uploadUri = session.getUploadUri(accountId);
consumeState(_uploadMultipleAttachmentInteractor.execute(pickedFiles, accountId, uploadUrl)); uploadController.justUploadAttachmentsAction(pickedFiles, uploadUri);
} }
} }
void _uploadAttachmentsFailure(Failure failure) { void deleteAttachmentUploaded(UploadTaskId uploadId) {
if (currentContext != null) { uploadController.deleteFileUploaded(uploadId);
_appToast.showErrorToast(AppLocalizations.of(currentContext!).can_not_upload_this_file_as_attachments);
}
}
void _uploadAttachmentsSuccess(Success success) {
if (success is UploadAttachmentSuccess) {
attachments.add(success.attachment);
} else if (success is UploadMultipleAttachmentAllSuccess) {
final listAttachment = success.listResults.where((either) => either.isRight())
.map((either) => either
.map((result) => (result as UploadAttachmentSuccess).attachment)
.toIterable().first)
.toList();
attachments.addAll(listAttachment);
} else if (success is UploadMultipleAttachmentHasSomeFailure) {
final listAttachment = success.listResults
.map((either) => either
.map((result) => (result as UploadAttachmentSuccess).attachment)
.toIterable().first)
.toList();
attachments.addAll(listAttachment);
}
if (currentContext != null) {
_appToast.showSuccessToast(AppLocalizations.of(currentContext!).attachments_uploaded_successfully);
}
}
void removeAttachmentAction(Attachment attachmentRemoved) {
attachments.removeWhere((attachment) => attachment == attachmentRemoved);
} }
Future<bool> _isEmailChanged(BuildContext context, ComposerArguments arguments) async { Future<bool> _isEmailChanged(BuildContext context, ComposerArguments arguments) async {
@@ -759,7 +708,7 @@ class ComposerController extends BaseController {
final oldBccEmailAddress = recipients.value1; final oldBccEmailAddress = recipients.value1;
final isBccEmailAddressChanged = !oldBccEmailAddress.isSame(newBccEmailAddress); final isBccEmailAddressChanged = !oldBccEmailAddress.isSame(newBccEmailAddress);
final isAttachmentsChanged = !initialAttachments.isSame(attachments.toList()); final isAttachmentsChanged = !initialAttachments.isSame(uploadController.attachmentsUploaded.toList());
if (isEmailBodyChanged || isEmailSubjectChanged if (isEmailBodyChanged || isEmailSubjectChanged
|| isToEmailAddressChanged || isCcEmailAddressChanged || isToEmailAddressChanged || isCcEmailAddressChanged
@@ -816,8 +765,10 @@ class ComposerController extends BaseController {
void _getEmailContentSuccess(GetEmailContentSuccess success) { void _getEmailContentSuccess(GetEmailContentSuccess success) {
emailContents.value = success.emailContents; emailContents.value = success.emailContents;
attachments.value = success.attachments; if (success.attachments.isNotEmpty) {
initialAttachments = success.attachments; initialAttachments = success.attachments;
uploadController.initializeUploadAttachments(success.attachments);
}
} }
String? getEmailContentDraftsAsHtml() { String? getEmailContentDraftsAsHtml() {
@@ -14,6 +14,8 @@ import 'package:tmail_ui_user/features/composer/domain/state/upload_attachment_s
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ComposerView extends GetWidget<ComposerController> { class ComposerView extends GetWidget<ComposerController> {
@@ -29,10 +31,7 @@ class ComposerView extends GetWidget<ComposerController> {
return ResponsiveWidget( return ResponsiveWidget(
responsiveUtils: responsiveUtils, responsiveUtils: responsiveUtils,
mobile: _buildComposerViewForMobile(context), mobile: _buildComposerViewForMobile(context),
landscapeMobile: _buildComposerViewForMobile(context),
tablet: _buildComposerViewForTablet(context), tablet: _buildComposerViewForTablet(context),
tabletLarge: _buildComposerViewForTablet(context),
desktop: _buildComposerViewForTablet(context),
); );
} }
@@ -398,19 +397,26 @@ class ComposerView extends GetWidget<ComposerController> {
const Divider(color: AppColor.colorDividerComposer, height: 1), const Divider(color: AppColor.colorDividerComposer, height: 1),
Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)), Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)),
const Divider(color: AppColor.colorDividerComposer, height: 1), const Divider(color: AppColor.colorDividerComposer, height: 1),
Obx(() => controller.attachments.isNotEmpty Obx(() {
? Padding( final uploadAttachments = controller.uploadController.listUploadAttachments;
padding: const EdgeInsets.symmetric(horizontal: 16), if (uploadAttachments.isEmpty) {
child: _buildAttachmentsTitle(context, controller.attachments, controller.expandModeAttachments.value)) return const SizedBox.shrink();
: const SizedBox.shrink()), } else {
Obx(() => controller.attachments.isEmpty return Column(children: [
? _buildAttachmentsLoadingView() Padding(
: const SizedBox.shrink()), padding: const EdgeInsets.symmetric(horizontal: 16),
Obx(() => controller.attachments.isNotEmpty child: _buildAttachmentsTitle(context,
? Padding( uploadAttachments,
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), controller.expandModeAttachments.value)),
child: _buildAttachmentsList(context, controller.attachments, controller.expandModeAttachments.value)) _buildAttachmentsLoadingView(),
: const SizedBox.shrink()), Padding(
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
child: _buildAttachmentsList(context,
uploadAttachments,
controller.expandModeAttachments.value))
]);
}
}),
Padding( Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20), padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20),
child: _buildComposerEditor(context)), child: _buildComposerEditor(context)),
@@ -453,19 +459,26 @@ class ComposerView extends GetWidget<ComposerController> {
Padding( Padding(
padding: const EdgeInsets.only(left: 60, right: 25), padding: const EdgeInsets.only(left: 60, right: 25),
child: Column(children: [ child: Column(children: [
Obx(() => controller.attachments.isNotEmpty Obx(() {
? Padding( final uploadAttachments = controller.uploadController.listUploadAttachments;
padding: const EdgeInsets.symmetric(horizontal: 16), if (uploadAttachments.isEmpty) {
child: _buildAttachmentsTitle(context, controller.attachments, controller.expandModeAttachments.value)) return const SizedBox.shrink();
: const SizedBox.shrink()), } else {
Obx(() => controller.attachments.isEmpty return Column(children: [
? _buildAttachmentsLoadingView() Padding(
: const SizedBox.shrink()), padding: const EdgeInsets.symmetric(horizontal: 16),
Obx(() => controller.attachments.isNotEmpty child: _buildAttachmentsTitle(context,
? Padding( uploadAttachments,
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16), controller.expandModeAttachments.value)),
child: _buildAttachmentsList(context, controller.attachments, controller.expandModeAttachments.value)) _buildAttachmentsLoadingView(),
: const SizedBox.shrink()), Padding(
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
child: _buildAttachmentsList(context,
uploadAttachments,
controller.expandModeAttachments.value))
]);
}
}),
Padding( Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10), padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10),
child: _buildComposerEditor(context)), child: _buildComposerEditor(context)),
@@ -509,11 +522,15 @@ class ComposerView extends GetWidget<ComposerController> {
: const SizedBox.shrink())); : const SizedBox.shrink()));
} }
Widget _buildAttachmentsTitle(BuildContext context, List<Attachment> attachments, ExpandMode expandModeAttachment) { Widget _buildAttachmentsTitle(
BuildContext context,
List<UploadFileState> uploadFilesState,
ExpandMode expandModeAttachment
) {
return Row( return Row(
children: [ children: [
Text( Text(
'${AppLocalizations.of(context).attachments} (${filesize(attachments.totalSize(), 0)}):', '${AppLocalizations.of(context).attachments} (${filesize(uploadFilesState.totalSize, 0)}):',
style: const TextStyle(fontSize: 12, color: AppColor.colorHintEmailAddressInput, fontWeight: FontWeight.normal)), style: const TextStyle(fontSize: 12, color: AppColor.colorHintEmailAddressInput, fontWeight: FontWeight.normal)),
_buildAttachmentsLoadingView(padding: const EdgeInsets.only(left: 16), size: 16), _buildAttachmentsLoadingView(padding: const EdgeInsets.only(left: 16), size: 16),
const Spacer(), const Spacer(),
@@ -524,7 +541,7 @@ class ComposerView extends GetWidget<ComposerController> {
child: Text( child: Text(
expandModeAttachment == ExpandMode.EXPAND expandModeAttachment == ExpandMode.EXPAND
? AppLocalizations.of(context).hide ? AppLocalizations.of(context).hide
: '${AppLocalizations.of(context).show_all} (${attachments.length})', : '${AppLocalizations.of(context).show_all} (${uploadFilesState.length})',
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)), style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)),
onPressed: () => controller.toggleDisplayAttachments() onPressed: () => controller.toggleDisplayAttachments()
) )
@@ -533,23 +550,28 @@ class ComposerView extends GetWidget<ComposerController> {
); );
} }
Widget _buildAttachmentsList(BuildContext context, List<Attachment> attachments, ExpandMode expandMode) { Widget _buildAttachmentsList(
BuildContext context,
List<UploadFileState> uploadFilesState,
ExpandMode expandMode
) {
const double maxHeightItem = 60;
if (expandMode == ExpandMode.EXPAND) { if (expandMode == ExpandMode.EXPAND) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
return GridView.builder( return GridView.builder(
key: const Key('list_attachment_full'), key: const Key('list_attachment_full'),
primary: false, primary: false,
shrinkWrap: true, shrinkWrap: true,
itemCount: attachments.length, itemCount: uploadFilesState.length,
gridDelegate: SliverGridDelegateFixedHeight( gridDelegate: SliverGridDelegateFixedHeight(
height: 60, height: maxHeightItem,
crossAxisCount: _getMaxItemRowListAttachment(context, constraints), crossAxisCount: _getMaxItemRowListAttachment(context, constraints),
crossAxisSpacing: 8.0, crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0), mainAxisSpacing: 8.0),
itemBuilder: (context, index) => itemBuilder: (context, index) => AttachmentFileComposerBuilder(
(AttachmentFileComposerBuilder(context, imagePaths, attachments[index]) uploadFilesState[index],
..addOnDeleteAttachmentAction((attachment) => controller.removeAttachmentAction(attachment))) onDeleteAttachmentAction: (attachment) =>
.build() controller.deleteAttachmentUploaded(attachment.uploadTaskId))
); );
}); });
} else { } else {
@@ -557,20 +579,19 @@ class ComposerView extends GetWidget<ComposerController> {
return Align( return Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: SizedBox( child: SizedBox(
height: 60, height: maxHeightItem,
child: ListView.builder( child: ListView.builder(
key: const Key('list_attachment_minimize'), key: const Key('list_attachment_minimize'),
shrinkWrap: true, shrinkWrap: true,
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: attachments.length, itemCount: uploadFilesState.length,
itemBuilder: (context, index) => itemBuilder: (context, index) => AttachmentFileComposerBuilder(
(AttachmentFileComposerBuilder(context, imagePaths, attachments[index], uploadFilesState[index],
itemMargin: const EdgeInsets.only(right: 8), itemMargin: const EdgeInsets.only(right: 8),
maxWidth: _getMaxWidthItemListAttachment(context, constraints), maxWidth: _getMaxWidthItemListAttachment(context, constraints),
maxHeight: 60) onDeleteAttachmentAction: (attachment) =>
..addOnDeleteAttachmentAction((attachment) => controller.removeAttachmentAction(attachment))) controller.deleteAttachmentUploaded(attachment.uploadTaskId))
.build()
) )
) )
); );
@@ -15,6 +15,8 @@ import 'package:tmail_ui_user/features/composer/presentation/composer_controller
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ComposerView extends GetWidget<ComposerController> { class ComposerView extends GetWidget<ComposerController> {
@@ -488,54 +490,65 @@ class ComposerView extends GetWidget<ComposerController> {
} }
Widget _buildEditorAndAttachments(BuildContext context) { Widget _buildEditorAndAttachments(BuildContext context) {
return Column( return Obx(() {
children: [ final uploadAttachments = controller.uploadController.listUploadAttachments;
Obx(() => controller.attachments.isNotEmpty
? Padding( return Column(
padding: EdgeInsets.only(top: 4, bottom: 4, left: responsiveUtils.isMobile(context) ? 16 : 20, right: responsiveUtils.isMobile(context) ? 16: 0), children: [
child: _buildAttachmentsTitle(context, controller.attachments, controller.expandModeAttachments.value)) if (uploadAttachments.isNotEmpty)
: const SizedBox.shrink()), ...[
Obx(() => controller.attachments.isEmpty Padding(
? _buildAttachmentsLoadingView() padding: EdgeInsets.only(
: const SizedBox.shrink()), top: 4,
Obx(() => controller.attachments.isNotEmpty bottom: 4,
? Padding( left: responsiveUtils.isMobile(context) ? 16 : 20,
padding: EdgeInsets.only(bottom: 8, left: responsiveUtils.isMobile(context) ? 16 : 10, right: responsiveUtils.isMobile(context) ? 16 : 10), right: responsiveUtils.isMobile(context) ? 16: 0),
child: _buildAttachmentsList(context, controller.attachments, controller.expandModeAttachments.value)) child: _buildAttachmentsTitle(context,
: const SizedBox.shrink()), uploadAttachments,
Obx(() { controller.expandModeAttachments.value)),
if (controller.composerArguments.value != null) { _buildAttachmentsLoadingView(),
if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) { Padding(
final initContent = controller.textEditorWeb ?? ''.addEditorDefaultSpace(); padding: EdgeInsets.only(
return Expanded(child: Padding( bottom: 8,
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10), left: responsiveUtils.isMobile(context) ? 16 : 10,
child: _buildEditor(context, initContent))); right: responsiveUtils.isMobile(context) ? 16 : 10),
} else if (controller.composerArguments.value?.emailActionType == EmailActionType.edit) { child: _buildAttachmentsList(context,
final initContent = controller.textEditorWeb ?? controller.getEmailContentDraftsAsHtml(); uploadAttachments,
if (initContent != null) { controller.expandModeAttachments.value))
return Expanded(child: Padding( ],
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10), if (controller.composerArguments.value != null)
child: _buildEditor(context, initContent))); _buildComposeEditor(context)
} else { ]
return const Padding( );
padding: EdgeInsets.all(16), });
child: SizedBox( }
width: 30,
height: 30, Widget _buildComposeEditor(BuildContext context) {
child: CupertinoActivityIndicator(color: AppColor.colorLoading))); if (controller.composerArguments.value?.emailActionType == EmailActionType.compose) {
} final initContent = controller.textEditorWeb ?? ''.addEditorDefaultSpace();
} else { return Expanded(child: Padding(
final initContent = controller.textEditorWeb ?? controller.getEmailContentQuotedAsHtml(context, controller.composerArguments.value!); padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
return Expanded(child: Padding( child: _buildEditor(context, initContent)));
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10), } else if (controller.composerArguments.value?.emailActionType == EmailActionType.edit) {
child: _buildEditor(context, initContent))); final initContent = controller.textEditorWeb ?? controller.getEmailContentDraftsAsHtml();
} if (initContent != null) {
} else { return Expanded(child: Padding(
return const SizedBox.shrink(); padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
} child: _buildEditor(context, initContent)));
}), } else {
] return const Padding(
); padding: EdgeInsets.all(16),
child: SizedBox(
width: 30,
height: 30,
child: CupertinoActivityIndicator(color: AppColor.colorLoading)));
}
} else {
final initContent = controller.textEditorWeb ?? controller.getEmailContentQuotedAsHtml(context, controller.composerArguments.value!);
return Expanded(child: Padding(
padding: EdgeInsets.symmetric(horizontal: responsiveUtils.isMobile(context) ? 8 : 10),
child: _buildEditor(context, initContent)));
}
} }
Widget _buildEditor(BuildContext context, String initContent) { Widget _buildEditor(BuildContext context, String initContent) {
@@ -590,11 +603,14 @@ class ComposerView extends GetWidget<ComposerController> {
: const SizedBox.shrink())); : const SizedBox.shrink()));
} }
Widget _buildAttachmentsTitle(BuildContext context, List<Attachment> attachments, ExpandMode expandModeAttachment) { Widget _buildAttachmentsTitle(
BuildContext context,
List<UploadFileState> uploadFilesState,
ExpandMode expandModeAttachment) {
return Row( return Row(
children: [ children: [
Text( Text(
'${AppLocalizations.of(context).attachments} (${filesize(attachments.totalSize(), 0)}):', '${AppLocalizations.of(context).attachments} (${filesize(uploadFilesState.totalSize, 0)}):',
style: const TextStyle(fontSize: 12, color: AppColor.colorHintEmailAddressInput, fontWeight: FontWeight.normal)), style: const TextStyle(fontSize: 12, color: AppColor.colorHintEmailAddressInput, fontWeight: FontWeight.normal)),
_buildAttachmentsLoadingView(padding: const EdgeInsets.only(left: 16), size: 16), _buildAttachmentsLoadingView(padding: const EdgeInsets.only(left: 16), size: 16),
const Spacer(), const Spacer(),
@@ -605,7 +621,7 @@ class ComposerView extends GetWidget<ComposerController> {
child: Text( child: Text(
expandModeAttachment == ExpandMode.EXPAND expandModeAttachment == ExpandMode.EXPAND
? AppLocalizations.of(context).hide ? AppLocalizations.of(context).hide
: '${AppLocalizations.of(context).show_all} (${attachments.length})', : '${AppLocalizations.of(context).show_all} (${uploadFilesState.length})',
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)), style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: AppColor.colorTextButton)),
onPressed: () => controller.toggleDisplayAttachments() onPressed: () => controller.toggleDisplayAttachments()
) )
@@ -614,7 +630,10 @@ class ComposerView extends GetWidget<ComposerController> {
); );
} }
Widget _buildAttachmentsList(BuildContext context, List<Attachment> attachments, ExpandMode expandMode) { Widget _buildAttachmentsList(
BuildContext context,
List<UploadFileState> uploadFilesState,
ExpandMode expandMode) {
if (expandMode == ExpandMode.COLLAPSE) { if (expandMode == ExpandMode.COLLAPSE) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
@@ -628,14 +647,13 @@ class ComposerView extends GetWidget<ComposerController> {
shrinkWrap: true, shrinkWrap: true,
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: attachments.length, itemCount: uploadFilesState.length,
itemBuilder: (context, index) => itemBuilder: (context, index) => AttachmentFileComposerBuilder(
(AttachmentFileComposerBuilder(context, imagePaths, attachments[index], uploadFilesState[index],
itemMargin: const EdgeInsets.only(right: 8), itemMargin: const EdgeInsets.only(right: 8),
maxWidth: _getMaxWidthItemListAttachment(context, constraints), maxWidth: _getMaxWidthItemListAttachment(context, constraints),
maxHeight: 60) onDeleteAttachmentAction: (attachment) =>
..addOnDeleteAttachmentAction((attachment) => controller.removeAttachmentAction(attachment))) controller.deleteAttachmentUploaded(attachment.uploadTaskId))
.build()
) )
) )
); );
@@ -1,88 +1,131 @@
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/model.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnDeleteAttachmentAction = void Function(Attachment attachment); typedef OnDeleteAttachmentAction = void Function(UploadFileState fileState);
class AttachmentFileComposerBuilder { class AttachmentFileComposerBuilder extends StatelessWidget with AppLoaderMixin {
final BuildContext context; final _imagePaths = Get.find<ImagePaths>();
final ImagePaths imagePaths;
final Attachment attachment; final UploadFileState fileState;
final double? maxWidth; final double? maxWidth;
final double? maxHeight;
final EdgeInsets? itemMargin; final EdgeInsets? itemMargin;
final OnDeleteAttachmentAction? onDeleteAttachmentAction;
final Widget? buttonAction;
OnDeleteAttachmentAction? _onDeleteAttachmentAction; AttachmentFileComposerBuilder(this.fileState, {
super.key,
this.maxWidth,
this.itemMargin,
this.buttonAction,
this.onDeleteAttachmentAction,
});
Widget? buttonAction; @override
Widget build(BuildContext context) {
AttachmentFileComposerBuilder(
this.context,
this.imagePaths,
this.attachment,
{
this.maxWidth,
this.maxHeight,
this.itemMargin,
}
);
void addOnDeleteAttachmentAction(OnDeleteAttachmentAction onDeleteAttachmentAction) {
_onDeleteAttachmentAction = onDeleteAttachmentAction;
}
Widget build() {
return Theme( return Theme(
data: ThemeData( data: ThemeData(
splashColor: Colors.transparent , splashColor: Colors.transparent ,
highlightColor: Colors.transparent), highlightColor: Colors.transparent),
child: Container( child: Container(
margin: itemMargin ?? EdgeInsets.zero, margin: itemMargin ?? EdgeInsets.zero,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
alignment: Alignment.center, alignment: Alignment.center,
width: maxWidth, width: maxWidth,
height: maxHeight, decoration: BoxDecoration(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(10),
borderRadius: BorderRadius.circular(10), border: Border.all(color: AppColor.colorInputBorderCreateMailbox),
border: Border.all(color: AppColor.colorInputBorderCreateMailbox), color: Colors.white),
color: Colors.white), child: Stack(children: [
child: Stack(children: [ ListTile(
ListTile( contentPadding: EdgeInsets.zero,
contentPadding: const EdgeInsets.only(right: kIsWeb ? 16 : 18), focusColor: AppColor.primaryColor,
hoverColor: AppColor.primaryColor,
onTap: () {}, onTap: () {},
leading: Transform( leading: Padding(
transform: Matrix4.translationValues(8.0, kIsWeb ? -3.0 : -5.0, 0.0), padding: const EdgeInsets.only(
child: SvgPicture.asset(imagePaths.icFileAttachment, fit: BoxFit.fill)), left: 8,
bottom: BuildUtils.isWeb ? 6 : 14),
child: SvgPicture.asset(
fileState.getIcon(_imagePaths),
width: 40,
height: 40,
fit: BoxFit.fill),
),
title: Transform( title: Transform(
transform: Matrix4.translationValues(kIsWeb ? -4.0 : -10.0, kIsWeb ? -6.0 : -9.0, 0.0), transform: Matrix4.translationValues(
child: Text( BuildUtils.isWeb ? 0.0 : -8.0,
attachment.name ?? '', BuildUtils.isWeb ? -8.0 : -10.0,
maxLines: 2, 0.0),
overflow: TextOverflow.ellipsis, child: Padding(
style: const TextStyle(fontSize: 12, color: AppColor.colorNameEmail, fontWeight: FontWeight.w500), padding: const EdgeInsets.only(right: BuildUtils.isWeb ? 20 : 16),
)), child: Text(
subtitle: attachment.size != null && attachment.size?.value != 0 fileState.fileName,
maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w500),
),
)
),
subtitle: fileState.fileSize != 0
? Transform( ? Transform(
transform: Matrix4.translationValues(kIsWeb ? -4.0 : -10.0, kIsWeb ? -5.0 : -8.0, 0.0), transform: Matrix4.translationValues(
BuildUtils.isWeb ? 0.0 : -8.0,
BuildUtils.isWeb ? -8.0 : -10.0,
0.0),
child: Text( child: Text(
filesize(attachment.size?.value, 0), filesize(fileState.fileSize),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: CommonTextStyle.defaultTextOverFlow,
style: const TextStyle(fontSize: 10, color: AppColor.colorContentEmail, fontWeight: FontWeight.normal))) style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail)))
: null, : null,
), ),
Positioned(right: kIsWeb ? -5 : -12, top: kIsWeb ? -5 : -12, child: buildIconWeb( Positioned(
icon: SvgPicture.asset(imagePaths.icDeleteAttachment, fit: BoxFit.fill), right: BuildUtils.isWeb ? -5 : -12,
tooltip: AppLocalizations.of(context).delete, top: BuildUtils.isWeb ? -5 : -12,
onTap: () => _onDeleteAttachmentAction?.call(attachment))) child: buildIconWeb(
]), icon: SvgPicture.asset(_imagePaths.icDeleteAttachment, fit: BoxFit.fill),
) tooltip: AppLocalizations.of(context).delete,
onTap: () {
if (onDeleteAttachmentAction != null) {
onDeleteAttachmentAction!.call(fileState);
}
}
)
),
Align(alignment: Alignment.bottomCenter, child: _progressLoading),
]),
)
); );
} }
Widget get _progressLoading {
switch(fileState.uploadStatus) {
case UploadFileStatus.waiting:
return Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 50),
child: horizontalLoadingWidget);
case UploadFileStatus.uploading:
return Padding(
padding: const EdgeInsets.only(top: 50),
child: horizontalPercentLoadingWidget(fileState.percentUploading));
case UploadFileStatus.uploadFailed:
case UploadFileStatus.succeed:
return const SizedBox.shrink();
}
}
} }
@@ -406,9 +406,11 @@ class EmailView extends GetWidget<EmailController> with NetworkConnectionMixin {
} }
int _getAttachmentLimitDisplayed(BuildContext context) { int _getAttachmentLimitDisplayed(BuildContext context) {
if (responsiveUtils.isScreenWithShortestSide(context)) { if (responsiveUtils.isMobile(context)
|| responsiveUtils.isLandscapeMobile(context)) {
return 2; return 2;
} else if (responsiveUtils.isTablet(context)) { } else if (responsiveUtils.isTablet(context) ||
responsiveUtils.isLandscapeTablet(context)) {
return 3; return 3;
} else { } else {
return 4; return 4;
@@ -82,8 +82,8 @@ class AttachmentFileTileBuilder {
padding: const EdgeInsets.only(left: 8, bottom: BuildUtils.isWeb ? 6 : 14), padding: const EdgeInsets.only(left: 8, bottom: BuildUtils.isWeb ? 6 : 14),
child: SvgPicture.asset( child: SvgPicture.asset(
_attachment.getIcon(_imagePaths), _attachment.getIcon(_imagePaths),
width: 44, width: 40,
height: 44, height: 40,
fit: BoxFit.fill), fit: BoxFit.fill),
), ),
title: Transform( title: Transform(
@@ -31,7 +31,6 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na
import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart';
import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart';
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
import 'package:uuid/uuid.dart';
class MailboxBindings extends BaseBindings { class MailboxBindings extends BaseBindings {
@@ -43,7 +42,6 @@ class MailboxBindings extends BaseBindings {
void _bindingsUtils() { void _bindingsUtils() {
Get.lazyPut(() => TreeBuilder()); Get.lazyPut(() => TreeBuilder());
Get.lazyPut(() => const Uuid());
} }
@override @override
@@ -57,7 +55,6 @@ class MailboxBindings extends BaseBindings {
Get.find<VerifyNameInteractor>(), Get.find<VerifyNameInteractor>(),
Get.find<RenameMailboxInteractor>(), Get.find<RenameMailboxInteractor>(),
Get.find<MoveMailboxInteractor>(), Get.find<MoveMailboxInteractor>(),
Get.find<Uuid>(),
Get.find<TreeBuilder>(), Get.find<TreeBuilder>(),
)); ));
} }
@@ -74,6 +74,7 @@ class MailboxController extends BaseMailboxController {
final _appToast = Get.find<AppToast>(); final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>(); final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>(); final _responsiveUtils = Get.find<ResponsiveUtils>();
final _uuid = Get.find<Uuid>();
final GetAllMailboxInteractor _getAllMailboxInteractor; final GetAllMailboxInteractor _getAllMailboxInteractor;
final RefreshAllMailboxInteractor _refreshAllMailboxInteractor; final RefreshAllMailboxInteractor _refreshAllMailboxInteractor;
@@ -83,7 +84,6 @@ class MailboxController extends BaseMailboxController {
final VerifyNameInteractor _verifyNameInteractor; final VerifyNameInteractor _verifyNameInteractor;
final RenameMailboxInteractor _renameMailboxInteractor; final RenameMailboxInteractor _renameMailboxInteractor;
final MoveMailboxInteractor _moveMailboxInteractor; final MoveMailboxInteractor _moveMailboxInteractor;
final Uuid _uuid;
final listMailboxSearched = <PresentationMailbox>[].obs; final listMailboxSearched = <PresentationMailbox>[].obs;
final searchState = SearchState.initial().obs; final searchState = SearchState.initial().obs;
@@ -110,7 +110,6 @@ class MailboxController extends BaseMailboxController {
this._verifyNameInteractor, this._verifyNameInteractor,
this._renameMailboxInteractor, this._renameMailboxInteractor,
this._moveMailboxInteractor, this._moveMailboxInteractor,
this._uuid,
treeBuilder, treeBuilder,
) : super(treeBuilder); ) : super(treeBuilder);
@@ -21,7 +21,6 @@ import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_draft
import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart';
import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_bindings.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_bindings.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_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/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
@@ -384,7 +383,7 @@ class MailboxDashBoardController extends ReloadableController {
void closeComposerOverlay() { void closeComposerOverlay() {
routerArguments = null; routerArguments = null;
Get.delete<ComposerController>(); ComposerBindings().dispose();
composerOverlayState.value = ComposerOverlayState.inActive; composerOverlayState.value = ComposerOverlayState.inActive;
} }
@@ -6,23 +6,15 @@ import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_ide
import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/edit_identity_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart';
import 'package:uuid/uuid.dart';
class IdentitiesBindings extends BaseBindings { class IdentitiesBindings extends BaseBindings {
@override
void dependencies() {
Get.lazyPut(() => const Uuid());
super.dependencies();
}
@override @override
void bindingsController() { void bindingsController() {
Get.lazyPut(() => IdentitiesController( Get.lazyPut(() => IdentitiesController(
Get.find<GetAllIdentitiesInteractor>(), Get.find<GetAllIdentitiesInteractor>(),
Get.find<DeleteIdentityInteractor>(), Get.find<DeleteIdentityInteractor>(),
Get.find<CreateNewIdentityInteractor>(), Get.find<CreateNewIdentityInteractor>(),
Get.find<Uuid>(),
Get.find<EditIdentityInteractor>(), Get.find<EditIdentityInteractor>(),
)); ));
} }
@@ -32,10 +32,10 @@ class IdentitiesController extends BaseController {
final _appToast = Get.find<AppToast>(); final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>(); final _imagePaths = Get.find<ImagePaths>();
final _responsiveUtils = Get.find<ResponsiveUtils>(); final _responsiveUtils = Get.find<ResponsiveUtils>();
final _uuid = Get.find<Uuid>();
final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor;
final CreateNewIdentityInteractor _createNewIdentityInteractor; final CreateNewIdentityInteractor _createNewIdentityInteractor;
final Uuid _uuid;
final DeleteIdentityInteractor _deleteIdentityInteractor; final DeleteIdentityInteractor _deleteIdentityInteractor;
final EditIdentityInteractor _editIdentityInteractor; final EditIdentityInteractor _editIdentityInteractor;
@@ -51,7 +51,6 @@ class IdentitiesController extends BaseController {
this._getAllIdentitiesInteractor, this._getAllIdentitiesInteractor,
this._deleteIdentityInteractor, this._deleteIdentityInteractor,
this._createNewIdentityInteractor, this._createNewIdentityInteractor,
this._uuid,
this._editIdentityInteractor, this._editIdentityInteractor,
); );
+7 -1
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2022-07-08T15:25:15.196374", "@@last_modified": "2022-07-05T19:24:56.876056",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -1561,5 +1561,11 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"messageDialogSendEmailUploadingAttachment": "Your message could not be sent because it uploading attachment",
"@messageDialogSendEmailUploadingAttachment": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
import 'package:uuid/uuid.dart';
class CoreBindings extends Bindings { class CoreBindings extends Bindings {
@@ -18,6 +19,7 @@ class CoreBindings extends Bindings {
_bindingToast(); _bindingToast();
_bindingDeviceManager(); _bindingDeviceManager();
_bindingReceivingSharingStream(); _bindingReceivingSharingStream();
_bindingUtils();
} }
void _bindingAppImagePaths() { void _bindingAppImagePaths() {
@@ -53,4 +55,8 @@ class CoreBindings extends Bindings {
void _bindingReceivingSharingStream() { void _bindingReceivingSharingStream() {
Get.put(EmailReceiveManager()); Get.put(EmailReceiveManager());
} }
void _bindingUtils() {
Get.put(const Uuid());
}
} }
@@ -7,7 +7,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/http/http_client.dart' as JmapHttpClient; import 'package:jmap_dart_client/http/http_client.dart' as JmapHttpClient;
import 'package:tmail_ui_user/features/composer/data/network/composer_api.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart';
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
@@ -77,7 +76,6 @@ class NetworkBindings extends Bindings {
Get.put(EmailAPI( Get.put(EmailAPI(
Get.find<JmapHttpClient.HttpClient>(), Get.find<JmapHttpClient.HttpClient>(),
Get.find<DownloadManager>())); Get.find<DownloadManager>()));
Get.put(ComposerAPI(Get.find<DioClient>()));
Get.put(ManageAccountAPI(Get.find<JmapHttpClient.HttpClient>())); Get.put(ManageAccountAPI(Get.find<JmapHttpClient.HttpClient>()));
} }
@@ -1601,4 +1601,11 @@ class AppLocalizations {
'Russian', 'Russian',
name: 'languageRussian'); name: 'languageRussian');
} }
String get messageDialogSendEmailUploadingAttachment {
return Intl.message(
'Your message could not be sent because it uploading attachment',
name: 'messageDialogSendEmailUploadingAttachment'
);
}
} }
+2 -1
View File
@@ -16,12 +16,13 @@ extension SessionExtension on Session {
return downloadUrlDecode; return downloadUrlDecode;
} }
Uri getUploadUrl(AccountId accountId) { Uri getUploadUri(AccountId accountId) {
final baseUrl = '${uploadUrl.origin}${uploadUrl.path}'; final baseUrl = '${uploadUrl.origin}${uploadUrl.path}';
final uploadUriTemplate = UriTemplate('${Uri.decodeFull(baseUrl)}'); final uploadUriTemplate = UriTemplate('${Uri.decodeFull(baseUrl)}');
final uploadUri = uploadUriTemplate.expand({ final uploadUri = uploadUriTemplate.expand({
'accountId' : '${accountId.id.value}' 'accountId' : '${accountId.id.value}'
}); });
log('SessionExtension::getUploadUri(): uploadUri: $uploadUri');
return Uri.parse(uploadUri); return Uri.parse(uploadUri);
} }
} }
+2 -2
View File
@@ -34,7 +34,7 @@ class UploadResponse with EquatableMixin {
} }
extension UploadResponseExtension on UploadResponse { extension UploadResponseExtension on UploadResponse {
Attachment toAttachmentFile(String nameFile, {MediaType? mediaType}) { Attachment toAttachment(String nameFile) {
return Attachment(blobId: blobId, size: UnsignedInt(size), name: nameFile, type: mediaType ?? type); return Attachment(blobId: blobId, size: UnsignedInt(size), name: nameFile, type: type);
} }
} }
+2
View File
@@ -172,6 +172,8 @@ dependencies:
# Isolate # Isolate
worker_manager: 4.4.0 worker_manager: 4.4.0
async: 2.8.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter