TF-32 Add presentation layer of export attachment for ios platform
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
import flutter_downloader
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
@@ -8,6 +9,11 @@ import Flutter
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
FlutterDownloaderPlugin.setPluginRegistrantCallback { registry in
|
||||
if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
|
||||
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
|
||||
}
|
||||
}
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>To save photo to library, we need permission to access your photo library.</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
|
||||
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
|
||||
@@ -22,17 +23,20 @@ class EmailBindings extends Bindings {
|
||||
Get.lazyPut<EmailRepository>(() => Get.find<EmailRepositoryImpl>());
|
||||
Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>()));
|
||||
Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>()));
|
||||
Get.put(EmailController(
|
||||
Get.find<GetEmailContentInteractor>(),
|
||||
Get.find<MarkAsEmailReadInteractor>()));
|
||||
Get.lazyPut(() => CredentialRepositoryImpl(Get.find<SharedPreferences>()));
|
||||
Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>());
|
||||
Get.lazyPut(() => DownloadAttachmentsInteractor(
|
||||
Get.find<EmailRepository>(),
|
||||
Get.find<CredentialRepository>()));
|
||||
Get.lazyPut(() => ExportAttachmentInteractor(
|
||||
Get.find<EmailRepository>(),
|
||||
Get.find<CredentialRepository>()));
|
||||
Get.put(EmailController(
|
||||
Get.find<GetEmailContentInteractor>(),
|
||||
Get.find<DownloadAttachmentsInteractor>(),
|
||||
Get.find<DeviceManager>()));
|
||||
Get.find<GetEmailContentInteractor>(),
|
||||
Get.find<MarkAsEmailReadInteractor>(),
|
||||
Get.find<DownloadAttachmentsInteractor>(),
|
||||
Get.find<DeviceManager>(),
|
||||
Get.find<AppToast>(),
|
||||
Get.find<ExportAttachmentInteractor>()));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
@@ -18,6 +23,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_da
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
import 'package:share/share.dart' as share_library;
|
||||
|
||||
class EmailController extends BaseController {
|
||||
|
||||
@@ -28,22 +34,27 @@ class EmailController extends BaseController {
|
||||
final MarkAsEmailReadInteractor _markAsEmailReadInteractor;
|
||||
final DownloadAttachmentsInteractor _downloadAttachmentsInteractor;
|
||||
final DeviceManager _deviceManager;
|
||||
final AppToast _appToast;
|
||||
final ExportAttachmentInteractor _exportAttachmentInteractor;
|
||||
|
||||
final emailAddressExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
EmailContent? emailContent;
|
||||
final attachmentsExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
final emailContent = Rxn<EmailContent>();
|
||||
|
||||
EmailController(this._getEmailContentInteractor, this._markAsEmailReadInteractor);
|
||||
EmailController(
|
||||
this._getEmailContentInteractor,
|
||||
this._markAsEmailReadInteractor,
|
||||
this._downloadAttachmentsInteractor,
|
||||
this._deviceManager
|
||||
this._deviceManager,
|
||||
this._appToast,
|
||||
this._exportAttachmentInteractor,
|
||||
);
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
mailboxDashBoardController.selectedEmail.listen((presentationEmail) {
|
||||
toggleDisplayEmailAddressAction(expandMode: ExpandMode.COLLAPSE);
|
||||
_clearEmailContent();
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId != null && presentationEmail != null) {
|
||||
_getEmailContentAction(accountId, presentationEmail.id);
|
||||
@@ -75,13 +86,19 @@ class EmailController extends BaseController {
|
||||
(failure) {
|
||||
if (failure is MarkAsEmailReadFailure) {
|
||||
_markAsEmailReadFailure(failure);
|
||||
} else if (failure is DownloadAttachmentsFailure) {
|
||||
_downloadAttachmentsFailure(failure);
|
||||
} else if (failure is ExportAttachmentFailure) {
|
||||
_exportAttachmentFailureAction(failure);
|
||||
}
|
||||
},
|
||||
(success) {
|
||||
if (success is GetEmailContentSuccess) {
|
||||
emailContent = success.emailContent;
|
||||
emailContent.value = success.emailContent;
|
||||
} else if (success is MarkAsEmailReadSuccess) {
|
||||
_markAsEmailReadSuccess(success);
|
||||
} else if (success is ExportAttachmentSuccess) {
|
||||
_exportAttachmentSuccessAction(success);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -90,6 +107,12 @@ class EmailController extends BaseController {
|
||||
void onError(error) {
|
||||
}
|
||||
|
||||
void _clearEmailContent() {
|
||||
toggleDisplayEmailAddressAction(expandMode: ExpandMode.COLLAPSE);
|
||||
attachmentsExpandMode.value = ExpandMode.COLLAPSE;
|
||||
emailContent.value = null;
|
||||
}
|
||||
|
||||
void toggleDisplayEmailAddressAction({required ExpandMode expandMode}) {
|
||||
emailAddressExpandMode.value = expandMode;
|
||||
}
|
||||
@@ -114,10 +137,17 @@ class EmailController extends BaseController {
|
||||
backToThreadView();
|
||||
}
|
||||
|
||||
void toggleDisplayAttachmentsAction() {
|
||||
final newExpandMode = attachmentsExpandMode.value == ExpandMode.COLLAPSE
|
||||
? ExpandMode.EXPAND
|
||||
: ExpandMode.COLLAPSE;
|
||||
attachmentsExpandMode.value = newExpandMode;
|
||||
}
|
||||
|
||||
void downloadAttachments(BuildContext context, List<Attachment> attachments) async {
|
||||
final needRequestPermission = await _deviceManager.isNeedRequestStoragePermissionOnAndroid();
|
||||
|
||||
if (Platform.isAndroid && needRequestPermission) {
|
||||
if (needRequestPermission) {
|
||||
final status = await Permission.storage.status;
|
||||
switch (status) {
|
||||
case PermissionStatus.granted:
|
||||
@@ -147,10 +177,55 @@ class EmailController extends BaseController {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId != null && mailboxDashBoardController.sessionCurrent != null) {
|
||||
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent!.getDownloadUrl();
|
||||
await _downloadAttachmentsInteractor.execute(attachments, accountId, baseDownloadUrl)
|
||||
.then((result) => result.fold(
|
||||
(failure) => AppLocalizations.of(context).attachment_download_failed,
|
||||
(success) => AppLocalizations.of(context).attachment_download_successfully));
|
||||
consumeState(_downloadAttachmentsInteractor.execute(attachments, accountId, baseDownloadUrl));
|
||||
}
|
||||
}
|
||||
|
||||
void _downloadAttachmentsFailure(Failure failure) {
|
||||
if (Get.context != null) {
|
||||
_appToast.showErrorToast(AppLocalizations.of(Get.context!).attachment_download_failed);
|
||||
}
|
||||
}
|
||||
|
||||
void exportAttachment(BuildContext context, Attachment attachment) {
|
||||
final cancelToken = CancelToken();
|
||||
_showDownloadingFileDialog(context, attachment, cancelToken);
|
||||
_exportAttachmentAction(attachment, cancelToken);
|
||||
}
|
||||
|
||||
void _showDownloadingFileDialog(BuildContext context, Attachment attachment, CancelToken cancelToken) {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (_) => (DownloadingFileDialogBuilder()
|
||||
..key(Key('downloading_file_dialog'))
|
||||
..title(AppLocalizations.of(context).preparing_to_export)
|
||||
..content(AppLocalizations.of(context).downloading_file(attachment.name ?? ''))
|
||||
..actionText(AppLocalizations.of(context).cancel)
|
||||
..addCancelDownloadActionClick(() {
|
||||
cancelToken.cancel([AppLocalizations.of(context).user_cancel_download_file]);
|
||||
Get.back();
|
||||
}))
|
||||
.build());
|
||||
}
|
||||
|
||||
void _exportAttachmentAction(Attachment attachment, CancelToken cancelToken) async {
|
||||
final accountId = mailboxDashBoardController.accountId.value;
|
||||
if (accountId != null && mailboxDashBoardController.sessionCurrent != null) {
|
||||
final baseDownloadUrl = mailboxDashBoardController.sessionCurrent!.getDownloadUrl();
|
||||
consumeState(_exportAttachmentInteractor.execute(attachment, accountId, baseDownloadUrl, cancelToken));
|
||||
}
|
||||
}
|
||||
|
||||
void _exportAttachmentFailureAction(Failure failure) {
|
||||
if (failure is ExportAttachmentFailure && !(failure.exception is CancelDownloadFileException)) {
|
||||
popBack();
|
||||
}
|
||||
}
|
||||
|
||||
void _exportAttachmentSuccessAction(Success success) async {
|
||||
popBack();
|
||||
if (success is ExportAttachmentSuccess) {
|
||||
await share_library.Share.shareFiles([success.filePath]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,12 +240,12 @@ class EmailController extends BaseController {
|
||||
|
||||
void pressEmailAction(EmailActionType emailActionType) {
|
||||
if (canComposeEmail()) {
|
||||
Get.toNamed(
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
emailActionType: emailActionType,
|
||||
presentationEmail: mailboxDashBoardController.selectedEmail.value!,
|
||||
emailContent: emailContent,
|
||||
emailContent: emailContent.value,
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
mapMailboxId: mailboxDashBoardController.mapMailboxId));
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_file_tile_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/list_attachment_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/message_content_tile_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
|
||||
class EmailView extends GetWidget {
|
||||
|
||||
@@ -138,11 +142,11 @@ class EmailView extends GetWidget {
|
||||
imagePaths,
|
||||
emailController.mailboxDashBoardController.selectedEmail.value,
|
||||
emailController.emailAddressExpandMode.value)
|
||||
.onOpenExpandAddressReceiverActionClick(() => emailController.toggleDisplayEmailAddressAction(expandMode: ExpandMode.EXPAND))
|
||||
.onOpenExpandAddressReceiverActionClick(() => emailController.toggleDisplayAttachmentsAction())
|
||||
.build()),
|
||||
_buildLoadingView(),
|
||||
_buildListMessageContent(),
|
||||
_buildListAttachments(context),
|
||||
_buildListMessageContent(),
|
||||
],
|
||||
)
|
||||
);
|
||||
@@ -150,66 +154,151 @@ class EmailView extends GetWidget {
|
||||
|
||||
Widget _buildListAttachments(BuildContext context) {
|
||||
if (emailController.mailboxDashBoardController.selectedEmail.value?.hasAttachment == true) {
|
||||
return Obx(() => emailController.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) {
|
||||
if (success is GetEmailContentSuccess && success.emailContent != null) {
|
||||
final attachments = success.emailContent!.getListAttachment();
|
||||
return attachments.isNotEmpty
|
||||
? GridView.builder(
|
||||
key: Key('list_attachment'),
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
itemCount: attachments.length,
|
||||
gridDelegate: SliverGridDelegateFixedHeight(
|
||||
height: 60,
|
||||
crossAxisCount: responsiveUtils.isMobile(context) ? 2 : 4,
|
||||
crossAxisSpacing: 16.0,
|
||||
mainAxisSpacing: 8.0),
|
||||
itemBuilder: (context, index) =>
|
||||
(AttachmentFileTileBuilder(imagePaths, attachments[index])
|
||||
..onDownloadAttachmentFileActionClick((attachment) =>
|
||||
emailController.downloadAttachments(context, [attachment])))
|
||||
.build())
|
||||
return Obx(() {
|
||||
if (emailController.emailContent.value != null) {
|
||||
final attachments = emailController.emailContent.value!.getListAttachment();
|
||||
return attachments.isNotEmpty
|
||||
? _buildAttachmentsBody(context, attachments)
|
||||
: SizedBox.shrink();
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
int _getAttachmentLimitDisplayed(BuildContext context) {
|
||||
if (responsiveUtils.isMobile(context)) {
|
||||
return 2;
|
||||
} else if (responsiveUtils.isTablet(context)) {
|
||||
return 4;
|
||||
} else {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAttachmentsBody(BuildContext context, List<Attachment> attachments) {
|
||||
final attachmentLimitDisplayed = _getAttachmentLimitDisplayed(context);
|
||||
final countAttachments = _getListAttachmentsSize(
|
||||
context,
|
||||
emailController.attachmentsExpandMode.value,
|
||||
attachments,
|
||||
attachmentLimitDisplayed);
|
||||
final isExpand = emailController.attachmentsExpandMode.value == ExpandMode.EXPAND
|
||||
&& attachments.length > attachmentLimitDisplayed;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (isExpand)
|
||||
Padding(
|
||||
padding: EdgeInsets.zero,
|
||||
child: _buildAttachmentsHeader(context, attachments)),
|
||||
GridView.builder(
|
||||
key: Key('list_attachment'),
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(top: isExpand ? 0 : 16),
|
||||
itemCount: countAttachments,
|
||||
gridDelegate: SliverGridDelegateFixedHeight(
|
||||
height: 60,
|
||||
crossAxisCount: attachmentLimitDisplayed,
|
||||
crossAxisSpacing: 16.0,
|
||||
mainAxisSpacing: 8.0),
|
||||
itemBuilder: (context, index) =>
|
||||
(AttachmentFileTileBuilder(
|
||||
imagePaths,
|
||||
attachments[index],
|
||||
attachments.length,
|
||||
attachmentLimitDisplayed)
|
||||
..setExpandMode((countAttachments - 1 == index) ? emailController.attachmentsExpandMode.value : null)
|
||||
..onExpandAttachmentActionClick(() => emailController.toggleDisplayAttachmentsAction())
|
||||
..onDownloadAttachmentFileActionClick((attachment) {
|
||||
if (Platform.isAndroid) {
|
||||
emailController.downloadAttachments(context, [attachment]);
|
||||
} else {
|
||||
emailController.exportAttachment(context, attachment);
|
||||
}
|
||||
}))
|
||||
.build())
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
int _getListAttachmentsSize(
|
||||
BuildContext context,
|
||||
ExpandMode expandMode,
|
||||
List<Attachment> attachments,
|
||||
int limitDisplayAttachment
|
||||
) {
|
||||
if (attachments.length > limitDisplayAttachment) {
|
||||
return expandMode == ExpandMode.EXPAND
|
||||
? attachments.length
|
||||
: attachments.sublist(0, limitDisplayAttachment).length;
|
||||
} else {
|
||||
return attachments.length;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAttachmentsHeader(BuildContext context, List<Attachment> attachments) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${AppLocalizations.of(context).count_attachment(attachments.length)}',
|
||||
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8),
|
||||
child: Text(
|
||||
'(${filesize(attachments.totalSize(), 1)})',
|
||||
style: TextStyle(fontSize: 12, color: AppColor.nameUserColor, fontWeight: FontWeight.w500)))
|
||||
],
|
||||
),
|
||||
if (attachments.length > 2)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 8),
|
||||
child: IconButton(
|
||||
icon: SvgPicture.asset(imagePaths.icExpandAttachment,
|
||||
width: 20,
|
||||
height: 20,
|
||||
fit: BoxFit.fill),
|
||||
onPressed: () => emailController.toggleDisplayAttachmentsAction()
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListMessageContent() {
|
||||
return Obx(() => emailController.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) {
|
||||
if (success is GetEmailContentSuccess && success.emailContent != null) {
|
||||
final messageContents = success.emailContent!.getListMessageContent();
|
||||
final attachmentsInline = success.emailContent!.getListAttachmentInline();
|
||||
return messageContents.isNotEmpty
|
||||
? ListView.builder(
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
key: Key('list_message_content'),
|
||||
itemCount: messageContents.length,
|
||||
itemBuilder: (context, index) =>
|
||||
return Obx(() {
|
||||
if (emailController.emailContent.value != null) {
|
||||
final messageContents = emailController.emailContent.value!.getListMessageContent();
|
||||
final attachmentsInline = emailController.emailContent.value!.getListAttachmentInline();
|
||||
return messageContents.isNotEmpty
|
||||
? ListView.builder(
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
key: Key('list_message_content'),
|
||||
itemCount: messageContents.length,
|
||||
itemBuilder: (context, index) =>
|
||||
MessageContentTileBuilder(
|
||||
htmlMessagePurifier: htmlMessagePurifier,
|
||||
messageContent: messageContents[index],
|
||||
attachmentInlines: attachmentsInline,
|
||||
session: emailController.mailboxDashBoardController.sessionCurrent,
|
||||
accountId: emailController.mailboxDashBoardController.accountId.value)
|
||||
.build())
|
||||
: SizedBox.shrink();
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
})
|
||||
);
|
||||
.build())
|
||||
: SizedBox.shrink();
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:model/model.dart';
|
||||
|
||||
extension ListAttachmentExtension on List<Attachment> {
|
||||
|
||||
num totalSize() {
|
||||
num totalSize = 0;
|
||||
forEach((attachment) {
|
||||
if (attachment.size != null) {
|
||||
totalSize += attachment.size!.value;
|
||||
}
|
||||
});
|
||||
return totalSize;
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,38 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
typedef OnDownloadAttachmentFileActionClick = void Function(Attachment attachment);
|
||||
typedef OnExpandAttachmentActionClick = void Function();
|
||||
|
||||
class AttachmentFileTileBuilder {
|
||||
|
||||
final ImagePaths _imagePaths;
|
||||
final Attachment _attachment;
|
||||
final int _attachmentSize;
|
||||
final int _limitDisplayAttachment;
|
||||
ExpandMode? _expandMode;
|
||||
|
||||
OnDownloadAttachmentFileActionClick? _onDownloadAttachmentFileActionClick;
|
||||
OnExpandAttachmentActionClick? _onExpandAttachmentActionClick;
|
||||
|
||||
AttachmentFileTileBuilder(this._imagePaths, this._attachment);
|
||||
AttachmentFileTileBuilder(
|
||||
this._imagePaths,
|
||||
this._attachment,
|
||||
this._attachmentSize,
|
||||
this._limitDisplayAttachment,
|
||||
);
|
||||
|
||||
void onDownloadAttachmentFileActionClick(OnDownloadAttachmentFileActionClick onDownloadAttachmentFileActionClick) {
|
||||
_onDownloadAttachmentFileActionClick = onDownloadAttachmentFileActionClick;
|
||||
}
|
||||
|
||||
void onExpandAttachmentActionClick(OnExpandAttachmentActionClick onExpandAttachmentActionClick) {
|
||||
_onExpandAttachmentActionClick = onExpandAttachmentActionClick;
|
||||
}
|
||||
|
||||
void setExpandMode(ExpandMode? expandMode) {
|
||||
_expandMode = expandMode;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return Theme(
|
||||
data: ThemeData(
|
||||
@@ -35,37 +53,72 @@ class AttachmentFileTileBuilder {
|
||||
color: Colors.white),
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData(padding: EdgeInsets.zero),
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
focusColor: AppColor.primaryColor,
|
||||
hoverColor: AppColor.primaryColor,
|
||||
onTap: () {
|
||||
if (_onDownloadAttachmentFileActionClick != null) {
|
||||
_onDownloadAttachmentFileActionClick!(_attachment);
|
||||
}},
|
||||
leading: Transform(
|
||||
transform: Matrix4.translationValues(14.0, 2.0, 0.0),
|
||||
child: SvgPicture.asset(_imagePaths.icAttachmentFile, width: 24, height: 24, fit: BoxFit.fill)),
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-8.0, -8.0, 0.0),
|
||||
child: Text(
|
||||
_attachment.name ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileNameColor, fontWeight: FontWeight.w500),
|
||||
)),
|
||||
subtitle: _attachment.size != null && _attachment.size?.value != 0
|
||||
? Transform(
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
focusColor: AppColor.primaryColor,
|
||||
hoverColor: AppColor.primaryColor,
|
||||
onTap: () {
|
||||
if (_onDownloadAttachmentFileActionClick != null) {
|
||||
_onDownloadAttachmentFileActionClick!(_attachment);
|
||||
}},
|
||||
leading: Transform(
|
||||
transform: Matrix4.translationValues(14.0, 2.0, 0.0),
|
||||
child: SvgPicture.asset(_imagePaths.icAttachmentFile, width: 24, height: 24, fit: BoxFit.fill)),
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-8.0, -8.0, 0.0),
|
||||
child: Text(
|
||||
filesize(_attachment.size?.value),
|
||||
_attachment.name ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileSizeColor)))
|
||||
: null
|
||||
),
|
||||
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileNameColor, fontWeight: FontWeight.w500),
|
||||
)),
|
||||
subtitle: _attachment.size != null && _attachment.size?.value != 0
|
||||
? Transform(
|
||||
transform: Matrix4.translationValues(-8.0, -8.0, 0.0),
|
||||
child: Text(
|
||||
filesize(_attachment.size?.value),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileSizeColor)))
|
||||
: null
|
||||
),
|
||||
Transform(
|
||||
transform: Matrix4.translationValues(5.0, 5.0, 0.0),
|
||||
child: IconButton(
|
||||
icon: SvgPicture.asset(_imagePaths.icDownload, width: 24, height: 24, fit: BoxFit.fill),
|
||||
onPressed: () {
|
||||
if (_onDownloadAttachmentFileActionClick != null) {
|
||||
_onDownloadAttachmentFileActionClick!(_attachment);
|
||||
}
|
||||
}
|
||||
)),
|
||||
if (_attachmentSize > _limitDisplayAttachment && _expandMode == ExpandMode.COLLAPSE) _buildItemBackground()
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItemBackground() {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (_onExpandAttachmentActionClick != null) {
|
||||
_onExpandAttachmentActionClick!();
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
Container(color: AppColor.backgroundCountAttachment),
|
||||
Text(
|
||||
'+${_attachmentSize - _limitDisplayAttachment + 1}',
|
||||
style: TextStyle(fontSize: 16, color: Colors.white, fontWeight: FontWeight.bold))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
class HomeController extends GetxController {
|
||||
final GetCredentialInteractor _getCredentialInteractor;
|
||||
@@ -36,7 +37,7 @@ class HomeController extends GetxController {
|
||||
}
|
||||
|
||||
void _goToLogin() {
|
||||
Get.offNamed(AppRoutes.LOGIN);
|
||||
pushAndPop(AppRoutes.LOGIN);
|
||||
}
|
||||
|
||||
void _goToMailbox(GetCredentialViewState credentialViewState) {
|
||||
@@ -45,6 +46,6 @@ class HomeController extends GetxController {
|
||||
credentialViewState.userName.userName,
|
||||
credentialViewState.password.value,
|
||||
);
|
||||
Get.offNamed(AppRoutes.SESSION);
|
||||
pushAndPop(AppRoutes.SESSION);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:tmail_ui_user/features/login/domain/state/authentication_user_st
|
||||
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
class LoginController extends GetxController {
|
||||
|
||||
@@ -60,7 +61,7 @@ class LoginController extends GetxController {
|
||||
loginState.value = LoginState(Right(success));
|
||||
_dynamicUrlInterceptors.changeBaseUrl(_urlText);
|
||||
_authorizationInterceptors.changeAuthorization(_userNameText, _passwordText);
|
||||
Get.offNamed(AppRoutes.SESSION);
|
||||
pushAndPop(AppRoutes.SESSION);
|
||||
}
|
||||
|
||||
void _loginFailureAction(AuthenticationUserFailure failure) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_b
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
class MailboxController extends BaseController {
|
||||
|
||||
@@ -141,6 +142,6 @@ class MailboxController extends BaseController {
|
||||
|
||||
void closeMailboxScreen() {
|
||||
_deleteCredential();
|
||||
Get.offAllNamed(AppRoutes.LOGIN);
|
||||
pushAndPopAll(AppRoutes.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_i
|
||||
import 'package:tmail_ui_user/features/session/domain/state/get_session_state.dart';
|
||||
import 'package:tmail_ui_user/features/session/domain/usecases/get_session_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
class SessionController extends GetxController {
|
||||
final GetSessionInteractor _getSessionInteractor;
|
||||
@@ -29,10 +30,10 @@ class SessionController extends GetxController {
|
||||
|
||||
void _goToLogin() {
|
||||
_deleteCredential();
|
||||
Get.offNamed(AppRoutes.LOGIN);
|
||||
pushAndPop(AppRoutes.LOGIN);
|
||||
}
|
||||
|
||||
void _goToMailboxDashBoard(GetSessionSuccess getSessionSuccess) {
|
||||
Get.offNamed(AppRoutes.MAILBOX_DASHBOARD, arguments: getSessionSuccess.session);
|
||||
pushAndPop(AppRoutes.MAILBOX_DASHBOARD, arguments: getSessionSuccess.session);
|
||||
}
|
||||
}
|
||||
@@ -392,7 +392,7 @@ class ThreadController extends BaseController {
|
||||
|
||||
void composeEmailAction() {
|
||||
if (canComposeEmail()) {
|
||||
Get.toNamed(
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
|
||||
+24
-2
@@ -276,8 +276,30 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"attachment_download_successfully": "Attachment downloaded successfully",
|
||||
"@attachment_download_successfully": {
|
||||
"downloading_file": "Downloading {fileName}",
|
||||
"@downloading_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"fileName"
|
||||
],
|
||||
"placeholders": {
|
||||
"fileName": {}
|
||||
}
|
||||
},
|
||||
"preparing_to_export": "Preparing to export",
|
||||
"@preparing_to_export": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"user_cancel_download_file": "User cancel download file",
|
||||
"@user_cancel_download_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
|
||||
+24
-2
@@ -276,8 +276,30 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"attachment_download_successfully": "Attachment downloaded successfully",
|
||||
"@attachment_download_successfully": {
|
||||
"downloading_file": "Downloading {fileName}",
|
||||
"@downloading_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"fileName"
|
||||
],
|
||||
"placeholders": {
|
||||
"fileName": {}
|
||||
}
|
||||
},
|
||||
"preparing_to_export": "Preparing to export",
|
||||
"@preparing_to_export": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"user_cancel_download_file": "User cancel download file",
|
||||
"@user_cancel_download_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2021-09-15T23:44:29.035668",
|
||||
"@@last_modified": "2021-09-16T14:31:23.463339",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -276,8 +276,30 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"attachment_download_successfully": "Attachment downloaded successfully",
|
||||
"@attachment_download_successfully": {
|
||||
"downloading_file": "Downloading {fileName}",
|
||||
"@downloading_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"fileName"
|
||||
],
|
||||
"placeholders": {
|
||||
"fileName": {}
|
||||
}
|
||||
},
|
||||
"preparing_to_export": "Preparing to export",
|
||||
"@preparing_to_export": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"user_cancel_download_file": "User cancel download file",
|
||||
"@user_cancel_download_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
|
||||
+24
-2
@@ -276,8 +276,30 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"attachment_download_successfully": "Attachment downloaded successfully",
|
||||
"@attachment_download_successfully": {
|
||||
"downloading_file": "Downloading {fileName}",
|
||||
"@downloading_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"fileName"
|
||||
],
|
||||
"placeholders": {
|
||||
"fileName": {}
|
||||
}
|
||||
},
|
||||
"preparing_to_export": "Preparing to export",
|
||||
"@preparing_to_export": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"user_cancel_download_file": "User cancel download file",
|
||||
"@user_cancel_download_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
|
||||
+24
-2
@@ -276,8 +276,30 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"attachment_download_successfully": "Attachment downloaded successfully",
|
||||
"@attachment_download_successfully": {
|
||||
"downloading_file": "Downloading {fileName}",
|
||||
"@downloading_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"fileName"
|
||||
],
|
||||
"placeholders": {
|
||||
"fileName": {}
|
||||
}
|
||||
},
|
||||
"preparing_to_export": "Preparing to export",
|
||||
"@preparing_to_export": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"user_cancel_download_file": "User cancel download file",
|
||||
"@user_cancel_download_file": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
|
||||
@@ -45,9 +45,13 @@ class NetworkBindings extends Bindings {
|
||||
void _bindingApi() {
|
||||
Get.put(DioClient(Get.find<Dio>()));
|
||||
Get.put(JmapHttpClient.HttpClient(Get.find<Dio>()));
|
||||
Get.put(DownloadClient(Get.find<DioClient>()));
|
||||
Get.put(DownloadManager(Get.find<DownloadClient>()));
|
||||
Get.put(MailboxAPI(Get.find<JmapHttpClient.HttpClient>()));
|
||||
Get.put(SessionAPI(Get.find<JmapHttpClient.HttpClient>()));
|
||||
Get.put(ThreadAPI(Get.find<JmapHttpClient.HttpClient>()));
|
||||
Get.put(EmailAPI(Get.find<JmapHttpClient.HttpClient>()));
|
||||
Get.put(EmailAPI(
|
||||
Get.find<JmapHttpClient.HttpClient>(),
|
||||
Get.find<DownloadManager>()));
|
||||
}
|
||||
}
|
||||
@@ -308,10 +308,47 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
String get attachment_download_successfully {
|
||||
String downloading_file(String fileName) {
|
||||
return Intl.message(
|
||||
'Attachment downloaded successfully',
|
||||
name: 'attachment_download_successfully',
|
||||
'Downloading $fileName',
|
||||
name: 'downloading_file',
|
||||
args: [fileName]
|
||||
);
|
||||
}
|
||||
|
||||
String get preparing_to_export {
|
||||
return Intl.message(
|
||||
'Preparing to export',
|
||||
name: 'preparing_to_export'
|
||||
);
|
||||
}
|
||||
|
||||
String get cancel {
|
||||
return Intl.message(
|
||||
'Cancel',
|
||||
name: 'cancel'
|
||||
);
|
||||
}
|
||||
|
||||
String get user_cancel_download_file {
|
||||
return Intl.message(
|
||||
'User cancel download file',
|
||||
name: 'user_cancel_download_file'
|
||||
);
|
||||
}
|
||||
|
||||
String get you_need_to_grant_files_permission_to_download_attachments {
|
||||
return Intl.message(
|
||||
'You need to grant files permission to download attachments',
|
||||
name: 'you_need_to_grant_files_permission_to_download_attachments'
|
||||
);
|
||||
}
|
||||
|
||||
String count_attachment(int count) {
|
||||
return Intl.message(
|
||||
'$count attachments',
|
||||
name: 'count_attachment',
|
||||
args: [count]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user