Files
workavia-mail-front/lib/features/email/presentation/utils/email_utils.dart
T
2025-07-31 16:37:10 +07:00

265 lines
9.3 KiB
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/utils/app_logger.dart';
import 'package:core/utils/mail/mail_address.dart';
import 'package:flutter/material.dart';
import 'package:get/get_utils/src/get_utils/get_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/email_unsubscribe.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
class EmailUtils {
EmailUtils._();
static Properties getPropertiesForEmailGetMethod(Session session, AccountId accountId) {
if (CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId)) {
return ThreadConstants.propertiesCalendarEvent;
} else {
return ThreadConstants.propertiesDefault;
}
}
static Properties getPropertiesForEmailChangeMethod(Session session, AccountId accountId) {
if (CapabilityIdentifier.jamesCalendarEvent.isSupported(session, accountId)) {
return ThreadConstants.propertiesCalendarEvent;
} else {
return ThreadConstants.propertiesUpdatedDefault;
}
}
static EmailUnsubscribe? parsingUnsubscribe(String listUnsubscribe) {
if (listUnsubscribe.isEmpty) {
return null;
}
final regExpMailtoLinks = RegExp(r'mailto:([^>,]*)');
final allMatchesMailtoLinks = regExpMailtoLinks.allMatches(listUnsubscribe);
final listMailtoLinks = allMatchesMailtoLinks
.map((match) => match.group(0))
.nonNulls
.toList();
log('EmailUtils::parsingUnsubscribe:listMailtoLinks: $listMailtoLinks');
final regExpHttpLinks = RegExp(r'http([^>,]*)');
final allMatchesHttpLinks = regExpHttpLinks.allMatches(listUnsubscribe);
final listHttpLinks = allMatchesHttpLinks
.map((match) => match.group(0))
.nonNulls
.toList();
log('EmailUtils::parsingUnsubscribe:listHttpLinks: $listHttpLinks');
if (listMailtoLinks.isNotEmpty || listHttpLinks.isNotEmpty) {
return EmailUnsubscribe(
httpLinks: listHttpLinks,
mailtoLinks: listMailtoLinks
);
} else {
return null;
}
}
static bool checkingIfAttachmentActionIsEnabled(Either<Failure, Success>? state) {
return state?.fold(
(failure) {
return failure is DownloadAttachmentForWebFailure
|| failure is GetHtmlContentFromAttachmentFailure;
},
(success) {
return success is DownloadAttachmentForWebSuccess
|| success is GetHtmlContentFromAttachmentSuccess
|| success is IdleDownloadAttachmentForWeb;
}) ?? false;
}
static bool isSameDomain({
required String emailAddress,
required String internalDomain
}) {
log('EmailUtils::isSameDomain: emailAddress = $emailAddress | internalDomain = $internalDomain');
return EmailUtils.isEmailAddressValid(emailAddress) &&
emailAddress.split('@').last.toLowerCase() == internalDomain.toLowerCase();
}
static bool isEmailAddressValid(String address) {
try {
MailAddress mailAddress = MailAddress.validateAddress(address);
return GetUtils.isEmail(mailAddress.stripDetails().asString()) && mailAddress.asString().isNotEmpty;
} catch(e) {
logError('EmailUtils::isEmailAddressValid: Exception = $e');
return false;
}
}
static List<String> extractMailtoLinksFromListPost(String listPost) {
try {
if (listPost.trim().isEmpty) return [];
final decodedInput = Uri.decodeComponent(listPost);
final mailtoRegex = RegExp(r'<(mailto:[^<>]+)>');
final matches = mailtoRegex.allMatches(decodedInput);
if (matches.isEmpty) {
log('EmailUtils::extractMailtoLinksFromListPost: Not found mailto link');
return [];
}
return matches.map((match) => match.group(1)!).toList();
} catch (e) {
logError('EmailUtils::extractMailtoLinksFromListPost:Exception = $e');
return [];
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromListMailtoLink(List<String> mailtoLinks) {
try {
log('EmailUtils::extractRecipientsFromListMailtoLink: mailtoLinks: $mailtoLinks:');
if (mailtoLinks.isEmpty) {
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
final toMailAddresses = <EmailAddress>[];
final ccMailAddresses = <EmailAddress>[];
final bccMailAddresses = <EmailAddress>[];
for (var mailtoLink in mailtoLinks) {
final recipientRecord = extractRecipientsFromMailtoLink(mailtoLink);
toMailAddresses.addAll(recipientRecord.toMailAddresses);
ccMailAddresses.addAll(recipientRecord.ccMailAddresses);
bccMailAddresses.addAll(recipientRecord.bccMailAddresses);
}
return (
toMailAddresses: toMailAddresses,
ccMailAddresses: ccMailAddresses,
bccMailAddresses: bccMailAddresses
);
} catch (e) {
logError('EmailUtils::extractRecipientsFromListMailtoLink:Exception = $e');
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromMailtoLink(String mailtoLink) {
try {
log('EmailUtils::extractRecipientsFromMailtoLink:mailtoLink: $mailtoLink:');
if (mailtoLink.isEmpty) {
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
final navigationRouter =
RouteUtils.generateNavigationRouterFromMailtoLink(mailtoLink);
log('EmailUtils::extractRecipientsFromMailtoLink:navigationRouter = $navigationRouter');
return (
toMailAddresses: navigationRouter.listEmailAddress ?? [],
ccMailAddresses: navigationRouter.cc ?? [],
bccMailAddresses: navigationRouter.bcc ?? [],
);
} catch (e) {
logError('EmailUtils::extractRecipientsFromMailtoLink:Exception = $e');
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromListPost(String listPost) {
final mailtoLinks = extractMailtoLinksFromListPost(listPost);
return extractRecipientsFromListMailtoLink(mailtoLinks);
}
static Attachment? parsingAttachmentByUri(Uri uri) {
try {
final blobId = uri.path;
final queryParams = uri.queryParameters;
final name = queryParams['name'];
final size = queryParams['size'];
final type = queryParams['type'];
log('EmailUtils::parsingAttachmentByUri:blobId = $blobId | name = $name | size = $size | type = $type');
return Attachment(
blobId: Id(blobId),
name: name,
size: size?.isNotEmpty == true ? UnsignedInt(int.parse(size!)) : null,
type: type?.isNotEmpty == true ? MediaType.parse(type!) : null,
);
} catch (e) {
logError('EmailUtils::parsingAttachmentByUri:Exception = $e:');
return null;
}
}
static bool isReplyToListEnabled(String listPost) {
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost);
return recipientRecord.toMailAddresses.isNotEmpty ||
recipientRecord.ccMailAddresses.isNotEmpty ||
recipientRecord.bccMailAddresses.isNotEmpty;
}
static List<Attachment> getAttachmentDisplayed({
required BuildContext context,
required double maxWidth,
required bool platformIsMobile,
required List<Attachment> attachments,
required ResponsiveUtils responsiveUtils,
}) {
if (attachments.isEmpty) return [];
final bool isMobile = responsiveUtils.isMobile(context);
if (isMobile) {
return attachments.length <= 3 ? attachments : attachments.sublist(0, 3);
}
const double maxWidthItem = 260;
const double buttonMoreMaxWidth = 120;
final int possibleDisplayedCount =
((maxWidth - buttonMoreMaxWidth) ~/ maxWidthItem)
.clamp(0, attachments.length);
return possibleDisplayedCount == 0
? [attachments.first]
: attachments.sublist(0, possibleDisplayedCount);
}
}