diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index db9c3c93f..5ef21a672 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -1,7 +1,7 @@ import 'package:core/core.dart'; -import 'package:core/presentation/extensions/color_extension.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:model/model.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/bottom_bar_mail_widget_builder.dart'; @@ -61,8 +61,8 @@ class EmailView extends GetWidget { padding: EdgeInsets.all(20), color: AppColor.bgMessenger, child: Obx(() => Text( - mailboxDashBoardController.mailCurrent.value.id != 'empty' - ? '${mailboxDashBoardController.mailCurrent.value.message}' + 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) diff --git a/lib/features/mailbox/data/network/mailbox_api.dart b/lib/features/mailbox/data/network/mailbox_api.dart index 63b6b93bf..d41d78282 100644 --- a/lib/features/mailbox/data/network/mailbox_api.dart +++ b/lib/features/mailbox/data/network/mailbox_api.dart @@ -24,7 +24,6 @@ class MailboxAPI { final queryInvocation = jmapRequestBuilder.invocation(getMailboxCreated); final result = await (jmapRequestBuilder - ..invocation(getMailboxCreated) ..usings(getMailboxCreated.requiredCapabilities)) .build() .execute(); diff --git a/lib/features/mailbox/presentation/mailbox_bindings.dart b/lib/features/mailbox/presentation/mailbox_bindings.dart index a71d96dc8..261fb8dbf 100644 --- a/lib/features/mailbox/presentation/mailbox_bindings.dart +++ b/lib/features/mailbox/presentation/mailbox_bindings.dart @@ -25,7 +25,7 @@ class MailboxBindings extends Bindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => GetAllMailboxInteractor(Get.find())); Get.lazyPut(() => TreeBuilder()); - Get.lazyPut(() => MailboxController( + Get.put(MailboxController( Get.find(), Get.find(), Get.find(), diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 7ab15cd88..344f95d76 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -22,24 +22,41 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; class MailboxController extends BaseController { + final mailboxDashBoardController = Get.find(); final GetAllMailboxInteractor _getAllMailboxInteractor; final DeleteCredentialInteractor _deleteCredentialInteractor; final TreeBuilder _treeBuilder; final ResponsiveUtils responsiveUtils; - final mailboxDashBoardController = Get.find(); - - MailboxController(this._getAllMailboxInteractor, this._deleteCredentialInteractor, this._treeBuilder, this.responsiveUtils); - final folderMailboxTree = MailboxTree(MailboxNode.root()).obs; - final selectedMailbox = PresentationMailbox.createMailboxEmpty().obs; + + MailboxController( + this._getAllMailboxInteractor, + this._deleteCredentialInteractor, + this._treeBuilder, + this.responsiveUtils + ); @override void onReady() { super.onReady(); - if (mailboxDashBoardController.sessionCurrent != null) { - getAllMailboxAction( - mailboxDashBoardController.sessionCurrent!.accounts.keys.first); + mailboxDashBoardController.accountId.listen((accountId) { + if (accountId != null) { + getAllMailboxAction(accountId); + } + }); + } + + @override + void dispose() { + super.dispose(); + mailboxDashBoardController.accountId.close(); + } + + void refreshGetAllMailboxAction() { + final accountId = mailboxDashBoardController.accountId.value; + if (accountId != null) { + getAllMailboxAction(accountId); } } @@ -53,57 +70,51 @@ class MailboxController extends BaseController { newState.map((success) { if (success is GetAllMailboxSuccess) { _buildTree(success.folderMailboxList); - _setDefaultSelected(success.defaultMailboxList); } }); } @override void onDone() { + viewState.value.map((success) { + if (success is GetAllMailboxSuccess) { + _setUpMailboxDefault(success.defaultMailboxList); + } + }); } @override - void onError(error) { - } + void onError(error) {} void _buildTree(List folderMailboxList) async { folderMailboxTree.value = await _treeBuilder.generateMailboxTree(folderMailboxList); } - void _setDefaultSelected(List defaultMailboxList) { + void _setUpMailboxDefault(List defaultMailboxList) { try { final mailboxDefault = defaultMailboxList .firstWhere((presentationMailbox) => presentationMailbox.role == Role(MailboxConstants.ROLE_DEFAULT)); - selectedMailbox.value = mailboxDefault; - mailboxDashBoardController.mailboxCurrent.value = mailboxDefault; + mailboxDashBoardController.setSelectedMailbox(mailboxDefault); } catch (e) {} } - SelectMode getSelectMode(PresentationMailbox presentationMailbox) { - return presentationMailbox.id == selectedMailbox.value.id + SelectMode getSelectMode(PresentationMailbox presentationMailbox, PresentationMailbox selectedMailbox) { + return presentationMailbox.id == selectedMailbox.id ? SelectMode.ACTIVE : SelectMode.INACTIVE; } void selectMailbox( BuildContext context, - PresentationMailbox presentationMailboxSelected, - { - required GlobalKey keyWidgetMailboxDashBoard - } + PresentationMailbox presentationMailboxSelected ) { - selectedMailbox.value = presentationMailboxSelected; - mailboxDashBoardController.mailboxCurrent.value = presentationMailboxSelected; + mailboxDashBoardController.setSelectedMailbox(presentationMailboxSelected); if (responsiveUtils.isMobile(context)) { - goToThread(keyWidgetMailboxDashBoard: keyWidgetMailboxDashBoard); + mailboxDashBoardController.closeDrawer(); } } - void goToThread({required GlobalKey keyWidgetMailboxDashBoard}) { - keyWidgetMailboxDashBoard.currentState?.openEndDrawer(); - } - void _deleteCredential() async { await _deleteCredentialInteractor.execute(); } diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index 7ef9a6f89..343969c41 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -17,14 +17,12 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_tile import 'package:tmail_ui_user/features/mailbox/presentation/widgets/search_form_widget_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/storage_widget_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class MailboxView extends GetWidget { final imagePaths = Get.find(); final responsiveUtils = Get.find(); - final mailboxDashBoardController = Get.find(); @override Widget build(BuildContext context) { @@ -39,9 +37,7 @@ class MailboxView extends GetWidget { left: false, child: RefreshIndicator( color: AppColor.primaryColor, - onRefresh: () async => mailboxDashBoardController.sessionCurrent != null - ? controller.getAllMailboxAction(mailboxDashBoardController.sessionCurrent!.accounts.keys.first) - : null, + onRefresh: () async => controller.refreshGetAllMailboxAction(), child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), child: Padding( @@ -177,8 +173,8 @@ class MailboxView extends GetWidget { imagePaths, responsiveUtils, defaultMailbox[index], - controller.getSelectMode(defaultMailbox[index])) - .onOpenMailboxAction(() => controller.selectMailbox(context, defaultMailbox[index], keyWidgetMailboxDashBoard: mailboxDashBoardController.scaffoldKey)) + controller.getSelectMode(defaultMailbox[index], controller.mailboxDashBoardController.selectedMailbox.value)) + .onOpenMailboxAction(() => controller.selectMailbox(context, defaultMailbox[index])) .build())); } @@ -201,11 +197,25 @@ class MailboxView extends GetWidget { padding: EdgeInsets.only(left: 16), child: TreeViewChild( key: Key('children_tree_mailbox_child'), - parent: MailBoxFolderTileBuilder(context, imagePaths, responsiveUtils, mailboxNode).build(context), + parent: Obx(() => MailBoxFolderTileBuilder( + context, + imagePaths, + responsiveUtils, + mailboxNode, + controller.getSelectMode(mailboxNode.item, controller.mailboxDashBoardController.selectedMailbox.value)) + .build(context)), children: _buildListChildTileWidget(context, mailboxNode.childrenItems))) : Padding( padding: EdgeInsets.only(left: 16), - child: MailBoxFolderTileBuilder(context, imagePaths, responsiveUtils, mailboxNode).build(context))) + child: GestureDetector( + onTap: () => controller.selectMailbox(context, mailboxNode.item), + child: Obx(() => MailBoxFolderTileBuilder( + context, + imagePaths, + responsiveUtils, + mailboxNode, + controller.getSelectMode(mailboxNode.item, controller.mailboxDashBoardController.selectedMailbox.value)) + .build(context))))) .toList(); } diff --git a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart index 8d76fa931..0f535d2a4 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart @@ -2,6 +2,7 @@ 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/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; @@ -13,6 +14,7 @@ class MailBoxFolderTileBuilder extends StatelessWidget { final BuildContext _context; final ImagePaths _imagePaths; final ResponsiveUtils _responsiveUtils; + final SelectMode _selectMode; final OnOpenMailBoxFolderActionClick? onOpenMailBoxFolderActionClick; @@ -21,6 +23,7 @@ class MailBoxFolderTileBuilder extends StatelessWidget { this._imagePaths, this._responsiveUtils, this._mailboxNode, + this._selectMode, { this.onOpenMailBoxFolderActionClick } @@ -33,20 +36,35 @@ class MailBoxFolderTileBuilder extends StatelessWidget { alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), - color: AppColor.mailboxBackgroundColor), + color: _selectMode== SelectMode.ACTIVE + ? AppColor.mailboxSelectedBackgroundColor + : AppColor.mailboxBackgroundColor), child: MediaQuery( data: MediaQueryData(padding: EdgeInsets.zero), child: ListTile( contentPadding: EdgeInsets.zero, leading: Padding( padding: EdgeInsets.only(left: _responsiveUtils.isMobile(_context) ? 40 : 34), - child: SvgPicture.asset(_imagePaths.icMailboxFolder, width: 24, height: 24, color: AppColor.mailboxIconColor, fit: BoxFit.fill)), + child: SvgPicture.asset( + _imagePaths.icMailboxFolder, + width: 24, + height: 24, + color: _selectMode == SelectMode.ACTIVE + ? AppColor.mailboxSelectedIconColor + : AppColor.mailboxIconColor, + fit: BoxFit.fill)), title: Padding( padding: EdgeInsets.only(left: 8), child: Text( _mailboxNode.item.name!.name, maxLines: 1, - style: TextStyle(fontSize: 15, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 15, + color: _selectMode == SelectMode.ACTIVE + ? AppColor.mailboxSelectedTextColor + : AppColor.mailboxTextColor, + fontWeight: FontWeight.bold), )), trailing: Padding( padding: EdgeInsets.only(right: _responsiveUtils.isMobile(_context) ? 36 : 8, left: 16), @@ -55,7 +73,9 @@ class MailBoxFolderTileBuilder extends StatelessWidget { _imagePaths.icFolderArrow, width: 12, height: 12, - color: AppColor.mailboxIconColor, + color: _selectMode == SelectMode.ACTIVE + ? AppColor.mailboxSelectedIconColor + : AppColor.mailboxIconColor, fit: BoxFit.fill) : SizedBox.shrink()) ) diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart index cfc72eea8..f762d2898 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart @@ -7,7 +7,7 @@ class MailboxDashBoardBindings extends Bindings { @override void dependencies() { - Get.lazyPut(() => MailboxDashBoardController()); + Get.put(MailboxDashBoardController()); MailboxBindings().dependencies(); ThreadBindings().dependencies(); diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index a4038d4d1..0b370d23a 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -1,6 +1,7 @@ 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/core/session/session.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; @@ -8,8 +9,10 @@ import 'package:tmail_ui_user/features/base/base_controller.dart'; class MailboxDashBoardController extends BaseController { final scaffoldKey = GlobalKey(); - final mailboxCurrent = PresentationMailbox.createMailboxEmpty().obs; - final mailCurrent = PresentationThread.createThreadEmpty().obs; + final selectedMailbox = PresentationMailbox.presentationMailboxEmpty.obs; + final selectedEmail = PresentationEmail.presentationEmailEmpty.obs; + final accountId = Rxn(); + Session? sessionCurrent; MailboxDashBoardController(); @@ -17,7 +20,7 @@ class MailboxDashBoardController extends BaseController { @override void onReady() { super.onReady(); - scaffoldKey.currentState?.openDrawer(); + _setSessionCurrent(); } @override @@ -28,7 +31,26 @@ class MailboxDashBoardController extends BaseController { void onError(error) { } - void setSessionCurrent(Session session) { - sessionCurrent = session; + void _setSessionCurrent() { + Future.delayed(const Duration(milliseconds: 500), () { + sessionCurrent = Get.arguments as Session; + accountId.value = sessionCurrent?.accounts.keys.first; + }); + } + + void setSelectedMailbox(PresentationMailbox newPresentationMailbox) { + selectedMailbox.value = newPresentationMailbox; + } + + void setSelectedEmail(PresentationEmail newPresentationEmail) { + selectedEmail.value = newPresentationEmail; + } + + void openDrawer() { + scaffoldKey.currentState?.openEndDrawer(); + } + + void closeDrawer() { + scaffoldKey.currentState?.openEndDrawer(); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart index 65703814a..fc78bd859 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart @@ -1,7 +1,6 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/core/session/session.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_controller.dart'; @@ -13,9 +12,6 @@ class MailboxDashBoardView extends GetWidget { @override Widget build(BuildContext context) { - final arguments = Get.arguments as Session; - controller.setSessionCurrent(arguments); - return Scaffold( key: controller.scaffoldKey, drawer: responsiveUtils.isMobile(context) diff --git a/lib/features/thread/presentation/model/load_more_state.dart b/lib/features/thread/presentation/model/load_more_state.dart new file mode 100644 index 000000000..f1b3c9c74 --- /dev/null +++ b/lib/features/thread/presentation/model/load_more_state.dart @@ -0,0 +1,6 @@ + +enum LoadMoreState { + IDLE, + LOADING, + COMPLETED +} \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_bindings.dart b/lib/features/thread/presentation/thread_bindings.dart index 2514b5a37..bd8bd68a6 100644 --- a/lib/features/thread/presentation/thread_bindings.dart +++ b/lib/features/thread/presentation/thread_bindings.dart @@ -1,10 +1,24 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:get/get.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/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/repository/thread_repository_impl.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; class ThreadBindings extends Bindings { @override void dependencies() { - Get.lazyPut(() => ThreadController(Get.find())); + Get.lazyPut(() => ThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => ThreadRepositoryImpl(Get.find())); + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => GetEmailsInMailboxInteractor(Get.find())); + Get.put(ThreadController( + Get.find(), + Get.find(), + )); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index de48b24ad..68a68eda6 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -3,47 +3,58 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; +import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/load_more_state.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; class ThreadController extends BaseController { - final ResponsiveUtils responsiveUtils; final mailboxDashBoardController = Get.find(); + final GetEmailsInMailboxInteractor _getEmailsInMailboxInteractor; + final ResponsiveUtils responsiveUtils; - final presentationMailCurrent = PresentationThread.createThreadEmpty().obs; + final _properties = Properties({ + 'id', 'subject', 'from', 'to', 'cc', 'bcc', 'keywords', 'receivedAt', + 'sentAt', 'preview', 'hasAttachment' + }); - ThreadController(this.responsiveUtils); + final emailList = [].obs; + final loadMoreState = LoadMoreState.IDLE.obs; + + int positionCurrent = 0; + int lastGetTotal = 0; + + ThreadController( + this.responsiveUtils, + this._getEmailsInMailboxInteractor + ); @override void onReady() { super.onReady(); - mailboxDashBoardController.scaffoldKey.currentState?.openEndDrawer(); + mailboxDashBoardController.selectedMailbox.listen((selectedMailbox) { + mailboxDashBoardController.setSelectedEmail(PresentationEmail.presentationEmailEmpty); + refreshGetAllEmailAction(); + }); } - SelectMode getSelectMode(PresentationThread presentationThread) { - return presentationMailCurrent.value.id == presentationThread.id - ? SelectMode.ACTIVE - : SelectMode.INACTIVE; - } - - void selectMail(BuildContext context, PresentationThread presentationMailSelected) { - presentationMailCurrent.value = presentationMailSelected; - mailboxDashBoardController.mailCurrent.value = presentationMailSelected; - - if (!responsiveUtils.isDesktop(context)) { - goToMail(context); - } - } - - void goToMailbox({required GlobalKey keyWidgetMailboxContainer}) { - keyWidgetMailboxContainer.currentState?.openDrawer(); - } - - void goToMail(BuildContext context) { - Get.toNamed(AppRoutes.EMAIL); + @override + void dispose() { + super.dispose(); + mailboxDashBoardController.selectedMailbox.close(); } @override @@ -52,8 +63,123 @@ class ThreadController extends BaseController { } @override - void onDone() {} + void onDone() { + viewState.value.fold( + (failure) => _resetPositionCurrentAndLoadMoreState(), + (success) => _updateEmailList(success is GetAllEmailSuccess ? success.emailList : []) + ); + } @override - void onError(error) {} + void onError(error) { + _resetPositionCurrentAndLoadMoreState(); + } + + void _updateEmailList(List newListEmail) { + emailList.value += newListEmail; + + lastGetTotal = emailList.length; + + if (newListEmail.isEmpty) { + _resetPositionCurrentAndLoadMoreState(); + } else { + loadMoreState.value = LoadMoreState.IDLE; + } + } + + EmailFilterCondition? _getFilterConditionCurrent() { + return mailboxDashBoardController.selectedMailbox.value.id != PresentationMailbox.presentationMailboxEmpty.id + ? EmailFilterCondition(inMailbox: mailboxDashBoardController.selectedMailbox.value.id) + : null; + } + + Set? _getSortCurrent() { + return Set() + ..add(EmailComparator(EmailComparatorProperty.sentAt) + ..setIsAscending(false)); + } + + void _resetPositionCurrentAndLoadMoreState() { + if (loadMoreState.value == LoadMoreState.LOADING) { + positionCurrent -= lastGetTotal; + } + loadMoreState.value = LoadMoreState.COMPLETED; + } + + void getAllEmailAction(AccountId accountId, + { + UnsignedInt? limit, + int position = 0, + Set? sort, + Filter? filter, + Properties? properties, + } + ) async { + consumeState(_getEmailsInMailboxInteractor.execute( + accountId, + limit: limit, + position: position, + sort: sort, + filter: filter, + properties: properties + )); + } + + void refreshGetAllEmailAction() { + loadMoreState.value = LoadMoreState.IDLE; + positionCurrent = 0; + emailList.clear(); + + final accountId = mailboxDashBoardController.accountId.value; + + if (accountId != null) { + getAllEmailAction( + accountId, + limit: ThreadConstants.defaultLimit, + position: positionCurrent, + sort: _getSortCurrent(), + properties: _properties, + filter: _getFilterConditionCurrent()); + } + } + + void loadMoreEmailAction() { + loadMoreState.value = LoadMoreState.LOADING; + positionCurrent += lastGetTotal; + + final accountId = mailboxDashBoardController.accountId.value; + + if (accountId != null) { + Future.delayed(const Duration(milliseconds: 500), () { + getAllEmailAction( + accountId, + limit: ThreadConstants.defaultLimit, + position: positionCurrent, + sort: _getSortCurrent(), + properties: _properties, + filter: _getFilterConditionCurrent()); + }); + } + } + + SelectMode getSelectMode(PresentationEmail presentationEmail, PresentationEmail selectedEmail) { + return presentationEmail.id == selectedEmail.id + ? SelectMode.ACTIVE + : SelectMode.INACTIVE; + } + + void selectEmail(BuildContext context, PresentationEmail presentationEmailSelected) { + mailboxDashBoardController.setSelectedEmail(presentationEmailSelected); + if (!responsiveUtils.isDesktop(context)) { + goToEmail(context); + } + } + + void goToMailbox() { + mailboxDashBoardController.openDrawer(); + } + + void goToEmail(BuildContext context) { + Get.toNamed(AppRoutes.EMAIL); + } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index dede075e0..62416cf13 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -2,16 +2,16 @@ import 'package:core/core.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:model/email/presentation_email.dart'; import 'package:model/model.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/load_more_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/thread_tile_builder.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class ThreadView extends GetWidget { - final mailboxDashBoardController = Get.find(); final responsiveUtils = Get.find(); final imagePaths = Get.find(); @@ -19,7 +19,7 @@ class ThreadView extends GetWidget { Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, - backgroundColor: AppColor.primaryLightColor, + backgroundColor: responsiveUtils.isMobile(context) ? AppColor.bgMailboxListMail : AppColor.primaryLightColor, body: SafeArea( right: false, left: false, @@ -29,27 +29,54 @@ class ThreadView extends GetWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Obx(() => _buildAppBarMailboxListMail(context)), - Expanded(child: _buildListMail(context)), - ]) + Obx(() => _buildAppBarMailboxListMail(context, controller.mailboxDashBoardController.selectedMailbox.value)), + _buildLoadingView(), + Expanded(child: _buildListEmail(context)), + _buildLoadingViewLoadMore() + ] + ) ) ), ); } - Widget _buildAppBarMailboxListMail(BuildContext context) { + Widget _buildAppBarMailboxListMail(BuildContext context, PresentationMailbox presentationMailbox) { return Padding( padding: EdgeInsets.only(left: 16, top: 8.0, right: 16), child: AppBarThreadWidgetBuilder( context, imagePaths, responsiveUtils, - mailboxDashBoardController.mailboxCurrent.value) - .onOpenListMailboxActionClick(() => controller.goToMailbox(keyWidgetMailboxContainer: mailboxDashBoardController.scaffoldKey)) + presentationMailbox) + .onOpenListMailboxActionClick(() => controller.goToMailbox()) .build()); } - Widget _buildListMail(BuildContext context) { + Widget _buildLoadingView() { + return Obx(() => controller.viewState.value.fold( + (failure) => SizedBox.shrink(), + (success) => success == UIState.loading + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink())); + } + + Widget _buildLoadingViewLoadMore() { + return Obx(() => controller.loadMoreState.value == LoadMoreState.LOADING + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink()); + } + + Widget _buildListEmail(BuildContext context) { return Container( margin: EdgeInsets.zero, width: double.infinity, @@ -57,37 +84,54 @@ class ThreadView extends GetWidget { alignment: Alignment.center, padding: EdgeInsets.zero, color: responsiveUtils.isMobile(context) ? AppColor.bgMailboxListMail : Colors.white, - child: Obx(() => controller.viewState.value.fold( - (failure) => _buildEmptyThread(context), - (success) => _buildListThreadBody(context, [])) - ), - ); + child: Obx(() => RefreshIndicator( + color: AppColor.primaryColor, + onRefresh: () async => controller.refreshGetAllEmailAction(), + child: controller.emailList.isNotEmpty + ? _buildListEmailBody(context, controller.emailList) + : _buildEmptyEmail(context))) + ); } - Widget _buildListThreadBody(BuildContext context, List listThread) { - if (listThread.isEmpty) { - return _buildEmptyThread(context); + Widget _buildListEmailBody(BuildContext context, List listPresentationEmail) { + if (listPresentationEmail.isEmpty) { + return _buildEmptyEmail(context); } else { - return ListView.builder( - padding: EdgeInsets.only(top: 16, left: 16, right: 16), - key: Key('thread_list'), - itemCount: listThread.length, - itemBuilder: (context, index) => - Obx(() => ThreadTileBuilder( - imagePaths, - controller.getSelectMode(listThread[index]), - listThread[index]) - .onOpenMailAction(() => controller.selectMail(context, listThread[index])) - .build() - ) - ); + return NotificationListener( + onNotification: (ScrollNotification scrollInfo) { + if (controller.loadMoreState.value == LoadMoreState.IDLE + && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { + controller.loadMoreEmailAction(); + return true; + } + return false; + }, + child: ListView.builder( + padding: EdgeInsets.only(top: 16, left: 16, right: 16), + key: Key('presentation_email_list'), + itemCount: listPresentationEmail.length, + itemBuilder: (context, index) => + Obx(() => EmailTileBuilder( + context, + imagePaths, + controller.getSelectMode(listPresentationEmail[index], controller.mailboxDashBoardController.selectedEmail.value), + listPresentationEmail[index], + responsiveUtils) + .onOpenMailAction(() => controller.selectEmail(context, listPresentationEmail[index])) + .build()) + )); } } - Widget _buildEmptyThread(BuildContext context) { - return Text( - AppLocalizations.of(context).no_threads, - maxLines: 1, - style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)); + Widget _buildEmptyEmail(BuildContext context) { + return Obx(() => controller.viewState.value.fold( + (failure) => SizedBox.shrink(), + (success) => success != UIState.loading + ? Text( + AppLocalizations.of(context).no_emails, + maxLines: 1, + style: TextStyle(fontSize: 25, color: AppColor.mailboxTextColor, fontWeight: FontWeight.bold)) + : SizedBox.shrink()) + ); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart index d98c3a6de..9c8bc5006 100644 --- a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart +++ b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart @@ -104,6 +104,7 @@ class AppBarThreadWidgetBuilder { child: Text( '${_presentationMailbox.name?.name}', maxLines: 1, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 22, color: AppColor.titleAppBarMailboxListMail, fontWeight: FontWeight.w500), ))), if(_presentationMailbox.getCountUnReadEmails().isNotEmpty) @@ -116,8 +117,9 @@ class AppBarThreadWidgetBuilder { child: Text( '${_presentationMailbox.getCountUnReadEmails()} new', maxLines: 1, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 10, color: AppColor.notifyCountAppBarMailboxListMail, fontWeight: FontWeight.w500), - )) + )), ] ); } diff --git a/lib/features/thread/presentation/widgets/thread_tile_builder.dart b/lib/features/thread/presentation/widgets/email_tile_builder.dart similarity index 67% rename from lib/features/thread/presentation/widgets/thread_tile_builder.dart rename to lib/features/thread/presentation/widgets/email_tile_builder.dart index 9067fa2eb..cdf55ac80 100644 --- a/lib/features/thread/presentation/widgets/thread_tile_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_builder.dart @@ -8,21 +8,25 @@ import 'package:model/model.dart'; typedef OnOpenMailActionClick = void Function(); -class ThreadTileBuilder { +class EmailTileBuilder { final SelectMode _selectMode; final ImagePaths _imagePaths; - final PresentationThread _presentationThread; + final PresentationEmail _presentationEmail; + final BuildContext _context; + final ResponsiveUtils _responsiveUtils; OnOpenMailActionClick? _onOpenMailActionClick; - ThreadTileBuilder( + EmailTileBuilder( + this._context, this._imagePaths, this._selectMode, - this._presentationThread, + this._presentationEmail, + this._responsiveUtils, ); - ThreadTileBuilder onOpenMailAction(OnOpenMailActionClick onOpenMailActionClick) { + EmailTileBuilder onOpenMailAction(OnOpenMailActionClick onOpenMailActionClick) { _onOpenMailActionClick = onOpenMailActionClick; return this; } @@ -40,8 +44,10 @@ class ThreadTileBuilder { decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: _selectMode== SelectMode.ACTIVE - ? AppColor.mailboxSelectedBackgroundColor - : AppColor.mailboxBackgroundColor), + ? _responsiveUtils.isDesktop(_context) + ? AppColor.mailboxSelectedBackgroundColor + : _responsiveUtils.isMobile(_context) ? AppColor.bgMailboxListMail : Colors.white + : _responsiveUtils.isMobile(_context) ? AppColor.bgMailboxListMail : Colors.white), child: MediaQuery( data: MediaQueryData(padding: EdgeInsets.zero), child: ListTile( @@ -59,7 +65,9 @@ class ThreadTileBuilder { _imagePaths.icTMailLogo, width: 32, height: 32, - color: _selectMode == SelectMode.ACTIVE ? AppColor.mailboxSelectedIconColor : AppColor.mailboxIconColor, + color: _selectMode == SelectMode.ACTIVE + ? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedIconColor : AppColor.mailboxIconColor + : AppColor.mailboxIconColor, fit: BoxFit.fill)), title: Padding( padding: EdgeInsets.only(left: 0), @@ -69,20 +77,24 @@ class ThreadTileBuilder { children: [ Expanded( child: Text( - '', + '${_presentationEmail.getSenderName()}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle( fontSize: 14, - color: _selectMode == SelectMode.ACTIVE ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor, + 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( fontSize: 12, - color: _selectMode == SelectMode.ACTIVE ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor)) + color: _selectMode == SelectMode.ACTIVE + ? _responsiveUtils.isDesktop(_context) ? AppColor.mailboxSelectedTextColor : AppColor.mailboxTextColor + : AppColor.mailboxTextColor)) ], ) ), @@ -95,12 +107,12 @@ class ThreadTileBuilder { Padding( padding: EdgeInsets.only(bottom: 3), child: Text( - '', + '${_presentationEmail.getEmailTitle()}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle( fontSize: 12, - color: AppColor.appColor, + color: AppColor.baseTextColor, fontWeight: FontWeight.bold))), Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -108,13 +120,11 @@ class ThreadTileBuilder { children: [ Expanded( child: Text( - '${_presentationThread.message}', + '${_presentationEmail.getPartialContent()}', maxLines: 1, overflow:TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: AppColor.baseTextColor))), - ButtonBuilder(_imagePaths.icCalendar).build(), - SizedBox(width: 12), - ButtonBuilder(_imagePaths.icShare).build(), + if (_presentationEmail.hasAttachment == true) ButtonBuilder(_imagePaths.icShare).build(), SizedBox(width: 12), ButtonBuilder(_imagePaths.icStar).build(), ], diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index b00f77b32..bf80f0a3b 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -8,6 +8,7 @@ import 'package:jmap_dart_client/http/http_client.dart' as JmapHttpClient; 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'; +import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; class NetworkBindings extends Bindings { @@ -47,5 +48,6 @@ class NetworkBindings extends Bindings { Get.put(LoginAPI(Get.find())); Get.put(MailboxAPI(Get.find())); Get.put(SessionAPI(Get.find())); + Get.put(ThreadAPI(Get.find())); } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 538b20e96..b4f76b570 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -108,10 +108,10 @@ class AppLocalizations { ); } - String get no_threads { + String get no_emails { return Intl.message( - 'No threads', - name: 'no_threads', + 'No emails', + name: 'no_emails', ); } diff --git a/lib/main/pages/app_pages.dart b/lib/main/pages/app_pages.dart index b34bfa8b3..447473fa5 100644 --- a/lib/main/pages/app_pages.dart +++ b/lib/main/pages/app_pages.dart @@ -5,11 +5,9 @@ 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_bindings.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'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_view.dart'; -import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart'; import 'package:tmail_ui_user/features/session/presentation/session_bindings.dart'; import 'package:tmail_ui_user/features/session/presentation/session_view.dart'; @@ -31,16 +29,14 @@ class AppPages { binding: SessionBindings()), GetPage( name: AppRoutes.MAILBOX, - page: () => MailboxView(), - binding: MailboxBindings()), + page: () => MailboxView()), GetPage( name: AppRoutes.MAILBOX_DASHBOARD, page: () => MailboxDashBoardView(), binding: MailboxDashBoardBindings()), GetPage( name: AppRoutes.THREAD, - page: () => ThreadView(), - binding: ThreadBindings()), + page: () => ThreadView()), GetPage( name: AppRoutes.EMAIL, page: () => EmailView(),