58a576adeb
TF-3643 Thread Detail cache email with thread id TF-3643 Thread Detail view thread detail TF-3643 Thread Detail [Part 2 fixup] remove ThreadDetailController dependency from MailboxDashboardController TF-3643 Thread Detail [Part 2 fixup] remove EmailSupervisorController dependency from ThreadDetailController TF-3643 Thread Detail [Part 2 fixup] move isSearchRunning check into ThreadDetailController TF-3643 Thread Detail [Part 2 fixup] ensure GetEmailsByIdsInteractor performance TF-3643 Thread Detail [Part 2 fixup] unit test loadMoreThreadDetailEmails TF-3643 Thread Detail [Part 2 fixup] unit test getEmailIdsToLoad TF-3643 Thread Detail [Part 2 fixup] optimize ThreadDetailBinding bindingsDataSource TF-3643 Thread Detail [Part 2 fixup] optimize getThreadDetailLoadingView TF-3643 Thread Detail [Part 2 fixup] readd unique ValueKey for HtmlContentViewerOnWebWidget TF-3643 Thread Detail [Part 2 fixup] unit test initializeThreadDetailEmails TF-3643 Thread Detail [Part 3 fixup] Update private action button methods TF-3643 Thread Detail [Part 4 fixup] Collapsed email preview 65 characters max TF-3643 Thread Detail [Part 4 fixup] Remove personal scripts TF-3643 Thread Detail move EmailInThreadStatus into PresentationEmail TF-3643 Thread Detail refactor toggle collapse expand TF-3643 Thread Detail EmailViewAppBarWidget required parameters TF-3643 Thread Detail tap avatar toggle expansion TF-3643 Thread Detail fix cannot open single email TF-3643 Thread Detail fix open thread first time blank view TF-3643 Thread Detail fix thread detail UI TF-3643 Thread Detail fix round bottom view TF-3643 Thread Detail fix round bottom view TF-3643 Thread Detail bottom actions depend on current expanded email TF-3643 Thread Detail hide next previous actions TF-3643 Thread Detail fix load more show initial loading view TF-3643 Thread Detail fix cannot view single email TF-3643 Thread Detail fix bottom place holder not aligned TF-3643 Thread Detail fix back to search view TF-3643 Thread Detail fix display 65 character preview collapsed email TF-3643 Thread Detail fix search not work TF-3643 Thread Detail fix bottom place holder UI TF-3643 Thread Detail fix expand view TF-3643 Thread Detail jump to top of last email view TF-3643 Thread Detail remove toMe condition of filtering thread TF-3643 Thread Detail fix sort thread detail TF-3643 Thread Detail fix assert not work in release TF-3643 Thread Detail add load more test case <20 TF-3643 Thread Detail remove the use of currentThreadId TF-3643 Thread Detail remove the use of selectedEmail in SingleEmailController TF-3643 Thread Detail fix unit test SingleEmailController calendar TF-3643 Thread Detail disable 2 way scroll control TF-3643 Thread Detail allow thread to load sent email incase thread only contains 1 email TF-3643 Thread Detail disable single email page view TF-3643 Thread Detail update emailsNotLoadedCount name TF-3643 Thread Detail remove collapsed email char limit TF-3643 Thread Detail fix CI TF-3643 Thread Detail unit test handleGetEmailIdsByThreadIdSuccess TF-3643 Thread Detail make _currentEmailId private TF-3643 Thread Detail fix search click users TF-3643 Thread Detail fix scroll jump TF-3643 Thread Detail fix scroll jump TF-3643 Thread Detail fix web back action TF-3643 Thread Detail round bottom actions TF-3643 Thread Detail fix tablet large empty email TF-3643 Thread Detail fix email subject selectable TF-3643 Thread Detail fix email single view with top divider TF-3643 Thread Detail fix email cache threadId missing TF-3643 Thread Detail fix filter sent email in thread details TF-3643 Thread Detail fix select text TF-3643 Thread Detail fix subject alignment TF-3643 Thread Detail fix subject alignment TF-3643 Thread Detail fix tablet large empty email TF-3643 Thread Detail add nullcheck for _jumpScrollViewToTopOfEmail TF-3643 Thread Detail decouple ThreadDetailController from SingleEmailController TF-3643 Thread Detail combine emailIds to emailIdsPresentation TF-3643 Thread Detail remove currentEmailLoaded obx listener of SingleEmailController TF-3643 Thread Detail replace threadAction with an email ui action TF-3643 Thread Detail fix top and bottom bar delay displaying TF-3643 Thread Detail update ReplyForwardBottomBarAction to PerformEmailActionInThreadDetailAction TF-3643 Thread Detail use getOwnEmailAddress in ThreadDetailAppBar TF-3643 Thread Detail add TextOverflow.ellipsis to EmailSubjectWidget TF-3643 Thread Detail fix round border threadDetailLoadingView
198 lines
6.5 KiB
Dart
198 lines
6.5 KiB
Dart
|
|
import 'dart:ui';
|
|
|
|
import 'package:core/presentation/extensions/uri_extension.dart';
|
|
import 'package:core/utils/app_logger.dart';
|
|
import 'package:get/get_utils/src/extensions/string_extensions.dart';
|
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/calendar_event_capability.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/default_capability.dart';
|
|
import 'package:jmap_dart_client/jmap/core/capability/empty_capability.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/unsigned_int.dart';
|
|
import 'package:model/error_type_handler/account_exception.dart';
|
|
import 'package:model/error_type_handler/unknown_address_exception.dart';
|
|
import 'package:model/error_type_handler/unknown_uri_exception.dart';
|
|
import 'package:model/model.dart';
|
|
import 'package:model/principals/capability_principals.dart';
|
|
import 'package:uri/uri.dart';
|
|
|
|
extension SessionExtension on Session {
|
|
|
|
String getDownloadUrl({String? jmapUrl}) {
|
|
final Uri downloadUrlValid;
|
|
if (jmapUrl != null) {
|
|
downloadUrlValid = downloadUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl));
|
|
} else if (downloadUrl.hasOrigin) {
|
|
downloadUrlValid = downloadUrl;
|
|
} else {
|
|
throw UnknownUriException();
|
|
}
|
|
|
|
var baseUrl = '${downloadUrlValid.origin}${downloadUrlValid.path}?${downloadUrlValid.query}';
|
|
if (baseUrl.endsWith('/')) {
|
|
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
|
|
}
|
|
final downloadUrlDecode = Uri.decodeFull(baseUrl);
|
|
return downloadUrlDecode;
|
|
}
|
|
|
|
Uri getUploadUri(AccountId accountId, {String? jmapUrl}) {
|
|
final Uri uploadUrlValid;
|
|
if (jmapUrl != null) {
|
|
uploadUrlValid = uploadUrl.toQualifiedUrl(baseUrl: Uri.parse(jmapUrl));
|
|
} else if (uploadUrl.hasOrigin) {
|
|
uploadUrlValid = uploadUrl;
|
|
} else {
|
|
throw UnknownUriException();
|
|
}
|
|
|
|
final baseUrl = '${uploadUrlValid.origin}${uploadUrlValid.path}';
|
|
final uploadUriTemplate = UriTemplate(Uri.decodeFull(baseUrl));
|
|
final uploadUri = uploadUriTemplate.expand({
|
|
'accountId' : accountId.id.value
|
|
});
|
|
return Uri.parse(uploadUri);
|
|
}
|
|
|
|
T? getCapabilityProperties<T extends CapabilityProperties>(
|
|
AccountId accountId,
|
|
CapabilityIdentifier identifier
|
|
) {
|
|
var capability = accounts[accountId]?.accountCapabilities[identifier];
|
|
if (capability == null || capability is EmptyCapability) {
|
|
capability = capabilities[identifier];
|
|
}
|
|
if (capability is T) {
|
|
return capability;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String getUserDisplayName() {
|
|
try {
|
|
final accountDisplayName = personalAccount.name.value;
|
|
if (accountDisplayName.isNotEmpty) {
|
|
return accountDisplayName;
|
|
} else {
|
|
return username.value;
|
|
}
|
|
} catch (e) {
|
|
logError('SessionExtension::getUserDisplayName:Exception: $e');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
String getOwnEmailAddressOrEmpty() {
|
|
try {
|
|
return getOwnEmailAddress();
|
|
} catch (e) {
|
|
logError('SessionExtension::getOwnEmailAddressOrEmpty:Exception: $e');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
String getOwnEmailAddress() {
|
|
return username.value.isEmail ? username.value
|
|
: _getOwnEmailAddressFromPersonalAccount()
|
|
?? _getOwnEmailAddressFromPrincipalsCapability()
|
|
?? (throw UnknownAddressException());
|
|
}
|
|
|
|
String? _getOwnEmailAddressFromPersonalAccount() {
|
|
try {
|
|
return personalAccount.name.value.isEmail ? personalAccount.name.value : null;
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String? _getOwnEmailAddressFromPrincipalsCapability() {
|
|
try {
|
|
var principalsCapability = getCapabilityProperties<DefaultCapability>(AccountId(Id(username.value)), capabilityPrincipals);
|
|
final sendTo = principalsCapability?.properties?['urn:ietf:params:jmap:calendars']?['sendTo'];
|
|
if (sendTo is Map<String, dynamic>) {
|
|
final wrappedAddress = sendTo['imip'];
|
|
if (wrappedAddress is String && wrappedAddress.startsWith('mailto:')) {
|
|
String address = wrappedAddress.substring("mailto:".length);
|
|
return address.isEmail ? address : null;
|
|
}
|
|
}
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
JmapAccount get personalAccount {
|
|
if (accounts.isNotEmpty) {
|
|
final listPersonalAccount = accounts.entries
|
|
.map((entry) => entry.value.toJmapAccount(entry.key))
|
|
.where((jmapAccount) => jmapAccount.isPersonal)
|
|
.toList();
|
|
|
|
if (listPersonalAccount.isNotEmpty) {
|
|
return listPersonalAccount.first;
|
|
}
|
|
}
|
|
throw NotFoundPersonalAccountException();
|
|
}
|
|
|
|
AccountId get accountId => personalAccount.accountId;
|
|
|
|
({
|
|
bool isAvailable,
|
|
CalendarEventCapability? calendarEventCapability
|
|
}) validateCalendarEventCapability(AccountId accountId) {
|
|
final capability = getCapabilityProperties<CalendarEventCapability>(
|
|
accountId,
|
|
CapabilityIdentifier.jamesCalendarEvent);
|
|
|
|
return (isAvailable: capability != null, calendarEventCapability: capability);
|
|
}
|
|
|
|
bool validateAcceptCounterCalendarEventCapability(AccountId accountId) {
|
|
final capability = getCapabilityProperties<CalendarEventCapability>(
|
|
accountId,
|
|
CapabilityIdentifier.jamesCalendarEvent);
|
|
|
|
return capability?.counterSupport == true;
|
|
}
|
|
|
|
String? getLanguageForCalendarEvent(
|
|
Locale locale,
|
|
AccountId accountId,
|
|
) {
|
|
final validation = validateCalendarEventCapability(accountId);
|
|
if (!validation.isAvailable) return null;
|
|
|
|
final supportedLanguages = validation.calendarEventCapability!.replySupportedLanguage;
|
|
if (supportedLanguages == null) return null;
|
|
|
|
final currentLanguage = locale.languageCode;
|
|
if (supportedLanguages.contains(currentLanguage)) {
|
|
return currentLanguage;
|
|
} else if (supportedLanguages.contains('en')) {
|
|
return 'en';
|
|
} else {
|
|
return supportedLanguages.firstOrNull;
|
|
}
|
|
}
|
|
|
|
UnsignedInt? getMaxObjectsInGet(AccountId accountId) {
|
|
try {
|
|
return getCapabilityProperties<CoreCapability>(
|
|
accountId,
|
|
CapabilityIdentifier.jmapCore
|
|
)?.maxObjectsInGet;
|
|
} catch (e) {
|
|
logError('SessionExtensions::getMaxObjectsInGet():Exception: $e');
|
|
return null;
|
|
}
|
|
}
|
|
} |