Add presentation layer for GetEmailContent

This commit is contained in:
dab246
2021-08-18 17:50:53 +07:00
committed by Dat H. Pham
parent f8b8f73a86
commit 842072e0d6
33 changed files with 1107 additions and 115 deletions
@@ -0,0 +1,5 @@
class EmailConstants {
static const HTML_TEXT = 'text/html';
static const PLAIN_TEXT = 'text/plain';
}
@@ -1,10 +1,20 @@
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
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/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
class EmailBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => EmailController(Get.find<ResponsiveUtils>()));
Get.lazyPut(() => EmailDataSourceImpl(Get.find<EmailAPI>()));
Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>());
Get.lazyPut(() => EmailRepositoryImpl(Get.find<EmailDataSource>()));
Get.lazyPut<EmailRepository>(() => Get.find<EmailRepositoryImpl>());
Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>()));
Get.put(EmailController(Get.find<GetEmailContentInteractor>()));
}
}
@@ -1,22 +1,49 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.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:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
class EmailController extends BaseController {
final ResponsiveUtils responsiveUtils;
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final GetEmailContentInteractor _getEmailContentInteractor;
EmailController(this.responsiveUtils);
final emailAddressExpandMode = ExpandMode.COLLAPSE.obs;
EmailController(this._getEmailContentInteractor);
@override
void onReady() {
super.onReady();
mailboxDashBoardController.selectedEmail.listen((presentationEmail) {
toggleDisplayEmailAddressAction(expandMode: ExpandMode.COLLAPSE);
final accountId = mailboxDashBoardController.accountId.value;
if (accountId != null && presentationEmail != null) {
_getEmailContentAction(accountId, presentationEmail.id);
}
});
}
void goToMailboxListMail(BuildContext context) {
Get.back();
@override
void dispose() {
super.dispose();
mailboxDashBoardController.selectedEmail.close();
}
void _getEmailContentAction(AccountId accountId, EmailId emailId) async {
consumeState(_getEmailContentInteractor.execute(accountId, emailId));
}
@override
void onData(Either<Failure, Success> newState) {
super.onData(newState);
}
@override
@@ -26,4 +53,12 @@ class EmailController extends BaseController {
@override
void onError(error) {
}
void toggleDisplayEmailAddressAction({required ExpandMode expandMode}) {
emailAddressExpandMode.value = expandMode;
}
void goToThreadView(BuildContext context) {
Get.back();
}
}
+159 -23
View File
@@ -1,16 +1,21 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_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';
class EmailView extends GetWidget<EmailController> {
class EmailView extends GetWidget {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final emailController = Get.find<EmailController>();
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
@@ -29,7 +34,7 @@ class EmailView extends GetWidget<EmailController> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildAppBar(context),
Expanded(child: _buildListMail(context)),
Expanded(child: _buildEmailContent(context)),
_buildBottomBar(context),
])
)
@@ -38,35 +43,166 @@ class EmailView extends GetWidget<EmailController> {
}
Widget _buildAppBar(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16),
child: AppBarMailWidgetBuilder(context, imagePaths, responsiveUtils)
.onBackActionClick(() => controller.goToMailboxListMail(context))
.build());
return Obx(() => Padding(
padding: EdgeInsets.only(left: 6, top: 6, right: 6, bottom: 6),
child: AppBarMailWidgetBuilder(
context,
imagePaths,
responsiveUtils,
emailController.mailboxDashBoardController.selectedEmail.value)
.onBackActionClick(() => emailController.goToThreadView(context))
.build()));
}
Widget _buildBottomBar(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 16.0, right: 16, bottom: 16),
padding: EdgeInsets.only(left: 6, top: 6, right: 6, bottom: 6),
child: BottomBarMailWidgetBuilder(context, imagePaths, responsiveUtils)
.build());
}
Widget _buildListMail(BuildContext context) {
Widget _buildEmailContent(BuildContext context) {
return Container(
margin: EdgeInsets.zero,
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.all(20),
color: AppColor.bgMessenger,
child: Obx(() => Text(
mailboxDashBoardController.selectedEmail.value != PresentationEmail.presentationEmailEmpty
? '${mailboxDashBoardController.selectedEmail.value.preview}'
: AppLocalizations.of(context).no_mail_selected,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)
))
margin: EdgeInsets.zero,
alignment: Alignment.topCenter,
child: Obx(() => emailController.mailboxDashBoardController.selectedEmail.value != null
? SingleChildScrollView(
physics : ClampingScrollPhysics(),
child: Container(
margin: EdgeInsets.zero,
width: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.all(16),
color: AppColor.bgMessenger,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
_buildEmailSubject(),
_buildEmailMessage(context),
])
))
: Center(child: _buildEmailEmpty(context))
)
);
}
Widget _buildEmailEmpty(BuildContext context) {
return Text(
AppLocalizations.of(context).no_mail_selected,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold));
}
Widget _buildEmailSubject() {
return Padding(
padding: EdgeInsets.only(left: 8, top: 4, bottom: 16),
child: Text(
'${emailController.mailboxDashBoardController.selectedEmail.value?.subject}',
style: TextStyle(fontSize: 18, color: AppColor.mailboxTextColor, fontWeight: FontWeight.w500)
));
}
Widget _buildLoadingView() {
return Obx(() => emailController.viewState.value.fold(
(failure) => SizedBox.shrink(),
(success) => success == UIState.loading
? Center(child: Padding(
padding: EdgeInsets.only(top: 25, bottom: 16),
child: SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(color: AppColor.primaryColor))))
: SizedBox.shrink()));
}
Widget _buildEmailMessage(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 12, right: 12, top: 16, bottom: 24),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Obx(() => SenderAndReceiverInformationTileBuilder(
context,
imagePaths,
emailController.mailboxDashBoardController.selectedEmail.value,
emailController.emailAddressExpandMode.value)
.onOpenExpandAddressReceiverActionClick(() => emailController.toggleDisplayEmailAddressAction(expandMode: ExpandMode.EXPAND))
.build()),
_buildLoadingView(),
SizedBox(height: 16),
_buildListAttachments(context),
_buildListMessageContent(),
],
)
);
}
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(bottom: 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]).build())
: SizedBox.shrink();
} else {
return SizedBox.shrink();
}
})
);
} else {
return SizedBox.shrink();
}
}
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,
key: Key('list_message_content'),
itemCount: messageContents.length,
itemBuilder: (context, index) =>
MessageContentTileBuilder(
messageContents[index],
imagePaths,
attachmentsInline,
emailController.mailboxDashBoardController.sessionCurrent,
emailController.mailboxDashBoardController.accountId.value)
.build())
: SizedBox.shrink();
} else {
return SizedBox.shrink();
}
})
);
}
}
@@ -0,0 +1,6 @@
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart';
extension EmailBodyPartExtension on EmailBodyPart {
AttachmentFile toAttachmentFile() => AttachmentFile(partId, blobId, size, name, type, cid);
}
@@ -0,0 +1,49 @@
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
import 'package:model/email/email_content.dart';
import 'package:tmail_ui_user/features/email/presentation/constants/email_contants.dart';
import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart';
import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart';
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/email_body_part_extension.dart';
extension EmailContentExtension on EmailContent {
List<MessageContent> getListMessageContent() {
Map<PartId, TextFormat> mapHtmlBody = Map();
List<MessageContent> listMessageContent = [];
htmlBody?.forEach((element) {
if (element.partId != null && element.type != null) {
mapHtmlBody[element.partId!] = element.type!.mimeType == EmailConstants.HTML_TEXT
? TextFormat.HTML
: TextFormat.PLAIN;
}
});
bodyValues?.forEach((key, value) {
listMessageContent.add(MessageContent(mapHtmlBody[key] ?? TextFormat.PLAIN, value.value));
});
return listMessageContent;
}
List<AttachmentFile> getListAttachment() {
if (attachments != null) {
return attachments!
.where((element) => (element.disposition != null && element.disposition != 'inline'))
.map((item) => item.toAttachmentFile())
.toList();
}
return [];
}
List<AttachmentFile> getListAttachmentInline() {
if (attachments != null) {
return attachments!
.where((element) => element.disposition == 'inline')
.map((item) => item.toAttachmentFile())
.toList();
}
return [];
}
}
@@ -0,0 +1,9 @@
import 'package:intl/intl.dart';
import 'package:model/model.dart';
extension PresentationEmailExtension on PresentationEmail {
int numberOfAllEmailAddress() => to.numberEmailAddress() + cc.numberEmailAddress() + bcc.numberEmailAddress();
String getSentTime() => sentAt != null ? DateFormat('dd/MM/yyyy h:mm a', 'en_US').format(sentAt!.value) : '';
}
@@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
class AttachmentFile with EquatableMixin {
final PartId? partId;
final Id? blobId;
final UnsignedInt? size;
final String? name;
final MediaType? type;
final String? cid;
AttachmentFile(
this.partId,
this.blobId,
this.size,
this.name,
this.type,
this.cid
);
@override
List<Object?> get props => [partId, blobId, size, name, type, cid];
}
@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart';
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
import 'package:model/model.dart';
class MessageContent with EquatableMixin {
final TextFormat textFormat;
final String content;
MessageContent(this.textFormat, this.content);
bool hasImageInlineWithCid() => content.contains('cid:');
String getContentHasInlineAttachment(Session session, AccountId accountId, List<AttachmentFile> attachmentFiles) {
var contentValid = content;
attachmentFiles.forEach((attachment) {
if(attachment.cid != null) {
final urlDownloadImage = session.getDownloadUrl(
'${accountId.id.value}',
'${attachment.blobId?.value}',
'${attachment.name}',
'${attachment.type?.mimeType}');
contentValid = content.replaceAll('cid:${attachment.cid}', '$urlDownloadImage');
}
});
return contentValid;
}
@override
List<Object?> get props => [textFormat, content];
}
@@ -0,0 +1,5 @@
enum TextFormat {
HTML,
PLAIN
}
@@ -2,6 +2,9 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
typedef OnBackActionClick = void Function();
@@ -11,11 +14,13 @@ class AppBarMailWidgetBuilder {
final BuildContext _context;
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
final PresentationEmail? _presentationEmail;
AppBarMailWidgetBuilder(
this._context,
this._imagePaths,
this._responsiveUtils,
this._presentationEmail,
);
AppBarMailWidgetBuilder onBackActionClick(
@@ -28,6 +33,7 @@ class AppBarMailWidgetBuilder {
return Container(
key: Key('app_bar_messenger_widget'),
alignment: Alignment.center,
padding: EdgeInsets.zero,
color: Colors.white,
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
@@ -35,7 +41,7 @@ class AppBarMailWidgetBuilder {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (!_responsiveUtils.isDesktop(_context))
if (!_responsiveUtils.isDesktop(_context) || Get.currentRoute != AppRoutes.MAILBOX_DASHBOARD)
Expanded(child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
@@ -63,11 +69,15 @@ class AppBarMailWidgetBuilder {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ButtonBuilder(_imagePaths.icTrash).key(Key('button_delete_message')).build(),
SizedBox(width: 20),
SizedBox(width: 10),
ButtonBuilder(_imagePaths.icEyeDisable).key(Key('button_hide_message')).build(),
SizedBox(width: 20),
ButtonBuilder(_imagePaths.icStar).key(Key('button_favorite_message')).build(),
SizedBox(width: 20),
SizedBox(width: 10),
ButtonBuilder((_presentationEmail != null && _presentationEmail!.isFlaggedEmail())
? _imagePaths.icFlagged
: _imagePaths.icFlag)
.key(Key('button_favorite_message'))
.build(),
SizedBox(width: 10),
ButtonBuilder(_imagePaths.icFolder).key(Key('button_add_folder_message')).build(),
]
);
@@ -0,0 +1,70 @@
import 'package:core/core.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart';
typedef OnOpenAttachmentFileActionClick = void Function();
class AttachmentFileTileBuilder {
final ImagePaths _imagePaths;
final AttachmentFile _attachmentFile;
OnOpenAttachmentFileActionClick? _onOpenAttachmentFileActionClick;
AttachmentFileTileBuilder(this._imagePaths, this._attachmentFile);
AttachmentFileTileBuilder onOpenAttachmentFileActionClick(
OnOpenAttachmentFileActionClick onOpenAttachmentFileActionClick) {
_onOpenAttachmentFileActionClick = onOpenAttachmentFileActionClick;
return this;
}
Widget build() {
return Theme(
data: ThemeData(
splashColor: Colors.transparent ,
highlightColor: Colors.transparent),
child: Container(
key: Key('attach_file_tile'),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.attachmentFileBorderColor),
color: Colors.white),
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: ListTile(
onTap: () {
if (_onOpenAttachmentFileActionClick != null) {
_onOpenAttachmentFileActionClick!();
}},
leading: Transform(
transform: Matrix4.translationValues(0.0, 2.0, 0.0),
child: SvgPicture.asset(_imagePaths.icAttachmentFile, width: 24, height: 24, fit: BoxFit.fill)),
title: Transform(
transform: Matrix4.translationValues(-18.0, -8.0, 0.0),
child: Text(
_attachmentFile.name ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileNameColor, fontWeight: FontWeight.w500),
)),
subtitle: _attachmentFile.size != null && _attachmentFile.size?.value != 0
? Transform(
transform: Matrix4.translationValues(-18.0, -8.0, 0.0),
child: Text(
filesize(_attachmentFile.size?.value),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: AppColor.attachmentFileSizeColor)))
: null
),
)
)
);
}
}
@@ -0,0 +1,103 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:photo_view/photo_view.dart';
import 'package:tmail_ui_user/features/email/presentation/model/attachment_file.dart';
import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart';
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
import 'package:sanitize_html/sanitize_html.dart' show sanitizeHtml;
class MessageContentTileBuilder {
final MessageContent _messageContent;
final ImagePaths _imagePaths;
final List<AttachmentFile> _attachmentInlines;
final Session? _session;
final AccountId? _accountId;
MessageContentTileBuilder(
this._messageContent,
this._imagePaths,
this._attachmentInlines,
this._session,
this._accountId,
);
Widget build() {
return Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent),
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: Center(
child: _messageContent.textFormat == TextFormat.PLAIN
? Text('${_messageContent.content}', style: TextStyle(fontSize: 12, color: AppColor.mailboxTextColor))
: HtmlWidget(
cleanHtmlMessage(_messageContent.content),
textStyle: TextStyle(fontSize: 12, color: AppColor.mailboxTextColor),
buildAsync: true,
factoryBuilder: () => _PopupPhotoViewWidgetFactory(_imagePaths),
buildAsyncBuilder: (_, snapshot) => snapshot.data ?? SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(color: AppColor.primaryColor)),
webViewJs: false),
)
)
);
}
String getHtmlMessageText() {
final message = (_attachmentInlines.isNotEmpty && _session != null && _accountId != null && _messageContent.hasImageInlineWithCid())
? '${_messageContent.getContentHasInlineAttachment(_session!, _accountId!, _attachmentInlines)}'
: '${_messageContent.content}';
return message;
}
String cleanHtmlMessage(String message) => hasTagTable(getHtmlMessageText()) ? sanitizeHtml(getHtmlMessageText()) : message;
bool hasTagTable(String html) => html.toLowerCase().contains('</table>');
}
class _PopupPhotoViewWidgetFactory extends WidgetFactory {
final ImagePaths _imagePaths;
_PopupPhotoViewWidgetFactory(this._imagePaths);
@override
Widget? buildImageWidget(BuildMetadata meta, {String? semanticLabel, required String url}) {
final built = super.buildImageWidget(meta, semanticLabel: semanticLabel, url: url);
if (built is Image) {
return Builder(
builder: (context) => GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (_) => Scaffold(
body: Stack(
children: [
PhotoView(
heroAttributes: PhotoViewHeroAttributes(tag: url),
imageProvider: built.image),
Padding(
padding: EdgeInsets.only(left: 16, top: 35),
child: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: SvgPicture.asset(_imagePaths.icCloseMailbox, width: 30, height: 30, fit: BoxFit.fill)))
],
),
))),
child: Hero(tag: url, child: built),
),
);
}
return built;
}
}
@@ -0,0 +1,196 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnOpenExpandAddressReceiverActionClick = void Function();
class SenderAndReceiverInformationTileBuilder {
static const int LIMIT_ADDRESS_DISPLAY = 1;
final ImagePaths _imagePaths;
final BuildContext _context;
final PresentationEmail? _presentationEmail;
final ExpandMode _expandMode;
OnOpenExpandAddressReceiverActionClick? _onOpenExpandAddressReceiverActionClick;
SenderAndReceiverInformationTileBuilder(
this._context,
this._imagePaths,
this._presentationEmail,
this._expandMode,
);
SenderAndReceiverInformationTileBuilder onOpenExpandAddressReceiverActionClick(OnOpenExpandAddressReceiverActionClick onOpenExpandAddressReceiverActionClick) {
_onOpenExpandAddressReceiverActionClick = onOpenExpandAddressReceiverActionClick;
return this;
}
Widget build() {
if (_presentationEmail == null) {
return SizedBox.shrink();
}
return Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent),
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: ListTile(
leading: Transform(
transform: Matrix4.translationValues(-15.0, -8.0, 0.0),
child: AvatarBuilder()
.text('${_presentationEmail!.getAvatarText()}')
.size(40)
.iconStatus(_imagePaths.icOffline)
.build()),
title: Transform(
transform: Matrix4.translationValues(-15.0, 0.0, 0.0),
child: Text(
'${AppLocalizations.of(_context).from_email_address_prefix}: ${_presentationEmail!.getSenderName().inCaps}',
style: TextStyle(fontSize: 16, color: AppColor.nameUserColor, fontWeight: FontWeight.w500),
)),
subtitle: Transform(
transform: Matrix4.translationValues(-15.0, 0.0, 0.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_presentationEmail!.to.numberEmailAddress() > 0) _buildAddressToReceiverWidget(),
if (_presentationEmail!.cc.numberEmailAddress() > 0) _buildAddressCcReceiverWidget(),
if (_presentationEmail!.bcc.numberEmailAddress() > 0) _buildAddressBccReceiverWidget(),
Padding(
padding: EdgeInsets.only(top: 6),
child: Text(
'${_presentationEmail!.getSentTime()}',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor),
))
],
))
)
)
);
}
Widget _buildExpandAddressWidget(String typeReceiver, String nameAddress) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$typeReceiver: ',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500)),
Expanded(child: Text(
'$nameAddress',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500),
))
]);
}
Widget _buildCollapseAddressWidget(String typeReceiver, String nameAddress) {
return Row(
children: [
Text(
'$typeReceiver: $nameAddress',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500)),
if (_expandMode == ExpandMode.COLLAPSE
&& _presentationEmail != null
&& _presentationEmail!.numberOfAllEmailAddress() > LIMIT_ADDRESS_DISPLAY)
_buildButtonExpandAddress()
]);
}
Widget _buildAddressWidget(String typeReceiver, String nameAddress) {
if (_expandMode == ExpandMode.EXPAND
|| (_presentationEmail != null && _presentationEmail!.numberOfAllEmailAddress() <= LIMIT_ADDRESS_DISPLAY)) {
return Padding(
padding: EdgeInsets.only(top: 6),
child: _buildExpandAddressWidget(typeReceiver, nameAddress));
} else {
return Padding(
padding: EdgeInsets.only(top: 6),
child: _buildCollapseAddressWidget(typeReceiver, nameAddress));
}
}
Widget _buildAddressToReceiverWidget() {
return _buildAddressWidget(
AppLocalizations.of(_context).to_email_address_prefix,
_presentationEmail!.to.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY));
}
Widget _buildAddressCcReceiverWidget() {
if (_presentationEmail!.to.numberEmailAddress() > 0) {
if (_expandMode == ExpandMode.EXPAND) {
return Padding(
padding: EdgeInsets.only(top: 6),
child: _buildExpandAddressWidget(AppLocalizations.of(_context).cc_email_address_prefix, _presentationEmail!.cc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)));
} else {
return SizedBox.shrink();
}
} else {
return _buildAddressWidget(
AppLocalizations.of(_context).cc_email_address_prefix,
_presentationEmail!.cc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY));
}
}
Widget _buildAddressBccReceiverWidget() {
if (_presentationEmail!.to.numberEmailAddress() > 0) {
if (_expandMode == ExpandMode.EXPAND) {
return Padding(
padding: EdgeInsets.only(top: 6),
child: _buildExpandAddressWidget(AppLocalizations.of(_context).bcc_email_address_prefix, _presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)));
} else {
return SizedBox.shrink();
}
} else {
if (_presentationEmail!.cc.numberEmailAddress() > 0) {
if (_expandMode == ExpandMode.EXPAND) {
return Padding(
padding: EdgeInsets.only(top: 6),
child: _buildExpandAddressWidget(AppLocalizations.of(_context).bcc_email_address_prefix, _presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY)));
} else {
return SizedBox.shrink();
}
} else {
return _buildAddressWidget(
AppLocalizations.of(_context).bcc_email_address_prefix,
_presentationEmail!.bcc.listEmailAddressToString(expandMode: _expandMode, limitAddress: LIMIT_ADDRESS_DISPLAY));
}
}
}
String getRemainCountAddressReceiver() {
if (_presentationEmail!.numberOfAllEmailAddress() - LIMIT_ADDRESS_DISPLAY >= 999) {
return '999';
}
return '${_presentationEmail!.numberOfAllEmailAddress() - LIMIT_ADDRESS_DISPLAY}';
}
Widget _buildButtonExpandAddress() {
return GestureDetector(
onTap: () {
if (_onOpenExpandAddressReceiverActionClick != null) {
_onOpenExpandAddressReceiverActionClick!();
}},
child: Row(
children: [
SizedBox(width: 8),
Text(
'+${getRemainCountAddressReceiver()}',
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor, fontWeight: FontWeight.w500),
),
SvgPicture.asset(_imagePaths.icMoreReceiver, width: 14, height: 14, fit: BoxFit.fill),
],
),
);
}
}
@@ -98,8 +98,8 @@ class MailboxController extends BaseController {
} catch (e) {}
}
SelectMode getSelectMode(PresentationMailbox presentationMailbox, PresentationMailbox selectedMailbox) {
return presentationMailbox.id == selectedMailbox.id
SelectMode getSelectMode(PresentationMailbox presentationMailbox, PresentationMailbox? selectedMailbox) {
return presentationMailbox.id == selectedMailbox?.id
? SelectMode.ACTIVE
: SelectMode.INACTIVE;
}
@@ -86,7 +86,7 @@ class MailboxView extends GetWidget<MailboxController> {
Widget _buildCloseScreenButton(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: 24, top: responsiveUtils.isMobile(context) ? 0 : 12),
padding: EdgeInsets.only(left: 16, top: responsiveUtils.isMobile(context) ? 0 : 12),
child: IconButton(
key: Key('mailbox_close_button'),
onPressed: () => controller.closeMailboxScreen(),
@@ -17,7 +17,7 @@ class StorageWidgetBuilder {
padding: EdgeInsets.only(left: 40, top: 16, bottom: 20, right: 40),
color: AppColor.storageBackgroundColor,
alignment: Alignment.bottomLeft,
height: 112,
height: 100,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -33,7 +33,10 @@ class UserInformationWidgetBuilder {
_onOpenUserInformationActionClick!();
}
},
leading: SvgPicture.asset(_imagePaths.icTMailLogo, width: 40, height: 40, fit: BoxFit.fill),
leading: AvatarBuilder()
.text('J')
.size(40)
.build(),
title: Transform(
transform: Matrix4.translationValues(0.0, 0.0, 0.0),
child: Text(
@@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart';
@@ -11,5 +12,6 @@ class MailboxDashBoardBindings extends Bindings {
MailboxBindings().dependencies();
ThreadBindings().dependencies();
EmailBindings().dependencies();
}
}
@@ -5,12 +5,13 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
class MailboxDashBoardController extends BaseController {
final scaffoldKey = GlobalKey<ScaffoldState>();
final selectedMailbox = PresentationMailbox.presentationMailboxEmpty.obs;
final selectedEmail = PresentationEmail.presentationEmailEmpty.obs;
final selectedMailbox = Rxn<PresentationMailbox>();
final selectedEmail = Rxn<PresentationEmail>();
final accountId = Rxn<AccountId>();
Session? sessionCurrent;
@@ -32,25 +33,34 @@ class MailboxDashBoardController extends BaseController {
}
void _setSessionCurrent() {
Future.delayed(const Duration(milliseconds: 500), () {
sessionCurrent = Get.arguments as Session;
accountId.value = sessionCurrent?.accounts.keys.first;
Future.delayed(const Duration(milliseconds: 100), () {
final arguments = Get.arguments;
if (arguments is Session) {
sessionCurrent = Get.arguments as Session;
accountId.value = sessionCurrent?.accounts.keys.first;
}
});
}
void setSelectedMailbox(PresentationMailbox newPresentationMailbox) {
void setSelectedMailbox(PresentationMailbox? newPresentationMailbox) {
selectedMailbox.value = newPresentationMailbox;
}
void setSelectedEmail(PresentationEmail newPresentationEmail) {
void setSelectedEmail(PresentationEmail? newPresentationEmail) {
selectedEmail.value = newPresentationEmail;
}
void openDrawer() {
scaffoldKey.currentState?.openEndDrawer();
scaffoldKey.currentState?.openDrawer();
}
void closeDrawer() {
scaffoldKey.currentState?.openEndDrawer();
}
@override
void onClose() {
super.onClose();
Get.delete<EmailController>();
}
}
@@ -46,7 +46,7 @@ class ThreadController extends BaseController {
void onReady() {
super.onReady();
mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) {
mailboxDashBoardController.setSelectedEmail(PresentationEmail.presentationEmailEmpty);
mailboxDashBoardController.setSelectedEmail(null);
refreshGetAllEmailAction();
});
}
@@ -80,17 +80,11 @@ class ThreadController extends BaseController {
lastGetTotal = emailList.length;
if (newListEmail.isEmpty) {
_resetPositionCurrentAndLoadMoreState();
} else {
loadMoreState.value = LoadMoreState.IDLE;
}
loadMoreState.value = newListEmail.isEmpty ? LoadMoreState.COMPLETED : LoadMoreState.IDLE;
}
EmailFilterCondition? _getFilterConditionCurrent() {
return mailboxDashBoardController.selectedMailbox.value.id != PresentationMailbox.presentationMailboxEmpty.id
? EmailFilterCondition(inMailbox: mailboxDashBoardController.selectedMailbox.value.id)
: null;
return EmailFilterCondition(inMailbox: mailboxDashBoardController.selectedMailbox.value?.id);
}
Set<Comparator>? _getSortCurrent() {
@@ -103,7 +97,7 @@ class ThreadController extends BaseController {
if (loadMoreState.value == LoadMoreState.LOADING) {
positionCurrent -= lastGetTotal;
}
loadMoreState.value = LoadMoreState.COMPLETED;
loadMoreState.value = LoadMoreState.IDLE;
}
void getAllEmailAction(AccountId accountId,
@@ -150,7 +144,7 @@ class ThreadController extends BaseController {
final accountId = mailboxDashBoardController.accountId.value;
if (accountId != null) {
Future.delayed(const Duration(milliseconds: 500), () {
Future.delayed(const Duration(milliseconds: 100), () {
getAllEmailAction(
accountId,
limit: ThreadConstants.defaultLimit,
@@ -162,8 +156,8 @@ class ThreadController extends BaseController {
}
}
SelectMode getSelectMode(PresentationEmail presentationEmail, PresentationEmail selectedEmail) {
return presentationEmail.id == selectedEmail.id
SelectMode getSelectMode(PresentationEmail presentationEmail, PresentationEmail? selectedEmail) {
return presentationEmail.id == selectedEmail?.id
? SelectMode.ACTIVE
: SelectMode.INACTIVE;
}
@@ -175,7 +169,7 @@ class ThreadController extends BaseController {
}
}
void goToMailbox() {
void openMailboxLeftMenu() {
mailboxDashBoardController.openDrawer();
}
@@ -25,6 +25,7 @@ class ThreadView extends GetWidget<ThreadController> {
left: false,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.zero,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -40,22 +41,22 @@ class ThreadView extends GetWidget<ThreadController> {
);
}
Widget _buildAppBarMailboxListMail(BuildContext context, PresentationMailbox presentationMailbox) {
Widget _buildAppBarMailboxListMail(BuildContext context, PresentationMailbox? presentationMailbox) {
return Padding(
padding: EdgeInsets.only(left: 16, top: 8.0, right: 16),
padding: EdgeInsets.only(left: 15, right: 12),
child: AppBarThreadWidgetBuilder(
context,
imagePaths,
responsiveUtils,
presentationMailbox)
.onOpenListMailboxActionClick(() => controller.goToMailbox())
.onOpenListMailboxActionClick(() => controller.openMailboxLeftMenu())
.build());
}
Widget _buildLoadingView() {
return Obx(() => controller.viewState.value.fold(
(failure) => SizedBox.shrink(),
(success) => success == UIState.loading
(success) => success == UIState.loading && controller.loadMoreState.value != LoadMoreState.LOADING
? Center(child: Padding(
padding: EdgeInsets.only(top: 16, bottom: 16),
child: SizedBox(
@@ -107,7 +108,7 @@ class ThreadView extends GetWidget<ThreadController> {
return false;
},
child: ListView.builder(
padding: EdgeInsets.only(top: 16, left: 16, right: 16),
padding: EdgeInsets.only(top: 16),
key: Key('presentation_email_list'),
itemCount: listPresentationEmail.length,
itemBuilder: (context, index) =>
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnOpenSearchMailActionClick = void Function();
typedef OnOpenListMailboxActionClick = void Function();
@@ -18,7 +19,7 @@ class AppBarThreadWidgetBuilder {
final BuildContext _context;
final ImagePaths _imagePaths;
final ResponsiveUtils _responsiveUtils;
final PresentationMailbox _presentationMailbox;
final PresentationMailbox? _presentationMailbox;
AppBarThreadWidgetBuilder(
this._context,
@@ -49,6 +50,7 @@ class AppBarThreadWidgetBuilder {
return Container(
key: Key('app_bar_thread_widget'),
alignment: Alignment.center,
padding: EdgeInsets.only(top: 8, bottom: 2),
color: Colors.white,
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
@@ -72,13 +74,16 @@ class AppBarThreadWidgetBuilder {
_onOpenUserInformationActionClick!()
}},
child: Padding(
padding: EdgeInsets.only(left: _responsiveUtils.isMobile(_context) ? 24 : 0),
child: SvgPicture.asset(_imagePaths.icTMailLogo, width: 24, height: 24, fit: BoxFit.fill)));
padding: EdgeInsets.zero,
child: AvatarBuilder()
.text('J')
.size(36)
.build()));
}
Widget _buildIconSearch() {
return Padding(
padding: EdgeInsets.only(left: 16, right: 0),
padding: EdgeInsets.only(left: 16),
child: IconButton(
icon: SvgPicture.asset(_imagePaths.icSearch, width: 24, height: 24, fit: BoxFit.fill),
onPressed: () => {
@@ -102,20 +107,20 @@ class AppBarThreadWidgetBuilder {
child: Padding(
padding: EdgeInsets.only(left: 16),
child: Text(
'${_presentationMailbox.name?.name}',
'${_presentationMailbox?.name != null ? _presentationMailbox?.name?.name : ''}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 22, color: AppColor.titleAppBarMailboxListMail, fontWeight: FontWeight.w500),
))),
if(_presentationMailbox.getCountUnReadEmails().isNotEmpty)
if(_presentationMailbox != null && _presentationMailbox!.getCountUnReadEmails().isNotEmpty)
Container(
margin: EdgeInsets.only(left: 9),
padding: EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
padding: EdgeInsets.only(left: 8, right: 8, top: 2.5, bottom: 2.5),
decoration:BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppColor.bgNotifyCountAppBarMailboxListMail),
child: Text(
'${_presentationMailbox.getCountUnReadEmails()} new',
'${_presentationMailbox!.getCountUnReadEmails()} ${AppLocalizations.of(_context).unread_email_notification}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 10, color: AppColor.notifyCountAppBarMailboxListMail, fontWeight: FontWeight.w500),
@@ -3,8 +3,8 @@ import 'dart:ui';
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/email/domain/extensions/presentation_email_extension.dart';
typedef OnOpenMailActionClick = void Function();
@@ -39,7 +39,7 @@ class EmailTileBuilder {
child: Container(
key: Key('thread_tile'),
margin: EdgeInsets.only(bottom: 10),
padding: EdgeInsets.only(left: 16, right: 16, top: 10, bottom: 10),
padding: EdgeInsets.only(top: 10, bottom: 10, left: 25, right: 6),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
@@ -51,8 +51,6 @@ class EmailTileBuilder {
child: MediaQuery(
data: MediaQueryData(padding: EdgeInsets.zero),
child: ListTile(
focusColor: AppColor.primaryLightColor,
hoverColor: AppColor.primaryLightColor,
contentPadding: EdgeInsets.zero,
onTap: () => {
if (_onOpenMailActionClick != null) {
@@ -60,46 +58,41 @@ class EmailTileBuilder {
}
},
leading: Transform(
transform: Matrix4.translationValues(0.0, -12.0, 0.0),
child: SvgPicture.asset(
_imagePaths.icTMailLogo,
width: 32,
height: 32,
color: _selectMode == SelectMode.ACTIVE
? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedIconColor : AppColor.mailboxIconColor
: AppColor.mailboxIconColor,
fit: BoxFit.fill)),
title: Padding(
padding: EdgeInsets.only(left: 0),
transform: Matrix4.translationValues(-10.0, -10.0, 0.0),
child: AvatarBuilder()
.text('${_presentationEmail.getAvatarText()}')
.size(40)
.iconStatus(_imagePaths.icOffline)
.build()),
title: Transform(
transform: Matrix4.translationValues(-10.0, 0.0, 0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Text(
'${_presentationEmail.getSenderName()}',
'${_presentationEmail.getSenderName().inCaps}',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: _selectMode == SelectMode.ACTIVE
? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor
: AppColor.mailboxTextColor,
fontWeight: FontWeight.bold))),
Text(
'${_presentationEmail.getTimeThisYear()}',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
color: AppColor.mailboxTextColor,
fontWeight: _presentationEmail.isUnReadEmail() ? FontWeight.bold : FontWeight.w500))),
Padding(
padding: EdgeInsets.only(right: 4),
child: Text(
'${_presentationEmail.getTimeSentEmail('${Localizations.localeOf(_context).languageCode}_${Localizations.localeOf(_context).countryCode}')}',
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: _selectMode == SelectMode.ACTIVE
? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor
: AppColor.mailboxTextColor))
color: _presentationEmail.isUnReadEmail() ? AppColor.sentTimeTextColorUnRead : AppColor.baseTextColor)))
],
)
),
subtitle: Padding(
padding: EdgeInsets.only(left: 0, top: 8),
subtitle: Transform(
transform: Matrix4.translationValues(-10.0, 8.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
@@ -112,8 +105,8 @@ class EmailTileBuilder {
overflow:TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
color: AppColor.baseTextColor,
fontWeight: FontWeight.bold))),
color: _presentationEmail.isUnReadEmail() ? AppColor.subjectEmailTextColorUnRead : AppColor.baseTextColor,
fontWeight: _presentationEmail.isUnReadEmail() ? FontWeight.bold : FontWeight.w500))),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
@@ -124,9 +117,11 @@ class EmailTileBuilder {
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: AppColor.baseTextColor))),
if (_presentationEmail.hasAttachment == true) ButtonBuilder(_imagePaths.icShare).build(),
SizedBox(width: 12),
ButtonBuilder(_imagePaths.icStar).build(),
if (_presentationEmail.hasAttachment == true) ButtonBuilder(_imagePaths.icShare).padding(2).build(),
SizedBox(width: 2),
ButtonBuilder(_presentationEmail.isFlaggedEmail() ? _imagePaths.icFlagged : _imagePaths.icFlag)
.padding(2)
.build(),
],
)
],
+50
View File
@@ -59,5 +59,55 @@
"@new_folder": {
"type": "text",
"placeholders": {}
},
"reply_all": "Reply all",
"@reply_all": {
"type": "text",
"placeholders": {}
},
"reply": "Reply",
"@reply": {
"type": "text",
"placeholders": {}
},
"forward": "Forward",
"@forward": {
"type": "text",
"placeholders": {}
},
"no_emails": "No emails",
"@no_emails": {
"type": "text",
"placeholders": {}
},
"no_mail_selected": "No email selected",
"@no_mail_selected": {
"type": "text",
"placeholders": {}
},
"from_email_address_prefix": "From",
"@from_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"to_email_address_prefix": "To",
"@to_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"unread_email_notification": "new",
"@unread_email_notification": {
"type": "text",
"placeholders": {}
},
"bcc_email_address_prefix": "Bcc",
"@bcc_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"cc_email_address_prefix": "Cc",
"@cc_email_address_prefix": {
"type": "text",
"placeholders": {}
}
}
+50
View File
@@ -59,5 +59,55 @@
"@new_folder": {
"type": "text",
"placeholders": {}
},
"reply_all": "Reply all",
"@reply_all": {
"type": "text",
"placeholders": {}
},
"reply": "Reply",
"@reply": {
"type": "text",
"placeholders": {}
},
"forward": "Forward",
"@forward": {
"type": "text",
"placeholders": {}
},
"no_emails": "No emails",
"@no_emails": {
"type": "text",
"placeholders": {}
},
"no_mail_selected": "No email selected",
"@no_mail_selected": {
"type": "text",
"placeholders": {}
},
"from_email_address_prefix": "From",
"@from_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"to_email_address_prefix": "To",
"@to_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"unread_email_notification": "new",
"@unread_email_notification": {
"type": "text",
"placeholders": {}
},
"bcc_email_address_prefix": "Bcc",
"@bcc_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"cc_email_address_prefix": "Cc",
"@cc_email_address_prefix": {
"type": "text",
"placeholders": {}
}
}
+51 -1
View File
@@ -1,5 +1,5 @@
{
"@@last_modified": "2021-07-27T15:14:36.105457",
"@@last_modified": "2021-08-18T17:44:10.952718",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -59,5 +59,55 @@
"@new_folder": {
"type": "text",
"placeholders": {}
},
"reply_all": "Reply all",
"@reply_all": {
"type": "text",
"placeholders": {}
},
"reply": "Reply",
"@reply": {
"type": "text",
"placeholders": {}
},
"forward": "Forward",
"@forward": {
"type": "text",
"placeholders": {}
},
"no_emails": "No emails",
"@no_emails": {
"type": "text",
"placeholders": {}
},
"no_mail_selected": "No email selected",
"@no_mail_selected": {
"type": "text",
"placeholders": {}
},
"from_email_address_prefix": "From",
"@from_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"to_email_address_prefix": "To",
"@to_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"unread_email_notification": "new",
"@unread_email_notification": {
"type": "text",
"placeholders": {}
},
"bcc_email_address_prefix": "Bcc",
"@bcc_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"cc_email_address_prefix": "Cc",
"@cc_email_address_prefix": {
"type": "text",
"placeholders": {}
}
}
+50
View File
@@ -59,5 +59,55 @@
"@new_folder": {
"type": "text",
"placeholders": {}
},
"reply_all": "Reply all",
"@reply_all": {
"type": "text",
"placeholders": {}
},
"reply": "Reply",
"@reply": {
"type": "text",
"placeholders": {}
},
"forward": "Forward",
"@forward": {
"type": "text",
"placeholders": {}
},
"no_emails": "No emails",
"@no_emails": {
"type": "text",
"placeholders": {}
},
"no_mail_selected": "No email selected",
"@no_mail_selected": {
"type": "text",
"placeholders": {}
},
"from_email_address_prefix": "From",
"@from_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"to_email_address_prefix": "To",
"@to_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"unread_email_notification": "new",
"@unread_email_notification": {
"type": "text",
"placeholders": {}
},
"bcc_email_address_prefix": "Bcc",
"@bcc_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"cc_email_address_prefix": "Cc",
"@cc_email_address_prefix": {
"type": "text",
"placeholders": {}
}
}
+50
View File
@@ -59,5 +59,55 @@
"@new_folder": {
"type": "text",
"placeholders": {}
},
"reply_all": "Reply all",
"@reply_all": {
"type": "text",
"placeholders": {}
},
"reply": "Reply",
"@reply": {
"type": "text",
"placeholders": {}
},
"forward": "Forward",
"@forward": {
"type": "text",
"placeholders": {}
},
"no_emails": "No emails",
"@no_emails": {
"type": "text",
"placeholders": {}
},
"no_mail_selected": "No email selected",
"@no_mail_selected": {
"type": "text",
"placeholders": {}
},
"from_email_address_prefix": "From",
"@from_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"to_email_address_prefix": "To",
"@to_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"unread_email_notification": "new",
"@unread_email_notification": {
"type": "text",
"placeholders": {}
},
"bcc_email_address_prefix": "Bcc",
"@bcc_email_address_prefix": {
"type": "text",
"placeholders": {}
},
"cc_email_address_prefix": "Cc",
"@cc_email_address_prefix": {
"type": "text",
"placeholders": {}
}
}
@@ -5,6 +5,7 @@ import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/http/http_client.dart' as JmapHttpClient;
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/login/data/network/login_api.dart';
import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart';
import 'package:tmail_ui_user/features/session/data/network/session_api.dart';
@@ -49,5 +50,6 @@ class NetworkBindings extends Bindings {
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>()));
}
}
@@ -121,5 +121,40 @@ class AppLocalizations {
name: 'no_mail_selected',
);
}
String get from_email_address_prefix {
return Intl.message(
'From',
name: 'from_email_address_prefix',
);
}
String get to_email_address_prefix {
return Intl.message(
'To',
name: 'to_email_address_prefix',
);
}
String get unread_email_notification {
return Intl.message(
'new',
name: 'unread_email_notification',
);
}
String get bcc_email_address_prefix {
return Intl.message(
'Bcc',
name: 'bcc_email_address_prefix',
);
}
String get cc_email_address_prefix {
return Intl.message(
'Cc',
name: 'cc_email_address_prefix',
);
}
}
+1 -3
View File
@@ -3,7 +3,6 @@ import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart';
import 'package:tmail_ui_user/features/home/presentation/home_view.dart';
import 'package:tmail_ui_user/features/login/presentation/login_bindings.dart';
import 'package:tmail_ui_user/features/login/presentation/login_view.dart';
import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart';
import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart';
@@ -39,7 +38,6 @@ class AppPages {
page: () => ThreadView()),
GetPage(
name: AppRoutes.EMAIL,
page: () => EmailView(),
binding: EmailBindings()),
page: () => EmailView()),
];
}
+1 -2
View File
@@ -92,8 +92,7 @@ dependencies:
# photo_view
photo_view: 0.12.0
# url_launcher
url_launcher: 6.0.9
sanitize_html: 2.0.0
dev_dependencies:
flutter_test: