TF-934 Implement open email in new tab
This commit is contained in:
@@ -10,6 +10,7 @@ export 'presentation/extensions/html_extension.dart';
|
|||||||
export 'presentation/extensions/compare_string_extension.dart';
|
export 'presentation/extensions/compare_string_extension.dart';
|
||||||
export 'presentation/extensions/compare_list_extensions.dart';
|
export 'presentation/extensions/compare_list_extensions.dart';
|
||||||
export 'presentation/extensions/string_extension.dart';
|
export 'presentation/extensions/string_extension.dart';
|
||||||
|
export 'presentation/extensions/tap_down_details_extension.dart';
|
||||||
export 'domain/extensions/media_type_extension.dart';
|
export 'domain/extensions/media_type_extension.dart';
|
||||||
|
|
||||||
// Exceptions
|
// Exceptions
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension TapDownDetailsExtension on TapDownDetails {
|
||||||
|
|
||||||
|
RelativeRect getPosition(BuildContext context) {
|
||||||
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
final offset = globalPosition;
|
||||||
|
final position = RelativeRect.fromLTRB(
|
||||||
|
offset.dx,
|
||||||
|
offset.dy,
|
||||||
|
screenSize.width - offset.dx,
|
||||||
|
screenSize.height - offset.dy,
|
||||||
|
);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
|
|
||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
typedef IconWebCallback = void Function();
|
typedef IconWebCallback = void Function();
|
||||||
typedef IconWebHasPositionCallback = void Function(RelativeRect);
|
typedef IconWebHasPositionCallback = void Function(RelativeRect);
|
||||||
|
typedef OnTapIconButtonCallbackAction = void Function();
|
||||||
|
typedef OnTapDownIconButtonCallbackAction = void Function(TapDownDetails TapDetails);
|
||||||
|
|
||||||
Widget buildIconWeb({
|
Widget buildIconWeb({
|
||||||
required Widget icon,
|
required Widget icon,
|
||||||
@@ -32,6 +35,47 @@ Widget buildIconWeb({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildSVGIconButton({
|
||||||
|
required String icon,
|
||||||
|
String? tooltip,
|
||||||
|
EdgeInsets? padding,
|
||||||
|
double? iconSize,
|
||||||
|
Color? iconColor,
|
||||||
|
OnTapIconButtonCallbackAction? onTap,
|
||||||
|
OnTapDownIconButtonCallbackAction? onTapDown,
|
||||||
|
}) {
|
||||||
|
Widget iconWidget = Padding(
|
||||||
|
padding: padding ?? const EdgeInsets.all(8),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon,
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize,
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
color: iconColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget itemChild;
|
||||||
|
if (tooltip != null) {
|
||||||
|
itemChild = Tooltip(
|
||||||
|
message: tooltip,
|
||||||
|
child: iconWidget,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
itemChild = iconWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
onTapDown: onTapDown,
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
child: itemChild,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildIconWebHasPosition(BuildContext context, {
|
Widget buildIconWebHasPosition(BuildContext context, {
|
||||||
required Widget icon,
|
required Widget icon,
|
||||||
String? tooltip,
|
String? tooltip,
|
||||||
@@ -43,18 +87,10 @@ Widget buildIconWebHasPosition(BuildContext context, {
|
|||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTapDown: (detail) {
|
onTapDown: (detail) {
|
||||||
final screenSize = MediaQuery.of(context).size;
|
onTapDown?.call(detail.getPosition(context));
|
||||||
final offset = detail.globalPosition;
|
|
||||||
final position = RelativeRect.fromLTRB(
|
|
||||||
offset.dx,
|
|
||||||
offset.dy,
|
|
||||||
screenSize.width - offset.dx,
|
|
||||||
screenSize.height - offset.dy,
|
|
||||||
);
|
|
||||||
onTapDown?.call(position);
|
|
||||||
},
|
},
|
||||||
onTap: () => onTap?.call(),
|
onTap: () => onTap?.call(),
|
||||||
borderRadius: const BorderRadius.all(const Radius.circular(12)),
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: tooltip ?? '',
|
message: tooltip ?? '',
|
||||||
child: icon,
|
child: icon,
|
||||||
|
|||||||
@@ -4,27 +4,39 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
|
|
||||||
mixin PopupMenuWidgetMixin {
|
mixin PopupMenuWidgetMixin {
|
||||||
|
|
||||||
Widget popupItem(String iconAction, String nameAction, {
|
Widget popupItem(
|
||||||
|
String iconAction,
|
||||||
|
String nameAction, {
|
||||||
Color? colorIcon,
|
Color? colorIcon,
|
||||||
|
double? iconSize,
|
||||||
TextStyle? styleName,
|
TextStyle? styleName,
|
||||||
Function? onCallbackAction
|
EdgeInsets? padding,
|
||||||
|
Function()? onCallbackAction
|
||||||
}) {
|
}) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => onCallbackAction?.call(),
|
onTap: onCallbackAction,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
padding: padding ?? const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
SvgPicture.asset(iconAction, width: 20, height: 20, fit: BoxFit.fill, color: colorIcon),
|
SvgPicture.asset(
|
||||||
const SizedBox(width: 12),
|
iconAction,
|
||||||
Expanded(child: Text(nameAction,
|
width: iconSize ?? 20,
|
||||||
style: styleName ?? const TextStyle(
|
height: iconSize ?? 20,
|
||||||
fontSize: 17,
|
fit: BoxFit.fill,
|
||||||
fontWeight: FontWeight.normal,
|
color: colorIcon
|
||||||
color: Colors.black))),
|
),
|
||||||
])
|
const SizedBox(width: 12),
|
||||||
),
|
Expanded(child: Text(
|
||||||
)
|
nameAction,
|
||||||
|
style: styleName ?? const TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.black)
|
||||||
|
)),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.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/widgets/app_bar_mail_widget_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart';
|
||||||
@@ -16,7 +17,8 @@ import 'package:tmail_ui_user/features/manage_account/presentation/extensions/va
|
|||||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class EmailView extends GetWidget<SingleEmailController> {
|
class EmailView extends GetWidget<SingleEmailController>
|
||||||
|
with PopupMenuWidgetMixin {
|
||||||
|
|
||||||
static const double maxSizeFullDisplayEmailAddressArrowDownButton = 30.0;
|
static const double maxSizeFullDisplayEmailAddressArrowDownButton = 30.0;
|
||||||
|
|
||||||
@@ -766,49 +768,58 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
|
|
||||||
Widget _markAsEmailUnreadAction(BuildContext context, PresentationEmail email) {
|
Widget _markAsEmailUnreadAction(BuildContext context, PresentationEmail email) {
|
||||||
return (EmailActionCupertinoActionSheetActionBuilder(
|
return (EmailActionCupertinoActionSheetActionBuilder(
|
||||||
const Key('mark_as_unread_action'),
|
const Key('mark_as_unread_action'),
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
imagePaths.icUnreadEmail,
|
imagePaths.icUnreadEmail,
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
color: AppColor.colorTextButton),
|
color: AppColor.colorTextButton
|
||||||
AppLocalizations.of(context).mark_as_unread,
|
),
|
||||||
email,
|
AppLocalizations.of(context).mark_as_unread,
|
||||||
iconLeftPadding: responsiveUtils.isMobile(context)
|
email,
|
||||||
? const EdgeInsets.only(left: 12, right: 16)
|
iconLeftPadding: responsiveUtils.isMobile(context)
|
||||||
: const EdgeInsets.only(right: 12),
|
? const EdgeInsets.only(left: 12, right: 16)
|
||||||
iconRightPadding: responsiveUtils.isMobile(context)
|
: const EdgeInsets.only(right: 12),
|
||||||
? const EdgeInsets.only(right: 12)
|
iconRightPadding: responsiveUtils.isMobile(context)
|
||||||
: EdgeInsets.zero)
|
? const EdgeInsets.only(right: 12)
|
||||||
..onActionClick((email) => controller.handleEmailAction(context, email, EmailActionType.markAsUnread)))
|
: EdgeInsets.zero)
|
||||||
.build();
|
..onActionClick((email) => controller.handleEmailAction(
|
||||||
|
context,
|
||||||
|
email,
|
||||||
|
EmailActionType.markAsUnread)
|
||||||
|
)
|
||||||
|
).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) {
|
Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) {
|
||||||
final currentMailbox = controller.getMailboxContain(email);
|
final currentMailbox = controller.getMailboxContain(email);
|
||||||
|
|
||||||
return (EmailActionCupertinoActionSheetActionBuilder(
|
return (EmailActionCupertinoActionSheetActionBuilder(
|
||||||
const Key('mark_as_spam_or_un_spam_action'),
|
const Key('mark_as_spam_or_un_spam_action'),
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
currentMailbox?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icMailboxSpam,
|
currentMailbox?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icSpam,
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
color: AppColor.colorTextButton),
|
color: AppColor.colorTextButton
|
||||||
currentMailbox?.isSpam == true
|
),
|
||||||
? AppLocalizations.of(context).remove_from_spam
|
currentMailbox?.isSpam == true
|
||||||
: AppLocalizations.of(context).mark_as_spam,
|
? AppLocalizations.of(context).remove_from_spam
|
||||||
email,
|
: AppLocalizations.of(context).mark_as_spam,
|
||||||
iconLeftPadding: responsiveUtils.isMobile(context)
|
email,
|
||||||
? const EdgeInsets.only(left: 12, right: 16)
|
iconLeftPadding: responsiveUtils.isMobile(context)
|
||||||
: const EdgeInsets.only(right: 12),
|
? const EdgeInsets.only(left: 12, right: 16)
|
||||||
iconRightPadding: responsiveUtils.isMobile(context)
|
: const EdgeInsets.only(right: 12),
|
||||||
? const EdgeInsets.only(right: 12)
|
iconRightPadding: responsiveUtils.isMobile(context)
|
||||||
: EdgeInsets.zero)
|
? const EdgeInsets.only(right: 12)
|
||||||
..onActionClick((email) => controller.handleEmailAction(context, email,
|
: EdgeInsets.zero)
|
||||||
currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam)))
|
..onActionClick((email) => controller.handleEmailAction(
|
||||||
.build();
|
context,
|
||||||
|
email,
|
||||||
|
currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam)
|
||||||
|
)
|
||||||
|
).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _quickCreatingRuleAction(BuildContext context, PresentationEmail email) {
|
Widget _quickCreatingRuleAction(BuildContext context, PresentationEmail email) {
|
||||||
@@ -833,85 +844,76 @@ class EmailView extends GetWidget<SingleEmailController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<PopupMenuEntry> _popupMenuEmailActionTile(BuildContext context, PresentationEmail email) {
|
List<PopupMenuEntry> _popupMenuEmailActionTile(BuildContext context, PresentationEmail email) {
|
||||||
|
final mailboxContain = controller.getMailboxContain(email);
|
||||||
return [
|
return [
|
||||||
PopupMenuItem(
|
_markAsEmailUnreadPopupItemAction(context, email),
|
||||||
padding: EdgeInsets.zero,
|
_markAsEmailSpamOrUnSpamPopupItemAction(context, email, mailboxContain),
|
||||||
child: _markAsEmailUnreadPopupItemAction(context, email)),
|
_quickCreatingRulePopupItemAction(context, email)
|
||||||
PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: _markAsEmailSpamOrUnSpamPopupItemAction(context, email)),
|
|
||||||
PopupMenuItem(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: _quickCreatingRulePopupItemAction(context, email)),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _markAsEmailUnreadPopupItemAction(BuildContext context, PresentationEmail email) {
|
PopupMenuEntry _markAsEmailUnreadPopupItemAction(BuildContext context, PresentationEmail email) {
|
||||||
return _popupMenuItem(
|
return PopupMenuItem(
|
||||||
icon: imagePaths.icUnreadEmail,
|
padding: EdgeInsets.zero,
|
||||||
title: AppLocalizations.of(context).mark_as_unread,
|
child: popupItem(
|
||||||
onTapCallback: () => controller.handleEmailAction(context, email, EmailActionType.markAsUnread)
|
imagePaths.icUnreadEmail,
|
||||||
);
|
AppLocalizations.of(context).mark_as_unread,
|
||||||
}
|
colorIcon: AppColor.colorTextButton,
|
||||||
|
styleName: const TextStyle(
|
||||||
Widget _markAsEmailSpamOrUnSpamPopupItemAction(BuildContext context, PresentationEmail email) {
|
fontWeight: FontWeight.w500,
|
||||||
final currentMailbox = controller.getMailboxContain(email);
|
fontSize: 16,
|
||||||
|
color: Colors.black
|
||||||
return _popupMenuItem(
|
),
|
||||||
icon: currentMailbox?.isSpam == true
|
onCallbackAction: () => controller.handleEmailAction(
|
||||||
? imagePaths.icNotSpam
|
context,
|
||||||
: imagePaths.icMailboxSpam,
|
email,
|
||||||
title: currentMailbox?.isSpam == true
|
EmailActionType.markAsUnread
|
||||||
? AppLocalizations.of(context).remove_from_spam
|
|
||||||
: AppLocalizations.of(context).mark_as_spam,
|
|
||||||
onTapCallback: () => controller.handleEmailAction(
|
|
||||||
context,
|
|
||||||
email,
|
|
||||||
currentMailbox?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _quickCreatingRulePopupItemAction(BuildContext context, PresentationEmail email) {
|
|
||||||
return _popupMenuItem(
|
|
||||||
icon: imagePaths.icQuickCreatingRule,
|
|
||||||
title: AppLocalizations.of(context).quickCreatingRule,
|
|
||||||
onTapCallback: () => controller.quickCreatingRule(context, email.from!.first)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _popupMenuItem({
|
|
||||||
required String icon,
|
|
||||||
required String title,
|
|
||||||
Function()? onTapCallback
|
|
||||||
}) {
|
|
||||||
return Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: onTapCallback,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: SizedBox(
|
|
||||||
width: 300,
|
|
||||||
height: 44,
|
|
||||||
child: Row(children: [
|
|
||||||
SvgPicture.asset(
|
|
||||||
icon,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
color: AppColor.colorTextButton,
|
|
||||||
fit: BoxFit.fill),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(child: Text(
|
|
||||||
title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.normal)
|
|
||||||
)),
|
|
||||||
])
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupMenuEntry _markAsEmailSpamOrUnSpamPopupItemAction(
|
||||||
|
BuildContext context,
|
||||||
|
PresentationEmail email,
|
||||||
|
PresentationMailbox? mailboxContain
|
||||||
|
) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: popupItem(
|
||||||
|
mailboxContain?.isSpam == true ? imagePaths.icNotSpam : imagePaths.icSpam,
|
||||||
|
mailboxContain?.isSpam == true
|
||||||
|
? AppLocalizations.of(context).remove_from_spam
|
||||||
|
: AppLocalizations.of(context).mark_as_spam,
|
||||||
|
colorIcon: AppColor.colorTextButton,
|
||||||
|
styleName: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black
|
||||||
|
),
|
||||||
|
onCallbackAction: () => controller.handleEmailAction(
|
||||||
|
context,
|
||||||
|
email,
|
||||||
|
mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupMenuEntry _quickCreatingRulePopupItemAction(BuildContext context, PresentationEmail email) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: popupItem(
|
||||||
|
imagePaths.icQuickCreatingRule,
|
||||||
|
AppLocalizations.of(context).quickCreatingRule,
|
||||||
|
colorIcon: AppColor.colorTextButton,
|
||||||
|
styleName: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black
|
||||||
|
),
|
||||||
|
onCallbackAction: () => controller.quickCreatingRule(context, email.from!.first)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
|||||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
mixin EmailActionController on ViewAsDialogActionMixin {
|
mixin EmailActionController on ViewAsDialogActionMixin {
|
||||||
|
|
||||||
@@ -288,4 +289,15 @@ mixin EmailActionController on ViewAsDialogActionMixin {
|
|||||||
mailboxCurrent: mailboxCurrent,
|
mailboxCurrent: mailboxCurrent,
|
||||||
onCancelSelectionEmail: onCancelSelectionEmail);
|
onCancelSelectionEmail: onCancelSelectionEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openEmailInNewTabAction(BuildContext context, PresentationEmail email) {
|
||||||
|
launchLink(email.routeWebAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> launchLink(String url, {bool isNewTab = true}) async {
|
||||||
|
await launchUrl(
|
||||||
|
Uri.parse(url),
|
||||||
|
webOnlyWindowName: isNewTab ? '_blank' : '_self',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -803,6 +803,9 @@ class ThreadController extends BaseController with EmailActionController {
|
|||||||
popBack();
|
popBack();
|
||||||
unSpam(selectedEmail);
|
unSpam(selectedEmail);
|
||||||
break;
|
break;
|
||||||
|
case EmailActionType.openInNewTab:
|
||||||
|
openEmailInNewTabAction(context, selectedEmail);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -821,7 +824,7 @@ class ThreadController extends BaseController with EmailActionController {
|
|||||||
|
|
||||||
void calculateDragValue(PresentationEmail? currentPresentationEmail) {
|
void calculateDragValue(PresentationEmail? currentPresentationEmail) {
|
||||||
if(currentPresentationEmail != null) {
|
if(currentPresentationEmail != null) {
|
||||||
if(mailboxDashBoardController.listEmailSelected.contains(currentPresentationEmail)){
|
if(mailboxDashBoardController.listEmailSelected.findEmail(currentPresentationEmail.id) != null){
|
||||||
listEmailDrag.clear();
|
listEmailDrag.clear();
|
||||||
listEmailDrag.addAll(mailboxDashBoardController.listEmailSelected);
|
listEmailDrag.addAll(mailboxDashBoardController.listEmailSelected);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:model/model.dart';
|
import 'package:model/model.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.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/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.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';
|
||||||
@@ -24,9 +25,12 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_bu
|
|||||||
if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart';
|
if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart';
|
import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.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';
|
||||||
|
|
||||||
class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
|
class ThreadView extends GetWidget<ThreadController>
|
||||||
FilterEmailPopupMenuMixin {
|
with AppLoaderMixin,
|
||||||
|
FilterEmailPopupMenuMixin,
|
||||||
|
PopupMenuWidgetMixin {
|
||||||
|
|
||||||
final _responsiveUtils = Get.find<ResponsiveUtils>();
|
final _responsiveUtils = Get.find<ResponsiveUtils>();
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
@@ -496,46 +500,132 @@ class ThreadView extends GetWidget<ThreadController> with AppLoaderMixin,
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _contextMenuActionTile(BuildContext context, PresentationEmail email) {
|
List<Widget> _contextMenuActionTile(BuildContext context, PresentationEmail email) {
|
||||||
|
final mailboxContain = controller.getMailboxContain(email);
|
||||||
|
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
_markAsEmailSpamOrUnSpamAction(context, email),
|
_openInNewTabContextMenuItemAction(context, email),
|
||||||
|
if (mailboxContain?.isDrafts == false)
|
||||||
|
_markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _markAsEmailSpamOrUnSpamAction(BuildContext context, PresentationEmail email) {
|
Widget _markAsEmailSpamOrUnSpamContextMenuItemAction(
|
||||||
final mailboxContain = controller.getMailboxContain(email);
|
BuildContext context,
|
||||||
|
PresentationEmail email,
|
||||||
|
PresentationMailbox? mailboxContain
|
||||||
|
) {
|
||||||
return (EmailActionCupertinoActionSheetActionBuilder(
|
return (EmailActionCupertinoActionSheetActionBuilder(
|
||||||
const Key('mark_as_spam_or_un_spam_action'),
|
const Key('mark_as_spam_or_un_spam_action'),
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam,
|
mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam,
|
||||||
width: 28,
|
width: 24,
|
||||||
height: 28,
|
height: 24,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
color: AppColor.colorTextButton),
|
color: AppColor.colorTextButton),
|
||||||
mailboxContain?.isSpam == true
|
mailboxContain?.isSpam == true
|
||||||
? AppLocalizations.of(context).remove_from_spam
|
? AppLocalizations.of(context).remove_from_spam
|
||||||
: AppLocalizations.of(context).mark_as_spam,
|
: AppLocalizations.of(context).mark_as_spam,
|
||||||
email,
|
email,
|
||||||
iconLeftPadding: _responsiveUtils.isMobile(context)
|
iconLeftPadding: _responsiveUtils.isMobile(context)
|
||||||
? const EdgeInsets.only(left: 12, right: 16)
|
? const EdgeInsets.only(left: 12, right: 16)
|
||||||
: const EdgeInsets.only(right: 12),
|
: const EdgeInsets.only(right: 12),
|
||||||
iconRightPadding: _responsiveUtils.isMobile(context)
|
iconRightPadding: _responsiveUtils.isMobile(context)
|
||||||
? const EdgeInsets.only(right: 12)
|
? const EdgeInsets.only(right: 12)
|
||||||
: EdgeInsets.zero)
|
: EdgeInsets.zero)
|
||||||
..onActionClick((email) => controller.pressEmailAction(context,
|
..onActionClick((email) => controller.pressEmailAction(context,
|
||||||
mailboxContain?.isSpam == true
|
mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
? EmailActionType.unSpam
|
email,
|
||||||
: EmailActionType.moveToSpam,
|
mailboxContain: mailboxContain)
|
||||||
email,
|
)
|
||||||
mailboxContain: mailboxContain)))
|
).build();
|
||||||
.build();
|
}
|
||||||
|
|
||||||
|
Widget _openInNewTabContextMenuItemAction(BuildContext context, PresentationEmail email) {
|
||||||
|
return (EmailActionCupertinoActionSheetActionBuilder(
|
||||||
|
const Key('open_in_new_tab_action'),
|
||||||
|
SvgPicture.asset(
|
||||||
|
_imagePaths.icOpenInNewTab,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
color: AppColor.colorTextButton),
|
||||||
|
AppLocalizations.of(context).openInNewTab,
|
||||||
|
email,
|
||||||
|
iconLeftPadding: _responsiveUtils.isMobile(context)
|
||||||
|
? const EdgeInsets.only(left: 12, right: 16)
|
||||||
|
: const EdgeInsets.only(right: 12),
|
||||||
|
iconRightPadding: _responsiveUtils.isMobile(context)
|
||||||
|
? const EdgeInsets.only(right: 12)
|
||||||
|
: EdgeInsets.zero)
|
||||||
|
..onActionClick((email) {
|
||||||
|
popBack();
|
||||||
|
controller.openEmailInNewTabAction(context, email);
|
||||||
|
})
|
||||||
|
).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PopupMenuEntry> _popupMenuActionTile(BuildContext context, PresentationEmail email) {
|
List<PopupMenuEntry> _popupMenuActionTile(BuildContext context, PresentationEmail email) {
|
||||||
|
final mailboxContain = controller.getMailboxContain(email);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)),
|
_buildOpenInNewTabPopupMenuItem(context, email, mailboxContain),
|
||||||
|
if (mailboxContain?.isDrafts == false)
|
||||||
|
_buildMarkAsSpamPopupMenuItem(context, email, mailboxContain)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopupMenuEntry _buildMarkAsSpamPopupMenuItem(
|
||||||
|
BuildContext context,
|
||||||
|
PresentationEmail email,
|
||||||
|
PresentationMailbox? mailboxContain
|
||||||
|
) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: popupItem(
|
||||||
|
mailboxContain?.isSpam == true ? _imagePaths.icNotSpam : _imagePaths.icSpam,
|
||||||
|
mailboxContain?.isSpam == true
|
||||||
|
? AppLocalizations.of(context).remove_from_spam
|
||||||
|
: AppLocalizations.of(context).mark_as_spam,
|
||||||
|
colorIcon: AppColor.colorTextButton,
|
||||||
|
styleName: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black
|
||||||
|
),
|
||||||
|
onCallbackAction: () => controller.pressEmailAction(
|
||||||
|
context,
|
||||||
|
mailboxContain?.isSpam == true ? EmailActionType.unSpam : EmailActionType.moveToSpam,
|
||||||
|
email,
|
||||||
|
mailboxContain: mailboxContain
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupMenuEntry _buildOpenInNewTabPopupMenuItem(
|
||||||
|
BuildContext context,
|
||||||
|
PresentationEmail email,
|
||||||
|
PresentationMailbox? mailboxContain
|
||||||
|
) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: popupItem(
|
||||||
|
_imagePaths.icOpenInNewTab,
|
||||||
|
AppLocalizations.of(context).openInNewTab,
|
||||||
|
colorIcon: AppColor.colorTextButton,
|
||||||
|
styleName: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black
|
||||||
|
),
|
||||||
|
onCallbackAction: () {
|
||||||
|
popBack();
|
||||||
|
controller.openEmailInNewTabAction(context, email);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildMarkAsMailboxReadLoading(BuildContext context) {
|
Widget _buildMarkAsMailboxReadLoading(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final viewState = controller.mailboxDashBoardController.viewStateMarkAsReadMailbox.value;
|
final viewState = controller.mailboxDashBoardController.viewStateMarkAsReadMailbox.value;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/extensions/tap_down_details_extension.dart';
|
||||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||||
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
import 'package:core/presentation/views/responsive/responsive_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -392,90 +393,84 @@ class EmailTileBuilder with BaseEmailItemTile {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double _getIconSize() {
|
||||||
|
return responsiveUtils.isDesktop(_context) ? 18 : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeInsets _getPaddingIcon() {
|
||||||
|
return responsiveUtils.isDesktop(_context)
|
||||||
|
? const EdgeInsets.all(8)
|
||||||
|
: const EdgeInsets.all(5);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildListActionButtonWhenHover() {
|
Widget _buildListActionButtonWhenHover() {
|
||||||
return Row(children: [
|
return Row(children: [
|
||||||
|
buildSVGIconButton(
|
||||||
|
icon: imagePaths.icOpenInNewTab,
|
||||||
|
iconColor: AppColor.colorActionButtonHover,
|
||||||
|
iconSize: _getIconSize(),
|
||||||
|
padding: _getPaddingIcon(),
|
||||||
|
tooltip: AppLocalizations.of(_context).openInNewTab,
|
||||||
|
onTap: () => _emailActionClick?.call(
|
||||||
|
EmailActionType.openInNewTab,
|
||||||
|
_presentationEmail
|
||||||
|
)
|
||||||
|
),
|
||||||
if(!_presentationEmail.isDraft)
|
if(!_presentationEmail.isDraft)
|
||||||
buildIconWeb(
|
buildSVGIconButton(
|
||||||
minSize: 18,
|
icon: _presentationEmail.hasRead ? imagePaths.icRead: imagePaths.icUnread,
|
||||||
iconSize: 18,
|
iconColor: AppColor.colorActionButtonHover,
|
||||||
iconPadding: const EdgeInsets.all(5),
|
iconSize: _getIconSize(),
|
||||||
splashRadius: 10,
|
padding: _getPaddingIcon(),
|
||||||
icon: SvgPicture.asset(
|
tooltip: _presentationEmail.hasRead
|
||||||
_presentationEmail.hasRead
|
? AppLocalizations.of(_context).mark_as_unread
|
||||||
? imagePaths.icRead
|
: AppLocalizations.of(_context).mark_as_read,
|
||||||
: imagePaths.icUnread,
|
onTap: () => _emailActionClick?.call(
|
||||||
color: AppColor.colorActionButtonHover,
|
_presentationEmail.hasRead ? EmailActionType.markAsUnread : EmailActionType.markAsRead,
|
||||||
width: 16,
|
_presentationEmail
|
||||||
height: 16,
|
)
|
||||||
fit: BoxFit.fill),
|
),
|
||||||
tooltip: _presentationEmail.hasRead
|
|
||||||
? AppLocalizations.of(_context).mark_as_unread
|
|
||||||
: AppLocalizations.of(_context).mark_as_read,
|
|
||||||
onTap: () => _emailActionClick?.call(
|
|
||||||
_presentationEmail.hasRead
|
|
||||||
? EmailActionType.markAsUnread
|
|
||||||
: EmailActionType.markAsRead,
|
|
||||||
_presentationEmail)),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
if (mailboxContain != null && mailboxContain?.isDrafts == false)
|
if (mailboxContain != null && mailboxContain?.isDrafts == false)
|
||||||
... [
|
... [
|
||||||
buildIconWeb(
|
buildSVGIconButton(
|
||||||
minSize: 18,
|
icon: imagePaths.icMove,
|
||||||
iconSize: 18,
|
iconColor: AppColor.colorActionButtonHover,
|
||||||
iconPadding: const EdgeInsets.all(5),
|
iconSize: _getIconSize(),
|
||||||
splashRadius: 10,
|
padding: _getPaddingIcon(),
|
||||||
icon: SvgPicture.asset(
|
tooltip: AppLocalizations.of(_context).move,
|
||||||
imagePaths.icMove,
|
onTap: () => _emailActionClick?.call(
|
||||||
width: 16,
|
EmailActionType.moveToMailbox,
|
||||||
height: 16,
|
_presentationEmail
|
||||||
color: AppColor.colorActionButtonHover,
|
)
|
||||||
fit: BoxFit.fill),
|
),
|
||||||
tooltip: AppLocalizations.of(_context).move,
|
|
||||||
onTap: () => _emailActionClick?.call(
|
|
||||||
EmailActionType.moveToMailbox,
|
|
||||||
_presentationEmail)),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
],
|
],
|
||||||
buildIconWeb(
|
buildSVGIconButton(
|
||||||
minSize: 18,
|
icon: imagePaths.icDeleteComposer,
|
||||||
iconSize: 18,
|
iconColor: AppColor.colorActionButtonHover,
|
||||||
iconPadding: const EdgeInsets.all(5),
|
iconSize: responsiveUtils.isDesktop(_context) ? 16 : 14,
|
||||||
splashRadius: 10,
|
padding: _getPaddingIcon(),
|
||||||
icon: SvgPicture.asset(
|
tooltip: canDeletePermanently
|
||||||
canDeletePermanently ? imagePaths.icDeleteComposer : imagePaths.icDelete,
|
? AppLocalizations.of(_context).delete_permanently
|
||||||
width: canDeletePermanently ? 14 : 16,
|
: AppLocalizations.of(_context).move_to_trash,
|
||||||
height: canDeletePermanently ? 14 : 16,
|
onTap: () => _emailActionClick?.call(
|
||||||
color: AppColor.colorActionButtonHover,
|
canDeletePermanently ? EmailActionType.deletePermanently : EmailActionType.moveToTrash,
|
||||||
fit: BoxFit.fill),
|
_presentationEmail
|
||||||
tooltip: canDeletePermanently
|
)
|
||||||
? AppLocalizations.of(_context).delete_permanently
|
),
|
||||||
: AppLocalizations.of(_context).move_to_trash,
|
buildSVGIconButton(
|
||||||
onTap: () => _emailActionClick?.call(
|
icon: imagePaths.icMore,
|
||||||
canDeletePermanently
|
iconColor: AppColor.colorActionButtonHover,
|
||||||
? EmailActionType.deletePermanently
|
iconSize: _getIconSize(),
|
||||||
: EmailActionType.moveToTrash,
|
padding: _getPaddingIcon(),
|
||||||
_presentationEmail)),
|
tooltip: AppLocalizations.of(_context).more,
|
||||||
const SizedBox(width: 5),
|
onTapDown: (tapDetails) {
|
||||||
if (mailboxContain?.isDrafts == false)
|
if (responsiveUtils.isScreenWithShortestSide(_context)) {
|
||||||
buildIconWebHasPosition(
|
_onMoreActionClick?.call(_presentationEmail, null);
|
||||||
_context,
|
} else {
|
||||||
icon: SvgPicture.asset(
|
_onMoreActionClick?.call(_presentationEmail, tapDetails.getPosition(_context));
|
||||||
imagePaths.icMore,
|
}
|
||||||
width: 16,
|
}
|
||||||
height: 16,
|
),
|
||||||
color: AppColor.colorActionButtonHover,
|
|
||||||
fit: BoxFit.fill),
|
|
||||||
tooltip: AppLocalizations.of(_context).more,
|
|
||||||
onTap: () {
|
|
||||||
if (responsiveUtils.isMobile(_context)) {
|
|
||||||
_onMoreActionClick?.call(_presentationEmail, null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onTapDown: (position) {
|
|
||||||
if (!responsiveUtils.isMobile(_context)) {
|
|
||||||
_onMoreActionClick?.call(_presentationEmail, position);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
if (responsiveUtils.isDesktop(_context)) const SizedBox(width: 16),
|
if (responsiveUtils.isDesktop(_context)) const SizedBox(width: 16),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -510,7 +505,6 @@ class EmailTileBuilder with BaseEmailItemTile {
|
|||||||
padding: const EdgeInsets.only(right: 4, left: 8),
|
padding: const EdgeInsets.only(right: 4, left: 8),
|
||||||
child: buildDateTime(_context, _presentationEmail)),
|
child: buildDateTime(_context, _presentationEmail)),
|
||||||
buildIconChevron()
|
buildIconChevron()
|
||||||
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2022-11-11T02:15:40.658946",
|
"@@last_modified": "2022-11-14T16:25:49.764077",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -2521,5 +2521,11 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"openInNewTab": "Open in New Tab",
|
||||||
|
"@openInNewTab": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2587,4 +2587,11 @@ class AppLocalizations {
|
|||||||
'Page 404',
|
'Page 404',
|
||||||
name: 'page404');
|
name: 'page404');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get openInNewTab {
|
||||||
|
return Intl.message(
|
||||||
|
'Open in New Tab',
|
||||||
|
name: 'openInNewTab',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,4 +17,5 @@ enum EmailActionType {
|
|||||||
selection,
|
selection,
|
||||||
moveToSpam,
|
moveToSpam,
|
||||||
unSpam,
|
unSpam,
|
||||||
|
openInNewTab
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ extension PresentationEmailExtension on PresentationEmail {
|
|||||||
mailboxIds: mailboxIds,
|
mailboxIds: mailboxIds,
|
||||||
mailboxNames: listMailboxName,
|
mailboxNames: listMailboxName,
|
||||||
selectMode: selectMode,
|
selectMode: selectMode,
|
||||||
routeWeb: routeWeb
|
routeWeb: routeWeb
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user