TF-2613 Handle display forward warning icon in recipient item

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2024-02-29 12:07:48 +07:00
committed by Dat H. Pham
parent 33f32783aa
commit 86991b5168
11 changed files with 181 additions and 40 deletions
@@ -71,10 +71,10 @@ class EmailUtils {
static bool isSameDomain({ static bool isSameDomain({
required String emailAddress, required String emailAddress,
required String serverDomain required String internalDomain
}) { }) {
log('EmailUtils::isSameDomain: emailAddress = $emailAddress | serverDomain = $serverDomain'); log('EmailUtils::isSameDomain: emailAddress = $emailAddress | internalDomain = $internalDomain');
return GetUtils.isEmail(emailAddress) && return GetUtils.isEmail(emailAddress) &&
emailAddress.split('@').last.toLowerCase() == serverDomain.toLowerCase(); emailAddress.split('@').last.toLowerCase() == internalDomain.toLowerCase();
} }
} }
@@ -2,6 +2,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:core/presentation/extensions/uri_extension.dart'; import 'package:core/presentation/extensions/uri_extension.dart';
import 'package:core/utils/app_logger.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/http/converter/state_converter.dart'; import 'package:jmap_dart_client/http/converter/state_converter.dart';
import 'package:jmap_dart_client/http/converter/user_name_converter.dart'; import 'package:jmap_dart_client/http/converter/user_name_converter.dart';
@@ -46,12 +47,17 @@ extension SessionExtensions on Session {
} }
} }
String get serverDomain { String get internalDomain {
if (GetUtils.isEmail(username.value)) { try {
return username.value.split('@').last; if (GetUtils.isEmail(username.value)) {
} else if (GetUtils.isEmail(personalAccount.name.value)) { return username.value.split('@').last;
return personalAccount.name.value.split('@').last; } else if (GetUtils.isEmail(personalAccount.name.value)) {
} else { return personalAccount.name.value.split('@').last;
} else {
return '';
}
} catch (e) {
logError('SessionExtensions::internalDomain: Exception: $e');
return ''; return '';
} }
} }
@@ -177,10 +177,10 @@ class ForwardController extends BaseController {
bool get _isExistRecipientSameServerDomain => bool get _isExistRecipientSameServerDomain =>
listRecipientForward.any((recipient) { listRecipientForward.any((recipient) {
final serverDomain = accountDashBoardController.sessionCurrent?.serverDomain ?? ''; final internalDomain = accountDashBoardController.sessionCurrent?.internalDomain ?? '';
final isSameDomain = EmailUtils.isSameDomain( final isSameDomain = EmailUtils.isSameDomain(
emailAddress: recipient.emailAddress.emailAddress, emailAddress: recipient.emailAddress.emailAddress,
serverDomain: serverDomain internalDomain: internalDomain
); );
return !isSameDomain; return !isSameDomain;
}); });
@@ -126,7 +126,7 @@ class ForwardView extends GetWidget<ForwardController> with AppLoaderMixin {
Widget _buildAddRecipientsFormWidget(BuildContext context) { Widget _buildAddRecipientsFormWidget(BuildContext context) {
return AutocompleteContactTextFieldWithTags( return AutocompleteContactTextFieldWithTags(
listEmailAddress: controller.recipientController.listRecipients, listEmailAddress: controller.recipientController.listRecipients,
serverDomain: controller.accountDashBoardController.sessionCurrent?.serverDomain ?? '', internalDomain: controller.accountDashBoardController.sessionCurrent?.internalDomain ?? '',
controller: controller.recipientController.inputRecipientController, controller: controller.recipientController.inputRecipientController,
onSuggestionCallback: controller.recipientController.getAutoCompleteSuggestion, onSuggestionCallback: controller.recipientController.getAutoCompleteSuggestion,
hasAddContactButton: true, hasAddContactButton: true,
@@ -35,12 +35,12 @@ class AutocompleteContactTextFieldWithTags extends StatefulWidget {
final OnSuggestionContactCallbackAction? onSuggestionCallback; final OnSuggestionContactCallbackAction? onSuggestionCallback;
final OnAddListContactCallbackAction? onAddContactCallback; final OnAddListContactCallbackAction? onAddContactCallback;
final OnExceptionAddListContactCallbackAction? onExceptionCallback; final OnExceptionAddListContactCallbackAction? onExceptionCallback;
final String serverDomain; final String internalDomain;
const AutocompleteContactTextFieldWithTags({ const AutocompleteContactTextFieldWithTags({
Key? key, Key? key,
required this.listEmailAddress, required this.listEmailAddress,
required this.serverDomain, required this.internalDomain,
this.controller, this.controller,
this.hasAddContactButton = false, this.hasAddContactButton = false,
this.onSuggestionCallback, this.onSuggestionCallback,
@@ -336,7 +336,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
final validateSameDomain = EmailUtils.isSameDomain( final validateSameDomain = EmailUtils.isSameDomain(
emailAddress: emailAddress.emailAddress, emailAddress: emailAddress.emailAddress,
serverDomain: widget.serverDomain internalDomain: widget.internalDomain
); );
if (!validateSameDomain) { if (!validateSameDomain) {
@@ -3,6 +3,7 @@ import 'package:core/presentation/extensions/string_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/style_utils.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/button/tmail_button_widget.dart';
import 'package:core/presentation/views/image/avatar_builder.dart'; import 'package:core/presentation/views/image/avatar_builder.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';
@@ -10,7 +11,9 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/select_mode.dart'; import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/recipient_forward.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnSelectRecipientCallbackAction = Function(RecipientForward recipientForward); typedef OnSelectRecipientCallbackAction = Function(RecipientForward recipientForward);
typedef OnDeleteRecipientCallbackAction = Function(RecipientForward recipientForward); typedef OnDeleteRecipientCallbackAction = Function(RecipientForward recipientForward);
@@ -19,11 +22,16 @@ class EmailForwardItemWidget extends StatelessWidget {
final RecipientForward recipientForward; final RecipientForward recipientForward;
final SelectMode selectionMode; final SelectMode selectionMode;
final String internalDomain;
final OnSelectRecipientCallbackAction? onSelectRecipientCallback; final OnSelectRecipientCallbackAction? onSelectRecipientCallback;
final OnDeleteRecipientCallbackAction? onDeleteRecipientCallback; final OnDeleteRecipientCallbackAction? onDeleteRecipientCallback;
const EmailForwardItemWidget(this.recipientForward, { final ImagePaths _imagePaths = Get.find<ImagePaths>();
EmailForwardItemWidget({
Key? key, Key? key,
required this.recipientForward,
required this.internalDomain,
this.selectionMode = SelectMode.INACTIVE, this.selectionMode = SelectMode.INACTIVE,
this.onSelectRecipientCallback, this.onSelectRecipientCallback,
this.onDeleteRecipientCallback, this.onDeleteRecipientCallback,
@@ -31,8 +39,6 @@ class EmailForwardItemWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final imagePaths = Get.find<ImagePaths>();
return Padding( return Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
child: Material( child: Material(
@@ -53,23 +59,51 @@ class EmailForwardItemWidget extends StatelessWidget {
recipientForward.selectMode == SelectMode.ACTIVE ? 12 : 0)) recipientForward.selectMode == SelectMode.ACTIVE ? 12 : 0))
), ),
child: Row(children: [ child: Row(children: [
_buildAvatarIcon(imagePaths), _buildAvatarIcon(_imagePaths),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded(child: Column( Expanded(child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( if (EmailUtils.isSameDomain(
recipientForward.emailAddress.asString(), emailAddress: recipientForward.emailAddress.emailAddress,
overflow: CommonTextStyle.defaultTextOverFlow, internalDomain: internalDomain))
softWrap: CommonTextStyle.defaultSoftWrap, Text(
maxLines: 1, recipientForward.emailAddress.asString(),
style: const TextStyle( overflow: CommonTextStyle.defaultTextOverFlow,
fontSize: 16, softWrap: CommonTextStyle.defaultSoftWrap,
fontWeight: FontWeight.normal, maxLines: 1,
color: Colors.black style: Theme.of(context).textTheme.labelSmall?.copyWith(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
)
) )
), else
Row(
children: [
Flexible(
child: Text(
recipientForward.emailAddress.asString(),
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
)
),
),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icInfoCircleOutline,
iconColor: AppColor.colorQuotaError,
iconSize: 20,
backgroundColor: Colors.transparent,
padding: const EdgeInsets.all(3),
tooltipMessage: AppLocalizations.of(context).externalDomain,
)
],
),
if (recipientForward.emailAddress.displayName.isNotEmpty) if (recipientForward.emailAddress.displayName.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
@@ -92,7 +126,7 @@ class EmailForwardItemWidget extends StatelessWidget {
buildIconWeb( buildIconWeb(
iconSize: 30, iconSize: 30,
splashRadius: 20, splashRadius: 20,
icon: SvgPicture.asset(imagePaths.icDeleteRecipient), icon: SvgPicture.asset(_imagePaths.icDeleteRecipient),
onTap: () => onDeleteRecipientCallback?.call(recipientForward) onTap: () => onDeleteRecipientCallback?.call(recipientForward)
) )
]), ]),
@@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/select_mode.dart'; import 'package:model/mailbox/select_mode.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_controller.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/email_forward_item_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/widgets/email_forward_item_widget.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart';
@@ -34,10 +35,10 @@ class ListEmailForwardsWidget extends GetWidget<ForwardController> {
primary: false, primary: false,
itemCount: controller.listRecipientForward.length, itemCount: controller.listRecipientForward.length,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
itemExtent: 75,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return EmailForwardItemWidget( return EmailForwardItemWidget(
controller.listRecipientForward[index], recipientForward: controller.listRecipientForward[index],
internalDomain: controller.accountDashBoardController.sessionCurrent?.internalDomain ?? '',
selectionMode: controller.selectionMode.value, selectionMode: controller.selectionMode.value,
onSelectRecipientCallback: controller.selectRecipientForward, onSelectRecipientCallback: controller.selectRecipientForward,
onDeleteRecipientCallback: (recipientForward) { onDeleteRecipientCallback: (recipientForward) {
+7 -1
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2024-02-29T10:18:57.883345", "@@last_modified": "2024-02-29T11:49:16.217272",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -3647,5 +3647,11 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"externalDomain": "External domain",
"@externalDomain": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -3800,4 +3800,10 @@ class AppLocalizations {
name: 'messageWarningDialogForForwardsToOtherDomains' name: 'messageWarningDialogForForwardsToOtherDomains'
); );
} }
String get externalDomain {
return Intl.message(
'External domain',
name: 'externalDomain');
}
} }
@@ -5,27 +5,27 @@ void main() {
group('EmailUtils', () { group('EmailUtils', () {
test('isSameDomain should return true when email is from the same domain as server', () { test('isSameDomain should return true when email is from the same domain as server', () {
String emailAddress = 'user@example.com'; String emailAddress = 'user@example.com';
String serverDomain = 'example.com'; String internalDomain = 'example.com';
bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, serverDomain: serverDomain); bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, internalDomain: internalDomain);
expect(result, true); expect(result, true);
}); });
test('isSameDomain should return false when email is not from the same domain as server', () { test('isSameDomain should return false when email is not from the same domain as server', () {
String emailAddress = 'user@example.com'; String emailAddress = 'user@example.com';
String serverDomain = 'example2.com'; String internalDomain = 'example2.com';
bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, serverDomain: serverDomain); bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, internalDomain: internalDomain);
expect(result, false); expect(result, false);
}); });
test('isSameDomain should return false when email is invalid', () { test('isSameDomain should return false when email is invalid', () {
String emailAddress = 'invalid_email'; String emailAddress = 'invalid_email';
String serverDomain = 'example.com'; String internalDomain = 'example.com';
bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, serverDomain: serverDomain); bool result = EmailUtils.isSameDomain(emailAddress: emailAddress, internalDomain: internalDomain);
expect(result, false); expect(result, false);
}); });
@@ -0,0 +1,88 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/account/account.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
void main() {
group('get internal domain in session test', () {
test('When username is valid email, internalDomain should be returns the domain', () {
final Session session = Session(
{},
{},
{},
UserName('example@example.com'),
Uri.parse('http://domain.com/jmap'),
Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'),
Uri.parse('http://domain.com/upload/{accountId}'),
Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'),
State('2c9f1b12-b35a-43e6-9af2-0106fb53a943')
);
String result = session.internalDomain;
expect(result, equals('example.com'));
});
test('When personal account name is valid email, internalDomain should be returns the domain', () {
final Session session = Session(
{},
{
AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account(AccountName('example@example.com'), true, false, {})
},
{},
UserName('example'),
Uri.parse('http://domain.com/jmap'),
Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'),
Uri.parse('http://domain.com/upload/{accountId}'),
Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'),
State('2c9f1b12-b35a-43e6-9af2-0106fb53a943')
);
String result = session.internalDomain;
expect(result, equals('example.com'));
});
test('When neither username nor personal account name is valid email, internalDomain should be returns an empty string', () {
final Session session = Session(
{},
{
AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account(AccountName('example'), true, false, {})
},
{},
UserName('example'),
Uri.parse('http://domain.com/jmap'),
Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'),
Uri.parse('http://domain.com/upload/{accountId}'),
Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'),
State('2c9f1b12-b35a-43e6-9af2-0106fb53a943')
);
String result = session.internalDomain;
expect(result, equals(''));
});
test('When personalAccount throw an exception, internalDomain should be returns an empty string', () {
final Session session = Session(
{},
{},
{},
UserName('example'),
Uri.parse('http://domain.com/jmap'),
Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'),
Uri.parse('http://domain.com/upload/{accountId}'),
Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'),
State('2c9f1b12-b35a-43e6-9af2-0106fb53a943')
);
final result = session.internalDomain;
expect(result, equals(''));
});
});
}