From e4cf4808cbb93781dd5269aa33bd3cc1ee46f11b Mon Sep 17 00:00:00 2001 From: ManhNTX Date: Thu, 3 Nov 2022 15:32:26 +0700 Subject: [PATCH] TF-987: Email range selection (Shift) --- .../widgets/top_bar_thread_selection.dart | 4 +- .../widgets/app_bar_selection_mode.dart | 4 +- .../presentation/thread_controller.dart | 62 +++++++++++++++++-- .../app_bar_thread_widget_builder.dart | 4 +- .../widgets/email_tile_web_builder.dart | 4 +- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart b/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart index 35b9b3e8d..0bdf165ce 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart @@ -55,8 +55,8 @@ class TopBarThreadSelection { buildIconWeb( icon: SvgPicture.asset( listEmail.isAllEmailRead - ? imagePaths.icRead - : imagePaths.icUnread, + ? imagePaths.icUnread + : imagePaths.icRead, fit: BoxFit.fill), tooltip: listEmail.isAllEmailRead ? AppLocalizations.of(context).mark_as_unread diff --git a/lib/features/search/presentation/widgets/app_bar_selection_mode.dart b/lib/features/search/presentation/widgets/app_bar_selection_mode.dart index cadce9f7e..83960862a 100644 --- a/lib/features/search/presentation/widgets/app_bar_selection_mode.dart +++ b/lib/features/search/presentation/widgets/app_bar_selection_mode.dart @@ -60,8 +60,8 @@ class AppBarSelectionMode extends StatelessWidget { splashRadius: 15, icon: SvgPicture.asset( listEmail.isAllEmailRead - ? _imagePaths.icRead - : _imagePaths.icUnread, + ? _imagePaths.icUnread + : _imagePaths.icRead, fit: BoxFit.fill), tooltip: listEmail.isAllEmailRead ? AppLocalizations.of(context).unread diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 2a03516a5..7a21547f1 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -1,6 +1,7 @@ import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; @@ -100,7 +101,7 @@ class ThreadController extends BaseController { final CachingManager _cachingManager; final listEmailDrag = [].obs; - + bool shiftSelectedMode = false; bool canLoadMore = true; bool canSearchMore = true; bool _isLoadingMore = false; @@ -108,6 +109,8 @@ class ThreadController extends BaseController { MailboxId? _currentMailboxId; jmap.State? _currentEmailState; final ScrollController listEmailController = ScrollController(); + final FocusNode focusNodeKeyBoard = FocusNode(); + final latestEmailAction = Rxn(); late Worker mailboxWorker, searchWorker, dashboardActionWorker, viewStateWorker, advancedSearchFilterWorker; Set? get _sortOrder => {} @@ -160,6 +163,7 @@ class ThreadController extends BaseController { @override void onClose() { listEmailController.dispose(); + focusNodeKeyBoard.dispose(); _clearWorker(); super.onClose(); } @@ -516,11 +520,45 @@ class ThreadController extends BaseController { mailboxDashBoardController.dispatchRoute(DashboardRoutes.emailDetailed); } - void selectEmail(BuildContext context, PresentationEmail presentationEmailSelected) { - emailList.value = emailList - .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) - .toList(); + void _shiftSelectEmail(PresentationEmail presentationEmailSelected) { + int startIndexAction = 0; + int endIndexAction = 0; + final indexOfPresentationEmailSelected = emailList.indexWhere((e) => e.id == presentationEmailSelected.id); + final indexOfLatestEmailAction = emailList.indexWhere((e) => e.id == latestEmailAction.value?.id); + if (indexOfPresentationEmailSelected > indexOfLatestEmailAction) { + startIndexAction = indexOfLatestEmailAction; + endIndexAction = indexOfPresentationEmailSelected; + } else { + startIndexAction = indexOfPresentationEmailSelected; + endIndexAction = indexOfLatestEmailAction; + } + final enableChangeSelectModeOfEmailsToActive = latestEmailAction.value?.selectMode == SelectMode.ACTIVE && + !emailList.sublist(startIndexAction, endIndexAction).every((e) => e.selectMode == SelectMode.ACTIVE) || + latestEmailAction.value?.selectMode == SelectMode.INACTIVE && + emailList.sublist(startIndexAction, endIndexAction).every((e) => e.selectMode == SelectMode.INACTIVE); + + if (enableChangeSelectModeOfEmailsToActive) { + emailList.value = emailList.asMap().map((index, email) { + return MapEntry(index, index >= startIndexAction && index <= endIndexAction ? email.toSelectedEmail(selectMode: SelectMode.ACTIVE) : email); + }).values.toList(); + } else { + emailList.value = emailList.asMap().map((index, email) { + return MapEntry(index, index >= startIndexAction && index <= endIndexAction ? email.toSelectedEmail(selectMode: SelectMode.INACTIVE) : email); + }).values.toList(); + } + } + + void selectEmail(BuildContext context, PresentationEmail presentationEmailSelected) { + if (shiftSelectedMode && latestEmailAction.value != null && latestEmailAction.value?.id != presentationEmailSelected.id) { + _shiftSelectEmail(presentationEmailSelected); + } else { + emailList.value = emailList + .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) + .toList(); + } + latestEmailAction.value = emailList.firstWhere((e) => e.id == presentationEmailSelected.id); + focusNodeKeyBoard.requestFocus(); if (_isUnSelectedAll()) { mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE; mailboxDashBoardController.listEmailSelected.clear(); @@ -1470,4 +1508,18 @@ class ThreadController extends BaseController { } } } + + KeyEventResult handleKeyEvent(FocusNode node, RawKeyEvent event) { + final shiftEvent = event.logicalKey == LogicalKeyboardKey.shiftLeft || event.logicalKey == LogicalKeyboardKey.shiftRight; + if (event is RawKeyDownEvent && shiftEvent) { + shiftSelectedMode = true; + } + + if (event is RawKeyUpEvent && shiftEvent) { + shiftSelectedMode = false; + } + return shiftEvent + ? KeyEventResult.handled + : KeyEventResult.ignored; + } } \ 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 5fd57946e..959d42490 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 @@ -112,8 +112,8 @@ class AppBarThreadWidgetBuilder extends StatelessWidget { splashRadius: 15, icon: SvgPicture.asset( _listSelectionEmail.isAllEmailRead - ? _imagePaths.icRead - : _imagePaths.icUnread, + ? _imagePaths.icUnread + : _imagePaths.icRead, fit: BoxFit.fill), tooltip: _listSelectionEmail.isAllEmailRead ? AppLocalizations.of(context).unread diff --git a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart index 05a55d720..b27ebeaf0 100644 --- a/lib/features/thread/presentation/widgets/email_tile_web_builder.dart +++ b/lib/features/thread/presentation/widgets/email_tile_web_builder.dart @@ -393,8 +393,8 @@ class EmailTileBuilder with BaseEmailItemTile { splashRadius: 10, icon: SvgPicture.asset( _presentationEmail.hasRead - ? imagePaths.icRead - : imagePaths.icUnread, + ? imagePaths.icUnread + : imagePaths.icRead, color: AppColor.colorActionButtonHover, width: 16, height: 16,