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? 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 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 toMailAddresses, List ccMailAddresses, List bccMailAddresses, }) extractRecipientsFromListMailtoLink(List mailtoLinks) { try { log('EmailUtils::extractRecipientsFromListMailtoLink: mailtoLinks: $mailtoLinks:'); if (mailtoLinks.isEmpty) { return ( toMailAddresses: [], ccMailAddresses: [], bccMailAddresses: [], ); } final toMailAddresses = []; final ccMailAddresses = []; final bccMailAddresses = []; 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 toMailAddresses, List ccMailAddresses, List 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 toMailAddresses, List ccMailAddresses, List 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 getAttachmentDisplayed({ required BuildContext context, required double maxWidth, required bool platformIsMobile, required List 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); } }