TF-4301 Add "Create a new label" button in Label As menus and wire up tagging in email view
This commit is contained in:
@@ -43,7 +43,7 @@ class TMailButtonWidget extends StatelessWidget {
|
|||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final Color? hoverColor;
|
final Color? hoverColor;
|
||||||
final TextOverflow? textOverflow;
|
final TextOverflow? textOverflow;
|
||||||
final Alignment? alignment;
|
final AlignmentGeometry? alignment;
|
||||||
final bool isTextExpanded;
|
final bool isTextExpanded;
|
||||||
|
|
||||||
const TMailButtonWidget({
|
const TMailButtonWidget({
|
||||||
@@ -109,7 +109,7 @@ class TMailButtonWidget extends StatelessWidget {
|
|||||||
List<BoxShadow>? boxShadow,
|
List<BoxShadow>? boxShadow,
|
||||||
EdgeInsetsGeometry? margin,
|
EdgeInsetsGeometry? margin,
|
||||||
Color? hoverColor,
|
Color? hoverColor,
|
||||||
Alignment? alignment,
|
AlignmentGeometry? alignment,
|
||||||
BoxBorder? border,
|
BoxBorder? border,
|
||||||
}) {
|
}) {
|
||||||
return TMailButtonWidget(
|
return TMailButtonWidget(
|
||||||
@@ -165,7 +165,7 @@ class TMailButtonWidget extends StatelessWidget {
|
|||||||
int? maxLines,
|
int? maxLines,
|
||||||
Color? hoverColor,
|
Color? hoverColor,
|
||||||
TextOverflow? textOverflow,
|
TextOverflow? textOverflow,
|
||||||
Alignment? alignment,
|
AlignmentGeometry? alignment,
|
||||||
bool isTextExpanded = false,
|
bool isTextExpanded = false,
|
||||||
}) {
|
}) {
|
||||||
return TMailButtonWidget(
|
return TMailButtonWidget(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class TMailContainerWidget extends StatelessWidget {
|
|||||||
final List<BoxShadow>? boxShadow;
|
final List<BoxShadow>? boxShadow;
|
||||||
final BoxBorder? border;
|
final BoxBorder? border;
|
||||||
final Color? hoverColor;
|
final Color? hoverColor;
|
||||||
final Alignment? alignment;
|
final AlignmentGeometry? alignment;
|
||||||
|
|
||||||
const TMailContainerWidget({
|
const TMailContainerWidget({
|
||||||
super.key,
|
super.key,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class ConfirmDialogButton extends StatelessWidget {
|
|||||||
final double? radius;
|
final double? radius;
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final VoidCallback? onTapAction;
|
final VoidCallback? onTapAction;
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
const ConfirmDialogButton({
|
const ConfirmDialogButton({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -28,6 +29,7 @@ class ConfirmDialogButton extends StatelessWidget {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.radius,
|
this.radius,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.child,
|
||||||
this.onTapAction,
|
this.onTapAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ class ConfirmDialogButton extends StatelessWidget {
|
|||||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 10),
|
padding: padding ?? const EdgeInsets.symmetric(horizontal: 10),
|
||||||
),
|
),
|
||||||
onPressed: onTapAction,
|
onPressed: onTapAction,
|
||||||
child: icon == null
|
child: child ?? (icon == null
|
||||||
? Text(
|
? Text(
|
||||||
label,
|
label,
|
||||||
style: textStyle ?? ThemeUtils.textStyleM3LabelLarge(color: textColor),
|
style: textStyle ?? ThemeUtils.textStyleM3LabelLarge(color: textColor),
|
||||||
@@ -79,7 +81,7 @@ class ConfirmDialogButton extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||||
|
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ModalListActionButtonWidget extends StatelessWidget {
|
class ModalListActionButtonWidget extends StatelessWidget {
|
||||||
@@ -11,6 +12,7 @@ class ModalListActionButtonWidget extends StatelessWidget {
|
|||||||
final VoidCallback onPositiveAction;
|
final VoidCallback onPositiveAction;
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final bool isPositiveActionEnabled;
|
final bool isPositiveActionEnabled;
|
||||||
|
final bool isProgressing;
|
||||||
|
|
||||||
const ModalListActionButtonWidget({
|
const ModalListActionButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -19,6 +21,7 @@ class ModalListActionButtonWidget extends StatelessWidget {
|
|||||||
required this.onPositiveAction,
|
required this.onPositiveAction,
|
||||||
required this.onNegativeAction,
|
required this.onNegativeAction,
|
||||||
this.isPositiveActionEnabled = true,
|
this.isPositiveActionEnabled = true,
|
||||||
|
this.isProgressing = false,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.positiveKey,
|
this.positiveKey,
|
||||||
this.negativeKey,
|
this.negativeKey,
|
||||||
@@ -52,12 +55,25 @@ class ModalListActionButtonWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget progressingButton = const SizedBox(
|
||||||
|
height: 48,
|
||||||
|
width: 153,
|
||||||
|
child: ConfirmDialogButton(
|
||||||
|
label: '',
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
backgroundColor: AppColor.primaryMain,
|
||||||
|
child: CupertinoLoadingWidget(color: Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Widget bodyWidget = Row(
|
Widget bodyWidget = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Flexible(child: negativeButton),
|
Flexible(child: negativeButton),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Flexible(child: positiveButton),
|
Flexible(
|
||||||
|
child: isProgressing ? progressingButton : positiveButton,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class CupertinoLoadingWidget extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final bool isCenter;
|
final bool isCenter;
|
||||||
final String? semanticLabel;
|
final String? semanticLabel;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
const CupertinoLoadingWidget({
|
const CupertinoLoadingWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -15,6 +16,7 @@ class CupertinoLoadingWidget extends StatelessWidget {
|
|||||||
this.padding,
|
this.padding,
|
||||||
this.isCenter = true,
|
this.isCenter = true,
|
||||||
this.semanticLabel,
|
this.semanticLabel,
|
||||||
|
this.color,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -24,8 +26,8 @@ class CupertinoLoadingWidget extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: size ?? CupertinoLoadingWidgetStyles.size,
|
width: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
height: size ?? CupertinoLoadingWidgetStyles.size,
|
height: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
child: const CupertinoActivityIndicator(
|
child: CupertinoActivityIndicator(
|
||||||
color: CupertinoLoadingWidgetStyles.progressColor
|
color: color ?? CupertinoLoadingWidgetStyles.progressColor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -34,8 +36,8 @@ class CupertinoLoadingWidget extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: size ?? CupertinoLoadingWidgetStyles.size,
|
width: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
height: size ?? CupertinoLoadingWidgetStyles.size,
|
height: size ?? CupertinoLoadingWidgetStyles.size,
|
||||||
child: const CupertinoActivityIndicator(
|
child: CupertinoActivityIndicator(
|
||||||
color: CupertinoLoadingWidgetStyles.progressColor
|
color: color ?? CupertinoLoadingWidgetStyles.progressColor
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|||||||
|
|
||||||
import '../../base/base_test_scenario.dart';
|
import '../../base/base_test_scenario.dart';
|
||||||
import '../../mixin/provisioning_label_scenario_mixin.dart';
|
import '../../mixin/provisioning_label_scenario_mixin.dart';
|
||||||
import '../../robots/label_robot.dart';
|
import '../../robots/labels/label_robot.dart';
|
||||||
import '../../robots/thread_robot.dart';
|
import '../../robots/thread_robot.dart';
|
||||||
|
|
||||||
class DisplayFolderInfoWhenOpenMailFromTagScenario extends BaseTestScenario
|
class DisplayFolderInfoWhenOpenMailFromTagScenario extends BaseTestScenario
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class ComposerView extends GetWidget<ComposerController> {
|
|||||||
MessageDialogActionManager().isDialogOpened ||
|
MessageDialogActionManager().isDialogOpened ||
|
||||||
EmailActionReactor.isDialogOpened ||
|
EmailActionReactor.isDialogOpened ||
|
||||||
ColorDialogPicker().isOpened.isTrue ||
|
ColorDialogPicker().isOpened.isTrue ||
|
||||||
dialogRouter.isRuleFilterDialogOpened.isTrue ||
|
|
||||||
dialogRouter.isDialogOpened;
|
dialogRouter.isDialogOpened;
|
||||||
|
|
||||||
if (isOverlayEnabled) {
|
if (isOverlayEnabled) {
|
||||||
|
|||||||
@@ -952,9 +952,15 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
pressEmailAction(actionType, presentationEmail);
|
pressEmailAction(actionType, presentationEmail);
|
||||||
break;
|
break;
|
||||||
case EmailActionType.labelAs:
|
case EmailActionType.labelAs:
|
||||||
if (!isLabelAvailable) return;
|
mailboxDashBoardController.openAddLabelToEmailDialogModal(
|
||||||
mailboxDashBoardController
|
email: presentationEmail,
|
||||||
.openAddLabelToEmailDialogModal(presentationEmail);
|
onCreateANewLabelAction: () {
|
||||||
|
mailboxDashBoardController.labelController.onCreateALabelAction(
|
||||||
|
accountId: accountId,
|
||||||
|
shouldPop: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -110,9 +110,14 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
||||||
onSelectLabelAction: (label, isSelected) =>
|
onSelectLabelAction: (label, isSelected) =>
|
||||||
controller.onToggleLabelAction(
|
controller.onToggleLabelAction(
|
||||||
|
emailId: presentationEmail.id,
|
||||||
|
label: label,
|
||||||
|
isSelected: isSelected,
|
||||||
|
),
|
||||||
|
onCreateANewLabelAction: () =>
|
||||||
|
controller.createNewLabelToEmail(
|
||||||
|
context,
|
||||||
presentationEmail.id,
|
presentationEmail.id,
|
||||||
label,
|
|
||||||
isSelected,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -276,9 +281,9 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
isMobileResponsive: isMobileResponsive,
|
isMobileResponsive: isMobileResponsive,
|
||||||
labels: emailLabels,
|
labels: emailLabels,
|
||||||
onDeleteLabelAction: (label) => controller.onToggleLabelAction(
|
onDeleteLabelAction: (label) => controller.onToggleLabelAction(
|
||||||
presentationEmail.id,
|
emailId: presentationEmail.id,
|
||||||
label,
|
label: label,
|
||||||
false,
|
isSelected: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -321,9 +326,14 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
||||||
onSelectLabelAction: (label, isSelected) =>
|
onSelectLabelAction: (label, isSelected) =>
|
||||||
controller.onToggleLabelAction(
|
controller.onToggleLabelAction(
|
||||||
|
emailId: presentationEmail.id,
|
||||||
|
label: label,
|
||||||
|
isSelected: isSelected,
|
||||||
|
),
|
||||||
|
onCreateANewLabelAction: () =>
|
||||||
|
controller.createNewLabelToEmail(
|
||||||
|
context,
|
||||||
presentationEmail.id,
|
presentationEmail.id,
|
||||||
label,
|
|
||||||
isSelected,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onToggleThreadDetailCollapseExpand: onToggleThreadDetailCollapseExpand,
|
onToggleThreadDetailCollapseExpand: onToggleThreadDetailCollapseExpand,
|
||||||
@@ -601,7 +611,6 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
MessageDialogActionManager().isDialogOpened ||
|
MessageDialogActionManager().isDialogOpened ||
|
||||||
EmailActionReactor.isDialogOpened ||
|
EmailActionReactor.isDialogOpened ||
|
||||||
ColorDialogPicker().isOpened.isTrue ||
|
ColorDialogPicker().isOpened.isTrue ||
|
||||||
dialogRouter.isRuleFilterDialogOpened.isTrue ||
|
|
||||||
dialogRouter.isDialogOpened;
|
dialogRouter.isDialogOpened;
|
||||||
|
|
||||||
if (isOverlayEnabled) {
|
if (isOverlayEnabled) {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
|
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_loaded_extension.dart';
|
import 'package:tmail_ui_user/features/email/presentation/extensions/email_loaded_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/labels/handle_logic_label_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/labels/handle_logic_label_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/data/extensions/map_keywords_extension.dart';
|
import 'package:tmail_ui_user/features/thread/data/extensions/map_keywords_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/extensions/presentation_email_map_extension.dart';
|
import 'package:tmail_ui_user/features/thread/domain/extensions/presentation_email_map_extension.dart';
|
||||||
@@ -15,7 +18,11 @@ extension HandleLabelForEmailExtension on SingleEmailController {
|
|||||||
return mailboxDashBoardController.isLabelAvailable;
|
return mailboxDashBoardController.isLabelAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onToggleLabelAction(EmailId? emailId, Label label, bool isSelected) {
|
void onToggleLabelAction({
|
||||||
|
required EmailId? emailId,
|
||||||
|
required Label label,
|
||||||
|
required bool isSelected,
|
||||||
|
}) {
|
||||||
if (emailId == null) {
|
if (emailId == null) {
|
||||||
logWarning('HandleLabelForEmailExtension::onToggleLabelAction: Email id is null');
|
logWarning('HandleLabelForEmailExtension::onToggleLabelAction: Email id is null');
|
||||||
return;
|
return;
|
||||||
@@ -125,4 +132,22 @@ extension HandleLabelForEmailExtension on SingleEmailController {
|
|||||||
remove: remove,
|
remove: remove,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createNewLabelToEmail(BuildContext context, EmailId? emailId) {
|
||||||
|
if (emailId == null) {
|
||||||
|
logWarning('HandleLabelForEmailExtension::createNewLabelToEmail: Email id is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxDashBoardController.labelController.handleLabelActionType(
|
||||||
|
actionType: LabelActionType.create,
|
||||||
|
accountId: accountId,
|
||||||
|
onLabelActionCallback: (label) =>
|
||||||
|
onToggleLabelAction(
|
||||||
|
emailId: emailId,
|
||||||
|
label: label,
|
||||||
|
isSelected: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -54,6 +54,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_email_rule_filter_request.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_email_rule_filter_request.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart';
|
||||||
@@ -486,6 +487,7 @@ class EmailActionReactor with LabelSubMenuMixin {
|
|||||||
required bool isLabelAvailable,
|
required bool isLabelAvailable,
|
||||||
required OpenBottomSheetContextMenuAction openBottomSheetContextMenu,
|
required OpenBottomSheetContextMenuAction openBottomSheetContextMenu,
|
||||||
required OpenPopupMenuActionGroup openPopupMenu,
|
required OpenPopupMenuActionGroup openPopupMenu,
|
||||||
|
required OnCreateANewLabelAction onCreateANewLabelAction,
|
||||||
List<Label>? labels,
|
List<Label>? labels,
|
||||||
OnSelectLabelAction? onSelectLabelAction,
|
OnSelectLabelAction? onSelectLabelAction,
|
||||||
}) {
|
}) {
|
||||||
@@ -508,8 +510,7 @@ class EmailActionReactor with LabelSubMenuMixin {
|
|||||||
EmailActionType.printAll,
|
EmailActionType.printAll,
|
||||||
if (additionalActions.contains(EmailActionType.moveToMailbox))
|
if (additionalActions.contains(EmailActionType.moveToMailbox))
|
||||||
EmailActionType.moveToMailbox,
|
EmailActionType.moveToMailbox,
|
||||||
if (isLabelAvailable && labels?.isNotEmpty == true)
|
if (isLabelAvailable) EmailActionType.labelAs,
|
||||||
EmailActionType.labelAs,
|
|
||||||
if (additionalActions.contains(EmailActionType.markAsStarred) &&
|
if (additionalActions.contains(EmailActionType.markAsStarred) &&
|
||||||
additionalActions.contains(EmailActionType.unMarkAsStarred))
|
additionalActions.contains(EmailActionType.unMarkAsStarred))
|
||||||
presentationEmail.hasStarred
|
presentationEmail.hasStarred
|
||||||
@@ -580,6 +581,11 @@ class EmailActionReactor with LabelSubMenuMixin {
|
|||||||
submenuController.hide();
|
submenuController.hide();
|
||||||
popBack();
|
popBack();
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () {
|
||||||
|
submenuController.hide();
|
||||||
|
popBack();
|
||||||
|
onCreateANewLabelAction();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|||||||
+33
-66
@@ -1,6 +1,4 @@
|
|||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:labels/extensions/label_extension.dart';
|
import 'package:labels/extensions/label_extension.dart';
|
||||||
@@ -8,112 +6,79 @@ import 'package:labels/extensions/list_label_extension.dart';
|
|||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
|
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/exceptions/label_exceptions.dart';
|
|
||||||
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart';
|
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
extension HandleLabelActionTypeExtension on LabelController {
|
extension HandleLabelActionTypeExtension on LabelController {
|
||||||
void handleLabelActionType({
|
void handleLabelActionType({
|
||||||
required BuildContext context,
|
|
||||||
required LabelActionType actionType,
|
required LabelActionType actionType,
|
||||||
required AccountId? accountId,
|
required AccountId? accountId,
|
||||||
Label? label,
|
Label? label,
|
||||||
|
OnLabelActionCallback? onLabelActionCallback,
|
||||||
}) {
|
}) {
|
||||||
switch (actionType) {
|
switch (actionType) {
|
||||||
case LabelActionType.create:
|
case LabelActionType.create:
|
||||||
openCreateNewLabelModal(accountId);
|
onCreateALabelAction(
|
||||||
|
accountId: accountId,
|
||||||
|
onLabelActionCallback: onLabelActionCallback,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case LabelActionType.edit:
|
case LabelActionType.edit:
|
||||||
if (label == null) return;
|
if (label == null) return;
|
||||||
openEditLabelModal(accountId: accountId, label: label);
|
onEditLabelAction(accountId: accountId, label: label);
|
||||||
break;
|
break;
|
||||||
case LabelActionType.delete:
|
case LabelActionType.delete:
|
||||||
if (label == null) return;
|
if (label == null) return;
|
||||||
_openDeleteLabelModal(
|
_openDeleteLabelModal(accountId: accountId, label: label);
|
||||||
context: context,
|
|
||||||
accountId: accountId,
|
|
||||||
label: label,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> openEditLabelModal({
|
Future<void> onEditLabelAction({
|
||||||
required AccountId? accountId,
|
required AccountId? accountId,
|
||||||
required Label label,
|
required Label label,
|
||||||
}) async {
|
}) async {
|
||||||
if (accountId == null) {
|
if (createNewLabelInteractor == null || editLabelInteractor == null) {
|
||||||
consumeState(
|
_handleEditLabelFailure(EditLabelFailure(const InteractorNotInitialized()));
|
||||||
Stream.value(Left(EditLabelFailure(NotFoundAccountIdException()))),
|
return;
|
||||||
);
|
}
|
||||||
|
final verifyNameInteractor = getBinding<VerifyNameInteractor>();
|
||||||
|
if (verifyNameInteractor == null) {
|
||||||
|
_handleEditLabelFailure(EditLabelFailure(const InteractorNotInitialized()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DialogRouter().openDialogModal(
|
|
||||||
child: CreateNewLabelModal(
|
|
||||||
key: const Key('edit_label_modal'),
|
|
||||||
labels: labels,
|
|
||||||
selectedLabel: label,
|
|
||||||
actionType: LabelActionType.edit,
|
|
||||||
onLabelActionCallback: (newLabel) =>
|
|
||||||
editLabel(
|
|
||||||
accountId: accountId,
|
|
||||||
selectedLabel: label,
|
|
||||||
newLabel: newLabel,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
dialogLabel: 'edit-label-modal',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void editLabel({
|
|
||||||
required AccountId? accountId,
|
|
||||||
required Label selectedLabel,
|
|
||||||
required Label newLabel,
|
|
||||||
}) {
|
|
||||||
log('LabelController::editLabel:selectedLabel: $selectedLabel, newLabel: $newLabel');
|
|
||||||
if (accountId == null) {
|
if (accountId == null) {
|
||||||
consumeState(
|
_handleEditLabelFailure(EditLabelFailure(NotFoundAccountIdException()));
|
||||||
Stream.value(Left(EditLabelFailure(NotFoundAccountIdException()))),
|
return;
|
||||||
);
|
}
|
||||||
} else if (editLabelInteractor == null) {
|
|
||||||
consumeState(
|
|
||||||
Stream.value(Left(EditLabelFailure(const InteractorNotInitialized()))),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final labelId = selectedLabel.id;
|
|
||||||
|
|
||||||
if (labelId == null) {
|
final resultState = await openEditLabelModal(
|
||||||
consumeState(
|
selectedLabel: label,
|
||||||
Stream.value(Left(EditLabelFailure(const LabelIdIsNull()))),
|
accountId: accountId,
|
||||||
);
|
verifyNameInteractor: verifyNameInteractor,
|
||||||
return;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
final labelRequest = EditLabelRequest(
|
if (resultState is EditLabelSuccess) {
|
||||||
labelId: labelId,
|
_handleEditLabelSuccess(resultState);
|
||||||
labelKeyword: selectedLabel.keyword,
|
} else if (resultState is EditLabelFailure) {
|
||||||
newLabel: newLabel,
|
_handleEditLabelFailure(resultState);
|
||||||
);
|
|
||||||
|
|
||||||
consumeState(editLabelInteractor!.execute(accountId, labelRequest));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleEditLabelSuccess(EditLabelSuccess success) {
|
void _handleEditLabelSuccess(EditLabelSuccess success) {
|
||||||
toastManager.showMessageSuccess(success);
|
toastManager.showMessageSuccess(success);
|
||||||
syncListLabels(success.newLabel);
|
syncListLabels(success.newLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleEditLabelFailure(EditLabelFailure failure) {
|
void _handleEditLabelFailure(EditLabelFailure failure) {
|
||||||
toastManager.showMessageFailure(failure);
|
toastManager.showMessageFailure(failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,10 +89,12 @@ extension HandleLabelActionTypeExtension on LabelController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openDeleteLabelModal({
|
Future<void> _openDeleteLabelModal({
|
||||||
required BuildContext context,
|
|
||||||
required AccountId? accountId,
|
required AccountId? accountId,
|
||||||
required Label label,
|
required Label label,
|
||||||
}) async {
|
}) async {
|
||||||
|
final context = currentContext;
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
final appLocalizations = AppLocalizations.of(context);
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
|
|
||||||
MessageDialogActionManager().showConfirmDialogAction(
|
MessageDialogActionManager().showConfirmDialogAction(
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
import 'package:core/utils/app_logger.dart';
|
|
||||||
import 'package:dartz/dartz.dart' hide State;
|
|
||||||
import 'package:flutter/material.dart' hide State;
|
import 'package:flutter/material.dart' hide State;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
@@ -14,7 +12,6 @@ import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.d
|
|||||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart';
|
import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart';
|
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/get_all_label_state.dart';
|
import 'package:tmail_ui_user/features/labels/domain/state/get_all_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_label_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_label_interactor.dart';
|
||||||
@@ -25,17 +22,21 @@ import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_lab
|
|||||||
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_websocket_extension.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_websocket_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_context_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_context_menu_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_modal_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart';
|
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart';
|
||||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
class LabelController extends BaseController with LabelContextMenuMixin {
|
class LabelController extends BaseController
|
||||||
final labels = <Label>[].obs;
|
with LabelContextMenuMixin,
|
||||||
|
LabelModalMixin {
|
||||||
|
@override
|
||||||
|
final RxList<Label> labels = <Label>[].obs;
|
||||||
final labelListExpandMode = Rx(ExpandMode.EXPAND);
|
final labelListExpandMode = Rx(ExpandMode.EXPAND);
|
||||||
final isLabelSettingEnabled = RxBool(false);
|
final isLabelSettingEnabled = RxBool(false);
|
||||||
final isLabelsLoaded = RxBool(false);
|
final isLabelsLoaded = RxBool(false);
|
||||||
@@ -80,8 +81,6 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
if (_getLabelSettingStateInteractor != null) {
|
if (_getLabelSettingStateInteractor != null) {
|
||||||
consumeState(_getLabelSettingStateInteractor!.execute(accountId));
|
consumeState(_getLabelSettingStateInteractor!.execute(accountId));
|
||||||
} else {
|
} else {
|
||||||
isLabelSettingEnabled.value = false;
|
|
||||||
isLabelSettingEnabled.refresh();
|
|
||||||
_clearLabelData();
|
_clearLabelData();
|
||||||
setLabelLoaded();
|
setLabelLoaded();
|
||||||
}
|
}
|
||||||
@@ -92,6 +91,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _clearLabelData() {
|
void _clearLabelData() {
|
||||||
|
isLabelSettingEnabled.value = false;
|
||||||
|
isLabelSettingEnabled.refresh();
|
||||||
labels.clear();
|
labels.clear();
|
||||||
isLabelsLoaded.value = false;
|
isLabelsLoaded.value = false;
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,10 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
_getLabelChangesInteractor = getBinding<GetLabelChangesInteractor>();
|
_getLabelChangesInteractor = getBinding<GetLabelChangesInteractor>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
CreateNewLabelInteractor? get createNewLabelInteractor => _createNewLabelInteractor;
|
||||||
|
|
||||||
|
@override
|
||||||
EditLabelInteractor? get editLabelInteractor => _editLabelInteractor;
|
EditLabelInteractor? get editLabelInteractor => _editLabelInteractor;
|
||||||
|
|
||||||
DeleteALabelInteractor? get deleteALabelInteractor => _deleteALabelInteractor;
|
DeleteALabelInteractor? get deleteALabelInteractor => _deleteALabelInteractor;
|
||||||
@@ -146,42 +151,79 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
: ExpandMode.COLLAPSE;
|
: ExpandMode.COLLAPSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> openCreateNewLabelModal(AccountId? accountId) async {
|
Future<void> onCreateALabelAction({
|
||||||
if (accountId == null) {
|
required AccountId? accountId,
|
||||||
consumeState(
|
OnLabelActionCallback? onLabelActionCallback,
|
||||||
Stream.value(Left(CreateNewLabelFailure(NotFoundAccountIdException()))),
|
bool shouldPop = false,
|
||||||
|
}) async {
|
||||||
|
if (_createNewLabelInteractor == null || _editLabelInteractor == null) {
|
||||||
|
_handleCreateNewLabelFailure(
|
||||||
|
failure: CreateNewLabelFailure(const InteractorNotInitialized()),
|
||||||
|
shouldPop: shouldPop,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final verifyNameInteractor = getBinding<VerifyNameInteractor>();
|
||||||
|
if (verifyNameInteractor == null) {
|
||||||
|
_handleCreateNewLabelFailure(
|
||||||
|
failure: CreateNewLabelFailure(const InteractorNotInitialized()),
|
||||||
|
shouldPop: shouldPop,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DialogRouter().openDialogModal(
|
if (accountId == null) {
|
||||||
child: CreateNewLabelModal(
|
_handleCreateNewLabelFailure(
|
||||||
key: const Key('create_new_label_modal'),
|
failure: CreateNewLabelFailure(NotFoundAccountIdException()),
|
||||||
labels: labels,
|
shouldPop: shouldPop,
|
||||||
onLabelActionCallback: (label) => _createNewLabel(accountId, label),
|
);
|
||||||
),
|
return;
|
||||||
dialogLabel: 'create-new-label-modal',
|
}
|
||||||
);
|
|
||||||
}
|
final resultState = await openCreateNewLabelModal(
|
||||||
|
accountId: accountId,
|
||||||
void _createNewLabel(AccountId accountId, Label label) {
|
verifyNameInteractor: verifyNameInteractor,
|
||||||
log('LabelController::_createNewLabel:Label: $label');
|
);
|
||||||
if (_createNewLabelInteractor == null) {
|
|
||||||
consumeState(
|
if (resultState is CreateNewLabelSuccess) {
|
||||||
Stream.value(Left(CreateNewLabelFailure(const InteractorNotInitialized()))),
|
_handleCreateNewLabelSuccess(
|
||||||
|
success: resultState,
|
||||||
|
onLabelActionCallback: onLabelActionCallback,
|
||||||
|
shouldPop: shouldPop,
|
||||||
|
);
|
||||||
|
} else if (resultState is CreateNewLabelFailure) {
|
||||||
|
_handleCreateNewLabelFailure(
|
||||||
|
failure: resultState,
|
||||||
|
shouldPop: shouldPop,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
consumeState(_createNewLabelInteractor!.execute(accountId, label));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleCreateNewLabelSuccess(CreateNewLabelSuccess success) {
|
void _handleCreateNewLabelSuccess({
|
||||||
toastManager.showMessageSuccess(success);
|
required CreateNewLabelSuccess success,
|
||||||
|
OnLabelActionCallback? onLabelActionCallback,
|
||||||
|
bool shouldPop = false,
|
||||||
|
}) {
|
||||||
|
if (onLabelActionCallback == null && !shouldPop) {
|
||||||
|
toastManager.showMessageSuccess(success);
|
||||||
|
}
|
||||||
_addLabelToList(success.newLabel);
|
_addLabelToList(success.newLabel);
|
||||||
|
if (onLabelActionCallback != null) {
|
||||||
|
onLabelActionCallback(success.newLabel);
|
||||||
|
}
|
||||||
|
if (shouldPop) {
|
||||||
|
popBack(result: success.newLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleCreateNewLabelFailure(CreateNewLabelFailure failure) {
|
void _handleCreateNewLabelFailure({
|
||||||
|
required CreateNewLabelFailure failure,
|
||||||
|
bool shouldPop = false,
|
||||||
|
}) {
|
||||||
toastManager.showMessageFailure(failure);
|
toastManager.showMessageFailure(failure);
|
||||||
|
if (shouldPop) {
|
||||||
|
popBack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addLabelToList(Label newLabel) {
|
void _addLabelToList(Label newLabel) {
|
||||||
@@ -190,6 +232,10 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleGetLabelSettingStateSuccess(bool isEnabled, AccountId accountId) {
|
void _handleGetLabelSettingStateSuccess(bool isEnabled, AccountId accountId) {
|
||||||
|
updateLabelSettingEnabled(isEnabled, accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateLabelSettingEnabled(bool isEnabled, AccountId accountId) {
|
||||||
isLabelSettingEnabled.value = isEnabled;
|
isLabelSettingEnabled.value = isEnabled;
|
||||||
isLabelSettingEnabled.refresh();
|
isLabelSettingEnabled.refresh();
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
@@ -207,12 +253,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
labels.value = success.labels..sortByAlphabetically();
|
labels.value = success.labels..sortByAlphabetically();
|
||||||
setCurrentLabelState(success.newState);
|
setCurrentLabelState(success.newState);
|
||||||
setLabelLoaded();
|
setLabelLoaded();
|
||||||
} else if (success is CreateNewLabelSuccess) {
|
|
||||||
_handleCreateNewLabelSuccess(success);
|
|
||||||
} else if (success is GetLabelSettingStateSuccess) {
|
} else if (success is GetLabelSettingStateSuccess) {
|
||||||
_handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId);
|
_handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId);
|
||||||
} else if (success is EditLabelSuccess) {
|
|
||||||
handleEditLabelSuccess(success);
|
|
||||||
} else if (success is DeleteALabelSuccess) {
|
} else if (success is DeleteALabelSuccess) {
|
||||||
handleDeleteLabelSuccess(success);
|
handleDeleteLabelSuccess(success);
|
||||||
} else {
|
} else {
|
||||||
@@ -225,15 +267,9 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
if (failure is GetAllLabelFailure) {
|
if (failure is GetAllLabelFailure) {
|
||||||
labels.value = [];
|
labels.value = [];
|
||||||
setLabelLoaded();
|
setLabelLoaded();
|
||||||
} else if (failure is CreateNewLabelFailure) {
|
|
||||||
_handleCreateNewLabelFailure(failure);
|
|
||||||
} else if (failure is GetLabelSettingStateFailure) {
|
} else if (failure is GetLabelSettingStateFailure) {
|
||||||
isLabelSettingEnabled.value = false;
|
|
||||||
isLabelSettingEnabled.refresh();
|
|
||||||
_clearLabelData();
|
_clearLabelData();
|
||||||
setLabelLoaded();
|
setLabelLoaded();
|
||||||
} else if (failure is EditLabelFailure) {
|
|
||||||
handleEditLabelFailure(failure);
|
|
||||||
} else if (failure is DeleteALabelFailure) {
|
} else if (failure is DeleteALabelFailure) {
|
||||||
handleDeleteLabelFailure(failure);
|
handleDeleteLabelFailure(failure);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
@@ -18,6 +19,7 @@ import 'package:tmail_ui_user/features/email/presentation/extensions/presentatio
|
|||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/exceptions/label_exceptions.dart';
|
import 'package:tmail_ui_user/features/labels/domain/exceptions/label_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/add_label_to_email_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/add_label_to_email_modal.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/domain/exceptions/thread_exceptions.dart';
|
import 'package:tmail_ui_user/features/thread/domain/exceptions/thread_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
|
||||||
@@ -180,7 +182,10 @@ mixin AddLabelToEmailMixin on EmitStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> openAddLabelToEmailDialogModal(PresentationEmail email) async {
|
Future<void> openAddLabelToEmailDialogModal({
|
||||||
|
required PresentationEmail email,
|
||||||
|
required OnCreateANewLabelAction onCreateANewLabelAction,
|
||||||
|
}) async {
|
||||||
final emailId = email.id;
|
final emailId = email.id;
|
||||||
if (emailId == null) {
|
if (emailId == null) {
|
||||||
logWarning(
|
logWarning(
|
||||||
@@ -192,8 +197,9 @@ mixin AddLabelToEmailMixin on EmitStateMixin {
|
|||||||
final labels = currentLabelList;
|
final labels = currentLabelList;
|
||||||
final emailLabels = email.getLabelList(labels);
|
final emailLabels = email.getLabelList(labels);
|
||||||
|
|
||||||
await DialogRouter().openDialogModal(
|
final newLabel = await DialogRouter().openDialogModal(
|
||||||
child: AddLabelToEmailModal(
|
child: AddLabelToEmailModal(
|
||||||
|
key: const Key('add_label_to_email_modal'),
|
||||||
labels: labels,
|
labels: labels,
|
||||||
emailLabels: emailLabels,
|
emailLabels: emailLabels,
|
||||||
emailIds: [emailId],
|
emailIds: [emailId],
|
||||||
@@ -202,9 +208,14 @@ mixin AddLabelToEmailMixin on EmitStateMixin {
|
|||||||
toggleLabelToEmail(emailIds.first, label, isSelected);
|
toggleLabelToEmail(emailIds.first, label, isSelected);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: onCreateANewLabelAction,
|
||||||
),
|
),
|
||||||
dialogLabel: 'add-label-to-email-modal',
|
dialogLabel: 'add-label-to-email-modal',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (newLabel is Label) {
|
||||||
|
toggleLabelToEmail(emailId, newLabel, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void subscribeLabelViewStateSuccess(Success success) {
|
void subscribeLabelViewStateSuccess(Success success) {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
||||||
|
|
||||||
|
mixin LabelModalMixin {
|
||||||
|
List<Label> get labels;
|
||||||
|
ImagePaths get imagePaths;
|
||||||
|
CreateNewLabelInteractor? get createNewLabelInteractor;
|
||||||
|
EditLabelInteractor? get editLabelInteractor;
|
||||||
|
|
||||||
|
Future<dynamic> openCreateNewLabelModal({
|
||||||
|
required AccountId accountId,
|
||||||
|
required VerifyNameInteractor verifyNameInteractor,
|
||||||
|
}) async {
|
||||||
|
return DialogRouter().openDialogModal(
|
||||||
|
child: CreateNewLabelModal(
|
||||||
|
key: const Key('create_label_modal'),
|
||||||
|
labels: labels,
|
||||||
|
accountId: accountId,
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
verifyNameInteractor: verifyNameInteractor,
|
||||||
|
createNewLabelInteractor: createNewLabelInteractor!,
|
||||||
|
editLabelInteractor: editLabelInteractor!,
|
||||||
|
),
|
||||||
|
dialogLabel: 'create-new-label-modal',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> openEditLabelModal({
|
||||||
|
required Label selectedLabel,
|
||||||
|
required AccountId accountId,
|
||||||
|
required VerifyNameInteractor verifyNameInteractor,
|
||||||
|
}) async {
|
||||||
|
return DialogRouter().openDialogModal(
|
||||||
|
child: CreateNewLabelModal(
|
||||||
|
key: const Key('edit_label_modal'),
|
||||||
|
labels: labels,
|
||||||
|
accountId: accountId,
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
selectedLabel: selectedLabel,
|
||||||
|
actionType: LabelActionType.edit,
|
||||||
|
verifyNameInteractor: verifyNameInteractor,
|
||||||
|
createNewLabelInteractor: createNewLabelInteractor!,
|
||||||
|
editLabelInteractor: editLabelInteractor!,
|
||||||
|
),
|
||||||
|
dialogLabel: 'edit-label-modal',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,8 +15,9 @@ mixin LabelSubMenuMixin {
|
|||||||
required PresentationEmail presentationEmail,
|
required PresentationEmail presentationEmail,
|
||||||
required List<Label>? labels,
|
required List<Label>? labels,
|
||||||
required OnSelectLabelAction onSelectLabelAction,
|
required OnSelectLabelAction onSelectLabelAction,
|
||||||
|
required OnCreateANewLabelAction onCreateANewLabelAction,
|
||||||
}) {
|
}) {
|
||||||
if (actionType == EmailActionType.labelAs && labels?.isNotEmpty == true) {
|
if (actionType == EmailActionType.labelAs) {
|
||||||
final listLabels = labels ?? [];
|
final listLabels = labels ?? [];
|
||||||
final emailLabels = presentationEmail.getLabelList(listLabels);
|
final emailLabels = presentationEmail.getLabelList(listLabels);
|
||||||
return LabelListContextMenu(
|
return LabelListContextMenu(
|
||||||
@@ -24,6 +25,7 @@ mixin LabelSubMenuMixin {
|
|||||||
emailLabels: emailLabels,
|
emailLabels: emailLabels,
|
||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
onSelectLabelAction: onSelectLabelAction,
|
onSelectLabelAction: onSelectLabelAction,
|
||||||
|
onCreateANewLabelAction: onCreateANewLabelAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
import 'package:core/presentation/views/button/default_close_button_widget.dart';
|
import 'package:core/presentation/views/button/default_close_button_widget.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
@@ -23,6 +26,7 @@ class AddLabelToEmailModal extends StatefulWidget {
|
|||||||
final List<Label> emailLabels;
|
final List<Label> emailLabels;
|
||||||
final List<EmailId> emailIds;
|
final List<EmailId> emailIds;
|
||||||
final OnAddLabelToEmailsCallback onAddLabelToEmailsCallback;
|
final OnAddLabelToEmailsCallback onAddLabelToEmailsCallback;
|
||||||
|
final OnCreateANewLabelAction onCreateANewLabelAction;
|
||||||
|
|
||||||
const AddLabelToEmailModal({
|
const AddLabelToEmailModal({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -30,6 +34,7 @@ class AddLabelToEmailModal extends StatefulWidget {
|
|||||||
required this.emailLabels,
|
required this.emailLabels,
|
||||||
required this.emailIds,
|
required this.emailIds,
|
||||||
required this.onAddLabelToEmailsCallback,
|
required this.onAddLabelToEmailsCallback,
|
||||||
|
required this.onCreateANewLabelAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -95,29 +100,46 @@ class _AddLabelToEmailModalState extends State<AddLabelToEmailModal> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
if (widget.labels.isNotEmpty)
|
||||||
child: Container(
|
Flexible(
|
||||||
padding: const EdgeInsetsDirectional.only(
|
child: Container(
|
||||||
start: 22,
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
end: 22,
|
horizontal: 22,
|
||||||
bottom: 16,
|
),
|
||||||
),
|
child: ListView.builder(
|
||||||
child: ListView.builder(
|
shrinkWrap: true,
|
||||||
shrinkWrap: true,
|
padding: EdgeInsets.zero,
|
||||||
padding: EdgeInsets.zero,
|
itemCount: widget.labels.length,
|
||||||
itemCount: widget.labels.length,
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
final label = widget.labels[index];
|
||||||
final label = widget.labels[index];
|
final isSelected = widget.emailLabels.contains(label);
|
||||||
final isSelected = widget.emailLabels.contains(label);
|
return LabelItemContextMenu(
|
||||||
return LabelItemContextMenu(
|
label: label,
|
||||||
label: label,
|
imagePaths: _imagePaths,
|
||||||
imagePaths: _imagePaths,
|
isSelected: isSelected,
|
||||||
isSelected: isSelected,
|
onSelectLabelAction: _onSelectLabel,
|
||||||
onSelectLabelAction: _onSelectLabel,
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
TMailButtonWidget(
|
||||||
|
text: appLocalizations.createANewLabel,
|
||||||
|
icon: _imagePaths.icAddNewFolder,
|
||||||
|
iconColor: AppColor.primaryMain,
|
||||||
|
iconSize: 18,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
height: 48,
|
||||||
|
flexibleText: true,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
margin: const EdgeInsetsDirectional.only(
|
||||||
|
bottom: 16,
|
||||||
|
start: 22,
|
||||||
|
),
|
||||||
|
textStyle: ThemeUtils.textStyleM3LabelLarge(
|
||||||
|
color: AppColor.primaryMain,
|
||||||
|
),
|
||||||
|
onTapActionCallback: widget.onCreateANewLabelAction,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/extensions/hex_color_extension.dart';
|
import 'package:core/presentation/extensions/hex_color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/presentation/utils/theme_utils.dart';
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
import 'package:core/presentation/views/button/default_close_button_widget.dart';
|
import 'package:core/presentation/views/button/default_close_button_widget.dart';
|
||||||
import 'package:core/presentation/views/color/color_picker_modal.dart';
|
import 'package:core/presentation/views/color/color_picker_modal.dart';
|
||||||
import 'package:core/presentation/views/color/colors_map_widget.dart';
|
import 'package:core/presentation/views/color/colors_map_widget.dart';
|
||||||
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/exceptions/label_exceptions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
||||||
@@ -27,16 +38,30 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
|||||||
|
|
||||||
typedef OnLabelActionCallback = Function(Label label);
|
typedef OnLabelActionCallback = Function(Label label);
|
||||||
|
|
||||||
|
enum LabelPositiveButtonState {
|
||||||
|
enabled,
|
||||||
|
disabled,
|
||||||
|
progressing;
|
||||||
|
}
|
||||||
|
|
||||||
class CreateNewLabelModal extends StatefulWidget {
|
class CreateNewLabelModal extends StatefulWidget {
|
||||||
final List<Label> labels;
|
final List<Label> labels;
|
||||||
|
final AccountId accountId;
|
||||||
|
final ImagePaths imagePaths;
|
||||||
final LabelActionType actionType;
|
final LabelActionType actionType;
|
||||||
final OnLabelActionCallback onLabelActionCallback;
|
final CreateNewLabelInteractor createNewLabelInteractor;
|
||||||
|
final EditLabelInteractor editLabelInteractor;
|
||||||
|
final VerifyNameInteractor verifyNameInteractor;
|
||||||
final Label? selectedLabel;
|
final Label? selectedLabel;
|
||||||
|
|
||||||
const CreateNewLabelModal({
|
const CreateNewLabelModal({
|
||||||
super.key,
|
super.key,
|
||||||
required this.labels,
|
required this.labels,
|
||||||
required this.onLabelActionCallback,
|
required this.accountId,
|
||||||
|
required this.imagePaths,
|
||||||
|
required this.createNewLabelInteractor,
|
||||||
|
required this.editLabelInteractor,
|
||||||
|
required this.verifyNameInteractor,
|
||||||
this.actionType = LabelActionType.create,
|
this.actionType = LabelActionType.create,
|
||||||
this.selectedLabel,
|
this.selectedLabel,
|
||||||
});
|
});
|
||||||
@@ -46,13 +71,11 @@ class CreateNewLabelModal extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
|
||||||
final _verifyNameInteractor = Get.find<VerifyNameInteractor>();
|
|
||||||
|
|
||||||
final ValueNotifier<String?> _labelNameErrorTextNotifier =
|
final ValueNotifier<String?> _labelNameErrorTextNotifier =
|
||||||
ValueNotifier(null);
|
ValueNotifier(null);
|
||||||
final ValueNotifier<Color?> _labelSelectedColorNotifier = ValueNotifier(null);
|
final ValueNotifier<Color?> _labelSelectedColorNotifier = ValueNotifier(null);
|
||||||
final ValueNotifier<bool> _createLabelStateNotifier = ValueNotifier(false);
|
final ValueNotifier<LabelPositiveButtonState> _createLabelStateNotifier =
|
||||||
|
ValueNotifier(LabelPositiveButtonState.disabled);
|
||||||
final TextEditingController _nameInputController = TextEditingController();
|
final TextEditingController _nameInputController = TextEditingController();
|
||||||
final FocusNode _nameInputFocusNode = FocusNode();
|
final FocusNode _nameInputFocusNode = FocusNode();
|
||||||
final TextEditingController _descriptionInputController =
|
final TextEditingController _descriptionInputController =
|
||||||
@@ -61,6 +84,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
|
|
||||||
List<String> _labelDisplayNameList = <String>[];
|
List<String> _labelDisplayNameList = <String>[];
|
||||||
Color? _selectedColor;
|
Color? _selectedColor;
|
||||||
|
StreamSubscription? _streamSubscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -69,8 +93,8 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
final labels = widget.labels;
|
final labels = widget.labels;
|
||||||
if (selectedLabel != null) {
|
if (selectedLabel != null) {
|
||||||
_selectedColor = selectedLabel.color?.value.toColor();
|
_selectedColor = selectedLabel.color?.value.toColor();
|
||||||
_labelDisplayNameList = labels
|
_labelDisplayNameList =
|
||||||
.getDisplayNameListWithoutSelectedLabel(selectedLabel);
|
labels.getDisplayNameListWithoutSelectedLabel(selectedLabel);
|
||||||
} else {
|
} else {
|
||||||
_labelDisplayNameList = labels.displayNameNotNullList;
|
_labelDisplayNameList = labels.displayNameNotNullList;
|
||||||
}
|
}
|
||||||
@@ -79,7 +103,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
_nameInputController.text = selectedLabel.safeDisplayName;
|
_nameInputController.text = selectedLabel.safeDisplayName;
|
||||||
_nameInputFocusNode.requestFocus();
|
_nameInputFocusNode.requestFocus();
|
||||||
_descriptionInputController.text = selectedLabel.safeDescription;
|
_descriptionInputController.text = selectedLabel.safeDescription;
|
||||||
_createLabelStateNotifier.value = true;
|
_createLabelStateNotifier.value = LabelPositiveButtonState.enabled;
|
||||||
_labelSelectedColorNotifier.value = _selectedColor;
|
_labelSelectedColorNotifier.value = _selectedColor;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,7 +194,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
valueListenable: _labelSelectedColorNotifier,
|
valueListenable: _labelSelectedColorNotifier,
|
||||||
builder: (_, value, __) {
|
builder: (_, value, __) {
|
||||||
return ColorsMapWidget(
|
return ColorsMapWidget(
|
||||||
imagePaths: _imagePaths,
|
imagePaths: widget.imagePaths,
|
||||||
customColor: value,
|
customColor: value,
|
||||||
onOpenColorPicker: () =>
|
onOpenColorPicker: () =>
|
||||||
_openColorPickerModal(appLocalizations),
|
_openColorPickerModal(appLocalizations),
|
||||||
@@ -182,17 +206,22 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
),
|
),
|
||||||
ValueListenableBuilder(
|
ValueListenableBuilder(
|
||||||
valueListenable: _createLabelStateNotifier,
|
valueListenable: _createLabelStateNotifier,
|
||||||
builder: (_, value, __) {
|
builder: (_, state, __) {
|
||||||
return ModalListActionButtonWidget(
|
return ModalListActionButtonWidget(
|
||||||
positiveLabel: widget.actionType.getModalPositiveAction(appLocalizations),
|
positiveLabel: widget.actionType
|
||||||
|
.getModalPositiveAction(appLocalizations),
|
||||||
negativeLabel: appLocalizations.cancel,
|
negativeLabel: appLocalizations.cancel,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 25,
|
vertical: 25,
|
||||||
),
|
),
|
||||||
isPositiveActionEnabled: value,
|
isPositiveActionEnabled:
|
||||||
|
state == LabelPositiveButtonState.enabled,
|
||||||
|
isProgressing: state ==
|
||||||
|
LabelPositiveButtonState.progressing,
|
||||||
onPositiveAction: _onCreateNewLabel,
|
onPositiveAction: _onCreateNewLabel,
|
||||||
onNegativeAction: _onCloseModal,
|
onNegativeAction: _onCloseModal,
|
||||||
positiveKey: widget.actionType.getModalPositiveActionKey(),
|
positiveKey: widget.actionType
|
||||||
|
.getModalPositiveActionKey(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -204,7 +233,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
DefaultCloseButtonWidget(
|
DefaultCloseButtonWidget(
|
||||||
iconClose: _imagePaths.icCloseDialog,
|
iconClose: widget.imagePaths.icCloseDialog,
|
||||||
onTapActionCallback: _onCloseModal,
|
onTapActionCallback: _onCloseModal,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -283,26 +312,23 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
return LabelInputFieldBuilder(
|
return LabelInputFieldBuilder(
|
||||||
key: const Key('label_name_input_field'),
|
key: const Key('label_name_input_field'),
|
||||||
label: appLocalizations.labelName,
|
label: appLocalizations.labelName,
|
||||||
hintText: appLocalizations
|
hintText: appLocalizations.pleaseEnterNameYourNewLabel,
|
||||||
.pleaseEnterNameYourNewLabel,
|
|
||||||
textEditingController: _nameInputController,
|
textEditingController: _nameInputController,
|
||||||
focusNode: _nameInputFocusNode,
|
focusNode: _nameInputFocusNode,
|
||||||
errorText: errorText,
|
errorText: errorText,
|
||||||
arrangeHorizontally: false,
|
arrangeHorizontally: false,
|
||||||
isLabelHasColon: false,
|
isLabelHasColon: false,
|
||||||
labelStyle:
|
labelStyle: ThemeUtils.textStyleInter600().copyWith(
|
||||||
ThemeUtils.textStyleInter600().copyWith(
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
height: 18 / 14,
|
height: 18 / 14,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
runSpacing: 16,
|
runSpacing: 16,
|
||||||
inputFieldMaxWidth: double.infinity,
|
inputFieldMaxWidth: double.infinity,
|
||||||
onTextChange: (value) =>
|
onTextChange: (value) => _onLabelNameInputChanged(
|
||||||
_onLabelNameInputChanged(
|
appLocalizations,
|
||||||
appLocalizations,
|
value,
|
||||||
value,
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -338,11 +364,13 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
) {
|
) {
|
||||||
final errorText = _verifyLabelName(appLocalizations, value);
|
final errorText = _verifyLabelName(appLocalizations, value);
|
||||||
_labelNameErrorTextNotifier.value = errorText;
|
_labelNameErrorTextNotifier.value = errorText;
|
||||||
_createLabelStateNotifier.value = errorText == null;
|
_createLabelStateNotifier.value = errorText == null
|
||||||
|
? LabelPositiveButtonState.enabled
|
||||||
|
: LabelPositiveButtonState.disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _verifyLabelName(AppLocalizations appLocalizations, String value) {
|
String? _verifyLabelName(AppLocalizations appLocalizations, String value) {
|
||||||
final result = _verifyNameInteractor.execute(
|
final result = widget.verifyNameInteractor.execute(
|
||||||
value,
|
value,
|
||||||
_validators,
|
_validators,
|
||||||
);
|
);
|
||||||
@@ -369,6 +397,8 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
void _onCreateNewLabel() {
|
void _onCreateNewLabel() {
|
||||||
_clearInputFocus();
|
_clearInputFocus();
|
||||||
|
|
||||||
|
_createLabelStateNotifier.value = LabelPositiveButtonState.progressing;
|
||||||
|
|
||||||
final newLabel = Label(
|
final newLabel = Label(
|
||||||
displayName: _nameInputController.text,
|
displayName: _nameInputController.text,
|
||||||
color: _selectedColor != null
|
color: _selectedColor != null
|
||||||
@@ -378,9 +408,17 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
? null
|
? null
|
||||||
: _descriptionInputController.text.trim(),
|
: _descriptionInputController.text.trim(),
|
||||||
);
|
);
|
||||||
widget.onLabelActionCallback(newLabel);
|
|
||||||
|
|
||||||
popBack();
|
switch (widget.actionType) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
_performCreateLabel(newLabel);
|
||||||
|
break;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
_performEditLabel(newLabel);
|
||||||
|
break;
|
||||||
|
case LabelActionType.delete:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCloseModal() {
|
void _onCloseModal() {
|
||||||
@@ -402,7 +440,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
barrierLabel: 'color-picker-modal',
|
barrierLabel: 'color-picker-modal',
|
||||||
pageBuilder: (_, __, ___) => ColorPickerModal(
|
pageBuilder: (_, __, ___) => ColorPickerModal(
|
||||||
imagePaths: _imagePaths,
|
imagePaths: widget.imagePaths,
|
||||||
modalTitle: appLocalizations.chooseCustomColour,
|
modalTitle: appLocalizations.chooseCustomColour,
|
||||||
modalSubtitle: appLocalizations.chooseAColourForThisLabel,
|
modalSubtitle: appLocalizations.chooseAColourForThisLabel,
|
||||||
initialColor: _selectedColor,
|
initialColor: _selectedColor,
|
||||||
@@ -411,6 +449,64 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _performCreateLabel(Label newLabel) {
|
||||||
|
_streamSubscription = widget.createNewLabelInteractor
|
||||||
|
.execute(widget.accountId, newLabel)
|
||||||
|
.listen(_handleDataStream, onError: _handleErrorStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDataStream(dartz.Either<Failure, Success> newState) {
|
||||||
|
newState.fold((failure) {
|
||||||
|
if (failure is CreateNewLabelFailure || failure is EditLabelFailure) {
|
||||||
|
popBack(result: failure);
|
||||||
|
}
|
||||||
|
}, (success) {
|
||||||
|
if (success is CreateNewLabelSuccess || success is EditLabelSuccess) {
|
||||||
|
popBack(result: success);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleErrorStream(Object error, StackTrace stackTrace) {
|
||||||
|
logWarning(
|
||||||
|
'CreateNewLabelModal::_handleErrorStream: Error: $error, StackTrace: $stackTrace');
|
||||||
|
|
||||||
|
switch (widget.actionType) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
popBack(result: CreateNewLabelFailure(error));
|
||||||
|
break;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
popBack(result: EditLabelFailure(error));
|
||||||
|
break;
|
||||||
|
case LabelActionType.delete:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _performEditLabel(Label newLabel) {
|
||||||
|
final currentLabelId = widget.selectedLabel?.id;
|
||||||
|
if (currentLabelId == null) {
|
||||||
|
popBack(result: EditLabelFailure(const LabelIdIsNull()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentLabelKeyword = widget.selectedLabel?.keyword;
|
||||||
|
if (currentLabelKeyword == null) {
|
||||||
|
popBack(result: EditLabelFailure(const LabelKeywordIsNull()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final labelRequest = EditLabelRequest(
|
||||||
|
labelId: currentLabelId,
|
||||||
|
labelKeyword: currentLabelKeyword,
|
||||||
|
newLabel: newLabel,
|
||||||
|
);
|
||||||
|
|
||||||
|
_streamSubscription = widget.editLabelInteractor
|
||||||
|
.execute(widget.accountId, labelRequest)
|
||||||
|
.listen(_handleDataStream, onError: _handleErrorStream);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_nameInputFocusNode.dispose();
|
_nameInputFocusNode.dispose();
|
||||||
@@ -421,6 +517,8 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
_labelSelectedColorNotifier.dispose();
|
_labelSelectedColorNotifier.dispose();
|
||||||
_createLabelStateNotifier.dispose();
|
_createLabelStateNotifier.dispose();
|
||||||
_labelDisplayNameList = [];
|
_labelDisplayNameList = [];
|
||||||
|
_streamSubscription?.cancel();
|
||||||
|
_streamSubscription = null;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||||
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
|
typedef OnCreateANewLabelAction = void Function();
|
||||||
|
|
||||||
class LabelListContextMenu extends StatelessWidget {
|
class LabelListContextMenu extends StatelessWidget {
|
||||||
final List<Label> labelList;
|
final List<Label> labelList;
|
||||||
final List<Label> emailLabels;
|
final List<Label> emailLabels;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final OnSelectLabelAction onSelectLabelAction;
|
final OnSelectLabelAction onSelectLabelAction;
|
||||||
|
final OnCreateANewLabelAction onCreateANewLabelAction;
|
||||||
|
|
||||||
const LabelListContextMenu({
|
const LabelListContextMenu({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -15,23 +22,56 @@ class LabelListContextMenu extends StatelessWidget {
|
|||||||
required this.emailLabels,
|
required this.emailLabels,
|
||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.onSelectLabelAction,
|
required this.onSelectLabelAction,
|
||||||
|
required this.onCreateANewLabelAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.builder(
|
final createLabelButton = TMailButtonWidget.fromText(
|
||||||
shrinkWrap: true,
|
text: AppLocalizations.of(context).createANewLabel,
|
||||||
itemCount: labelList.length,
|
backgroundColor: Colors.transparent,
|
||||||
itemBuilder: (_, index) {
|
textStyle: ThemeUtils.textStyleBodyBody3(color: Colors.black),
|
||||||
final label = labelList[index];
|
width: double.infinity,
|
||||||
final isSelected = emailLabels.contains(label);
|
alignment: AlignmentDirectional.centerStart,
|
||||||
return LabelItemContextMenu(
|
borderRadius: 0,
|
||||||
label: label,
|
height: 48,
|
||||||
imagePaths: imagePaths,
|
padding: const EdgeInsetsDirectional.symmetric(horizontal: 12),
|
||||||
isSelected: isSelected,
|
margin: labelList.isNotEmpty
|
||||||
onSelectLabelAction: onSelectLabelAction,
|
? const EdgeInsetsDirectional.only(bottom: 8)
|
||||||
);
|
: null,
|
||||||
},
|
onTapActionCallback: onCreateANewLabelAction,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (labelList.isNotEmpty) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: labelList.length,
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final label = labelList[index];
|
||||||
|
final isSelected = emailLabels.contains(label);
|
||||||
|
return LabelItemContextMenu(
|
||||||
|
label: label,
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
isSelected: isSelected,
|
||||||
|
onSelectLabelAction: onSelectLabelAction,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
color: AppColor.gray424244.withValues(alpha: 0.12),
|
||||||
|
),
|
||||||
|
createLabelButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return createLabelButton;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,7 +382,6 @@ abstract class BaseMailboxView extends GetWidget<MailboxController>
|
|||||||
onToggleLabelListState: labelController.toggleLabelListState,
|
onToggleLabelListState: labelController.toggleLabelListState,
|
||||||
onAddNewLabel: () =>
|
onAddNewLabel: () =>
|
||||||
labelController.handleLabelActionType(
|
labelController.handleLabelActionType(
|
||||||
context: context,
|
|
||||||
actionType: LabelActionType.create,
|
actionType: LabelActionType.create,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController {
|
|||||||
required LabelActionType actionType,
|
required LabelActionType actionType,
|
||||||
}) {
|
}) {
|
||||||
labelController.handleLabelActionType(
|
labelController.handleLabelActionType(
|
||||||
context: context,
|
|
||||||
actionType: actionType,
|
actionType: actionType,
|
||||||
accountId: accountId.value,
|
accountId: accountId.value,
|
||||||
label: label,
|
label: label,
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ class SignatureBuilder extends StatelessWidget {
|
|||||||
final iframeOverlay = Obx(() {
|
final iframeOverlay = Obx(() {
|
||||||
final dialogRouter = DialogRouter();
|
final dialogRouter = DialogRouter();
|
||||||
if (MessageDialogActionManager().isDialogOpened ||
|
if (MessageDialogActionManager().isDialogOpened ||
|
||||||
dialogRouter.isDialogOpened ||
|
dialogRouter.isDialogOpened) {
|
||||||
dialogRouter.isRuleFilterDialogOpened.isTrue) {
|
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: PointerInterceptor(
|
child: PointerInterceptor(
|
||||||
child: const SizedBox.expand(),
|
child: const SizedBox.expand(),
|
||||||
|
|||||||
+12
@@ -2,6 +2,8 @@ import 'package:core/core.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:model/email/presentation_email.dart';
|
import 'package:model/email/presentation_email.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.dart';
|
import 'package:tmail_ui_user/features/search/email/presentation/search_email_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/presentation/mixin/email_more_action_context_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/thread/presentation/mixin/email_more_action_context_menu_mixin.dart';
|
||||||
|
|
||||||
@@ -37,6 +39,16 @@ extension HandleEmailMoreActionExtension on SearchEmailController {
|
|||||||
isSelected,
|
isSelected,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () {
|
||||||
|
final emailId = presentationEmail.id;
|
||||||
|
if (emailId == null) return;
|
||||||
|
mailboxDashBoardController.labelController.handleLabelActionType(
|
||||||
|
actionType: LabelActionType.create,
|
||||||
|
accountId: accountId,
|
||||||
|
onLabelActionCallback: (label) =>
|
||||||
|
mailboxDashBoardController.toggleLabelToEmail(emailId, label, true),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart';
|
import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ class EmailContextMenuParams {
|
|||||||
final OpenPopupMenuActionGroup openPopupMenu;
|
final OpenPopupMenuActionGroup openPopupMenu;
|
||||||
final OnHandleEmailByActionType onHandleEmailByActionType;
|
final OnHandleEmailByActionType onHandleEmailByActionType;
|
||||||
final OnSelectLabelAction onSelectLabelAction;
|
final OnSelectLabelAction onSelectLabelAction;
|
||||||
|
final OnCreateANewLabelAction onCreateANewLabelAction;
|
||||||
|
|
||||||
EmailContextMenuParams({
|
EmailContextMenuParams({
|
||||||
required this.context,
|
required this.context,
|
||||||
@@ -38,6 +40,7 @@ class EmailContextMenuParams {
|
|||||||
required this.openPopupMenu,
|
required this.openPopupMenu,
|
||||||
required this.onHandleEmailByActionType,
|
required this.onHandleEmailByActionType,
|
||||||
required this.onSelectLabelAction,
|
required this.onSelectLabelAction,
|
||||||
|
required this.onCreateANewLabelAction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +68,7 @@ mixin EmailMoreActionContextMenu on LabelSubMenuMixin {
|
|||||||
final isChildOfTeam = mb?.isChildOfTeamMailboxes ?? false;
|
final isChildOfTeam = mb?.isChildOfTeamMailboxes ?? false;
|
||||||
|
|
||||||
final canPermanentlyDelete = isDrafts || isSpam || isTrash;
|
final canPermanentlyDelete = isDrafts || isSpam || isTrash;
|
||||||
final shouldShowLabelAs =
|
final shouldShowLabelAs = params.isLabelAvailable;
|
||||||
params.isLabelAvailable && (params.labels?.isNotEmpty ?? false);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
isRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead,
|
isRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead,
|
||||||
@@ -136,6 +138,11 @@ mixin EmailMoreActionContextMenu on LabelSubMenuMixin {
|
|||||||
popBack();
|
popBack();
|
||||||
params.onSelectLabelAction(label, isSelected);
|
params.onSelectLabelAction(label, isSelected);
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () {
|
||||||
|
submenuController.hide();
|
||||||
|
popBack();
|
||||||
|
params.onCreateANewLabelAction();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import 'package:tmail_ui_user/features/base/widget/keyboard/keyboard_handler_wra
|
|||||||
import 'package:tmail_ui_user/features/base/widget/report_message_banner.dart';
|
import 'package:tmail_ui_user/features/base/widget/report_message_banner.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
|
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_sub_menu_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart';
|
||||||
@@ -633,6 +635,14 @@ class ThreadView extends GetWidget<ThreadController>
|
|||||||
isSelected,
|
isSelected,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () {
|
||||||
|
dashboardController.labelController.handleLabelActionType(
|
||||||
|
actionType: LabelActionType.create,
|
||||||
|
accountId: dashboardController.accountId.value,
|
||||||
|
onLabelActionCallback: (label) => dashboardController
|
||||||
|
.toggleLabelToEmail(email.id!, label, true),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -19,19 +19,18 @@ import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
|||||||
|
|
||||||
extension AddLabelToThreadExtension on ThreadDetailController {
|
extension AddLabelToThreadExtension on ThreadDetailController {
|
||||||
Future<void> openAddLabelToEmailDialogModal() async {
|
Future<void> openAddLabelToEmailDialogModal() async {
|
||||||
if (!mailboxDashBoardController.isLabelAvailable) return;
|
if (!mailboxDashBoardController.isLabelAvailable ||
|
||||||
|
emailsInThreadDetailInfo.isEmpty) {
|
||||||
final labels = mailboxDashBoardController.labelController.labels;
|
|
||||||
if (emailsInThreadDetailInfo.isEmpty || labels.isEmpty) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final labelController = mailboxDashBoardController.labelController;
|
||||||
|
final labels = labelController.labels;
|
||||||
final threadLabels =
|
final threadLabels =
|
||||||
emailsInThreadDetailInfo.findCommonLabelsInThread(labels: labels);
|
emailsInThreadDetailInfo.findCommonLabelsInThread(labels: labels);
|
||||||
|
|
||||||
final emailIds = emailsInThreadDetailInfo.emailIdsToDisplay(true);
|
final emailIds = emailsInThreadDetailInfo.emailIdsToDisplay(true);
|
||||||
|
|
||||||
await DialogRouter().openDialogModal(
|
final newLabel = await DialogRouter().openDialogModal(
|
||||||
child: AddLabelToEmailModal(
|
child: AddLabelToEmailModal(
|
||||||
labels: labels,
|
labels: labels,
|
||||||
emailLabels: threadLabels,
|
emailLabels: threadLabels,
|
||||||
@@ -43,9 +42,17 @@ extension AddLabelToThreadExtension on ThreadDetailController {
|
|||||||
currentEmailIds: emailIds,
|
currentEmailIds: emailIds,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () => labelController.onCreateALabelAction(
|
||||||
|
accountId: accountId,
|
||||||
|
shouldPop: true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
dialogLabel: 'add-label-to-thread-modal',
|
dialogLabel: 'add-label-to-thread-modal',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (newLabel is Label) {
|
||||||
|
toggleLabelToThread(newLabel, isSelected: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleLabelToThread(
|
void toggleLabelToThread(
|
||||||
|
|||||||
+28
-9
@@ -15,6 +15,9 @@ import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action
|
|||||||
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||||
@@ -125,7 +128,6 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
_moveToMailbox(mailboxId, threadDetailActionType);
|
_moveToMailbox(mailboxId, threadDetailActionType);
|
||||||
break;
|
break;
|
||||||
case EmailActionType.labelAs:
|
case EmailActionType.labelAs:
|
||||||
if (!mailboxDashBoardController.isLabelAvailable) return;
|
|
||||||
openAddLabelToEmailDialogModal();
|
openAddLabelToEmailDialogModal();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -144,9 +146,7 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
? EmailActionType.unMarkAsStarred
|
? EmailActionType.unMarkAsStarred
|
||||||
: EmailActionType.markAsStarred,
|
: EmailActionType.markAsStarred,
|
||||||
EmailActionType.moveToMailbox,
|
EmailActionType.moveToMailbox,
|
||||||
if (mailboxDashBoardController.isLabelAvailable &&
|
if (mailboxDashBoardController.isLabelAvailable) EmailActionType.labelAs,
|
||||||
mailboxDashBoardController.labelController.labels.isNotEmpty)
|
|
||||||
EmailActionType.labelAs,
|
|
||||||
if (!threadDetailIsArchived && !threadDetailIsTeamMailbox) EmailActionType.archiveMessage,
|
if (!threadDetailIsArchived && !threadDetailIsTeamMailbox) EmailActionType.archiveMessage,
|
||||||
threadDetailIsSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
threadDetailIsSpam ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
threadDetailIsTrashed
|
threadDetailIsTrashed
|
||||||
@@ -175,6 +175,7 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final submenuController = PopupSubmenuController();
|
final submenuController = PopupSubmenuController();
|
||||||
|
final labelController = mailboxDashBoardController.labelController;
|
||||||
|
|
||||||
final popupMenuItemEmailActions = moreActions.map((actionType) {
|
final popupMenuItemEmailActions = moreActions.map((actionType) {
|
||||||
return PopupMenuItemEmailAction(
|
return PopupMenuItemEmailAction(
|
||||||
@@ -187,12 +188,16 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
actionType: actionType,
|
actionType: actionType,
|
||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
emailInThreadDetailInfos: emailsInThreadDetailInfo,
|
emailInThreadDetailInfos: emailsInThreadDetailInfo,
|
||||||
labels: mailboxDashBoardController.labelController.labels,
|
labels: labelController.labels,
|
||||||
onSelectLabelAction: (label, isSelected) {
|
onSelectLabelAction: (label, isSelected) {
|
||||||
toggleLabelToThread(label, isSelected: isSelected);
|
toggleLabelToThread(label, isSelected: isSelected);
|
||||||
submenuController.hide();
|
submenuController.hide();
|
||||||
popBack();
|
popBack();
|
||||||
},
|
},
|
||||||
|
onCreateANewLabelAction: () => _handleCreateLabelFromSubmenu(
|
||||||
|
submenuController: submenuController,
|
||||||
|
labelController: labelController,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
@@ -215,6 +220,19 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleCreateLabelFromSubmenu({
|
||||||
|
required PopupSubmenuController submenuController,
|
||||||
|
required LabelController labelController,
|
||||||
|
}) {
|
||||||
|
submenuController.hide();
|
||||||
|
popBack();
|
||||||
|
labelController.handleLabelActionType(
|
||||||
|
actionType: LabelActionType.create,
|
||||||
|
accountId: accountId,
|
||||||
|
onLabelActionCallback: (label) => toggleLabelToThread(label, isSelected: true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bool _shouldHandleAction(EmailActionType action) {
|
bool _shouldHandleAction(EmailActionType action) {
|
||||||
if (action != EmailActionType.labelAs) {
|
if (action != EmailActionType.labelAs) {
|
||||||
return true;
|
return true;
|
||||||
@@ -228,9 +246,10 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
required ImagePaths imagePaths,
|
required ImagePaths imagePaths,
|
||||||
required List<EmailInThreadDetailInfo> emailInThreadDetailInfos,
|
required List<EmailInThreadDetailInfo> emailInThreadDetailInfos,
|
||||||
required List<Label>? labels,
|
required List<Label>? labels,
|
||||||
OnSelectLabelAction? onSelectLabelAction,
|
required OnSelectLabelAction onSelectLabelAction,
|
||||||
|
required OnCreateANewLabelAction onCreateANewLabelAction,
|
||||||
}) {
|
}) {
|
||||||
if (actionType == EmailActionType.labelAs && labels?.isNotEmpty == true) {
|
if (actionType == EmailActionType.labelAs) {
|
||||||
final listLabels = labels ?? [];
|
final listLabels = labels ?? [];
|
||||||
final threadLabels =
|
final threadLabels =
|
||||||
emailInThreadDetailInfos.findCommonLabelsInThread(labels: listLabels);
|
emailInThreadDetailInfos.findCommonLabelsInThread(labels: listLabels);
|
||||||
@@ -239,8 +258,8 @@ extension OnThreadDetailActionClick on ThreadDetailController {
|
|||||||
labelList: listLabels,
|
labelList: listLabels,
|
||||||
emailLabels: threadLabels,
|
emailLabels: threadLabels,
|
||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
onSelectLabelAction: (label, isSelected) =>
|
onSelectLabelAction: onSelectLabelAction,
|
||||||
onSelectLabelAction?.call(label, isSelected),
|
onCreateANewLabelAction: onCreateANewLabelAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache
|
|||||||
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
|
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart';
|
import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_sort_order_manager.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_sort_order_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/data/local/preferences_setting_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/preferences_setting_manager.dart';
|
||||||
@@ -58,6 +59,7 @@ class LocalBindings extends Bindings {
|
|||||||
_bindingKeychainSharing();
|
_bindingKeychainSharing();
|
||||||
_bindingCaching();
|
_bindingCaching();
|
||||||
_bindingWorkerQueue();
|
_bindingWorkerQueue();
|
||||||
|
_bindingVerifyLogicInteractor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bindingCaching() {
|
void _bindingCaching() {
|
||||||
@@ -170,4 +172,8 @@ class LocalBindings extends Bindings {
|
|||||||
void _bindingKeychainSharing() {
|
void _bindingKeychainSharing() {
|
||||||
Get.put(KeychainSharingManager(Get.find<FlutterSecureStorage>()));
|
Get.put(KeychainSharingManager(Get.find<FlutterSecureStorage>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _bindingVerifyLogicInteractor() {
|
||||||
|
Get.put(VerifyNameInteractor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ class DialogRouter {
|
|||||||
required Object? arguments
|
required Object? arguments
|
||||||
}) async {
|
}) async {
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
_isDialogOpened.value = true;
|
_isMapDialogOpened[routeName] = true;
|
||||||
}
|
}
|
||||||
_bindingDI(routeName);
|
_bindingDI(routeName);
|
||||||
|
|
||||||
@@ -40,18 +40,15 @@ class DialogRouter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
_isDialogOpened.value = false;
|
_isMapDialogOpened.remove(routeName);
|
||||||
if (routeName == AppRoutes.rulesFilterCreator) {
|
|
||||||
isRuleFilterDialogOpened.value = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return returnedValue;
|
return returnedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final RxBool _isDialogOpened = false.obs;
|
final RxMap<String, bool> _isMapDialogOpened = RxMap<String, bool>();
|
||||||
final RxBool isRuleFilterDialogOpened = false.obs;
|
|
||||||
|
|
||||||
bool get isDialogOpened => _isDialogOpened.value;
|
bool get isDialogOpened =>
|
||||||
|
_isMapDialogOpened.values.any((isOpened) => isOpened == true);
|
||||||
|
|
||||||
void _bindingDI(String routeName) {
|
void _bindingDI(String routeName) {
|
||||||
log('DialogRouter::_bindingDI():routeName: $routeName');
|
log('DialogRouter::_bindingDI():routeName: $routeName');
|
||||||
@@ -60,9 +57,6 @@ class DialogRouter {
|
|||||||
MailboxCreatorBindings().dependencies();
|
MailboxCreatorBindings().dependencies();
|
||||||
break;
|
break;
|
||||||
case AppRoutes.rulesFilterCreator:
|
case AppRoutes.rulesFilterCreator:
|
||||||
if (PlatformInfo.isWeb) {
|
|
||||||
isRuleFilterDialogOpened.value = true;
|
|
||||||
}
|
|
||||||
RulesFilterCreatorBindings().dependencies();
|
RulesFilterCreatorBindings().dependencies();
|
||||||
break;
|
break;
|
||||||
case AppRoutes.identityCreator:
|
case AppRoutes.identityCreator:
|
||||||
@@ -99,22 +93,26 @@ class DialogRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> openDialogModal({
|
Future<dynamic> openDialogModal({
|
||||||
required Widget child,
|
required Widget child,
|
||||||
String? dialogLabel,
|
required String dialogLabel,
|
||||||
}) async {
|
}) async {
|
||||||
if (PlatformInfo.isWeb) {
|
try {
|
||||||
_isDialogOpened.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Get.generalDialog(
|
|
||||||
barrierDismissible: true,
|
|
||||||
barrierLabel: dialogLabel ?? 'dialog-modal',
|
|
||||||
pageBuilder: (_, __, ___) => child,
|
|
||||||
).whenComplete(() {
|
|
||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
_isDialogOpened.value = false;
|
_isMapDialogOpened[dialogLabel] = true;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return await Get.generalDialog(
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: dialogLabel,
|
||||||
|
pageBuilder: (_, __, ___) => child,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logWarning('DialogRouter::openDialogModal: Exception = $e');
|
||||||
|
} finally {
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
_isMapDialogOpened.remove(dialogLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user