TF-934 Select mailbox, email when change route on browser

This commit is contained in:
dab246
2022-11-10 20:14:47 +07:00
committed by Dat H. Pham
parent aa8b80975e
commit 6050ee5386
21 changed files with 491 additions and 287 deletions
@@ -1,16 +1,20 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:model/model.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
class GetEmailByIdLoading extends LoadingState {}
class GetEmailByIdSuccess extends UIState {
final PresentationEmail email;
final MailboxId? mailboxId;
final DashboardType dashboardType;
GetEmailByIdSuccess(this.email);
GetEmailByIdSuccess(this.email, this.mailboxId, this.dashboardType);
@override
List<Object?> get props => [email];
List<Object?> get props => [email, mailboxId, dashboardType];
}
class GetEmailByIdFailure extends FeatureFailure {
@@ -4,8 +4,10 @@ import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart';
import 'package:tmail_ui_user/features/thread/domain/state/get_email_by_id_state.dart';
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
class GetEmailByIdInteractor {
final ThreadRepository _threadRepository;
@@ -16,13 +18,15 @@ class GetEmailByIdInteractor {
AccountId accountId,
EmailId emailId,
{
MailboxId? mailboxId,
DashboardType dashboardType = DashboardType.normal,
Properties? properties,
}
) async* {
try {
yield Right<Failure, Success>(GetEmailByIdLoading());
final email = await _threadRepository.getEmailById(accountId, emailId, properties: properties);
yield Right<Failure, Success>(GetEmailByIdSuccess(email));
yield Right<Failure, Success>(GetEmailByIdSuccess(email, mailboxId, dashboardType));
} catch (e) {
yield Left<Failure, Success>(GetEmailByIdFailure(e));
}
@@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_t
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
mixin EmailActionController on ViewAsDialogActionMixin {
@@ -47,6 +48,11 @@ mixin EmailActionController on ViewAsDialogActionMixin {
void previewEmail(BuildContext context, PresentationEmail presentationEmail) {
mailboxDashBoardController.setSelectedEmail(presentationEmail);
mailboxDashBoardController.dispatchRoute(DashboardRoutes.emailDetailed);
if (BuildUtils.isWeb && presentationEmail.routeWeb != null) {
RouteUtils.updateRouteOnBrowser(
'Email-${presentationEmail.id.id.value}',
presentationEmail.routeWeb!);
}
}
void moveToTrash(PresentationEmail email) async {
@@ -3,10 +3,10 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/build_utils.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';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
@@ -85,6 +85,8 @@ class ThreadController extends BaseController with EmailActionController {
final listEmailDrag = <PresentationEmail>[].obs;
bool _rangeSelectionMode = false;
final openingEmail = RxBool(false);
bool canLoadMore = true;
bool canSearchMore = true;
bool _isLoadingMore = false;
@@ -153,6 +155,8 @@ class ThreadController extends BaseController with EmailActionController {
emailList.clear();
} else if (failure is SearchMoreEmailFailure || failure is LoadMoreEmailsFailure) {
_isLoadingMore = false;
} else if (failure is GetEmailByIdFailure) {
openingEmail.value = false;
}
},
(success) {
@@ -168,26 +172,11 @@ class ThreadController extends BaseController with EmailActionController {
_searchMoreEmailsSuccess(success);
} else if (success is SearchingMoreState || success is LoadingMoreState) {
_isLoadingMore = true;
} else if (success is GetEmailByIdLoading) {
openingEmail.value = true;
} else if (success is GetEmailByIdSuccess) {
if (currentContext != null) {
final mailboxContain = success.email
.findMailboxContain(mailboxDashBoardController.mapMailboxById);
final route = RouteUtils.generateRouteBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: success.email.id,
mailboxId: mailboxContain?.id,
dashboardType: searchController.isSearchEmailRunning
? DashboardType.search
: DashboardType.normal
)
);
pressEmailAction(
currentContext!,
EmailActionType.preview,
success.email.withRouteWeb(route),
mailboxContain: mailboxContain);
}
openingEmail.value = false;
_openEmailDetailView(success.email, success.mailboxId, success.dashboardType);
}
}
);
@@ -246,23 +235,14 @@ class ThreadController extends BaseController with EmailActionController {
pressEmailSelectionAction(action.context, action.emailAction, action.listEmailSelected);
mailboxDashBoardController.clearDashBoardAction();
} else if (action is OpenEmailDetailedFromSuggestionQuickSearchAction) {
final mailboxContain = action.presentationEmail
.findMailboxContain(mailboxDashBoardController.mapMailboxById);
final route = RouteUtils.generateRouteBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: action.presentationEmail.id,
mailboxId: mailboxContain?.id,
dashboardType: searchController.isSearchEmailRunning
? DashboardType.search
: DashboardType.normal
)
);
final mailboxContain = action.presentationEmail.findMailboxContain(mailboxDashBoardController.mapMailboxById);
final newEmail = generateEmailByPlatform(action.presentationEmail);
pressEmailAction(
action.context,
EmailActionType.preview,
action.presentationEmail.withRouteWeb(route),
mailboxContain: mailboxContain);
action.context,
EmailActionType.preview,
newEmail,
mailboxContain: mailboxContain
);
mailboxDashBoardController.clearDashBoardAction();
} else if (action is StartSearchEmailAction) {
cancelSelectEmail();
@@ -272,7 +252,7 @@ class ThreadController extends BaseController with EmailActionController {
deleteSelectionEmailsPermanently(action.context, DeleteActionType.all);
mailboxDashBoardController.clearDashBoardAction();
} else if (action is SelectEmailByIdAction) {
_openEmailDetailedView(action.emailId);
_getEmailByIdAction(action.emailId, action.mailboxId, action.dashboardType);
mailboxDashBoardController.clearDashBoardAction();
}
});
@@ -503,11 +483,6 @@ class ThreadController extends BaseController with EmailActionController {
: SelectMode.INACTIVE;
}
void previewEmail(BuildContext context, PresentationEmail presentationEmailSelected) {
mailboxDashBoardController.setSelectedEmail(presentationEmailSelected);
mailboxDashBoardController.dispatchRoute(DashboardRoutes.emailDetailed);
}
Tuple2<StartRangeSelection,EndRangeSelection> _getSelectionEmailsRange(PresentationEmail presentationEmailSelected) {
final emailSelectedIndex = emailList.indexWhere((e) => e.id == presentationEmailSelected.id);
final latestEmailSelectedOrUnselectedIndex = emailList.indexWhere((e) => e.id == latestEmailSelectedOrUnselected.value?.id);
@@ -833,12 +808,57 @@ class ThreadController extends BaseController with EmailActionController {
: KeyEventResult.ignored;
}
void _openEmailDetailedView(EmailId emailId) {
PresentationEmail generateEmailByPlatform(PresentationEmail currentEmail) {
if (BuildUtils.isWeb) {
final route = RouteUtils.generateRouteBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: currentEmail.id,
mailboxId: currentMailbox?.id,
dashboardType: searchController.isSearchEmailRunning
? DashboardType.search
: DashboardType.normal
)
);
final emailOnWeb = currentEmail.withRouteWeb(route);
return emailOnWeb;
} else {
return currentEmail;
}
}
void _getEmailByIdAction(EmailId emailId, MailboxId? mailboxId, DashboardType dashboardType) {
if (_accountId != null) {
consumeState(_getEmailByIdInteractor.execute(
_accountId!,
emailId,
mailboxId: mailboxId,
dashboardType: dashboardType,
properties: ThreadConstants.propertiesDefault));
}
}
void _openEmailDetailView(
PresentationEmail email,
MailboxId? mailboxId,
DashboardType dashboardType
) {
if (currentContext != null) {
final mailboxContain = email.findMailboxContain(mailboxDashBoardController.mapMailboxById);
final route = RouteUtils.generateRouteBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: email.id,
mailboxId: mailboxId,
dashboardType: dashboardType
)
);
pressEmailAction(
currentContext!,
EmailActionType.preview,
email.withRouteWeb(route),
mailboxContain: mailboxContain
);
}
}
}
@@ -251,7 +251,7 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget)
: const SizedBox.shrink();
} else {
return success is LoadingState
return success is LoadingState || controller.openingEmail.isTrue
? Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: loadingWidget)
: const SizedBox.shrink();
}
@@ -283,7 +283,9 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
padding: EdgeInsets.zero,
color: Colors.white,
child: Obx(() {
return _buildResultListEmail(context, controller.emailList);
return Visibility(
visible: controller.openingEmail.isFalse,
child: _buildResultListEmail(context, controller.emailList));
})
);
}
@@ -333,48 +335,39 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
itemExtent: _getItemExtent(context),
itemCount: listPresentationEmail.length,
itemBuilder: (context, index) {
final mailboxContain = _getMailboxContain(listPresentationEmail[index]);
final newPresentationEmail = controller.generateEmailByPlatform(listPresentationEmail[index]);
return Obx(() => Draggable<List<PresentationEmail>>(
maxSimultaneousDrags: kIsWeb ? null : 0,
data: controller.listEmailDrag,
child: (EmailTileBuilder(
context,
listPresentationEmail[index],
controller.mailboxDashBoardController.currentSelectMode.value,
controller.searchQuery,
controller.mailboxDashBoardController.selectedEmail.value?.id == listPresentationEmail[index].id,
mailboxCurrent: controller.searchController.isSearchEmailRunning
? listPresentationEmail[index].findMailboxContain(controller.mailboxDashBoardController.mapMailboxById)
: controller.currentMailbox,
isSearchEmailRunning: controller.searchController.isSearchEmailRunning)
..addOnPressEmailActionClick((action, email) =>
controller.pressEmailAction(
context,
action,
email,
mailboxContain: controller.searchController.isSearchEmailRunning
? email.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById)
: controller.currentMailbox))
..addOnMoreActionClick((email, position) => _responsiveUtils.isMobile(context)
? controller.openContextMenuAction(context, _contextMenuActionTile(context, email))
: controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email)))
newPresentationEmail,
controller.mailboxDashBoardController.currentSelectMode.value,
controller.searchQuery,
controller.mailboxDashBoardController.selectedEmail.value?.id == newPresentationEmail.id,
mailboxContain: mailboxContain,
isSearchEmailRunning: controller.searchController.isSearchEmailRunning)
..addOnPressEmailActionClick((action, email) =>
controller.pressEmailAction(context, action, email, mailboxContain: mailboxContain))
..addOnMoreActionClick((email, position) => _responsiveUtils.isScreenWithShortestSide(context)
? controller.openContextMenuAction(context, _contextMenuActionTile(context, email))
: controller.openPopupMenuAction(context, position, _popupMenuActionTile(context, email)))
).build(),
feedback: _buildFeedBackWidget(context),
childWhenDragging: (EmailTileBuilder(
context,
listPresentationEmail[index],
newPresentationEmail,
controller.mailboxDashBoardController.currentSelectMode.value,
controller.searchQuery,
controller.mailboxDashBoardController.selectedEmail.value?.id == listPresentationEmail[index].id,
mailboxCurrent: controller.searchController.isSearchEmailRunning
? listPresentationEmail[index].findMailboxContain(
controller.mailboxDashBoardController.mapMailboxById)
: controller.currentMailbox,
controller.mailboxDashBoardController.selectedEmail.value?.id == newPresentationEmail.id,
mailboxContain: mailboxContain,
isSearchEmailRunning: controller.searchController.isSearchEmailRunning,
isDrag: true)
).build(),
dragAnchorStrategy: pointerDragAnchorStrategy,
onDragStarted: () {
controller.calculateDragValue(listPresentationEmail[index]);
controller.calculateDragValue(newPresentationEmail);
},
));
}),
@@ -382,6 +375,12 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
);
}
PresentationMailbox? _getMailboxContain(PresentationEmail currentEmail) {
return controller.searchController.isSearchEmailRunning
? currentEmail.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById)
: controller.currentMailbox;
}
Widget _buildFeedBackWidget(BuildContext context) {
return SizedBox(
height: 60,
@@ -509,9 +508,7 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
}
Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) {
final mailboxContain = controller.searchController.isSearchEmailRunning
? email.findMailboxContain(controller.mailboxDashBoardController.mapMailboxById)
: controller.currentMailbox;
final mailboxContain = _getMailboxContain(email);
return (EmailActionCupertinoActionSheetActionBuilder(
const Key('mark_as_spam_or_un_spam_action'),
SvgPicture.asset(
@@ -13,7 +13,7 @@ class EmailTileBuilder with BaseEmailItemTile {
final PresentationEmail _presentationEmail;
final BuildContext _context;
final SelectMode _selectModeAll;
final PresentationMailbox? mailboxCurrent;
final PresentationMailbox? mailboxContain;
final SearchQuery? _searchQuery;
final bool isSearchEmailRunning;
final EdgeInsets? padding;
@@ -31,7 +31,7 @@ class EmailTileBuilder with BaseEmailItemTile {
this._isShowingEmailContent,
{
this.isSearchEmailRunning = false,
this.mailboxCurrent,
this.mailboxContain,
this.padding,
this.paddingDivider,
this.isDrag = false,
@@ -83,7 +83,7 @@ class EmailTileBuilder with BaseEmailItemTile {
fit: BoxFit.fill)),
Expanded(child: buildInformationSender(
_presentationEmail,
mailboxCurrent,
mailboxContain,
isSearchEmailRunning,
_searchQuery)),
if (_presentationEmail.hasAttachment == true)
@@ -11,9 +11,6 @@ import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/presentation/mixin/base_email_item_tile.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/navigation_router.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:url_launcher/link.dart';
class EmailTileBuilder with BaseEmailItemTile {
@@ -21,7 +18,7 @@ class EmailTileBuilder with BaseEmailItemTile {
final PresentationEmail _presentationEmail;
final BuildContext _context;
final SelectMode _selectModeAll;
final PresentationMailbox? mailboxCurrent;
final PresentationMailbox? mailboxContain;
final SearchQuery? _searchQuery;
final bool isSearchEmailRunning;
final EdgeInsets? padding;
@@ -43,7 +40,7 @@ class EmailTileBuilder with BaseEmailItemTile {
this._isShowingEmailContent,
{
this.isSearchEmailRunning = false,
this.mailboxCurrent,
this.mailboxContain,
this.padding,
this.paddingDivider,
this.isDrag = false,
@@ -80,56 +77,43 @@ class EmailTileBuilder with BaseEmailItemTile {
}
Widget _wrapContainerForTile(Widget tile) {
return Container(
margin: _getMarginItem(),
padding: _getPaddingItem(),
decoration: _getDecorationItem(),
alignment: Alignment.center,
child: Link(
uri: _presentationEmail.routeWeb,
builder: (_, __) => tile
)
);
}
EdgeInsets _getMarginItem() {
if (responsiveUtils.isDesktop(_context)) {
return Container(
margin: const EdgeInsets.only(top: 3),
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: (_selectModeAll == SelectMode.ACTIVE &&
_presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent
? BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorItemEmailSelectedDesktop)
: null,
child: Link(
uri: RouteUtils.generateRoutePathBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: _presentationEmail.id,
mailboxId: mailboxCurrent?.id,
dashboardType: isSearchEmailRunning
? DashboardType.search
: DashboardType.normal
)
),
builder: (_, __) => tile
)
);
return const EdgeInsets.only(top: 3);
} else {
return Container(
margin: const EdgeInsets.only(top: 3, left: 16, right: 16),
padding: const EdgeInsets.only(bottom: 8, right: 8, top: 8),
decoration: (_selectModeAll == SelectMode.ACTIVE &&
_presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent
? BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorItemEmailSelectedDesktop)
: BoxDecoration(
borderRadius: BorderRadius.circular(0),
color: Colors.white),
alignment: Alignment.center,
child: Link(
uri: RouteUtils.generateRoutePathBrowser(
AppRoutes.dashboard,
NavigationRouter(
emailId: _presentationEmail.id,
mailboxId: mailboxCurrent?.id,
dashboardType: isSearchEmailRunning
? DashboardType.search
: DashboardType.normal
)
),
builder: (_, __) => tile
));
return const EdgeInsets.only(top: 3, left: 16, right: 16);
}
}
EdgeInsets _getPaddingItem() {
if (responsiveUtils.isDesktop(_context)) {
return const EdgeInsets.symmetric(vertical: 8);
} else {
return const EdgeInsets.only(bottom: 8, right: 8, top: 8);
}
}
BoxDecoration _getDecorationItem() {
if ((_selectModeAll == SelectMode.ACTIVE && _presentationEmail.selectMode == SelectMode.ACTIVE) || isDrag || _isShowingEmailContent) {
return BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorItemEmailSelectedDesktop);
} else {
return BoxDecoration(
borderRadius: BorderRadius.circular(0),
color: Colors.white);
}
}
@@ -168,7 +152,7 @@ class EmailTileBuilder with BaseEmailItemTile {
fit: BoxFit.fill)),
Expanded(child: buildInformationSender(
_presentationEmail,
mailboxCurrent,
mailboxContain,
isSearchEmailRunning,
_searchQuery
)),
@@ -264,7 +248,7 @@ class EmailTileBuilder with BaseEmailItemTile {
fit: BoxFit.fill)),
Expanded(child: buildInformationSender(
_presentationEmail,
mailboxCurrent,
mailboxContain,
isSearchEmailRunning,
_searchQuery
)),
@@ -385,7 +369,7 @@ class EmailTileBuilder with BaseEmailItemTile {
width: 160,
child: buildInformationSender(
_presentationEmail,
mailboxCurrent,
mailboxContain,
isSearchEmailRunning,
_searchQuery
)),
@@ -437,7 +421,7 @@ class EmailTileBuilder with BaseEmailItemTile {
: EmailActionType.markAsRead,
_presentationEmail)),
const SizedBox(width: 5),
if (mailboxCurrent?.isDrafts == false)
if (mailboxContain?.isDrafts == false)
... [
buildIconWeb(
minSize: 18,
@@ -476,7 +460,7 @@ class EmailTileBuilder with BaseEmailItemTile {
: EmailActionType.moveToTrash,
_presentationEmail)),
const SizedBox(width: 5),
if (mailboxCurrent?.isDrafts == false)
if (mailboxContain?.isDrafts == false)
buildIconWebHasPosition(
_context,
icon: SvgPicture.asset(
@@ -501,7 +485,7 @@ class EmailTileBuilder with BaseEmailItemTile {
}
bool get canDeletePermanently {
return mailboxCurrent?.isTrash == true || mailboxCurrent?.isDrafts == true;
return mailboxContain?.isTrash == true || mailboxContain?.isDrafts == true;
}
Widget _buildDateTimeForDesktopScreen() {